openfda-mcp-server 1.1.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.
Files changed (54) hide show
  1. package/README.md +6 -0
  2. package/dist/defineProperty-D36W847_.js +1 -0
  3. package/dist/handlers/bulk-data-handlers.d.ts +108 -0
  4. package/dist/handlers/bulk-data-handlers.js +2 -0
  5. package/dist/handlers/bulk-data-handlers.js.map +1 -0
  6. package/dist/handlers/device-handlers.d.ts +13 -14
  7. package/dist/handlers/device-handlers.js +2 -1
  8. package/dist/handlers/device-handlers.js.map +1 -0
  9. package/dist/handlers/drug-handlers.d.ts +17 -18
  10. package/dist/handlers/drug-handlers.js +2 -1
  11. package/dist/handlers/drug-handlers.js.map +1 -0
  12. package/dist/handlers/query.d.ts +21 -0
  13. package/dist/handlers/query.js +2 -0
  14. package/dist/handlers/query.js.map +1 -0
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/lib.d.ts +9 -3
  19. package/dist/lib.js +1 -1
  20. package/dist/server.d.ts +9 -10
  21. package/dist/server.js +1 -1
  22. package/dist/server.js.map +1 -1
  23. package/dist/tools/index.d.ts +231 -409
  24. package/dist/tools/index.js +2 -1
  25. package/dist/tools/index.js.map +1 -0
  26. package/dist/types/fda.d.ts +71 -18
  27. package/dist/utils/api-client.d.ts +27 -24
  28. package/dist/utils/api-client.js +2 -1
  29. package/dist/utils/api-client.js.map +1 -0
  30. package/dist/utils/bulk-data-client.d.ts +30 -0
  31. package/dist/utils/bulk-data-client.js +3 -0
  32. package/dist/utils/bulk-data-client.js.map +1 -0
  33. package/dist/utils/errors.d.ts +8 -0
  34. package/dist/utils/errors.js +2 -0
  35. package/dist/utils/errors.js.map +1 -0
  36. package/dist/utils/fields.d.ts +11 -0
  37. package/dist/utils/fields.js +2 -0
  38. package/dist/utils/fields.js.map +1 -0
  39. package/dist/utils/logger.d.ts +9 -9
  40. package/dist/utils/logger.js +1 -1
  41. package/dist/utils/logger.js.map +1 -1
  42. package/dist/version-BNWZ_jnk.d.ts +7 -0
  43. package/dist/version.d.ts +2 -0
  44. package/dist/version.js +2 -0
  45. package/dist/version.js.map +1 -0
  46. package/package.json +32 -24
  47. package/dist/api-client-BGnUXMwX.js +0 -2
  48. package/dist/api-client-BGnUXMwX.js.map +0 -1
  49. package/dist/device-handlers-D8AD5oIk.js +0 -2
  50. package/dist/device-handlers-D8AD5oIk.js.map +0 -1
  51. package/dist/drug-handlers-D5VxbBCL.js +0 -2
  52. package/dist/drug-handlers-D5VxbBCL.js.map +0 -1
  53. package/dist/tools-3M3lSxQv.js +0 -2
  54. package/dist/tools-3M3lSxQv.js.map +0 -1
@@ -1 +1,2 @@
1
- import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,r as c,s as l,t as u,u as d}from"../tools-3M3lSxQv.js";export{u as labelSections,o as searchDevice510KSchema,c as searchDeviceAdverseEventsSchema,i as searchDeviceClassificationsSchema,e as searchDeviceEnforcementSchema,s as searchDrugAdverseEventsSchema,l as searchDrugEnforcementSchema,t as searchDrugLabelsSchema,a as searchDrugNDCSchema,d as searchDrugShortagesSchema,n as searchDrugsFDASchema,r as toolSchemas};
1
+ import{z as e}from"zod";const t={limit:e.number().int().min(1).max(100).optional().describe(`Maximum results to return (1-100, default 10)`),skip:e.number().int().min(0).optional().describe(`Number of results to skip for pagination`)},n={dateFrom:e.string().optional().describe(`Start date (YYYY-MM-DD or YYYYMMDD format)`),dateTo:e.string().optional().describe(`End date (YYYY-MM-DD or YYYYMMDD format)`)},r=e.object({safetyReportId:e.string().optional().describe(`Unique safety report ID for retrieving a specific adverse event report`),drugName:e.string().optional().describe(`Drug or product name to search`),reaction:e.string().optional().describe(`Adverse reaction to search (e.g., 'headache', 'nausea')`),manufacturer:e.string().optional().describe(`Drug manufacturer name`),serious:e.boolean().optional().describe(`Filter for serious adverse events only`),...n,...t}),i=[`indications_and_usage`,`dosage_and_administration`,`contraindications`,`warnings`,`warnings_and_cautions`,`adverse_reactions`,`drug_interactions`,`clinical_pharmacology`,`mechanism_of_action`,`pharmacokinetics`,`overdosage`,`description`,`how_supplied`,`storage_and_handling`,`boxed_warning`],a=e.object({setId:e.string().optional().describe(`Unique label identifier (set_id) for retrieving a specific drug label`),drugName:e.string().optional().describe(`Drug brand or generic name`),indication:e.string().optional().describe(`Medical indication or use case`),activeIngredient:e.string().optional().describe(`Active ingredient/substance name`),route:e.string().optional().describe(`Route of administration (e.g., 'oral', 'intravenous')`),hasBoxedWarning:e.boolean().optional().describe(`Filter for drugs with boxed warnings (most serious safety warnings)`),sections:e.array(e.enum(i)).optional().describe(`Specific label sections to return (e.g., ['indications_and_usage', 'warnings', 'adverse_reactions'])`),...t}),o=e.object({productNdc:e.string().optional().describe(`National Drug Code (NDC)`),brandName:e.string().optional().describe(`Drug brand name`),genericName:e.string().optional().describe(`Drug generic name`),labelerName:e.string().optional().describe(`Drug labeler/manufacturer name`),dosageForm:e.string().optional().describe(`Dosage form (e.g., 'tablet', 'capsule', 'injection')`),route:e.string().optional().describe(`Route of administration`),...t}),s=e.object({recallingFirm:e.string().optional().describe(`Name of the recalling company`),classification:e.enum([`Class I`,`Class II`,`Class III`]).optional().describe(`Recall classification (I=most serious, III=least)`),status:e.enum([`Ongoing`,`Completed`,`Terminated`,`Pending`]).optional().describe(`Recall status`),state:e.string().optional().describe(`US state code (e.g., 'CA', 'NY')`),...n,...t}),c=e.object({sponsorName:e.string().optional().describe(`Drug sponsor/company name`),applicationNumber:e.string().optional().describe(`FDA application number (e.g., 'NDA012345')`),brandName:e.string().optional().describe(`Drug brand name`),marketingStatus:e.string().optional().describe(`Marketing status (e.g., 'Prescription', 'OTC')`),...t}),l=e.object({genericName:e.string().optional().describe(`Generic drug name`),status:e.enum([`Current`,`Resolved`]).optional().describe(`Shortage status`),...t}),u=e.object({deviceName:e.string().optional().describe(`Device name to search`),applicant:e.string().optional().describe(`Applicant/company name`),productCode:e.string().optional().describe(`FDA product code`),clearanceType:e.string().optional().describe(`Clearance type (e.g., 'Traditional', 'Special')`),decisionDateFrom:e.string().optional().describe(`Decision date range start (YYYY-MM-DD)`),decisionDateTo:e.string().optional().describe(`Decision date range end (YYYY-MM-DD)`),...t}),d=e.object({deviceName:e.string().optional().describe(`Device name to search`),deviceClass:e.enum([`1`,`2`,`3`]).optional().describe(`Device class (1=lowest risk, 3=highest)`),medicalSpecialty:e.string().optional().describe(`Medical specialty code`),productCode:e.string().optional().describe(`FDA product code`),regulationNumber:e.string().optional().describe(`CFR regulation number`),...t}),f=e.object({reportNumber:e.string().optional().describe(`MDR report number for retrieving a specific device adverse event report`),deviceName:e.string().optional().describe(`Device generic name`),brandName:e.string().optional().describe(`Device brand name`),manufacturerName:e.string().optional().describe(`Device manufacturer name`),eventType:e.enum([`Injury`,`Malfunction`,`Death`,`Other`]).optional().describe(`Type of adverse event`),...n,...t}),p=e.object({recallingFirm:e.string().optional().describe(`Name of the recalling company`),productDescription:e.string().optional().describe(`Product description keywords`),classification:e.enum([`Class I`,`Class II`,`Class III`]).optional().describe(`Recall classification (I=most serious)`),status:e.enum([`Ongoing`,`Completed`,`Terminated`,`Pending`]).optional().describe(`Recall status`),...n,...t}),m=e.object({drugName:e.string().optional().describe(`Drug trade name or ingredient to search`),applicant:e.string().optional().describe(`Applicant/company name`),applNo:e.string().optional().describe(`FDA application number`),teCode:e.string().optional().describe(`Therapeutic equivalence code (e.g., 'AB', 'BX')`),...t}),h=e.object({drugName:e.string().optional().describe(`Drug trade name or ingredient to search`),applNo:e.string().optional().describe(`FDA application number`),patentNo:e.string().optional().describe(`Patent number`),...t}),g=e.object({productName:e.string().optional().describe(`Product proprietary or proper name to search`),applicant:e.string().optional().describe(`Applicant/company name`),blaNumber:e.string().optional().describe(`Biologics License Application (BLA) number`),licenseType:e.enum([`351(a)`,`351(k)`]).optional().describe(`License type: 351(a) for originator, 351(k) for biosimilar`),biosimilar:e.boolean().optional().describe(`Filter for biosimilar products`),interchangeable:e.boolean().optional().describe(`Filter for interchangeable products`),...t}),_=e.object({drugName:e.string().optional().describe(`Drug trade name or ingredient to search`),applNo:e.string().optional().describe(`FDA application number`),includeExclusivity:e.boolean().optional().default(!0).describe(`Include exclusivity data (default: true)`),includePurpleBook:e.boolean().optional().default(!1).describe(`Include Purple Book biologics data (default: false)`),...t}),v={searchDrugAdverseEvents:r,searchDrugLabels:a,searchDrugNDC:o,searchDrugEnforcement:s,searchDrugsFDA:c,searchDrugShortages:l,searchDevice510K:u,searchDeviceClassifications:d,searchDeviceAdverseEvents:f,searchDeviceEnforcement:p,searchOrangeBook:m,searchOrangeBookPatents:h,searchPurpleBook:g,searchDrugPatentExpiry:_};export{i as labelSections,u as searchDevice510KSchema,f as searchDeviceAdverseEventsSchema,d as searchDeviceClassificationsSchema,p as searchDeviceEnforcementSchema,r as searchDrugAdverseEventsSchema,s as searchDrugEnforcementSchema,a as searchDrugLabelsSchema,o as searchDrugNDCSchema,_ as searchDrugPatentExpirySchema,l as searchDrugShortagesSchema,c as searchDrugsFDASchema,h as searchOrangeBookPatentsSchema,m as searchOrangeBookSchema,g as searchPurpleBookSchema,v as toolSchemas};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["paginationSchema","z","dateRangeSchema","searchDrugAdverseEventsSchema","labelSections","searchDrugLabelsSchema","searchDrugNDCSchema","searchDrugEnforcementSchema","searchDrugsFDASchema","searchDrugShortagesSchema","searchDevice510KSchema","searchDeviceClassificationsSchema","searchDeviceAdverseEventsSchema","searchDeviceEnforcementSchema","searchOrangeBookSchema","searchOrangeBookPatentsSchema","searchPurpleBookSchema","searchDrugPatentExpirySchema","toolSchemas"],"sources":["../../src/tools/index.ts"],"sourcesContent":["/**\n * MCP Tool Definitions\n * Zod schemas for all FDA MCP tools\n */\n\nimport { z } from \"zod\"\n\n// Common pagination schema\nconst paginationSchema = {\n limit: z.number().int().min(1).max(100).optional().describe(\"Maximum results to return (1-100, default 10)\"),\n skip: z.number().int().min(0).optional().describe(\"Number of results to skip for pagination\"),\n}\n\n// Date range schema\nconst dateRangeSchema = {\n dateFrom: z.string().optional().describe(\"Start date (YYYY-MM-DD or YYYYMMDD format)\"),\n dateTo: z.string().optional().describe(\"End date (YYYY-MM-DD or YYYYMMDD format)\"),\n}\n\n// Drug Tool Schemas\n\nexport const searchDrugAdverseEventsSchema = z.object({\n safetyReportId: z\n .string()\n .optional()\n .describe(\"Unique safety report ID for retrieving a specific adverse event report\"),\n drugName: z.string().optional().describe(\"Drug or product name to search\"),\n reaction: z.string().optional().describe(\"Adverse reaction to search (e.g., 'headache', 'nausea')\"),\n manufacturer: z.string().optional().describe(\"Drug manufacturer name\"),\n serious: z.boolean().optional().describe(\"Filter for serious adverse events only\"),\n ...dateRangeSchema,\n ...paginationSchema,\n})\n\n// Available label sections for filtering\nexport const labelSections = [\n \"indications_and_usage\",\n \"dosage_and_administration\",\n \"contraindications\",\n \"warnings\",\n \"warnings_and_cautions\",\n \"adverse_reactions\",\n \"drug_interactions\",\n \"clinical_pharmacology\",\n \"mechanism_of_action\",\n \"pharmacokinetics\",\n \"overdosage\",\n \"description\",\n \"how_supplied\",\n \"storage_and_handling\",\n \"boxed_warning\",\n] as const\n\nexport const searchDrugLabelsSchema = z.object({\n setId: z.string().optional().describe(\"Unique label identifier (set_id) for retrieving a specific drug label\"),\n drugName: z.string().optional().describe(\"Drug brand or generic name\"),\n indication: z.string().optional().describe(\"Medical indication or use case\"),\n activeIngredient: z.string().optional().describe(\"Active ingredient/substance name\"),\n route: z.string().optional().describe(\"Route of administration (e.g., 'oral', 'intravenous')\"),\n hasBoxedWarning: z\n .boolean()\n .optional()\n .describe(\"Filter for drugs with boxed warnings (most serious safety warnings)\"),\n sections: z\n .array(z.enum(labelSections))\n .optional()\n .describe(\"Specific label sections to return (e.g., ['indications_and_usage', 'warnings', 'adverse_reactions'])\"),\n ...paginationSchema,\n})\n\nexport const searchDrugNDCSchema = z.object({\n productNdc: z.string().optional().describe(\"National Drug Code (NDC)\"),\n brandName: z.string().optional().describe(\"Drug brand name\"),\n genericName: z.string().optional().describe(\"Drug generic name\"),\n labelerName: z.string().optional().describe(\"Drug labeler/manufacturer name\"),\n dosageForm: z.string().optional().describe(\"Dosage form (e.g., 'tablet', 'capsule', 'injection')\"),\n route: z.string().optional().describe(\"Route of administration\"),\n ...paginationSchema,\n})\n\nexport const searchDrugEnforcementSchema = z.object({\n recallingFirm: z.string().optional().describe(\"Name of the recalling company\"),\n classification: z\n .enum([\"Class I\", \"Class II\", \"Class III\"])\n .optional()\n .describe(\"Recall classification (I=most serious, III=least)\"),\n status: z.enum([\"Ongoing\", \"Completed\", \"Terminated\", \"Pending\"]).optional().describe(\"Recall status\"),\n state: z.string().optional().describe(\"US state code (e.g., 'CA', 'NY')\"),\n ...dateRangeSchema,\n ...paginationSchema,\n})\n\nexport const searchDrugsFDASchema = z.object({\n sponsorName: z.string().optional().describe(\"Drug sponsor/company name\"),\n applicationNumber: z.string().optional().describe(\"FDA application number (e.g., 'NDA012345')\"),\n brandName: z.string().optional().describe(\"Drug brand name\"),\n marketingStatus: z.string().optional().describe(\"Marketing status (e.g., 'Prescription', 'OTC')\"),\n ...paginationSchema,\n})\n\nexport const searchDrugShortagesSchema = z.object({\n genericName: z.string().optional().describe(\"Generic drug name\"),\n status: z.enum([\"Current\", \"Resolved\"]).optional().describe(\"Shortage status\"),\n ...paginationSchema,\n})\n\n// Device Tool Schemas\n\nexport const searchDevice510KSchema = z.object({\n deviceName: z.string().optional().describe(\"Device name to search\"),\n applicant: z.string().optional().describe(\"Applicant/company name\"),\n productCode: z.string().optional().describe(\"FDA product code\"),\n clearanceType: z.string().optional().describe(\"Clearance type (e.g., 'Traditional', 'Special')\"),\n decisionDateFrom: z.string().optional().describe(\"Decision date range start (YYYY-MM-DD)\"),\n decisionDateTo: z.string().optional().describe(\"Decision date range end (YYYY-MM-DD)\"),\n ...paginationSchema,\n})\n\nexport const searchDeviceClassificationsSchema = z.object({\n deviceName: z.string().optional().describe(\"Device name to search\"),\n deviceClass: z.enum([\"1\", \"2\", \"3\"]).optional().describe(\"Device class (1=lowest risk, 3=highest)\"),\n medicalSpecialty: z.string().optional().describe(\"Medical specialty code\"),\n productCode: z.string().optional().describe(\"FDA product code\"),\n regulationNumber: z.string().optional().describe(\"CFR regulation number\"),\n ...paginationSchema,\n})\n\nexport const searchDeviceAdverseEventsSchema = z.object({\n reportNumber: z\n .string()\n .optional()\n .describe(\"MDR report number for retrieving a specific device adverse event report\"),\n deviceName: z.string().optional().describe(\"Device generic name\"),\n brandName: z.string().optional().describe(\"Device brand name\"),\n manufacturerName: z.string().optional().describe(\"Device manufacturer name\"),\n eventType: z.enum([\"Injury\", \"Malfunction\", \"Death\", \"Other\"]).optional().describe(\"Type of adverse event\"),\n ...dateRangeSchema,\n ...paginationSchema,\n})\n\nexport const searchDeviceEnforcementSchema = z.object({\n recallingFirm: z.string().optional().describe(\"Name of the recalling company\"),\n productDescription: z.string().optional().describe(\"Product description keywords\"),\n classification: z\n .enum([\"Class I\", \"Class II\", \"Class III\"])\n .optional()\n .describe(\"Recall classification (I=most serious)\"),\n status: z.enum([\"Ongoing\", \"Completed\", \"Terminated\", \"Pending\"]).optional().describe(\"Recall status\"),\n ...dateRangeSchema,\n ...paginationSchema,\n})\n\n// Bulk Data Tool Schemas (Orange Book & Purple Book)\n\nexport const searchOrangeBookSchema = z.object({\n drugName: z.string().optional().describe(\"Drug trade name or ingredient to search\"),\n applicant: z.string().optional().describe(\"Applicant/company name\"),\n applNo: z.string().optional().describe(\"FDA application number\"),\n teCode: z.string().optional().describe(\"Therapeutic equivalence code (e.g., 'AB', 'BX')\"),\n ...paginationSchema,\n})\n\nexport const searchOrangeBookPatentsSchema = z.object({\n drugName: z.string().optional().describe(\"Drug trade name or ingredient to search\"),\n applNo: z.string().optional().describe(\"FDA application number\"),\n patentNo: z.string().optional().describe(\"Patent number\"),\n ...paginationSchema,\n})\n\nexport const searchPurpleBookSchema = z.object({\n productName: z.string().optional().describe(\"Product proprietary or proper name to search\"),\n applicant: z.string().optional().describe(\"Applicant/company name\"),\n blaNumber: z.string().optional().describe(\"Biologics License Application (BLA) number\"),\n licenseType: z\n .enum([\"351(a)\", \"351(k)\"])\n .optional()\n .describe(\"License type: 351(a) for originator, 351(k) for biosimilar\"),\n biosimilar: z.boolean().optional().describe(\"Filter for biosimilar products\"),\n interchangeable: z.boolean().optional().describe(\"Filter for interchangeable products\"),\n ...paginationSchema,\n})\n\nexport const searchDrugPatentExpirySchema = z.object({\n drugName: z.string().optional().describe(\"Drug trade name or ingredient to search\"),\n applNo: z.string().optional().describe(\"FDA application number\"),\n includeExclusivity: z.boolean().optional().default(true).describe(\"Include exclusivity data (default: true)\"),\n includePurpleBook: z\n .boolean()\n .optional()\n .default(false)\n .describe(\"Include Purple Book biologics data (default: false)\"),\n ...paginationSchema,\n})\n\n// Export all schemas for use in tool registration\nexport const toolSchemas = {\n searchDrugAdverseEvents: searchDrugAdverseEventsSchema,\n searchDrugLabels: searchDrugLabelsSchema,\n searchDrugNDC: searchDrugNDCSchema,\n searchDrugEnforcement: searchDrugEnforcementSchema,\n searchDrugsFDA: searchDrugsFDASchema,\n searchDrugShortages: searchDrugShortagesSchema,\n searchDevice510K: searchDevice510KSchema,\n searchDeviceClassifications: searchDeviceClassificationsSchema,\n searchDeviceAdverseEvents: searchDeviceAdverseEventsSchema,\n searchDeviceEnforcement: searchDeviceEnforcementSchema,\n searchOrangeBook: searchOrangeBookSchema,\n searchOrangeBookPatents: searchOrangeBookPatentsSchema,\n searchPurpleBook: searchPurpleBookSchema,\n searchDrugPatentExpiry: searchDrugPatentExpirySchema,\n}\n\n// Export inferred types\nexport type SearchDrugAdverseEventsParams = z.infer<typeof searchDrugAdverseEventsSchema>\nexport type SearchDrugLabelsParams = z.infer<typeof searchDrugLabelsSchema>\nexport type SearchDrugNDCParams = z.infer<typeof searchDrugNDCSchema>\nexport type SearchDrugEnforcementParams = z.infer<typeof searchDrugEnforcementSchema>\nexport type SearchDrugsFDAParams = z.infer<typeof searchDrugsFDASchema>\nexport type SearchDrugShortagesParams = z.infer<typeof searchDrugShortagesSchema>\nexport type SearchDevice510KParams = z.infer<typeof searchDevice510KSchema>\nexport type SearchDeviceClassificationsParams = z.infer<typeof searchDeviceClassificationsSchema>\nexport type SearchDeviceAdverseEventsParams = z.infer<typeof searchDeviceAdverseEventsSchema>\nexport type SearchDeviceEnforcementParams = z.infer<typeof searchDeviceEnforcementSchema>\nexport type SearchOrangeBookParams = z.infer<typeof searchOrangeBookSchema>\nexport type SearchOrangeBookPatentsParams = z.infer<typeof searchOrangeBookPatentsSchema>\nexport type SearchPurpleBookParams = z.infer<typeof searchPurpleBookSchema>\nexport type SearchDrugPatentExpiryParams = z.infer<typeof searchDrugPatentExpirySchema>\nexport type LabelSection = (typeof labelSections)[number]\n"],"mappings":"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"}
@@ -1,9 +1,8 @@
1
- //#region src/types/fda.d.ts
2
1
  /**
3
2
  * FDA API Types
4
3
  * Type definitions for FDA OpenAPI responses
5
4
  */
6
- type FDAResponse<T> = {
5
+ export type FDAResponse<T> = {
7
6
  meta?: {
8
7
  disclaimer?: string;
9
8
  terms?: string;
@@ -21,7 +20,7 @@ type FDAResponse<T> = {
21
20
  message: string;
22
21
  };
23
22
  };
24
- type OpenFDAFields = {
23
+ export type OpenFDAFields = {
25
24
  application_number?: string[];
26
25
  brand_name?: string[];
27
26
  generic_name?: string[];
@@ -40,7 +39,7 @@ type OpenFDAFields = {
40
39
  nui?: string[];
41
40
  unii?: string[];
42
41
  };
43
- type DrugAdverseEvent = {
42
+ export type DrugAdverseEvent = {
44
43
  safetyreportid?: string;
45
44
  safetyreportversion?: string;
46
45
  receivedate?: string;
@@ -119,7 +118,7 @@ type DrugAdverseEvent = {
119
118
  }>;
120
119
  };
121
120
  };
122
- type DrugLabel = {
121
+ export type DrugLabel = {
123
122
  effective_time?: string;
124
123
  id?: string;
125
124
  set_id?: string;
@@ -157,7 +156,7 @@ type DrugLabel = {
157
156
  clinical_studies?: string[];
158
157
  openfda?: OpenFDAFields;
159
158
  };
160
- type DrugNDC = {
159
+ export type DrugNDC = {
161
160
  product_id?: string;
162
161
  product_ndc?: string;
163
162
  spl_id?: string;
@@ -188,7 +187,7 @@ type DrugNDC = {
188
187
  pharm_class?: string[];
189
188
  openfda?: OpenFDAFields;
190
189
  };
191
- type DrugEnforcement = {
190
+ export type DrugEnforcement = {
192
191
  classification?: string;
193
192
  center_classification_date?: string;
194
193
  report_date?: string;
@@ -215,7 +214,7 @@ type DrugEnforcement = {
215
214
  product_quantity?: string;
216
215
  openfda?: OpenFDAFields;
217
216
  };
218
- type DrugShortage = {
217
+ export type DrugShortage = {
219
218
  generic_name?: string;
220
219
  proprietary_name?: string;
221
220
  status?: string;
@@ -224,7 +223,7 @@ type DrugShortage = {
224
223
  resolved_shortage_date?: string;
225
224
  initial_posting_date?: string;
226
225
  };
227
- type DrugsFDA = {
226
+ export type DrugsFDA = {
228
227
  application_number?: string;
229
228
  sponsor_name?: string;
230
229
  products?: Array<{
@@ -257,7 +256,7 @@ type DrugsFDA = {
257
256
  }>;
258
257
  openfda?: OpenFDAFields;
259
258
  };
260
- type Device510K = {
259
+ export type Device510K = {
261
260
  k_number?: string;
262
261
  applicant?: string;
263
262
  contact?: string;
@@ -286,7 +285,7 @@ type Device510K = {
286
285
  fei_number?: string[];
287
286
  };
288
287
  };
289
- type DeviceClassification = {
288
+ export type DeviceClassification = {
290
289
  device_name?: string;
291
290
  device_class?: string;
292
291
  definition?: string;
@@ -309,7 +308,7 @@ type DeviceClassification = {
309
308
  registration_number?: string[];
310
309
  };
311
310
  };
312
- type DeviceAdverseEvent = {
311
+ export type DeviceAdverseEvent = {
313
312
  adverse_event_flag?: string;
314
313
  product_problem_flag?: string;
315
314
  date_of_event?: string;
@@ -367,7 +366,7 @@ type DeviceAdverseEvent = {
367
366
  text?: string;
368
367
  }>;
369
368
  };
370
- type DeviceEnforcement = {
369
+ export type DeviceEnforcement = {
371
370
  classification?: string;
372
371
  center_classification_date?: string;
373
372
  report_date?: string;
@@ -400,13 +399,13 @@ type DeviceEnforcement = {
400
399
  registration_number?: string[];
401
400
  };
402
401
  };
403
- type SearchParams = {
402
+ export type SearchParams = {
404
403
  search?: string;
405
404
  count?: string;
406
405
  limit?: number;
407
406
  skip?: number;
408
407
  };
409
- type FDAToolResponse<T> = {
408
+ export type FDAToolResponse<T> = {
410
409
  success: boolean;
411
410
  data?: T;
412
411
  error?: string;
@@ -417,7 +416,61 @@ type FDAToolResponse<T> = {
417
416
  authenticated: boolean;
418
417
  rateLimit: string;
419
418
  };
419
+ dataSource?: {
420
+ name: string;
421
+ lastUpdated: string;
422
+ url: string;
423
+ };
424
+ };
425
+ export type OrangeBookProduct = {
426
+ ingredient: string;
427
+ dfRoute: string;
428
+ tradeName: string;
429
+ applicant: string;
430
+ applType: string;
431
+ applNo: string;
432
+ productNo: string;
433
+ teCode: string;
434
+ approvalDate: string;
435
+ rld: string;
436
+ rs: string;
437
+ type: string;
438
+ applicantFullName: string;
439
+ };
440
+ export type OrangeBookPatent = {
441
+ applType: string;
442
+ applNo: string;
443
+ productNo: string;
444
+ patentNo: string;
445
+ patentExpireDate: string;
446
+ drugSubstanceFlag: string;
447
+ drugProductFlag: string;
448
+ patentUseCode: string;
449
+ delistFlag: string;
450
+ submissionDate: string;
451
+ };
452
+ export type OrangeBookExclusivity = {
453
+ applType: string;
454
+ applNo: string;
455
+ productNo: string;
456
+ exclusivityCode: string;
457
+ exclusivityDate: string;
420
458
  };
421
- //#endregion
422
- export { Device510K, DeviceAdverseEvent, DeviceClassification, DeviceEnforcement, DrugAdverseEvent, DrugEnforcement, DrugLabel, DrugNDC, DrugShortage, DrugsFDA, FDAResponse, FDAToolResponse, OpenFDAFields, SearchParams };
423
- //# sourceMappingURL=fda.d.ts.map
459
+ export type PurpleBookEntry = {
460
+ blaNumber: string;
461
+ proprietaryName: string;
462
+ properName: string;
463
+ applicant: string;
464
+ blaType: string;
465
+ strength: string;
466
+ dosageForm: string;
467
+ route: string;
468
+ productPresentation: string;
469
+ status: string;
470
+ licensingStatus: string;
471
+ referenceProductBla: string;
472
+ referenceProductProprietaryName: string;
473
+ biosimilar: boolean;
474
+ interchangeable: boolean;
475
+ approvalDate: string;
476
+ };
@@ -1,38 +1,41 @@
1
+ /**
2
+ * FDA API Client
3
+ * HTTP client for interacting with FDA OpenAPI endpoints
4
+ */
1
5
  import { Device510K, DeviceAdverseEvent, DeviceClassification, DeviceEnforcement, DrugAdverseEvent, DrugEnforcement, DrugLabel, DrugNDC, DrugShortage, DrugsFDA, FDAResponse, SearchParams } from "../types/fda.js";
2
-
3
- //#region src/utils/api-client.d.ts
4
-
5
- declare class FDAAPIClient {
6
- private apiKey;
7
- private baseUrl;
6
+ import { Either } from "functype";
7
+ type RateLimitInfo = {
8
+ authenticated: boolean;
9
+ rateLimit: string;
10
+ };
11
+ export declare class FDAAPIClient {
12
+ private readonly apiKey;
13
+ private readonly baseUrl;
8
14
  constructor();
9
15
  /**
10
16
  * Build URL search parameters for the FDA API
11
17
  */
12
18
  private buildSearchParams;
19
+ private redact;
13
20
  /**
14
21
  * Make a request to an FDA API endpoint
15
22
  */
16
23
  private request;
24
+ private readResponse;
17
25
  /**
18
26
  * Get rate limit info based on authentication status
19
27
  */
20
- getRateLimitInfo(): {
21
- authenticated: boolean;
22
- rateLimit: string;
23
- };
24
- searchDrugAdverseEvents(params: SearchParams): Promise<FDAResponse<DrugAdverseEvent>>;
25
- searchDrugLabels(params: SearchParams): Promise<FDAResponse<DrugLabel>>;
26
- searchDrugNDC(params: SearchParams): Promise<FDAResponse<DrugNDC>>;
27
- searchDrugEnforcement(params: SearchParams): Promise<FDAResponse<DrugEnforcement>>;
28
- searchDrugsFDA(params: SearchParams): Promise<FDAResponse<DrugsFDA>>;
29
- searchDrugShortages(params: SearchParams): Promise<FDAResponse<DrugShortage>>;
30
- searchDevice510K(params: SearchParams): Promise<FDAResponse<Device510K>>;
31
- searchDeviceClassifications(params: SearchParams): Promise<FDAResponse<DeviceClassification>>;
32
- searchDeviceAdverseEvents(params: SearchParams): Promise<FDAResponse<DeviceAdverseEvent>>;
33
- searchDeviceEnforcement(params: SearchParams): Promise<FDAResponse<DeviceEnforcement>>;
28
+ getRateLimitInfo(): RateLimitInfo;
29
+ searchDrugAdverseEvents(params: SearchParams): Promise<Either<Error, FDAResponse<DrugAdverseEvent>>>;
30
+ searchDrugLabels(params: SearchParams): Promise<Either<Error, FDAResponse<DrugLabel>>>;
31
+ searchDrugNDC(params: SearchParams): Promise<Either<Error, FDAResponse<DrugNDC>>>;
32
+ searchDrugEnforcement(params: SearchParams): Promise<Either<Error, FDAResponse<DrugEnforcement>>>;
33
+ searchDrugsFDA(params: SearchParams): Promise<Either<Error, FDAResponse<DrugsFDA>>>;
34
+ searchDrugShortages(params: SearchParams): Promise<Either<Error, FDAResponse<DrugShortage>>>;
35
+ searchDevice510K(params: SearchParams): Promise<Either<Error, FDAResponse<Device510K>>>;
36
+ searchDeviceClassifications(params: SearchParams): Promise<Either<Error, FDAResponse<DeviceClassification>>>;
37
+ searchDeviceAdverseEvents(params: SearchParams): Promise<Either<Error, FDAResponse<DeviceAdverseEvent>>>;
38
+ searchDeviceEnforcement(params: SearchParams): Promise<Either<Error, FDAResponse<DeviceEnforcement>>>;
34
39
  }
35
- declare const fdaAPIClient: FDAAPIClient;
36
- //#endregion
37
- export { FDAAPIClient, fdaAPIClient };
38
- //# sourceMappingURL=api-client.d.ts.map
40
+ export declare const fdaAPIClient: FDAAPIClient;
41
+ export {};
@@ -1 +1,2 @@
1
- import"./logger.js";import{n as e,t}from"../api-client-BGnUXMwX.js";export{t as FDAAPIClient,e as fdaAPIClient};
1
+ import{toError as e}from"./errors.js";import{isRecord as t}from"./fields.js";import{loggers as n}from"./logger.js";import{t as r}from"../defineProperty-D36W847_.js";import{Left as i,Option as a,Right as o,Try as s}from"functype";function c(e,t,n){if(e===429)return/* @__PURE__ */ Error(`Rate limit exceeded. Consider using an API key for higher limits.`);if(e===400){let e=a(n).flatMap(e=>a(e.error)).flatMap(e=>a(e.message)).orElse(`Bad request parameters`);return/* @__PURE__ */ Error(`Invalid request: ${e}`)}return/* @__PURE__ */ Error(`FDA API error: ${e} ${t}`)}var l=class{constructor(){r(this,`apiKey`,void 0),r(this,`baseUrl`,void 0),this.apiKey=a(process.env.OPENFDA_API_KEY).filter(e=>e!==``),this.baseUrl=`https://api.fda.gov`}buildSearchParams(e){let t=new URLSearchParams;e.search&&t.set(`search`,e.search),e.count&&t.set(`count`,e.count);let n=Math.min(e.limit??10,100);return t.set(`limit`,String(n)),e.skip!==void 0&&e.skip>0&&t.set(`skip`,String(e.skip)),this.apiKey.toArray().forEach(e=>t.set(`api_key`,e)),t}redact(e){return this.apiKey.fold(()=>e,t=>e.replace(t,`[REDACTED]`))}async request(t,r){let i=this.buildSearchParams(r),a=`${this.baseUrl}${t}?${i.toString()}`;return n.api(`Request: ${this.redact(a)}`),(await s.fromPromise(fetch(a))).toEither(e).flatMapAsync(e=>this.readResponse(e))}async readResponse(r){if(r.status===404)return await s.fromPromise(r.body?.cancel()??Promise.resolve()),o({results:[]});if(!r.ok){let e=(await s.fromPromise(r.json())).orElse({});return i(c(r.status,r.statusText,e))}return(await s.fromPromise(r.json())).toEither(e).flatMap(e=>t(e)?o(e):i(/* @__PURE__ */ Error(`Unexpected response body from FDA API`))).map(e=>(n.api(`Response: ${e.meta?.results?.total??0} total results`),e))}getRateLimitInfo(){return this.apiKey.fold(()=>({authenticated:!1,rateLimit:`40/minute, 1,000/hour`}),()=>({authenticated:!0,rateLimit:`240/minute, 120,000/hour`}))}async searchDrugAdverseEvents(e){return this.request(`/drug/event.json`,e)}async searchDrugLabels(e){return this.request(`/drug/label.json`,e)}async searchDrugNDC(e){return this.request(`/drug/ndc.json`,e)}async searchDrugEnforcement(e){return this.request(`/drug/enforcement.json`,e)}async searchDrugsFDA(e){return this.request(`/drug/drugsfda.json`,e)}async searchDrugShortages(e){return this.request(`/drug/shortages.json`,e)}async searchDevice510K(e){return this.request(`/device/510k.json`,e)}async searchDeviceClassifications(e){return this.request(`/device/classification.json`,e)}async searchDeviceAdverseEvents(e){return this.request(`/device/event.json`,e)}async searchDeviceEnforcement(e){return this.request(`/device/enforcement.json`,e)}};const u=new l;export{l as FDAAPIClient,u as fdaAPIClient};
2
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","names":["_defineProperty","errorForStatus","status","message","Option","body","b","statusText","FDAAPIClient","key","urlParams","params","limit","url","endpoint","loggers","Try","toError","response","Right","Left","isRecord","data","fdaAPIClient"],"sources":["../../src/utils/api-client.ts"],"sourcesContent":["/**\n * FDA API Client\n * HTTP client for interacting with FDA OpenAPI endpoints\n */\n\nimport { type Either, Left, Option, Right, Try } from \"functype\"\n\nimport type {\n Device510K,\n DeviceAdverseEvent,\n DeviceClassification,\n DeviceEnforcement,\n DrugAdverseEvent,\n DrugEnforcement,\n DrugLabel,\n DrugNDC,\n DrugsFDA,\n DrugShortage,\n FDAResponse,\n SearchParams,\n} from \"../types/fda.js\"\nimport { toError } from \"./errors.js\"\nimport { isRecord } from \"./fields.js\"\nimport { loggers } from \"./logger.js\"\n\nconst FDA_BASE_URL = \"https://api.fda.gov\"\nconst DEFAULT_LIMIT = 10\nconst MAX_LIMIT = 100\n\ntype FDAEndpoint =\n | \"/drug/event.json\"\n | \"/drug/label.json\"\n | \"/drug/ndc.json\"\n | \"/drug/enforcement.json\"\n | \"/drug/drugsfda.json\"\n | \"/drug/shortages.json\"\n | \"/device/510k.json\"\n | \"/device/classification.json\"\n | \"/device/event.json\"\n | \"/device/enforcement.json\"\n\ntype RateLimitInfo = { authenticated: boolean; rateLimit: string }\n\ntype ErrorBody = { error?: { message?: string } }\n\n/** The error for a non-OK status other than 404, which means \"no results\" */\nfunction errorForStatus(status: number, statusText: string, body: unknown): Error {\n if (status === 429) return new Error(\"Rate limit exceeded. Consider using an API key for higher limits.\")\n if (status === 400) {\n const message = Option(body as ErrorBody | null)\n .flatMap((b) => Option(b.error))\n .flatMap((e) => Option(e.message))\n .orElse(\"Bad request parameters\")\n return new Error(`Invalid request: ${message}`)\n }\n return new Error(`FDA API error: ${status} ${statusText}`)\n}\n\nexport class FDAAPIClient {\n private readonly apiKey: Option<string>\n private readonly baseUrl: string\n\n constructor() {\n this.apiKey = Option(process.env.OPENFDA_API_KEY).filter((key) => key !== \"\")\n this.baseUrl = FDA_BASE_URL\n }\n\n /**\n * Build URL search parameters for the FDA API\n */\n private buildSearchParams(params: SearchParams): URLSearchParams {\n const urlParams = new URLSearchParams()\n\n if (params.search) {\n urlParams.set(\"search\", params.search)\n }\n\n if (params.count) {\n urlParams.set(\"count\", params.count)\n }\n\n const limit = Math.min(params.limit ?? DEFAULT_LIMIT, MAX_LIMIT)\n urlParams.set(\"limit\", String(limit))\n\n if (params.skip !== undefined && params.skip > 0) {\n urlParams.set(\"skip\", String(params.skip))\n }\n\n this.apiKey.toArray().forEach((key) => urlParams.set(\"api_key\", key))\n\n return urlParams\n }\n\n private redact(url: string): string {\n return this.apiKey.fold(\n () => url,\n (key) => url.replace(key, \"[REDACTED]\"),\n )\n }\n\n /**\n * Make a request to an FDA API endpoint\n */\n private async request<T>(endpoint: FDAEndpoint, params: SearchParams): Promise<Either<Error, FDAResponse<T>>> {\n const urlParams = this.buildSearchParams(params)\n const url = `${this.baseUrl}${endpoint}?${urlParams.toString()}`\n\n loggers.api(`Request: ${this.redact(url)}`)\n\n const fetched = (await Try.fromPromise(fetch(url))).toEither(toError)\n return fetched.flatMapAsync((response) => this.readResponse<T>(response))\n }\n\n private async readResponse<T>(response: Response): Promise<Either<Error, FDAResponse<T>>> {\n if (response.status === 404) {\n await Try.fromPromise(response.body?.cancel() ?? Promise.resolve()) // release the connection\n return Right({ results: [] }) // No results found\n }\n\n if (!response.ok) {\n const body = (await Try.fromPromise(response.json() as Promise<unknown>)).orElse({})\n return Left(errorForStatus(response.status, response.statusText, body))\n }\n\n const parsed = (await Try.fromPromise(response.json() as Promise<unknown>))\n .toEither(toError)\n .flatMap((body) =>\n isRecord(body)\n ? Right<Error, FDAResponse<T>>(body as FDAResponse<T>)\n : Left<Error, FDAResponse<T>>(new Error(\"Unexpected response body from FDA API\")),\n )\n return parsed.map((data) => {\n loggers.api(`Response: ${data.meta?.results?.total ?? 0} total results`)\n return data\n })\n }\n\n /**\n * Get rate limit info based on authentication status\n */\n getRateLimitInfo(): RateLimitInfo {\n return this.apiKey.fold(\n (): RateLimitInfo => ({ authenticated: false, rateLimit: \"40/minute, 1,000/hour\" }),\n (): RateLimitInfo => ({ authenticated: true, rateLimit: \"240/minute, 120,000/hour\" }),\n )\n }\n\n // Drug endpoints\n\n async searchDrugAdverseEvents(params: SearchParams): Promise<Either<Error, FDAResponse<DrugAdverseEvent>>> {\n return this.request<DrugAdverseEvent>(\"/drug/event.json\", params)\n }\n\n async searchDrugLabels(params: SearchParams): Promise<Either<Error, FDAResponse<DrugLabel>>> {\n return this.request<DrugLabel>(\"/drug/label.json\", params)\n }\n\n async searchDrugNDC(params: SearchParams): Promise<Either<Error, FDAResponse<DrugNDC>>> {\n return this.request<DrugNDC>(\"/drug/ndc.json\", params)\n }\n\n async searchDrugEnforcement(params: SearchParams): Promise<Either<Error, FDAResponse<DrugEnforcement>>> {\n return this.request<DrugEnforcement>(\"/drug/enforcement.json\", params)\n }\n\n async searchDrugsFDA(params: SearchParams): Promise<Either<Error, FDAResponse<DrugsFDA>>> {\n return this.request<DrugsFDA>(\"/drug/drugsfda.json\", params)\n }\n\n async searchDrugShortages(params: SearchParams): Promise<Either<Error, FDAResponse<DrugShortage>>> {\n return this.request<DrugShortage>(\"/drug/shortages.json\", params)\n }\n\n // Device endpoints\n\n async searchDevice510K(params: SearchParams): Promise<Either<Error, FDAResponse<Device510K>>> {\n return this.request<Device510K>(\"/device/510k.json\", params)\n }\n\n async searchDeviceClassifications(params: SearchParams): Promise<Either<Error, FDAResponse<DeviceClassification>>> {\n return this.request<DeviceClassification>(\"/device/classification.json\", params)\n }\n\n async searchDeviceAdverseEvents(params: SearchParams): Promise<Either<Error, FDAResponse<DeviceAdverseEvent>>> {\n return this.request<DeviceAdverseEvent>(\"/device/event.json\", params)\n }\n\n async searchDeviceEnforcement(params: SearchParams): Promise<Either<Error, FDAResponse<DeviceEnforcement>>> {\n return this.request<DeviceEnforcement>(\"/device/enforcement.json\", params)\n }\n}\n\n// Singleton instance\nexport const fdaAPIClient = new FDAAPIClient()\n"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Bulk Data Client
3
+ * Downloads and parses FDA Orange Book and Purple Book data files
4
+ */
5
+ import { OrangeBookExclusivity, OrangeBookPatent, OrangeBookProduct, PurpleBookEntry } from "../types/fda.js";
6
+ import { Either } from "functype";
7
+ type OrangeBookData = {
8
+ products: OrangeBookProduct[];
9
+ patents: OrangeBookPatent[];
10
+ exclusivities: OrangeBookExclusivity[];
11
+ };
12
+ /** A parsed row: column name to cell text */
13
+ export type Row = Readonly<Record<string, string>>;
14
+ export declare class BulkDataClient {
15
+ private orangeBookCache;
16
+ private purpleBookCache;
17
+ downloadBinary(url: string): Promise<Either<Error, Uint8Array>>;
18
+ parseTildeDelimited(text: string): Row[];
19
+ parseCSV(text: string): Row[];
20
+ private parseRows;
21
+ private parseCSVLine;
22
+ private parseOrangeBookFiles;
23
+ getOrangeBookData(): Promise<Either<Error, OrangeBookData>>;
24
+ private loadOrangeBook;
25
+ getPurpleBookData(): Promise<Either<Error, PurpleBookEntry[]>>;
26
+ private loadPurpleBook;
27
+ clearCache(): void;
28
+ }
29
+ export declare const bulkDataClient: BulkDataClient;
30
+ export {};
@@ -0,0 +1,3 @@
1
+ import{toError as e}from"./errors.js";import{isRecord as t,textField as n}from"./fields.js";import{loggers as r}from"./logger.js";import{t as i}from"../defineProperty-D36W847_.js";import{Left as a,None as o,Option as s,Right as c,Some as l,Try as u}from"functype";import{unzipSync as d}from"fflate";function f(e){return e.filter(e=>Date.now()-e.fetchedAt<e.ttlMs).map(e=>e.data)}function p(e,t){return l({data:e,fetchedAt:Date.now(),ttlMs:t})}async function m(t,n){return(await u.fromPromise(fetch(t))).toEither(e).flatMap(e=>e.ok?c(e):a(Error(n(e))))}function h(e){return{ingredient:n(e,`Ingredient`,`ingredient`),dfRoute:n(e,`DF;Route`,`df;route`,`DF_Route`),tradeName:n(e,`Trade_Name`,`trade_name`),applicant:n(e,`Applicant`,`applicant`),applType:n(e,`Appl_Type`,`appl_type`),applNo:n(e,`Appl_No`,`appl_no`),productNo:n(e,`Product_No`,`product_no`),teCode:n(e,`TE_Code`,`te_code`),approvalDate:n(e,`Approval_Date`,`approval_date`),rld:n(e,`RLD`,`rld`),rs:n(e,`RS`,`rs`),type:n(e,`Type`,`type`),applicantFullName:n(e,`Applicant_Full_Name`,`applicant_full_name`)}}function g(e){return{applType:n(e,`Appl_Type`,`appl_type`),applNo:n(e,`Appl_No`,`appl_no`),productNo:n(e,`Product_No`,`product_no`),patentNo:n(e,`Patent_No`,`patent_no`),patentExpireDate:n(e,`Patent_Expire_Date_Text`,`patent_expire_date_text`),drugSubstanceFlag:n(e,`Drug_Substance_Flag`,`drug_substance_flag`),drugProductFlag:n(e,`Drug_Product_Flag`,`drug_product_flag`),patentUseCode:n(e,`Patent_Use_Code`,`patent_use_code`),delistFlag:n(e,`Delist_Flag`,`delist_flag`),submissionDate:n(e,`Submission_Date`,`submission_date`)}}function _(e){return{applType:n(e,`Appl_Type`,`appl_type`),applNo:n(e,`Appl_No`,`appl_no`),productNo:n(e,`Product_No`,`product_no`),exclusivityCode:n(e,`Exclusivity_Code`,`exclusivity_code`),exclusivityDate:n(e,`Exclusivity_Date`,`exclusivity_date`)}}function v(e){let t=n(e,`license_type`);return{blaNumber:n(e,`bla_number`),proprietaryName:n(e,`proprietary_name`),properName:n(e,`proper_name`),applicant:n(e,`applicant`),blaType:t,strength:n(e,`strength`),dosageForm:n(e,`dosage_form`),route:n(e,`route_of_administration`),productPresentation:n(e,`product_presentation`),status:n(e,`status`),licensingStatus:n(e,`licensure`),referenceProductBla:n(e,`ref_product_proper_name`).trim(),referenceProductProprietaryName:n(e,`ref_prod_proprietary_name`),biosimilar:t===`351(k)`,interchangeable:n(e,`interchangeable_approval_date`).trim()!==``,approvalDate:n(e,`approval_date`,`date_of_first_licensure`)}}var y=class{constructor(){i(this,`orangeBookCache`,o()),i(this,`purpleBookCache`,o())}async downloadBinary(t){return r.bulk(`Downloading: ${t}`),(await m(t,e=>`Failed to download ${t}: ${e.status} ${e.statusText}`)).flatMapAsync(async t=>(await u.fromPromise(t.arrayBuffer())).toEither(e).map(e=>new Uint8Array(e)))}parseTildeDelimited(e){return this.parseRows(e,e=>e.split(`~`))}parseCSV(e){return this.parseRows(e,e=>this.parseCSVLine(e))}parseRows(e,t){let n=e.split(`
2
+ `).filter(e=>e.trim()!==``);if(n.length<2)return[];let r=t(n[0]).map(e=>e.trim());return n.slice(1).map(e=>{let n=t(e);return Object.fromEntries(r.map((e,t)=>[e,(n[t]??``).trim()]))})}parseCSVLine(e){let t=Array.from(e).reduce((t,n,r)=>t.inQuotes?n===`"`?r+1<e.length&&e[r+1]===`"`?t:r>0&&e[r-1]===`"`?{...t,current:`${t.current}"`}:{...t,inQuotes:!1}:{...t,current:t.current+n}:n===`"`?{...t,inQuotes:!0}:n===`,`?{fields:[...t.fields,t.current.trim()],current:``,inQuotes:!1}:{...t,current:t.current+n},{fields:[],current:``,inQuotes:!1});return[...t.fields,t.current.trim()]}parseOrangeBookFiles(e){let t=new TextDecoder(`utf-8`),n=Object.entries(e).map(([e,n])=>({name:e.toLowerCase(),text:t.decode(n)})),i=e=>s(n.find(t=>t.name.includes(e))).map(e=>this.parseTildeDelimited(e.text)).orElse([]),a=i(`products`).map(h);r.bulk(`Parsed ${a.length} Orange Book products`);let o=i(`patent`).map(g);r.bulk(`Parsed ${o.length} Orange Book patents`);let c=i(`exclusivity`).map(_);return r.bulk(`Parsed ${c.length} Orange Book exclusivities`),{products:a,patents:o,exclusivities:c}}async getOrangeBookData(){return f(this.orangeBookCache).fold(()=>this.loadOrangeBook(),e=>(r.bulk(`Using cached Orange Book data`),Promise.resolve(c(e))))}async loadOrangeBook(){return r.bulk(`Downloading Orange Book ZIP...`),(await this.downloadBinary(`https://www.fda.gov/media/76860/download`)).flatMap(t=>u(()=>d(t)).toEither(e)).map(e=>this.parseOrangeBookFiles(e)).map(e=>(this.orangeBookCache=p(e,864e5),e))}async getPurpleBookData(){return f(this.purpleBookCache).fold(()=>this.loadPurpleBook(),e=>(r.bulk(`Using cached Purple Book data`),Promise.resolve(c(e))))}async loadPurpleBook(){return r.bulk(`Fetching Purple Book data from API...`),(await(await m(`https://purplebooksearch.fda.gov/api/v1/fdaproducts?token=18dd75c5-23c2-4318-970b-ca97c34470cb`,e=>`Failed to fetch Purple Book data: ${e.status} ${e.statusText}`)).flatMapAsync(async n=>(await u.fromPromise(n.json())).toEither(e).flatMap(e=>Array.isArray(e)&&e.every(t)?c(e):a(/* @__PURE__ */ Error(`Unexpected response body from Purple Book API`))))).map(e=>(r.bulk(`Received ${e.length} Purple Book entries from API`),e.map(v))).map(e=>(this.purpleBookCache=p(e,6048e5),e))}clearCache(){this.orangeBookCache=o(),this.purpleBookCache=o()}};const b=new y;export{y as BulkDataClient,b as bulkDataClient};
3
+ //# sourceMappingURL=bulk-data-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-data-client.js","names":["_defineProperty","fresh","cache","entry","cacheEntry","Some","fetchOk","Try","url","toError","response","Right","Left","describeFailure","toOrangeBookProduct","textField","r","toOrangeBookPatent","toOrangeBookExclusivity","toPurpleBookEntry","licenseType","BulkDataClient","None","loggers","buffer","text","line","lines","headers","splitLine","h","values","j","final","state","char","i","decoder","fileEntries","files","filename","content","rowsOf","Option","f","namePart","products","patents","exclusivities","data","unzipSync","zip","body","isRecord","raw","entries","bulkDataClient"],"sources":["../../src/utils/bulk-data-client.ts"],"sourcesContent":["/**\n * Bulk Data Client\n * Downloads and parses FDA Orange Book and Purple Book data files\n */\n\nimport { unzipSync } from \"fflate\"\nimport { type Either, Left, None, Option, Right, Some, Try } from \"functype\"\n\nimport type { OrangeBookExclusivity, OrangeBookPatent, OrangeBookProduct, PurpleBookEntry } from \"../types/fda.js\"\nimport { toError } from \"./errors.js\"\nimport { isRecord, textField } from \"./fields.js\"\nimport { loggers } from \"./logger.js\"\n\nconst ORANGE_BOOK_ZIP_URL = \"https://www.fda.gov/media/76860/download\"\nconst PURPLE_BOOK_API_URL = \"https://purplebooksearch.fda.gov/api/v1/fdaproducts\"\nconst PURPLE_BOOK_API_TOKEN = \"18dd75c5-23c2-4318-970b-ca97c34470cb\"\n\nconst ORANGE_BOOK_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours\nconst PURPLE_BOOK_TTL_MS = 7 * 24 * 60 * 60 * 1000 // 7 days\n\ntype CacheEntry<T> = {\n data: T\n fetchedAt: number\n ttlMs: number\n}\n\ntype OrangeBookData = {\n products: OrangeBookProduct[]\n patents: OrangeBookPatent[]\n exclusivities: OrangeBookExclusivity[]\n}\n\n/** A parsed row: column name to cell text */\nexport type Row = Readonly<Record<string, string>>\n\ntype ApiRecord = Readonly<Record<string, unknown>>\n\n/** The cached data, if it has not expired */\nfunction fresh<T>(cache: Option<CacheEntry<T>>): Option<T> {\n return cache.filter((entry) => Date.now() - entry.fetchedAt < entry.ttlMs).map((entry) => entry.data)\n}\n\nfunction cacheEntry<T>(data: T, ttlMs: number): Option<CacheEntry<T>> {\n return Some({ data, fetchedAt: Date.now(), ttlMs })\n}\n\n/** Fetch a URL, turning a network failure or a non-OK status into Left */\nasync function fetchOk(url: string, describeFailure: (response: Response) => string): Promise<Either<Error, Response>> {\n const fetched = (await Try.fromPromise(fetch(url))).toEither(toError)\n return fetched.flatMap((response) => (response.ok ? Right(response) : Left(new Error(describeFailure(response)))))\n}\n\nfunction toOrangeBookProduct(r: Row): OrangeBookProduct {\n return {\n ingredient: textField(r, \"Ingredient\", \"ingredient\"),\n dfRoute: textField(r, \"DF;Route\", \"df;route\", \"DF_Route\"),\n tradeName: textField(r, \"Trade_Name\", \"trade_name\"),\n applicant: textField(r, \"Applicant\", \"applicant\"),\n applType: textField(r, \"Appl_Type\", \"appl_type\"),\n applNo: textField(r, \"Appl_No\", \"appl_no\"),\n productNo: textField(r, \"Product_No\", \"product_no\"),\n teCode: textField(r, \"TE_Code\", \"te_code\"),\n approvalDate: textField(r, \"Approval_Date\", \"approval_date\"),\n rld: textField(r, \"RLD\", \"rld\"),\n rs: textField(r, \"RS\", \"rs\"),\n type: textField(r, \"Type\", \"type\"),\n applicantFullName: textField(r, \"Applicant_Full_Name\", \"applicant_full_name\"),\n }\n}\n\nfunction toOrangeBookPatent(r: Row): OrangeBookPatent {\n return {\n applType: textField(r, \"Appl_Type\", \"appl_type\"),\n applNo: textField(r, \"Appl_No\", \"appl_no\"),\n productNo: textField(r, \"Product_No\", \"product_no\"),\n patentNo: textField(r, \"Patent_No\", \"patent_no\"),\n patentExpireDate: textField(r, \"Patent_Expire_Date_Text\", \"patent_expire_date_text\"),\n drugSubstanceFlag: textField(r, \"Drug_Substance_Flag\", \"drug_substance_flag\"),\n drugProductFlag: textField(r, \"Drug_Product_Flag\", \"drug_product_flag\"),\n patentUseCode: textField(r, \"Patent_Use_Code\", \"patent_use_code\"),\n delistFlag: textField(r, \"Delist_Flag\", \"delist_flag\"),\n submissionDate: textField(r, \"Submission_Date\", \"submission_date\"),\n }\n}\n\nfunction toOrangeBookExclusivity(r: Row): OrangeBookExclusivity {\n return {\n applType: textField(r, \"Appl_Type\", \"appl_type\"),\n applNo: textField(r, \"Appl_No\", \"appl_no\"),\n productNo: textField(r, \"Product_No\", \"product_no\"),\n exclusivityCode: textField(r, \"Exclusivity_Code\", \"exclusivity_code\"),\n exclusivityDate: textField(r, \"Exclusivity_Date\", \"exclusivity_date\"),\n }\n}\n\nfunction toPurpleBookEntry(r: ApiRecord): PurpleBookEntry {\n const licenseType = textField(r, \"license_type\")\n return {\n blaNumber: textField(r, \"bla_number\"),\n proprietaryName: textField(r, \"proprietary_name\"),\n properName: textField(r, \"proper_name\"),\n applicant: textField(r, \"applicant\"),\n blaType: licenseType,\n strength: textField(r, \"strength\"),\n dosageForm: textField(r, \"dosage_form\"),\n route: textField(r, \"route_of_administration\"),\n productPresentation: textField(r, \"product_presentation\"),\n status: textField(r, \"status\"),\n licensingStatus: textField(r, \"licensure\"),\n referenceProductBla: textField(r, \"ref_product_proper_name\").trim(),\n referenceProductProprietaryName: textField(r, \"ref_prod_proprietary_name\"),\n biosimilar: licenseType === \"351(k)\",\n interchangeable: textField(r, \"interchangeable_approval_date\").trim() !== \"\",\n approvalDate: textField(r, \"approval_date\", \"date_of_first_licensure\"),\n }\n}\n\nexport class BulkDataClient {\n private orangeBookCache: Option<CacheEntry<OrangeBookData>> = None()\n private purpleBookCache: Option<CacheEntry<PurpleBookEntry[]>> = None()\n\n async downloadBinary(url: string): Promise<Either<Error, Uint8Array>> {\n loggers.bulk(`Downloading: ${url}`)\n const response = await fetchOk(url, (r) => `Failed to download ${url}: ${r.status} ${r.statusText}`)\n return response.flatMapAsync(async (r) =>\n (await Try.fromPromise(r.arrayBuffer())).toEither(toError).map((buffer) => new Uint8Array(buffer)),\n )\n }\n\n parseTildeDelimited(text: string): Row[] {\n return this.parseRows(text, (line) => line.split(\"~\"))\n }\n\n parseCSV(text: string): Row[] {\n return this.parseRows(text, (line) => this.parseCSVLine(line))\n }\n\n private parseRows(text: string, splitLine: (line: string) => string[]): Row[] {\n const lines = text.split(\"\\n\").filter((line) => line.trim() !== \"\")\n if (lines.length < 2) return []\n\n const headers = splitLine(lines[0]).map((h) => h.trim())\n\n return lines.slice(1).map((line) => {\n const values = splitLine(line)\n return Object.fromEntries(headers.map((h, j) => [h, (values[j] ?? \"\").trim()]))\n })\n }\n\n private parseCSVLine(line: string): string[] {\n type ParseState = { readonly fields: readonly string[]; readonly current: string; readonly inQuotes: boolean }\n\n const initial: ParseState = { fields: [], current: \"\", inQuotes: false }\n\n const final = Array.from(line).reduce<ParseState>((state, char, i) => {\n if (state.inQuotes) {\n if (char === '\"') {\n if (i + 1 < line.length && line[i + 1] === '\"') {\n return state // skip — next iteration handles the escaped quote\n }\n if (i > 0 && line[i - 1] === '\"') {\n return { ...state, current: `${state.current}\"` } // second char of escaped quote\n }\n return { ...state, inQuotes: false }\n }\n return { ...state, current: state.current + char }\n }\n if (char === '\"') return { ...state, inQuotes: true }\n if (char === \",\") return { fields: [...state.fields, state.current.trim()], current: \"\", inQuotes: false }\n return { ...state, current: state.current + char }\n }, initial)\n\n return [...final.fields, final.current.trim()]\n }\n\n private parseOrangeBookFiles(files: Record<string, Uint8Array>): OrangeBookData {\n const decoder = new TextDecoder(\"utf-8\")\n\n const fileEntries = Object.entries(files).map(([filename, content]) => ({\n name: filename.toLowerCase(),\n text: decoder.decode(content),\n }))\n\n const rowsOf = (namePart: string): Row[] =>\n Option(fileEntries.find((f) => f.name.includes(namePart)))\n .map((f) => this.parseTildeDelimited(f.text))\n .orElse([])\n\n const products = rowsOf(\"products\").map(toOrangeBookProduct)\n loggers.bulk(`Parsed ${products.length} Orange Book products`)\n\n const patents = rowsOf(\"patent\").map(toOrangeBookPatent)\n loggers.bulk(`Parsed ${patents.length} Orange Book patents`)\n\n const exclusivities = rowsOf(\"exclusivity\").map(toOrangeBookExclusivity)\n loggers.bulk(`Parsed ${exclusivities.length} Orange Book exclusivities`)\n\n return { products, patents, exclusivities }\n }\n\n async getOrangeBookData(): Promise<Either<Error, OrangeBookData>> {\n return fresh(this.orangeBookCache).fold(\n () => this.loadOrangeBook(),\n (data) => {\n loggers.bulk(\"Using cached Orange Book data\")\n return Promise.resolve(Right<Error, OrangeBookData>(data))\n },\n )\n }\n\n private async loadOrangeBook(): Promise<Either<Error, OrangeBookData>> {\n loggers.bulk(\"Downloading Orange Book ZIP...\")\n const zipData = await this.downloadBinary(ORANGE_BOOK_ZIP_URL)\n\n return zipData\n .flatMap((zip) => Try(() => unzipSync(zip)).toEither(toError))\n .map((files) => this.parseOrangeBookFiles(files))\n .map((data) => {\n this.orangeBookCache = cacheEntry(data, ORANGE_BOOK_TTL_MS)\n return data\n })\n }\n\n async getPurpleBookData(): Promise<Either<Error, PurpleBookEntry[]>> {\n return fresh(this.purpleBookCache).fold(\n () => this.loadPurpleBook(),\n (data) => {\n loggers.bulk(\"Using cached Purple Book data\")\n return Promise.resolve(Right<Error, PurpleBookEntry[]>(data))\n },\n )\n }\n\n private async loadPurpleBook(): Promise<Either<Error, PurpleBookEntry[]>> {\n loggers.bulk(\"Fetching Purple Book data from API...\")\n\n const url = `${PURPLE_BOOK_API_URL}?token=${PURPLE_BOOK_API_TOKEN}`\n const response = await fetchOk(url, (r) => `Failed to fetch Purple Book data: ${r.status} ${r.statusText}`)\n const records = await response.flatMapAsync(async (r) =>\n (await Try.fromPromise(r.json() as Promise<unknown>))\n .toEither(toError)\n .flatMap((body) =>\n Array.isArray(body) && body.every(isRecord)\n ? Right<Error, ApiRecord[]>(body)\n : Left<Error, ApiRecord[]>(new Error(\"Unexpected response body from Purple Book API\")),\n ),\n )\n\n return records\n .map((raw) => {\n loggers.bulk(`Received ${raw.length} Purple Book entries from API`)\n return raw.map(toPurpleBookEntry)\n })\n .map((entries) => {\n this.purpleBookCache = cacheEntry(entries, PURPLE_BOOK_TTL_MS)\n return entries\n })\n }\n\n clearCache(): void {\n this.orangeBookCache = None()\n this.purpleBookCache = None()\n }\n}\n\n// Singleton instance\nexport const bulkDataClient = new BulkDataClient()\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Error helpers shared by the clients and handlers
3
+ */
4
+ import { FDAToolResponse } from "../types/fda.js";
5
+ /** Normalise anything thrown or rejected into an Error */
6
+ export declare const toError: (error: unknown) => Error;
7
+ /** The tool response for a failed call */
8
+ export declare const failure: <T>(error: Error) => FDAToolResponse<T>;
@@ -0,0 +1,2 @@
1
+ const e=e=>e instanceof Error?e:Error(String(e)),t=e=>({success:!1,error:e.message});export{t as failure,e as toError};
2
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","names":["toError","error","failure"],"sources":["../../src/utils/errors.ts"],"sourcesContent":["/**\n * Error helpers shared by the clients and handlers\n */\n\nimport type { FDAToolResponse } from \"../types/fda.js\"\n\n/** Normalise anything thrown or rejected into an Error */\nexport const toError = (error: unknown): Error => (error instanceof Error ? error : new Error(String(error)))\n\n/** The tool response for a failed call */\nexport const failure = <T>(error: Error): FDAToolResponse<T> => ({ success: false, error: error.message })\n"],"mappings":"AAOA,MAAaA,EAAW,GAA2BC,aAAiB,MAAQA,EAAY,MAAM,OAAOA,CAAK,CAAC,EAG9FC,EAAc,IAAsC,CAAE,QAAS,GAAO,MAAOD,EAAM,OAAQ,G"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Field access for loosely-typed rows (parsed bulk files, JSON API records)
3
+ */
4
+ /** A plain JSON object (not null, not an array) */
5
+ export declare const isRecord: (value: unknown) => value is Readonly<Record<string, unknown>>;
6
+ /**
7
+ * The value of the first key that holds text, or "" when none does.
8
+ * An empty string counts as present, as it did with `??` chains; null,
9
+ * missing and non-text values are skipped. Numbers are kept as text.
10
+ */
11
+ export declare const textField: (row: Readonly<Record<string, unknown>>, ...keys: readonly string[]) => string;
@@ -0,0 +1,2 @@
1
+ import{List as e,None as t,Some as n}from"functype";const r=e=>typeof e==`string`?n(e):typeof e==`number`&&Number.isFinite(e)?n(String(e)):t(),i=e=>typeof e==`object`&&!!e&&!Array.isArray(e),a=(t,...n)=>e(n).find(e=>r(t[e]).isSome()).flatMap(e=>r(t[e])).orElse(``);export{i as isRecord,a as textField};
2
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.js","names":["asText","value","Some","None","isRecord","textField","keys","List","row","key"],"sources":["../../src/utils/fields.ts"],"sourcesContent":["/**\n * Field access for loosely-typed rows (parsed bulk files, JSON API records)\n */\n\nimport { List, None, type Option, Some } from \"functype\"\n\nconst asText = (value: unknown): Option<string> => {\n if (typeof value === \"string\") return Some(value)\n if (typeof value === \"number\" && Number.isFinite(value)) return Some(String(value))\n return None()\n}\n\n/** A plain JSON object (not null, not an array) */\nexport const isRecord = (value: unknown): value is Readonly<Record<string, unknown>> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value)\n\n/**\n * The value of the first key that holds text, or \"\" when none does.\n * An empty string counts as present, as it did with `??` chains; null,\n * missing and non-text values are skipped. Numbers are kept as text.\n */\nexport const textField = (row: Readonly<Record<string, unknown>>, ...keys: readonly string[]): string =>\n List(keys)\n .find((key) => asText(row[key]).isSome())\n .flatMap((key) => asText(row[key]))\n .orElse(\"\")\n"],"mappings":"A,oDAMA,MAAMA,EAAU,GACV,OAAOC,GAAU,SAAiBC,EAAKD,CAAK,EAC5C,OAAOA,GAAU,UAAY,OAAO,SAASA,CAAK,EAAUC,EAAK,OAAOD,CAAK,CAAC,EAC3EE,EAAK,EAIDC,EAAY,GACvB,OAAOH,GAAU,YAAYA,GAAkB,CAAC,MAAM,QAAQA,CAAK,EAOxDI,GAAa,EAAwC,GAAGC,IACnEC,EAAKD,CAAI,CAAC,CACP,KAAM,GAAQN,EAAOQ,EAAIC,EAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CACxC,QAAS,GAAQT,EAAOQ,EAAIC,EAAI,CAAC,CAAC,CAClC,OAAO,EAAE,E"}
@@ -1,14 +1,14 @@
1
+ /**
2
+ * Logger utility for OpenFDA MCP Server
3
+ * Uses debug module for namespace-based logging
4
+ *
5
+ * Enable with: DEBUG=openfda-mcp:* <command>
6
+ */
1
7
  import debug from "debug";
2
-
3
- //#region src/utils/logger.d.ts
4
-
5
- declare const loggers: {
8
+ export declare const loggers: {
6
9
  core: debug.Debugger;
7
10
  api: debug.Debugger;
8
11
  tools: debug.Debugger;
9
12
  main: debug.Debugger;
10
- fastmcp: debug.Debugger;
11
- };
12
- //#endregion
13
- export { loggers };
14
- //# sourceMappingURL=logger.d.ts.map
13
+ bulk: debug.Debugger;
14
+ };
@@ -1,2 +1,2 @@
1
- import e from"debug";const t={core:e(`openfda-mcp:core`),api:e(`openfda-mcp:api`),tools:e(`openfda-mcp:tools`),main:e(`openfda-mcp:main`),fastmcp:e(`openfda-mcp:fastmcp`)};export{t as loggers};
1
+ import e from"debug";const t={core:e(`openfda-mcp:core`),api:e(`openfda-mcp:api`),tools:e(`openfda-mcp:tools`),main:e(`openfda-mcp:main`),bulk:e(`openfda-mcp:bulk`)};export{t as loggers};
2
2
  //# sourceMappingURL=logger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","names":[],"sources":["../../src/utils/logger.ts"],"sourcesContent":["/**\n * Logger utility for OpenFDA MCP Server\n * Uses debug module for namespace-based logging\n *\n * Enable with: DEBUG=openfda-mcp:* <command>\n */\n\nimport debug from \"debug\"\n\nexport const loggers = {\n core: debug(\"openfda-mcp:core\"),\n api: debug(\"openfda-mcp:api\"),\n tools: debug(\"openfda-mcp:tools\"),\n main: debug(\"openfda-mcp:main\"),\n fastmcp: debug(\"openfda-mcp:fastmcp\"),\n}\n"],"mappings":"qBASA,MAAa,EAAU,CACrB,KAAM,EAAM,mBAAmB,CAC/B,IAAK,EAAM,kBAAkB,CAC7B,MAAO,EAAM,oBAAoB,CACjC,KAAM,EAAM,mBAAmB,CAC/B,QAAS,EAAM,sBAAsB,CACtC"}
1
+ {"version":3,"file":"logger.js","names":["debug","loggers"],"sources":["../../src/utils/logger.ts"],"sourcesContent":["/**\n * Logger utility for OpenFDA MCP Server\n * Uses debug module for namespace-based logging\n *\n * Enable with: DEBUG=openfda-mcp:* <command>\n */\n\nimport debug from \"debug\"\n\nexport const loggers = {\n core: debug(\"openfda-mcp:core\"),\n api: debug(\"openfda-mcp:api\"),\n tools: debug(\"openfda-mcp:tools\"),\n main: debug(\"openfda-mcp:main\"),\n bulk: debug(\"openfda-mcp:bulk\"),\n}\n"],"mappings":"A,qBASA,MAAaC,EAAU,CACrB,KAAMD,EAAM,kBAAkB,EAC9B,IAAKA,EAAM,iBAAiB,EAC5B,MAAOA,EAAM,mBAAmB,EAChC,KAAMA,EAAM,kBAAkB,EAC9B,KAAMA,EAAM,kBAAkB,CAChC,E"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The package version, read from package.json so the CLI, MCP serverInfo
3
+ * and SomaMCP's info tool report what was actually installed.
4
+ */
5
+ type Semver = `${number}.${number}.${number}`;
6
+ declare const VERSION: Semver;
7
+ export { VERSION as n, Semver as t };
@@ -0,0 +1,2 @@
1
+ import { n as VERSION, t as Semver } from "./version-BNWZ_jnk.js";
2
+ export { Semver, VERSION };
@@ -0,0 +1,2 @@
1
+ import{readFileSync as e}from"node:fs";const t=JSON.parse(e(new URL(`../package.json`,import.meta.url),`utf8`)).version;export{t as VERSION};
2
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","names":["VERSION","readFileSync"],"sources":["../src/version.ts"],"sourcesContent":["/**\n * The package version, read from package.json so the CLI, MCP serverInfo\n * and SomaMCP's info tool report what was actually installed.\n */\n\nimport { readFileSync } from \"node:fs\"\n\nexport type Semver = `${number}.${number}.${number}`\n\n// src/version.ts and dist/version.js both sit one level below package.json\nconst packageJson = JSON.parse(readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\")) as {\n version: Semver\n}\n\nexport const VERSION: Semver = packageJson.version\n"],"mappings":"A,uCAcA,MAAaA,EAJO,KAAK,MAAMC,EAAa,IAAI,IAAI,kBAAmB,YAAY,GAAG,EAAG,MAAM,CAIhE,CAAA,CAAY,Q"}