@cosyte/terminology 0.1.6 → 0.1.7
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 +34 -0
- package/dist/index.cjs +377 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +377 -90
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -4
package/dist/index.d.cts
CHANGED
|
@@ -1443,8 +1443,8 @@ declare function parseRrf(source: RrfSource): {
|
|
|
1443
1443
|
*/
|
|
1444
1444
|
|
|
1445
1445
|
/**
|
|
1446
|
-
* Parse RFC-4180 CSV content into rows of fields
|
|
1447
|
-
*
|
|
1446
|
+
* Parse RFC-4180 CSV content into rows of fields. Lenient: a stray mid-field quote is literal, so a
|
|
1447
|
+
* malformed quote never silently swallows subsequent rows.
|
|
1448
1448
|
*
|
|
1449
1449
|
* @param content - The raw CSV text.
|
|
1450
1450
|
* @returns The rows, each an array of field strings (untrimmed).
|
package/dist/index.d.ts
CHANGED
|
@@ -1443,8 +1443,8 @@ declare function parseRrf(source: RrfSource): {
|
|
|
1443
1443
|
*/
|
|
1444
1444
|
|
|
1445
1445
|
/**
|
|
1446
|
-
* Parse RFC-4180 CSV content into rows of fields
|
|
1447
|
-
*
|
|
1446
|
+
* Parse RFC-4180 CSV content into rows of fields. Lenient: a stray mid-field quote is literal, so a
|
|
1447
|
+
* malformed quote never silently swallows subsequent rows.
|
|
1448
1448
|
*
|
|
1449
1449
|
* @param content - The raw CSV text.
|
|
1450
1450
|
* @returns The rows, each an array of field strings (untrimmed).
|
package/dist/index.mjs
CHANGED
|
@@ -444,6 +444,10 @@ function loadUnmapped(raw, path) {
|
|
|
444
444
|
assignString(out, raw, "url");
|
|
445
445
|
return Object.freeze(out);
|
|
446
446
|
}
|
|
447
|
+
var loadedGroups = /* @__PURE__ */ new WeakSet();
|
|
448
|
+
function isLoadedGroup(group) {
|
|
449
|
+
return loadedGroups.has(group);
|
|
450
|
+
}
|
|
447
451
|
function loadGroup(raw, path) {
|
|
448
452
|
if (!isJsonObject(raw)) malformed(path, "group is not an object");
|
|
449
453
|
const rawElements = getArray(raw, "element") ?? [];
|
|
@@ -458,7 +462,9 @@ function loadGroup(raw, path) {
|
|
|
458
462
|
if (isJsonObject(raw) && raw["unmapped"] !== void 0) {
|
|
459
463
|
out.unmapped = loadUnmapped(raw["unmapped"], `${path}.unmapped`);
|
|
460
464
|
}
|
|
461
|
-
|
|
465
|
+
const group = Object.freeze(out);
|
|
466
|
+
loadedGroups.add(group);
|
|
467
|
+
return group;
|
|
462
468
|
}
|
|
463
469
|
function loadConceptMap(json) {
|
|
464
470
|
if (!isJsonObject(json)) malformed("", "resource is not a JSON object");
|
|
@@ -502,6 +508,24 @@ function groupApplies(group, system) {
|
|
|
502
508
|
if (system === void 0) return true;
|
|
503
509
|
return group.source === void 0 || group.source === system;
|
|
504
510
|
}
|
|
511
|
+
var elementIndex = /* @__PURE__ */ new WeakMap();
|
|
512
|
+
var NO_ELEMENTS = Object.freeze([]);
|
|
513
|
+
function candidates(group, code) {
|
|
514
|
+
if (typeof code !== "string" || !isLoadedGroup(group)) return group.element;
|
|
515
|
+
let index = elementIndex.get(group);
|
|
516
|
+
if (index === void 0) {
|
|
517
|
+
const built = /* @__PURE__ */ new Map();
|
|
518
|
+
for (const element of group.element) {
|
|
519
|
+
if (element.code === void 0) continue;
|
|
520
|
+
const list = built.get(element.code);
|
|
521
|
+
if (list === void 0) built.set(element.code, [element]);
|
|
522
|
+
else list.push(element);
|
|
523
|
+
}
|
|
524
|
+
index = built;
|
|
525
|
+
elementIndex.set(group, index);
|
|
526
|
+
}
|
|
527
|
+
return index.get(code) ?? NO_ELEMENTS;
|
|
528
|
+
}
|
|
505
529
|
function provenanceOf(map, group, sourceSystem) {
|
|
506
530
|
const out = {};
|
|
507
531
|
if (map.url !== void 0) out.conceptMapUrl = map.url;
|
|
@@ -535,7 +559,7 @@ function translate(sourceCoding, map) {
|
|
|
535
559
|
for (const group of map.group) {
|
|
536
560
|
if (!groupApplies(group, system)) continue;
|
|
537
561
|
if (fallbackGroup === void 0 && group.unmapped !== void 0) fallbackGroup = group;
|
|
538
|
-
for (const element of group.
|
|
562
|
+
for (const element of candidates(group, source.code)) {
|
|
539
563
|
if (element.code !== source.code) continue;
|
|
540
564
|
sourceCodePresent = true;
|
|
541
565
|
for (const t of element.target) {
|
|
@@ -638,6 +662,12 @@ function frozenMap(source, what) {
|
|
|
638
662
|
return Object.freeze(view);
|
|
639
663
|
}
|
|
640
664
|
|
|
665
|
+
// src/common/own-string.ts
|
|
666
|
+
var VIEW_MIN_LENGTH = 13;
|
|
667
|
+
function ownString(s) {
|
|
668
|
+
return s.length < VIEW_MIN_LENGTH ? s : ` ${s}`.slice(1);
|
|
669
|
+
}
|
|
670
|
+
|
|
641
671
|
// src/codesystem/status.ts
|
|
642
672
|
var defaultStatusMapper = (raw) => {
|
|
643
673
|
switch (raw.trim().toUpperCase()) {
|
|
@@ -672,7 +702,79 @@ function makeStatus(activity, raw, billable) {
|
|
|
672
702
|
}
|
|
673
703
|
|
|
674
704
|
// src/codesystem/csv.ts
|
|
675
|
-
|
|
705
|
+
var QUOTE = 34;
|
|
706
|
+
var COMMA = 44;
|
|
707
|
+
var LINE_FEED = 10;
|
|
708
|
+
var CARRIAGE_RETURN = 13;
|
|
709
|
+
function scanCsv(content, sink) {
|
|
710
|
+
if (typeof content !== "string") return readCsvByCharAt(content, sink);
|
|
711
|
+
const n = content.length;
|
|
712
|
+
const cells = [];
|
|
713
|
+
let column = 0;
|
|
714
|
+
const keeps = (col) => sink.wanted === void 0 || sink.wanted[col] === true;
|
|
715
|
+
let keep = keeps(0);
|
|
716
|
+
let field = "";
|
|
717
|
+
let runStart = 0;
|
|
718
|
+
let inQuotes = false;
|
|
719
|
+
let fieldStart = true;
|
|
720
|
+
let sawAny = false;
|
|
721
|
+
const endField = (end) => {
|
|
722
|
+
if (keep) cells[column] = field + content.slice(runStart, end);
|
|
723
|
+
field = "";
|
|
724
|
+
};
|
|
725
|
+
const endRow = () => {
|
|
726
|
+
sink.row(cells, column + 1);
|
|
727
|
+
column = 0;
|
|
728
|
+
keep = keeps(0);
|
|
729
|
+
};
|
|
730
|
+
for (let i = 0; i < n; i++) {
|
|
731
|
+
const ch = content.charCodeAt(i);
|
|
732
|
+
if (inQuotes) {
|
|
733
|
+
if (ch === QUOTE) {
|
|
734
|
+
if (content.charCodeAt(i + 1) === QUOTE) {
|
|
735
|
+
if (keep) field += content.slice(runStart, i + 1);
|
|
736
|
+
i++;
|
|
737
|
+
} else {
|
|
738
|
+
if (keep) field += content.slice(runStart, i);
|
|
739
|
+
inQuotes = false;
|
|
740
|
+
}
|
|
741
|
+
runStart = i + 1;
|
|
742
|
+
}
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
if (ch === QUOTE && fieldStart) {
|
|
746
|
+
inQuotes = true;
|
|
747
|
+
fieldStart = false;
|
|
748
|
+
sawAny = true;
|
|
749
|
+
runStart = i + 1;
|
|
750
|
+
} else if (ch === COMMA) {
|
|
751
|
+
endField(i);
|
|
752
|
+
column++;
|
|
753
|
+
keep = keeps(column);
|
|
754
|
+
runStart = i + 1;
|
|
755
|
+
fieldStart = true;
|
|
756
|
+
sawAny = true;
|
|
757
|
+
} else if (ch === LINE_FEED) {
|
|
758
|
+
endField(i);
|
|
759
|
+
endRow();
|
|
760
|
+
runStart = i + 1;
|
|
761
|
+
fieldStart = true;
|
|
762
|
+
sawAny = false;
|
|
763
|
+
} else if (ch === CARRIAGE_RETURN) {
|
|
764
|
+
if (keep) field += content.slice(runStart, i);
|
|
765
|
+
runStart = i + 1;
|
|
766
|
+
} else {
|
|
767
|
+
fieldStart = false;
|
|
768
|
+
sawAny = true;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (sawAny) {
|
|
772
|
+
endField(n);
|
|
773
|
+
endRow();
|
|
774
|
+
}
|
|
775
|
+
return inQuotes;
|
|
776
|
+
}
|
|
777
|
+
function readCsvByCharAt(content, sink) {
|
|
676
778
|
const rows = [];
|
|
677
779
|
let row = [];
|
|
678
780
|
let field = "";
|
|
@@ -711,20 +813,30 @@ function parseCsvRows(content) {
|
|
|
711
813
|
field = "";
|
|
712
814
|
fieldStart = true;
|
|
713
815
|
sawAny = false;
|
|
714
|
-
} else if (ch
|
|
816
|
+
} else if (ch !== "\r") {
|
|
715
817
|
field += ch;
|
|
716
818
|
fieldStart = false;
|
|
717
819
|
sawAny = true;
|
|
718
820
|
}
|
|
719
821
|
}
|
|
720
|
-
if (sawAny
|
|
822
|
+
if (sawAny) {
|
|
721
823
|
row.push(field);
|
|
722
824
|
rows.push(row);
|
|
723
825
|
}
|
|
724
|
-
|
|
826
|
+
for (const r of rows) sink.row(r, r.length);
|
|
827
|
+
return inQuotes;
|
|
725
828
|
}
|
|
726
829
|
function parseCsv(content) {
|
|
727
|
-
|
|
830
|
+
const rows = [];
|
|
831
|
+
scanCsv(content, {
|
|
832
|
+
wanted: void 0,
|
|
833
|
+
row: (cells, count) => {
|
|
834
|
+
const row = [];
|
|
835
|
+
for (let c = 0; c < count; c++) row.push(ownString(cells[c]));
|
|
836
|
+
rows.push(row);
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
return rows;
|
|
728
840
|
}
|
|
729
841
|
var MISSING_COLUMN_MESSAGE = Object.freeze({
|
|
730
842
|
code: "CSV source: the configured 'code' column is not present in the header row",
|
|
@@ -741,47 +853,44 @@ function requireHeader(header, name, role) {
|
|
|
741
853
|
}
|
|
742
854
|
function parseCsvSource(source) {
|
|
743
855
|
const statusMap = source.statusMap ?? defaultStatusMapper;
|
|
744
|
-
const
|
|
745
|
-
const
|
|
746
|
-
if (header === void 0) {
|
|
747
|
-
throw new TerminologyError(
|
|
748
|
-
FATAL_CODES.TERM_CODESYSTEM_MALFORMED,
|
|
749
|
-
"CSV source: the file has no header row"
|
|
750
|
-
);
|
|
751
|
-
}
|
|
856
|
+
const content = source.content;
|
|
857
|
+
const concepts = /* @__PURE__ */ new Map();
|
|
752
858
|
const warnings = [];
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
line: rows.length,
|
|
758
|
-
detail: "an opened quoted field was not terminated before end-of-input"
|
|
759
|
-
})
|
|
760
|
-
);
|
|
761
|
-
}
|
|
762
|
-
const codeIdx = requireHeader(header, source.columns.code, "code");
|
|
763
|
-
const displayIdx = requireHeader(header, source.columns.display, "display");
|
|
764
|
-
const statusIdx = source.columns.status !== void 0 ? requireHeader(header, source.columns.status, "status") : void 0;
|
|
859
|
+
let rowCount = 0;
|
|
860
|
+
let codeIdx = 0;
|
|
861
|
+
let displayIdx = 0;
|
|
862
|
+
let statusIdx;
|
|
765
863
|
const propIdx = [];
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
|
|
864
|
+
let needCols = 0;
|
|
865
|
+
const header = (cells, count) => {
|
|
866
|
+
const names = cells.slice(0, count);
|
|
867
|
+
codeIdx = requireHeader(names, source.columns.code, "code");
|
|
868
|
+
displayIdx = requireHeader(names, source.columns.display, "display");
|
|
869
|
+
statusIdx = source.columns.status !== void 0 ? requireHeader(names, source.columns.status, "status") : void 0;
|
|
870
|
+
for (const n of source.columns.properties ?? []) {
|
|
871
|
+
propIdx.push({ name: n, index: requireHeader(names, n, "property") });
|
|
872
|
+
}
|
|
873
|
+
needCols = Math.max(codeIdx, displayIdx, statusIdx ?? 0, ...propIdx.map((p) => p.index)) + 1;
|
|
874
|
+
const wanted = [];
|
|
875
|
+
wanted[codeIdx] = true;
|
|
876
|
+
wanted[displayIdx] = true;
|
|
877
|
+
if (statusIdx !== void 0) wanted[statusIdx] = true;
|
|
878
|
+
for (const p of propIdx) wanted[p.index] = true;
|
|
879
|
+
sink.wanted = wanted;
|
|
880
|
+
};
|
|
881
|
+
const dataRow = (cells, count, lineNo) => {
|
|
882
|
+
if (count < needCols) {
|
|
775
883
|
warnings.push(
|
|
776
884
|
Object.freeze({
|
|
777
885
|
code: DIAGNOSTIC_CODES.TERM_CSV_MALFORMED,
|
|
778
886
|
line: lineNo,
|
|
779
|
-
detail: `row has ${String(
|
|
887
|
+
detail: `row has ${String(count)} fields, need ${String(needCols)}`
|
|
780
888
|
})
|
|
781
889
|
);
|
|
782
|
-
|
|
890
|
+
return;
|
|
783
891
|
}
|
|
784
|
-
const
|
|
892
|
+
const cell3 = (index) => cells[index];
|
|
893
|
+
const code = cell3(codeIdx).trim();
|
|
785
894
|
if (code === "") {
|
|
786
895
|
warnings.push(
|
|
787
896
|
Object.freeze({
|
|
@@ -790,23 +899,47 @@ function parseCsvSource(source) {
|
|
|
790
899
|
detail: "row is missing its code"
|
|
791
900
|
})
|
|
792
901
|
);
|
|
793
|
-
|
|
902
|
+
return;
|
|
794
903
|
}
|
|
795
|
-
if (concepts.has(code))
|
|
796
|
-
const out = { code, properties: [] };
|
|
797
|
-
const display = (
|
|
798
|
-
if (display !== "") out.display = display;
|
|
904
|
+
if (concepts.has(code)) return;
|
|
905
|
+
const out = { code: ownString(code), properties: [] };
|
|
906
|
+
const display = cell3(displayIdx).trim();
|
|
907
|
+
if (display !== "") out.display = ownString(display);
|
|
799
908
|
if (statusIdx !== void 0) {
|
|
800
|
-
const rawStatus = (
|
|
909
|
+
const rawStatus = ownString(cell3(statusIdx).trim());
|
|
801
910
|
out.status = makeStatus(statusMap(rawStatus), rawStatus);
|
|
802
911
|
}
|
|
803
912
|
const props = [];
|
|
804
913
|
for (const p of propIdx) {
|
|
805
|
-
const value = (
|
|
806
|
-
if (value !== "") props.push(Object.freeze({ code: p.name, value }));
|
|
914
|
+
const value = cell3(p.index).trim();
|
|
915
|
+
if (value !== "") props.push(Object.freeze({ code: p.name, value: ownString(value) }));
|
|
807
916
|
}
|
|
808
917
|
out.properties = Object.freeze(props);
|
|
809
|
-
concepts.set(code, Object.freeze(out));
|
|
918
|
+
concepts.set(out.code, Object.freeze(out));
|
|
919
|
+
};
|
|
920
|
+
const sink = {
|
|
921
|
+
wanted: void 0,
|
|
922
|
+
row: (cells, count) => {
|
|
923
|
+
rowCount++;
|
|
924
|
+
if (rowCount === 1) header(cells, count);
|
|
925
|
+
else dataRow(cells, count, rowCount);
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
const unterminatedQuote = scanCsv(content, sink);
|
|
929
|
+
if (rowCount === 0) {
|
|
930
|
+
throw new TerminologyError(
|
|
931
|
+
FATAL_CODES.TERM_CODESYSTEM_MALFORMED,
|
|
932
|
+
"CSV source: the file has no header row"
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
if (unterminatedQuote) {
|
|
936
|
+
warnings.unshift(
|
|
937
|
+
Object.freeze({
|
|
938
|
+
code: DIAGNOSTIC_CODES.TERM_CSV_MALFORMED,
|
|
939
|
+
line: rowCount,
|
|
940
|
+
detail: "an opened quoted field was not terminated before end-of-input"
|
|
941
|
+
})
|
|
942
|
+
);
|
|
810
943
|
}
|
|
811
944
|
return { concepts, warnings };
|
|
812
945
|
}
|
|
@@ -1066,6 +1199,10 @@ function parseRrf(source) {
|
|
|
1066
1199
|
}
|
|
1067
1200
|
|
|
1068
1201
|
// src/codesystem/load.ts
|
|
1202
|
+
var loadedConcepts = /* @__PURE__ */ new WeakSet();
|
|
1203
|
+
function isLoadedConcepts(concepts) {
|
|
1204
|
+
return loadedConcepts.has(concepts);
|
|
1205
|
+
}
|
|
1069
1206
|
function loadCodeSystem(source) {
|
|
1070
1207
|
let concepts;
|
|
1071
1208
|
let warnings;
|
|
@@ -1104,6 +1241,7 @@ function loadCodeSystem(source) {
|
|
|
1104
1241
|
}
|
|
1105
1242
|
out.count = concepts.size;
|
|
1106
1243
|
out.concepts = frozenMap(concepts, "a loaded CodeSystem's concepts map");
|
|
1244
|
+
loadedConcepts.add(out.concepts);
|
|
1107
1245
|
out.warnings = Object.freeze(warnings);
|
|
1108
1246
|
return Object.freeze(out);
|
|
1109
1247
|
}
|
|
@@ -1306,8 +1444,11 @@ function directParents(concept) {
|
|
|
1306
1444
|
return parents;
|
|
1307
1445
|
}
|
|
1308
1446
|
function buildSubsumption(cs) {
|
|
1447
|
+
return subsumptionOver(cs.concepts);
|
|
1448
|
+
}
|
|
1449
|
+
function subsumptionOver(concepts) {
|
|
1309
1450
|
const parentsOf = /* @__PURE__ */ new Map();
|
|
1310
|
-
for (const concept of
|
|
1451
|
+
for (const concept of concepts.values()) {
|
|
1311
1452
|
const parents = directParents(concept);
|
|
1312
1453
|
if (parents.length > 0) parentsOf.set(concept.code, parents);
|
|
1313
1454
|
}
|
|
@@ -1328,6 +1469,17 @@ function buildSubsumption(cs) {
|
|
|
1328
1469
|
};
|
|
1329
1470
|
return { ancestorsOf };
|
|
1330
1471
|
}
|
|
1472
|
+
var loadedSubsumption = /* @__PURE__ */ new WeakMap();
|
|
1473
|
+
function subsumptionOf(cs) {
|
|
1474
|
+
const concepts = cs.concepts;
|
|
1475
|
+
if (!isLoadedConcepts(concepts)) return subsumptionOver(concepts);
|
|
1476
|
+
let sub = loadedSubsumption.get(concepts);
|
|
1477
|
+
if (sub === void 0) {
|
|
1478
|
+
sub = subsumptionOver(concepts);
|
|
1479
|
+
loadedSubsumption.set(concepts, sub);
|
|
1480
|
+
}
|
|
1481
|
+
return sub;
|
|
1482
|
+
}
|
|
1331
1483
|
function isA(sub, code, anchor) {
|
|
1332
1484
|
return code === anchor || sub.ancestorsOf(code).has(anchor);
|
|
1333
1485
|
}
|
|
@@ -1730,7 +1882,7 @@ function matchComponent(target, component, ctx, visited, path, activeOnly = fals
|
|
|
1730
1882
|
baseComplete = false;
|
|
1731
1883
|
} else {
|
|
1732
1884
|
const concpt = cs.concepts.get(target.code);
|
|
1733
|
-
baseMatched = concpt !== void 0 && matchesAllFilters(concpt, filter,
|
|
1885
|
+
baseMatched = concpt !== void 0 && matchesAllFilters(concpt, filter, subsumptionOf(cs)).matched;
|
|
1734
1886
|
}
|
|
1735
1887
|
} else {
|
|
1736
1888
|
baseMatched = cs.concepts.get(target.code) !== void 0;
|
|
@@ -4020,7 +4172,29 @@ function atBoundary(s, pos) {
|
|
|
4020
4172
|
function prefixRank(m) {
|
|
4021
4173
|
return m.prefix ? 1 : 0;
|
|
4022
4174
|
}
|
|
4175
|
+
function byPreference(a, b) {
|
|
4176
|
+
return b.len - a.len || prefixRank(a) - prefixRank(b);
|
|
4177
|
+
}
|
|
4023
4178
|
function simpleMatches(essence, s, pos) {
|
|
4179
|
+
if (typeof s !== "string") return simpleMatchesByScan(essence, s, pos);
|
|
4180
|
+
const index = indexOf(essence);
|
|
4181
|
+
const out = [];
|
|
4182
|
+
for (const atom of index.atoms.get(s.charCodeAt(pos)) ?? NONE) {
|
|
4183
|
+
if (s.startsWith(atom.code, pos)) out.push({ atom, len: atom.code.length });
|
|
4184
|
+
}
|
|
4185
|
+
for (const prefix of index.prefixes.get(s.charCodeAt(pos)) ?? NONE) {
|
|
4186
|
+
if (!s.startsWith(prefix.code, pos)) continue;
|
|
4187
|
+
const rem = pos + prefix.code.length;
|
|
4188
|
+
for (const atom of index.metricAtoms.get(s.charCodeAt(rem)) ?? NONE) {
|
|
4189
|
+
if (s.startsWith(atom.code, rem)) {
|
|
4190
|
+
out.push({ prefix, atom, len: prefix.code.length + atom.code.length });
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
out.sort(byPreference);
|
|
4195
|
+
return out;
|
|
4196
|
+
}
|
|
4197
|
+
function simpleMatchesByScan(essence, s, pos) {
|
|
4024
4198
|
const out = [];
|
|
4025
4199
|
for (const atom of essence.atoms) {
|
|
4026
4200
|
if (s.startsWith(atom.code, pos)) out.push({ atom, len: atom.code.length });
|
|
@@ -4034,9 +4208,33 @@ function simpleMatches(essence, s, pos) {
|
|
|
4034
4208
|
}
|
|
4035
4209
|
}
|
|
4036
4210
|
}
|
|
4037
|
-
out.sort(
|
|
4211
|
+
out.sort(byPreference);
|
|
4038
4212
|
return out;
|
|
4039
4213
|
}
|
|
4214
|
+
var NONE = Object.freeze([]);
|
|
4215
|
+
var firstCharIndexes = /* @__PURE__ */ new WeakMap();
|
|
4216
|
+
function byFirstChar(items) {
|
|
4217
|
+
const out = /* @__PURE__ */ new Map();
|
|
4218
|
+
for (const item of items) {
|
|
4219
|
+
const first = item.code.charCodeAt(0);
|
|
4220
|
+
const list = out.get(first);
|
|
4221
|
+
if (list === void 0) out.set(first, [item]);
|
|
4222
|
+
else list.push(item);
|
|
4223
|
+
}
|
|
4224
|
+
return out;
|
|
4225
|
+
}
|
|
4226
|
+
function indexOf(essence) {
|
|
4227
|
+
let index = firstCharIndexes.get(essence);
|
|
4228
|
+
if (index === void 0) {
|
|
4229
|
+
index = {
|
|
4230
|
+
atoms: byFirstChar(essence.atoms),
|
|
4231
|
+
metricAtoms: byFirstChar(essence.atoms.filter((a) => a.metric)),
|
|
4232
|
+
prefixes: byFirstChar(essence.prefixes)
|
|
4233
|
+
};
|
|
4234
|
+
firstCharIndexes.set(essence, index);
|
|
4235
|
+
}
|
|
4236
|
+
return index;
|
|
4237
|
+
}
|
|
4040
4238
|
var MAX_PAREN_DEPTH = 100;
|
|
4041
4239
|
function parseExponent(cur) {
|
|
4042
4240
|
const { s } = cur;
|
|
@@ -4069,8 +4267,8 @@ function parseAnnotation(cur) {
|
|
|
4069
4267
|
return { ok: false, reason: "annotation is not closed with '}'" };
|
|
4070
4268
|
}
|
|
4071
4269
|
function parseSimpleUnit(cur) {
|
|
4072
|
-
const
|
|
4073
|
-
for (const cand of
|
|
4270
|
+
const candidates2 = simpleMatches(cur.essence, cur.s, cur.pos);
|
|
4271
|
+
for (const cand of candidates2) {
|
|
4074
4272
|
const save = cur.pos;
|
|
4075
4273
|
cur.pos = save + cand.len;
|
|
4076
4274
|
const exp = parseExponent(cur);
|
|
@@ -4296,57 +4494,62 @@ function isNoMapSentinel(target) {
|
|
|
4296
4494
|
return target === NO_DX || target === NO_PCS;
|
|
4297
4495
|
}
|
|
4298
4496
|
function decodeFlags(raw) {
|
|
4299
|
-
if (!/^[0-9]{5}$/.test(raw)) return void 0;
|
|
4300
4497
|
return Object.freeze({
|
|
4301
|
-
approximate: raw
|
|
4302
|
-
noMap: raw
|
|
4303
|
-
combination: raw
|
|
4304
|
-
scenario:
|
|
4305
|
-
choiceList:
|
|
4498
|
+
approximate: raw.charCodeAt(0) === DIGIT_ONE,
|
|
4499
|
+
noMap: raw.charCodeAt(1) === DIGIT_ONE,
|
|
4500
|
+
combination: raw.charCodeAt(2) === DIGIT_ONE,
|
|
4501
|
+
scenario: raw.charCodeAt(3) - DIGIT_ZERO,
|
|
4502
|
+
choiceList: raw.charCodeAt(4) - DIGIT_ZERO,
|
|
4306
4503
|
raw
|
|
4307
4504
|
});
|
|
4308
4505
|
}
|
|
4506
|
+
var DIGIT_ZERO = 48;
|
|
4507
|
+
var DIGIT_ONE = 49;
|
|
4508
|
+
var DIGIT_NINE = 57;
|
|
4509
|
+
var SPACE = 32;
|
|
4510
|
+
var NON_ASCII_SPACE = /^\s$/;
|
|
4511
|
+
function isSpace(c) {
|
|
4512
|
+
if (c < 128) return c === SPACE || c >= 9 && c <= 13;
|
|
4513
|
+
return NON_ASCII_SPACE.test(String.fromCharCode(c));
|
|
4514
|
+
}
|
|
4309
4515
|
function loadGems(source) {
|
|
4310
4516
|
const entries = /* @__PURE__ */ new Map();
|
|
4311
4517
|
const warnings = [];
|
|
4312
4518
|
let count = 0;
|
|
4313
|
-
const
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
if (raw === void 0) continue;
|
|
4317
|
-
const trimmed = raw.trim();
|
|
4318
|
-
if (trimmed === "") continue;
|
|
4319
|
-
const fields = trimmed.split(/\s+/);
|
|
4320
|
-
if (fields.length !== 3) {
|
|
4519
|
+
const sink = {
|
|
4520
|
+
flags: /* @__PURE__ */ new Map(),
|
|
4521
|
+
malformed: (lineNo, fieldCount) => {
|
|
4321
4522
|
warnings.push(
|
|
4322
4523
|
Object.freeze({
|
|
4323
4524
|
code: "TERM_GEM_MALFORMED_ROW",
|
|
4324
|
-
line:
|
|
4325
|
-
detail: `expected 3 whitespace-delimited fields, found ${String(
|
|
4525
|
+
line: lineNo,
|
|
4526
|
+
detail: `expected 3 whitespace-delimited fields, found ${String(fieldCount)}`
|
|
4326
4527
|
})
|
|
4327
4528
|
);
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4529
|
+
},
|
|
4530
|
+
line: (lineNo, src, tgt, flags) => {
|
|
4531
|
+
if (flags === void 0) {
|
|
4532
|
+
warnings.push(
|
|
4533
|
+
Object.freeze({
|
|
4534
|
+
code: "TERM_GEM_MALFORMED_ROW",
|
|
4535
|
+
line: lineNo,
|
|
4536
|
+
detail: "flag field is not a 5-digit code"
|
|
4537
|
+
})
|
|
4538
|
+
);
|
|
4539
|
+
return;
|
|
4540
|
+
}
|
|
4541
|
+
const frozen = Object.freeze(
|
|
4542
|
+
isNoMapSentinel(tgt) ? { source: src, flags } : { source: src, flags, target: tgt }
|
|
4339
4543
|
);
|
|
4340
|
-
|
|
4544
|
+
const list = entries.get(src);
|
|
4545
|
+
if (list) list.push(frozen);
|
|
4546
|
+
else entries.set(src, [frozen]);
|
|
4547
|
+
count++;
|
|
4341
4548
|
}
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
if (list) list.push(frozen);
|
|
4347
|
-
else entries.set(src, [frozen]);
|
|
4348
|
-
count++;
|
|
4349
|
-
}
|
|
4549
|
+
};
|
|
4550
|
+
const content = source.content;
|
|
4551
|
+
if (typeof content === "string") readLinesInPlace(content, sink);
|
|
4552
|
+
else readLinesBySplit(content, sink);
|
|
4350
4553
|
for (const list of entries.values()) Object.freeze(list);
|
|
4351
4554
|
return Object.freeze({
|
|
4352
4555
|
direction: source.direction,
|
|
@@ -4356,6 +4559,90 @@ function loadGems(source) {
|
|
|
4356
4559
|
warnings: Object.freeze(warnings)
|
|
4357
4560
|
});
|
|
4358
4561
|
}
|
|
4562
|
+
function sharedFlags(sink, text, from, to) {
|
|
4563
|
+
const key = fiveDigitsAt(text, from, to);
|
|
4564
|
+
if (key === -1) return void 0;
|
|
4565
|
+
let flags = sink.flags.get(key);
|
|
4566
|
+
if (flags === void 0) {
|
|
4567
|
+
flags = decodeFlags(text.slice(from, to));
|
|
4568
|
+
sink.flags.set(key, flags);
|
|
4569
|
+
}
|
|
4570
|
+
return flags;
|
|
4571
|
+
}
|
|
4572
|
+
function fiveDigitsAt(content, from, to) {
|
|
4573
|
+
if (to - from !== 5) return -1;
|
|
4574
|
+
let value = 0;
|
|
4575
|
+
for (let i = from; i < to; i++) {
|
|
4576
|
+
const c = content.charCodeAt(i);
|
|
4577
|
+
if (c < DIGIT_ZERO || c > DIGIT_NINE) return -1;
|
|
4578
|
+
value = value * 10 + (c - DIGIT_ZERO);
|
|
4579
|
+
}
|
|
4580
|
+
return value;
|
|
4581
|
+
}
|
|
4582
|
+
function readLinesInPlace(content, sink) {
|
|
4583
|
+
const n = content.length;
|
|
4584
|
+
let lineNo = 0;
|
|
4585
|
+
for (let start = 0; start <= n; ) {
|
|
4586
|
+
let end = content.indexOf("\n", start);
|
|
4587
|
+
if (end === -1) end = n;
|
|
4588
|
+
lineNo++;
|
|
4589
|
+
let fieldCount = 0;
|
|
4590
|
+
let srcStart = 0;
|
|
4591
|
+
let srcEnd = 0;
|
|
4592
|
+
let tgtStart = 0;
|
|
4593
|
+
let tgtEnd = 0;
|
|
4594
|
+
let flagStart = 0;
|
|
4595
|
+
let flagEnd = 0;
|
|
4596
|
+
for (let i = start; i < end; ) {
|
|
4597
|
+
if (isSpace(content.charCodeAt(i))) {
|
|
4598
|
+
i++;
|
|
4599
|
+
continue;
|
|
4600
|
+
}
|
|
4601
|
+
const from = i;
|
|
4602
|
+
while (i < end && !isSpace(content.charCodeAt(i))) i++;
|
|
4603
|
+
fieldCount++;
|
|
4604
|
+
if (fieldCount === 1) {
|
|
4605
|
+
srcStart = from;
|
|
4606
|
+
srcEnd = i;
|
|
4607
|
+
} else if (fieldCount === 2) {
|
|
4608
|
+
tgtStart = from;
|
|
4609
|
+
tgtEnd = i;
|
|
4610
|
+
} else if (fieldCount === 3) {
|
|
4611
|
+
flagStart = from;
|
|
4612
|
+
flagEnd = i;
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
start = end + 1;
|
|
4616
|
+
if (fieldCount === 0) continue;
|
|
4617
|
+
if (fieldCount !== 3) {
|
|
4618
|
+
sink.malformed(lineNo, fieldCount);
|
|
4619
|
+
continue;
|
|
4620
|
+
}
|
|
4621
|
+
const flags = sharedFlags(sink, content, flagStart, flagEnd);
|
|
4622
|
+
sink.line(
|
|
4623
|
+
lineNo,
|
|
4624
|
+
ownString(content.slice(srcStart, srcEnd)),
|
|
4625
|
+
ownString(content.slice(tgtStart, tgtEnd)),
|
|
4626
|
+
flags
|
|
4627
|
+
);
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
function readLinesBySplit(content, sink) {
|
|
4631
|
+
const lines = content.split(/\r?\n/);
|
|
4632
|
+
for (let i = 0; i < lines.length; i++) {
|
|
4633
|
+
const raw = lines[i];
|
|
4634
|
+
if (raw === void 0) continue;
|
|
4635
|
+
const trimmed = raw.trim();
|
|
4636
|
+
if (trimmed === "") continue;
|
|
4637
|
+
const fields = trimmed.split(/\s+/);
|
|
4638
|
+
if (fields.length !== 3) {
|
|
4639
|
+
sink.malformed(i + 1, fields.length);
|
|
4640
|
+
continue;
|
|
4641
|
+
}
|
|
4642
|
+
const [src, tgt, flagStr] = fields;
|
|
4643
|
+
sink.line(i + 1, src, tgt, sharedFlags(sink, flagStr, 0, flagStr.length));
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4359
4646
|
function buildCombinations(entries) {
|
|
4360
4647
|
const combo = entries.filter((e) => e.flags.combination && e.target !== void 0);
|
|
4361
4648
|
if (combo.length === 0) return void 0;
|
|
@@ -5017,7 +5304,7 @@ function approximateMatch(graph, query, options = {}) {
|
|
|
5017
5304
|
}
|
|
5018
5305
|
|
|
5019
5306
|
// src/index.ts
|
|
5020
|
-
var VERSION = "0.1.
|
|
5307
|
+
var VERSION = "0.1.7";
|
|
5021
5308
|
|
|
5022
5309
|
export { COMPLEX_MAP_SOURCE_SYSTEM, DIAGNOSTIC_CODES, FATAL_CODES, HL7_V2_OID_ROOT, HL7_V2_URL_PREFIX, ICD10CM_ORDER_FILE_FIELDS, ICD10CM_SYSTEM, ICD9CM_SYSTEM, MAP_CATEGORIES, NO_MAP_CATEGORIES, RELA, RELA_INVERSE, RXNORM_SYSTEM, SNOMEDCT_SYSTEM, SYSTEM_IDENTITIES, TERM_TYPES, TerminologyError, VERSION, applyComplexMap, applyGem, approximateMatch, asTermType, brandsFor, buildSubsumption, codeableConcept, coding, consistsOf, defaultStatusMapper, doseFormsOf, expand, genericFor, getArray, getBoolean, getConcept, getNumber, getString, ingredientsOf, invertGem, isA, isJsonObject, isSynonymTermType, isUnknownSystem, loadCodeSystem, loadComplexMap, loadConceptMap, loadGems, loadRxNormGraph, loadUcumEssence, loadValueSet, lookup, makeStatus, matchesFilter, parseCsv, parseCsvSource, parseFhirCodeSystem, parseFixedWidth, parseRrf, parseRrfLine, parseUcum, reduce, relatedByRela, resolveNdc, resolveSystem, sliceField, translate, ucumEqual, unsupportedOps, validateCode, validateCodeInValueSet, validateUcum };
|
|
5023
5310
|
//# sourceMappingURL=index.mjs.map
|