@ythalorossy/openfda 1.0.13 → 1.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ythalorossy/openfda",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "OpenFDA Model Context Protocol",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,49 +0,0 @@
1
- /*
2
- * Copyright (c) 2025 Ythalo Saldanha
3
- * Licensed under the MIT License
4
- */
5
- /**
6
- * The OpenFDABuilder class helps construct URLs for the OpenFDA API.
7
- *
8
- * Usage:
9
- * - Set the context (such as 'label', 'ndc', or 'event') using the context() method.
10
- * - Set the search query using the search() method.
11
- * - Optionally set the result limit using the limit() method (default is 1).
12
- * - Call build() to assemble and return the final API URL.
13
- *
14
- * Example:
15
- * const url = new OpenFDABuilder()
16
- * .context('label')
17
- * .search('openfda.brand_name:"Advil"')
18
- * .limit(1)
19
- * .build();
20
- *
21
- * The build() method will throw an error if any required parameter is missing.
22
- * The API key is read from the OPENFDA_API_KEY environment variable.
23
- */
24
- export class OpenFDABuilder {
25
- url = "https://api.fda.gov/drug/";
26
- params = new Map();
27
- context(context) {
28
- this.params.set("context", context);
29
- return this;
30
- }
31
- search(query) {
32
- this.params.set("search", query);
33
- return this;
34
- }
35
- limit(max = 1) {
36
- this.params.set("limit", max);
37
- return this;
38
- }
39
- build() {
40
- const context = this.params.get("context");
41
- const search = this.params.get("search");
42
- const limit = this.params.get("limit");
43
- const apiKey = process.env.OPENFDA_API_KEY;
44
- if (!context || !search || !limit) {
45
- throw new Error("Missing required parameters: context, search, or limit");
46
- }
47
- return `${this.url}${context}.json?api_key=${apiKey}&search=${search}&limit=${limit}`;
48
- }
49
- }
@@ -1,178 +0,0 @@
1
- /*
2
- * Copyright (c) 2025 Ythalo Saldanha
3
- * Licensed under the MIT License
4
- */
5
- const DEFAULT_CONFIG = {
6
- maxRetries: 3,
7
- retryDelay: 1000, // 1 second
8
- timeout: 30000, // 30 seconds
9
- };
10
- // Helper function to determine if error is retryable
11
- function isRetryableError(error) {
12
- // Network errors, timeouts, and 5xx server errors are retryable
13
- if (error.name === "TypeError" && error.message.includes("fetch"))
14
- return true;
15
- if (error.name === "AbortError")
16
- return true;
17
- if (error.status >= 500 && error.status <= 599)
18
- return true;
19
- if (error.status === 429)
20
- return true; // Rate limit
21
- return false;
22
- }
23
- // Sleep utility for retry delays
24
- function sleep(ms) {
25
- return new Promise((resolve) => setTimeout(resolve, ms));
26
- }
27
- // Enhanced OpenFDA request function
28
- async function makeOpenFDARequest(url, config = {}) {
29
- const { maxRetries, retryDelay, timeout } = { ...DEFAULT_CONFIG, ...config };
30
- const headers = {
31
- "User-Agent": "@ythalorossy/openfda",
32
- Accept: "application/json",
33
- };
34
- let lastError = null;
35
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
36
- try {
37
- // Create abort controller for timeout handling
38
- const controller = new AbortController();
39
- const timeoutId = setTimeout(() => controller.abort(), timeout);
40
- console.log(`Making OpenFDA request (attempt ${attempt + 1}/${maxRetries + 1}): ${url}`);
41
- const response = await fetch(url, {
42
- headers,
43
- signal: controller.signal,
44
- });
45
- clearTimeout(timeoutId);
46
- // Handle HTTP errors with OpenFDA-specific context
47
- if (!response.ok) {
48
- const errorText = await response
49
- .text()
50
- .catch(() => "Unable to read error response");
51
- const httpError = {
52
- type: "http",
53
- message: `HTTP ${response.status}: ${response.statusText}`,
54
- status: response.status,
55
- details: errorText,
56
- };
57
- console.error(`OpenFDA HTTP Error (${response.status}):`, {
58
- url,
59
- status: response.status,
60
- statusText: response.statusText,
61
- errorText: errorText.substring(0, 200), // Truncate long error messages
62
- });
63
- // OpenFDA-specific status code handling
64
- switch (response.status) {
65
- case 400:
66
- httpError.message = `Bad Request: Invalid search query or parameters`;
67
- break;
68
- case 401:
69
- httpError.message = `Unauthorized: Invalid or missing API key`;
70
- break;
71
- case 403:
72
- httpError.message = `Forbidden: API key may be invalid or quota exceeded`;
73
- break;
74
- case 404:
75
- httpError.message = `Not Found: No results found for the specified query`;
76
- break;
77
- case 429:
78
- httpError.message = `Rate Limited: Too many requests. Retrying...`;
79
- break;
80
- case 500:
81
- httpError.message = `Server Error: OpenFDA service is experiencing issues`;
82
- break;
83
- default:
84
- httpError.message = `HTTP Error ${response.status}: ${response.statusText}`;
85
- }
86
- lastError = httpError;
87
- // Don't retry client errors (4xx) except rate limiting
88
- if (response.status >= 400 &&
89
- response.status < 500 &&
90
- response.status !== 429) {
91
- break;
92
- }
93
- // Retry server errors and rate limits
94
- if (attempt < maxRetries &&
95
- isRetryableError({ status: response.status })) {
96
- const delay = retryDelay * Math.pow(2, attempt); // Exponential backoff
97
- console.log(`Retrying in ${delay}ms...`);
98
- await sleep(delay);
99
- continue;
100
- }
101
- break;
102
- }
103
- // Parse JSON response
104
- let parsedData;
105
- try {
106
- parsedData = await response.json();
107
- }
108
- catch (parseError) {
109
- const parsingError = {
110
- type: "parsing",
111
- message: `Failed to parse JSON response: ${parseError instanceof Error
112
- ? parseError.message
113
- : "Unknown parsing error"}`,
114
- details: parseError,
115
- };
116
- console.error("OpenFDA JSON Parsing Error:", {
117
- url,
118
- parseError: parseError instanceof Error ? parseError.message : parseError,
119
- });
120
- lastError = parsingError;
121
- break; // Don't retry parsing errors
122
- }
123
- // Check for empty response
124
- if (!parsedData) {
125
- const emptyError = {
126
- type: "empty_response",
127
- message: "Received empty response from OpenFDA API",
128
- };
129
- lastError = emptyError;
130
- break;
131
- }
132
- console.log(`OpenFDA request successful on attempt ${attempt + 1}`);
133
- return { data: parsedData, error: null };
134
- }
135
- catch (error) {
136
- // Handle network errors, timeouts, and other fetch errors
137
- let networkError;
138
- if (error.name === "AbortError") {
139
- networkError = {
140
- type: "timeout",
141
- message: `Request timeout after ${timeout}ms`,
142
- details: error,
143
- };
144
- }
145
- else if (error instanceof TypeError &&
146
- error.message.includes("fetch")) {
147
- networkError = {
148
- type: "network",
149
- message: `Network error: Unable to connect to OpenFDA API`,
150
- details: error.message,
151
- };
152
- }
153
- else {
154
- networkError = {
155
- type: "unknown",
156
- message: `Unexpected error: ${error.message || "Unknown error occurred"}`,
157
- details: error,
158
- };
159
- }
160
- console.error(`OpenFDA Request Error (attempt ${attempt + 1}):`, {
161
- url,
162
- error: error.message,
163
- type: error.name,
164
- });
165
- lastError = networkError;
166
- // Retry network errors and timeouts
167
- if (attempt < maxRetries && isRetryableError(error)) {
168
- const delay = retryDelay * Math.pow(2, attempt); // Exponential backoff
169
- console.log(`Network error, retrying in ${delay}ms...`);
170
- await sleep(delay);
171
- continue;
172
- }
173
- break;
174
- }
175
- }
176
- return { data: null, error: lastError };
177
- }
178
- export { makeOpenFDARequest };
package/bin/types.js DELETED
@@ -1,5 +0,0 @@
1
- /*
2
- * Copyright (c) 2025 Ythalo Saldanha
3
- * Licensed under the MIT License
4
- */
5
- export {};