@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/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/sdk/0.1.0 Node.js/${process.version}`
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.product_name); // "Lipitor"
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.found) {
411
- * console.log(item.data.product_name);
410
+ * if (item.status === "ok") {
411
+ * console.log(item.data.brand_name);
412
412
  * } else {
413
- * console.log(`Not found: ${item.code}`);
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 500)
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 500)
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 500)
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 500)
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 500)
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 500)
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 500)
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