@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/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 (the `.rows` of {@link parseCsvRows}). Lenient: a
1447
- * stray mid-field quote is literal, so a malformed quote never silently swallows subsequent rows.
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 (the `.rows` of {@link parseCsvRows}). Lenient: a
1447
- * stray mid-field quote is literal, so a malformed quote never silently swallows subsequent rows.
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
- return Object.freeze(out);
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.element) {
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
- function parseCsvRows(content) {
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 === "\r") ; else {
816
+ } else if (ch !== "\r") {
715
817
  field += ch;
716
818
  fieldStart = false;
717
819
  sawAny = true;
718
820
  }
719
821
  }
720
- if (sawAny || field !== "" || row.length > 0) {
822
+ if (sawAny) {
721
823
  row.push(field);
722
824
  rows.push(row);
723
825
  }
724
- return { rows, unterminatedQuote: inQuotes };
826
+ for (const r of rows) sink.row(r, r.length);
827
+ return inQuotes;
725
828
  }
726
829
  function parseCsv(content) {
727
- return parseCsvRows(content).rows;
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 { rows, unterminatedQuote } = parseCsvRows(source.content);
745
- const header = rows[0];
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
- if (unterminatedQuote) {
754
- warnings.push(
755
- Object.freeze({
756
- code: DIAGNOSTIC_CODES.TERM_CSV_MALFORMED,
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
- for (const n of source.columns.properties ?? []) {
767
- propIdx.push({ name: n, index: requireHeader(header, n, "property") });
768
- }
769
- const needCols = Math.max(codeIdx, displayIdx, statusIdx ?? 0, ...propIdx.map((p) => p.index)) + 1;
770
- const concepts = /* @__PURE__ */ new Map();
771
- for (let r = 1; r < rows.length; r++) {
772
- const cells = rows[r] ?? [];
773
- const lineNo = r + 1;
774
- if (cells.length < needCols) {
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(cells.length)} fields, need ${String(needCols)}`
887
+ detail: `row has ${String(count)} fields, need ${String(needCols)}`
780
888
  })
781
889
  );
782
- continue;
890
+ return;
783
891
  }
784
- const code = (cells[codeIdx] ?? "").trim();
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
- continue;
902
+ return;
794
903
  }
795
- if (concepts.has(code)) continue;
796
- const out = { code, properties: [] };
797
- const display = (cells[displayIdx] ?? "").trim();
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 = (cells[statusIdx] ?? "").trim();
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 = (cells[p.index] ?? "").trim();
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 cs.concepts.values()) {
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, buildSubsumption(cs)).matched;
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((a, b) => b.len - a.len || prefixRank(a) - prefixRank(b));
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 candidates = simpleMatches(cur.essence, cur.s, cur.pos);
4073
- for (const cand of candidates) {
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[0] === "1",
4302
- noMap: raw[1] === "1",
4303
- combination: raw[2] === "1",
4304
- scenario: Number(raw[3]),
4305
- choiceList: Number(raw[4]),
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 lines = source.content.split(/\r?\n/);
4314
- for (let i = 0; i < lines.length; i++) {
4315
- const raw = lines[i];
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: i + 1,
4325
- detail: `expected 3 whitespace-delimited fields, found ${String(fields.length)}`
4525
+ line: lineNo,
4526
+ detail: `expected 3 whitespace-delimited fields, found ${String(fieldCount)}`
4326
4527
  })
4327
4528
  );
4328
- continue;
4329
- }
4330
- const [src, tgt, flagStr] = fields;
4331
- const flags = decodeFlags(flagStr);
4332
- if (flags === void 0) {
4333
- warnings.push(
4334
- Object.freeze({
4335
- code: "TERM_GEM_MALFORMED_ROW",
4336
- line: i + 1,
4337
- detail: "flag field is not a 5-digit code"
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
- continue;
4544
+ const list = entries.get(src);
4545
+ if (list) list.push(frozen);
4546
+ else entries.set(src, [frozen]);
4547
+ count++;
4341
4548
  }
4342
- const entry = { source: src, flags };
4343
- if (!isNoMapSentinel(tgt)) entry.target = tgt;
4344
- const frozen = Object.freeze(entry);
4345
- const list = entries.get(src);
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.6";
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