@cosyte/synth 0.0.2 → 0.0.4

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 +321 -0
  2. package/README.md +13 -4
  3. package/dist/astm/index.cjs +83 -42
  4. package/dist/astm/index.cjs.map +1 -1
  5. package/dist/astm/index.d.cts +1 -1
  6. package/dist/astm/index.d.ts +1 -1
  7. package/dist/astm/index.mjs +83 -42
  8. package/dist/astm/index.mjs.map +1 -1
  9. package/dist/ccda/index.cjs +89 -44
  10. package/dist/ccda/index.cjs.map +1 -1
  11. package/dist/ccda/index.d.cts +5 -3
  12. package/dist/ccda/index.d.ts +5 -3
  13. package/dist/ccda/index.mjs +89 -44
  14. package/dist/ccda/index.mjs.map +1 -1
  15. package/dist/deid/index.cjs +92 -13
  16. package/dist/deid/index.cjs.map +1 -1
  17. package/dist/deid/index.d.cts +1 -1
  18. package/dist/deid/index.d.ts +1 -1
  19. package/dist/deid/index.mjs +93 -14
  20. package/dist/deid/index.mjs.map +1 -1
  21. package/dist/fhir/index.cjs +78 -5
  22. package/dist/fhir/index.cjs.map +1 -1
  23. package/dist/fhir/index.mjs +78 -5
  24. package/dist/fhir/index.mjs.map +1 -1
  25. package/dist/hl7/index.cjs +89 -41
  26. package/dist/hl7/index.cjs.map +1 -1
  27. package/dist/hl7/index.d.cts +1 -1
  28. package/dist/hl7/index.d.ts +1 -1
  29. package/dist/hl7/index.mjs +89 -41
  30. package/dist/hl7/index.mjs.map +1 -1
  31. package/dist/index.cjs +91 -40
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +126 -8
  34. package/dist/index.d.ts +126 -8
  35. package/dist/index.mjs +89 -41
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/ncpdp/index.cjs +56 -4
  38. package/dist/ncpdp/index.cjs.map +1 -1
  39. package/dist/ncpdp/index.mjs +56 -4
  40. package/dist/ncpdp/index.mjs.map +1 -1
  41. package/dist/{quirk-JLyO1Ncj.d.ts → quirk-C9t9CkPS.d.ts} +17 -6
  42. package/dist/{quirk-DmkgoZdh.d.cts → quirk-DYMDojVw.d.cts} +17 -6
  43. package/dist/x12/index.cjs +82 -13
  44. package/dist/x12/index.cjs.map +1 -1
  45. package/dist/x12/index.d.cts +2 -1
  46. package/dist/x12/index.d.ts +2 -1
  47. package/dist/x12/index.mjs +83 -14
  48. package/dist/x12/index.mjs.map +1 -1
  49. package/package.json +2 -2
@@ -43,6 +43,49 @@ function sfc32Next(s) {
43
43
  return t >>> 0;
44
44
  }
45
45
 
46
+ // src/codes.ts
47
+ var SYNTH_FATAL_CODES = {
48
+ /** A concept's code-system URI has no OID mapping in the C-CDA example-code table. Fatal. */
49
+ SYNTH_UNMAPPED_CODE_SYSTEM: "SYNTH_UNMAPPED_CODE_SYSTEM",
50
+ /** A money value could not be read as an X12 decimal. Fatal — a generator never rounds to a float. */
51
+ SYNTH_INVALID_DECIMAL: "SYNTH_INVALID_DECIMAL",
52
+ /** An integer range was requested with its maximum below its minimum. Fatal. */
53
+ SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
54
+ /** A value was drawn from an empty pool. Fatal — never a fabricated substitute. */
55
+ SYNTH_EMPTY_POOL: "SYNTH_EMPTY_POOL",
56
+ /**
57
+ * A caller-supplied selector (a message kind, a document type, a corpus mix entry, a claim
58
+ * variant, a Bundle type, a resource profile) is not in the closed set that governs it. Fatal —
59
+ * see `resolveKind`: a selector union is erased at run time, and a selector that falls through
60
+ * either mislabels the fixture or hands the value to a peer builder that quotes it back.
61
+ */
62
+ SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND"
63
+ };
64
+ var SYNTH_FATAL_MESSAGES = Object.freeze({
65
+ 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.",
66
+ 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).",
67
+ SYNTH_QUIRK_ANCHOR_ABSENT: "The quirk transform found no structural anchor to mutate, so the fixture would not carry the deviation it is labelled with. Refusing to emit a mislabeled fixture.",
68
+ SYNTH_INTENDED_WARNING_MISMATCH: "A bare parse of the generated quirk artifact did not produce exactly the declared intended warning code(s). Refusing to emit a mislabeled fixture.",
69
+ SYNTH_UNMAPPED_CODE_SYSTEM: "The concept's code-system URI has no OID mapping in the C-CDA example-code table.",
70
+ SYNTH_INVALID_DECIMAL: "The value could not be read as an X12 decimal.",
71
+ SYNTH_INVALID_RANGE: "An integer range was requested with its maximum below its minimum.",
72
+ SYNTH_EMPTY_POOL: "A value was drawn from an empty pool.",
73
+ SYNTH_INVALID_PROFILE: "defineSynthProfile requires a non-empty string name.",
74
+ SYNTH_UNSUPPORTED_KIND: "The requested kind, document type, corpus mix entry, variant or profile is not one this generator supports. The supported set is the exported union for that option."
75
+ });
76
+ var SynthError = class extends Error {
77
+ /** The stable fatal code. */
78
+ code;
79
+ /**
80
+ * @param code - The stable {@link SynthFatalCode}. The message comes from the frozen registry.
81
+ */
82
+ constructor(code) {
83
+ super(SYNTH_FATAL_MESSAGES[code]);
84
+ this.name = "SynthError";
85
+ this.code = code;
86
+ }
87
+ };
88
+
46
89
  // src/rng/rng.ts
47
90
  var Sfc32Rng = class {
48
91
  seed;
@@ -60,7 +103,7 @@ var Sfc32Rng = class {
60
103
  return this.nextUint32() / 4294967296;
61
104
  }
62
105
  int(min, max) {
63
- if (max < min) throw new RangeError(`Rng.int: max (${String(max)}) < min (${String(min)})`);
106
+ if (max < min) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INVALID_RANGE);
64
107
  const span = max - min + 1;
65
108
  return min + Math.floor(this.float() * span);
66
109
  }
@@ -68,7 +111,7 @@ var Sfc32Rng = class {
68
111
  return this.float() < p;
69
112
  }
70
113
  pick(items) {
71
- if (items.length === 0) throw new RangeError("Rng.pick: empty array");
114
+ if (items.length === 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_EMPTY_POOL);
72
115
  return items[this.int(0, items.length - 1)];
73
116
  }
74
117
  digits(n) {
@@ -438,10 +481,18 @@ function pidSegment(id) {
438
481
  ];
439
482
  }
440
483
 
484
+ // src/select.ts
485
+ function resolveKind(allowed, requested) {
486
+ const match = allowed.find((value) => value === requested);
487
+ if (match === void 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_UNSUPPORTED_KIND);
488
+ return match;
489
+ }
490
+
441
491
  // src/hl7/adt.ts
492
+ var ADT_TRIGGERS = Object.freeze(["A01", "A04", "A08"]);
442
493
  function generateAdt(options = {}) {
443
494
  const seed = options.seed ?? 0;
444
- const trigger = options.trigger ?? "A01";
495
+ const trigger = resolveKind(ADT_TRIGGERS, options.trigger ?? "A01");
445
496
  const rng = createRng(seed);
446
497
  const { message, timestamp } = mshScaffold(rng, `ADT^${trigger}`);
447
498
  message.addSegment("EVN", [trigger, timestamp]);
@@ -712,8 +763,19 @@ function generateVxu(options = {}) {
712
763
  ]);
713
764
  return message;
714
765
  }
766
+
767
+ // src/hl7/index.ts
768
+ var ALL_KINDS = Object.freeze([
769
+ "ADT^A01",
770
+ "ADT^A04",
771
+ "ADT^A08",
772
+ "ORU^R01",
773
+ "ORM^O01",
774
+ "SIU^S12",
775
+ "VXU^V04"
776
+ ]);
715
777
  function generateHl7(kind, seed) {
716
- switch (kind) {
778
+ switch (resolveKind(ALL_KINDS, kind)) {
717
779
  case "ADT^A01":
718
780
  return generateAdt({ seed, trigger: "A01" });
719
781
  case "ADT^A04":
@@ -1132,6 +1194,7 @@ var EXAMPLE_ENCOUNTER_CLASSES = Object.freeze([
1132
1194
  ]);
1133
1195
 
1134
1196
  // src/fhir/patient.ts
1197
+ var PATIENT_PROFILES = Object.freeze(["base", "us-core"]);
1135
1198
  function ombExtension(url, category) {
1136
1199
  return fhir.complex([
1137
1200
  prop("url", str(url)),
@@ -1145,7 +1208,8 @@ function ombExtension(url, category) {
1145
1208
  ]);
1146
1209
  }
1147
1210
  function generatePatient(options = {}) {
1148
- const { seed = 0, profile = "base" } = options;
1211
+ const { seed = 0 } = options;
1212
+ const profile = resolveKind(PATIENT_PROFILES, options.profile ?? "base");
1149
1213
  const rng = createRng(seed);
1150
1214
  const id = fhirPatientIdentity(rng);
1151
1215
  const usCore = profile === "us-core";
@@ -1528,6 +1592,11 @@ function buildComposition(rng, input) {
1528
1592
  }
1529
1593
 
1530
1594
  // src/fhir/bundle.ts
1595
+ var BUNDLE_TYPES = Object.freeze([
1596
+ "collection",
1597
+ "transaction",
1598
+ "document"
1599
+ ]);
1531
1600
  function resourceTypeOf(resource) {
1532
1601
  const rt = resource.properties.find((p) => p.name === "resourceType");
1533
1602
  return rt !== void 0 && rt.value.kind === "primitive" ? String(rt.value.value) : "Resource";
@@ -1629,7 +1698,8 @@ function documentSections(urls) {
1629
1698
  ];
1630
1699
  }
1631
1700
  function generateBundle(options = {}) {
1632
- const { seed = 0, type = "collection" } = options;
1701
+ const { seed = 0 } = options;
1702
+ const type = resolveKind(BUNDLE_TYPES, options.type ?? "collection");
1633
1703
  const rng = createRng(seed);
1634
1704
  const spine = buildSpine(rng);
1635
1705
  const members = type === "document" ? [
@@ -1682,7 +1752,7 @@ function generateBundle(options = {}) {
1682
1752
  }
1683
1753
  function dec2(value) {
1684
1754
  const d = x12.X12Decimal.fromString(value);
1685
- if (d === void 0) throw new Error(`invalid synthetic decimal: ${value}`);
1755
+ if (d === void 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INVALID_DECIMAL);
1686
1756
  return d;
1687
1757
  }
1688
1758
  function money(n) {
@@ -1788,6 +1858,7 @@ var TOOTH_SURFACES = Object.freeze(["M", "O", "D", "B", "L"]);
1788
1858
  var SERVICE_TYPE_CODES = Object.freeze(["30", "1", "35", "88", "98"]);
1789
1859
 
1790
1860
  // src/x12/claim-837.ts
1861
+ var CLAIM_837_VARIANTS = Object.freeze(["P", "I", "D"]);
1791
1862
  function serviceDtp(serviceDate2) {
1792
1863
  return { qualifier: "472", formatQualifier: "D8", value: serviceDate2 };
1793
1864
  }
@@ -1970,11 +2041,17 @@ function specOf(rng, variant) {
1970
2041
  };
1971
2042
  }
1972
2043
  function generate837(variant, options) {
2044
+ const resolved = resolveKind(CLAIM_837_VARIANTS, variant);
1973
2045
  const rng = createRng(options.seed);
1974
- const spec = specOf(rng, variant);
1975
- if (variant === "P") return x12.build837P(spec);
1976
- if (variant === "I") return x12.build837I(spec);
1977
- return x12.build837D(spec);
2046
+ const spec = specOf(rng, resolved);
2047
+ switch (resolved) {
2048
+ case "P":
2049
+ return x12.build837P(spec);
2050
+ case "I":
2051
+ return x12.build837I(spec);
2052
+ case "D":
2053
+ return x12.build837D(spec);
2054
+ }
1978
2055
  }
1979
2056
  function generate837P(options) {
1980
2057
  return generate837("P", options);
@@ -2339,7 +2416,7 @@ var URI_TO_OID = Object.freeze({
2339
2416
  function toBuildCode(concept) {
2340
2417
  const codeSystem = URI_TO_OID[concept.system];
2341
2418
  if (codeSystem === void 0) {
2342
- throw new Error(`ccda example-codes: no OID mapping for code system "${concept.system}"`);
2419
+ throw new SynthError(SYNTH_FATAL_CODES.SYNTH_UNMAPPED_CODE_SYSTEM);
2343
2420
  }
2344
2421
  return { code: concept.code, codeSystem, displayName: concept.display };
2345
2422
  }
@@ -2397,6 +2474,7 @@ function ccdaPatientIdentity(rng) {
2397
2474
  }
2398
2475
 
2399
2476
  // src/ccda/ccd.ts
2477
+ var CCDA_DOCUMENT_TYPES = Object.freeze(["ccd", "referralNote"]);
2400
2478
  function pickN(rng, pool, n) {
2401
2479
  const take = Math.min(n, pool.length);
2402
2480
  const indices = pool.map((_v, i) => i);
@@ -2479,7 +2557,8 @@ function buildInit(rng, documentType) {
2479
2557
  return base;
2480
2558
  }
2481
2559
  function generateCcda(options = {}) {
2482
- const { seed = 0, documentType = "ccd" } = options;
2560
+ const { seed = 0 } = options;
2561
+ const documentType = resolveKind(CCDA_DOCUMENT_TYPES, options.documentType ?? "ccd");
2483
2562
  const rng = createRng(seed);
2484
2563
  return ccda.buildCcda(buildInit(rng, documentType));
2485
2564
  }