@cosyte/astm 0.0.22 → 0.1.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/CHANGELOG.md +346 -7
- package/README.md +559 -224
- package/dist/index.cjs +309 -64
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1061 -184
- package/dist/index.d.ts +1061 -184
- package/dist/index.mjs +301 -65
- package/dist/index.mjs.map +1 -1
- package/package.json +25 -8
package/dist/index.cjs
CHANGED
|
@@ -102,13 +102,13 @@ function parseAstmDate(raw) {
|
|
|
102
102
|
return { ...base, month, day, hour, minute, second, precision: "second" };
|
|
103
103
|
}
|
|
104
104
|
function astmDateToLocalISO(d) {
|
|
105
|
-
const
|
|
105
|
+
const p22 = (n) => String(n).padStart(2, "0");
|
|
106
106
|
let out = String(d.year).padStart(4, "0");
|
|
107
|
-
if (d.month !== void 0) out += `-${
|
|
108
|
-
if (d.day !== void 0) out += `-${
|
|
109
|
-
if (d.hour !== void 0) out += `T${
|
|
110
|
-
if (d.minute !== void 0) out += `:${
|
|
111
|
-
if (d.second !== void 0) out += `:${
|
|
107
|
+
if (d.month !== void 0) out += `-${p22(d.month)}`;
|
|
108
|
+
if (d.day !== void 0) out += `-${p22(d.day)}`;
|
|
109
|
+
if (d.hour !== void 0) out += `T${p22(d.hour)}`;
|
|
110
|
+
if (d.minute !== void 0) out += `:${p22(d.minute)}`;
|
|
111
|
+
if (d.second !== void 0) out += `:${p22(d.second)}`;
|
|
112
112
|
return out;
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -118,26 +118,40 @@ function recognizeUniversalTestId(components) {
|
|
|
118
118
|
const c = components[i];
|
|
119
119
|
return c !== void 0 && c.length > 0 ? c : void 0;
|
|
120
120
|
};
|
|
121
|
-
const
|
|
121
|
+
const unvalidatedWireValue = nonEmpty(0);
|
|
122
122
|
const testName = nonEmpty(1);
|
|
123
123
|
const codingScheme = nonEmpty(2);
|
|
124
124
|
const localCode = nonEmpty(3);
|
|
125
125
|
let provenance;
|
|
126
|
-
if (
|
|
127
|
-
else if (
|
|
126
|
+
if (localCode !== void 0) provenance = "local-code";
|
|
127
|
+
else if (unvalidatedWireValue !== void 0) provenance = "unvalidated-wire-value-only";
|
|
128
128
|
else if (testName !== void 0) provenance = "name-only";
|
|
129
129
|
else provenance = "empty";
|
|
130
130
|
const base = { components, provenance };
|
|
131
131
|
return {
|
|
132
132
|
...base,
|
|
133
|
-
...
|
|
133
|
+
...unvalidatedWireValue !== void 0 ? { unvalidatedWireValue } : {},
|
|
134
134
|
...testName !== void 0 ? { testName } : {},
|
|
135
135
|
...codingScheme !== void 0 ? { codingScheme } : {},
|
|
136
136
|
...localCode !== void 0 ? { localCode } : {}
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
139
|
function primaryCode(u) {
|
|
140
|
-
return u.
|
|
140
|
+
return u.localCode;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/common/vocabulary.ts
|
|
144
|
+
var ABNORMAL_FLAG_VOCABULARY = Object.freeze({
|
|
145
|
+
attributed: true,
|
|
146
|
+
system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
|
|
147
|
+
version: "4.0.0"
|
|
148
|
+
});
|
|
149
|
+
var RESULT_STATUS_VOCABULARY = Object.freeze({
|
|
150
|
+
attributed: false,
|
|
151
|
+
reason: "The result-status letter set is graded against no citable published source: the normative text that would bind it is purchase-gated and unread here, and no neighbouring code system is assumed in its place."
|
|
152
|
+
});
|
|
153
|
+
function describeVocabulary(vocabulary) {
|
|
154
|
+
return vocabulary.attributed ? `graded against ${vocabulary.system} version ${vocabulary.version}` : vocabulary.reason;
|
|
141
155
|
}
|
|
142
156
|
|
|
143
157
|
// src/common/warnings.ts
|
|
@@ -747,14 +761,14 @@ function ambiguousValueSplit(position) {
|
|
|
747
761
|
function undefinedAbnormalFlag(position) {
|
|
748
762
|
return {
|
|
749
763
|
code: WARNING_CODES.ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG,
|
|
750
|
-
message:
|
|
764
|
+
message: `Abnormal flag is not a code in the vocabulary it was ${describeVocabulary(ABNORMAL_FLAG_VOCABULARY)}, surfaced as undefined, never as normal.`,
|
|
751
765
|
position
|
|
752
766
|
};
|
|
753
767
|
}
|
|
754
768
|
function undefinedResultStatus(position) {
|
|
755
769
|
return {
|
|
756
770
|
code: WARNING_CODES.ASTM_RECORD_UNDEFINED_RESULT_STATUS,
|
|
757
|
-
message:
|
|
771
|
+
message: `Result status is not a recognized status letter, surfaced as undefined. ${describeVocabulary(RESULT_STATUS_VOCABULARY)}`,
|
|
758
772
|
position
|
|
759
773
|
};
|
|
760
774
|
}
|
|
@@ -828,14 +842,25 @@ var ABNORMAL_FLAG_MEANINGS = {
|
|
|
828
842
|
W: "worse",
|
|
829
843
|
S: "susceptible",
|
|
830
844
|
R: "resistant",
|
|
831
|
-
I: "intermediate"
|
|
845
|
+
I: "intermediate",
|
|
846
|
+
HU: "significantly-high",
|
|
847
|
+
LU: "significantly-low"
|
|
832
848
|
};
|
|
849
|
+
var ABNORMAL_FLAG_CODES = Object.freeze(
|
|
850
|
+
Object.keys(ABNORMAL_FLAG_MEANINGS)
|
|
851
|
+
);
|
|
833
852
|
function interpretAbnormalFlag(raw) {
|
|
834
853
|
const key = raw.trim();
|
|
835
854
|
if (isAbnormalFlagCode(key)) {
|
|
836
|
-
return {
|
|
855
|
+
return {
|
|
856
|
+
raw,
|
|
857
|
+
code: key,
|
|
858
|
+
meaning: ABNORMAL_FLAG_MEANINGS[key],
|
|
859
|
+
recognized: true,
|
|
860
|
+
vocabulary: ABNORMAL_FLAG_VOCABULARY
|
|
861
|
+
};
|
|
837
862
|
}
|
|
838
|
-
return { raw, meaning: "undefined", recognized: false };
|
|
863
|
+
return { raw, meaning: "undefined", recognized: false, vocabulary: ABNORMAL_FLAG_VOCABULARY };
|
|
839
864
|
}
|
|
840
865
|
function isAbnormalFlagCode(value) {
|
|
841
866
|
return Object.prototype.hasOwnProperty.call(ABNORMAL_FLAG_MEANINGS, value);
|
|
@@ -856,7 +881,8 @@ function interpretResultStatus(raw) {
|
|
|
856
881
|
recognized: false,
|
|
857
882
|
isActiveFinal: false,
|
|
858
883
|
supersedes: false,
|
|
859
|
-
cancelled: false
|
|
884
|
+
cancelled: false,
|
|
885
|
+
vocabulary: RESULT_STATUS_VOCABULARY
|
|
860
886
|
};
|
|
861
887
|
}
|
|
862
888
|
const key = raw.trim();
|
|
@@ -868,7 +894,8 @@ function interpretResultStatus(raw) {
|
|
|
868
894
|
recognized: true,
|
|
869
895
|
isActiveFinal: key === "F",
|
|
870
896
|
supersedes: key === "C",
|
|
871
|
-
cancelled: key === "X"
|
|
897
|
+
cancelled: key === "X",
|
|
898
|
+
vocabulary: RESULT_STATUS_VOCABULARY
|
|
872
899
|
};
|
|
873
900
|
}
|
|
874
901
|
return {
|
|
@@ -877,7 +904,8 @@ function interpretResultStatus(raw) {
|
|
|
877
904
|
recognized: false,
|
|
878
905
|
isActiveFinal: false,
|
|
879
906
|
supersedes: false,
|
|
880
|
-
cancelled: false
|
|
907
|
+
cancelled: false,
|
|
908
|
+
vocabulary: RESULT_STATUS_VOCABULARY
|
|
881
909
|
};
|
|
882
910
|
}
|
|
883
911
|
function isResultStatusCode(value) {
|
|
@@ -1802,10 +1830,10 @@ function buildResult(recordIndex, fields, warnings) {
|
|
|
1802
1830
|
13,
|
|
1803
1831
|
warnings
|
|
1804
1832
|
);
|
|
1805
|
-
const
|
|
1806
|
-
const valueComponents =
|
|
1807
|
-
const value =
|
|
1808
|
-
if (
|
|
1833
|
+
const ambiguous2 = valueField !== void 0 && valueField.components.length > 1;
|
|
1834
|
+
const valueComponents = ambiguous2 ? valueField.components : void 0;
|
|
1835
|
+
const value = ambiguous2 ? valueField.raw : fieldScalar(valueField);
|
|
1836
|
+
if (ambiguous2) {
|
|
1809
1837
|
warnings.push(ambiguousValueSplit({ recordIndex, recordType: "R", fieldIndex: 4 }));
|
|
1810
1838
|
}
|
|
1811
1839
|
const units = fieldScalar(astmField(fields, 5));
|
|
@@ -2309,41 +2337,77 @@ function query(msg) {
|
|
|
2309
2337
|
return msg.records.filter((r) => r.type === "Q");
|
|
2310
2338
|
}
|
|
2311
2339
|
|
|
2340
|
+
// src/common/date-conversion.ts
|
|
2341
|
+
function isStated(component) {
|
|
2342
|
+
return typeof component === "number" && Number.isFinite(component);
|
|
2343
|
+
}
|
|
2344
|
+
function p2(n) {
|
|
2345
|
+
return String(n).padStart(2, "0");
|
|
2346
|
+
}
|
|
2347
|
+
function isLeapYear(year) {
|
|
2348
|
+
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
2349
|
+
}
|
|
2350
|
+
function longestDay(year, month) {
|
|
2351
|
+
if (month === 2) return year === void 0 || isLeapYear(year) ? 29 : 28;
|
|
2352
|
+
return month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;
|
|
2353
|
+
}
|
|
2354
|
+
function inRange(component, low, high) {
|
|
2355
|
+
if (component === void 0) return true;
|
|
2356
|
+
return Number.isInteger(component) && component >= low && component <= high;
|
|
2357
|
+
}
|
|
2358
|
+
function statesARealCalendarDate(parts) {
|
|
2359
|
+
return inRange(parts.year, 0, 9999) && inRange(parts.month, 1, 12) && inRange(parts.day, 1, parts.month === void 0 ? 31 : longestDay(parts.year, parts.month)) && inRange(parts.hour, 0, 23) && inRange(parts.minute, 0, 59) && // 60 is refused with the rest: a leap second is legal in ISO-8601 prose and
|
|
2360
|
+
// is an Invalid Date to every JS reader, so accepting it here would hand out
|
|
2361
|
+
// a string the next hop cannot read.
|
|
2362
|
+
inRange(parts.second, 0, 59);
|
|
2363
|
+
}
|
|
2364
|
+
function toObject(value) {
|
|
2365
|
+
if (value === null || value === void 0) return void 0;
|
|
2366
|
+
const parts = {};
|
|
2367
|
+
if (isStated(value.year)) parts.year = value.year;
|
|
2368
|
+
if (isStated(value.month)) parts.month = value.month;
|
|
2369
|
+
if (isStated(value.day)) parts.day = value.day;
|
|
2370
|
+
if (isStated(value.hour)) parts.hour = value.hour;
|
|
2371
|
+
if (isStated(value.minute)) parts.minute = value.minute;
|
|
2372
|
+
if (isStated(value.second)) parts.second = value.second;
|
|
2373
|
+
if (Object.keys(parts).length === 0) return void 0;
|
|
2374
|
+
if (!statesARealCalendarDate(parts)) return void 0;
|
|
2375
|
+
return Object.freeze(parts);
|
|
2376
|
+
}
|
|
2377
|
+
function toISO(value) {
|
|
2378
|
+
const parts = toObject(value);
|
|
2379
|
+
if (parts === void 0 || parts.year === void 0) return void 0;
|
|
2380
|
+
let out = String(parts.year).padStart(4, "0");
|
|
2381
|
+
if (parts.month === void 0) return out;
|
|
2382
|
+
out += `-${p2(parts.month)}`;
|
|
2383
|
+
if (parts.day === void 0) return out;
|
|
2384
|
+
out += `-${p2(parts.day)}`;
|
|
2385
|
+
if (parts.hour === void 0) return out;
|
|
2386
|
+
out += `T${p2(parts.hour)}`;
|
|
2387
|
+
if (parts.minute === void 0) return out;
|
|
2388
|
+
out += `:${p2(parts.minute)}`;
|
|
2389
|
+
if (parts.second === void 0) return out;
|
|
2390
|
+
out += `:${p2(parts.second)}`;
|
|
2391
|
+
return out;
|
|
2392
|
+
}
|
|
2393
|
+
function toDate(value, options) {
|
|
2394
|
+
const parts = toObject(value);
|
|
2395
|
+
if (parts === void 0 || parts.year === void 0) return void 0;
|
|
2396
|
+
const offsetMinutes = options?.assumeOffsetMinutes;
|
|
2397
|
+
if (!isStated(offsetMinutes)) return void 0;
|
|
2398
|
+
const instant = /* @__PURE__ */ new Date(0);
|
|
2399
|
+
instant.setUTCFullYear(parts.year, (parts.month ?? 1) - 1, parts.day ?? 1);
|
|
2400
|
+
instant.setUTCHours(parts.hour ?? 0, parts.minute ?? 0, parts.second ?? 0, 0);
|
|
2401
|
+
const shifted = new Date(instant.getTime() - offsetMinutes * 6e4);
|
|
2402
|
+
return Number.isNaN(shifted.getTime()) ? void 0 : shifted;
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2312
2405
|
// src/profiles/index.ts
|
|
2313
2406
|
var astmProfiles = Object.freeze({
|
|
2314
2407
|
default: defaultProfile,
|
|
2315
2408
|
referenceCorpus
|
|
2316
2409
|
});
|
|
2317
2410
|
|
|
2318
|
-
// src/terminology/catalog.ts
|
|
2319
|
-
function defineLivdCatalog(entries) {
|
|
2320
|
-
const index = /* @__PURE__ */ new Map();
|
|
2321
|
-
for (const entry of entries) {
|
|
2322
|
-
const bucket = index.get(entry.vendorCode);
|
|
2323
|
-
if (bucket) bucket.push(entry);
|
|
2324
|
-
else index.set(entry.vendorCode, [entry]);
|
|
2325
|
-
}
|
|
2326
|
-
const catalog = {
|
|
2327
|
-
size: index.size,
|
|
2328
|
-
lookup(vendorCode) {
|
|
2329
|
-
const bucket = index.get(vendorCode);
|
|
2330
|
-
if (!bucket || bucket.length === 0) return { status: "unmapped" };
|
|
2331
|
-
const distinct = [...new Set(bucket.map((e) => e.loinc))];
|
|
2332
|
-
if (distinct.length > 1) {
|
|
2333
|
-
return deepFreeze({ status: "ambiguous", candidates: distinct });
|
|
2334
|
-
}
|
|
2335
|
-
const [first] = bucket;
|
|
2336
|
-
if (first === void 0) return { status: "unmapped" };
|
|
2337
|
-
return deepFreeze({
|
|
2338
|
-
status: "mapped",
|
|
2339
|
-
loinc: first.loinc,
|
|
2340
|
-
...first.loincLongName !== void 0 ? { loincLongName: first.loincLongName } : {}
|
|
2341
|
-
});
|
|
2342
|
-
}
|
|
2343
|
-
};
|
|
2344
|
-
return Object.freeze(catalog);
|
|
2345
|
-
}
|
|
2346
|
-
|
|
2347
2411
|
// src/terminology/warnings.ts
|
|
2348
2412
|
var LIVD_WARNING_CODES = {
|
|
2349
2413
|
/**
|
|
@@ -2358,7 +2422,15 @@ var LIVD_WARNING_CODES = {
|
|
|
2358
2422
|
* candidate LOINCs are surfaced for inspection, but **none is chosen**: refusing to pick is the
|
|
2359
2423
|
* fail-safe, since guessing wrong mis-identifies the test.
|
|
2360
2424
|
*/
|
|
2361
|
-
ASTM_LIVD_AMBIGUOUS_MAPPING: "ASTM_LIVD_AMBIGUOUS_MAPPING"
|
|
2425
|
+
ASTM_LIVD_AMBIGUOUS_MAPPING: "ASTM_LIVD_AMBIGUOUS_MAPPING",
|
|
2426
|
+
/**
|
|
2427
|
+
* A consumer-supplied LIVD catalog was defined **without a LOINC version**, so a mapping it
|
|
2428
|
+
* produces cannot say which version of LOINC it was made against. Advisory and **never a
|
|
2429
|
+
* refusal**: the catalog is still built, still indexed and still answers exactly as it would
|
|
2430
|
+
* have. Raised **once, where the catalog is defined**, and never per record: it is a fact about
|
|
2431
|
+
* the catalog rather than about any message, so it never joins the per-record warning stream.
|
|
2432
|
+
*/
|
|
2433
|
+
ASTM_LIVD_CATALOG_NO_LOINC_VERSION: "ASTM_LIVD_CATALOG_NO_LOINC_VERSION"
|
|
2362
2434
|
};
|
|
2363
2435
|
function livdUnmappedCode(position) {
|
|
2364
2436
|
return {
|
|
@@ -2374,44 +2446,208 @@ function livdAmbiguousMapping(position) {
|
|
|
2374
2446
|
position
|
|
2375
2447
|
};
|
|
2376
2448
|
}
|
|
2449
|
+
var LIVD_CATALOG_IDENTITY_UNDECLARED = Object.freeze({
|
|
2450
|
+
declared: false,
|
|
2451
|
+
reason: "The catalog declared neither a publisher nor a publication version, so it declared no identity at all. Nothing stands in for one here: no name is invented, no ordinal or position is substituted, and no value from the catalog's rows is borrowed to label it."
|
|
2452
|
+
});
|
|
2453
|
+
var CATALOG_NO_LOINC_VERSION_MESSAGE = "The LIVD catalog declared no LOINC version, so an annotation it produces cannot state which version of LOINC the mapping was made against. The catalog is built and usable, and nothing about a lookup changes. What the catalog declared about itself is on this warning's catalog field, never in this message.";
|
|
2454
|
+
function livdCatalogMissingLoincVersion(catalog) {
|
|
2455
|
+
return {
|
|
2456
|
+
code: LIVD_WARNING_CODES.ASTM_LIVD_CATALOG_NO_LOINC_VERSION,
|
|
2457
|
+
message: CATALOG_NO_LOINC_VERSION_MESSAGE,
|
|
2458
|
+
catalog
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
// src/terminology/catalog.ts
|
|
2463
|
+
var VERBATIM_UNIT_NOTE = "Selected by comparing the units the record reported with the catalog entry's representative unit verbatim and case sensitively (exact string equality). This is NOT a UCUM semantic comparison: neither side was normalized, case folded, scaled or converted, and this package does not claim full UCUM conformance.";
|
|
2464
|
+
function declaredValue(value) {
|
|
2465
|
+
return value === void 0 || value.trim() === "" ? void 0 : value;
|
|
2466
|
+
}
|
|
2467
|
+
function declaredPublication(publication) {
|
|
2468
|
+
const publisher = declaredValue(publication?.publisher);
|
|
2469
|
+
const publicationVersion = declaredValue(publication?.publicationVersion);
|
|
2470
|
+
const loincVersion = declaredValue(publication?.loincVersion);
|
|
2471
|
+
const loincCopyright = declaredValue(publication?.loincCopyright);
|
|
2472
|
+
const declared = {
|
|
2473
|
+
...publisher !== void 0 ? { publisher } : {},
|
|
2474
|
+
...publicationVersion !== void 0 ? { publicationVersion } : {},
|
|
2475
|
+
...loincVersion !== void 0 ? { loincVersion } : {},
|
|
2476
|
+
...loincCopyright !== void 0 ? { loincCopyright } : {}
|
|
2477
|
+
};
|
|
2478
|
+
return Object.keys(declared).length === 0 ? void 0 : declared;
|
|
2479
|
+
}
|
|
2480
|
+
function identityOf(publication) {
|
|
2481
|
+
const publisher = publication?.publisher;
|
|
2482
|
+
const publicationVersion = publication?.publicationVersion;
|
|
2483
|
+
if (publisher === void 0 && publicationVersion === void 0) {
|
|
2484
|
+
return LIVD_CATALOG_IDENTITY_UNDECLARED;
|
|
2485
|
+
}
|
|
2486
|
+
return {
|
|
2487
|
+
declared: true,
|
|
2488
|
+
...publisher !== void 0 ? { publisher } : {},
|
|
2489
|
+
...publicationVersion !== void 0 ? { publicationVersion } : {}
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
function definitionWarnings(publication) {
|
|
2493
|
+
if (publication?.loincVersion !== void 0) return [];
|
|
2494
|
+
return [livdCatalogMissingLoincVersion(identityOf(publication))];
|
|
2495
|
+
}
|
|
2496
|
+
function carriesLivdAttributes(entry) {
|
|
2497
|
+
return entry.vendorSpecimenDescription !== void 0 || entry.vendorResultDescription !== void 0 || entry.representativeUnit !== void 0;
|
|
2498
|
+
}
|
|
2499
|
+
function isUnitQualified(entry) {
|
|
2500
|
+
const unit = entry.representativeUnit;
|
|
2501
|
+
return unit !== void 0 && unit.trim() !== "";
|
|
2502
|
+
}
|
|
2503
|
+
function usableUnits(reportedUnits) {
|
|
2504
|
+
if (reportedUnits === void 0 || reportedUnits.trim() === "") return void 0;
|
|
2505
|
+
return reportedUnits;
|
|
2506
|
+
}
|
|
2507
|
+
function candidateOf(entry) {
|
|
2508
|
+
return {
|
|
2509
|
+
loinc: entry.loinc,
|
|
2510
|
+
...entry.loincLongName !== void 0 ? { loincLongName: entry.loincLongName } : {},
|
|
2511
|
+
...entry.vendorSpecimenDescription !== void 0 ? { vendorSpecimenDescription: entry.vendorSpecimenDescription } : {},
|
|
2512
|
+
...entry.vendorResultDescription !== void 0 ? { vendorResultDescription: entry.vendorResultDescription } : {},
|
|
2513
|
+
...entry.representativeUnit !== void 0 ? { representativeUnit: entry.representativeUnit } : {},
|
|
2514
|
+
unitQualified: isUnitQualified(entry)
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
function unitComparisonOf(matchedUnits) {
|
|
2518
|
+
return {
|
|
2519
|
+
comparison: "verbatim-case-sensitive",
|
|
2520
|
+
ucumSemantic: false,
|
|
2521
|
+
reportedUnits: matchedUnits,
|
|
2522
|
+
representativeUnit: matchedUnits,
|
|
2523
|
+
note: VERBATIM_UNIT_NOTE
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
function mappedFrom(entry, unitComparison) {
|
|
2527
|
+
return {
|
|
2528
|
+
status: "mapped",
|
|
2529
|
+
loinc: entry.loinc,
|
|
2530
|
+
...entry.loincLongName !== void 0 ? { loincLongName: entry.loincLongName } : {},
|
|
2531
|
+
...entry.vendorSpecimenDescription !== void 0 ? { vendorSpecimenDescription: entry.vendorSpecimenDescription } : {},
|
|
2532
|
+
...entry.vendorResultDescription !== void 0 ? { vendorResultDescription: entry.vendorResultDescription } : {},
|
|
2533
|
+
...entry.representativeUnit !== void 0 ? { representativeUnit: entry.representativeUnit } : {},
|
|
2534
|
+
...unitComparison !== void 0 ? { unitComparison } : {}
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
function ambiguous(candidates, candidateDetails, reason) {
|
|
2538
|
+
return { status: "ambiguous", candidates, candidateDetails, reason };
|
|
2539
|
+
}
|
|
2540
|
+
function defineLivdCatalog(entries, publication) {
|
|
2541
|
+
const index = /* @__PURE__ */ new Map();
|
|
2542
|
+
for (const entry of entries) {
|
|
2543
|
+
const bucket = index.get(entry.vendorCode);
|
|
2544
|
+
if (bucket) bucket.push(entry);
|
|
2545
|
+
else index.set(entry.vendorCode, [entry]);
|
|
2546
|
+
}
|
|
2547
|
+
const declared = declaredPublication(publication);
|
|
2548
|
+
const catalog = {
|
|
2549
|
+
size: index.size,
|
|
2550
|
+
...declared !== void 0 ? { publication: deepFreeze(declared) } : {},
|
|
2551
|
+
warnings: deepFreeze(definitionWarnings(declared)),
|
|
2552
|
+
lookup(vendorCode, reportedUnits) {
|
|
2553
|
+
const bucket = index.get(vendorCode);
|
|
2554
|
+
if (!bucket || bucket.length === 0) return { status: "unmapped" };
|
|
2555
|
+
const distinct = [...new Set(bucket.map((e) => e.loinc))];
|
|
2556
|
+
if (distinct.length === 1) {
|
|
2557
|
+
const [first] = bucket;
|
|
2558
|
+
if (first === void 0) return { status: "unmapped" };
|
|
2559
|
+
return deepFreeze(mappedFrom(first));
|
|
2560
|
+
}
|
|
2561
|
+
if (!bucket.some(carriesLivdAttributes)) {
|
|
2562
|
+
const plain = { status: "ambiguous", candidates: distinct };
|
|
2563
|
+
return deepFreeze(plain);
|
|
2564
|
+
}
|
|
2565
|
+
const details = bucket.map(candidateOf);
|
|
2566
|
+
const units = usableUnits(reportedUnits);
|
|
2567
|
+
if (units === void 0) {
|
|
2568
|
+
return deepFreeze(ambiguous(distinct, details, "no-reported-units"));
|
|
2569
|
+
}
|
|
2570
|
+
const matches = bucket.filter((e) => isUnitQualified(e) && e.representativeUnit === units);
|
|
2571
|
+
const matchedLoincs = [...new Set(matches.map((e) => e.loinc))];
|
|
2572
|
+
const [chosen] = matches;
|
|
2573
|
+
if (matchedLoincs.length === 1 && chosen !== void 0) {
|
|
2574
|
+
return deepFreeze(mappedFrom(chosen, unitComparisonOf(units)));
|
|
2575
|
+
}
|
|
2576
|
+
return deepFreeze(
|
|
2577
|
+
ambiguous(
|
|
2578
|
+
distinct,
|
|
2579
|
+
details,
|
|
2580
|
+
matchedLoincs.length === 0 ? "no-candidate-matched-units" : "multiple-candidates-matched-units"
|
|
2581
|
+
)
|
|
2582
|
+
);
|
|
2583
|
+
}
|
|
2584
|
+
};
|
|
2585
|
+
return Object.freeze(catalog);
|
|
2586
|
+
}
|
|
2377
2587
|
|
|
2378
2588
|
// src/terminology/apply.ts
|
|
2379
|
-
function mapTestId(uid, catalog) {
|
|
2380
|
-
|
|
2381
|
-
|
|
2589
|
+
function mapTestId(uid, catalog, reportedUnits) {
|
|
2590
|
+
const code = uid.localCode;
|
|
2591
|
+
if (code === void 0) {
|
|
2592
|
+
return uid.unvalidatedWireValue !== void 0 ? { status: "no-vendor-code" } : { status: "no-code" };
|
|
2382
2593
|
}
|
|
2383
|
-
const
|
|
2384
|
-
if (code === void 0) return { status: "no-code" };
|
|
2385
|
-
const hit = catalog.lookup(code);
|
|
2594
|
+
const hit = catalog.lookup(code, reportedUnits);
|
|
2386
2595
|
switch (hit.status) {
|
|
2387
2596
|
case "mapped":
|
|
2597
|
+
if (hit.loinc.length === 0) return { status: "unmapped" };
|
|
2388
2598
|
return {
|
|
2389
2599
|
status: "mapped",
|
|
2390
2600
|
loinc: hit.loinc,
|
|
2391
2601
|
...hit.loincLongName !== void 0 ? { loincLongName: hit.loincLongName } : {},
|
|
2602
|
+
...hit.vendorSpecimenDescription !== void 0 ? { vendorSpecimenDescription: hit.vendorSpecimenDescription } : {},
|
|
2603
|
+
...hit.vendorResultDescription !== void 0 ? { vendorResultDescription: hit.vendorResultDescription } : {},
|
|
2604
|
+
...hit.representativeUnit !== void 0 ? { representativeUnit: hit.representativeUnit } : {},
|
|
2605
|
+
...hit.unitComparison !== void 0 ? { unitComparison: hit.unitComparison } : {},
|
|
2392
2606
|
source: "livd",
|
|
2393
2607
|
derived: true
|
|
2394
2608
|
};
|
|
2395
2609
|
case "ambiguous":
|
|
2396
|
-
return {
|
|
2610
|
+
return {
|
|
2611
|
+
status: "ambiguous",
|
|
2612
|
+
candidates: hit.candidates,
|
|
2613
|
+
...hit.candidateDetails !== void 0 ? { candidateDetails: hit.candidateDetails } : {},
|
|
2614
|
+
...hit.reason !== void 0 ? { reason: hit.reason } : {}
|
|
2615
|
+
};
|
|
2397
2616
|
case "unmapped":
|
|
2398
2617
|
return { status: "unmapped" };
|
|
2399
2618
|
}
|
|
2400
2619
|
}
|
|
2620
|
+
function unitsOf(record) {
|
|
2621
|
+
return record.type === "R" ? record.units : void 0;
|
|
2622
|
+
}
|
|
2623
|
+
function disagrees(uid, mapping) {
|
|
2624
|
+
if (mapping.status !== "mapped") return false;
|
|
2625
|
+
const wire = uid.unvalidatedWireValue;
|
|
2626
|
+
return wire !== void 0 && wire !== mapping.loinc;
|
|
2627
|
+
}
|
|
2628
|
+
function declaredLoincVersion(catalog) {
|
|
2629
|
+
const version = catalog.publication?.loincVersion;
|
|
2630
|
+
return version === void 0 || version.trim() === "" ? void 0 : version;
|
|
2631
|
+
}
|
|
2401
2632
|
function testIdOf(record) {
|
|
2402
2633
|
return record.universalTestId;
|
|
2403
2634
|
}
|
|
2404
2635
|
function lookupLivdForRecord(record, catalog) {
|
|
2405
2636
|
const uid = testIdOf(record);
|
|
2406
2637
|
const recognized = uid ?? recognizeUniversalTestId([]);
|
|
2407
|
-
const mapping = mapTestId(recognized, catalog);
|
|
2408
|
-
const reportedCode =
|
|
2638
|
+
const mapping = mapTestId(recognized, catalog, unitsOf(record));
|
|
2639
|
+
const reportedCode = recognized.localCode;
|
|
2640
|
+
const wireValue = recognized.unvalidatedWireValue;
|
|
2641
|
+
const catalogLoincVersion = declaredLoincVersion(catalog);
|
|
2409
2642
|
return {
|
|
2410
2643
|
recordIndex: record.recordIndex,
|
|
2411
2644
|
recordType: record.type,
|
|
2412
2645
|
...reportedCode !== void 0 ? { reportedCode } : {},
|
|
2646
|
+
...wireValue !== void 0 ? { unvalidatedWireValue: wireValue } : {},
|
|
2647
|
+
wireValueDisagreesWithCatalog: disagrees(recognized, mapping),
|
|
2413
2648
|
provenance: recognized.provenance,
|
|
2414
|
-
mapping
|
|
2649
|
+
mapping,
|
|
2650
|
+
...catalogLoincVersion !== void 0 ? { catalogLoincVersion } : {}
|
|
2415
2651
|
};
|
|
2416
2652
|
}
|
|
2417
2653
|
function applyLivd(msg, catalog) {
|
|
@@ -2908,8 +3144,10 @@ function concatBytes3(chunks) {
|
|
|
2908
3144
|
}
|
|
2909
3145
|
|
|
2910
3146
|
// src/index.ts
|
|
2911
|
-
var VERSION = "0.
|
|
3147
|
+
var VERSION = "0.1.1";
|
|
2912
3148
|
|
|
3149
|
+
exports.ABNORMAL_FLAG_CODES = ABNORMAL_FLAG_CODES;
|
|
3150
|
+
exports.ABNORMAL_FLAG_VOCABULARY = ABNORMAL_FLAG_VOCABULARY;
|
|
2913
3151
|
exports.ALL_ASTM_WARNING_CODES = ALL_ASTM_WARNING_CODES;
|
|
2914
3152
|
exports.AMBIGUOUS_CODES = AMBIGUOUS_CODES;
|
|
2915
3153
|
exports.ASTM_ACK = ACK;
|
|
@@ -2926,8 +3164,10 @@ exports.AstmStrictError = AstmStrictError;
|
|
|
2926
3164
|
exports.CANONICAL_DELIMITERS = CANONICAL_DELIMITERS;
|
|
2927
3165
|
exports.FATAL_CODES = FATAL_CODES;
|
|
2928
3166
|
exports.FRAME_WARNING_CODES = FRAME_WARNING_CODES;
|
|
3167
|
+
exports.LIVD_CATALOG_IDENTITY_UNDECLARED = LIVD_CATALOG_IDENTITY_UNDECLARED;
|
|
2929
3168
|
exports.LIVD_WARNING_CODES = LIVD_WARNING_CODES;
|
|
2930
3169
|
exports.LTP_WARNING_CODES = LTP_WARNING_CODES;
|
|
3170
|
+
exports.RESULT_STATUS_VOCABULARY = RESULT_STATUS_VOCABULARY;
|
|
2931
3171
|
exports.SAFETY_CRITICAL_CODES = SAFETY_CRITICAL_CODES;
|
|
2932
3172
|
exports.TOLERABLE_CODES = TOLERABLE_CODES;
|
|
2933
3173
|
exports.VERSION = VERSION;
|
|
@@ -2958,6 +3198,7 @@ exports.defineLivdCatalog = defineLivdCatalog;
|
|
|
2958
3198
|
exports.delimiterRoleCollision = delimiterRoleCollision;
|
|
2959
3199
|
exports.delimiterSwallowedByEscape = delimiterSwallowedByEscape;
|
|
2960
3200
|
exports.delimitersRedeclared = delimitersRedeclared;
|
|
3201
|
+
exports.describeVocabulary = describeVocabulary;
|
|
2961
3202
|
exports.detectFraming = detectFraming;
|
|
2962
3203
|
exports.encodeComponent = encodeComponent;
|
|
2963
3204
|
exports.fieldScalar = fieldScalar;
|
|
@@ -2975,6 +3216,7 @@ exports.isNonStandard = isNonStandard;
|
|
|
2975
3216
|
exports.isSafetyCriticalCode = isSafetyCriticalCode;
|
|
2976
3217
|
exports.listAstmProfiles = listAstmProfiles;
|
|
2977
3218
|
exports.livdAmbiguousMapping = livdAmbiguousMapping;
|
|
3219
|
+
exports.livdCatalogMissingLoincVersion = livdCatalogMissingLoincVersion;
|
|
2978
3220
|
exports.livdUnmappedCode = livdUnmappedCode;
|
|
2979
3221
|
exports.lookupLivdForRecord = lookupLivdForRecord;
|
|
2980
3222
|
exports.ltpAmbiguousTransport = ltpAmbiguousTransport;
|
|
@@ -3008,6 +3250,9 @@ exports.serializeFramedAstm = serializeFramedAstm;
|
|
|
3008
3250
|
exports.setDefaultAstmProfile = setDefaultAstmProfile;
|
|
3009
3251
|
exports.splitEscapeAware = splitEscapeAware;
|
|
3010
3252
|
exports.toChecksumHex = toChecksumHex;
|
|
3253
|
+
exports.toDate = toDate;
|
|
3254
|
+
exports.toISO = toISO;
|
|
3255
|
+
exports.toObject = toObject;
|
|
3011
3256
|
exports.tokenizeHeader = tokenizeHeader;
|
|
3012
3257
|
exports.tokenizeRecord = tokenizeRecord;
|
|
3013
3258
|
exports.undefinedAbnormalFlag = undefinedAbnormalFlag;
|