Skip to main content
Version: v0.0.1

@cosyte/synth

@cosyte/synth - a deterministic, seedable synthetic-data / test-fixture generator for the cosyte healthcare formats. A consumer of the parsers, not a parser: it builds artifacts through each parser's own builder/serializer (so output is spec-clean by construction) and draws every value from a guaranteed-non-colliding synthetic source (so no output can be real or plausibly-real PHI). It is a format/conformance generator, not a clinical simulator.

This root entry point exposes the format-agnostic core: the seeded PRNG, the synthetic-safety providers, the Corpus abstraction, the profile skeleton, and the fatal codes. Per-format generation lives behind its own subpath (@cosyte/synth/hl7) so importing the root never pulls a parser - the lazy per-format boundary.

Classes

SynthError

The typed error every fatal @cosyte/synth condition throws. Carries a stable SynthFatalCode so callers branch on err.code without matching message text.

Example

import { SynthError, SYNTH_FATAL_CODES } from "@cosyte/synth";
throw new SynthError(SYNTH_FATAL_CODES.SYNTH_UNSUPPORTED_FORMAT, "astm is not yet generable");

Extends

  • Error

Constructors

Constructor

new SynthError(code, message): SynthError

Parameters
code

SynthFatalCode

The stable SynthFatalCode.

message

string

A human-readable detail (never contains PHI - there is none).

Returns

SynthError

Overrides

Error.constructor

Properties

code

readonly code: SynthFatalCode

The stable fatal code.

Interfaces

Artifact

One generated artifact - the serialized wire text plus the metadata needed to reproduce and check it. warnings records what the artifact's own parser reported on the round-trip (zero for a spec-clean artifact).

Properties

content

readonly content: string

The serialized wire text, produced by the parser's own conservative serializer.

format

readonly format: SynthFormat

The format this artifact belongs to.

kind

readonly kind: string

A format-specific kind label (e.g. "ADT^A01").

warnings

readonly warnings: readonly string[]

The warning codes the parser emitted when the artifact was round-tripped (empty = spec-clean).


Corpus

A reproducible, self-describing set of generated artifacts.

Properties

artifacts

readonly artifacts: readonly Artifact[]

The generated artifacts, in generation order.

manifest

readonly manifest: CorpusManifest

The manifest describing what was generated.

seed

readonly seed: number

The seed the corpus was generated from - regenerating from it yields byte-identical artifacts.


CorpusManifest

A self-describing manifest of a Corpus.

Properties

counts

readonly counts: Readonly<Record<string, number>>

Per-kind artifact counts (e.g. { "ADT^A01": 3 }).

formats

readonly formats: readonly SynthFormat[]

The formats present in the corpus.

quirks

readonly quirks: readonly string[]

The quirk names applied.


QuirkArtifact

One generated quirk artifact - the off-spec wire text plus the contract it is meant to satisfy.

Properties

content

readonly content: string

The quirked wire text (deterministic in the seed + quirk).

format

readonly format: SynthFormat

The format this artifact belongs to.

intendedWarnings

readonly intendedWarnings: readonly string[]

The exact parser warning code(s) this artifact is meant to round-trip to.

kind

readonly kind: string

The underlying spec-clean message kind the quirk was injected into (e.g. "ORU^R01").

quirk

readonly quirk: string

The quirk recipe applied.


QuirkDescriptor

A public, grounded description of one vendor quirk - the metadata that binds a quirk recipe to a real parser warning code and a publicly-groundable deviation (cited-public, never a private vendor corpus).

Properties

disposition

readonly disposition: QuirkProfileDisposition

How toleratingProfile treats the quirk.

format

readonly format: SynthFormat

The format this quirk applies to.

grounding

readonly grounding: string

The public grounding for this quirk - the spec clause or the parser's public profile that documents the tolerance. Never a private vendor-attributed corpus.

intendedWarnings

readonly intendedWarnings: readonly string[]

The exact parser warning code(s) a bare parse (no profile) surfaces for this quirk - the intended-warning contract. A quirk that produces any other code, or none, is a generation bug.

name

readonly name: string

The quirk recipe name (e.g. "unknown-zsegment"). Stable; part of the public contract.

toleratingProfile?

readonly optional toleratingProfile?: string

The parser profile that tolerates this quirk (when a built-in public one exists).


QuirkProfiledVerdict

The verdict of a bare parse under the tolerating profile, if any.

Properties

disposition

readonly disposition: QuirkProfileDisposition

How the profile treats the quirk.

profileName

readonly profileName: string

The profile applied.

tolerated

readonly tolerated: boolean

true iff the profile handled the quirk as its disposition declares: "suppressed" ⇒ the intended code is gone; "rebadged" ⇒ the intended code is gone and PROFILE_QUIRK_APPLIED is present.

warnings

readonly warnings: readonly string[]

The warning codes the parser emitted with the profile active.


QuirkRoundTripResult

The verdict of round-tripping a quirk artifact through its parser.

Properties

content

readonly content: string

The quirked wire text that was parsed.

intendedWarningHeld

readonly intendedWarningHeld: boolean

true iff the bare parse produced exactly the intended code(s) - the intended-warning contract.

intendedWarnings

readonly intendedWarnings: readonly string[]

The exact code(s) the quirk is meant to produce.

warnings

readonly warnings: readonly string[]

The warning codes a bare parse (no profile) emitted.

withProfile?

readonly optional withProfile?: QuirkProfiledVerdict

The verdict under the tolerating profile, when a built-in public one exists.


Rng

A seeded, deterministic random source. Created via createRng; passed explicitly to every provider. All draw methods advance the internal state deterministically.

Properties

seed

readonly seed: number

The integer seed this generator was created from (part of the Corpus manifest).

Methods

bool()

bool(p?): boolean

true with probability p (default 0.5).

Parameters
p?

number

Returns

boolean

digits()

digits(n): string

A string of n decimal digits (09), each drawn uniformly.

Parameters
n

number

The number of digits (>= 0).

Returns

string

float()

float(): number

The next float in [0, 1).

Returns

number

int()

int(min, max): number

A uniformly-distributed integer in the inclusive range [min, max].

Parameters
min

number

Inclusive lower bound (integer).

max

number

Inclusive upper bound (integer, >= min).

Returns

number

nextUint32()

nextUint32(): number

The next unsigned 32-bit integer.

Returns

number

pick()

pick<T>(items): T

Pick one element from a non-empty array.

Type Parameters
T

T

Parameters
items

readonly T[]

A non-empty readonly array.

Returns

T


Sfc32State

The mutable four-word sfc32 state. Threaded explicitly (never global) by ../rng/rng.Rng.

Properties

a

a: number

State word a.

b

b: number

State word b.

c

c: number

State word c.

d

d: number

Counter word d.


SyntheticAddress

A synthetic postal address - synthetic street + city, a fixed non-real ZIP.

Properties

city

readonly city: string

A clearly-fake city.

state

readonly state: string

A US state abbreviation (structural only; never combined with a real street + name + DOB).

street

readonly street: string

A clearly-fake street line.

zip

readonly zip: string

A reserved non-real ZIP (00000).


SyntheticIdentifier

A synthetic identifier scoped to the synthetic assigning authority.

Properties

assigningAuthority

readonly assigningAuthority: string

The synthetic assigning-authority namespace id.

assigningAuthorityOid

readonly assigningAuthorityOid: string

The synthetic assigning-authority OID (HL7 example arc).

typeCode

readonly typeCode: "MR" | "AN" | "MB"

HL7 identifier type code (MR = medical record, AN = account, MB = member).

value

readonly value: string

The identifier value (digits) - unique only within the synthetic namespace.


SyntheticName

A synthetic person name drawn from the shipped fake-name pool.

Properties

family

readonly family: string

A clearly-fake family name.

given

readonly given: string

A clearly-fake given name.


SynthProfile

A frozen, validated fixture recipe produced by defineSynthProfile.

Properties

familyNames?

readonly optional familyNames?: readonly string[]

The family-name pool this profile draws from.

givenNames?

readonly optional givenNames?: readonly string[]

The given-name pool this profile draws from (overrides or the built-in default).

name

readonly name: string

The profile name.

quirks

readonly quirks: readonly string[]

The requested quirk recipe names.


SynthProfileSpec

The user-authored spec passed to defineSynthProfile.

Properties

familyNames?

readonly optional familyNames?: readonly string[]

Optional family-name pool override (clearly-synthetic names only).

givenNames?

readonly optional givenNames?: readonly string[]

Optional given-name pool override (clearly-synthetic names only - see the safety invariant).

name

readonly name: string

A stable, human-readable profile name (e.g. "acme-hospital"). Required, non-empty.

quirks?

readonly optional quirks?: readonly string[]

The vendor quirk recipe names this profile requests. Validated against the target format's quirk registry when the profile drives a quirk corpus (an unsupported quirk is a fatal SYNTH_UNSUPPORTED_QUIRK, never a silent no-op).

Type Aliases

QuirkProfileDisposition

QuirkProfileDisposition = "suppressed" | "rebadged" | "bare"

How the parser's matching profile treats a quirk once it is active - the three shapes the parsers' profile systems actually exhibit (verified firsthand against each parser):

  • "suppressed" - the profile makes the warning disappear (HL7 v2: a defineProfile customSegments claim suppresses UNKNOWN_SEGMENT for a declared Z-segment).
  • "rebadged" - the profile downgrades the warning to the value-free PROFILE_QUIRK_APPLIED marker with expected: true (C-CDA defineCcdaProfile / ASTM defineAstmProfile profileQuirkApplied).
  • "bare" - no shipped profile tolerates it; the quirk targets a real coded leniency a consumer can tolerate via their own defineProfile/defineAstmProfile, but no built-in re-badges it.

SsnBlock

SsnBlock = "never-issued" | "advertising"

Which SSN reserved space to draw from.


SynthFatalCode

SynthFatalCode = typeof SYNTH_FATAL_CODES[keyof typeof SYNTH_FATAL_CODES]

A value from SYNTH_FATAL_CODES - the type carried by a thrown SynthError.


SynthFormat

SynthFormat = "hl7v2" | "fhir" | "ccda" | "x12" | "ncpdp" | "astm"

The format an artifact was generated for.

Variables

DEA_REGISTRANT_TYPES

const DEA_REGISTRANT_TYPES: readonly string[]

The DEA-registration prefix letters @cosyte/synth draws a synthetic DEA number's first character from. A real DEA number is <registrant-type><last-name-initial> + 7 digits; the first letter is the registrant type (A/B/F/G/M/P/R/X are the widely-published values; the second letter is the registrant's last-name initial). These letters are a fact about the number's shape, not copyrighted prose - they only shape the value; the synthetic guarantee is the deliberately-invalid checksum (see dea / isSyntheticDea).


DOC_V6_PREFIX

const DOC_V6_PREFIX: "2001:db8" = "2001:db8"

RFC 3849 IPv6 documentation prefix.


NPI_LUHN_PREFIX

const NPI_LUHN_PREFIX: "80840" = "80840"

The 80840 prefix prepended to a 10-digit NPI before the Luhn check (the CMS NPI check-digit rule - 80840 is the ISO 7812 issuer identifier for the US health-application namespace). A real NPI satisfies luhn("80840" + npi) ≡ 0 (mod 10).


PROFILE_QUIRK_APPLIED

const PROFILE_QUIRK_APPLIED: "PROFILE_QUIRK_APPLIED" = "PROFILE_QUIRK_APPLIED"

The stable, value-free re-badge code the C-CDA and ASTM parsers emit when a profile tolerates a quirk. HL7 v2 has no equivalent (it suppresses instead - see QuirkProfileDisposition).


RESERVED_EMAIL_DOMAINS

const RESERVED_EMAIL_DOMAINS: readonly string[]

RFC 2606 / 6761 reserved email domains @cosyte/synth draws from.


safe

const safe: Readonly<{ address: (rng) => SyntheticAddress; dateYmd: (rng, minYear, maxYear) => string; dea: (rng, person?) => string; email: (rng, person?) => string; identifier: (rng, typeCode) => SyntheticIdentifier; ipv4: (rng) => string; ipv6: (rng) => string; name: (rng) => SyntheticName; npi: (rng) => string; phone: (rng) => string; ssn: (rng, block) => string; uuid: (rng) => string; }>

The synthetic-safety provider namespace. Every function draws only from a reserved range or the shipped fake-name pool - no value it returns can be real or plausibly-real PHI.

Example

import { createRng, safe } from "@cosyte/synth";
const rng = createRng(42);
safe.ssn(rng); // never-issued SSN
safe.phone(rng); // reserved 555-01NN number

SYNTH_FATAL_CODES

const SYNTH_FATAL_CODES: object

The stable fatal code registry. Additions-only thereafter.

Type Declaration

SYNTH_UNSUPPORTED_FORMAT

readonly SYNTH_UNSUPPORTED_FORMAT: "SYNTH_UNSUPPORTED_FORMAT" = "SYNTH_UNSUPPORTED_FORMAT"

A format was requested that this build cannot generate through a real parser builder/serializer (e.g. ASTM before @cosyte/astm's serializer ships). Fatal - never a hand-written byte fallback.

SYNTH_UNSUPPORTED_QUIRK

readonly SYNTH_UNSUPPORTED_QUIRK: "SYNTH_UNSUPPORTED_QUIRK" = "SYNTH_UNSUPPORTED_QUIRK"

A vendor quirk was requested that the target format's profile system does not support. Fatal - never a silent no-op and never a fabricated quirk.

Example

import { SYNTH_FATAL_CODES, SynthError } from "@cosyte/synth";
try {
// ...generate...
} catch (err) {
if (err instanceof SynthError && err.code === SYNTH_FATAL_CODES.SYNTH_UNSUPPORTED_FORMAT) {
// handle an unsupported format request
}
}

SYNTHETIC_ASSIGNING_AUTHORITY

const SYNTHETIC_ASSIGNING_AUTHORITY: Readonly<{ namespaceId: "COSYTE-SYNTH"; universalId: "2.16.840.1.113883.19.999"; universalIdType: "ISO"; }>

The synthetic assigning authority @cosyte/synth mints MRNs / account / member identifiers under. There is no reserved MRN range (an MRN is unique only within its assigning-authority / OID namespace), so - a documented design decision - every synthetic identifier is scoped to a namespace that clearly cannot be a real facility's: a SYNTH-labelled authority whose OID lives under HL7's designated example root 2.16.840.1.113883.19. A value under this AA can never collide with a real record because the namespace itself is synthetic.


SYNTHETIC_CITY_NAMES

const SYNTHETIC_CITY_NAMES: readonly string[]

Obviously-synthetic city names. Combined only ever with a synthetic street + a fake name (the combination is what identifies, and the combination is always synthetic).


SYNTHETIC_FAMILY_NAMES

const SYNTHETIC_FAMILY_NAMES: readonly string[]

Obviously-synthetic family names. None is a plausible real surname at a real address.


SYNTHETIC_GIVEN_NAMES

const SYNTHETIC_GIVEN_NAMES: readonly string[]

Obviously-synthetic given names. None is a plausible real person's name.


SYNTHETIC_STREET_NAMES

const SYNTHETIC_STREET_NAMES: readonly string[]

Obviously-synthetic street names for structured address fields.


TEST_NET_V4_PREFIXES

const TEST_NET_V4_PREFIXES: readonly string[]

RFC 5737 IPv4 documentation (TEST-NET) /24 network prefixes.


VERSION

const VERSION: "0.0.0" = "0.0.0"

Library version string, synced with package.json#version by downstream release tooling.

Example

import { VERSION } from "@cosyte/synth";
console.log(VERSION);

Functions

address()

address(rng): SyntheticAddress

A synthetic address - a fake street + city, a reserved non-real ZIP (00000). A real state abbreviation may appear (structural only) but is never combined with a real street + name + DOB.

Parameters

rng

Rng

The seeded generator.

Returns

SyntheticAddress

A SyntheticAddress.

Example

import { createRng, address } from "@cosyte/synth";
address(createRng(1)); // { street, city, state, zip: "00000" }

assertIntendedWarnings()

assertIntendedWarnings(quirk, intendedWarnings, bareWarnings): void

Assert a freshly-generated quirk artifact actually round-trips to its intended warning(s), or fail closed. This is the generator's self-check on the intended-warning contract: a fixture whose bare parse does not produce exactly the declared code(s) is a mislabeled fixture - a golden file that lies about the parser verdict it anchors - and must never be emitted. It is a stronger guard than "the transform changed some bytes": a transform can mutate the wrong element (a template a given document type does not key its warning on) and still change bytes while producing no warning. Every format's generate*Quirk calls this after transforming, so the contract is enforced at generation time, not merely at round-trip time.

Parameters

quirk

string

The quirk name (for the error message).

intendedWarnings

readonly string[]

The declared intended code(s).

bareWarnings

readonly string[]

The code(s) a bare parse of the generated artifact actually produced.

Returns

void

Throws

Error when the bare parse did not produce exactly the intended code(s).

Example

import { assertIntendedWarnings } from "@cosyte/synth";
assertIntendedWarnings("unknown-zsegment", ["UNKNOWN_SEGMENT"], ["UNKNOWN_SEGMENT"]); // ok

createRng()

createRng(seed): Rng

Create a seeded, deterministic Rng. The same seed yields the same sequence everywhere.

Parameters

seed

number

The integer seed. Coerced to a 32-bit integer.

Returns

Rng

A fresh, independent Rng.

Example

import { createRng } from "@cosyte/synth";
const rng = createRng(12345);
rng.int(1, 6); // deterministic for seed 12345

dateYmd()

dateYmd(rng, minYear?, maxYear?): string

A synthetic date in HL7 YYYYMMDD form, drawn uniformly within an inclusive year range. Comes from the seeded generator (never wall-clock), so it is reproducible and implies no real event.

Parameters

rng

Rng

The seeded generator.

minYear?

number = 1930

Inclusive lower year bound (default 1930).

maxYear?

number = 2010

Inclusive upper year bound (default 2010).

Returns

string

An YYYYMMDD date string (always a valid calendar day).

Example

import { createRng, dateYmd } from "@cosyte/synth";
dateYmd(createRng(1), 1970, 2000); // "YYYYMMDD"

dea()

dea(rng, person?): string

A synthetic DEA number - <registrant-type><initial> + 7 digits with a deliberately-invalid checksum, so it can never be a validly-issued DEA registration (a real DEA number's 7th digit satisfies the published DEA checksum). The first letter is a registrant-type letter, the second is derived from person (its family initial) when supplied so the number reads plausibly; the 6-digit base is seeded and the check digit is set to (correct + 1) mod 10, guaranteeing the value fails validation. NCPDP carries prescriber DEA, and this is the identity locus a refuter attacks hardest, so - like npi - non-collision is a construction-level guarantee, not a heuristic.

Parameters

rng

Rng

The seeded generator.

person?

SyntheticName

Optional name whose family initial becomes the DEA's second letter.

Returns

string

A DEA-shaped string that is provably not a real DEA registration.

Example

import { createRng, dea, isSyntheticDea } from "@cosyte/synth";
isSyntheticDea(dea(createRng(1))); // true - invalid checksum by construction

deaCheckDigit()

deaCheckDigit(base6): number

The correct DEA check digit for a 7-digit numeric base. The published DEA checksum is (d1 + d3 + d5) + 2·(d2 + d4 + d6), whose units digit is the 7th (check) digit. A real DEA number satisfies this; a number whose 7th digit differs cannot be a validly-issued DEA registration.

Parameters

base6

string

The first 6 digits of the DEA number (positions 1–6).

Returns

number

The check digit (09) a real DEA number would carry for this base.

Example

import { deaCheckDigit } from "@cosyte/synth";
deaCheckDigit("123456"); // the units digit of (1+3+5) + 2·(2+4+6)

defineSynthProfile()

defineSynthProfile(spec): SynthProfile

Define a reusable, frozen synthetic-fixture profile.

Parameters

spec

SynthProfileSpec

The profile spec; name is required and non-empty.

Returns

SynthProfile

A deep-frozen SynthProfile.

Throws

TypeError when name is missing or blank.

Example

import { defineSynthProfile } from "@cosyte/synth";
const acme = defineSynthProfile({ name: "acme-hospital", quirks: [] });

email()

email(rng, person?): string

A synthetic email at an RFC 2606 / 6761 reserved domain - <slug>@example.com.

Parameters

rng

Rng

The seeded generator.

person?

SyntheticName

Optional name to derive the local-part slug from; otherwise a random slug is used.

Returns

string

A synthetic email address.

Example

import { createRng, email, name } from "@cosyte/synth";
email(createRng(1), name(createRng(1))); // "<given>.<family>@example.com"

identifier()

identifier(rng, typeCode?): SyntheticIdentifier

A synthetic identifier (MRN / account / member id) scoped to the synthetic assigning authority. There is no reserved MRN range, so non-collision is guaranteed by the namespace, not the value: the identifier lives under a SYNTH authority no real facility uses.

Parameters

rng

Rng

The seeded generator.

typeCode?

"MR" | "AN" | "MB"

The HL7 identifier type: MR (default), AN, or MB.

Returns

SyntheticIdentifier

A SyntheticIdentifier.

Example

import { createRng, identifier } from "@cosyte/synth";
identifier(createRng(1), "MR"); // { value, typeCode: "MR", assigningAuthority: "COSYTE-SYNTH", ... }

ipv4()

ipv4(rng): string

A synthetic IPv4 in an RFC 5737 TEST-NET block - never routable.

Parameters

rng

Rng

The seeded generator.

Returns

string

A TEST-NET IPv4 address string.

Example

import { createRng, ipv4 } from "@cosyte/synth";
ipv4(createRng(1)); // e.g. "192.0.2.NN"

ipv6()

ipv6(rng): string

A synthetic IPv6 in the RFC 3849 documentation prefix 2001:db8::/32 - never routable.

Parameters

rng

Rng

The seeded generator.

Returns

string

A documentation-prefix IPv6 address string.

Example

import { createRng, ipv6 } from "@cosyte/synth";
ipv6(createRng(1)); // e.g. "2001:db8::NNNN"

isSyntheticDea()

isSyntheticDea(value): boolean

Whether a DEA number (XX + 7 digits, case-insensitive) is provably synthetic - its check digit (the 7th digit) does not match the published DEA checksum, so it cannot be a validly-issued DEA registration. A checksum-valid DEA number (which could denote a real prescriber) returns false; a value that is not the DEA shape returns false.

Parameters

value

string

The candidate DEA number (with or without incidental separators).

Returns

boolean

true when the DEA number's checksum is wrong (never a real DEA registration).

Example

import { isSyntheticDea } from "@cosyte/synth";
isSyntheticDea("AF1234561"); // depends on the base - true when the 7th digit is wrong

isSyntheticEmail()

isSyntheticEmail(value): boolean

Whether an email's domain is an RFC 2606 / 6761 reserved / test domain.

Parameters

value

string

The candidate email address.

Returns

boolean

true when the domain is reserved (never real).

Example

import { isSyntheticEmail } from "@cosyte/synth";
isSyntheticEmail("faux.testerson@example.com"); // true

isSyntheticIp()

isSyntheticIp(value): boolean

Whether an IP string is in an RFC 5737 (IPv4 TEST-NET) or RFC 3849 (IPv6 documentation) reserved block. A real routable address returns false.

Parameters

value

string

The candidate IPv4 or IPv6 address.

Returns

boolean

true when the address is a reserved documentation address.

Example

import { isSyntheticIp } from "@cosyte/synth";
isSyntheticIp("192.0.2.44"); // true (TEST-NET-1)
isSyntheticIp("8.8.8.8"); // false (real)

isSyntheticNpi()

isSyntheticNpi(value): boolean

Whether a 10-digit NPI is provably synthetic - i.e. its check digit is invalid, so it cannot be a NPPES-issued NPI. A Luhn-valid 10-digit NPI (which could denote a real registered provider) returns false; a non-10-digit value returns false (not an NPI shape).

Parameters

value

string

The candidate NPI (digits only, or with incidental separators).

Returns

boolean

true when the NPI's check digit is wrong (never a real NPI).

Example

import { isSyntheticNpi } from "@cosyte/synth";
isSyntheticNpi("1234567894"); // true - invalid check digit (valid would be 1234567893)
isSyntheticNpi("1234567893"); // false - Luhn-valid, could be a real NPI

isSyntheticPhone()

isSyntheticPhone(value): boolean

Whether a phone string contains the NANP 555-0100…555-0199 reserved fictional line range.

Parameters

value

string

The candidate phone (any formatting).

Returns

boolean

true when the number is in the reserved fictional block.

Example

import { isSyntheticPhone } from "@cosyte/synth";
isSyntheticPhone("(202) 555-0142"); // true

isSyntheticSsn()

isSyntheticSsn(value): boolean

Whether a ddd-dd-dddd (or ddddddddd) SSN string is drawn from an SSA never-issued / reserved space - area 000, 666, or 900–999. A real, issuable SSN returns false.

Parameters

value

string

The candidate SSN (dashes optional).

Returns

boolean

true when the SSN is provably synthetic.

Example

import { isSyntheticSsn } from "@cosyte/synth";
isSyntheticSsn("900-12-3456"); // true (never issued)
isSyntheticSsn("123456789"); // false (issuable area 123)

luhnMod10()

luhnMod10(digits): number

Internal

The Luhn sum (mod 10) of a numeric string, doubling every second digit from the right. Used to verify (or deliberately break) an NPI check digit.

Parameters

digits

string

A string of decimal digits.

Returns

number

The Luhn sum modulo 10 (0 ⇒ the string passes the Luhn check).


makeCorpus()

makeCorpus(seed, artifacts, quirks?): Corpus

Assemble a deep-frozen Corpus from a seed and its artifacts, deriving the manifest.

Parameters

seed

number

The seed the artifacts were generated from.

artifacts

readonly Artifact[]

The generated artifacts, in order.

quirks?

readonly string[] = []

The quirk names applied (default none).

Returns

Corpus

A deep-frozen, self-describing Corpus.

Example

import { makeCorpus } from "@cosyte/synth";
const corpus = makeCorpus(1, [{ format: "hl7v2", kind: "ADT^A01", content, warnings: [] }]);
corpus.manifest.counts["ADT^A01"]; // 1

name()

name(rng): SyntheticName

A synthetic name drawn from the shipped clearly-fake pool.

Parameters

rng

Rng

The seeded generator.

Returns

SyntheticName

A SyntheticName.

Example

import { createRng, name } from "@cosyte/synth";
const { given, family } = name(createRng(1));

npi()

npi(rng): string

A synthetic NPI - a 10-digit National Provider Identifier with a deliberately-invalid Luhn check digit, so it can never be a NPPES-issued NPI (a real NPI must satisfy the 80840-prefixed Luhn check). The 9-digit base is drawn from the seeded generator; the check digit is set to (correct + 1) mod 10, guaranteeing the full value fails validation.

Parameters

rng

Rng

The seeded generator.

Returns

string

A 10-digit NPI-shaped string that is provably not a real NPI.

Example

import { createRng, npi, isSyntheticNpi } from "@cosyte/synth";
isSyntheticNpi(npi(createRng(1))); // true - invalid check digit by construction

npiCheckDigit()

npiCheckDigit(base9): number

The correct NPI check digit for a 9-digit base - the value that makes 80840 + base + check pass the Luhn check.

Parameters

base9

string

The 9-digit NPI base (positions 1–9).

Returns

number

The check digit (09) a real NPI would carry for this base.

Example

import { npiCheckDigit } from "@cosyte/synth";
npiCheckDigit("123456789"); // 3 - so 1234567893 is a Luhn-valid NPI shape

phone()

phone(rng): string

A synthetic phone in the NANP reserved fictional block - (AAA) 555-01NN. The reserved guarantee is the 555-01NN tail (exchange 555, line 0100–0199); the area code is any NANP-valid NXX. Can never be a working number.

Parameters

rng

Rng

The seeded generator.

Returns

string

A formatted synthetic phone string.

Example

import { createRng, phone } from "@cosyte/synth";
phone(createRng(1)); // e.g. "(2XX) 555-01NN"

profileTolerated()

profileTolerated(disposition, intendedWarnings, warningsUnderProfile): boolean

Evaluate whether a profiled parse tolerated a quirk as its disposition declares. Shared across the formats so the "suppressed vs re-badged" logic lives in exactly one place.

Parameters

disposition

QuirkProfileDisposition

The quirk's declared profile disposition.

intendedWarnings

readonly string[]

The bare-parse intended code(s).

warningsUnderProfile

readonly string[]

The code(s) the parser emitted with the profile active.

Returns

boolean

true iff the profile handled the quirk correctly for its disposition.

Example

import { profileTolerated } from "@cosyte/synth";
profileTolerated("suppressed", ["UNKNOWN_SEGMENT"], []); // true - the profile suppressed it

resolveQuirk()

resolveQuirk(registry, format, name): QuirkDescriptor

Resolve a requested quirk name against a format's registry, or fail closed. A quirk the format's profile system does not support is a fatal SYNTH_UNSUPPORTED_QUIRK - never a silent no-op and never a fabricated quirk with a made-up warning.

Parameters

registry

Readonly<Record<string, QuirkDescriptor>>

The format's quirk descriptors, keyed by name.

format

SynthFormat

The format being generated (for the error message).

name

string

The requested quirk name.

Returns

QuirkDescriptor

The matching QuirkDescriptor.

Throws

SynthError with code SYNTH_UNSUPPORTED_QUIRK when name is not a supported quirk.

Example

import { resolveQuirk } from "@cosyte/synth";
import { HL7_QUIRKS } from "@cosyte/synth/hl7";
resolveQuirk(HL7_QUIRKS, "hl7v2", "unknown-zsegment").intendedWarnings; // ["UNKNOWN_SEGMENT"]

sameCodeSet()

sameCodeSet(a, b): boolean

Exact multiset (order-independent) equality of two code lists - the intended-warning comparison.

Parameters

a

readonly string[]

The first code list.

b

readonly string[]

The second code list.

Returns

boolean

true iff the two lists contain the same codes with the same multiplicities.

Example

import { sameCodeSet } from "@cosyte/synth";
sameCodeSet(["A", "B"], ["B", "A"]); // true

sfc32Next()

sfc32Next(s): number

Advance an Sfc32State in place by one step and return the next unsigned 32-bit integer.

This is the canonical sfc32 step. The state object is mutated (the counter d increments and the mixing words rotate); callers that need reproducible independence hold their own state and never share it - ../rng/rng.Rng creates a fresh state per seed so two runs from the same seed are identical.

Parameters

s

Sfc32State

The state to advance. Mutated in place.

Returns

number

The next uint32 in the stream.

Example

import { sfc32Next, type Sfc32State } from "@cosyte/synth";
const s: Sfc32State = { a: 1, b: 2, c: 3, d: 4 };
const x = sfc32Next(s); // uint32

splitmix32()

splitmix32(seed): () => number

A stateful splitmix32 step function. Each call advances the internal 32-bit state and returns the next unsigned 32-bit integer. Deterministic for a given seed.

Parameters

seed

number

The 32-bit seed. Coerced to a 32-bit integer via | 0.

Returns

A nullary function returning the next uint32 in the stream.

() => number

Example

import { splitmix32 } from "@cosyte/synth";
const next = splitmix32(12345);
const a = next(); // deterministic uint32

ssn()

ssn(rng, block?): string

A synthetic SSN - dashed AAA-GG-SSSS. Default draws the SSA never-issued area space (900–999); block: "advertising" draws SSA's reserved advertising block (987-65-4320…4329). A value from this function can never be a real SSN.

Parameters

rng

Rng

The seeded generator.

block?

SsnBlock = "never-issued"

Which reserved space to draw from. Defaults to "never-issued".

Returns

string

A dashed synthetic SSN string.

Example

import { createRng, ssn } from "@cosyte/synth";
ssn(createRng(1)); // e.g. a 900-area, never-issued SSN

uuid()

uuid(rng): string

A deterministic UUIDv4-shaped surrogate key from the seeded generator. Because it is seeded (not from node:crypto, which is not reproducible), the cryptographic non-collision argument is weaker - acceptable because the identifier namespace is synthetic anyway, and noted honestly.

Parameters

rng

Rng

The seeded generator.

Returns

string

A canonical 8-4-4-4-12 lowercase-hex UUID string with version 4 and RFC 4122 variant.

Example

import { createRng, uuid } from "@cosyte/synth";
uuid(createRng(1)); // "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"

validateProfileQuirks()

validateProfileQuirks(profile, registry, format): readonly string[]

Validate the quirk names carried by a SynthProfile against a format's registry, failing closed on the first unsupported one. Lets a consumer author a fixture recipe with defineSynthProfile and have its quirks checked against the parser's real tolerance before any fixture is generated.

Parameters

profile

SynthProfile

The synth profile whose quirks to validate.

registry

Readonly<Record<string, QuirkDescriptor>>

The format's quirk descriptors.

format

SynthFormat

The format being generated.

Returns

readonly string[]

The validated quirk names (the profile's, in order).

Throws

SynthError SYNTH_UNSUPPORTED_QUIRK for the first unsupported quirk.

Example

import { validateProfileQuirks, defineSynthProfile } from "@cosyte/synth";
import { HL7_QUIRKS } from "@cosyte/synth/hl7";
const p = defineSynthProfile({ name: "site", quirks: ["unknown-zsegment"] });
validateProfileQuirks(p, HL7_QUIRKS, "hl7v2"); // ["unknown-zsegment"]