@cosyte/synth 0.0.3 → 0.0.5

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 +335 -0
  2. package/README.md +6 -2
  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
@@ -1,6 +1,6 @@
1
1
  import { Hl7Message, RawField } from '@cosyte/hl7';
2
2
  import { S as SyntheticName, a as SyntheticIdentifier, b as SyntheticAddress, R as Rng, C as Corpus } from '../providers-OLz3zAc-.cjs';
3
- import { Q as QuirkDescriptor, S as SynthProfile, a as QuirkArtifact, b as QuirkRoundTripResult } from '../quirk-DmkgoZdh.cjs';
3
+ import { Q as QuirkDescriptor, S as SynthProfile, a as QuirkArtifact, b as QuirkRoundTripResult } from '../quirk-DYMDojVw.cjs';
4
4
 
5
5
  /**
6
6
  * Spec-clean HL7 v2 `ADT` generation, built **through `@cosyte/hl7`'s `buildMessage`** so MSH
@@ -1,6 +1,6 @@
1
1
  import { Hl7Message, RawField } from '@cosyte/hl7';
2
2
  import { S as SyntheticName, a as SyntheticIdentifier, b as SyntheticAddress, R as Rng, C as Corpus } from '../providers-OLz3zAc-.js';
3
- import { Q as QuirkDescriptor, S as SynthProfile, a as QuirkArtifact, b as QuirkRoundTripResult } from '../quirk-JLyO1Ncj.js';
3
+ import { Q as QuirkDescriptor, S as SynthProfile, a as QuirkArtifact, b as QuirkRoundTripResult } from '../quirk-C9t9CkPS.js';
4
4
 
5
5
  /**
6
6
  * Spec-clean HL7 v2 `ADT` generation, built **through `@cosyte/hl7`'s `buildMessage`** so MSH
@@ -29,6 +29,57 @@ function sfc32Next(s) {
29
29
  return t >>> 0;
30
30
  }
31
31
 
32
+ // src/codes.ts
33
+ var SYNTH_FATAL_CODES = {
34
+ /**
35
+ * A vendor quirk was requested that the target format's profile system does not support. Fatal —
36
+ * never a silent no-op and never a fabricated quirk.
37
+ */
38
+ SYNTH_UNSUPPORTED_QUIRK: "SYNTH_UNSUPPORTED_QUIRK",
39
+ /**
40
+ * A bare parse of a freshly-generated quirk artifact did not produce exactly the declared intended
41
+ * warning code(s). Fatal — never emit a mislabeled fixture.
42
+ */
43
+ SYNTH_INTENDED_WARNING_MISMATCH: "SYNTH_INTENDED_WARNING_MISMATCH",
44
+ /** An integer range was requested with its maximum below its minimum. Fatal. */
45
+ SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
46
+ /** A value was drawn from an empty pool. Fatal — never a fabricated substitute. */
47
+ SYNTH_EMPTY_POOL: "SYNTH_EMPTY_POOL",
48
+ /** A `defineSynthProfile` spec was not usable (a missing or blank `name`). Fatal. */
49
+ SYNTH_INVALID_PROFILE: "SYNTH_INVALID_PROFILE",
50
+ /**
51
+ * A caller-supplied selector (a message kind, a document type, a corpus mix entry, a claim
52
+ * variant, a Bundle type, a resource profile) is not in the closed set that governs it. Fatal —
53
+ * see `resolveKind`: a selector union is erased at run time, and a selector that falls through
54
+ * either mislabels the fixture or hands the value to a peer builder that quotes it back.
55
+ */
56
+ SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND"
57
+ };
58
+ var SYNTH_FATAL_MESSAGES = Object.freeze({
59
+ 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.",
60
+ 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).",
61
+ 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.",
62
+ 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.",
63
+ SYNTH_UNMAPPED_CODE_SYSTEM: "The concept's code-system URI has no OID mapping in the C-CDA example-code table.",
64
+ SYNTH_INVALID_DECIMAL: "The value could not be read as an X12 decimal.",
65
+ SYNTH_INVALID_RANGE: "An integer range was requested with its maximum below its minimum.",
66
+ SYNTH_EMPTY_POOL: "A value was drawn from an empty pool.",
67
+ SYNTH_INVALID_PROFILE: "defineSynthProfile requires a non-empty string name.",
68
+ 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."
69
+ });
70
+ var SynthError = class extends Error {
71
+ /** The stable fatal code. */
72
+ code;
73
+ /**
74
+ * @param code - The stable {@link SynthFatalCode}. The message comes from the frozen registry.
75
+ */
76
+ constructor(code) {
77
+ super(SYNTH_FATAL_MESSAGES[code]);
78
+ this.name = "SynthError";
79
+ this.code = code;
80
+ }
81
+ };
82
+
32
83
  // src/rng/rng.ts
33
84
  var Sfc32Rng = class {
34
85
  seed;
@@ -46,7 +97,7 @@ var Sfc32Rng = class {
46
97
  return this.nextUint32() / 4294967296;
47
98
  }
48
99
  int(min, max) {
49
- if (max < min) throw new RangeError(`Rng.int: max (${String(max)}) < min (${String(min)})`);
100
+ if (max < min) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INVALID_RANGE);
50
101
  const span = max - min + 1;
51
102
  return min + Math.floor(this.float() * span);
52
103
  }
@@ -54,7 +105,7 @@ var Sfc32Rng = class {
54
105
  return this.float() < p;
55
106
  }
56
107
  pick(items) {
57
- if (items.length === 0) throw new RangeError("Rng.pick: empty array");
108
+ if (items.length === 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_EMPTY_POOL);
58
109
  return items[this.int(0, items.length - 1)];
59
110
  }
60
111
  digits(n) {
@@ -445,10 +496,22 @@ function pidSegment(id) {
445
496
  ];
446
497
  }
447
498
 
499
+ // src/select.ts
500
+ function resolveKind(allowed, requested) {
501
+ const match = allowed.find((value) => value === requested);
502
+ if (match === void 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_UNSUPPORTED_KIND);
503
+ return match;
504
+ }
505
+ function resolveMix(allowed, requested, fallback) {
506
+ if (requested === void 0) return fallback;
507
+ return requested.map((entry) => resolveKind(allowed, entry));
508
+ }
509
+
448
510
  // src/hl7/adt.ts
511
+ var ADT_TRIGGERS = Object.freeze(["A01", "A04", "A08"]);
449
512
  function generateAdt(options = {}) {
450
513
  const seed = options.seed ?? 0;
451
- const trigger = options.trigger ?? "A01";
514
+ const trigger = resolveKind(ADT_TRIGGERS, options.trigger ?? "A01");
452
515
  const rng = createRng(seed);
453
516
  const { message, timestamp } = mshScaffold(rng, `ADT^${trigger}`);
454
517
  message.addSegment("EVN", [trigger, timestamp]);
@@ -735,7 +798,7 @@ function roundTrip(message) {
735
798
  // src/profile.ts
736
799
  function defineSynthProfile(spec) {
737
800
  if (typeof spec.name !== "string" || spec.name.trim().length === 0) {
738
- throw new TypeError("defineSynthProfile: `name` is required and must be a non-empty string.");
801
+ throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INVALID_PROFILE);
739
802
  }
740
803
  return Object.freeze({
741
804
  name: spec.name,
@@ -745,28 +808,6 @@ function defineSynthProfile(spec) {
745
808
  });
746
809
  }
747
810
 
748
- // src/codes.ts
749
- var SYNTH_FATAL_CODES = {
750
- /**
751
- * A vendor quirk was requested that the target format's profile system does not support. Fatal —
752
- * never a silent no-op and never a fabricated quirk.
753
- */
754
- SYNTH_UNSUPPORTED_QUIRK: "SYNTH_UNSUPPORTED_QUIRK"
755
- };
756
- var SynthError = class extends Error {
757
- /** The stable fatal code. */
758
- code;
759
- /**
760
- * @param code - The stable {@link SynthFatalCode}.
761
- * @param message - A human-readable detail (never contains PHI — there is none).
762
- */
763
- constructor(code, message) {
764
- super(message);
765
- this.name = "SynthError";
766
- this.code = code;
767
- }
768
- };
769
-
770
811
  // src/quirk.ts
771
812
  var PROFILE_QUIRK_APPLIED = "PROFILE_QUIRK_APPLIED";
772
813
  function sameCodeSet(a, b) {
@@ -783,12 +824,8 @@ function sameCodeSet(a, b) {
783
824
  }
784
825
  function resolveQuirk(registry, format, name2) {
785
826
  const descriptor = registry[name2];
786
- if (descriptor === void 0) {
787
- const supported = Object.keys(registry).sort().join(", ");
788
- throw new SynthError(
789
- SYNTH_FATAL_CODES.SYNTH_UNSUPPORTED_QUIRK,
790
- `${format}: unsupported quirk "${name2}". The ${format} profile system supports: ${supported}.`
791
- );
827
+ if (descriptor === void 0 || descriptor.format !== format) {
828
+ throw new SynthError(SYNTH_FATAL_CODES.SYNTH_UNSUPPORTED_QUIRK);
792
829
  }
793
830
  return descriptor;
794
831
  }
@@ -803,11 +840,9 @@ function profileTolerated(disposition, intendedWarnings, warningsUnderProfile) {
803
840
  return false;
804
841
  }
805
842
  }
806
- function assertIntendedWarnings(quirk, intendedWarnings, bareWarnings) {
843
+ function assertIntendedWarnings(intendedWarnings, bareWarnings) {
807
844
  if (!sameCodeSet(bareWarnings, intendedWarnings)) {
808
- throw new Error(
809
- `quirk "${quirk}": the intended-warning contract does not hold \u2014 expected exactly [${intendedWarnings.join(", ")}] but a bare parse produced [${bareWarnings.join(", ")}]. Refusing to emit a mislabeled fixture.`
810
- );
845
+ throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INTENDED_WARNING_MISMATCH);
811
846
  }
812
847
  }
813
848
  function validateProfileQuirks(profile, registry, format) {
@@ -848,6 +883,15 @@ function applyQuirk(quirk, wire) {
848
883
  return [...segments, "NTE|1||\\Zff\\"].join("\r");
849
884
  }
850
885
  }
886
+ var ALL_QUIRK_KINDS = Object.freeze([
887
+ "ADT^A01",
888
+ "ADT^A04",
889
+ "ADT^A08",
890
+ "ORU^R01",
891
+ "ORM^O01",
892
+ "SIU^S12",
893
+ "VXU^V04"
894
+ ]);
851
895
  function baseMessage(kind, seed) {
852
896
  switch (kind) {
853
897
  case "ADT^A01":
@@ -868,11 +912,10 @@ function baseMessage(kind, seed) {
868
912
  }
869
913
  function generateHl7Quirk(options) {
870
914
  const seed = options.seed ?? 0;
871
- const kind = options.kind ?? "ORU^R01";
915
+ const kind = resolveKind(ALL_QUIRK_KINDS, options.kind ?? "ORU^R01");
872
916
  const descriptor = resolveQuirk(HL7_QUIRKS, "hl7v2", options.quirk);
873
917
  const content = applyQuirk(options.quirk, baseMessage(kind, seed).toString());
874
918
  assertIntendedWarnings(
875
- descriptor.name,
876
919
  descriptor.intendedWarnings,
877
920
  parseHL7(content).warnings.map((w) => String(w.code))
878
921
  );
@@ -918,6 +961,7 @@ var ALL_HL7_QUIRKS = Object.freeze(
918
961
  function hl7QuirkCorpus(options) {
919
962
  const quirks = options.profile ? validateProfileQuirks(options.profile, HL7_QUIRKS, "hl7v2") : options.quirks ?? ALL_HL7_QUIRKS;
920
963
  const names = quirks.length > 0 ? quirks : ALL_HL7_QUIRKS;
964
+ for (const name2 of names) resolveQuirk(HL7_QUIRKS, "hl7v2", name2);
921
965
  const kind = options.kind ?? "ORU^R01";
922
966
  const count = options.count ?? names.length;
923
967
  const seedStream = createRng(options.seed);
@@ -940,7 +984,7 @@ var hl7QuirkProfile = defineSynthProfile({
940
984
  });
941
985
 
942
986
  // src/hl7/index.ts
943
- var DEFAULT_MIX = Object.freeze([
987
+ var ALL_KINDS = Object.freeze([
944
988
  "ADT^A01",
945
989
  "ADT^A04",
946
990
  "ADT^A08",
@@ -949,8 +993,10 @@ var DEFAULT_MIX = Object.freeze([
949
993
  "SIU^S12",
950
994
  "VXU^V04"
951
995
  ]);
996
+ var DEFAULT_MIX = ALL_KINDS;
997
+ var ADT_TRIGGERS2 = Object.freeze(["A01", "A04", "A08"]);
952
998
  function generateHl7(kind, seed) {
953
- switch (kind) {
999
+ switch (resolveKind(ALL_KINDS, kind)) {
954
1000
  case "ADT^A01":
955
1001
  return generateAdt({ seed, trigger: "A01" });
956
1002
  case "ADT^A04":
@@ -969,7 +1015,9 @@ function generateHl7(kind, seed) {
969
1015
  }
970
1016
  function hl7Corpus(options) {
971
1017
  const { seed, count = 1 } = options;
972
- const kinds = options.triggers !== void 0 ? options.triggers.map((t) => `ADT^${t}`) : options.mix ?? DEFAULT_MIX;
1018
+ const kinds = options.triggers !== void 0 ? options.triggers.map(
1019
+ (t) => `ADT^${resolveKind(ADT_TRIGGERS2, t)}`
1020
+ ) : resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
973
1021
  const seedStream = createRng(seed);
974
1022
  const artifacts = Array.from({ length: count }, (_unused, i) => {
975
1023
  const kind = kinds[i % kinds.length] ?? "ADT^A01";