fda-mcp-server 0.0.1
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/LICENSE +21 -0
- package/README.md +145 -0
- package/dist/db.d.ts +105 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +288 -0
- package/dist/db.js.map +1 -0
- package/dist/fda-client.d.ts +92 -0
- package/dist/fda-client.d.ts.map +1 -0
- package/dist/fda-client.js +262 -0
- package/dist/fda-client.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +717 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jasen Carroll
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# fda-mcp-server
|
|
2
|
+
|
|
3
|
+
Query FDA Warning Letters, Form 483 Inspections, and Recalls directly from Claude. Built for quality engineers, regulatory professionals, and compliance teams working in FDA-regulated industries.
|
|
4
|
+
|
|
5
|
+
No API key required for core tools. Just install and start asking questions.
|
|
6
|
+
|
|
7
|
+
## What You Can Do
|
|
8
|
+
|
|
9
|
+
**Ask Claude things like:**
|
|
10
|
+
|
|
11
|
+
- "Show me recent warning letters from CDRH mentioning software validation"
|
|
12
|
+
- "Find Class I device recalls from the last 6 months"
|
|
13
|
+
- "Pull up the enforcement profile for Siemens Healthcare"
|
|
14
|
+
- "What 483 inspections happened in New Jersey for medical devices?"
|
|
15
|
+
|
|
16
|
+
**With the enriched dataset (API key required):**
|
|
17
|
+
|
|
18
|
+
- "Run a CSA risk assessment on Fresenius Kabi"
|
|
19
|
+
- "Generate a supplier qualification report for Baxter International"
|
|
20
|
+
- "Analyze citation patterns for CFR Part 820"
|
|
21
|
+
|
|
22
|
+
## Tools
|
|
23
|
+
|
|
24
|
+
### Free (live FDA data)
|
|
25
|
+
|
|
26
|
+
| Tool | What it does |
|
|
27
|
+
|------|-------------|
|
|
28
|
+
| `search_warning_letters` | Search warning letters by company, keyword, issuing office, or date range |
|
|
29
|
+
| `search_483_inspections` | Search Form 483 observations by firm, state, FDA center, or project area |
|
|
30
|
+
| `search_recalls` | Search recalls by product type, classification, firm, or date |
|
|
31
|
+
| `company_enforcement_profile` | Warning letters + 483s + recalls for a single company in one view |
|
|
32
|
+
|
|
33
|
+
### Enriched (curated CSA dataset, API key required)
|
|
34
|
+
|
|
35
|
+
| Tool | What it does |
|
|
36
|
+
|------|-------------|
|
|
37
|
+
| `csa_risk_assessment` | Risk score with 4.2x escalation multiplier, failure modes, citation breakdown |
|
|
38
|
+
| `citation_analysis` | Citation patterns by CFR part, section, and category with CSA-relevant percentages |
|
|
39
|
+
| `supplier_qualification_report` | Audit-ready qualification brief with risk level and go/no-go recommendation |
|
|
40
|
+
|
|
41
|
+
The enriched tools are powered by a curated dataset of 1,047 warning letters, 1,800 Form 483s, 3,400 extracted citations, and 157 failure modes — all classified for CSA relevance. This is especially valuable for CDRH-regulated firms navigating the QMSR transition.
|
|
42
|
+
|
|
43
|
+
## Setup
|
|
44
|
+
|
|
45
|
+
### Prerequisites
|
|
46
|
+
|
|
47
|
+
Install [Node.js](https://nodejs.org/) (v18 or later). Download the LTS version from [nodejs.org](https://nodejs.org/) and run the installer.
|
|
48
|
+
|
|
49
|
+
To check if it's already installed, open Terminal (Mac) or Command Prompt (Windows) and type:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
node --version
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Claude Desktop
|
|
56
|
+
|
|
57
|
+
1. Open Claude Desktop
|
|
58
|
+
2. Click the **gear icon** (Settings) → **Developer** → **Edit Config**
|
|
59
|
+
3. Paste this into the file that opens:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"fda": {
|
|
65
|
+
"command": "npx",
|
|
66
|
+
"args": ["-y", "fda-mcp-server"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
4. Save the file and **restart Claude Desktop**
|
|
73
|
+
5. Start a new conversation — you should see a hammer icon indicating FDA tools are available
|
|
74
|
+
|
|
75
|
+
That's it. You can now ask Claude about FDA warning letters, 483s, and recalls.
|
|
76
|
+
|
|
77
|
+
### Enabling Enriched Tools
|
|
78
|
+
|
|
79
|
+
To unlock CSA risk assessments, citation analysis, and supplier qualification reports, add your API key:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"fda": {
|
|
85
|
+
"command": "npx",
|
|
86
|
+
"args": ["-y", "fda-mcp-server"],
|
|
87
|
+
"env": {
|
|
88
|
+
"FDA_API_KEY": "your-api-key-here"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Get an API key at [jasenc.dev/fda-mcp](https://jasenc.dev/fda-mcp).
|
|
96
|
+
|
|
97
|
+
### Claude Code
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
claude mcp add fda -- npx -y fda-mcp-server
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
With enriched tools:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
claude mcp add fda -e FDA_API_KEY=your-api-key-here -- npx -y fda-mcp-server
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Data Sources
|
|
110
|
+
|
|
111
|
+
All core tool data comes from public FDA sources — no authentication required:
|
|
112
|
+
|
|
113
|
+
- **Warning Letters** — FDA.gov (~3,300+ records, updated continuously)
|
|
114
|
+
- **Form 483 Inspections** — FDA.gov FOIA Reading Room
|
|
115
|
+
- **Recalls** — openFDA API (drugs, devices, food)
|
|
116
|
+
|
|
117
|
+
Enriched tool data comes from a curated PostgreSQL database maintained at [fda-csa-data](https://github.com/jasencarroll/fda-csa-data). The dataset classifies each warning letter and 483 observation for CSA relevance based on CFR citations (21 CFR 211.68, 820.70(i), 820.30(g), Part 11) and narrative keywords (software validation, data integrity, electronic records, audit trail, etc.).
|
|
118
|
+
|
|
119
|
+
## Self-Hosting
|
|
120
|
+
|
|
121
|
+
If you want to run the enriched tools against your own database instead of using an API key:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"mcpServers": {
|
|
126
|
+
"fda": {
|
|
127
|
+
"command": "npx",
|
|
128
|
+
"args": ["-y", "fda-mcp-server"],
|
|
129
|
+
"env": {
|
|
130
|
+
"DATABASE_URL": "postgresql://localhost:5432/fda-csa-data"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
See [fda-csa-data](https://github.com/jasencarroll/fda-csa-data) for the database dump and restore instructions.
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
|
142
|
+
|
|
143
|
+
## Author
|
|
144
|
+
|
|
145
|
+
[Jasen Carroll](https://jasenc.dev) — Quality engineering + software, FDA-regulated industries.
|
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional PostgreSQL connection for enriched CSA dataset.
|
|
3
|
+
*
|
|
4
|
+
* When DATABASE_URL is set, enables premium tools:
|
|
5
|
+
* - csa_risk_assessment
|
|
6
|
+
* - citation_analysis
|
|
7
|
+
* - supplier_qualification_report
|
|
8
|
+
*
|
|
9
|
+
* API key gating:
|
|
10
|
+
* - FDA_MCP_API_KEY env var on the server side sets the required key
|
|
11
|
+
* - FDA_API_KEY env var on the client side must match
|
|
12
|
+
* - If FDA_MCP_API_KEY is not set, enriched tools are open (local/dev use)
|
|
13
|
+
*
|
|
14
|
+
* Schema: github.com/jasencarroll/fda-csa-data
|
|
15
|
+
*/
|
|
16
|
+
import pg from "pg";
|
|
17
|
+
export declare function getPool(): pg.Pool | null;
|
|
18
|
+
export declare function isEnrichedAvailable(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Validate API key for enriched tool access.
|
|
21
|
+
* If FDA_MCP_API_KEY is set on the server, FDA_API_KEY must match.
|
|
22
|
+
* If FDA_MCP_API_KEY is not set, access is open (local dev mode).
|
|
23
|
+
*/
|
|
24
|
+
export declare function validateApiKey(): {
|
|
25
|
+
valid: boolean;
|
|
26
|
+
message?: string;
|
|
27
|
+
};
|
|
28
|
+
export declare function testConnection(): Promise<boolean>;
|
|
29
|
+
export interface CSARiskResult {
|
|
30
|
+
company_name: string;
|
|
31
|
+
warning_letter_count: number;
|
|
32
|
+
csa_warning_letter_count: number;
|
|
33
|
+
total_citations: number;
|
|
34
|
+
csa_citations: number;
|
|
35
|
+
inspection_count: number;
|
|
36
|
+
csa_inspection_count: number;
|
|
37
|
+
failure_modes: string[];
|
|
38
|
+
top_cfr_citations: {
|
|
39
|
+
cfr_full: string;
|
|
40
|
+
category: string;
|
|
41
|
+
count: number;
|
|
42
|
+
}[];
|
|
43
|
+
risk_score: number;
|
|
44
|
+
risk_level: "LOW" | "MODERATE" | "HIGH" | "CRITICAL";
|
|
45
|
+
escalation_multiplier: number;
|
|
46
|
+
}
|
|
47
|
+
export declare function csaRiskAssessment(companyName: string): Promise<CSARiskResult | null>;
|
|
48
|
+
export interface CitationAnalysisResult {
|
|
49
|
+
total_citations: number;
|
|
50
|
+
by_type: {
|
|
51
|
+
type: string;
|
|
52
|
+
count: number;
|
|
53
|
+
}[];
|
|
54
|
+
by_cfr_part: {
|
|
55
|
+
part: string;
|
|
56
|
+
count: number;
|
|
57
|
+
}[];
|
|
58
|
+
by_category: {
|
|
59
|
+
category: string;
|
|
60
|
+
count: number;
|
|
61
|
+
csa_relevant: boolean;
|
|
62
|
+
}[];
|
|
63
|
+
top_sections: {
|
|
64
|
+
cfr_full: string;
|
|
65
|
+
description: string;
|
|
66
|
+
count: number;
|
|
67
|
+
}[];
|
|
68
|
+
csa_percentage: number;
|
|
69
|
+
}
|
|
70
|
+
export declare function citationAnalysis(params: {
|
|
71
|
+
cfr_part?: string;
|
|
72
|
+
category?: string;
|
|
73
|
+
center?: string;
|
|
74
|
+
fiscal_year?: number;
|
|
75
|
+
}): Promise<CitationAnalysisResult | null>;
|
|
76
|
+
export interface SupplierQualResult {
|
|
77
|
+
company_name: string;
|
|
78
|
+
fei_numbers: string[];
|
|
79
|
+
warning_letters: {
|
|
80
|
+
issue_date: string;
|
|
81
|
+
center: string;
|
|
82
|
+
regime: string;
|
|
83
|
+
csa_relevant: boolean;
|
|
84
|
+
url: string;
|
|
85
|
+
}[];
|
|
86
|
+
inspections: {
|
|
87
|
+
inspection_end_date: string;
|
|
88
|
+
center: string;
|
|
89
|
+
product_area: string;
|
|
90
|
+
csa_relevant: boolean;
|
|
91
|
+
}[];
|
|
92
|
+
citation_summary: {
|
|
93
|
+
cfr_full: string;
|
|
94
|
+
category: string;
|
|
95
|
+
count: number;
|
|
96
|
+
}[];
|
|
97
|
+
failure_modes: {
|
|
98
|
+
mode: string;
|
|
99
|
+
confidence: string;
|
|
100
|
+
count: number;
|
|
101
|
+
}[];
|
|
102
|
+
csa_risk: CSARiskResult | null;
|
|
103
|
+
}
|
|
104
|
+
export declare function supplierQualification(companyName: string): Promise<SupplierQualResult | null>;
|
|
105
|
+
//# sourceMappingURL=db.d.ts.map
|
package/dist/db.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional PostgreSQL connection for enriched CSA dataset.
|
|
3
|
+
*
|
|
4
|
+
* When DATABASE_URL is set, enables premium tools:
|
|
5
|
+
* - csa_risk_assessment
|
|
6
|
+
* - citation_analysis
|
|
7
|
+
* - supplier_qualification_report
|
|
8
|
+
*
|
|
9
|
+
* API key gating:
|
|
10
|
+
* - FDA_MCP_API_KEY env var on the server side sets the required key
|
|
11
|
+
* - FDA_API_KEY env var on the client side must match
|
|
12
|
+
* - If FDA_MCP_API_KEY is not set, enriched tools are open (local/dev use)
|
|
13
|
+
*
|
|
14
|
+
* Schema: github.com/jasencarroll/fda-csa-data
|
|
15
|
+
*/
|
|
16
|
+
import pg from "pg";
|
|
17
|
+
const { Pool } = pg;
|
|
18
|
+
let pool = null;
|
|
19
|
+
export function getPool() {
|
|
20
|
+
if (pool)
|
|
21
|
+
return pool;
|
|
22
|
+
const connectionString = process.env.DATABASE_URL;
|
|
23
|
+
if (!connectionString)
|
|
24
|
+
return null;
|
|
25
|
+
pool = new Pool({
|
|
26
|
+
connectionString,
|
|
27
|
+
max: 5,
|
|
28
|
+
idleTimeoutMillis: 30000,
|
|
29
|
+
connectionTimeoutMillis: 5000,
|
|
30
|
+
});
|
|
31
|
+
pool.on("error", (err) => {
|
|
32
|
+
console.error("PostgreSQL pool error:", err.message);
|
|
33
|
+
});
|
|
34
|
+
return pool;
|
|
35
|
+
}
|
|
36
|
+
export function isEnrichedAvailable() {
|
|
37
|
+
return !!process.env.DATABASE_URL;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validate API key for enriched tool access.
|
|
41
|
+
* If FDA_MCP_API_KEY is set on the server, FDA_API_KEY must match.
|
|
42
|
+
* If FDA_MCP_API_KEY is not set, access is open (local dev mode).
|
|
43
|
+
*/
|
|
44
|
+
export function validateApiKey() {
|
|
45
|
+
const serverKey = process.env.FDA_MCP_API_KEY;
|
|
46
|
+
if (!serverKey)
|
|
47
|
+
return { valid: true }; // no key required (local mode)
|
|
48
|
+
const clientKey = process.env.FDA_API_KEY;
|
|
49
|
+
if (!clientKey) {
|
|
50
|
+
return {
|
|
51
|
+
valid: false,
|
|
52
|
+
message: "Enriched tools require an API key. Set FDA_API_KEY in your MCP client config.\n\nGet a key at: https://jasenc.dev/fda-mcp",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (clientKey !== serverKey) {
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
message: "Invalid API key. Check your FDA_API_KEY value.",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return { valid: true };
|
|
62
|
+
}
|
|
63
|
+
export async function testConnection() {
|
|
64
|
+
const p = getPool();
|
|
65
|
+
if (!p)
|
|
66
|
+
return false;
|
|
67
|
+
try {
|
|
68
|
+
await p.query("SELECT 1");
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export async function csaRiskAssessment(companyName) {
|
|
76
|
+
const p = getPool();
|
|
77
|
+
if (!p)
|
|
78
|
+
return null;
|
|
79
|
+
// Warning letter counts
|
|
80
|
+
const wlResult = await p.query(`SELECT
|
|
81
|
+
COUNT(*) AS total,
|
|
82
|
+
COUNT(*) FILTER (WHERE csa_relevant = true) AS csa_count
|
|
83
|
+
FROM warning_letter
|
|
84
|
+
WHERE LOWER(company_name) LIKE LOWER($1)`, [`%${companyName}%`]);
|
|
85
|
+
// Citation breakdown
|
|
86
|
+
const citResult = await p.query(`SELECT
|
|
87
|
+
COUNT(*) AS total,
|
|
88
|
+
COUNT(*) FILTER (WHERE c.normalized_category IN (
|
|
89
|
+
'software_validation', 'data_integrity', 'electronic_records',
|
|
90
|
+
'computer_systems', 'audit_trail', 'part_11'
|
|
91
|
+
)) AS csa_count
|
|
92
|
+
FROM citation c
|
|
93
|
+
JOIN warning_letter wl ON c.letter_id = wl.id
|
|
94
|
+
WHERE LOWER(wl.company_name) LIKE LOWER($1)`, [`%${companyName}%`]);
|
|
95
|
+
// Top CFR citations
|
|
96
|
+
const topCfr = await p.query(`SELECT c.cfr_full, c.normalized_category, COUNT(*) AS cnt
|
|
97
|
+
FROM citation c
|
|
98
|
+
JOIN warning_letter wl ON c.letter_id = wl.id
|
|
99
|
+
WHERE LOWER(wl.company_name) LIKE LOWER($1)
|
|
100
|
+
GROUP BY c.cfr_full, c.normalized_category
|
|
101
|
+
ORDER BY cnt DESC
|
|
102
|
+
LIMIT 10`, [`%${companyName}%`]);
|
|
103
|
+
// 483 inspection counts
|
|
104
|
+
const inspResult = await p.query(`SELECT
|
|
105
|
+
COUNT(*) AS total,
|
|
106
|
+
COUNT(*) FILTER (WHERE csa_relevant = true) AS csa_count
|
|
107
|
+
FROM observation_483
|
|
108
|
+
WHERE LOWER(company_name) LIKE LOWER($1)`, [`%${companyName}%`]);
|
|
109
|
+
// Failure modes
|
|
110
|
+
const fmResult = await p.query(`SELECT DISTINCT fm.mode
|
|
111
|
+
FROM failure_mode fm
|
|
112
|
+
JOIN warning_letter wl ON fm.letter_id = wl.id
|
|
113
|
+
WHERE LOWER(wl.company_name) LIKE LOWER($1)
|
|
114
|
+
ORDER BY fm.mode`, [`%${companyName}%`]);
|
|
115
|
+
const wlTotal = parseInt(wlResult.rows[0]?.total ?? "0");
|
|
116
|
+
const wlCsa = parseInt(wlResult.rows[0]?.csa_count ?? "0");
|
|
117
|
+
const citTotal = parseInt(citResult.rows[0]?.total ?? "0");
|
|
118
|
+
const citCsa = parseInt(citResult.rows[0]?.csa_count ?? "0");
|
|
119
|
+
const inspTotal = parseInt(inspResult.rows[0]?.total ?? "0");
|
|
120
|
+
const inspCsa = parseInt(inspResult.rows[0]?.csa_count ?? "0");
|
|
121
|
+
// Risk scoring: weighted composite
|
|
122
|
+
// CSA warning letters are 4.2x more significant (per dataset analysis)
|
|
123
|
+
const ESCALATION_MULTIPLIER = 4.2;
|
|
124
|
+
const score = wlCsa * 30 * ESCALATION_MULTIPLIER +
|
|
125
|
+
wlTotal * 10 +
|
|
126
|
+
citCsa * 5 +
|
|
127
|
+
inspCsa * 15 +
|
|
128
|
+
inspTotal * 3;
|
|
129
|
+
let riskLevel = "LOW";
|
|
130
|
+
if (score >= 500)
|
|
131
|
+
riskLevel = "CRITICAL";
|
|
132
|
+
else if (score >= 200)
|
|
133
|
+
riskLevel = "HIGH";
|
|
134
|
+
else if (score >= 50)
|
|
135
|
+
riskLevel = "MODERATE";
|
|
136
|
+
return {
|
|
137
|
+
company_name: companyName,
|
|
138
|
+
warning_letter_count: wlTotal,
|
|
139
|
+
csa_warning_letter_count: wlCsa,
|
|
140
|
+
total_citations: citTotal,
|
|
141
|
+
csa_citations: citCsa,
|
|
142
|
+
inspection_count: inspTotal,
|
|
143
|
+
csa_inspection_count: inspCsa,
|
|
144
|
+
failure_modes: fmResult.rows.map((r) => r.mode),
|
|
145
|
+
top_cfr_citations: topCfr.rows.map((r) => ({
|
|
146
|
+
cfr_full: r.cfr_full,
|
|
147
|
+
category: r.normalized_category,
|
|
148
|
+
count: parseInt(r.cnt),
|
|
149
|
+
})),
|
|
150
|
+
risk_score: Math.round(score),
|
|
151
|
+
risk_level: riskLevel,
|
|
152
|
+
escalation_multiplier: ESCALATION_MULTIPLIER,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
export async function citationAnalysis(params) {
|
|
156
|
+
const p = getPool();
|
|
157
|
+
if (!p)
|
|
158
|
+
return null;
|
|
159
|
+
const conditions = [];
|
|
160
|
+
const values = [];
|
|
161
|
+
let idx = 1;
|
|
162
|
+
if (params.cfr_part) {
|
|
163
|
+
conditions.push(`c.cfr_part = $${idx++}`);
|
|
164
|
+
values.push(params.cfr_part);
|
|
165
|
+
}
|
|
166
|
+
if (params.category) {
|
|
167
|
+
conditions.push(`c.normalized_category = $${idx++}`);
|
|
168
|
+
values.push(params.category);
|
|
169
|
+
}
|
|
170
|
+
if (params.center) {
|
|
171
|
+
conditions.push(`wl.center = $${idx++}`);
|
|
172
|
+
values.push(params.center);
|
|
173
|
+
}
|
|
174
|
+
if (params.fiscal_year) {
|
|
175
|
+
conditions.push(`wl.fiscal_year = $${idx++}`);
|
|
176
|
+
values.push(params.fiscal_year);
|
|
177
|
+
}
|
|
178
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
179
|
+
const totalResult = await p.query(`SELECT COUNT(*) AS total FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}`, values);
|
|
180
|
+
const byType = await p.query(`SELECT c.citation_type AS type, COUNT(*) AS cnt
|
|
181
|
+
FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}
|
|
182
|
+
GROUP BY c.citation_type ORDER BY cnt DESC`, values);
|
|
183
|
+
const byPart = await p.query(`SELECT c.cfr_part AS part, COUNT(*) AS cnt
|
|
184
|
+
FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}
|
|
185
|
+
GROUP BY c.cfr_part ORDER BY cnt DESC LIMIT 15`, values);
|
|
186
|
+
const byCategory = await p.query(`SELECT c.normalized_category AS category, COUNT(*) AS cnt,
|
|
187
|
+
CASE WHEN c.normalized_category IN (
|
|
188
|
+
'software_validation', 'data_integrity', 'electronic_records',
|
|
189
|
+
'computer_systems', 'audit_trail', 'part_11'
|
|
190
|
+
) THEN true ELSE false END AS csa_relevant
|
|
191
|
+
FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}
|
|
192
|
+
GROUP BY c.normalized_category ORDER BY cnt DESC`, values);
|
|
193
|
+
const topSections = await p.query(`SELECT c.cfr_full, c.normalized_category AS description, COUNT(*) AS cnt
|
|
194
|
+
FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}
|
|
195
|
+
GROUP BY c.cfr_full, c.normalized_category ORDER BY cnt DESC LIMIT 20`, values);
|
|
196
|
+
const total = parseInt(totalResult.rows[0]?.total ?? "0");
|
|
197
|
+
const csaCount = byCategory.rows
|
|
198
|
+
.filter((r) => r.csa_relevant)
|
|
199
|
+
.reduce((sum, r) => sum + parseInt(r.cnt), 0);
|
|
200
|
+
return {
|
|
201
|
+
total_citations: total,
|
|
202
|
+
by_type: byType.rows.map((r) => ({ type: r.type, count: parseInt(r.cnt) })),
|
|
203
|
+
by_cfr_part: byPart.rows.map((r) => ({ part: r.part, count: parseInt(r.cnt) })),
|
|
204
|
+
by_category: byCategory.rows.map((r) => ({
|
|
205
|
+
category: r.category,
|
|
206
|
+
count: parseInt(r.cnt),
|
|
207
|
+
csa_relevant: r.csa_relevant,
|
|
208
|
+
})),
|
|
209
|
+
top_sections: topSections.rows.map((r) => ({
|
|
210
|
+
cfr_full: r.cfr_full,
|
|
211
|
+
description: r.description,
|
|
212
|
+
count: parseInt(r.cnt),
|
|
213
|
+
})),
|
|
214
|
+
csa_percentage: total > 0 ? Math.round((csaCount / total) * 100 * 10) / 10 : 0,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
export async function supplierQualification(companyName) {
|
|
218
|
+
const p = getPool();
|
|
219
|
+
if (!p)
|
|
220
|
+
return null;
|
|
221
|
+
// FEI numbers
|
|
222
|
+
const feiResult = await p.query(`SELECT DISTINCT fei_number FROM (
|
|
223
|
+
SELECT fei_number FROM warning_letter WHERE LOWER(company_name) LIKE LOWER($1) AND fei_number IS NOT NULL
|
|
224
|
+
UNION
|
|
225
|
+
SELECT fei_number FROM observation_483 WHERE LOWER(company_name) LIKE LOWER($1) AND fei_number IS NOT NULL
|
|
226
|
+
) sub WHERE fei_number != ''`, [`%${companyName}%`]);
|
|
227
|
+
// Warning letters
|
|
228
|
+
const wlResult = await p.query(`SELECT
|
|
229
|
+
to_timestamp(issue_date) AS issue_ts,
|
|
230
|
+
center, regime, csa_relevant, url
|
|
231
|
+
FROM warning_letter
|
|
232
|
+
WHERE LOWER(company_name) LIKE LOWER($1)
|
|
233
|
+
ORDER BY issue_date DESC
|
|
234
|
+
LIMIT 20`, [`%${companyName}%`]);
|
|
235
|
+
// 483 inspections
|
|
236
|
+
const inspResult = await p.query(`SELECT inspection_end_date, center, product_area, csa_relevant
|
|
237
|
+
FROM observation_483
|
|
238
|
+
WHERE LOWER(company_name) LIKE LOWER($1)
|
|
239
|
+
ORDER BY inspection_end_date DESC
|
|
240
|
+
LIMIT 20`, [`%${companyName}%`]);
|
|
241
|
+
// Citation summary
|
|
242
|
+
const citResult = await p.query(`SELECT c.cfr_full, c.normalized_category, COUNT(*) AS cnt
|
|
243
|
+
FROM citation c
|
|
244
|
+
JOIN warning_letter wl ON c.letter_id = wl.id
|
|
245
|
+
WHERE LOWER(wl.company_name) LIKE LOWER($1)
|
|
246
|
+
GROUP BY c.cfr_full, c.normalized_category
|
|
247
|
+
ORDER BY cnt DESC
|
|
248
|
+
LIMIT 15`, [`%${companyName}%`]);
|
|
249
|
+
// Failure modes
|
|
250
|
+
const fmResult = await p.query(`SELECT fm.mode, fm.confidence, COUNT(*) AS cnt
|
|
251
|
+
FROM failure_mode fm
|
|
252
|
+
JOIN warning_letter wl ON fm.letter_id = wl.id
|
|
253
|
+
WHERE LOWER(wl.company_name) LIKE LOWER($1)
|
|
254
|
+
GROUP BY fm.mode, fm.confidence
|
|
255
|
+
ORDER BY cnt DESC`, [`%${companyName}%`]);
|
|
256
|
+
const risk = await csaRiskAssessment(companyName);
|
|
257
|
+
return {
|
|
258
|
+
company_name: companyName,
|
|
259
|
+
fei_numbers: feiResult.rows.map((r) => r.fei_number),
|
|
260
|
+
warning_letters: wlResult.rows.map((r) => ({
|
|
261
|
+
issue_date: r.issue_ts
|
|
262
|
+
? new Date(r.issue_ts).toISOString().split("T")[0]
|
|
263
|
+
: "",
|
|
264
|
+
center: r.center ?? "",
|
|
265
|
+
regime: r.regime ?? "",
|
|
266
|
+
csa_relevant: r.csa_relevant ?? false,
|
|
267
|
+
url: r.url ?? "",
|
|
268
|
+
})),
|
|
269
|
+
inspections: inspResult.rows.map((r) => ({
|
|
270
|
+
inspection_end_date: r.inspection_end_date ?? "",
|
|
271
|
+
center: r.center ?? "",
|
|
272
|
+
product_area: r.product_area ?? "",
|
|
273
|
+
csa_relevant: r.csa_relevant ?? false,
|
|
274
|
+
})),
|
|
275
|
+
citation_summary: citResult.rows.map((r) => ({
|
|
276
|
+
cfr_full: r.cfr_full,
|
|
277
|
+
category: r.normalized_category,
|
|
278
|
+
count: parseInt(r.cnt),
|
|
279
|
+
})),
|
|
280
|
+
failure_modes: fmResult.rows.map((r) => ({
|
|
281
|
+
mode: r.mode,
|
|
282
|
+
confidence: r.confidence,
|
|
283
|
+
count: parseInt(r.cnt),
|
|
284
|
+
})),
|
|
285
|
+
csa_risk: risk,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AAEpB,IAAI,IAAI,GAAmB,IAAI,CAAC;AAEhC,MAAM,UAAU,OAAO;IACrB,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAClD,IAAI,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAEnC,IAAI,GAAG,IAAI,IAAI,CAAC;QACd,gBAAgB;QAChB,GAAG,EAAE,CAAC;QACN,iBAAiB,EAAE,KAAK;QACxB,uBAAuB,EAAE,IAAI;KAC9B,CAAC,CAAC;IAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACvB,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AACpC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC9C,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,+BAA+B;IAEvE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IAC1C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,OAAO,EACL,2HAA2H;SAC9H,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,gDAAgD;SAC1D,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACrB,IAAI,CAAC;QACH,MAAM,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAmBD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,WAAmB;IAEnB,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpB,wBAAwB;IACxB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,KAAK,CAC5B;;;;8CAI0C,EAC1C,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,qBAAqB;IACrB,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,KAAK,CAC7B;;;;;;;;iDAQ6C,EAC7C,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,oBAAoB;IACpB,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,KAAK,CAC1B;;;;;;cAMU,EACV,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,wBAAwB;IACxB,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,KAAK,CAC9B;;;;8CAI0C,EAC1C,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,gBAAgB;IAChB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,KAAK,CAC5B;;;;sBAIkB,EAClB,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;IACzD,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,GAAG,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,GAAG,CAAC,CAAC;IAC7D,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,GAAG,CAAC,CAAC;IAE/D,mCAAmC;IACnC,uEAAuE;IACvE,MAAM,qBAAqB,GAAG,GAAG,CAAC;IAClC,MAAM,KAAK,GACT,KAAK,GAAG,EAAE,GAAG,qBAAqB;QAClC,OAAO,GAAG,EAAE;QACZ,MAAM,GAAG,CAAC;QACV,OAAO,GAAG,EAAE;QACZ,SAAS,GAAG,CAAC,CAAC;IAEhB,IAAI,SAAS,GAAgC,KAAK,CAAC;IACnD,IAAI,KAAK,IAAI,GAAG;QAAE,SAAS,GAAG,UAAU,CAAC;SACpC,IAAI,KAAK,IAAI,GAAG;QAAE,SAAS,GAAG,MAAM,CAAC;SACrC,IAAI,KAAK,IAAI,EAAE;QAAE,SAAS,GAAG,UAAU,CAAC;IAE7C,OAAO;QACL,YAAY,EAAE,WAAW;QACzB,oBAAoB,EAAE,OAAO;QAC7B,wBAAwB,EAAE,KAAK;QAC/B,eAAe,EAAE,QAAQ;QACzB,aAAa,EAAE,MAAM;QACrB,gBAAgB,EAAE,SAAS;QAC3B,oBAAoB,EAAE,OAAO;QAC7B,aAAa,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACpD,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC9C,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,QAAQ,EAAE,CAAC,CAAC,mBAAmB;YAC/B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;SACvB,CAAC,CAAC;QACH,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAC7B,UAAU,EAAE,SAAS;QACrB,qBAAqB,EAAE,qBAAqB;KAC7C,CAAC;AACJ,CAAC;AAWD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAKtC;IACC,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpB,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,MAAM,GAAU,EAAE,CAAC;IACzB,IAAI,GAAG,GAAG,CAAC,CAAC;IAEZ,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,UAAU,CAAC,IAAI,CAAC,iBAAiB,GAAG,EAAE,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,UAAU,CAAC,IAAI,CAAC,4BAA4B,GAAG,EAAE,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,UAAU,CAAC,IAAI,CAAC,gBAAgB,GAAG,EAAE,EAAE,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,UAAU,CAAC,IAAI,CAAC,qBAAqB,GAAG,EAAE,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/E,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,KAAK,CAC/B,0FAA0F,KAAK,EAAE,EACjG,MAAM,CACP,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,KAAK,CAC1B;qEACiE,KAAK;gDAC1B,EAC5C,MAAM,CACP,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,KAAK,CAC1B;qEACiE,KAAK;oDACtB,EAChD,MAAM,CACP,CAAC;IAEF,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,KAAK,CAC9B;;;;;qEAKiE,KAAK;sDACpB,EAClD,MAAM,CACP,CAAC;IAEF,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,KAAK,CAC/B;qEACiE,KAAK;2EACC,EACvE,MAAM,CACP,CAAC;IAEF,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI;SAC7B,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;SAClC,MAAM,CAAC,CAAC,GAAW,EAAE,CAAM,EAAE,EAAE,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAE7D,OAAO;QACL,eAAe,EAAE,KAAK;QACtB,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChF,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpF,WAAW,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC5C,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;YACtB,YAAY,EAAE,CAAC,CAAC,YAAY;SAC7B,CAAC,CAAC;QACH,YAAY,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC9C,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;SACvB,CAAC,CAAC;QACH,cAAc,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;KAC/E,CAAC;AACJ,CAAC;AAuBD,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,WAAmB;IAEnB,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpB,cAAc;IACd,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,KAAK,CAC7B;;;;kCAI8B,EAC9B,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,kBAAkB;IAClB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,KAAK,CAC5B;;;;;;cAMU,EACV,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,kBAAkB;IAClB,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,KAAK,CAC9B;;;;cAIU,EACV,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,mBAAmB;IACnB,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,KAAK,CAC7B;;;;;;cAMU,EACV,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,gBAAgB;IAChB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,KAAK,CAC5B;;;;;uBAKmB,EACnB,CAAC,IAAI,WAAW,GAAG,CAAC,CACrB,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAElD,OAAO;QACL,YAAY,EAAE,WAAW;QACzB,WAAW,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;QACzD,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC9C,UAAU,EAAE,CAAC,CAAC,QAAQ;gBACpB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAClD,CAAC,CAAC,EAAE;YACN,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;YACtB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;YACtB,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,KAAK;YACrC,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE;SACjB,CAAC,CAAC;QACH,WAAW,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC5C,mBAAmB,EAAE,CAAC,CAAC,mBAAmB,IAAI,EAAE;YAChD,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;YACtB,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,EAAE;YAClC,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,KAAK;SACtC,CAAC,CAAC;QACH,gBAAgB,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAChD,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,QAAQ,EAAE,CAAC,CAAC,mBAAmB;YAC/B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;SACvB,CAAC,CAAC;QACH,aAAa,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC5C,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;SACvB,CAAC,CAAC;QACH,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FDA API Client
|
|
3
|
+
*
|
|
4
|
+
* Wraps FDA public data sources:
|
|
5
|
+
* 1. FDA.gov Warning Letters (Drupal DataTables / Solr)
|
|
6
|
+
* 2. FDA.gov 483 Inspection Observations (FOIA Reading Room)
|
|
7
|
+
* 3. openFDA Enforcement/Recalls API
|
|
8
|
+
*/
|
|
9
|
+
export interface WarningLetter {
|
|
10
|
+
posted_date: string;
|
|
11
|
+
issue_date: string;
|
|
12
|
+
company_name: string;
|
|
13
|
+
issuing_office: string;
|
|
14
|
+
subject: string;
|
|
15
|
+
response_letter: string;
|
|
16
|
+
closeout_letter: string;
|
|
17
|
+
excerpt: string;
|
|
18
|
+
url: string;
|
|
19
|
+
}
|
|
20
|
+
export interface EnforcementRecord {
|
|
21
|
+
recall_number: string;
|
|
22
|
+
status: string;
|
|
23
|
+
classification: string;
|
|
24
|
+
product_type: string;
|
|
25
|
+
recalling_firm: string;
|
|
26
|
+
city: string;
|
|
27
|
+
state: string;
|
|
28
|
+
country: string;
|
|
29
|
+
reason_for_recall: string;
|
|
30
|
+
product_description: string;
|
|
31
|
+
product_quantity: string;
|
|
32
|
+
distribution_pattern: string;
|
|
33
|
+
voluntary_mandated: string;
|
|
34
|
+
recall_initiation_date: string;
|
|
35
|
+
report_date: string;
|
|
36
|
+
center_classification_date: string;
|
|
37
|
+
termination_date: string;
|
|
38
|
+
event_id: string;
|
|
39
|
+
}
|
|
40
|
+
export interface InspectionRecord {
|
|
41
|
+
firm_name: string;
|
|
42
|
+
fei_number: string;
|
|
43
|
+
city: string;
|
|
44
|
+
state: string;
|
|
45
|
+
country: string;
|
|
46
|
+
zip_code: string;
|
|
47
|
+
inspection_end_date: string;
|
|
48
|
+
classification: string;
|
|
49
|
+
center: string;
|
|
50
|
+
project_area: string;
|
|
51
|
+
posted_citations: string;
|
|
52
|
+
url?: string;
|
|
53
|
+
}
|
|
54
|
+
export declare function searchWarningLetters(params: {
|
|
55
|
+
search?: string;
|
|
56
|
+
issuing_office?: string;
|
|
57
|
+
limit?: number;
|
|
58
|
+
offset?: number;
|
|
59
|
+
date_from?: string;
|
|
60
|
+
date_to?: string;
|
|
61
|
+
}): Promise<{
|
|
62
|
+
total: number;
|
|
63
|
+
letters: WarningLetter[];
|
|
64
|
+
}>;
|
|
65
|
+
export declare function searchInspections(params: {
|
|
66
|
+
firm_name?: string;
|
|
67
|
+
state?: string;
|
|
68
|
+
center?: string;
|
|
69
|
+
project_area?: string;
|
|
70
|
+
limit?: number;
|
|
71
|
+
offset?: number;
|
|
72
|
+
date_from?: string;
|
|
73
|
+
date_to?: string;
|
|
74
|
+
}): Promise<{
|
|
75
|
+
total: number;
|
|
76
|
+
inspections: InspectionRecord[];
|
|
77
|
+
}>;
|
|
78
|
+
export declare function searchEnforcement(params: {
|
|
79
|
+
search?: string;
|
|
80
|
+
product_type?: "drug" | "device" | "food";
|
|
81
|
+
classification?: "Class I" | "Class II" | "Class III";
|
|
82
|
+
state?: string;
|
|
83
|
+
recalling_firm?: string;
|
|
84
|
+
date_from?: string;
|
|
85
|
+
date_to?: string;
|
|
86
|
+
limit?: number;
|
|
87
|
+
skip?: number;
|
|
88
|
+
}): Promise<{
|
|
89
|
+
total: number;
|
|
90
|
+
records: EnforcementRecord[];
|
|
91
|
+
}>;
|
|
92
|
+
//# sourceMappingURL=fda-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fda-client.d.ts","sourceRoot":"","sources":["../src/fda-client.ts"],"names":[],"mappings":"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"}
|