@cosyte/astm 0.0.21 → 0.1.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/CHANGELOG.md +411 -0
- package/README.md +558 -223
- package/dist/index.cjs +309 -64
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1054 -177
- package/dist/index.d.ts +1054 -177
- package/dist/index.mjs +301 -65
- package/dist/index.mjs.map +1 -1
- package/package.json +58 -31
package/dist/index.mjs
CHANGED
|
@@ -100,13 +100,13 @@ function parseAstmDate(raw) {
|
|
|
100
100
|
return { ...base, month, day, hour, minute, second, precision: "second" };
|
|
101
101
|
}
|
|
102
102
|
function astmDateToLocalISO(d) {
|
|
103
|
-
const
|
|
103
|
+
const p22 = (n) => String(n).padStart(2, "0");
|
|
104
104
|
let out = String(d.year).padStart(4, "0");
|
|
105
|
-
if (d.month !== void 0) out += `-${
|
|
106
|
-
if (d.day !== void 0) out += `-${
|
|
107
|
-
if (d.hour !== void 0) out += `T${
|
|
108
|
-
if (d.minute !== void 0) out += `:${
|
|
109
|
-
if (d.second !== void 0) out += `:${
|
|
105
|
+
if (d.month !== void 0) out += `-${p22(d.month)}`;
|
|
106
|
+
if (d.day !== void 0) out += `-${p22(d.day)}`;
|
|
107
|
+
if (d.hour !== void 0) out += `T${p22(d.hour)}`;
|
|
108
|
+
if (d.minute !== void 0) out += `:${p22(d.minute)}`;
|
|
109
|
+
if (d.second !== void 0) out += `:${p22(d.second)}`;
|
|
110
110
|
return out;
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -116,26 +116,40 @@ function recognizeUniversalTestId(components) {
|
|
|
116
116
|
const c = components[i];
|
|
117
117
|
return c !== void 0 && c.length > 0 ? c : void 0;
|
|
118
118
|
};
|
|
119
|
-
const
|
|
119
|
+
const unvalidatedWireValue = nonEmpty(0);
|
|
120
120
|
const testName = nonEmpty(1);
|
|
121
121
|
const codingScheme = nonEmpty(2);
|
|
122
122
|
const localCode = nonEmpty(3);
|
|
123
123
|
let provenance;
|
|
124
|
-
if (
|
|
125
|
-
else if (
|
|
124
|
+
if (localCode !== void 0) provenance = "local-code";
|
|
125
|
+
else if (unvalidatedWireValue !== void 0) provenance = "unvalidated-wire-value-only";
|
|
126
126
|
else if (testName !== void 0) provenance = "name-only";
|
|
127
127
|
else provenance = "empty";
|
|
128
128
|
const base = { components, provenance };
|
|
129
129
|
return {
|
|
130
130
|
...base,
|
|
131
|
-
...
|
|
131
|
+
...unvalidatedWireValue !== void 0 ? { unvalidatedWireValue } : {},
|
|
132
132
|
...testName !== void 0 ? { testName } : {},
|
|
133
133
|
...codingScheme !== void 0 ? { codingScheme } : {},
|
|
134
134
|
...localCode !== void 0 ? { localCode } : {}
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
function primaryCode(u) {
|
|
138
|
-
return u.
|
|
138
|
+
return u.localCode;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/common/vocabulary.ts
|
|
142
|
+
var ABNORMAL_FLAG_VOCABULARY = Object.freeze({
|
|
143
|
+
attributed: true,
|
|
144
|
+
system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
|
|
145
|
+
version: "4.0.0"
|
|
146
|
+
});
|
|
147
|
+
var RESULT_STATUS_VOCABULARY = Object.freeze({
|
|
148
|
+
attributed: false,
|
|
149
|
+
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."
|
|
150
|
+
});
|
|
151
|
+
function describeVocabulary(vocabulary) {
|
|
152
|
+
return vocabulary.attributed ? `graded against ${vocabulary.system} version ${vocabulary.version}` : vocabulary.reason;
|
|
139
153
|
}
|
|
140
154
|
|
|
141
155
|
// src/common/warnings.ts
|
|
@@ -745,14 +759,14 @@ function ambiguousValueSplit(position) {
|
|
|
745
759
|
function undefinedAbnormalFlag(position) {
|
|
746
760
|
return {
|
|
747
761
|
code: WARNING_CODES.ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG,
|
|
748
|
-
message:
|
|
762
|
+
message: `Abnormal flag is not a code in the vocabulary it was ${describeVocabulary(ABNORMAL_FLAG_VOCABULARY)}, surfaced as undefined, never as normal.`,
|
|
749
763
|
position
|
|
750
764
|
};
|
|
751
765
|
}
|
|
752
766
|
function undefinedResultStatus(position) {
|
|
753
767
|
return {
|
|
754
768
|
code: WARNING_CODES.ASTM_RECORD_UNDEFINED_RESULT_STATUS,
|
|
755
|
-
message:
|
|
769
|
+
message: `Result status is not a recognized status letter, surfaced as undefined. ${describeVocabulary(RESULT_STATUS_VOCABULARY)}`,
|
|
756
770
|
position
|
|
757
771
|
};
|
|
758
772
|
}
|
|
@@ -826,14 +840,25 @@ var ABNORMAL_FLAG_MEANINGS = {
|
|
|
826
840
|
W: "worse",
|
|
827
841
|
S: "susceptible",
|
|
828
842
|
R: "resistant",
|
|
829
|
-
I: "intermediate"
|
|
843
|
+
I: "intermediate",
|
|
844
|
+
HU: "significantly-high",
|
|
845
|
+
LU: "significantly-low"
|
|
830
846
|
};
|
|
847
|
+
var ABNORMAL_FLAG_CODES = Object.freeze(
|
|
848
|
+
Object.keys(ABNORMAL_FLAG_MEANINGS)
|
|
849
|
+
);
|
|
831
850
|
function interpretAbnormalFlag(raw) {
|
|
832
851
|
const key = raw.trim();
|
|
833
852
|
if (isAbnormalFlagCode(key)) {
|
|
834
|
-
return {
|
|
853
|
+
return {
|
|
854
|
+
raw,
|
|
855
|
+
code: key,
|
|
856
|
+
meaning: ABNORMAL_FLAG_MEANINGS[key],
|
|
857
|
+
recognized: true,
|
|
858
|
+
vocabulary: ABNORMAL_FLAG_VOCABULARY
|
|
859
|
+
};
|
|
835
860
|
}
|
|
836
|
-
return { raw, meaning: "undefined", recognized: false };
|
|
861
|
+
return { raw, meaning: "undefined", recognized: false, vocabulary: ABNORMAL_FLAG_VOCABULARY };
|
|
837
862
|
}
|
|
838
863
|
function isAbnormalFlagCode(value) {
|
|
839
864
|
return Object.prototype.hasOwnProperty.call(ABNORMAL_FLAG_MEANINGS, value);
|
|
@@ -854,7 +879,8 @@ function interpretResultStatus(raw) {
|
|
|
854
879
|
recognized: false,
|
|
855
880
|
isActiveFinal: false,
|
|
856
881
|
supersedes: false,
|
|
857
|
-
cancelled: false
|
|
882
|
+
cancelled: false,
|
|
883
|
+
vocabulary: RESULT_STATUS_VOCABULARY
|
|
858
884
|
};
|
|
859
885
|
}
|
|
860
886
|
const key = raw.trim();
|
|
@@ -866,7 +892,8 @@ function interpretResultStatus(raw) {
|
|
|
866
892
|
recognized: true,
|
|
867
893
|
isActiveFinal: key === "F",
|
|
868
894
|
supersedes: key === "C",
|
|
869
|
-
cancelled: key === "X"
|
|
895
|
+
cancelled: key === "X",
|
|
896
|
+
vocabulary: RESULT_STATUS_VOCABULARY
|
|
870
897
|
};
|
|
871
898
|
}
|
|
872
899
|
return {
|
|
@@ -875,7 +902,8 @@ function interpretResultStatus(raw) {
|
|
|
875
902
|
recognized: false,
|
|
876
903
|
isActiveFinal: false,
|
|
877
904
|
supersedes: false,
|
|
878
|
-
cancelled: false
|
|
905
|
+
cancelled: false,
|
|
906
|
+
vocabulary: RESULT_STATUS_VOCABULARY
|
|
879
907
|
};
|
|
880
908
|
}
|
|
881
909
|
function isResultStatusCode(value) {
|
|
@@ -1800,10 +1828,10 @@ function buildResult(recordIndex, fields, warnings) {
|
|
|
1800
1828
|
13,
|
|
1801
1829
|
warnings
|
|
1802
1830
|
);
|
|
1803
|
-
const
|
|
1804
|
-
const valueComponents =
|
|
1805
|
-
const value =
|
|
1806
|
-
if (
|
|
1831
|
+
const ambiguous2 = valueField !== void 0 && valueField.components.length > 1;
|
|
1832
|
+
const valueComponents = ambiguous2 ? valueField.components : void 0;
|
|
1833
|
+
const value = ambiguous2 ? valueField.raw : fieldScalar(valueField);
|
|
1834
|
+
if (ambiguous2) {
|
|
1807
1835
|
warnings.push(ambiguousValueSplit({ recordIndex, recordType: "R", fieldIndex: 4 }));
|
|
1808
1836
|
}
|
|
1809
1837
|
const units = fieldScalar(astmField(fields, 5));
|
|
@@ -2307,41 +2335,77 @@ function query(msg) {
|
|
|
2307
2335
|
return msg.records.filter((r) => r.type === "Q");
|
|
2308
2336
|
}
|
|
2309
2337
|
|
|
2338
|
+
// src/common/date-conversion.ts
|
|
2339
|
+
function isStated(component) {
|
|
2340
|
+
return typeof component === "number" && Number.isFinite(component);
|
|
2341
|
+
}
|
|
2342
|
+
function p2(n) {
|
|
2343
|
+
return String(n).padStart(2, "0");
|
|
2344
|
+
}
|
|
2345
|
+
function isLeapYear(year) {
|
|
2346
|
+
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
2347
|
+
}
|
|
2348
|
+
function longestDay(year, month) {
|
|
2349
|
+
if (month === 2) return year === void 0 || isLeapYear(year) ? 29 : 28;
|
|
2350
|
+
return month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;
|
|
2351
|
+
}
|
|
2352
|
+
function inRange(component, low, high) {
|
|
2353
|
+
if (component === void 0) return true;
|
|
2354
|
+
return Number.isInteger(component) && component >= low && component <= high;
|
|
2355
|
+
}
|
|
2356
|
+
function statesARealCalendarDate(parts) {
|
|
2357
|
+
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
|
|
2358
|
+
// is an Invalid Date to every JS reader, so accepting it here would hand out
|
|
2359
|
+
// a string the next hop cannot read.
|
|
2360
|
+
inRange(parts.second, 0, 59);
|
|
2361
|
+
}
|
|
2362
|
+
function toObject(value) {
|
|
2363
|
+
if (value === null || value === void 0) return void 0;
|
|
2364
|
+
const parts = {};
|
|
2365
|
+
if (isStated(value.year)) parts.year = value.year;
|
|
2366
|
+
if (isStated(value.month)) parts.month = value.month;
|
|
2367
|
+
if (isStated(value.day)) parts.day = value.day;
|
|
2368
|
+
if (isStated(value.hour)) parts.hour = value.hour;
|
|
2369
|
+
if (isStated(value.minute)) parts.minute = value.minute;
|
|
2370
|
+
if (isStated(value.second)) parts.second = value.second;
|
|
2371
|
+
if (Object.keys(parts).length === 0) return void 0;
|
|
2372
|
+
if (!statesARealCalendarDate(parts)) return void 0;
|
|
2373
|
+
return Object.freeze(parts);
|
|
2374
|
+
}
|
|
2375
|
+
function toISO(value) {
|
|
2376
|
+
const parts = toObject(value);
|
|
2377
|
+
if (parts === void 0 || parts.year === void 0) return void 0;
|
|
2378
|
+
let out = String(parts.year).padStart(4, "0");
|
|
2379
|
+
if (parts.month === void 0) return out;
|
|
2380
|
+
out += `-${p2(parts.month)}`;
|
|
2381
|
+
if (parts.day === void 0) return out;
|
|
2382
|
+
out += `-${p2(parts.day)}`;
|
|
2383
|
+
if (parts.hour === void 0) return out;
|
|
2384
|
+
out += `T${p2(parts.hour)}`;
|
|
2385
|
+
if (parts.minute === void 0) return out;
|
|
2386
|
+
out += `:${p2(parts.minute)}`;
|
|
2387
|
+
if (parts.second === void 0) return out;
|
|
2388
|
+
out += `:${p2(parts.second)}`;
|
|
2389
|
+
return out;
|
|
2390
|
+
}
|
|
2391
|
+
function toDate(value, options) {
|
|
2392
|
+
const parts = toObject(value);
|
|
2393
|
+
if (parts === void 0 || parts.year === void 0) return void 0;
|
|
2394
|
+
const offsetMinutes = options?.assumeOffsetMinutes;
|
|
2395
|
+
if (!isStated(offsetMinutes)) return void 0;
|
|
2396
|
+
const instant = /* @__PURE__ */ new Date(0);
|
|
2397
|
+
instant.setUTCFullYear(parts.year, (parts.month ?? 1) - 1, parts.day ?? 1);
|
|
2398
|
+
instant.setUTCHours(parts.hour ?? 0, parts.minute ?? 0, parts.second ?? 0, 0);
|
|
2399
|
+
const shifted = new Date(instant.getTime() - offsetMinutes * 6e4);
|
|
2400
|
+
return Number.isNaN(shifted.getTime()) ? void 0 : shifted;
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2310
2403
|
// src/profiles/index.ts
|
|
2311
2404
|
var astmProfiles = Object.freeze({
|
|
2312
2405
|
default: defaultProfile,
|
|
2313
2406
|
referenceCorpus
|
|
2314
2407
|
});
|
|
2315
2408
|
|
|
2316
|
-
// src/terminology/catalog.ts
|
|
2317
|
-
function defineLivdCatalog(entries) {
|
|
2318
|
-
const index = /* @__PURE__ */ new Map();
|
|
2319
|
-
for (const entry of entries) {
|
|
2320
|
-
const bucket = index.get(entry.vendorCode);
|
|
2321
|
-
if (bucket) bucket.push(entry);
|
|
2322
|
-
else index.set(entry.vendorCode, [entry]);
|
|
2323
|
-
}
|
|
2324
|
-
const catalog = {
|
|
2325
|
-
size: index.size,
|
|
2326
|
-
lookup(vendorCode) {
|
|
2327
|
-
const bucket = index.get(vendorCode);
|
|
2328
|
-
if (!bucket || bucket.length === 0) return { status: "unmapped" };
|
|
2329
|
-
const distinct = [...new Set(bucket.map((e) => e.loinc))];
|
|
2330
|
-
if (distinct.length > 1) {
|
|
2331
|
-
return deepFreeze({ status: "ambiguous", candidates: distinct });
|
|
2332
|
-
}
|
|
2333
|
-
const [first] = bucket;
|
|
2334
|
-
if (first === void 0) return { status: "unmapped" };
|
|
2335
|
-
return deepFreeze({
|
|
2336
|
-
status: "mapped",
|
|
2337
|
-
loinc: first.loinc,
|
|
2338
|
-
...first.loincLongName !== void 0 ? { loincLongName: first.loincLongName } : {}
|
|
2339
|
-
});
|
|
2340
|
-
}
|
|
2341
|
-
};
|
|
2342
|
-
return Object.freeze(catalog);
|
|
2343
|
-
}
|
|
2344
|
-
|
|
2345
2409
|
// src/terminology/warnings.ts
|
|
2346
2410
|
var LIVD_WARNING_CODES = {
|
|
2347
2411
|
/**
|
|
@@ -2356,7 +2420,15 @@ var LIVD_WARNING_CODES = {
|
|
|
2356
2420
|
* candidate LOINCs are surfaced for inspection, but **none is chosen**: refusing to pick is the
|
|
2357
2421
|
* fail-safe, since guessing wrong mis-identifies the test.
|
|
2358
2422
|
*/
|
|
2359
|
-
ASTM_LIVD_AMBIGUOUS_MAPPING: "ASTM_LIVD_AMBIGUOUS_MAPPING"
|
|
2423
|
+
ASTM_LIVD_AMBIGUOUS_MAPPING: "ASTM_LIVD_AMBIGUOUS_MAPPING",
|
|
2424
|
+
/**
|
|
2425
|
+
* A consumer-supplied LIVD catalog was defined **without a LOINC version**, so a mapping it
|
|
2426
|
+
* produces cannot say which version of LOINC it was made against. Advisory and **never a
|
|
2427
|
+
* refusal**: the catalog is still built, still indexed and still answers exactly as it would
|
|
2428
|
+
* have. Raised **once, where the catalog is defined**, and never per record: it is a fact about
|
|
2429
|
+
* the catalog rather than about any message, so it never joins the per-record warning stream.
|
|
2430
|
+
*/
|
|
2431
|
+
ASTM_LIVD_CATALOG_NO_LOINC_VERSION: "ASTM_LIVD_CATALOG_NO_LOINC_VERSION"
|
|
2360
2432
|
};
|
|
2361
2433
|
function livdUnmappedCode(position) {
|
|
2362
2434
|
return {
|
|
@@ -2372,44 +2444,208 @@ function livdAmbiguousMapping(position) {
|
|
|
2372
2444
|
position
|
|
2373
2445
|
};
|
|
2374
2446
|
}
|
|
2447
|
+
var LIVD_CATALOG_IDENTITY_UNDECLARED = Object.freeze({
|
|
2448
|
+
declared: false,
|
|
2449
|
+
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."
|
|
2450
|
+
});
|
|
2451
|
+
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.";
|
|
2452
|
+
function livdCatalogMissingLoincVersion(catalog) {
|
|
2453
|
+
return {
|
|
2454
|
+
code: LIVD_WARNING_CODES.ASTM_LIVD_CATALOG_NO_LOINC_VERSION,
|
|
2455
|
+
message: CATALOG_NO_LOINC_VERSION_MESSAGE,
|
|
2456
|
+
catalog
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
// src/terminology/catalog.ts
|
|
2461
|
+
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.";
|
|
2462
|
+
function declaredValue(value) {
|
|
2463
|
+
return value === void 0 || value.trim() === "" ? void 0 : value;
|
|
2464
|
+
}
|
|
2465
|
+
function declaredPublication(publication) {
|
|
2466
|
+
const publisher = declaredValue(publication?.publisher);
|
|
2467
|
+
const publicationVersion = declaredValue(publication?.publicationVersion);
|
|
2468
|
+
const loincVersion = declaredValue(publication?.loincVersion);
|
|
2469
|
+
const loincCopyright = declaredValue(publication?.loincCopyright);
|
|
2470
|
+
const declared = {
|
|
2471
|
+
...publisher !== void 0 ? { publisher } : {},
|
|
2472
|
+
...publicationVersion !== void 0 ? { publicationVersion } : {},
|
|
2473
|
+
...loincVersion !== void 0 ? { loincVersion } : {},
|
|
2474
|
+
...loincCopyright !== void 0 ? { loincCopyright } : {}
|
|
2475
|
+
};
|
|
2476
|
+
return Object.keys(declared).length === 0 ? void 0 : declared;
|
|
2477
|
+
}
|
|
2478
|
+
function identityOf(publication) {
|
|
2479
|
+
const publisher = publication?.publisher;
|
|
2480
|
+
const publicationVersion = publication?.publicationVersion;
|
|
2481
|
+
if (publisher === void 0 && publicationVersion === void 0) {
|
|
2482
|
+
return LIVD_CATALOG_IDENTITY_UNDECLARED;
|
|
2483
|
+
}
|
|
2484
|
+
return {
|
|
2485
|
+
declared: true,
|
|
2486
|
+
...publisher !== void 0 ? { publisher } : {},
|
|
2487
|
+
...publicationVersion !== void 0 ? { publicationVersion } : {}
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
function definitionWarnings(publication) {
|
|
2491
|
+
if (publication?.loincVersion !== void 0) return [];
|
|
2492
|
+
return [livdCatalogMissingLoincVersion(identityOf(publication))];
|
|
2493
|
+
}
|
|
2494
|
+
function carriesLivdAttributes(entry) {
|
|
2495
|
+
return entry.vendorSpecimenDescription !== void 0 || entry.vendorResultDescription !== void 0 || entry.representativeUnit !== void 0;
|
|
2496
|
+
}
|
|
2497
|
+
function isUnitQualified(entry) {
|
|
2498
|
+
const unit = entry.representativeUnit;
|
|
2499
|
+
return unit !== void 0 && unit.trim() !== "";
|
|
2500
|
+
}
|
|
2501
|
+
function usableUnits(reportedUnits) {
|
|
2502
|
+
if (reportedUnits === void 0 || reportedUnits.trim() === "") return void 0;
|
|
2503
|
+
return reportedUnits;
|
|
2504
|
+
}
|
|
2505
|
+
function candidateOf(entry) {
|
|
2506
|
+
return {
|
|
2507
|
+
loinc: entry.loinc,
|
|
2508
|
+
...entry.loincLongName !== void 0 ? { loincLongName: entry.loincLongName } : {},
|
|
2509
|
+
...entry.vendorSpecimenDescription !== void 0 ? { vendorSpecimenDescription: entry.vendorSpecimenDescription } : {},
|
|
2510
|
+
...entry.vendorResultDescription !== void 0 ? { vendorResultDescription: entry.vendorResultDescription } : {},
|
|
2511
|
+
...entry.representativeUnit !== void 0 ? { representativeUnit: entry.representativeUnit } : {},
|
|
2512
|
+
unitQualified: isUnitQualified(entry)
|
|
2513
|
+
};
|
|
2514
|
+
}
|
|
2515
|
+
function unitComparisonOf(matchedUnits) {
|
|
2516
|
+
return {
|
|
2517
|
+
comparison: "verbatim-case-sensitive",
|
|
2518
|
+
ucumSemantic: false,
|
|
2519
|
+
reportedUnits: matchedUnits,
|
|
2520
|
+
representativeUnit: matchedUnits,
|
|
2521
|
+
note: VERBATIM_UNIT_NOTE
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
function mappedFrom(entry, unitComparison) {
|
|
2525
|
+
return {
|
|
2526
|
+
status: "mapped",
|
|
2527
|
+
loinc: entry.loinc,
|
|
2528
|
+
...entry.loincLongName !== void 0 ? { loincLongName: entry.loincLongName } : {},
|
|
2529
|
+
...entry.vendorSpecimenDescription !== void 0 ? { vendorSpecimenDescription: entry.vendorSpecimenDescription } : {},
|
|
2530
|
+
...entry.vendorResultDescription !== void 0 ? { vendorResultDescription: entry.vendorResultDescription } : {},
|
|
2531
|
+
...entry.representativeUnit !== void 0 ? { representativeUnit: entry.representativeUnit } : {},
|
|
2532
|
+
...unitComparison !== void 0 ? { unitComparison } : {}
|
|
2533
|
+
};
|
|
2534
|
+
}
|
|
2535
|
+
function ambiguous(candidates, candidateDetails, reason) {
|
|
2536
|
+
return { status: "ambiguous", candidates, candidateDetails, reason };
|
|
2537
|
+
}
|
|
2538
|
+
function defineLivdCatalog(entries, publication) {
|
|
2539
|
+
const index = /* @__PURE__ */ new Map();
|
|
2540
|
+
for (const entry of entries) {
|
|
2541
|
+
const bucket = index.get(entry.vendorCode);
|
|
2542
|
+
if (bucket) bucket.push(entry);
|
|
2543
|
+
else index.set(entry.vendorCode, [entry]);
|
|
2544
|
+
}
|
|
2545
|
+
const declared = declaredPublication(publication);
|
|
2546
|
+
const catalog = {
|
|
2547
|
+
size: index.size,
|
|
2548
|
+
...declared !== void 0 ? { publication: deepFreeze(declared) } : {},
|
|
2549
|
+
warnings: deepFreeze(definitionWarnings(declared)),
|
|
2550
|
+
lookup(vendorCode, reportedUnits) {
|
|
2551
|
+
const bucket = index.get(vendorCode);
|
|
2552
|
+
if (!bucket || bucket.length === 0) return { status: "unmapped" };
|
|
2553
|
+
const distinct = [...new Set(bucket.map((e) => e.loinc))];
|
|
2554
|
+
if (distinct.length === 1) {
|
|
2555
|
+
const [first] = bucket;
|
|
2556
|
+
if (first === void 0) return { status: "unmapped" };
|
|
2557
|
+
return deepFreeze(mappedFrom(first));
|
|
2558
|
+
}
|
|
2559
|
+
if (!bucket.some(carriesLivdAttributes)) {
|
|
2560
|
+
const plain = { status: "ambiguous", candidates: distinct };
|
|
2561
|
+
return deepFreeze(plain);
|
|
2562
|
+
}
|
|
2563
|
+
const details = bucket.map(candidateOf);
|
|
2564
|
+
const units = usableUnits(reportedUnits);
|
|
2565
|
+
if (units === void 0) {
|
|
2566
|
+
return deepFreeze(ambiguous(distinct, details, "no-reported-units"));
|
|
2567
|
+
}
|
|
2568
|
+
const matches = bucket.filter((e) => isUnitQualified(e) && e.representativeUnit === units);
|
|
2569
|
+
const matchedLoincs = [...new Set(matches.map((e) => e.loinc))];
|
|
2570
|
+
const [chosen] = matches;
|
|
2571
|
+
if (matchedLoincs.length === 1 && chosen !== void 0) {
|
|
2572
|
+
return deepFreeze(mappedFrom(chosen, unitComparisonOf(units)));
|
|
2573
|
+
}
|
|
2574
|
+
return deepFreeze(
|
|
2575
|
+
ambiguous(
|
|
2576
|
+
distinct,
|
|
2577
|
+
details,
|
|
2578
|
+
matchedLoincs.length === 0 ? "no-candidate-matched-units" : "multiple-candidates-matched-units"
|
|
2579
|
+
)
|
|
2580
|
+
);
|
|
2581
|
+
}
|
|
2582
|
+
};
|
|
2583
|
+
return Object.freeze(catalog);
|
|
2584
|
+
}
|
|
2375
2585
|
|
|
2376
2586
|
// src/terminology/apply.ts
|
|
2377
|
-
function mapTestId(uid, catalog) {
|
|
2378
|
-
|
|
2379
|
-
|
|
2587
|
+
function mapTestId(uid, catalog, reportedUnits) {
|
|
2588
|
+
const code = uid.localCode;
|
|
2589
|
+
if (code === void 0) {
|
|
2590
|
+
return uid.unvalidatedWireValue !== void 0 ? { status: "no-vendor-code" } : { status: "no-code" };
|
|
2380
2591
|
}
|
|
2381
|
-
const
|
|
2382
|
-
if (code === void 0) return { status: "no-code" };
|
|
2383
|
-
const hit = catalog.lookup(code);
|
|
2592
|
+
const hit = catalog.lookup(code, reportedUnits);
|
|
2384
2593
|
switch (hit.status) {
|
|
2385
2594
|
case "mapped":
|
|
2595
|
+
if (hit.loinc.length === 0) return { status: "unmapped" };
|
|
2386
2596
|
return {
|
|
2387
2597
|
status: "mapped",
|
|
2388
2598
|
loinc: hit.loinc,
|
|
2389
2599
|
...hit.loincLongName !== void 0 ? { loincLongName: hit.loincLongName } : {},
|
|
2600
|
+
...hit.vendorSpecimenDescription !== void 0 ? { vendorSpecimenDescription: hit.vendorSpecimenDescription } : {},
|
|
2601
|
+
...hit.vendorResultDescription !== void 0 ? { vendorResultDescription: hit.vendorResultDescription } : {},
|
|
2602
|
+
...hit.representativeUnit !== void 0 ? { representativeUnit: hit.representativeUnit } : {},
|
|
2603
|
+
...hit.unitComparison !== void 0 ? { unitComparison: hit.unitComparison } : {},
|
|
2390
2604
|
source: "livd",
|
|
2391
2605
|
derived: true
|
|
2392
2606
|
};
|
|
2393
2607
|
case "ambiguous":
|
|
2394
|
-
return {
|
|
2608
|
+
return {
|
|
2609
|
+
status: "ambiguous",
|
|
2610
|
+
candidates: hit.candidates,
|
|
2611
|
+
...hit.candidateDetails !== void 0 ? { candidateDetails: hit.candidateDetails } : {},
|
|
2612
|
+
...hit.reason !== void 0 ? { reason: hit.reason } : {}
|
|
2613
|
+
};
|
|
2395
2614
|
case "unmapped":
|
|
2396
2615
|
return { status: "unmapped" };
|
|
2397
2616
|
}
|
|
2398
2617
|
}
|
|
2618
|
+
function unitsOf(record) {
|
|
2619
|
+
return record.type === "R" ? record.units : void 0;
|
|
2620
|
+
}
|
|
2621
|
+
function disagrees(uid, mapping) {
|
|
2622
|
+
if (mapping.status !== "mapped") return false;
|
|
2623
|
+
const wire = uid.unvalidatedWireValue;
|
|
2624
|
+
return wire !== void 0 && wire !== mapping.loinc;
|
|
2625
|
+
}
|
|
2626
|
+
function declaredLoincVersion(catalog) {
|
|
2627
|
+
const version = catalog.publication?.loincVersion;
|
|
2628
|
+
return version === void 0 || version.trim() === "" ? void 0 : version;
|
|
2629
|
+
}
|
|
2399
2630
|
function testIdOf(record) {
|
|
2400
2631
|
return record.universalTestId;
|
|
2401
2632
|
}
|
|
2402
2633
|
function lookupLivdForRecord(record, catalog) {
|
|
2403
2634
|
const uid = testIdOf(record);
|
|
2404
2635
|
const recognized = uid ?? recognizeUniversalTestId([]);
|
|
2405
|
-
const mapping = mapTestId(recognized, catalog);
|
|
2406
|
-
const reportedCode =
|
|
2636
|
+
const mapping = mapTestId(recognized, catalog, unitsOf(record));
|
|
2637
|
+
const reportedCode = recognized.localCode;
|
|
2638
|
+
const wireValue = recognized.unvalidatedWireValue;
|
|
2639
|
+
const catalogLoincVersion = declaredLoincVersion(catalog);
|
|
2407
2640
|
return {
|
|
2408
2641
|
recordIndex: record.recordIndex,
|
|
2409
2642
|
recordType: record.type,
|
|
2410
2643
|
...reportedCode !== void 0 ? { reportedCode } : {},
|
|
2644
|
+
...wireValue !== void 0 ? { unvalidatedWireValue: wireValue } : {},
|
|
2645
|
+
wireValueDisagreesWithCatalog: disagrees(recognized, mapping),
|
|
2411
2646
|
provenance: recognized.provenance,
|
|
2412
|
-
mapping
|
|
2647
|
+
mapping,
|
|
2648
|
+
...catalogLoincVersion !== void 0 ? { catalogLoincVersion } : {}
|
|
2413
2649
|
};
|
|
2414
2650
|
}
|
|
2415
2651
|
function applyLivd(msg, catalog) {
|
|
@@ -2906,8 +3142,8 @@ function concatBytes3(chunks) {
|
|
|
2906
3142
|
}
|
|
2907
3143
|
|
|
2908
3144
|
// src/index.ts
|
|
2909
|
-
var VERSION = "0.0
|
|
3145
|
+
var VERSION = "0.1.0";
|
|
2910
3146
|
|
|
2911
|
-
export { ALL_ASTM_WARNING_CODES, AMBIGUOUS_CODES, ACK as ASTM_ACK, ENQ as ASTM_ENQ, EOT as ASTM_EOT, NAK as ASTM_NAK, AstmAmbiguousStreamError, AstmFrameEncodeError, AstmFrameStrictError, AstmParseError, AstmProfileDefinitionError, AstmSerializeError, AstmStrictError, CANONICAL_DELIMITERS, FATAL_CODES, FRAME_WARNING_CODES, LIVD_WARNING_CODES, LTP_WARNING_CODES, SAFETY_CRITICAL_CODES, TOLERABLE_CODES, VERSION, WARNING_CODES, alignmentShiftedComponents, alignmentShiftedFields, alignmentTruncatedField, ambiguousEscapeAlignment, ambiguousMessageKind, ambiguousValueSplit, applyAstmProfile, applyAstmProfileToWarnings, applyLivd, astmDateToLocalISO, astmProfiles, attachComments, buildAstmMessage, classifyMessage, comments, commentsFor, composeAstmFrames, computeChecksum, decodeAstmFrames, decodeEscapes, deepFreeze, defineAstmProfile, defineLivdCatalog, delimiterRoleCollision, delimiterSwallowedByEscape, delimitersRedeclared, detectFraming, encodeComponent, fieldScalar, fieldsUnseparated, frameBadChecksum, frameOversize, frameSequenceGap, frameUnterminated, getAstmProfile, getDefaultAstmProfile, hasCollidingRoles, interpretAbnormalFlag, interpretResultStatus, isNonStandard, isSafetyCriticalCode, listAstmProfiles, livdAmbiguousMapping, livdUnmappedCode, lookupLivdForRecord, ltpAmbiguousTransport, ltpFrameRejected, ltpInitialState, ltpReduce, ltpUnexpectedEvent, messages, nonStandardDelimiters, orders, orphanComment, parseAstmDate, parseAstmRecords, parseChecksumHex, parseFramedAstm, parseReferenceRange, partialTimestamp, patient, primaryCode, profileQuirkApplied, query, readDelimiterDeclaration, readDelimiters, recognizeUniversalTestId, resolveProfileTransport, results, serializeAstmRecord, serializeAstmRecords, serializeField, serializeFramedAstm, setDefaultAstmProfile, splitEscapeAware, toChecksumHex, tokenizeHeader, tokenizeRecord, undefinedAbnormalFlag, undefinedResultStatus, uninterpretedQueryStatus, unitsAbsent, unknownEscapeSequence, unknownRecordType, unpairedEscapeCharacter, unparseableReferenceRange, unreadableRedeclaration };
|
|
3147
|
+
export { ABNORMAL_FLAG_CODES, ABNORMAL_FLAG_VOCABULARY, ALL_ASTM_WARNING_CODES, AMBIGUOUS_CODES, ACK as ASTM_ACK, ENQ as ASTM_ENQ, EOT as ASTM_EOT, NAK as ASTM_NAK, AstmAmbiguousStreamError, AstmFrameEncodeError, AstmFrameStrictError, AstmParseError, AstmProfileDefinitionError, AstmSerializeError, AstmStrictError, CANONICAL_DELIMITERS, FATAL_CODES, FRAME_WARNING_CODES, LIVD_CATALOG_IDENTITY_UNDECLARED, LIVD_WARNING_CODES, LTP_WARNING_CODES, RESULT_STATUS_VOCABULARY, SAFETY_CRITICAL_CODES, TOLERABLE_CODES, VERSION, WARNING_CODES, alignmentShiftedComponents, alignmentShiftedFields, alignmentTruncatedField, ambiguousEscapeAlignment, ambiguousMessageKind, ambiguousValueSplit, applyAstmProfile, applyAstmProfileToWarnings, applyLivd, astmDateToLocalISO, astmProfiles, attachComments, buildAstmMessage, classifyMessage, comments, commentsFor, composeAstmFrames, computeChecksum, decodeAstmFrames, decodeEscapes, deepFreeze, defineAstmProfile, defineLivdCatalog, delimiterRoleCollision, delimiterSwallowedByEscape, delimitersRedeclared, describeVocabulary, detectFraming, encodeComponent, fieldScalar, fieldsUnseparated, frameBadChecksum, frameOversize, frameSequenceGap, frameUnterminated, getAstmProfile, getDefaultAstmProfile, hasCollidingRoles, interpretAbnormalFlag, interpretResultStatus, isNonStandard, isSafetyCriticalCode, listAstmProfiles, livdAmbiguousMapping, livdCatalogMissingLoincVersion, livdUnmappedCode, lookupLivdForRecord, ltpAmbiguousTransport, ltpFrameRejected, ltpInitialState, ltpReduce, ltpUnexpectedEvent, messages, nonStandardDelimiters, orders, orphanComment, parseAstmDate, parseAstmRecords, parseChecksumHex, parseFramedAstm, parseReferenceRange, partialTimestamp, patient, primaryCode, profileQuirkApplied, query, readDelimiterDeclaration, readDelimiters, recognizeUniversalTestId, resolveProfileTransport, results, serializeAstmRecord, serializeAstmRecords, serializeField, serializeFramedAstm, setDefaultAstmProfile, splitEscapeAware, toChecksumHex, toDate, toISO, toObject, tokenizeHeader, tokenizeRecord, undefinedAbnormalFlag, undefinedResultStatus, uninterpretedQueryStatus, unitsAbsent, unknownEscapeSequence, unknownRecordType, unpairedEscapeCharacter, unparseableReferenceRange, unreadableRedeclaration };
|
|
2912
3148
|
//# sourceMappingURL=index.mjs.map
|
|
2913
3149
|
//# sourceMappingURL=index.mjs.map
|