@erosolaraijs/cure 1.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/LICENSE +21 -0
  2. package/README.md +120 -0
  3. package/dist/bin/cure.d.ts +10 -0
  4. package/dist/bin/cure.d.ts.map +1 -0
  5. package/dist/bin/cure.js +169 -0
  6. package/dist/bin/cure.js.map +1 -0
  7. package/dist/capabilities/cancerTreatmentCapability.d.ts +167 -0
  8. package/dist/capabilities/cancerTreatmentCapability.d.ts.map +1 -0
  9. package/dist/capabilities/cancerTreatmentCapability.js +912 -0
  10. package/dist/capabilities/cancerTreatmentCapability.js.map +1 -0
  11. package/dist/capabilities/index.d.ts +2 -0
  12. package/dist/capabilities/index.d.ts.map +1 -0
  13. package/dist/capabilities/index.js +3 -0
  14. package/dist/capabilities/index.js.map +1 -0
  15. package/dist/compliance/hipaa.d.ts +337 -0
  16. package/dist/compliance/hipaa.d.ts.map +1 -0
  17. package/dist/compliance/hipaa.js +929 -0
  18. package/dist/compliance/hipaa.js.map +1 -0
  19. package/dist/examples/cancerTreatmentDemo.d.ts +21 -0
  20. package/dist/examples/cancerTreatmentDemo.d.ts.map +1 -0
  21. package/dist/examples/cancerTreatmentDemo.js +216 -0
  22. package/dist/examples/cancerTreatmentDemo.js.map +1 -0
  23. package/dist/integrations/clinicalTrials/clinicalTrialsGov.d.ts +265 -0
  24. package/dist/integrations/clinicalTrials/clinicalTrialsGov.d.ts.map +1 -0
  25. package/dist/integrations/clinicalTrials/clinicalTrialsGov.js +808 -0
  26. package/dist/integrations/clinicalTrials/clinicalTrialsGov.js.map +1 -0
  27. package/dist/integrations/ehr/fhir.d.ts +455 -0
  28. package/dist/integrations/ehr/fhir.d.ts.map +1 -0
  29. package/dist/integrations/ehr/fhir.js +859 -0
  30. package/dist/integrations/ehr/fhir.js.map +1 -0
  31. package/dist/integrations/genomics/genomicPlatforms.d.ts +362 -0
  32. package/dist/integrations/genomics/genomicPlatforms.d.ts.map +1 -0
  33. package/dist/integrations/genomics/genomicPlatforms.js +1079 -0
  34. package/dist/integrations/genomics/genomicPlatforms.js.map +1 -0
  35. package/package.json +52 -0
  36. package/src/bin/cure.ts +182 -0
  37. package/src/capabilities/cancerTreatmentCapability.ts +1161 -0
  38. package/src/capabilities/index.ts +2 -0
  39. package/src/compliance/hipaa.ts +1365 -0
  40. package/src/examples/cancerTreatmentDemo.ts +241 -0
  41. package/src/integrations/clinicalTrials/clinicalTrialsGov.ts +1143 -0
  42. package/src/integrations/ehr/fhir.ts +1304 -0
  43. package/src/integrations/genomics/genomicPlatforms.ts +1480 -0
  44. package/src/ml/outcomePredictor.ts +1301 -0
  45. package/src/safety/drugInteractions.ts +942 -0
  46. package/src/validation/retrospectiveValidator.ts +887 -0
@@ -0,0 +1,1079 @@
1
+ /**
2
+ * Genomic Testing Platform Integrations
3
+ *
4
+ * Connects to major genomic testing providers:
5
+ * - Foundation Medicine (FoundationOne CDx, FoundationOne Liquid CDx)
6
+ * - Guardant Health (Guardant360, GuardantOMNI)
7
+ * - Tempus (xT, xF, xR panels)
8
+ * - Caris Life Sciences (Caris Molecular Intelligence)
9
+ * - NeoGenomics
10
+ *
11
+ * All data handling complies with HIPAA and CAP/CLIA requirements.
12
+ */
13
+ import { EventEmitter } from 'events';
14
+ // ═══════════════════════════════════════════════════════════════════════════════
15
+ // ABSTRACT GENOMIC PLATFORM CLIENT
16
+ // ═══════════════════════════════════════════════════════════════════════════════
17
+ export class GenomicPlatformClient extends EventEmitter {
18
+ config;
19
+ accessToken;
20
+ tokenExpiry;
21
+ constructor(config) {
22
+ super();
23
+ this.config = {
24
+ timeout: 60000,
25
+ ...config
26
+ };
27
+ }
28
+ async httpRequest(url, options) {
29
+ const response = await fetch(url, {
30
+ method: options.method,
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ ...options.headers
34
+ },
35
+ body: options.body,
36
+ signal: AbortSignal.timeout(this.config.timeout || 60000)
37
+ });
38
+ if (!response.ok) {
39
+ const errorBody = await response.text();
40
+ throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorBody}`);
41
+ }
42
+ return await response.text();
43
+ }
44
+ async getAuthHeaders() {
45
+ if (this.tokenExpiry && new Date() >= this.tokenExpiry) {
46
+ await this.authenticate();
47
+ }
48
+ if (this.accessToken) {
49
+ return { 'Authorization': `Bearer ${this.accessToken}` };
50
+ }
51
+ if (this.config.apiKey) {
52
+ return { 'X-API-Key': this.config.apiKey };
53
+ }
54
+ return {};
55
+ }
56
+ }
57
+ // ═══════════════════════════════════════════════════════════════════════════════
58
+ // FOUNDATION MEDICINE CLIENT
59
+ // ═══════════════════════════════════════════════════════════════════════════════
60
+ export class FoundationMedicineClient extends GenomicPlatformClient {
61
+ constructor(config) {
62
+ super({ ...config, platform: 'foundation-medicine' });
63
+ }
64
+ async authenticate() {
65
+ const tokenUrl = `${this.config.apiBaseUrl}/oauth/token`;
66
+ const response = await this.httpRequest(tokenUrl, {
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/x-www-form-urlencoded'
70
+ },
71
+ body: new URLSearchParams({
72
+ grant_type: 'client_credentials',
73
+ client_id: this.config.clientId || '',
74
+ client_secret: this.config.clientSecret || ''
75
+ }).toString()
76
+ });
77
+ const data = JSON.parse(response);
78
+ this.accessToken = data.access_token;
79
+ this.tokenExpiry = new Date(Date.now() + (data.expires_in * 1000));
80
+ }
81
+ async submitOrder(order) {
82
+ const url = `${this.config.apiBaseUrl}/v1/orders`;
83
+ const fmiOrder = this.mapToFMIOrder(order);
84
+ const response = await this.httpRequest(url, {
85
+ method: 'POST',
86
+ headers: await this.getAuthHeaders(),
87
+ body: JSON.stringify(fmiOrder)
88
+ });
89
+ const result = JSON.parse(response);
90
+ return {
91
+ orderId: result.orderId || result.id,
92
+ status: result.status || 'submitted'
93
+ };
94
+ }
95
+ async getOrderStatus(orderId) {
96
+ const url = `${this.config.apiBaseUrl}/v1/orders/${orderId}`;
97
+ const response = await this.httpRequest(url, {
98
+ method: 'GET',
99
+ headers: await this.getAuthHeaders()
100
+ });
101
+ const fmiOrder = JSON.parse(response);
102
+ return this.mapFromFMIOrder(fmiOrder);
103
+ }
104
+ async getResults(orderId) {
105
+ const url = `${this.config.apiBaseUrl}/v1/orders/${orderId}/report`;
106
+ const response = await this.httpRequest(url, {
107
+ method: 'GET',
108
+ headers: await this.getAuthHeaders()
109
+ });
110
+ const fmiReport = JSON.parse(response);
111
+ return this.mapFromFMIReport(fmiReport, orderId);
112
+ }
113
+ async listPatientResults(patientId) {
114
+ const url = `${this.config.apiBaseUrl}/v1/patients/${patientId}/reports`;
115
+ const response = await this.httpRequest(url, {
116
+ method: 'GET',
117
+ headers: await this.getAuthHeaders()
118
+ });
119
+ const reports = JSON.parse(response);
120
+ return reports.map((r) => this.mapFromFMIReport(r, r.orderId));
121
+ }
122
+ /**
123
+ * Get FoundationOne CDx specific data
124
+ */
125
+ async getFoundationOneCDxReport(orderId) {
126
+ const result = await this.getResults(orderId);
127
+ return {
128
+ variants: result.variants,
129
+ cnvs: result.copyNumberAlterations,
130
+ fusions: result.fusions,
131
+ msi: result.signatures?.microsatelliteInstability || { status: 'MSS', method: 'NGS' },
132
+ tmb: result.signatures?.tumorMutationalBurden || { value: 0, unit: 'mutations/Mb', status: 'low', threshold: 10 },
133
+ loh: result.signatures?.lossOfHeterozygosity || { percentage: 0, status: 'low' },
134
+ therapies: result.therapyMatches
135
+ };
136
+ }
137
+ mapToFMIOrder(order) {
138
+ return {
139
+ externalOrderId: order.orderId,
140
+ patient: {
141
+ externalId: order.patientId
142
+ },
143
+ specimen: {
144
+ type: order.specimenType,
145
+ externalId: order.specimenId
146
+ },
147
+ test: {
148
+ code: this.mapPanelToFMICode(order.panelName)
149
+ },
150
+ diagnosis: {
151
+ icdCodes: order.icdCodes,
152
+ clinicalHistory: order.clinicalIndication
153
+ },
154
+ orderingPhysician: {
155
+ name: order.orderingPhysician.name,
156
+ npi: order.orderingPhysician.npi,
157
+ facility: order.orderingPhysician.facility
158
+ }
159
+ };
160
+ }
161
+ mapPanelToFMICode(panelName) {
162
+ const mapping = {
163
+ 'FoundationOne CDx': 'F1CDX',
164
+ 'FoundationOne Liquid CDx': 'F1LCDX',
165
+ 'FoundationOne Heme': 'F1HEME',
166
+ 'FoundationACT': 'FACT'
167
+ };
168
+ return mapping[panelName] || 'F1CDX';
169
+ }
170
+ mapFromFMIOrder(fmiOrder) {
171
+ return {
172
+ orderId: fmiOrder.externalOrderId || fmiOrder.id,
173
+ patientId: fmiOrder.patient?.externalId,
174
+ testType: 'comprehensive-genomic-profiling',
175
+ panelName: this.mapFMICodeToPanel(fmiOrder.test?.code),
176
+ specimenType: fmiOrder.specimen?.type || 'tissue',
177
+ specimenId: fmiOrder.specimen?.externalId,
178
+ orderDate: new Date(fmiOrder.createdAt),
179
+ clinicalIndication: fmiOrder.diagnosis?.clinicalHistory || '',
180
+ icdCodes: fmiOrder.diagnosis?.icdCodes || [],
181
+ orderingPhysician: {
182
+ name: fmiOrder.orderingPhysician?.name || '',
183
+ npi: fmiOrder.orderingPhysician?.npi || '',
184
+ facility: fmiOrder.orderingPhysician?.facility || ''
185
+ },
186
+ status: this.mapFMIStatus(fmiOrder.status)
187
+ };
188
+ }
189
+ mapFMICodeToPanel(code) {
190
+ const mapping = {
191
+ 'F1CDX': 'FoundationOne CDx',
192
+ 'F1LCDX': 'FoundationOne Liquid CDx',
193
+ 'F1HEME': 'FoundationOne Heme',
194
+ 'FACT': 'FoundationACT'
195
+ };
196
+ return mapping[code] || 'FoundationOne CDx';
197
+ }
198
+ mapFMIStatus(status) {
199
+ const mapping = {
200
+ 'ORDERED': 'ordered',
201
+ 'SPECIMEN_RECEIVED': 'specimen-received',
202
+ 'IN_PROCESS': 'in-process',
203
+ 'COMPLETE': 'completed',
204
+ 'FAILED': 'failed',
205
+ 'CANCELLED': 'cancelled'
206
+ };
207
+ return mapping[status] || 'ordered';
208
+ }
209
+ mapFromFMIReport(fmiReport, orderId) {
210
+ const variants = (fmiReport.shortVariants || []).map((v) => ({
211
+ gene: v.gene,
212
+ hgvsC: v.cdsEffect,
213
+ hgvsP: v.proteinEffect,
214
+ transcript: v.transcript,
215
+ variantType: this.mapVariantType(v.variantType),
216
+ variantAlleleFrequency: v.alleleFrequency || 0,
217
+ coverage: v.depth,
218
+ clinicalSignificance: this.mapPathogenicity(v.pathogenicity),
219
+ tier: this.mapTier(v.tier),
220
+ actionability: v.therapies ? {
221
+ level: v.fdaApproved ? 'FDA-approved' : 'clinical-evidence',
222
+ therapies: v.therapies,
223
+ evidence: v.evidenceSummary ? [v.evidenceSummary] : []
224
+ } : undefined
225
+ }));
226
+ const cnvs = (fmiReport.copyNumberAlterations || []).map((c) => ({
227
+ gene: c.gene,
228
+ type: c.type?.toLowerCase() === 'amplification' ? 'amplification' : 'deep-deletion',
229
+ copyNumber: c.copyNumber,
230
+ clinicalSignificance: this.mapPathogenicity(c.pathogenicity),
231
+ actionability: c.therapies ? {
232
+ level: c.fdaApproved ? 'FDA-approved' : 'clinical-evidence',
233
+ therapies: c.therapies
234
+ } : undefined
235
+ }));
236
+ const fusions = (fmiReport.rearrangements || []).map((r) => ({
237
+ gene5Prime: r.gene1,
238
+ gene3Prime: r.gene2,
239
+ fusionName: `${r.gene1}-${r.gene2}`,
240
+ inFrame: r.inFrame,
241
+ clinicalSignificance: this.mapPathogenicity(r.pathogenicity),
242
+ actionability: r.therapies ? {
243
+ level: r.fdaApproved ? 'FDA-approved' : 'clinical-evidence',
244
+ therapies: r.therapies
245
+ } : undefined
246
+ }));
247
+ return {
248
+ reportId: fmiReport.reportId || fmiReport.id,
249
+ orderId,
250
+ patientId: fmiReport.patient?.externalId,
251
+ testType: 'comprehensive-genomic-profiling',
252
+ panelName: this.mapFMICodeToPanel(fmiReport.testCode),
253
+ specimenInfo: {
254
+ type: fmiReport.specimen?.type || 'tissue',
255
+ site: fmiReport.specimen?.site,
256
+ collectionDate: new Date(fmiReport.specimen?.collectionDate || Date.now()),
257
+ tumorPurity: fmiReport.specimen?.tumorPurity,
258
+ cellularity: fmiReport.specimen?.cellularity
259
+ },
260
+ reportDate: new Date(fmiReport.reportDate || Date.now()),
261
+ variants,
262
+ copyNumberAlterations: cnvs,
263
+ fusions,
264
+ biomarkers: [],
265
+ therapyMatches: (fmiReport.therapyMatches || []).map((t) => ({
266
+ therapy: t.therapy,
267
+ drugs: t.drugs || [t.therapy],
268
+ biomarkers: t.biomarkers || [],
269
+ evidenceLevel: t.fdaApproved ? 'FDA-approved' : 'clinical-evidence',
270
+ cancerType: t.indication || '',
271
+ references: t.references || []
272
+ })),
273
+ clinicalTrialMatches: (fmiReport.clinicalTrials || []).map((ct) => ({
274
+ trialId: ct.nctId,
275
+ title: ct.title,
276
+ phase: ct.phase,
277
+ matchingBiomarkers: ct.matchingBiomarkers || [],
278
+ status: ct.status || 'recruiting'
279
+ })),
280
+ signatures: {
281
+ microsatelliteInstability: fmiReport.msi ? {
282
+ status: fmiReport.msi.status,
283
+ score: fmiReport.msi.score,
284
+ method: 'NGS'
285
+ } : undefined,
286
+ tumorMutationalBurden: fmiReport.tmb ? {
287
+ value: fmiReport.tmb.score,
288
+ unit: 'mutations/Mb',
289
+ status: fmiReport.tmb.score >= 10 ? 'high' : fmiReport.tmb.score >= 6 ? 'intermediate' : 'low',
290
+ threshold: 10,
291
+ percentile: fmiReport.tmb.percentile
292
+ } : undefined,
293
+ lossOfHeterozygosity: fmiReport.loh ? {
294
+ percentage: fmiReport.loh.percentage,
295
+ status: fmiReport.loh.percentage >= 16 ? 'high' : 'low',
296
+ genomeFraction: fmiReport.loh.genomeFraction
297
+ } : undefined
298
+ },
299
+ qualityMetrics: {
300
+ meanCoverage: fmiReport.qc?.meanCoverage,
301
+ percentBases100x: fmiReport.qc?.percentBases100x,
302
+ tumorFraction: fmiReport.qc?.tumorFraction
303
+ }
304
+ };
305
+ }
306
+ mapVariantType(type) {
307
+ const mapping = {
308
+ 'SNV': 'SNV',
309
+ 'INSERTION': 'insertion',
310
+ 'DELETION': 'deletion',
311
+ 'INDEL': 'indel',
312
+ 'MNV': 'MNV'
313
+ };
314
+ return mapping[type?.toUpperCase()] || 'SNV';
315
+ }
316
+ mapPathogenicity(path) {
317
+ const mapping = {
318
+ 'PATHOGENIC': 'pathogenic',
319
+ 'LIKELY_PATHOGENIC': 'likely-pathogenic',
320
+ 'VUS': 'vus',
321
+ 'LIKELY_BENIGN': 'likely-benign',
322
+ 'BENIGN': 'benign'
323
+ };
324
+ return mapping[path?.toUpperCase()] || 'vus';
325
+ }
326
+ mapTier(tier) {
327
+ if (tier === '1' || tier === 'I')
328
+ return 'I';
329
+ if (tier === '2' || tier === 'II')
330
+ return 'II';
331
+ if (tier === '3' || tier === 'III')
332
+ return 'III';
333
+ return 'IV';
334
+ }
335
+ }
336
+ // ═══════════════════════════════════════════════════════════════════════════════
337
+ // GUARDANT HEALTH CLIENT
338
+ // ═══════════════════════════════════════════════════════════════════════════════
339
+ export class GuardantHealthClient extends GenomicPlatformClient {
340
+ constructor(config) {
341
+ super({ ...config, platform: 'guardant' });
342
+ }
343
+ async authenticate() {
344
+ // Guardant uses API key authentication
345
+ if (!this.config.apiKey) {
346
+ throw new Error('Guardant Health API requires an API key');
347
+ }
348
+ // No token fetch needed for API key auth
349
+ }
350
+ async submitOrder(order) {
351
+ const url = `${this.config.apiBaseUrl}/v2/orders`;
352
+ const guardantOrder = {
353
+ externalOrderId: order.orderId,
354
+ patient: {
355
+ externalId: order.patientId
356
+ },
357
+ test: this.mapPanelToGuardantTest(order.panelName),
358
+ specimen: {
359
+ type: 'blood', // Guardant is primarily liquid biopsy
360
+ collectionDate: new Date().toISOString()
361
+ },
362
+ diagnosis: {
363
+ icdCodes: order.icdCodes,
364
+ cancerType: order.clinicalIndication
365
+ },
366
+ orderingProvider: {
367
+ name: order.orderingPhysician.name,
368
+ npi: order.orderingPhysician.npi,
369
+ organization: order.orderingPhysician.facility
370
+ }
371
+ };
372
+ const response = await this.httpRequest(url, {
373
+ method: 'POST',
374
+ headers: {
375
+ ...await this.getAuthHeaders()
376
+ },
377
+ body: JSON.stringify(guardantOrder)
378
+ });
379
+ const result = JSON.parse(response);
380
+ return {
381
+ orderId: result.orderId,
382
+ status: 'ordered'
383
+ };
384
+ }
385
+ async getOrderStatus(orderId) {
386
+ const url = `${this.config.apiBaseUrl}/v2/orders/${orderId}`;
387
+ const response = await this.httpRequest(url, {
388
+ method: 'GET',
389
+ headers: await this.getAuthHeaders()
390
+ });
391
+ const data = JSON.parse(response);
392
+ return this.mapFromGuardantOrder(data);
393
+ }
394
+ async getResults(orderId) {
395
+ const url = `${this.config.apiBaseUrl}/v2/orders/${orderId}/results`;
396
+ const response = await this.httpRequest(url, {
397
+ method: 'GET',
398
+ headers: await this.getAuthHeaders()
399
+ });
400
+ const data = JSON.parse(response);
401
+ return this.mapFromGuardantReport(data, orderId);
402
+ }
403
+ async listPatientResults(patientId) {
404
+ const url = `${this.config.apiBaseUrl}/v2/patients/${patientId}/results`;
405
+ const response = await this.httpRequest(url, {
406
+ method: 'GET',
407
+ headers: await this.getAuthHeaders()
408
+ });
409
+ const reports = JSON.parse(response);
410
+ return reports.map((r) => this.mapFromGuardantReport(r, r.orderId));
411
+ }
412
+ /**
413
+ * Get Guardant360 specific metrics including ctDNA fraction
414
+ */
415
+ async getGuardant360Metrics(orderId) {
416
+ const result = await this.getResults(orderId);
417
+ // Filter for clonal hematopoiesis variants (common in blood-based testing)
418
+ const chVariants = result.variants.filter(v => ['DNMT3A', 'TET2', 'ASXL1', 'PPM1D', 'TP53', 'SF3B1', 'SRSF2'].includes(v.gene) &&
419
+ v.variantAlleleFrequency < 0.1);
420
+ const somaticVariants = result.variants.filter(v => !chVariants.includes(v));
421
+ return {
422
+ ctDNAFraction: result.qualityMetrics.tumorFraction || 0,
423
+ maxMAF: Math.max(...result.variants.map(v => v.variantAlleleFrequency), 0),
424
+ somatic: somaticVariants,
425
+ clonalHematopoiesis: chVariants,
426
+ msi: result.signatures?.microsatelliteInstability || { status: 'MSS', method: 'NGS' }
427
+ };
428
+ }
429
+ mapPanelToGuardantTest(panelName) {
430
+ const mapping = {
431
+ 'Guardant360': 'G360',
432
+ 'Guardant360 CDx': 'G360CDX',
433
+ 'GuardantOMNI': 'GOMNI',
434
+ 'Guardant360 TissueNext': 'G360TN',
435
+ 'GuardantReveal': 'GREVEAL'
436
+ };
437
+ return mapping[panelName] || 'G360';
438
+ }
439
+ mapFromGuardantOrder(data) {
440
+ return {
441
+ orderId: data.externalOrderId || data.orderId,
442
+ patientId: data.patient?.externalId,
443
+ testType: 'liquid-biopsy',
444
+ panelName: 'Guardant360',
445
+ specimenType: 'blood',
446
+ specimenId: data.specimen?.id,
447
+ orderDate: new Date(data.orderDate),
448
+ clinicalIndication: data.diagnosis?.cancerType || '',
449
+ icdCodes: data.diagnosis?.icdCodes || [],
450
+ orderingPhysician: {
451
+ name: data.orderingProvider?.name || '',
452
+ npi: data.orderingProvider?.npi || '',
453
+ facility: data.orderingProvider?.organization || ''
454
+ },
455
+ status: this.mapGuardantStatus(data.status)
456
+ };
457
+ }
458
+ mapGuardantStatus(status) {
459
+ const mapping = {
460
+ 'ORDERED': 'ordered',
461
+ 'RECEIVED': 'specimen-received',
462
+ 'PROCESSING': 'in-process',
463
+ 'COMPLETE': 'completed',
464
+ 'FAILED': 'failed',
465
+ 'CANCELLED': 'cancelled'
466
+ };
467
+ return mapping[status?.toUpperCase()] || 'ordered';
468
+ }
469
+ mapFromGuardantReport(data, orderId) {
470
+ const variants = (data.alterations || [])
471
+ .filter((a) => a.type === 'SNV' || a.type === 'INDEL')
472
+ .map((v) => ({
473
+ gene: v.gene,
474
+ hgvsP: v.proteinChange,
475
+ hgvsC: v.cdsChange,
476
+ variantType: v.type === 'INDEL' ? 'indel' : 'SNV',
477
+ variantAlleleFrequency: v.plasmaAF || v.af || 0,
478
+ clinicalSignificance: 'likely-pathogenic',
479
+ actionability: v.therapies ? {
480
+ level: v.fdaApproved ? 'FDA-approved' : 'clinical-evidence',
481
+ therapies: v.therapies,
482
+ evidence: []
483
+ } : undefined
484
+ }));
485
+ const cnvs = (data.alterations || [])
486
+ .filter((a) => a.type === 'AMPLIFICATION' || a.type === 'LOSS')
487
+ .map((c) => ({
488
+ gene: c.gene,
489
+ type: c.type === 'AMPLIFICATION' ? 'amplification' : 'deep-deletion',
490
+ copyNumber: c.copyNumber,
491
+ clinicalSignificance: 'likely-pathogenic'
492
+ }));
493
+ const fusions = (data.alterations || [])
494
+ .filter((a) => a.type === 'FUSION')
495
+ .map((f) => ({
496
+ gene5Prime: f.gene.split('-')[0] || f.gene,
497
+ gene3Prime: f.gene.split('-')[1] || f.partner,
498
+ fusionName: f.gene,
499
+ clinicalSignificance: 'pathogenic'
500
+ }));
501
+ return {
502
+ reportId: data.reportId,
503
+ orderId,
504
+ patientId: data.patient?.externalId,
505
+ testType: 'liquid-biopsy',
506
+ panelName: 'Guardant360',
507
+ specimenInfo: {
508
+ type: 'blood',
509
+ collectionDate: new Date(data.collectionDate || Date.now()),
510
+ tumorPurity: data.ctDNAFraction
511
+ },
512
+ reportDate: new Date(data.reportDate || Date.now()),
513
+ variants,
514
+ copyNumberAlterations: cnvs,
515
+ fusions,
516
+ biomarkers: [],
517
+ therapyMatches: (data.therapyAssociations || []).map((t) => ({
518
+ therapy: t.therapy,
519
+ drugs: t.drugs || [t.therapy],
520
+ biomarkers: [t.biomarker],
521
+ evidenceLevel: t.level || 'clinical-evidence',
522
+ cancerType: t.indication || '',
523
+ references: t.references || []
524
+ })),
525
+ clinicalTrialMatches: [],
526
+ signatures: {
527
+ microsatelliteInstability: data.msi ? {
528
+ status: data.msi.status,
529
+ method: 'NGS'
530
+ } : undefined,
531
+ tumorMutationalBurden: data.bTMB ? {
532
+ value: data.bTMB.score,
533
+ unit: 'mutations/Mb',
534
+ status: data.bTMB.score >= 16 ? 'high' : 'low',
535
+ threshold: 16
536
+ } : undefined
537
+ },
538
+ qualityMetrics: {
539
+ tumorFraction: data.ctDNAFraction,
540
+ meanCoverage: data.qc?.meanCoverage
541
+ }
542
+ };
543
+ }
544
+ }
545
+ // ═══════════════════════════════════════════════════════════════════════════════
546
+ // TEMPUS CLIENT
547
+ // ═══════════════════════════════════════════════════════════════════════════════
548
+ export class TempusClient extends GenomicPlatformClient {
549
+ constructor(config) {
550
+ super({ ...config, platform: 'tempus' });
551
+ }
552
+ async authenticate() {
553
+ const tokenUrl = `${this.config.apiBaseUrl}/auth/token`;
554
+ const response = await this.httpRequest(tokenUrl, {
555
+ method: 'POST',
556
+ headers: {
557
+ 'Content-Type': 'application/json'
558
+ },
559
+ body: JSON.stringify({
560
+ client_id: this.config.clientId,
561
+ client_secret: this.config.clientSecret,
562
+ grant_type: 'client_credentials'
563
+ })
564
+ });
565
+ const data = JSON.parse(response);
566
+ this.accessToken = data.access_token;
567
+ this.tokenExpiry = new Date(Date.now() + (data.expires_in * 1000));
568
+ }
569
+ async submitOrder(order) {
570
+ const url = `${this.config.apiBaseUrl}/v1/orders`;
571
+ const tempusOrder = {
572
+ externalId: order.orderId,
573
+ patient: {
574
+ externalId: order.patientId
575
+ },
576
+ testCode: this.mapPanelToTempusCode(order.panelName),
577
+ specimen: {
578
+ type: order.specimenType,
579
+ externalId: order.specimenId
580
+ },
581
+ indication: {
582
+ codes: order.icdCodes,
583
+ description: order.clinicalIndication
584
+ },
585
+ provider: {
586
+ name: order.orderingPhysician.name,
587
+ npi: order.orderingPhysician.npi,
588
+ facility: order.orderingPhysician.facility
589
+ }
590
+ };
591
+ const response = await this.httpRequest(url, {
592
+ method: 'POST',
593
+ headers: await this.getAuthHeaders(),
594
+ body: JSON.stringify(tempusOrder)
595
+ });
596
+ const result = JSON.parse(response);
597
+ return {
598
+ orderId: result.id,
599
+ status: 'ordered'
600
+ };
601
+ }
602
+ async getOrderStatus(orderId) {
603
+ const url = `${this.config.apiBaseUrl}/v1/orders/${orderId}`;
604
+ const response = await this.httpRequest(url, {
605
+ method: 'GET',
606
+ headers: await this.getAuthHeaders()
607
+ });
608
+ const data = JSON.parse(response);
609
+ return this.mapFromTempusOrder(data);
610
+ }
611
+ async getResults(orderId) {
612
+ const url = `${this.config.apiBaseUrl}/v1/orders/${orderId}/report`;
613
+ const response = await this.httpRequest(url, {
614
+ method: 'GET',
615
+ headers: await this.getAuthHeaders()
616
+ });
617
+ const data = JSON.parse(response);
618
+ return this.mapFromTempusReport(data, orderId);
619
+ }
620
+ async listPatientResults(patientId) {
621
+ const url = `${this.config.apiBaseUrl}/v1/patients/${patientId}/reports`;
622
+ const response = await this.httpRequest(url, {
623
+ method: 'GET',
624
+ headers: await this.getAuthHeaders()
625
+ });
626
+ const reports = JSON.parse(response);
627
+ return reports.map((r) => this.mapFromTempusReport(r, r.orderId));
628
+ }
629
+ /**
630
+ * Get Tempus xT/xF specific analysis including RNA expression
631
+ */
632
+ async getTempusAnalysis(orderId) {
633
+ const result = await this.getResults(orderId);
634
+ // Tempus provides RNA expression data for xT panel
635
+ let rnaExpression;
636
+ if (result.biomarkers.some(b => b.name.toLowerCase().includes('expression'))) {
637
+ rnaExpression = result.biomarkers
638
+ .filter(b => b.name.toLowerCase().includes('expression'))
639
+ .map(b => ({
640
+ gene: b.name.replace(' expression', ''),
641
+ zscore: typeof b.value === 'number' ? b.value : 0,
642
+ percentile: 50 // Would come from actual data
643
+ }));
644
+ }
645
+ return {
646
+ dnaFindings: result.variants,
647
+ rnaExpression,
648
+ immuneProfile: result.biomarkers.some(b => b.name === 'PD-L1') ? {
649
+ pdl1: {
650
+ score: Number(result.biomarkers.find(b => b.name === 'PD-L1')?.value || 0),
651
+ method: 'IHC'
652
+ },
653
+ tils: Number(result.biomarkers.find(b => b.name === 'TILs')?.value || 0),
654
+ immuneScore: 0
655
+ } : undefined,
656
+ hrd: result.signatures?.homologousRecombinationDeficiency || {
657
+ status: 'indeterminate'
658
+ }
659
+ };
660
+ }
661
+ mapPanelToTempusCode(panelName) {
662
+ const mapping = {
663
+ 'Tempus xT': 'XT',
664
+ 'Tempus xF': 'XF',
665
+ 'Tempus xR': 'XR',
666
+ 'Tempus xG': 'XG',
667
+ 'Tempus xE': 'XE'
668
+ };
669
+ return mapping[panelName] || 'XT';
670
+ }
671
+ mapFromTempusOrder(data) {
672
+ return {
673
+ orderId: data.externalId || data.id,
674
+ patientId: data.patient?.externalId,
675
+ testType: 'comprehensive-genomic-profiling',
676
+ panelName: this.mapTempusCodeToPanel(data.testCode),
677
+ specimenType: data.specimen?.type || 'tissue',
678
+ specimenId: data.specimen?.externalId,
679
+ orderDate: new Date(data.createdAt),
680
+ clinicalIndication: data.indication?.description || '',
681
+ icdCodes: data.indication?.codes || [],
682
+ orderingPhysician: {
683
+ name: data.provider?.name || '',
684
+ npi: data.provider?.npi || '',
685
+ facility: data.provider?.facility || ''
686
+ },
687
+ status: this.mapTempusStatus(data.status)
688
+ };
689
+ }
690
+ mapTempusCodeToPanel(code) {
691
+ const mapping = {
692
+ 'XT': 'Tempus xT',
693
+ 'XF': 'Tempus xF',
694
+ 'XR': 'Tempus xR',
695
+ 'XG': 'Tempus xG',
696
+ 'XE': 'Tempus xE'
697
+ };
698
+ return mapping[code] || 'Tempus xT';
699
+ }
700
+ mapTempusStatus(status) {
701
+ const mapping = {
702
+ 'ordered': 'ordered',
703
+ 'received': 'specimen-received',
704
+ 'processing': 'in-process',
705
+ 'completed': 'completed',
706
+ 'failed': 'failed',
707
+ 'cancelled': 'cancelled'
708
+ };
709
+ return mapping[status?.toLowerCase()] || 'ordered';
710
+ }
711
+ mapFromTempusReport(data, orderId) {
712
+ // Map Tempus report format to our common format
713
+ const variants = (data.somaticVariants || []).map((v) => ({
714
+ gene: v.gene,
715
+ hgvsP: v.proteinChange,
716
+ hgvsC: v.codingChange,
717
+ transcript: v.transcript,
718
+ variantType: v.type || 'SNV',
719
+ variantAlleleFrequency: v.vaf || 0,
720
+ coverage: v.depth,
721
+ clinicalSignificance: v.pathogenicity || 'vus',
722
+ tier: v.tier,
723
+ oncogenicity: v.oncogenicity,
724
+ functionalEffect: v.functionalEffect,
725
+ somaticStatus: 'somatic'
726
+ }));
727
+ return {
728
+ reportId: data.reportId || data.id,
729
+ orderId,
730
+ patientId: data.patient?.externalId,
731
+ testType: 'comprehensive-genomic-profiling',
732
+ panelName: this.mapTempusCodeToPanel(data.testCode),
733
+ specimenInfo: {
734
+ type: data.specimen?.type || 'tissue',
735
+ site: data.specimen?.site,
736
+ collectionDate: new Date(data.specimen?.collectionDate || Date.now()),
737
+ tumorPurity: data.specimen?.tumorContent
738
+ },
739
+ reportDate: new Date(data.reportDate || Date.now()),
740
+ variants,
741
+ copyNumberAlterations: (data.copyNumberVariants || []).map((c) => ({
742
+ gene: c.gene,
743
+ type: c.type,
744
+ copyNumber: c.copyNumber,
745
+ clinicalSignificance: c.pathogenicity || 'vus'
746
+ })),
747
+ fusions: (data.fusions || []).map((f) => ({
748
+ gene5Prime: f.gene1,
749
+ gene3Prime: f.gene2,
750
+ fusionName: `${f.gene1}-${f.gene2}`,
751
+ inFrame: f.inFrame,
752
+ clinicalSignificance: f.pathogenicity || 'pathogenic'
753
+ })),
754
+ biomarkers: (data.biomarkers || []).map((b) => ({
755
+ name: b.name,
756
+ value: b.value,
757
+ unit: b.unit,
758
+ status: b.status,
759
+ method: b.method
760
+ })),
761
+ therapyMatches: (data.therapyOptions || []).map((t) => ({
762
+ therapy: t.name,
763
+ drugs: t.drugs || [t.name],
764
+ biomarkers: t.biomarkers || [],
765
+ evidenceLevel: t.evidenceLevel || 'clinical-evidence',
766
+ cancerType: t.indication || '',
767
+ references: t.references || []
768
+ })),
769
+ clinicalTrialMatches: (data.clinicalTrials || []).map((ct) => ({
770
+ trialId: ct.nctNumber,
771
+ title: ct.title,
772
+ phase: ct.phase,
773
+ matchingBiomarkers: ct.matchingBiomarkers || [],
774
+ status: ct.status || 'recruiting',
775
+ locations: ct.sites
776
+ })),
777
+ signatures: {
778
+ microsatelliteInstability: data.msi,
779
+ tumorMutationalBurden: data.tmb ? {
780
+ value: data.tmb.score,
781
+ unit: 'mutations/Mb',
782
+ status: data.tmb.score >= 10 ? 'high' : 'low',
783
+ threshold: 10
784
+ } : undefined,
785
+ homologousRecombinationDeficiency: data.hrd
786
+ },
787
+ qualityMetrics: data.qcMetrics
788
+ };
789
+ }
790
+ }
791
+ // ═══════════════════════════════════════════════════════════════════════════════
792
+ // UNIFIED GENOMICS SERVICE
793
+ // ═══════════════════════════════════════════════════════════════════════════════
794
+ export class UnifiedGenomicsService {
795
+ clients = new Map();
796
+ /**
797
+ * Register a genomic platform client
798
+ */
799
+ registerClient(name, client) {
800
+ this.clients.set(name, client);
801
+ }
802
+ /**
803
+ * Get results from all platforms for a patient
804
+ */
805
+ async getAllPatientResults(patientId) {
806
+ const allResults = [];
807
+ for (const [platform, client] of this.clients) {
808
+ try {
809
+ const results = await client.listPatientResults(patientId);
810
+ allResults.push({ platform, results });
811
+ }
812
+ catch (error) {
813
+ console.error(`Failed to get results from ${platform}:`, error);
814
+ }
815
+ }
816
+ return allResults;
817
+ }
818
+ /**
819
+ * Aggregate and deduplicate variants across all platforms
820
+ */
821
+ async getAggregatedVariants(patientId) {
822
+ const allResults = await this.getAllPatientResults(patientId);
823
+ const variantMap = new Map();
824
+ const cnvMap = new Map();
825
+ const fusionMap = new Map();
826
+ let latestMsi;
827
+ let latestTmb;
828
+ let latestHrd;
829
+ let latestReportDate = new Date(0);
830
+ for (const { results } of allResults) {
831
+ for (const result of results) {
832
+ // Track latest report for biomarkers
833
+ if (result.reportDate > latestReportDate) {
834
+ latestReportDate = result.reportDate;
835
+ latestMsi = result.signatures?.microsatelliteInstability;
836
+ latestTmb = result.signatures?.tumorMutationalBurden;
837
+ latestHrd = result.signatures?.homologousRecombinationDeficiency;
838
+ }
839
+ // Deduplicate variants by gene + protein change
840
+ for (const variant of result.variants) {
841
+ const key = `${variant.gene}:${variant.hgvsP || variant.hgvsC}`;
842
+ const existing = variantMap.get(key);
843
+ // Keep the variant with higher VAF or better evidence
844
+ if (!existing || variant.variantAlleleFrequency > existing.variantAlleleFrequency) {
845
+ variantMap.set(key, variant);
846
+ }
847
+ }
848
+ // Deduplicate CNVs
849
+ for (const cnv of result.copyNumberAlterations) {
850
+ const key = `${cnv.gene}:${cnv.type}`;
851
+ if (!cnvMap.has(key)) {
852
+ cnvMap.set(key, cnv);
853
+ }
854
+ }
855
+ // Deduplicate fusions
856
+ for (const fusion of result.fusions) {
857
+ const key = fusion.fusionName;
858
+ if (!fusionMap.has(key)) {
859
+ fusionMap.set(key, fusion);
860
+ }
861
+ }
862
+ }
863
+ }
864
+ return {
865
+ variants: Array.from(variantMap.values()),
866
+ cnvs: Array.from(cnvMap.values()),
867
+ fusions: Array.from(fusionMap.values()),
868
+ biomarkers: {
869
+ msi: latestMsi,
870
+ tmb: latestTmb,
871
+ hrd: latestHrd
872
+ }
873
+ };
874
+ }
875
+ /**
876
+ * Match patient genomics to actionable therapies
877
+ */
878
+ async matchToTherapies(patientId, cancerType) {
879
+ const genomics = await this.getAggregatedVariants(patientId);
880
+ const therapyMatches = [];
881
+ // FDA-approved biomarker-drug matches
882
+ const fdaMatches = this.getFDAApprovedMatches(genomics, cancerType);
883
+ therapyMatches.push(...fdaMatches);
884
+ // NCCN guideline matches
885
+ const nccnMatches = this.getNCCNMatches(genomics, cancerType);
886
+ therapyMatches.push(...nccnMatches);
887
+ // Deduplicate and sort by evidence level
888
+ const evidenceOrder = {
889
+ 'FDA-approved': 0,
890
+ 'NCCN-guideline': 1,
891
+ 'clinical-evidence': 2,
892
+ 'case-report': 3,
893
+ 'preclinical': 4
894
+ };
895
+ const uniqueMatches = new Map();
896
+ for (const match of therapyMatches) {
897
+ const key = `${match.therapy}:${match.biomarkers.join(',')}`;
898
+ const existing = uniqueMatches.get(key);
899
+ if (!existing || evidenceOrder[match.evidenceLevel] < evidenceOrder[existing.evidenceLevel]) {
900
+ uniqueMatches.set(key, match);
901
+ }
902
+ }
903
+ return Array.from(uniqueMatches.values())
904
+ .sort((a, b) => evidenceOrder[a.evidenceLevel] - evidenceOrder[b.evidenceLevel]);
905
+ }
906
+ getFDAApprovedMatches(genomics, cancerType) {
907
+ const matches = [];
908
+ // Check MSI-H for pembrolizumab (tumor-agnostic)
909
+ if (genomics.biomarkers.msi?.status === 'MSI-H') {
910
+ matches.push({
911
+ therapy: 'Pembrolizumab',
912
+ drugs: ['Pembrolizumab'],
913
+ biomarkers: ['MSI-H'],
914
+ evidenceLevel: 'FDA-approved',
915
+ cancerType: 'Tumor-agnostic',
916
+ approvalStatus: 'FDA-approved for MSI-H/dMMR solid tumors',
917
+ references: ['KEYNOTE-158', 'KEYNOTE-177']
918
+ });
919
+ }
920
+ // Check TMB-H for pembrolizumab
921
+ if (genomics.biomarkers.tmb?.status === 'high' && genomics.biomarkers.tmb.value >= 10) {
922
+ matches.push({
923
+ therapy: 'Pembrolizumab',
924
+ drugs: ['Pembrolizumab'],
925
+ biomarkers: ['TMB-H (' + genomics.biomarkers.tmb.value + ' mut/Mb)'],
926
+ evidenceLevel: 'FDA-approved',
927
+ cancerType: 'Tumor-agnostic',
928
+ approvalStatus: 'FDA-approved for TMB-H solid tumors',
929
+ references: ['KEYNOTE-158']
930
+ });
931
+ }
932
+ // Check NTRK fusions
933
+ const ntrkFusion = genomics.fusions.find(f => f.gene5Prime.includes('NTRK') || f.gene3Prime.includes('NTRK'));
934
+ if (ntrkFusion) {
935
+ matches.push({
936
+ therapy: 'Larotrectinib or Entrectinib',
937
+ drugs: ['Larotrectinib', 'Entrectinib'],
938
+ biomarkers: [ntrkFusion.fusionName],
939
+ evidenceLevel: 'FDA-approved',
940
+ cancerType: 'Tumor-agnostic',
941
+ approvalStatus: 'FDA-approved for NTRK fusion-positive solid tumors',
942
+ references: ['NAVIGATE', 'STARTRK-2']
943
+ });
944
+ }
945
+ // Check BRAF V600E
946
+ const brafV600 = genomics.variants.find(v => v.gene === 'BRAF' && v.hgvsP?.includes('V600'));
947
+ if (brafV600) {
948
+ if (cancerType.toLowerCase().includes('melanoma')) {
949
+ matches.push({
950
+ therapy: 'Dabrafenib + Trametinib',
951
+ drugs: ['Dabrafenib', 'Trametinib'],
952
+ biomarkers: ['BRAF V600E/K'],
953
+ evidenceLevel: 'FDA-approved',
954
+ cancerType: 'Melanoma',
955
+ references: ['COMBI-d', 'COMBI-v']
956
+ });
957
+ }
958
+ else if (cancerType.toLowerCase().includes('nsclc') || cancerType.toLowerCase().includes('lung')) {
959
+ matches.push({
960
+ therapy: 'Dabrafenib + Trametinib',
961
+ drugs: ['Dabrafenib', 'Trametinib'],
962
+ biomarkers: ['BRAF V600E'],
963
+ evidenceLevel: 'FDA-approved',
964
+ cancerType: 'NSCLC',
965
+ references: ['BRF113928']
966
+ });
967
+ }
968
+ }
969
+ // Check EGFR mutations
970
+ const egfrMut = genomics.variants.find(v => v.gene === 'EGFR');
971
+ if (egfrMut && (cancerType.toLowerCase().includes('nsclc') || cancerType.toLowerCase().includes('lung'))) {
972
+ matches.push({
973
+ therapy: 'Osimertinib',
974
+ drugs: ['Osimertinib'],
975
+ biomarkers: [`EGFR ${egfrMut.hgvsP || 'mutation'}`],
976
+ evidenceLevel: 'FDA-approved',
977
+ cancerType: 'NSCLC',
978
+ references: ['FLAURA', 'ADAURA']
979
+ });
980
+ }
981
+ // Check ALK fusions
982
+ const alkFusion = genomics.fusions.find(f => f.gene5Prime === 'ALK' || f.gene3Prime === 'ALK');
983
+ if (alkFusion && (cancerType.toLowerCase().includes('nsclc') || cancerType.toLowerCase().includes('lung'))) {
984
+ matches.push({
985
+ therapy: 'Alectinib',
986
+ drugs: ['Alectinib'],
987
+ biomarkers: [alkFusion.fusionName],
988
+ evidenceLevel: 'FDA-approved',
989
+ cancerType: 'NSCLC',
990
+ references: ['ALEX', 'J-ALEX']
991
+ });
992
+ }
993
+ // Check HER2 amplification
994
+ const her2Amp = genomics.cnvs.find(c => c.gene === 'HER2' || c.gene === 'ERBB2');
995
+ if (her2Amp && cancerType.toLowerCase().includes('breast')) {
996
+ matches.push({
997
+ therapy: 'Trastuzumab + Pertuzumab',
998
+ drugs: ['Trastuzumab', 'Pertuzumab'],
999
+ biomarkers: ['HER2 amplification'],
1000
+ evidenceLevel: 'FDA-approved',
1001
+ cancerType: 'Breast',
1002
+ references: ['CLEOPATRA', 'APHINITY']
1003
+ });
1004
+ }
1005
+ // Check BRCA1/2 mutations
1006
+ const brcaMut = genomics.variants.find(v => (v.gene === 'BRCA1' || v.gene === 'BRCA2') &&
1007
+ (v.clinicalSignificance === 'pathogenic' || v.clinicalSignificance === 'likely-pathogenic'));
1008
+ if (brcaMut || genomics.biomarkers.hrd?.status === 'positive') {
1009
+ const biomarker = brcaMut ? `${brcaMut.gene} ${brcaMut.hgvsP || 'mutation'}` : 'HRD-positive';
1010
+ if (cancerType.toLowerCase().includes('ovarian')) {
1011
+ matches.push({
1012
+ therapy: 'Olaparib',
1013
+ drugs: ['Olaparib'],
1014
+ biomarkers: [biomarker],
1015
+ evidenceLevel: 'FDA-approved',
1016
+ cancerType: 'Ovarian',
1017
+ references: ['SOLO-1', 'PAOLA-1']
1018
+ });
1019
+ }
1020
+ else if (cancerType.toLowerCase().includes('breast')) {
1021
+ matches.push({
1022
+ therapy: 'Olaparib or Talazoparib',
1023
+ drugs: ['Olaparib', 'Talazoparib'],
1024
+ biomarkers: [biomarker],
1025
+ evidenceLevel: 'FDA-approved',
1026
+ cancerType: 'Breast',
1027
+ references: ['OlympiAD', 'EMBRACA']
1028
+ });
1029
+ }
1030
+ else if (cancerType.toLowerCase().includes('prostate')) {
1031
+ matches.push({
1032
+ therapy: 'Olaparib or Rucaparib',
1033
+ drugs: ['Olaparib', 'Rucaparib'],
1034
+ biomarkers: [biomarker],
1035
+ evidenceLevel: 'FDA-approved',
1036
+ cancerType: 'Prostate',
1037
+ references: ['PROfound', 'TRITON2']
1038
+ });
1039
+ }
1040
+ }
1041
+ // Check KRAS G12C
1042
+ const krasG12C = genomics.variants.find(v => v.gene === 'KRAS' && v.hgvsP?.includes('G12C'));
1043
+ if (krasG12C) {
1044
+ if (cancerType.toLowerCase().includes('nsclc') || cancerType.toLowerCase().includes('lung')) {
1045
+ matches.push({
1046
+ therapy: 'Sotorasib or Adagrasib',
1047
+ drugs: ['Sotorasib', 'Adagrasib'],
1048
+ biomarkers: ['KRAS G12C'],
1049
+ evidenceLevel: 'FDA-approved',
1050
+ cancerType: 'NSCLC',
1051
+ references: ['CodeBreaK 100', 'KRYSTAL-1']
1052
+ });
1053
+ }
1054
+ }
1055
+ return matches;
1056
+ }
1057
+ getNCCNMatches(genomics, cancerType) {
1058
+ // Additional NCCN guideline-based matches would go here
1059
+ // This is a simplified version
1060
+ return [];
1061
+ }
1062
+ }
1063
+ // ═══════════════════════════════════════════════════════════════════════════════
1064
+ // FACTORY FUNCTION
1065
+ // ═══════════════════════════════════════════════════════════════════════════════
1066
+ export function createGenomicClient(config) {
1067
+ switch (config.platform) {
1068
+ case 'foundation-medicine':
1069
+ return new FoundationMedicineClient(config);
1070
+ case 'guardant':
1071
+ return new GuardantHealthClient(config);
1072
+ case 'tempus':
1073
+ return new TempusClient(config);
1074
+ default:
1075
+ throw new Error(`Unsupported genomic platform: ${config.platform}`);
1076
+ }
1077
+ }
1078
+ export default UnifiedGenomicsService;
1079
+ //# sourceMappingURL=genomicPlatforms.js.map