@cosyte/synth 0.1.4 → 0.1.6

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +14 -10
  3. package/dist/astm/index.cjs +164 -102
  4. package/dist/astm/index.cjs.map +1 -1
  5. package/dist/astm/index.d.cts +7 -0
  6. package/dist/astm/index.d.ts +7 -0
  7. package/dist/astm/index.mjs +163 -102
  8. package/dist/astm/index.mjs.map +1 -1
  9. package/dist/ccda/index.cjs +100 -28
  10. package/dist/ccda/index.cjs.map +1 -1
  11. package/dist/ccda/index.d.cts +6 -0
  12. package/dist/ccda/index.d.ts +6 -0
  13. package/dist/ccda/index.mjs +87 -16
  14. package/dist/ccda/index.mjs.map +1 -1
  15. package/dist/deid/index.cjs +305 -165
  16. package/dist/deid/index.cjs.map +1 -1
  17. package/dist/deid/index.mjs +292 -153
  18. package/dist/deid/index.mjs.map +1 -1
  19. package/dist/fhir/index.cjs +179 -76
  20. package/dist/fhir/index.cjs.map +1 -1
  21. package/dist/fhir/index.d.cts +6 -0
  22. package/dist/fhir/index.d.ts +6 -0
  23. package/dist/fhir/index.mjs +178 -76
  24. package/dist/fhir/index.mjs.map +1 -1
  25. package/dist/hl7/index.cjs +86 -12
  26. package/dist/hl7/index.cjs.map +1 -1
  27. package/dist/hl7/index.d.cts +6 -0
  28. package/dist/hl7/index.d.ts +6 -0
  29. package/dist/hl7/index.mjs +85 -12
  30. package/dist/hl7/index.mjs.map +1 -1
  31. package/dist/index.cjs +3 -5
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +8 -4
  34. package/dist/index.d.ts +8 -4
  35. package/dist/index.mjs +3 -5
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/ncpdp/index.cjs +131 -36
  38. package/dist/ncpdp/index.cjs.map +1 -1
  39. package/dist/ncpdp/index.d.cts +7 -0
  40. package/dist/ncpdp/index.d.ts +7 -0
  41. package/dist/ncpdp/index.mjs +130 -36
  42. package/dist/ncpdp/index.mjs.map +1 -1
  43. package/dist/x12/index.cjs +147 -50
  44. package/dist/x12/index.cjs.map +1 -1
  45. package/dist/x12/index.d.cts +6 -0
  46. package/dist/x12/index.d.ts +6 -0
  47. package/dist/x12/index.mjs +146 -50
  48. package/dist/x12/index.mjs.map +1 -1
  49. package/package.json +20 -15
@@ -362,6 +362,12 @@ interface CcdaCorpusOptions {
362
362
  readonly count?: number;
363
363
  /** The document types to cycle through. Defaults to one CCD + one Referral Note. */
364
364
  readonly mix?: readonly CcdaCorpusKind[];
365
+ /**
366
+ * Whether each artifact is read back through its parser and the parser's findings recorded as its
367
+ * `warnings`. Defaults to `true`. `false` skips the read, a large share of a corpus's time; every
368
+ * artifact's `warnings` is then empty without having been checked.
369
+ */
370
+ readonly verify?: boolean;
365
371
  }
366
372
  /**
367
373
  * Build a reproducible {@link Corpus} of spec-clean C-CDA documents. Each document is generated from a
@@ -362,6 +362,12 @@ interface CcdaCorpusOptions {
362
362
  readonly count?: number;
363
363
  /** The document types to cycle through. Defaults to one CCD + one Referral Note. */
364
364
  readonly mix?: readonly CcdaCorpusKind[];
365
+ /**
366
+ * Whether each artifact is read back through its parser and the parser's findings recorded as its
367
+ * `warnings`. Defaults to `true`. `false` skips the read, a large share of a corpus's time; every
368
+ * artifact's `warnings` is then empty without having been checked.
369
+ */
370
+ readonly verify?: boolean;
365
371
  }
366
372
  /**
367
373
  * Build a reproducible {@link Corpus} of spec-clean C-CDA documents. Each document is generated from a
@@ -1,4 +1,12 @@
1
- import { SNOMED_CT, NCI_ROUTE, buildCcda, serializeCcda, parseCcda, CVX, RXNORM, LOINC, ccdaProfiles } from '@cosyte/ccda';
1
+ import { createRequire } from 'module';
2
+ import { fileURLToPath } from 'url';
3
+
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
2
10
 
3
11
  // src/rng/splitmix32.ts
4
12
  function splitmix32(seed) {
@@ -30,7 +38,16 @@ function sfc32Next(s) {
30
38
  }
31
39
 
32
40
  // src/codes.ts
33
- var SYNTH_FATAL_CODES = {
41
+ var SYNTH_FATAL_CODES = Object.freeze({
42
+ /**
43
+ * A format was requested that this build cannot generate through a real parser builder/serializer.
44
+ * Fatal, never a hand-written byte fallback.
45
+ *
46
+ * **No code path in this build raises it.** All six formats generate, so it is reserved for a
47
+ * future format that does not, and is kept because removing a published code is a breaking change.
48
+ * An unsupported *kind* within a format that does generate is `SYNTH_UNSUPPORTED_KIND`.
49
+ */
50
+ SYNTH_UNSUPPORTED_FORMAT: "SYNTH_UNSUPPORTED_FORMAT",
34
51
  /**
35
52
  * A vendor quirk was requested that the target format's profile system does not support. Fatal,
36
53
  * never a silent no-op and never a fabricated quirk.
@@ -49,6 +66,8 @@ var SYNTH_FATAL_CODES = {
49
66
  SYNTH_INTENDED_WARNING_MISMATCH: "SYNTH_INTENDED_WARNING_MISMATCH",
50
67
  /** A concept's code-system URI has no OID mapping in the C-CDA example-code table. Fatal. */
51
68
  SYNTH_UNMAPPED_CODE_SYSTEM: "SYNTH_UNMAPPED_CODE_SYSTEM",
69
+ /** A money value could not be read as an X12 decimal. Fatal: a generator never rounds to a float. */
70
+ SYNTH_INVALID_DECIMAL: "SYNTH_INVALID_DECIMAL",
52
71
  /** An integer range was requested with its maximum below its minimum. Fatal. */
53
72
  SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
54
73
  /** A value was drawn from an empty pool. Fatal, never a fabricated substitute. */
@@ -61,7 +80,17 @@ var SYNTH_FATAL_CODES = {
61
80
  * see `resolveKind`: a selector union is erased at run time, and a selector that falls through
62
81
  * either mislabels the fixture or hands the value to a peer builder that quotes it back.
63
82
  */
64
- SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND"};
83
+ SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND",
84
+ /**
85
+ * A requested profile IS published by the adopted implementation guide, and this build does not
86
+ * generate it. Deliberately distinct from `SYNTH_UNSUPPORTED_KIND`, which says the name is not in
87
+ * the adopted set at all: "the guide does not publish this" and "we do not generate this yet" are
88
+ * different answers, and a caller building to a regulatory profile set has to be able to tell them
89
+ * apart without matching message text. Fatal, and raised **before** anything is generated: a
90
+ * mislabelled artifact is worse than no artifact.
91
+ */
92
+ SYNTH_PROFILE_NOT_GENERATED: "SYNTH_PROFILE_NOT_GENERATED"
93
+ });
65
94
  var SYNTH_FATAL_MESSAGES = Object.freeze({
66
95
  SYNTH_UNSUPPORTED_FORMAT: "The requested format is not generable by this build. A generator has no byte fallback: it builds through a parser's own serializer or it refuses.",
67
96
  SYNTH_UNSUPPORTED_QUIRK: "The requested vendor quirk is not in the target format's quirk registry. Compare the request against that format's exported registry (HL7_QUIRKS, CCDA_QUIRKS, ASTM_QUIRKS).",
@@ -127,6 +156,7 @@ function createRng(seed) {
127
156
  }
128
157
 
129
158
  // src/corpus.ts
159
+ var UNCHECKED = Object.freeze([]);
130
160
  function makeCorpus(seed, artifacts, quirks = []) {
131
161
  const counts = {};
132
162
  const formats = /* @__PURE__ */ new Set();
@@ -737,6 +767,16 @@ Object.freeze([
737
767
  ]);
738
768
 
739
769
  // src/ccda/example-codes.ts
770
+ var LOINC_OID = "2.16.840.1.113883.6.1";
771
+ var SNOMED_CT_OID = "2.16.840.1.113883.6.96";
772
+ var RXNORM_OID = "2.16.840.1.113883.6.88";
773
+ var CVX_OID = "2.16.840.1.113883.12.292";
774
+ var NCI_ROUTE_OID = "2.16.840.1.113883.3.26.1.1";
775
+ var LOINC = LOINC_OID;
776
+ var SNOMED_CT = SNOMED_CT_OID;
777
+ var RXNORM = RXNORM_OID;
778
+ var CVX = CVX_OID;
779
+ var NCI_ROUTE = NCI_ROUTE_OID;
740
780
  var URI_TO_OID = Object.freeze({
741
781
  "http://loinc.org": LOINC,
742
782
  "http://snomed.info/sct": SNOMED_CT,
@@ -813,6 +853,28 @@ function resolveMix(allowed, requested, fallback) {
813
853
  if (requested === void 0) return fallback;
814
854
  return requested.map((entry) => resolveKind(allowed, entry));
815
855
  }
856
+ function defaultHost() {
857
+ if (typeof import.meta.resolve !== "function") return { require: __require, resolve: void 0 };
858
+ return { require: createRequire(import.meta.url), resolve: (s) => import.meta.resolve(s) };
859
+ }
860
+ function isRequireEsmRefusal(error) {
861
+ return error?.code === "ERR_REQUIRE_ESM";
862
+ }
863
+ function loadPeer(specifier, host) {
864
+ if (host.resolve === void 0) return host.require(specifier);
865
+ const entry = fileURLToPath(host.resolve(specifier));
866
+ try {
867
+ return host.require(entry);
868
+ } catch (error) {
869
+ if (!isRequireEsmRefusal(error)) throw error;
870
+ return host.require(specifier);
871
+ }
872
+ }
873
+ function lazyPeer(specifier, host) {
874
+ let loaded;
875
+ return () => loaded ??= loadPeer(specifier, defaultHost());
876
+ }
877
+ var ccdaPeer = lazyPeer("@cosyte/ccda");
816
878
 
817
879
  // src/ccda/ccd.ts
818
880
  var CCDA_DOCUMENT_TYPES = Object.freeze(["ccd", "referralNote"]);
@@ -901,7 +963,7 @@ function generateCcda(options = {}) {
901
963
  const { seed = 0 } = options;
902
964
  const documentType = resolveKind(CCDA_DOCUMENT_TYPES, options.documentType ?? "ccd");
903
965
  const rng = createRng(seed);
904
- return buildCcda(buildInit(rng, documentType));
966
+ return ccdaPeer().buildCcda(buildInit(rng, documentType));
905
967
  }
906
968
  function generateCcd(options = {}) {
907
969
  return generateCcda({ ...options, documentType: "ccd" });
@@ -909,11 +971,13 @@ function generateCcd(options = {}) {
909
971
  function generateReferralNote(options = {}) {
910
972
  return generateCcda({ ...options, documentType: "referralNote" });
911
973
  }
974
+
975
+ // src/ccda/round-trip.ts
912
976
  function roundTrip(doc) {
913
- const content = serializeCcda(doc);
914
- const reparsed = parseCcda(content);
977
+ const content = ccdaPeer().serializeCcda(doc);
978
+ const reparsed = ccdaPeer().parseCcda(content);
915
979
  const warnings = reparsed.warnings.map((w) => String(w.code));
916
- const byteStable = serializeCcda(reparsed) === content;
980
+ const byteStable = ccdaPeer().serializeCcda(reparsed) === content;
917
981
  return {
918
982
  content,
919
983
  warnings,
@@ -921,6 +985,14 @@ function roundTrip(doc) {
921
985
  specClean: warnings.length === 0 && byteStable
922
986
  };
923
987
  }
988
+ function ccdaCorpusArtifact(doc, verify) {
989
+ const content = ccdaPeer().serializeCcda(doc);
990
+ if (!verify) return { content, warnings: UNCHECKED };
991
+ return {
992
+ content,
993
+ warnings: ccdaPeer().parseCcda(content).warnings.map((w) => String(w.code))
994
+ };
995
+ }
924
996
 
925
997
  // src/profile.ts
926
998
  function defineSynthProfile(spec) {
@@ -1005,7 +1077,7 @@ var CCDA_QUIRKS = Object.freeze({
1005
1077
  })
1006
1078
  });
1007
1079
  function toleratingProfile(quirk) {
1008
- return quirk === "template-extension-absent" ? ccdaProfiles.legacyR11 : ccdaProfiles.smartScorecard;
1080
+ return quirk === "template-extension-absent" ? ccdaPeer().ccdaProfiles.legacyR11 : ccdaPeer().ccdaProfiles.smartScorecard;
1009
1081
  }
1010
1082
  var DOC_TEMPLATE_ROOTS = [
1011
1083
  "2.16.840.1.113883.10.20.22.1.1",
@@ -1045,11 +1117,11 @@ function generateCcdaQuirk(options) {
1045
1117
  const seed = options.seed ?? 0;
1046
1118
  const documentType = options.documentType ?? "ccd";
1047
1119
  const descriptor = resolveQuirk(CCDA_QUIRKS, "ccda", options.quirk);
1048
- const clean = serializeCcda(generateCcda({ seed, documentType }));
1120
+ const clean = ccdaPeer().serializeCcda(generateCcda({ seed, documentType }));
1049
1121
  const content = injectCcdaQuirk(options.quirk, clean);
1050
1122
  assertIntendedWarnings(
1051
1123
  descriptor.intendedWarnings,
1052
- parseCcda(content).warnings.map((w) => String(w.code))
1124
+ ccdaPeer().parseCcda(content).warnings.map((w) => String(w.code))
1053
1125
  );
1054
1126
  return Object.freeze({
1055
1127
  format: "ccda",
@@ -1062,17 +1134,16 @@ function generateCcdaQuirk(options) {
1062
1134
  function ccdaQuirkRoundTrip(artifact) {
1063
1135
  const quirk = artifact.quirk;
1064
1136
  const descriptor = resolveQuirk(CCDA_QUIRKS, "ccda", quirk);
1065
- const bare = parseCcda(artifact.content).warnings.map((w) => String(w.code));
1137
+ const bare = ccdaPeer().parseCcda(artifact.content).warnings.map((w) => String(w.code));
1066
1138
  const profile = toleratingProfile(quirk);
1067
- const profiledWarnings = parseCcda(artifact.content, { profile }).warnings.map(
1068
- (w) => String(w.code)
1069
- );
1139
+ const profiledWarnings = ccdaPeer().parseCcda(artifact.content, { profile }).warnings.map((w) => String(w.code));
1070
1140
  return {
1071
1141
  content: artifact.content,
1072
1142
  warnings: bare,
1073
1143
  intendedWarnings: artifact.intendedWarnings,
1074
1144
  intendedWarningHeld: sameCodeSet(bare, artifact.intendedWarnings),
1075
1145
  withProfile: {
1146
+ /* v8 ignore next -- the `?? profile.name` is unreachable: every descriptor in the frozen CCDA_QUIRKS names its toleratingProfile */
1076
1147
  profileName: descriptor.toleratingProfile ?? profile.name,
1077
1148
  disposition: descriptor.disposition,
1078
1149
  warnings: profiledWarnings,
@@ -1116,13 +1187,13 @@ var ccdaQuirkProfile = defineSynthProfile({
1116
1187
  var ALL_KINDS = Object.freeze(["ccd", "referralNote"]);
1117
1188
  var DEFAULT_MIX = ALL_KINDS;
1118
1189
  function ccdaCorpus(options) {
1119
- const { seed, count = 1 } = options;
1190
+ const { seed, count = 1, verify = true } = options;
1120
1191
  const mix = resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
1121
1192
  const seedStream = createRng(seed);
1122
1193
  const artifacts = Array.from({ length: count }, (_unused, i) => {
1123
1194
  const documentType = mix[i % mix.length] ?? "ccd";
1124
1195
  const docSeed = seedStream.nextUint32();
1125
- const rt = roundTrip(generateCcda({ seed: docSeed, documentType }));
1196
+ const rt = ccdaCorpusArtifact(generateCcda({ seed: docSeed, documentType }), verify);
1126
1197
  return {
1127
1198
  format: "ccda",
1128
1199
  kind: documentType,