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.
Files changed (46) 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 +13 -13
  4. package/dist/handlers/bulk-data-handlers.js +1 -1
  5. package/dist/handlers/bulk-data-handlers.js.map +1 -1
  6. package/dist/handlers/device-handlers.d.ts +13 -13
  7. package/dist/handlers/device-handlers.js +1 -1
  8. package/dist/handlers/device-handlers.js.map +1 -1
  9. package/dist/handlers/drug-handlers.d.ts +17 -17
  10. package/dist/handlers/drug-handlers.js +1 -1
  11. package/dist/handlers/drug-handlers.js.map +1 -1
  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 +6 -2
  19. package/dist/lib.js +1 -1
  20. package/dist/server.d.ts +9 -9
  21. package/dist/server.js +1 -1
  22. package/dist/server.js.map +1 -1
  23. package/dist/tools/index.d.ts +35 -36
  24. package/dist/tools/index.js.map +1 -1
  25. package/dist/types/fda.d.ts +19 -23
  26. package/dist/utils/api-client.d.ts +27 -23
  27. package/dist/utils/api-client.js +1 -1
  28. package/dist/utils/api-client.js.map +1 -1
  29. package/dist/utils/bulk-data-client.d.ts +19 -13
  30. package/dist/utils/bulk-data-client.js +2 -3
  31. package/dist/utils/bulk-data-client.js.map +1 -1
  32. package/dist/utils/errors.d.ts +8 -0
  33. package/dist/utils/errors.js +2 -0
  34. package/dist/utils/errors.js.map +1 -0
  35. package/dist/utils/fields.d.ts +11 -0
  36. package/dist/utils/fields.js +2 -0
  37. package/dist/utils/fields.js.map +1 -0
  38. package/dist/utils/logger.d.ts +8 -8
  39. package/dist/utils/logger.js +1 -1
  40. package/dist/utils/logger.js.map +1 -1
  41. package/dist/version-BNWZ_jnk.d.ts +7 -0
  42. package/dist/version.d.ts +2 -0
  43. package/dist/version.js +2 -0
  44. package/dist/version.js.map +1 -0
  45. package/package.json +16 -12
  46. package/dist/defineProperty-BEGP2H1W.js +0 -1
@@ -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":"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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;
@@ -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
- //#region src/utils/api-client.d.ts
4
- declare class FDAAPIClient {
5
- private apiKey;
6
- 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;
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
- authenticated: boolean;
21
- rateLimit: string;
22
- };
23
- searchDrugAdverseEvents(params: SearchParams): Promise<FDAResponse<DrugAdverseEvent>>;
24
- searchDrugLabels(params: SearchParams): Promise<FDAResponse<DrugLabel>>;
25
- searchDrugNDC(params: SearchParams): Promise<FDAResponse<DrugNDC>>;
26
- searchDrugEnforcement(params: SearchParams): Promise<FDAResponse<DrugEnforcement>>;
27
- searchDrugsFDA(params: SearchParams): Promise<FDAResponse<DrugsFDA>>;
28
- searchDrugShortages(params: SearchParams): Promise<FDAResponse<DrugShortage>>;
29
- searchDevice510K(params: SearchParams): Promise<FDAResponse<Device510K>>;
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
- //#endregion
36
- export { FDAAPIClient, fdaAPIClient };
37
- //# sourceMappingURL=api-client.d.ts.map
40
+ export declare const fdaAPIClient: FDAAPIClient;
41
+ export {};
@@ -1,2 +1,2 @@
1
- import{loggers as e}from"./logger.js";import{t}from"../defineProperty-BEGP2H1W.js";var n=class{constructor(){t(this,`apiKey`,void 0),t(this,`baseUrl`,void 0),this.apiKey=process.env.OPENFDA_API_KEY,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&&t.set(`api_key`,this.apiKey),t}async request(t,n){let r=this.buildSearchParams(n),i=`${this.baseUrl}${t}?${r.toString()}`;e.api(`Request: ${i.replace(this.apiKey??``,`[REDACTED]`)}`);try{let t=await fetch(i);if(!t.ok){let e=await t.json().catch(()=>({}));if(t.status===429)throw Error(`Rate limit exceeded. Consider using an API key for higher limits.`);if(t.status===404)return{results:[]};throw t.status===400?Error(`Invalid request: ${e.error?.message??`Bad request parameters`}`):Error(`FDA API error: ${t.status} ${t.statusText}`)}let n=await t.json();return e.api(`Response: ${n.meta?.results?.total??0} total results`),n}catch(e){throw e instanceof Error?e:Error(`Failed to fetch from FDA API: ${String(e)}`,{cause:e})}}getRateLimitInfo(){return{authenticated:!!this.apiKey,rateLimit:this.apiKey?`240/minute, 120,000/hour`:`40/minute, 1,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 r=new n;export{n as FDAAPIClient,r 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
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 | undefined\n private baseUrl: string\n\n constructor() {\n this.apiKey = process.env.OPENFDA_API_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 if (this.apiKey) {\n urlParams.set(\"api_key\", this.apiKey)\n }\n\n return urlParams\n }\n\n /**\n * Make a request to an FDA API endpoint\n */\n private async request<T>(endpoint: FDAEndpoint, params: SearchParams): Promise<FDAResponse<T>> {\n const urlParams = this.buildSearchParams(params)\n const url = `${this.baseUrl}${endpoint}?${urlParams.toString()}`\n\n loggers.api(`Request: ${url.replace(this.apiKey ?? \"\", \"[REDACTED]\")}`)\n\n try {\n const response = await fetch(url)\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}))\n\n if (response.status === 429) {\n throw new Error(\"Rate limit exceeded. Consider using an API key for higher limits.\")\n }\n if (response.status === 404) {\n return { results: [] } // No results found\n }\n if (response.status === 400) {\n throw new Error(\n `Invalid request: ${(errorData as { error?: { message?: string } }).error?.message ?? \"Bad request parameters\"}`,\n )\n }\n\n throw new Error(`FDA API error: ${response.status} ${response.statusText}`)\n }\n\n const data = (await response.json()) as FDAResponse<T>\n loggers.api(`Response: ${data.meta?.results?.total ?? 0} total results`)\n return data\n } catch (error) {\n if (error instanceof Error) {\n throw error\n }\n throw new Error(`Failed to fetch from FDA API: ${String(error)}`, { cause: error })\n }\n }\n\n /**\n * Get rate limit info based on authentication status\n */\n getRateLimitInfo(): { authenticated: boolean; rateLimit: string } {\n return {\n authenticated: !!this.apiKey,\n rateLimit: this.apiKey ? \"240/minute, 120,000/hour\" : \"40/minute, 1,000/hour\",\n }\n }\n\n // Drug endpoints\n\n async searchDrugAdverseEvents(params: SearchParams): Promise<FDAResponse<DrugAdverseEvent>> {\n return this.request<DrugAdverseEvent>(\"/drug/event.json\", params)\n }\n\n async searchDrugLabels(params: SearchParams): Promise<FDAResponse<DrugLabel>> {\n return this.request<DrugLabel>(\"/drug/label.json\", params)\n }\n\n async searchDrugNDC(params: SearchParams): Promise<FDAResponse<DrugNDC>> {\n return this.request<DrugNDC>(\"/drug/ndc.json\", params)\n }\n\n async searchDrugEnforcement(params: SearchParams): Promise<FDAResponse<DrugEnforcement>> {\n return this.request<DrugEnforcement>(\"/drug/enforcement.json\", params)\n }\n\n async searchDrugsFDA(params: SearchParams): Promise<FDAResponse<DrugsFDA>> {\n return this.request<DrugsFDA>(\"/drug/drugsfda.json\", params)\n }\n\n async searchDrugShortages(params: SearchParams): Promise<FDAResponse<DrugShortage>> {\n return this.request<DrugShortage>(\"/drug/shortages.json\", params)\n }\n\n // Device endpoints\n\n async searchDevice510K(params: SearchParams): Promise<FDAResponse<Device510K>> {\n return this.request<Device510K>(\"/device/510k.json\", params)\n }\n\n async searchDeviceClassifications(params: SearchParams): Promise<FDAResponse<DeviceClassification>> {\n return this.request<DeviceClassification>(\"/device/classification.json\", params)\n }\n\n async searchDeviceAdverseEvents(params: SearchParams): Promise<FDAResponse<DeviceAdverseEvent>> {\n return this.request<DeviceAdverseEvent>(\"/device/event.json\", params)\n }\n\n async searchDeviceEnforcement(params: SearchParams): Promise<FDAResponse<DeviceEnforcement>> {\n return this.request<DeviceEnforcement>(\"/device/enforcement.json\", params)\n }\n}\n\n// Singleton instance\nexport const fdaAPIClient = new FDAAPIClient()\n"],"mappings":"mFAqCA,IAAa,EAAb,KAA0B,CAIxB,aAAc,QAHN,SAAA,IAAA,GAA0B,QAC1B,UAAA,IAAA,GAAe,CAGrB,KAAK,OAAS,QAAQ,IAAI,gBAC1B,KAAK,QAAU,sBAMjB,kBAA0B,EAAuC,CAC/D,IAAM,EAAY,IAAI,gBAElB,EAAO,QACT,EAAU,IAAI,SAAU,EAAO,OAAO,CAGpC,EAAO,OACT,EAAU,IAAI,QAAS,EAAO,MAAM,CAGtC,IAAM,EAAQ,KAAK,IAAI,EAAO,OAAS,GAAe,IAAU,CAWhE,OAVA,EAAU,IAAI,QAAS,OAAO,EAAM,CAAC,CAEjC,EAAO,OAAS,IAAA,IAAa,EAAO,KAAO,GAC7C,EAAU,IAAI,OAAQ,OAAO,EAAO,KAAK,CAAC,CAGxC,KAAK,QACP,EAAU,IAAI,UAAW,KAAK,OAAO,CAGhC,EAMT,MAAc,QAAW,EAAuB,EAA+C,CAC7F,IAAM,EAAY,KAAK,kBAAkB,EAAO,CAC1C,EAAM,GAAG,KAAK,UAAU,EAAS,GAAG,EAAU,UAAU,GAE9D,EAAQ,IAAI,YAAY,EAAI,QAAQ,KAAK,QAAU,GAAI,aAAa,GAAG,CAEvE,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,EAAI,CAEjC,GAAI,CAAC,EAAS,GAAI,CAChB,IAAM,EAAY,MAAM,EAAS,MAAM,CAAC,WAAa,EAAE,EAAE,CAEzD,GAAI,EAAS,SAAW,IACtB,MAAU,MAAM,oEAAoE,CAEtF,GAAI,EAAS,SAAW,IACtB,MAAO,CAAE,QAAS,EAAE,CAAE,CAQxB,MANI,EAAS,SAAW,IACZ,MACR,oBAAqB,EAA+C,OAAO,SAAW,2BACvF,CAGO,MAAM,kBAAkB,EAAS,OAAO,GAAG,EAAS,aAAa,CAG7E,IAAM,EAAQ,MAAM,EAAS,MAAM,CAEnC,OADA,EAAQ,IAAI,aAAa,EAAK,MAAM,SAAS,OAAS,EAAE,gBAAgB,CACjE,QACA,EAAO,CAId,MAHI,aAAiB,MACb,EAEE,MAAM,iCAAiC,OAAO,EAAM,GAAI,CAAE,MAAO,EAAO,CAAC,EAOvF,kBAAkE,CAChE,MAAO,CACL,cAAe,CAAC,CAAC,KAAK,OACtB,UAAW,KAAK,OAAS,2BAA6B,wBACvD,CAKH,MAAM,wBAAwB,EAA8D,CAC1F,OAAO,KAAK,QAA0B,mBAAoB,EAAO,CAGnE,MAAM,iBAAiB,EAAuD,CAC5E,OAAO,KAAK,QAAmB,mBAAoB,EAAO,CAG5D,MAAM,cAAc,EAAqD,CACvE,OAAO,KAAK,QAAiB,iBAAkB,EAAO,CAGxD,MAAM,sBAAsB,EAA6D,CACvF,OAAO,KAAK,QAAyB,yBAA0B,EAAO,CAGxE,MAAM,eAAe,EAAsD,CACzE,OAAO,KAAK,QAAkB,sBAAuB,EAAO,CAG9D,MAAM,oBAAoB,EAA0D,CAClF,OAAO,KAAK,QAAsB,uBAAwB,EAAO,CAKnE,MAAM,iBAAiB,EAAwD,CAC7E,OAAO,KAAK,QAAoB,oBAAqB,EAAO,CAG9D,MAAM,4BAA4B,EAAkE,CAClG,OAAO,KAAK,QAA8B,8BAA+B,EAAO,CAGlF,MAAM,0BAA0B,EAAgE,CAC9F,OAAO,KAAK,QAA4B,qBAAsB,EAAO,CAGvE,MAAM,wBAAwB,EAA+D,CAC3F,OAAO,KAAK,QAA2B,2BAA4B,EAAO,GAK9E,MAAa,EAAe,IAAI"}
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
- declare class BulkDataClient {
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
- private isCacheValid;
13
- downloadBinary(url: string): Promise<Uint8Array>;
14
- parseTildeDelimited<T extends Record<string, string>>(text: string): T[];
15
- parseCSV<T extends Record<string, string>>(text: string): T[];
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
- getOrangeBookData(): Promise<OrangeBookData>;
18
- getPurpleBookData(): Promise<PurpleBookEntry[]>;
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
- //#endregion
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{loggers as e}from"./logger.js";import{t}from"../defineProperty-BEGP2H1W.js";import{unzipSync as n}from"fflate";var r=class{constructor(){t(this,`orangeBookCache`,void 0),t(this,`purpleBookCache`,void 0)}isCacheValid(e){return e?Date.now()-e.fetchedAt<e.ttlMs:!1}async downloadBinary(t){e.bulk(`Downloading: ${t}`);let n=await fetch(t);if(!n.ok)throw Error(`Failed to download ${t}: ${n.status} ${n.statusText}`);let r=await n.arrayBuffer();return new Uint8Array(r)}parseTildeDelimited(e){let t=e.split(`
2
- `).filter(e=>e.trim()!==``);if(t.length<2)return[];let n=t[0].split(`~`).map(e=>e.trim()),r=[];for(let e=1;e<t.length;e++){let i=t[e].split(`~`),a={};for(let e=0;e<n.length;e++)a[n[e]]=(i[e]??``).trim();r.push(a)}return r}parseCSV(e){let t=e.split(`
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,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
- 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
+ };