diff --git a/.changeset/quickstart-page-matches-readme.md b/.changeset/quickstart-page-matches-readme.md new file mode 100644 index 0000000..c861013 --- /dev/null +++ b/.changeset/quickstart-page-matches-readme.md @@ -0,0 +1,7 @@ +--- +"@cosyte/synth": patch +--- + +The Quickstart page now opens with the same program as the README: an HL7 v2 message generated from a seed and round-tripped through `@cosyte/hl7`, plus a mixed corpus. + +Before this, the page opened with the seeded value providers while the README opened with the HL7 v2 generators, so a reader arriving from npm and one arriving from the documentation site met two different first programs. The value-provider, round-trip, corpus and determinism examples follow it. The test suite now fails if the page's first program differs from the README's quickstart, and runs it against the built package. Documentation and test change only. diff --git a/docs-content/quickstart.md b/docs-content/quickstart.md index ffbcd20..512793a 100644 --- a/docs-content/quickstart.md +++ b/docs-content/quickstart.md @@ -9,6 +9,36 @@ sidebar_position: 1 Generate reproducible, synthetic-by-construction fixtures in a few lines. A **seed** determines the output entirely, so a fixture set regenerates identically anywhere. +## Generate a spec-clean HL7 v2 message + +This is the program the README opens with. The `@cosyte/synth/hl7` subpath builds each message +**through `@cosyte/hl7`'s own `buildMessage`**, so it is spec-clean by construction, and `roundTrip` +feeds it straight back to the parser. `@cosyte/hl7` is the optional peer this subpath needs; see +[Installation](./installation.md): + +```ts runnable +import { generateAdt, generateOru, generateHl7, hl7Corpus, roundTrip } from "@cosyte/synth/hl7"; + +// Same seed → byte-identical message, on any machine, any run. +const adt = generateAdt({ seed: 12345, trigger: "A01" }); +const oru = generateOru({ seed: 12345 }); + +// Spec-clean by construction: it round-trips through @cosyte/hl7 with zero warnings. +console.log(roundTrip(adt).specClean); // true +console.log(roundTrip(oru).specClean); // true + +// Or generate a reproducible mixed corpus across every family: +const corpus = hl7Corpus({ seed: 42, count: 7 }); // one of each family, cycled +console.log(corpus.artifacts.every((a) => a.warnings.length === 0)); // true, all spec-clean + +// Dispatch by kind when the message type is data: +console.log(roundTrip(generateHl7("VXU^V04", 12345)).content.split("\r")[0]); // its MSH segment +``` + +The first three lines print `true`, and the last prints the `MSH` segment of a generated `VXU^V04`. +Every identifier, name and date in these messages is drawn from a reserved range or the shipped +fake-name pool. + ## Draw a synthetic value The `safe` providers each draw from a reserved, never-collide source. Everything is a pure function of @@ -24,10 +54,10 @@ const nationalId = safe.ssn(rng); isSyntheticSsn(nationalId); // => true ``` -## Generate an HL7 v2 message +## Check the round trip yourself -The `@cosyte/synth/hl7` subpath builds messages **through `@cosyte/hl7`**, so they are spec-clean by -construction and round-trip with zero warnings: +`roundTrip` returns more than the verdict: the warnings the parser raised on the way back, which for +a spec-clean artifact is none at all: ```ts runnable import { generateAdt, roundTrip } from "@cosyte/synth/hl7"; diff --git a/test/docs-content.test.ts b/test/docs-content.test.ts index c4955d6..c3df914 100644 --- a/test/docs-content.test.ts +++ b/test/docs-content.test.ts @@ -1,9 +1,10 @@ import { execFileSync } from "node:child_process"; +import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { beforeAll } from "vitest"; +import { beforeAll, describe, expect, it } from "vitest"; -import { docSnippetSuite } from "@cosyte/vitest-config/snippets"; +import { docSnippetSuite, extractRunnableSnippets } from "@cosyte/vitest-config/snippets"; /** * Doc/code-agreement gate. Every ```` ```ts runnable ```` block in `docs-content/` is extracted, @@ -44,3 +45,23 @@ docSnippetSuite({ return undefined; }, }); + +/** + * The README's quickstart (the first TypeScript block after `## Install`) is what a reader runs from + * npm, and the Quickstart page is what the same reader runs from the docs site. Two first programs + * that disagree send a reader two ways at once, so the page must open with the README's program, + * byte for byte, and the sweep above is what executes it. + */ +describe("the Quickstart page opens with the README's quickstart program", () => { + it("carries the README's first TypeScript block after ## Install as its first runnable block", () => { + const readme = readFileSync(join(root, "README.md"), "utf8"); + const afterInstall = readme.slice(readme.indexOf("\n## Install\n")); + const readmeFirst = /\n```ts\n([\s\S]*?)\n```\n/.exec(afterInstall)?.[1]; + const quickstart = readFileSync(join(root, "docs-content", "quickstart.md"), "utf8"); + const pageFirst = extractRunnableSnippets(quickstart)[0]?.code; + + expect(readme.includes("\n## Install\n")).toBe(true); + expect(readmeFirst).toBeDefined(); + expect(pageFirst).toBe(readmeFirst); + }); +});