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/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"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
|
+
{"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":"A,wBAQA,MAAMA,EAAmB,CACvB,MAAOC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,+CAA+C,EAC3G,KAAMA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,0CAA0C,CAC9F,EAGMC,EAAkB,CACtB,SAAUD,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,4CAA4C,EACrF,OAAQA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,0CAA0C,CACnF,EAIaE,EAAgCF,EAAE,OAAO,CACpD,eAAgBA,EACb,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,wEAAwE,EACpF,SAAUA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC,EACzE,SAAUA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,yDAAyD,EAClG,aAAcA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB,EACrE,QAASA,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wCAAwC,EACjF,GAAGC,EACH,GAAGF,CACL,CAAC,EAGYI,EAAgB,CAC3B,wBACA,4BACA,oBACA,WACA,wBACA,oBACA,oBACA,wBACA,sBACA,mBACA,aACA,cACA,eACA,uBACA,eACF,EAEaC,EAAyBJ,EAAE,OAAO,CAC7C,MAAOA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,uEAAuE,EAC7G,SAAUA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,4BAA4B,EACrE,WAAYA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC,EAC3E,iBAAkBA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,kCAAkC,EACnF,MAAOA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,uDAAuD,EAC7F,gBAAiBA,EACd,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,qEAAqE,EACjF,SAAUA,EACP,MAAMA,EAAE,KAAKG,CAAa,CAAC,CAAC,CAC5B,SAAS,CAAC,CACV,SAAS,sGAAsG,EAClH,GAAGJ,CACL,CAAC,EAEYM,EAAsBL,EAAE,OAAO,CAC1C,WAAYA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,0BAA0B,EACrE,UAAWA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,iBAAiB,EAC3D,YAAaA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,mBAAmB,EAC/D,YAAaA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC,EAC5E,WAAYA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,sDAAsD,EACjG,MAAOA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,yBAAyB,EAC/D,GAAGD,CACL,CAAC,EAEYO,EAA8BN,EAAE,OAAO,CAClD,cAAeA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,+BAA+B,EAC7E,eAAgBA,EACb,KAAK,CAAC,UAAW,WAAY,WAAW,CAAC,CAAC,CAC1C,SAAS,CAAC,CACV,SAAS,mDAAmD,EAC/D,OAAQA,EAAE,KAAK,CAAC,UAAW,YAAa,aAAc,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,eAAe,EACrG,MAAOA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,kCAAkC,EACxE,GAAGC,EACH,GAAGF,CACL,CAAC,EAEYQ,EAAuBP,EAAE,OAAO,CAC3C,YAAaA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,2BAA2B,EACvE,kBAAmBA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,4CAA4C,EAC9F,UAAWA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,iBAAiB,EAC3D,gBAAiBA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gDAAgD,EAChG,GAAGD,CACL,CAAC,EAEYS,EAA4BR,EAAE,OAAO,CAChD,YAAaA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,mBAAmB,EAC/D,OAAQA,EAAE,KAAK,CAAC,UAAW,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,iBAAiB,EAC7E,GAAGD,CACL,CAAC,EAIYU,EAAyBT,EAAE,OAAO,CAC7C,WAAYA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,uBAAuB,EAClE,UAAWA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB,EAClE,YAAaA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,kBAAkB,EAC9D,cAAeA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,iDAAiD,EAC/F,iBAAkBA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wCAAwC,EACzF,eAAgBA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,sCAAsC,EACrF,GAAGD,CACL,CAAC,EAEYW,EAAoCV,EAAE,OAAO,CACxD,WAAYA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,uBAAuB,EAClE,YAAaA,EAAE,KAAK,CAAC,IAAK,IAAK,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,yCAAyC,EAClG,iBAAkBA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB,EACzE,YAAaA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,kBAAkB,EAC9D,iBAAkBA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,uBAAuB,EACxE,GAAGD,CACL,CAAC,EAEYY,EAAkCX,EAAE,OAAO,CACtD,aAAcA,EACX,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,yEAAyE,EACrF,WAAYA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,qBAAqB,EAChE,UAAWA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,mBAAmB,EAC7D,iBAAkBA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,0BAA0B,EAC3E,UAAWA,EAAE,KAAK,CAAC,SAAU,cAAe,QAAS,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,uBAAuB,EAC1G,GAAGC,EACH,GAAGF,CACL,CAAC,EAEYa,EAAgCZ,EAAE,OAAO,CACpD,cAAeA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,+BAA+B,EAC7E,mBAAoBA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,8BAA8B,EACjF,eAAgBA,EACb,KAAK,CAAC,UAAW,WAAY,WAAW,CAAC,CAAC,CAC1C,SAAS,CAAC,CACV,SAAS,wCAAwC,EACpD,OAAQA,EAAE,KAAK,CAAC,UAAW,YAAa,aAAc,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,eAAe,EACrG,GAAGC,EACH,GAAGF,CACL,CAAC,EAIYc,EAAyBb,EAAE,OAAO,CAC7C,SAAUA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,yCAAyC,EAClF,UAAWA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB,EAClE,OAAQA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB,EAC/D,OAAQA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,iDAAiD,EACxF,GAAGD,CACL,CAAC,EAEYe,EAAgCd,EAAE,OAAO,CACpD,SAAUA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,yCAAyC,EAClF,OAAQA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB,EAC/D,SAAUA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,eAAe,EACxD,GAAGD,CACL,CAAC,EAEYgB,EAAyBf,EAAE,OAAO,CAC7C,YAAaA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,8CAA8C,EAC1F,UAAWA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB,EAClE,UAAWA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,4CAA4C,EACtF,YAAaA,EACV,KAAK,CAAC,SAAU,QAAQ,CAAC,CAAC,CAC1B,SAAS,CAAC,CACV,SAAS,4DAA4D,EACxE,WAAYA,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC,EAC5E,gBAAiBA,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,qCAAqC,EACtF,GAAGD,CACL,CAAC,EAEYiB,EAA+BhB,EAAE,OAAO,CACnD,SAAUA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,yCAAyC,EAClF,OAAQA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB,EAC/D,mBAAoBA,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAI,CAAC,CAAC,SAAS,0CAA0C,EAC5G,kBAAmBA,EAChB,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,QAAQ,EAAK,CAAC,CACd,SAAS,qDAAqD,EACjE,GAAGD,CACL,CAAC,EAGYkB,EAAc,CACzB,wBAAyBf,EACzB,iBAAkBE,EAClB,cAAeC,EACf,sBAAuBC,EACvB,eAAgBC,EAChB,oBAAqBC,EACrB,iBAAkBC,EAClB,4BAA6BC,EAC7B,0BAA2BC,EAC3B,wBAAyBC,EACzB,iBAAkBC,EAClB,wBAAyBC,EACzB,iBAAkBC,EAClB,uBAAwBC,CAC1B,E"}
|
package/dist/types/fda.d.ts
CHANGED
|
@@ -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;
|
|
@@ -423,7 +422,7 @@ type FDAToolResponse<T> = {
|
|
|
423
422
|
url: string;
|
|
424
423
|
};
|
|
425
424
|
};
|
|
426
|
-
type OrangeBookProduct = {
|
|
425
|
+
export type OrangeBookProduct = {
|
|
427
426
|
ingredient: string;
|
|
428
427
|
dfRoute: string;
|
|
429
428
|
tradeName: string;
|
|
@@ -438,7 +437,7 @@ type OrangeBookProduct = {
|
|
|
438
437
|
type: string;
|
|
439
438
|
applicantFullName: string;
|
|
440
439
|
};
|
|
441
|
-
type OrangeBookPatent = {
|
|
440
|
+
export type OrangeBookPatent = {
|
|
442
441
|
applType: string;
|
|
443
442
|
applNo: string;
|
|
444
443
|
productNo: string;
|
|
@@ -450,14 +449,14 @@ type OrangeBookPatent = {
|
|
|
450
449
|
delistFlag: string;
|
|
451
450
|
submissionDate: string;
|
|
452
451
|
};
|
|
453
|
-
type OrangeBookExclusivity = {
|
|
452
|
+
export type OrangeBookExclusivity = {
|
|
454
453
|
applType: string;
|
|
455
454
|
applNo: string;
|
|
456
455
|
productNo: string;
|
|
457
456
|
exclusivityCode: string;
|
|
458
457
|
exclusivityDate: string;
|
|
459
458
|
};
|
|
460
|
-
type PurpleBookEntry = {
|
|
459
|
+
export type PurpleBookEntry = {
|
|
461
460
|
blaNumber: string;
|
|
462
461
|
proprietaryName: string;
|
|
463
462
|
properName: string;
|
|
@@ -474,7 +473,4 @@ type PurpleBookEntry = {
|
|
|
474
473
|
biosimilar: boolean;
|
|
475
474
|
interchangeable: boolean;
|
|
476
475
|
approvalDate: string;
|
|
477
|
-
};
|
|
478
|
-
//#endregion
|
|
479
|
-
export { Device510K, DeviceAdverseEvent, DeviceClassification, DeviceEnforcement, DrugAdverseEvent, DrugEnforcement, DrugLabel, DrugNDC, DrugShortage, DrugsFDA, FDAResponse, FDAToolResponse, OpenFDAFields, OrangeBookExclusivity, OrangeBookPatent, OrangeBookProduct, PurpleBookEntry, SearchParams };
|
|
480
|
-
//# sourceMappingURL=fda.d.ts.map
|
|
476
|
+
};
|
|
@@ -1,37 +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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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;
|
|
7
14
|
constructor();
|
|
8
15
|
/**
|
|
9
16
|
* Build URL search parameters for the FDA API
|
|
10
17
|
*/
|
|
11
18
|
private buildSearchParams;
|
|
19
|
+
private redact;
|
|
12
20
|
/**
|
|
13
21
|
* Make a request to an FDA API endpoint
|
|
14
22
|
*/
|
|
15
23
|
private request;
|
|
24
|
+
private readResponse;
|
|
16
25
|
/**
|
|
17
26
|
* Get rate limit info based on authentication status
|
|
18
27
|
*/
|
|
19
|
-
getRateLimitInfo():
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
searchDeviceClassifications(params: SearchParams): Promise<FDAResponse<DeviceClassification>>;
|
|
31
|
-
searchDeviceAdverseEvents(params: SearchParams): Promise<FDAResponse<DeviceAdverseEvent>>;
|
|
32
|
-
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>>>;
|
|
33
39
|
}
|
|
34
|
-
declare const fdaAPIClient: FDAAPIClient;
|
|
35
|
-
|
|
36
|
-
export { FDAAPIClient, fdaAPIClient };
|
|
37
|
-
//# sourceMappingURL=api-client.d.ts.map
|
|
40
|
+
export declare const fdaAPIClient: FDAAPIClient;
|
|
41
|
+
export {};
|
package/dist/utils/api-client.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
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
2
|
//# sourceMappingURL=api-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","names":[],"sources":["../../src/utils/api-client.ts"],"sourcesContent":["/**\n * FDA API Client\n * HTTP client for interacting with FDA OpenAPI endpoints\n */\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 { 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\nexport class FDAAPIClient {\n private apiKey: string
|
|
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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"}
|
|
@@ -1,24 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulk Data Client
|
|
3
|
+
* Downloads and parses FDA Orange Book and Purple Book data files
|
|
4
|
+
*/
|
|
1
5
|
import { OrangeBookExclusivity, OrangeBookPatent, OrangeBookProduct, PurpleBookEntry } from "../types/fda.js";
|
|
2
|
-
|
|
3
|
-
//#region src/utils/bulk-data-client.d.ts
|
|
6
|
+
import { Either } from "functype";
|
|
4
7
|
type OrangeBookData = {
|
|
5
8
|
products: OrangeBookProduct[];
|
|
6
9
|
patents: OrangeBookPatent[];
|
|
7
10
|
exclusivities: OrangeBookExclusivity[];
|
|
8
11
|
};
|
|
9
|
-
|
|
12
|
+
/** A parsed row: column name to cell text */
|
|
13
|
+
export type Row = Readonly<Record<string, string>>;
|
|
14
|
+
export declare class BulkDataClient {
|
|
10
15
|
private orangeBookCache;
|
|
11
16
|
private purpleBookCache;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
downloadBinary(url: string): Promise<Either<Error, Uint8Array>>;
|
|
18
|
+
parseTildeDelimited(text: string): Row[];
|
|
19
|
+
parseCSV(text: string): Row[];
|
|
20
|
+
private parseRows;
|
|
16
21
|
private parseCSVLine;
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
private parseOrangeBookFiles;
|
|
23
|
+
getOrangeBookData(): Promise<Either<Error, OrangeBookData>>;
|
|
24
|
+
private loadOrangeBook;
|
|
25
|
+
getPurpleBookData(): Promise<Either<Error, PurpleBookEntry[]>>;
|
|
26
|
+
private loadPurpleBook;
|
|
19
27
|
clearCache(): void;
|
|
20
28
|
}
|
|
21
|
-
declare const bulkDataClient: BulkDataClient;
|
|
22
|
-
|
|
23
|
-
export { BulkDataClient, bulkDataClient };
|
|
24
|
-
//# sourceMappingURL=bulk-data-client.d.ts.map
|
|
29
|
+
export declare const bulkDataClient: BulkDataClient;
|
|
30
|
+
export {};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`).filter(e=>e.trim()!==``);if(
|
|
3
|
-
`).filter(e=>e.trim()!==``);if(t.length<2)return[];let n=this.parseCSVLine(t[0]),r=[];for(let e=1;e<t.length;e++){let i=this.parseCSVLine(t[e]),a={};for(let e=0;e<n.length;e++)a[n[e]]=(i[e]??``).trim();r.push(a)}return r}parseCSVLine(e){let t=[],n=``,r=!1;for(let i=0;i<e.length;i++){let a=e[i];r?a===`"`?i+1<e.length&&e[i+1]===`"`?(n+=`"`,i++):r=!1:n+=a:a===`"`?r=!0:a===`,`?(t.push(n.trim()),n=``):n+=a}return t.push(n.trim()),t}async getOrangeBookData(){if(this.isCacheValid(this.orangeBookCache))return e.bulk(`Using cached Orange Book data`),this.orangeBookCache.data;e.bulk(`Downloading Orange Book ZIP...`);let t=n(await this.downloadBinary(`https://www.fda.gov/media/76860/download`)),r=new TextDecoder(`utf-8`),i=[],a=[],o=[];for(let[n,s]of Object.entries(t)){let t=n.toLowerCase(),c=r.decode(s);t.includes(`products`)?(i=this.parseTildeDelimited(c).map(e=>({ingredient:e.Ingredient??e.ingredient??``,dfRoute:e[`DF;Route`]??e[`df;route`]??e.DF_Route??``,tradeName:e.Trade_Name??e.trade_name??``,applicant:e.Applicant??e.applicant??``,applType:e.Appl_Type??e.appl_type??``,applNo:e.Appl_No??e.appl_no??``,productNo:e.Product_No??e.product_no??``,teCode:e.TE_Code??e.te_code??``,approvalDate:e.Approval_Date??e.approval_date??``,rld:e.RLD??e.rld??``,rs:e.RS??e.rs??``,type:e.Type??e.type??``,applicantFullName:e.Applicant_Full_Name??e.applicant_full_name??``})),e.bulk(`Parsed ${i.length} Orange Book products`)):t.includes(`patent`)?(a=this.parseTildeDelimited(c).map(e=>({applType:e.Appl_Type??e.appl_type??``,applNo:e.Appl_No??e.appl_no??``,productNo:e.Product_No??e.product_no??``,patentNo:e.Patent_No??e.patent_no??``,patentExpireDate:e.Patent_Expire_Date_Text??e.patent_expire_date_text??``,drugSubstanceFlag:e.Drug_Substance_Flag??e.drug_substance_flag??``,drugProductFlag:e.Drug_Product_Flag??e.drug_product_flag??``,patentUseCode:e.Patent_Use_Code??e.patent_use_code??``,delistFlag:e.Delist_Flag??e.delist_flag??``,submissionDate:e.Submission_Date??e.submission_date??``})),e.bulk(`Parsed ${a.length} Orange Book patents`)):t.includes(`exclusivity`)&&(o=this.parseTildeDelimited(c).map(e=>({applType:e.Appl_Type??e.appl_type??``,applNo:e.Appl_No??e.appl_no??``,productNo:e.Product_No??e.product_no??``,exclusivityCode:e.Exclusivity_Code??e.exclusivity_code??``,exclusivityDate:e.Exclusivity_Date??e.exclusivity_date??``})),e.bulk(`Parsed ${o.length} Orange Book exclusivities`))}let s={products:i,patents:a,exclusivities:o};return this.orangeBookCache={data:s,fetchedAt:Date.now(),ttlMs:1440*60*1e3},s}async getPurpleBookData(){if(this.isCacheValid(this.purpleBookCache))return e.bulk(`Using cached Purple Book data`),this.purpleBookCache.data;e.bulk(`Fetching Purple Book data from API...`);let t=await fetch(`https://purplebooksearch.fda.gov/api/v1/fdaproducts?token=18dd75c5-23c2-4318-970b-ca97c34470cb`);if(!t.ok)throw Error(`Failed to fetch Purple Book data: ${t.status} ${t.statusText}`);let n=await t.json();e.bulk(`Received ${n.length} Purple Book entries from API`);let r=n.map(e=>({blaNumber:e.bla_number??``,proprietaryName:e.proprietary_name??``,properName:e.proper_name??``,applicant:e.applicant??``,blaType:e.license_type??``,strength:e.strength??``,dosageForm:e.dosage_form??``,route:e.route_of_administration??``,productPresentation:e.product_presentation??``,status:e.status??``,licensingStatus:e.licensure??``,referenceProductBla:e.ref_product_proper_name?.trim()??``,referenceProductProprietaryName:e.ref_prod_proprietary_name??``,biosimilar:(e.license_type??``)===`351(k)`,interchangeable:(e.interchangeable_approval_date??``).trim()!==``,approvalDate:e.approval_date??e.date_of_first_licensure??``}));return this.purpleBookCache={data:r,fetchedAt:Date.now(),ttlMs:10080*60*1e3},r}clearCache(){this.orangeBookCache=void 0,this.purpleBookCache=void 0}};const i=new r;export{r as BulkDataClient,i as bulkDataClient};
|
|
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};
|
|
4
3
|
//# sourceMappingURL=bulk-data-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-data-client.js","names":[],"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\"\n\nimport type { OrangeBookExclusivity, OrangeBookPatent, OrangeBookProduct, PurpleBookEntry } from \"../types/fda.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\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\nexport class BulkDataClient {\n private orangeBookCache: CacheEntry<OrangeBookData> | undefined\n private purpleBookCache: CacheEntry<PurpleBookEntry[]> | undefined\n\n private isCacheValid<T>(entry: CacheEntry<T> | undefined): entry is CacheEntry<T> {\n if (!entry) return false\n return Date.now() - entry.fetchedAt < entry.ttlMs\n }\n\n async downloadBinary(url: string): Promise<Uint8Array> {\n loggers.bulk(`Downloading: ${url}`)\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`)\n }\n const buffer = await response.arrayBuffer()\n return new Uint8Array(buffer)\n }\n\n parseTildeDelimited<T extends Record<string, string>>(text: string): T[] {\n const lines = text.split(\"\\n\").filter((line) => line.trim() !== \"\")\n if (lines.length < 2) return []\n\n const headers = lines[0].split(\"~\").map((h) => h.trim())\n const results: T[] = []\n\n for (let i = 1; i < lines.length; i++) {\n const values = lines[i].split(\"~\")\n const record: Record<string, string> = {}\n for (let j = 0; j < headers.length; j++) {\n record[headers[j]] = (values[j] ?? \"\").trim()\n }\n results.push(record as T)\n }\n\n return results\n }\n\n parseCSV<T extends Record<string, string>>(text: string): T[] {\n const lines = text.split(\"\\n\").filter((line) => line.trim() !== \"\")\n if (lines.length < 2) return []\n\n const headers = this.parseCSVLine(lines[0])\n const results: T[] = []\n\n for (let i = 1; i < lines.length; i++) {\n const values = this.parseCSVLine(lines[i])\n const record: Record<string, string> = {}\n for (let j = 0; j < headers.length; j++) {\n record[headers[j]] = (values[j] ?? \"\").trim()\n }\n results.push(record as T)\n }\n\n return results\n }\n\n private parseCSVLine(line: string): string[] {\n const result: string[] = []\n let current = \"\"\n let inQuotes = false\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i]\n if (inQuotes) {\n if (char === '\"') {\n if (i + 1 < line.length && line[i + 1] === '\"') {\n current += '\"'\n i++\n } else {\n inQuotes = false\n }\n } else {\n current += char\n }\n } else {\n if (char === '\"') {\n inQuotes = true\n } else if (char === \",\") {\n result.push(current.trim())\n current = \"\"\n } else {\n current += char\n }\n }\n }\n result.push(current.trim())\n return result\n }\n\n async getOrangeBookData(): Promise<OrangeBookData> {\n if (this.isCacheValid(this.orangeBookCache)) {\n loggers.bulk(\"Using cached Orange Book data\")\n return this.orangeBookCache.data\n }\n\n loggers.bulk(\"Downloading Orange Book ZIP...\")\n const zipData = await this.downloadBinary(ORANGE_BOOK_ZIP_URL)\n const files = unzipSync(zipData)\n\n const decoder = new TextDecoder(\"utf-8\")\n let products: OrangeBookProduct[] = []\n let patents: OrangeBookPatent[] = []\n let exclusivities: OrangeBookExclusivity[] = []\n\n for (const [filename, content] of Object.entries(files)) {\n const lowerName = filename.toLowerCase()\n const text = decoder.decode(content)\n\n if (lowerName.includes(\"products\")) {\n products = this.parseTildeDelimited<Record<string, string>>(text).map((r) => ({\n ingredient: r[\"Ingredient\"] ?? r[\"ingredient\"] ?? \"\",\n dfRoute: r[\"DF;Route\"] ?? r[\"df;route\"] ?? r[\"DF_Route\"] ?? \"\",\n tradeName: r[\"Trade_Name\"] ?? r[\"trade_name\"] ?? \"\",\n applicant: r[\"Applicant\"] ?? r[\"applicant\"] ?? \"\",\n applType: r[\"Appl_Type\"] ?? r[\"appl_type\"] ?? \"\",\n applNo: r[\"Appl_No\"] ?? r[\"appl_no\"] ?? \"\",\n productNo: r[\"Product_No\"] ?? r[\"product_no\"] ?? \"\",\n teCode: r[\"TE_Code\"] ?? r[\"te_code\"] ?? \"\",\n approvalDate: r[\"Approval_Date\"] ?? r[\"approval_date\"] ?? \"\",\n rld: r[\"RLD\"] ?? r[\"rld\"] ?? \"\",\n rs: r[\"RS\"] ?? r[\"rs\"] ?? \"\",\n type: r[\"Type\"] ?? r[\"type\"] ?? \"\",\n applicantFullName: r[\"Applicant_Full_Name\"] ?? r[\"applicant_full_name\"] ?? \"\",\n }))\n loggers.bulk(`Parsed ${products.length} Orange Book products`)\n } else if (lowerName.includes(\"patent\")) {\n patents = this.parseTildeDelimited<Record<string, string>>(text).map((r) => ({\n applType: r[\"Appl_Type\"] ?? r[\"appl_type\"] ?? \"\",\n applNo: r[\"Appl_No\"] ?? r[\"appl_no\"] ?? \"\",\n productNo: r[\"Product_No\"] ?? r[\"product_no\"] ?? \"\",\n patentNo: r[\"Patent_No\"] ?? r[\"patent_no\"] ?? \"\",\n patentExpireDate: r[\"Patent_Expire_Date_Text\"] ?? r[\"patent_expire_date_text\"] ?? \"\",\n drugSubstanceFlag: r[\"Drug_Substance_Flag\"] ?? r[\"drug_substance_flag\"] ?? \"\",\n drugProductFlag: r[\"Drug_Product_Flag\"] ?? r[\"drug_product_flag\"] ?? \"\",\n patentUseCode: r[\"Patent_Use_Code\"] ?? r[\"patent_use_code\"] ?? \"\",\n delistFlag: r[\"Delist_Flag\"] ?? r[\"delist_flag\"] ?? \"\",\n submissionDate: r[\"Submission_Date\"] ?? r[\"submission_date\"] ?? \"\",\n }))\n loggers.bulk(`Parsed ${patents.length} Orange Book patents`)\n } else if (lowerName.includes(\"exclusivity\")) {\n exclusivities = this.parseTildeDelimited<Record<string, string>>(text).map((r) => ({\n applType: r[\"Appl_Type\"] ?? r[\"appl_type\"] ?? \"\",\n applNo: r[\"Appl_No\"] ?? r[\"appl_no\"] ?? \"\",\n productNo: r[\"Product_No\"] ?? r[\"product_no\"] ?? \"\",\n exclusivityCode: r[\"Exclusivity_Code\"] ?? r[\"exclusivity_code\"] ?? \"\",\n exclusivityDate: r[\"Exclusivity_Date\"] ?? r[\"exclusivity_date\"] ?? \"\",\n }))\n loggers.bulk(`Parsed ${exclusivities.length} Orange Book exclusivities`)\n }\n }\n\n const data: OrangeBookData = { products, patents, exclusivities }\n\n this.orangeBookCache = {\n data,\n fetchedAt: Date.now(),\n ttlMs: 24 * 60 * 60 * 1000, // 24 hours\n }\n\n return data\n }\n\n async getPurpleBookData(): Promise<PurpleBookEntry[]> {\n if (this.isCacheValid(this.purpleBookCache)) {\n loggers.bulk(\"Using cached Purple Book data\")\n return this.purpleBookCache.data\n }\n\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 fetch(url)\n\n if (!response.ok) {\n throw new Error(`Failed to fetch Purple Book data: ${response.status} ${response.statusText}`)\n }\n\n const rawEntries = (await response.json()) as Array<Record<string, string>>\n loggers.bulk(`Received ${rawEntries.length} Purple Book entries from API`)\n\n const entries: PurpleBookEntry[] = rawEntries.map((r) => ({\n blaNumber: r.bla_number ?? \"\",\n proprietaryName: r.proprietary_name ?? \"\",\n properName: r.proper_name ?? \"\",\n applicant: r.applicant ?? \"\",\n blaType: r.license_type ?? \"\",\n strength: r.strength ?? \"\",\n dosageForm: r.dosage_form ?? \"\",\n route: r.route_of_administration ?? \"\",\n productPresentation: r.product_presentation ?? \"\",\n status: r.status ?? \"\",\n licensingStatus: r.licensure ?? \"\",\n referenceProductBla: r.ref_product_proper_name?.trim() ?? \"\",\n referenceProductProprietaryName: r.ref_prod_proprietary_name ?? \"\",\n biosimilar: (r.license_type ?? \"\") === \"351(k)\",\n interchangeable: (r.interchangeable_approval_date ?? \"\").trim() !== \"\",\n approvalDate: r.approval_date ?? r.date_of_first_licensure ?? \"\",\n }))\n\n this.purpleBookCache = {\n data: entries,\n fetchedAt: Date.now(),\n ttlMs: 7 * 24 * 60 * 60 * 1000, // 7 days\n }\n\n return entries\n }\n\n clearCache(): void {\n this.orangeBookCache = undefined\n this.purpleBookCache = undefined\n }\n}\n\n// Singleton instance\nexport const bulkDataClient = new BulkDataClient()\n"],"mappings":"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"}
|
|
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 @@
|
|
|
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"}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -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
|
-
declare const loggers: {
|
|
8
|
+
export declare const loggers: {
|
|
5
9
|
core: debug.Debugger;
|
|
6
10
|
api: debug.Debugger;
|
|
7
11
|
tools: debug.Debugger;
|
|
8
12
|
main: debug.Debugger;
|
|
9
|
-
fastmcp: debug.Debugger;
|
|
10
13
|
bulk: debug.Debugger;
|
|
11
|
-
};
|
|
12
|
-
//#endregion
|
|
13
|
-
export { loggers };
|
|
14
|
-
//# sourceMappingURL=logger.d.ts.map
|
|
14
|
+
};
|