@fhirfly-io/terminology 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -29
- package/dist/index.cjs +199 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +389 -24
- package/dist/index.d.ts +389 -24
- package/dist/index.js +199 -13
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
package/dist/index.js
CHANGED
|
@@ -169,7 +169,7 @@ var HttpClient = class {
|
|
|
169
169
|
timeout: config.timeout ?? 3e4,
|
|
170
170
|
maxRetries: config.maxRetries ?? 3,
|
|
171
171
|
retryDelay: config.retryDelay ?? 1e3,
|
|
172
|
-
userAgent: config.userAgent ?? `@fhirfly/
|
|
172
|
+
userAgent: config.userAgent ?? `@fhirfly-io/terminology/0.7.1 Node.js/${process.version}`
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
175
|
/**
|
|
@@ -306,7 +306,7 @@ var HttpClient = class {
|
|
|
306
306
|
if (response.status === 429) {
|
|
307
307
|
const retryAfter = response.headers.get("retry-after");
|
|
308
308
|
if (retryAfter && attempt < this.config.maxRetries) {
|
|
309
|
-
await this.sleep(parseInt(retryAfter, 10) * 1e3);
|
|
309
|
+
await this.sleep(Math.min(parseInt(retryAfter, 10) * 1e3, 12e4));
|
|
310
310
|
continue;
|
|
311
311
|
}
|
|
312
312
|
await this.parseErrorResponse(response, endpoint);
|
|
@@ -385,7 +385,7 @@ var NdcEndpoint = class {
|
|
|
385
385
|
* @example
|
|
386
386
|
* ```ts
|
|
387
387
|
* const ndc = await client.ndc.lookup("0069-0151-01");
|
|
388
|
-
* console.log(ndc.data.
|
|
388
|
+
* console.log(ndc.data.brand_name); // "Lipitor"
|
|
389
389
|
* ```
|
|
390
390
|
*/
|
|
391
391
|
async lookup(code, options) {
|
|
@@ -407,15 +407,17 @@ var NdcEndpoint = class {
|
|
|
407
407
|
* ]);
|
|
408
408
|
*
|
|
409
409
|
* for (const item of results.results) {
|
|
410
|
-
* if (item.
|
|
411
|
-
* console.log(item.data.
|
|
410
|
+
* if (item.status === "ok") {
|
|
411
|
+
* console.log(item.data.brand_name);
|
|
412
412
|
* } else {
|
|
413
|
-
* console.log(`Not found: ${item.
|
|
413
|
+
* console.log(`Not found: ${item.input}`);
|
|
414
414
|
* }
|
|
415
415
|
* }
|
|
416
416
|
* ```
|
|
417
417
|
*/
|
|
418
418
|
async lookupMany(codes, options) {
|
|
419
|
+
if (codes.length === 0) throw new ValidationError("codes array must not be empty");
|
|
420
|
+
if (codes.length > 500) throw new ValidationError(`NDC batch lookup supports max 500 codes, got ${codes.length}`);
|
|
419
421
|
return this.http.post(
|
|
420
422
|
"/v1/ndc/_batch",
|
|
421
423
|
{ codes },
|
|
@@ -480,7 +482,7 @@ var NpiEndpoint = class {
|
|
|
480
482
|
/**
|
|
481
483
|
* Look up multiple NPIs in a single request.
|
|
482
484
|
*
|
|
483
|
-
* @param npis - Array of 10-digit NPI numbers (max
|
|
485
|
+
* @param npis - Array of 10-digit NPI numbers (max 100)
|
|
484
486
|
* @param options - Response shape, include, and batch options
|
|
485
487
|
* @returns Batch response with results for each NPI
|
|
486
488
|
*
|
|
@@ -493,6 +495,8 @@ var NpiEndpoint = class {
|
|
|
493
495
|
* ```
|
|
494
496
|
*/
|
|
495
497
|
async lookupMany(npis, options) {
|
|
498
|
+
if (npis.length === 0) throw new ValidationError("npis array must not be empty");
|
|
499
|
+
if (npis.length > 100) throw new ValidationError(`NPI batch lookup supports max 100 codes, got ${npis.length}`);
|
|
496
500
|
return this.http.post(
|
|
497
501
|
"/v1/npi/_batch",
|
|
498
502
|
{ codes: npis },
|
|
@@ -554,11 +558,13 @@ var RxNormEndpoint = class {
|
|
|
554
558
|
/**
|
|
555
559
|
* Look up multiple RxCUIs in a single request.
|
|
556
560
|
*
|
|
557
|
-
* @param rxcuis - Array of RxCUIs (max
|
|
561
|
+
* @param rxcuis - Array of RxCUIs (max 100)
|
|
558
562
|
* @param options - Response shape, include, and batch options
|
|
559
563
|
* @returns Batch response with results for each RxCUI
|
|
560
564
|
*/
|
|
561
565
|
async lookupMany(rxcuis, options) {
|
|
566
|
+
if (rxcuis.length === 0) throw new ValidationError("rxcuis array must not be empty");
|
|
567
|
+
if (rxcuis.length > 100) throw new ValidationError(`RxNorm batch lookup supports max 100 codes, got ${rxcuis.length}`);
|
|
562
568
|
return this.http.post(
|
|
563
569
|
"/v1/rxnorm/_batch",
|
|
564
570
|
{ codes: rxcuis },
|
|
@@ -617,11 +623,13 @@ var LoincEndpoint = class {
|
|
|
617
623
|
/**
|
|
618
624
|
* Look up multiple LOINC codes in a single request.
|
|
619
625
|
*
|
|
620
|
-
* @param loincNums - Array of LOINC numbers (max
|
|
626
|
+
* @param loincNums - Array of LOINC numbers (max 100)
|
|
621
627
|
* @param options - Response shape, include, and batch options
|
|
622
628
|
* @returns Batch response with results for each LOINC
|
|
623
629
|
*/
|
|
624
630
|
async lookupMany(loincNums, options) {
|
|
631
|
+
if (loincNums.length === 0) throw new ValidationError("loincNums array must not be empty");
|
|
632
|
+
if (loincNums.length > 100) throw new ValidationError(`LOINC batch lookup supports max 100 codes, got ${loincNums.length}`);
|
|
625
633
|
return this.http.post(
|
|
626
634
|
"/v1/loinc/_batch",
|
|
627
635
|
{ codes: loincNums },
|
|
@@ -691,11 +699,13 @@ var Icd10Endpoint = class {
|
|
|
691
699
|
*
|
|
692
700
|
* Codes can be a mix of CM (diagnoses) and PCS (procedures).
|
|
693
701
|
*
|
|
694
|
-
* @param codes - Array of ICD-10 codes (max
|
|
702
|
+
* @param codes - Array of ICD-10 codes (max 100)
|
|
695
703
|
* @param options - Response shape, include, and batch options
|
|
696
704
|
* @returns Batch response with results for each code
|
|
697
705
|
*/
|
|
698
706
|
async lookupMany(codes, options) {
|
|
707
|
+
if (codes.length === 0) throw new ValidationError("codes array must not be empty");
|
|
708
|
+
if (codes.length > 100) throw new ValidationError(`ICD-10 batch lookup supports max 100 codes, got ${codes.length}`);
|
|
699
709
|
return this.http.post(
|
|
700
710
|
"/v1/icd10/_batch",
|
|
701
711
|
{ codes },
|
|
@@ -758,11 +768,13 @@ var CvxEndpoint = class {
|
|
|
758
768
|
/**
|
|
759
769
|
* Look up multiple CVX codes in a single request.
|
|
760
770
|
*
|
|
761
|
-
* @param cvxCodes - Array of CVX codes (max
|
|
771
|
+
* @param cvxCodes - Array of CVX codes (max 100)
|
|
762
772
|
* @param options - Response shape, include, and batch options
|
|
763
773
|
* @returns Batch response with results for each code
|
|
764
774
|
*/
|
|
765
775
|
async lookupMany(cvxCodes, options) {
|
|
776
|
+
if (cvxCodes.length === 0) throw new ValidationError("cvxCodes array must not be empty");
|
|
777
|
+
if (cvxCodes.length > 100) throw new ValidationError(`CVX batch lookup supports max 100 codes, got ${cvxCodes.length}`);
|
|
766
778
|
return this.http.post(
|
|
767
779
|
"/v1/cvx/_batch",
|
|
768
780
|
{ codes: cvxCodes },
|
|
@@ -821,11 +833,13 @@ var MvxEndpoint = class {
|
|
|
821
833
|
/**
|
|
822
834
|
* Look up multiple MVX codes in a single request.
|
|
823
835
|
*
|
|
824
|
-
* @param mvxCodes - Array of MVX codes (max
|
|
836
|
+
* @param mvxCodes - Array of MVX codes (max 100)
|
|
825
837
|
* @param options - Response shape, include, and batch options
|
|
826
838
|
* @returns Batch response with results for each code
|
|
827
839
|
*/
|
|
828
840
|
async lookupMany(mvxCodes, options) {
|
|
841
|
+
if (mvxCodes.length === 0) throw new ValidationError("mvxCodes array must not be empty");
|
|
842
|
+
if (mvxCodes.length > 100) throw new ValidationError(`MVX batch lookup supports max 100 codes, got ${mvxCodes.length}`);
|
|
829
843
|
return this.http.post(
|
|
830
844
|
"/v1/mvx/_batch",
|
|
831
845
|
{ codes: mvxCodes },
|
|
@@ -907,10 +921,12 @@ var FdaLabelsEndpoint = class {
|
|
|
907
921
|
* Returns metadata only (no sections) for batch efficiency.
|
|
908
922
|
* Identifiers can be Set IDs, NDC codes, or RxCUIs (mixed).
|
|
909
923
|
*
|
|
910
|
-
* @param identifiers - Array of identifiers (max
|
|
924
|
+
* @param identifiers - Array of identifiers (max 50)
|
|
911
925
|
* @returns Batch response with results for each identifier
|
|
912
926
|
*/
|
|
913
927
|
async lookupMany(identifiers) {
|
|
928
|
+
if (identifiers.length === 0) throw new ValidationError("identifiers array must not be empty");
|
|
929
|
+
if (identifiers.length > 50) throw new ValidationError(`FDA Labels batch lookup supports max 50 identifiers, got ${identifiers.length}`);
|
|
914
930
|
return this.http.post(
|
|
915
931
|
"/v1/fda-label/_batch",
|
|
916
932
|
{ codes: identifiers }
|
|
@@ -1032,6 +1048,8 @@ var SnomedEndpoint = class {
|
|
|
1032
1048
|
* ```
|
|
1033
1049
|
*/
|
|
1034
1050
|
async lookupMany(conceptIds) {
|
|
1051
|
+
if (conceptIds.length === 0) throw new ValidationError("conceptIds array must not be empty");
|
|
1052
|
+
if (conceptIds.length > 100) throw new ValidationError(`SNOMED batch lookup supports max 100 codes, got ${conceptIds.length}`);
|
|
1035
1053
|
return this.http.post(
|
|
1036
1054
|
"/v1/snomed/_batch",
|
|
1037
1055
|
{ codes: conceptIds }
|
|
@@ -1098,6 +1116,168 @@ var SnomedEndpoint = class {
|
|
|
1098
1116
|
}
|
|
1099
1117
|
};
|
|
1100
1118
|
|
|
1119
|
+
// src/endpoints/claims.ts
|
|
1120
|
+
var ClaimsEndpoint = class {
|
|
1121
|
+
constructor(http) {
|
|
1122
|
+
this.http = http;
|
|
1123
|
+
}
|
|
1124
|
+
// ==========================================================================
|
|
1125
|
+
// NCCI PTP Edits
|
|
1126
|
+
// ==========================================================================
|
|
1127
|
+
/**
|
|
1128
|
+
* Validate whether two CPT/HCPCS codes can be billed together.
|
|
1129
|
+
*
|
|
1130
|
+
* @param code1 - First CPT/HCPCS code (4-5 alphanumeric characters)
|
|
1131
|
+
* @param code2 - Second CPT/HCPCS code (4-5 alphanumeric characters)
|
|
1132
|
+
* @param options - Optional filters (claim_type)
|
|
1133
|
+
* @returns Validation result with edit details and billing summary
|
|
1134
|
+
*
|
|
1135
|
+
* @example
|
|
1136
|
+
* ```ts
|
|
1137
|
+
* const result = await client.claims.validateNcci("99213", "99214");
|
|
1138
|
+
* if (!result.data.can_bill_together) {
|
|
1139
|
+
* console.log(result.data.summary);
|
|
1140
|
+
* for (const edit of result.data.edits) {
|
|
1141
|
+
* console.log(`${edit.claim_type}: modifier ${edit.modifier_allowed ? "allowed" : "not allowed"}`);
|
|
1142
|
+
* }
|
|
1143
|
+
* }
|
|
1144
|
+
* ```
|
|
1145
|
+
*/
|
|
1146
|
+
async validateNcci(code1, code2, options) {
|
|
1147
|
+
const params = {
|
|
1148
|
+
code1: encodeURIComponent(code1),
|
|
1149
|
+
code2: encodeURIComponent(code2)
|
|
1150
|
+
};
|
|
1151
|
+
if (options?.claim_type) {
|
|
1152
|
+
params.claim_type = options.claim_type;
|
|
1153
|
+
}
|
|
1154
|
+
const queryString = this.http.buildSearchQueryString(params);
|
|
1155
|
+
return this.http.get(`/v1/ncci/validate${queryString}`);
|
|
1156
|
+
}
|
|
1157
|
+
// ==========================================================================
|
|
1158
|
+
// MUE (Medically Unlikely Edits)
|
|
1159
|
+
// ==========================================================================
|
|
1160
|
+
/**
|
|
1161
|
+
* Look up MUE limits for a HCPCS/CPT code.
|
|
1162
|
+
*
|
|
1163
|
+
* @param hcpcs - HCPCS/CPT code (4-5 alphanumeric characters)
|
|
1164
|
+
* @param options - Optional filters (service_type)
|
|
1165
|
+
* @returns MUE limit data including max units per service type
|
|
1166
|
+
*
|
|
1167
|
+
* @example
|
|
1168
|
+
* ```ts
|
|
1169
|
+
* const mue = await client.claims.lookupMue("99213");
|
|
1170
|
+
* for (const limit of mue.data.limits) {
|
|
1171
|
+
* console.log(`${limit.service_type}: max ${limit.mue_value} units`);
|
|
1172
|
+
* }
|
|
1173
|
+
* ```
|
|
1174
|
+
*/
|
|
1175
|
+
async lookupMue(hcpcs, options) {
|
|
1176
|
+
const params = {};
|
|
1177
|
+
if (options?.service_type) {
|
|
1178
|
+
params.service_type = options.service_type;
|
|
1179
|
+
}
|
|
1180
|
+
const queryString = this.http.buildSearchQueryString(params);
|
|
1181
|
+
return this.http.get(
|
|
1182
|
+
`/v1/mue/${encodeURIComponent(hcpcs)}${queryString}`
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Batch MUE lookup for multiple HCPCS codes.
|
|
1187
|
+
*
|
|
1188
|
+
* @param codes - Array of HCPCS/CPT codes (max 100)
|
|
1189
|
+
* @returns Batch results with per-code MUE limits
|
|
1190
|
+
*
|
|
1191
|
+
* @example
|
|
1192
|
+
* ```ts
|
|
1193
|
+
* const results = await client.claims.lookupMueMany(["99213", "99214", "99215"]);
|
|
1194
|
+
* for (const item of results.results) {
|
|
1195
|
+
* if (item.status === "ok") {
|
|
1196
|
+
* console.log(`${item.hcpcs_code}: ${item.data.limits.length} limits`);
|
|
1197
|
+
* }
|
|
1198
|
+
* }
|
|
1199
|
+
* ```
|
|
1200
|
+
*/
|
|
1201
|
+
async lookupMueMany(codes) {
|
|
1202
|
+
if (codes.length === 0) throw new ValidationError("codes array must not be empty");
|
|
1203
|
+
if (codes.length > 100) throw new ValidationError(`MUE batch lookup supports max 100 codes, got ${codes.length}`);
|
|
1204
|
+
return this.http.post("/v1/mue/_batch", { codes });
|
|
1205
|
+
}
|
|
1206
|
+
// ==========================================================================
|
|
1207
|
+
// PFS/RVU (Physician Fee Schedule / Relative Value Units)
|
|
1208
|
+
// ==========================================================================
|
|
1209
|
+
/**
|
|
1210
|
+
* Look up Physician Fee Schedule and RVU data for a HCPCS/CPT code.
|
|
1211
|
+
*
|
|
1212
|
+
* @param hcpcs - HCPCS/CPT code (4-5 alphanumeric characters)
|
|
1213
|
+
* @returns Fee schedule data including RVU breakdown and calculated payments
|
|
1214
|
+
*
|
|
1215
|
+
* @example
|
|
1216
|
+
* ```ts
|
|
1217
|
+
* const pfs = await client.claims.lookupPfs("99213");
|
|
1218
|
+
* console.log(`Description: ${pfs.data.description}`);
|
|
1219
|
+
* console.log(`Work RVU: ${pfs.data.rvu.work}`);
|
|
1220
|
+
* console.log(`Non-facility payment: $${pfs.data.calculated_payment.non_facility}`);
|
|
1221
|
+
* console.log(`Facility payment: $${pfs.data.calculated_payment.facility}`);
|
|
1222
|
+
* ```
|
|
1223
|
+
*/
|
|
1224
|
+
async lookupPfs(hcpcs) {
|
|
1225
|
+
return this.http.get(
|
|
1226
|
+
`/v1/pfs/${encodeURIComponent(hcpcs)}`
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Batch PFS/RVU lookup for multiple HCPCS codes.
|
|
1231
|
+
*
|
|
1232
|
+
* @param codes - Array of HCPCS/CPT codes (max 100)
|
|
1233
|
+
* @returns Batch results with per-code fee schedule data
|
|
1234
|
+
*
|
|
1235
|
+
* @example
|
|
1236
|
+
* ```ts
|
|
1237
|
+
* const results = await client.claims.lookupPfsMany(["99213", "99214", "99215"]);
|
|
1238
|
+
* for (const item of results.results) {
|
|
1239
|
+
* if (item.status === "ok") {
|
|
1240
|
+
* console.log(`${item.hcpcs_code}: $${item.data.calculated_payment.non_facility}`);
|
|
1241
|
+
* }
|
|
1242
|
+
* }
|
|
1243
|
+
* ```
|
|
1244
|
+
*/
|
|
1245
|
+
async lookupPfsMany(codes) {
|
|
1246
|
+
if (codes.length === 0) throw new ValidationError("codes array must not be empty");
|
|
1247
|
+
if (codes.length > 100) throw new ValidationError(`PFS batch lookup supports max 100 codes, got ${codes.length}`);
|
|
1248
|
+
return this.http.post("/v1/pfs/_batch", { codes });
|
|
1249
|
+
}
|
|
1250
|
+
// ==========================================================================
|
|
1251
|
+
// LCD/NCD Coverage Determination
|
|
1252
|
+
// ==========================================================================
|
|
1253
|
+
/**
|
|
1254
|
+
* Check LCD/NCD coverage determinations linked to a HCPCS code.
|
|
1255
|
+
*
|
|
1256
|
+
* @param hcpcs - HCPCS/CPT code (4-5 alphanumeric characters)
|
|
1257
|
+
* @param options - Optional filters (active only)
|
|
1258
|
+
* @returns Coverage policies linked to the code
|
|
1259
|
+
*
|
|
1260
|
+
* @example
|
|
1261
|
+
* ```ts
|
|
1262
|
+
* const coverage = await client.claims.checkCoverage("99213");
|
|
1263
|
+
* console.log(`${coverage.data.policies_found} policies found`);
|
|
1264
|
+
* for (const policy of coverage.data.policies) {
|
|
1265
|
+
* console.log(`${policy.display_id}: ${policy.policy_title} [${policy.status}]`);
|
|
1266
|
+
* }
|
|
1267
|
+
* ```
|
|
1268
|
+
*/
|
|
1269
|
+
async checkCoverage(hcpcs, options) {
|
|
1270
|
+
const params = {
|
|
1271
|
+
hcpcs: encodeURIComponent(hcpcs)
|
|
1272
|
+
};
|
|
1273
|
+
if (options?.active !== void 0) {
|
|
1274
|
+
params.active = options.active.toString();
|
|
1275
|
+
}
|
|
1276
|
+
const queryString = this.http.buildSearchQueryString(params);
|
|
1277
|
+
return this.http.get(`/v1/coverage/check${queryString}`);
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1101
1281
|
// src/client.ts
|
|
1102
1282
|
var Fhirfly = class {
|
|
1103
1283
|
http;
|
|
@@ -1143,6 +1323,11 @@ var Fhirfly = class {
|
|
|
1143
1323
|
* Look up clinical concepts from the SNOMED CT IPS free set (~12K concepts, CC BY 4.0).
|
|
1144
1324
|
*/
|
|
1145
1325
|
snomed;
|
|
1326
|
+
/**
|
|
1327
|
+
* Claims Intelligence endpoints (requires `claims.read` scope).
|
|
1328
|
+
* NCCI PTP validation, MUE limits, PFS/RVU fee schedule, LCD/NCD coverage.
|
|
1329
|
+
*/
|
|
1330
|
+
claims;
|
|
1146
1331
|
/**
|
|
1147
1332
|
* Create a new FHIRfly client.
|
|
1148
1333
|
*
|
|
@@ -1190,6 +1375,7 @@ var Fhirfly = class {
|
|
|
1190
1375
|
this.fdaLabels = new FdaLabelsEndpoint(this.http);
|
|
1191
1376
|
this.connectivity = new ConnectivityEndpoint(this.http);
|
|
1192
1377
|
this.snomed = new SnomedEndpoint(this.http);
|
|
1378
|
+
this.claims = new ClaimsEndpoint(this.http);
|
|
1193
1379
|
}
|
|
1194
1380
|
};
|
|
1195
1381
|
|