fhir-data-utils-ts 0.2.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.
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: govern-fhir-observation-components
3
+ description: Implement or audit neutral FHIR Observation claim catalogs, vital-sign terminology, parent-owned component graphs, reduced component indexes, historical array and bp aliases, and R4/R5 projection adapters. Use when changing Observation.component, blood pressure, assessment scales, device readings, flat claims, portal or assistant vital-sign builders, or migrating code from gdc-common-utils-ts or uhc-fhir-utils-typescript.
4
+ ---
5
+
6
+ # Govern FHIR Observation Components
7
+
8
+ ## Establish the boundary
9
+
10
+ 1. Treat native FHIR import/export as an adapter boundary. Keep flat claims as
11
+ the version-neutral internal contract.
12
+ 2. Read `src/observation-claims.ts`, `src/vital-sign-observations.ts`, their
13
+ tests, and `docs/UHC_FHIR_UTILS_MIGRATION.md` before changing behavior.
14
+ 3. Treat every `gdc-*` repository and `uhc-fhir-utils-typescript` as read-only
15
+ compatibility evidence unless the user explicitly changes that scope.
16
+ 4. Keep product policies in SOS, Vet or UHC packages. This package owns only
17
+ neutral FHIR data semantics.
18
+
19
+ ## Preserve the component contract
20
+
21
+ - Model blood pressure as one native FHIR Observation with systolic and
22
+ diastolic `component[]` entries. Do not export those components as three
23
+ independent native Observations.
24
+ - Build a flat resource graph. The primary Observation owns comma-separated
25
+ `Observation.has-member` references to reduced Observation entries.
26
+ - Give every reduced member its graph identifier, subject, category, date,
27
+ generic code, numeric value and unit. Omit `Observation.status` so normal
28
+ top-level listings ignore it while ordinary Observation indexes can query it.
29
+ - Never use `is-contained`, `contained-parent-reference` or
30
+ `contained-reference-list` for Observation components.
31
+ - Accept historical JSON arrays and CSV only through
32
+ `decodeObservationClaimList`; never emit either from a current writer.
33
+ - Never introduce scale-specific numeric claims. In particular,
34
+ `Observation.bp-systolic-number` and `Observation.bp-diastolic-number` are
35
+ read-only migration aliases. Apply the same rule to APGAR, Glasgow and other
36
+ composite observations.
37
+ - Reconstruct native `Observation.component[]` only at the explicit R4/R5
38
+ adapter boundary by resolving the primary entry's governed member graph.
39
+
40
+ ## Reuse governed names
41
+
42
+ - Import every Observation claim key from `ObservationClaim`.
43
+ - Import terminology from `ObservationCategoryCodes`, `VitalSignsCodes` and
44
+ `VitalSignsUnits`.
45
+ - Let `buildVitalSignObservation` author the primary/member graph, canonical
46
+ displays and optional `Observation.user-selected` flag; consumers submit its
47
+ entries and must not rebuild those fields or relationships.
48
+ - Add a missing neutral constant here once and consume it downstream. Do not
49
+ copy `Observation.*`, LOINC, UCUM or category strings into portals,
50
+ assistants, tests or snippets.
51
+ - Keep displays separate from `system|code` tokens and local-language text.
52
+
53
+ ## Change with TDD
54
+
55
+ 1. Begin every new or modified test file with:
56
+ `// Flow contract: reuse shared test fixtures and canonical types; do not introduce duplicated literals.`
57
+ 2. Add the smallest failing test and retain its intended red result.
58
+ 3. Prove parent-owned `has-member`, independent component indexing, legacy
59
+ array reading, absence of `bp-*` and containment output, and a single native
60
+ Observation with two components in R4 and R5 projection.
61
+ 4. Add negative assertions against raw claim literals outside the owning
62
+ catalog and against child-owned containment markers.
63
+ 5. Run Node 24, `npm ci`, `npm run check` and `npm pack --dry-run`.
64
+
65
+ ## Promote consumers
66
+
67
+ Release bottom-up. Publish this package first, install its exact registry
68
+ version in the lowest consumer, and run that consumer's complete local matrix.
69
+ Never commit `file:`, Git, workspace or tarball dependencies. Portals and
70
+ assistants should call shared builders or use governed constants; they submit
71
+ the returned entries and must not assemble component arrays or graph links.
72
+
73
+ Update JSDoc, high-level docs, tests, snippets and this skill together whenever
74
+ the contract changes.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Govern FHIR Observation Components"
3
+ short_description: "Preserve canonical Observation component and index contracts"
4
+ default_prompt: "Use $govern-fhir-observation-components to implement or audit FHIR Observation flat claims, component indexes, vital signs, or R4/R5 projections without custom per-scale claims."
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: preserve-fhir-flat-coding-review
3
+ description: Preserve the neutral flat-claim contract for imported local clinical text, terminology proposals and confirmed codings. Use when changing DataConv, Vet or UHC ingestion, ResearchSubject batches, contained clinical resources, coding-review UI/API contracts, examples, fixtures or tests.
4
+ ---
5
+
6
+ # Preserve FHIR Flat Coding Review
7
+
8
+ Keep imported local text, proposed terminology and confirmed coding as distinct
9
+ states. Reuse the public builders and fixtures from `fhir-data-utils-ts`; do not
10
+ redeclare governed claim paths, code systems, UUIDs or example values.
11
+
12
+ ## Canonical contract
13
+
14
+ - Store source wording as the resource's canonical code-like local-text claim,
15
+ such as `<ResourceType>.<code-field>-text`. Never serialize `coding-input:*`.
16
+ - Put `meta.codingProposals[]` on the same contained clinical resource as
17
+ `meta.claims`. Both are siblings. Never aggregate proposals into
18
+ `ResearchSubject.meta`.
19
+ - Each proposal `field` names the coded target claim. Confirmation writes
20
+ `system|code` to that target and the terminology display to `-display`; it
21
+ must preserve the imported `-text` value.
22
+ - A localized generated label may use a language-qualified `-text_<BCP47>`
23
+ companion. It must not overwrite the imported local text before explicit
24
+ review.
25
+ - In a DIDComm-style batch, traverse
26
+ `body.data[].resource.contained[].meta.codingProposals[]`. In a native FHIR
27
+ Bundle projection, traverse `entry[].resource.contained[]`. Never invent
28
+ `entry[].data[]`.
29
+ - Multiple imported rows may resolve to one stable UUID ResearchSubject. Keep
30
+ every proposal on the clinical resource generated from its source row.
31
+
32
+ ## Workflow
33
+
34
+ 1. Import `deriveCodingLocalTextClaim`, `deriveCodingDisplayClaim`,
35
+ `buildFlatClaimCodingReviewResource`, `applyFlatClaimCodingSelection` and
36
+ `CODING_REVIEW_EXAMPLES` from `fhir-data-utils-ts`.
37
+ 2. Write the smallest failing contract test first. Begin its suite with a flow
38
+ contract comment.
39
+ 3. Build the resource with source `-text` plus a resource-scoped proposal.
40
+ 4. Confirm a candidate only through the shared selection helper or an adapter
41
+ proven equivalent by contract tests.
42
+ 5. Test the real DataConv or product boundary, then update JSDoc, SYNC and
43
+ changelog together.
44
+ 6. Use local tarballs for the consumer matrix. Merge validated branches to
45
+ `main`, publish bottom-up, reinstall exact registry versions and only then
46
+ promote to staging.
47
+
48
+ ## Boundaries
49
+
50
+ - This is FHIR-neutral shared data behavior. It belongs in
51
+ `fhir-data-utils-ts`, not SOSChain, Vet, UHC or frozen `gdc-*` packages.
52
+ - DataConv applies the contract during import. Vet and UHC consume the same
53
+ contract and may add sector policy without forking its claim shape.
54
+ - Do not put HTTP clients, gateway policy, product authorization or terminology
55
+ catalogs in this package.
56
+ - Tests and examples import package-owned fixtures/builders. Do not scatter raw
57
+ claim names, code systems, codes, identifiers or UUIDs through consumers.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Preserve FHIR Flat Coding Review"
3
+ short_description: "Keep local text and coding proposals canonical"
4
+ default_prompt: "Use $preserve-fhir-flat-coding-review to implement or audit flat-claim coding review without duplicated literals."
package/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ ## 0.2.4 - 2026-09-21
4
+
5
+ - Restore the canonical composite-Observation resource graph: the primary
6
+ Observation owns `has-member` references to independently indexable reduced
7
+ Observation entries, while R4/R5 projection reconstructs native
8
+ `component[]`.
9
+ - Keep JSON/CSV component arrays, `bp-*` aliases and deprecated child-owned
10
+ containment markers read-only; current writers emit none of them.
11
+
12
+ ## 0.2.3 - 2026-09-21
13
+
14
+ - Preserve the deployed flat-claim component number wire contract by encoding
15
+ aligned numeric values as JSON string arrays while parsing them as numbers
16
+ at typed and native-FHIR boundaries.
17
+
18
+ ## 0.2.2 - 2026-09-21
19
+
20
+ - Make the neutral vital-sign builder own canonical code displays, component
21
+ displays and the optional user-selected provenance claim so portals and
22
+ assistants no longer assemble those Observation fields from literals.
23
+
24
+ ## 0.2.1 - 2026-09-21
25
+
26
+ - Add the repository skill that governs Observation component authoring,
27
+ compatibility, generic indexing, R4/R5 projection and consumer promotion.
28
+
29
+ ## 0.2.0 - 2026-09-21
30
+
31
+ - Consolidate the canonical Observation claim names, vital-sign terminology,
32
+ JSON repeatable-component encoding, generic component index projection and
33
+ explicit R4/R5 projection boundary. Historical component CSV and `bp-*`
34
+ aliases remain read-only compatibility inputs.
35
+ - Record the reuse/rewrite/retire audit of the original
36
+ `uhc-fhir-utils-typescript` architecture without adding its obsolete product,
37
+ transport, SHA-1, DICOM or build dependencies to the neutral package.
38
+ - Add the neutral contained-resource coding-review contract: canonical imported
39
+ `*-text`, resource-scoped `meta.codingProposals[]`, batch traversal, candidate
40
+ confirmation and reusable governed fixtures for DataConv, Vet and UHC.
41
+ - Add channel-neutral FHIR-like vital-sign Observation builders and lossless device-batch normalization while preserving device source without inventing professional attestation.
42
+ - Introduce product-neutral flat-claim resource graphs, parent-owned
43
+ `contained-reference-list`, narrative `language`/`xhtml`, and governed
44
+ SearchParameter definitions.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # fhir-data-utils-ts
2
+
3
+ FHIR-neutral data contracts for flat claims, governed SearchParameter names,
4
+ resource graphs and explicit R4/R5/R6 projection adapters. This package has no
5
+ network clients and no dependency on GDC, SOS, Vet or UHC packages.
6
+
7
+ Release order is local-first: local tarball consumers, complete local matrix,
8
+ merge and push `main`, publish bottom-up, install exact registry versions, then
9
+ staging. Never commit `file:`, Git or workspace dependencies.
10
+
11
+ ## Where this package sits
12
+
13
+ ```text
14
+ fhir-data-utils-ts
15
+ └─ FHIR SearchParameters, flat-claim graphs and R4/R5/R6 projection rules
16
+
17
+ sos-data-utils-ts
18
+ └─ cross-sector clinical catalogs and shared SOS/Vet/UHC data contracts
19
+ ├─ vet-data-utils-ts: animal species and veterinary policy
20
+ └─ uhc-data-utils-ts: human-health policy
21
+
22
+ terminology-api-node-ts reads the neutral FHIR and shared SOS contracts.
23
+ It never depends on Vet or UHC product packages.
24
+ ```
25
+
26
+ FHIR resource graphs use independent entries. A parent links children only
27
+ through `<ResourceType>.contained-reference-list`; children do not carry
28
+ `is-contained` or `contained-parent-reference`. Narrative source is preserved
29
+ as `<ResourceType>.language`, `.narrative-status` and `.xhtml`. Native FHIR is
30
+ materialized only by an explicit R4/R5/R6 import or export adapter.
31
+
32
+ ## Observation components
33
+
34
+ Composite observations use a claims-first resource graph. The primary
35
+ Observation owns `Observation.has-member`; each component is an independent
36
+ reduced Observation entry using the ordinary code and quantity claims and no
37
+ top-level `Observation.status`. This allows every component to be indexed
38
+ without inventing scale-specific claim families. At the explicit FHIR export
39
+ boundary, the graph is projected back to one native Observation with
40
+ `component[]`.
41
+
42
+ Historical JSON/CSV `Observation.component-*` claims and blood-pressure `bp-*`
43
+ aliases are read-only compatibility inputs. Current writers emit none of them.
44
+ Components never use `is-contained`, `contained-parent-reference` or the
45
+ generic contained-resource link.
46
+
47
+ `buildVitalSignObservation` owns the graph, canonical displays and optional
48
+ `Observation.user-selected` provenance flag. Consumers submit its `entries`
49
+ without reconstructing component claims or relationships.
50
+
51
+ See [the UHC FHIR utilities migration](docs/UHC_FHIR_UTILS_MIGRATION.md) for the
52
+ reuse/rewrite/retire inventory and the rule for moving frozen contracts without
53
+ changing any `gdc-*` repository.
54
+
55
+ ## Coding review for imported clinical text
56
+
57
+ DataConv, Vet and UHC share one neutral review contract from
58
+ `fhir-data-utils-ts/coding-review-flat-claims`:
59
+
60
+ - imported wording is the canonical `<ResourceType>.<code-field>-text` claim;
61
+ - `meta.codingProposals[]` is a sibling of `meta.claims` on that same contained
62
+ clinical resource, never on `ResearchSubject.meta`;
63
+ - a proposal targets the coded claim, such as `Condition.code` or
64
+ `Immunization.vaccination-code`;
65
+ - accepting a candidate adds `system|code` and the official `-display`, while
66
+ retaining the imported local `-text` value;
67
+ - language-qualified generated local labels use `-text_<BCP47>` and do not
68
+ replace imported text until review explicitly chooses that operation.
69
+
70
+ The package exports builders, traversal/confirmation helpers and
71
+ `CODING_REVIEW_EXAMPLES`. Tests and consumers reuse them instead of duplicating
72
+ claim strings, terminology identifiers, codes or UUIDs.
@@ -0,0 +1,134 @@
1
+ export type FlatClaimCodingCandidate = Readonly<{
2
+ id: string;
3
+ system: string;
4
+ code: string;
5
+ display: string;
6
+ localText?: string;
7
+ }>;
8
+ export type FlatClaimCodingProposal = Readonly<{
9
+ id: string;
10
+ status: 'proposed' | 'accepted' | 'rejected' | 'deferred';
11
+ field: string;
12
+ inputText: string;
13
+ language: string;
14
+ candidates: readonly FlatClaimCodingCandidate[];
15
+ selectedCandidateId?: string;
16
+ }>;
17
+ export type FlatClaimCodingReviewResource = Readonly<{
18
+ resourceType: string;
19
+ id: string;
20
+ meta: Readonly<{
21
+ claims: Readonly<Record<string, string>>;
22
+ codingProposals?: readonly FlatClaimCodingProposal[];
23
+ }>;
24
+ }>;
25
+ export type ResearchSubjectCodingReviewEntry = Readonly<{
26
+ resource: Readonly<{
27
+ resourceType: 'ResearchSubject';
28
+ id: string;
29
+ meta: Readonly<{
30
+ claims: Readonly<Record<string, string>>;
31
+ codingProposals?: never;
32
+ }>;
33
+ contained: readonly FlatClaimCodingReviewResource[];
34
+ }>;
35
+ }>;
36
+ export type FhirLikeCodingReviewBatch = Readonly<{
37
+ resourceType: 'Bundle';
38
+ type: 'batch';
39
+ data: readonly ResearchSubjectCodingReviewEntry[];
40
+ }>;
41
+ export type ContainedCodingReviewItem = Readonly<{
42
+ researchSubjectId: string;
43
+ resource: FlatClaimCodingReviewResource;
44
+ proposal: FlatClaimCodingProposal;
45
+ }>;
46
+ /** Return the canonical local-text companion for a code-like flat claim. */
47
+ export declare function deriveCodingLocalTextClaim(targetClaim: string): string;
48
+ /** Return the official-display companion for a code-like flat claim. */
49
+ export declare function deriveCodingDisplayClaim(targetClaim: string): string;
50
+ /**
51
+ * Build a reviewable contained resource.
52
+ *
53
+ * Imported wording remains the canonical local `*-text` flat claim. The
54
+ * proposal targets the code-like claim and is a sibling of `meta.claims`; it
55
+ * never becomes a claim and is never copied to ResearchSubject metadata.
56
+ */
57
+ export declare function buildFlatClaimCodingReviewResource(input: Readonly<{
58
+ resourceType: string;
59
+ resourceId: string;
60
+ targetClaim: string;
61
+ localText: string;
62
+ language: string;
63
+ baseClaims: Readonly<Record<string, string>>;
64
+ proposalId: string;
65
+ candidates: readonly FlatClaimCodingCandidate[];
66
+ }>): FlatClaimCodingReviewResource;
67
+ /** Traverse every ResearchSubject entry and return resource-scoped proposals. */
68
+ export declare function collectBatchCodingReviewItems(batch: FhirLikeCodingReviewBatch): readonly ContainedCodingReviewItem[];
69
+ /**
70
+ * Confirm one terminology candidate on the resource that owns the proposal.
71
+ * The coded claim and official display are added while the imported local
72
+ * `*-text` claim remains untouched for provenance and multilingual search.
73
+ */
74
+ export declare function applyFlatClaimCodingSelection(resource: FlatClaimCodingReviewResource, proposalId: string, candidateId: string): FlatClaimCodingReviewResource;
75
+ /** Package-owned reusable fixture; tests and consumer examples must import it. */
76
+ export declare const CODING_REVIEW_EXAMPLES: Readonly<{
77
+ condition: Readonly<{
78
+ input: Readonly<{
79
+ resourceType: "Condition";
80
+ resourceId: "00000000-0000-4000-8000-000000000102";
81
+ targetClaim: "Condition.code";
82
+ localText: "otitis";
83
+ language: "es-ES";
84
+ baseClaims: Readonly<{
85
+ 'Condition.identifier': "00000000-0000-4000-8000-000000000102";
86
+ 'Condition.subject': "ResearchSubject/00000000-0000-4000-8000-000000000101";
87
+ }>;
88
+ proposalId: "condition-code-proposal-example";
89
+ candidates: readonly Readonly<{
90
+ id: "condition-candidate-example";
91
+ system: "http://snomed.info/sct";
92
+ code: "129127001";
93
+ display: "Otitis externa";
94
+ }>[];
95
+ }>;
96
+ resource: Readonly<{
97
+ resourceType: string;
98
+ id: string;
99
+ meta: Readonly<{
100
+ claims: Readonly<Record<string, string>>;
101
+ codingProposals?: readonly FlatClaimCodingProposal[];
102
+ }>;
103
+ }>;
104
+ expectedAcceptedClaims: Readonly<{
105
+ [x: string]: string;
106
+ "Condition.code": "http://snomed.info/sct|129127001";
107
+ }>;
108
+ expectedAcceptedProposalStatus: "accepted";
109
+ }>;
110
+ batch: Readonly<{
111
+ resourceType: "Bundle";
112
+ type: "batch";
113
+ data: readonly Readonly<{
114
+ resource: Readonly<{
115
+ resourceType: "ResearchSubject";
116
+ id: "00000000-0000-4000-8000-000000000101";
117
+ meta: Readonly<{
118
+ claims: Readonly<{
119
+ 'ResearchSubject.identifier': "urn:uuid:00000000-0000-4000-8000-000000000101";
120
+ }>;
121
+ }>;
122
+ contained: readonly Readonly<{
123
+ resourceType: string;
124
+ id: string;
125
+ meta: Readonly<{
126
+ claims: Readonly<Record<string, string>>;
127
+ codingProposals?: readonly FlatClaimCodingProposal[];
128
+ }>;
129
+ }>[];
130
+ }>;
131
+ }>[];
132
+ }>;
133
+ expectedReviewIdentities: readonly (readonly string[])[];
134
+ }>;
@@ -0,0 +1,160 @@
1
+ import { buildFlatClaimResourceEntry } from './flat-claim-resource-graph.js';
2
+ /** Return the canonical local-text companion for a code-like flat claim. */
3
+ export function deriveCodingLocalTextClaim(targetClaim) {
4
+ return `${requiredCodingTarget(targetClaim)}-text`;
5
+ }
6
+ /** Return the official-display companion for a code-like flat claim. */
7
+ export function deriveCodingDisplayClaim(targetClaim) {
8
+ return `${requiredCodingTarget(targetClaim)}-display`;
9
+ }
10
+ /**
11
+ * Build a reviewable contained resource.
12
+ *
13
+ * Imported wording remains the canonical local `*-text` flat claim. The
14
+ * proposal targets the code-like claim and is a sibling of `meta.claims`; it
15
+ * never becomes a claim and is never copied to ResearchSubject metadata.
16
+ */
17
+ export function buildFlatClaimCodingReviewResource(input) {
18
+ const targetClaim = requiredCodingTarget(input.targetClaim);
19
+ const prefix = `${input.resourceType}.`;
20
+ if (!targetClaim.startsWith(prefix))
21
+ throw new TypeError('coding_target_resource_type_mismatch');
22
+ const entry = buildFlatClaimResourceEntry({
23
+ entryId: input.resourceId,
24
+ resourceType: input.resourceType,
25
+ claims: {
26
+ ...input.baseClaims,
27
+ [deriveCodingLocalTextClaim(targetClaim)]: requiredText(input.localText, 'coding_local_text_required'),
28
+ [`${input.resourceType}.language`]: requiredText(input.language, 'coding_language_required'),
29
+ },
30
+ });
31
+ const proposal = Object.freeze({
32
+ id: requiredText(input.proposalId, 'coding_proposal_id_required'),
33
+ status: 'proposed',
34
+ field: targetClaim,
35
+ inputText: input.localText.trim(),
36
+ language: input.language.trim(),
37
+ candidates: Object.freeze(input.candidates.map(candidate => Object.freeze({ ...candidate }))),
38
+ });
39
+ return Object.freeze({
40
+ resourceType: entry.resourceType,
41
+ id: entry.entryId,
42
+ meta: Object.freeze({ claims: entry.claims, codingProposals: Object.freeze([proposal]) }),
43
+ });
44
+ }
45
+ /** Traverse every ResearchSubject entry and return resource-scoped proposals. */
46
+ export function collectBatchCodingReviewItems(batch) {
47
+ const items = [];
48
+ for (const entry of batch.data) {
49
+ for (const resource of entry.resource.contained) {
50
+ for (const proposal of resource.meta.codingProposals ?? []) {
51
+ items.push(Object.freeze({ researchSubjectId: entry.resource.id, resource, proposal }));
52
+ }
53
+ }
54
+ }
55
+ return Object.freeze(items);
56
+ }
57
+ /**
58
+ * Confirm one terminology candidate on the resource that owns the proposal.
59
+ * The coded claim and official display are added while the imported local
60
+ * `*-text` claim remains untouched for provenance and multilingual search.
61
+ */
62
+ export function applyFlatClaimCodingSelection(resource, proposalId, candidateId) {
63
+ const proposals = resource.meta.codingProposals ?? [];
64
+ const proposalIndex = proposals.findIndex(proposal => proposal.id === proposalId);
65
+ if (proposalIndex < 0)
66
+ throw new TypeError('coding_proposal_not_found');
67
+ const proposal = proposals[proposalIndex];
68
+ const candidate = proposal.candidates.find(item => item.id === candidateId);
69
+ if (!candidate)
70
+ throw new TypeError('coding_candidate_not_found');
71
+ const updatedProposal = Object.freeze({
72
+ ...proposal,
73
+ status: 'accepted',
74
+ selectedCandidateId: candidate.id,
75
+ });
76
+ const updatedProposals = proposals.map((item, index) => (index === proposalIndex ? updatedProposal : item));
77
+ return Object.freeze({
78
+ ...resource,
79
+ meta: Object.freeze({
80
+ claims: Object.freeze({
81
+ ...resource.meta.claims,
82
+ [proposal.field]: `${candidate.system}|${candidate.code}`,
83
+ [deriveCodingDisplayClaim(proposal.field)]: candidate.display,
84
+ }),
85
+ codingProposals: Object.freeze(updatedProposals),
86
+ }),
87
+ });
88
+ }
89
+ const RESEARCH_SUBJECT_EXAMPLE_ID = '00000000-0000-4000-8000-000000000101';
90
+ const CONDITION_EXAMPLE_ID = '00000000-0000-4000-8000-000000000102';
91
+ const CONDITION_PROPOSAL_EXAMPLE_ID = 'condition-code-proposal-example';
92
+ const CONDITION_CANDIDATE_EXAMPLE_ID = 'condition-candidate-example';
93
+ const CONDITION_EXAMPLE_INPUT = Object.freeze({
94
+ resourceType: 'Condition',
95
+ resourceId: CONDITION_EXAMPLE_ID,
96
+ targetClaim: 'Condition.code',
97
+ localText: 'otitis',
98
+ language: 'es-ES',
99
+ baseClaims: Object.freeze({
100
+ 'Condition.identifier': CONDITION_EXAMPLE_ID,
101
+ 'Condition.subject': `ResearchSubject/${RESEARCH_SUBJECT_EXAMPLE_ID}`,
102
+ }),
103
+ proposalId: CONDITION_PROPOSAL_EXAMPLE_ID,
104
+ candidates: Object.freeze([
105
+ Object.freeze({
106
+ id: CONDITION_CANDIDATE_EXAMPLE_ID,
107
+ system: 'http://snomed.info/sct',
108
+ code: '129127001',
109
+ display: 'Otitis externa',
110
+ }),
111
+ ]),
112
+ });
113
+ const CONDITION_EXAMPLE_RESOURCE = buildFlatClaimCodingReviewResource(CONDITION_EXAMPLE_INPUT);
114
+ const CONDITION_EXAMPLE_CANDIDATE = CONDITION_EXAMPLE_INPUT.candidates[0];
115
+ const CONDITION_EXPECTED_ACCEPTED_CLAIMS = Object.freeze({
116
+ ...CONDITION_EXAMPLE_RESOURCE.meta.claims,
117
+ [CONDITION_EXAMPLE_INPUT.targetClaim]: `${CONDITION_EXAMPLE_CANDIDATE.system}|${CONDITION_EXAMPLE_CANDIDATE.code}`,
118
+ [deriveCodingDisplayClaim(CONDITION_EXAMPLE_INPUT.targetClaim)]: CONDITION_EXAMPLE_CANDIDATE.display,
119
+ });
120
+ /** Package-owned reusable fixture; tests and consumer examples must import it. */
121
+ export const CODING_REVIEW_EXAMPLES = Object.freeze({
122
+ condition: Object.freeze({
123
+ input: CONDITION_EXAMPLE_INPUT,
124
+ resource: CONDITION_EXAMPLE_RESOURCE,
125
+ expectedAcceptedClaims: CONDITION_EXPECTED_ACCEPTED_CLAIMS,
126
+ expectedAcceptedProposalStatus: 'accepted',
127
+ }),
128
+ batch: Object.freeze({
129
+ resourceType: 'Bundle',
130
+ type: 'batch',
131
+ data: Object.freeze([
132
+ Object.freeze({
133
+ resource: Object.freeze({
134
+ resourceType: 'ResearchSubject',
135
+ id: RESEARCH_SUBJECT_EXAMPLE_ID,
136
+ meta: Object.freeze({
137
+ claims: Object.freeze({ 'ResearchSubject.identifier': `urn:uuid:${RESEARCH_SUBJECT_EXAMPLE_ID}` }),
138
+ }),
139
+ contained: Object.freeze([CONDITION_EXAMPLE_RESOURCE]),
140
+ }),
141
+ }),
142
+ ]),
143
+ }),
144
+ expectedReviewIdentities: Object.freeze([
145
+ Object.freeze([RESEARCH_SUBJECT_EXAMPLE_ID, CONDITION_EXAMPLE_ID, CONDITION_PROPOSAL_EXAMPLE_ID]),
146
+ ]),
147
+ });
148
+ function requiredCodingTarget(value) {
149
+ const target = requiredText(value, 'coding_target_claim_required');
150
+ if (!/^[A-Z][A-Za-z0-9]+\.(?:code|[a-z][a-z0-9-]*-code)$/.test(target)) {
151
+ throw new TypeError('coding_target_claim_invalid');
152
+ }
153
+ return target;
154
+ }
155
+ function requiredText(value, error) {
156
+ const normalized = value.trim();
157
+ if (!normalized)
158
+ throw new TypeError(error);
159
+ return normalized;
160
+ }
@@ -0,0 +1,48 @@
1
+ /** A canonical flat-claims resource entry; `entryId` is graph identity, never a business identifier. */
2
+ export type FlatClaimResourceEntry = Readonly<{
3
+ entryId: string;
4
+ reference: string;
5
+ resourceType: string;
6
+ claims: Readonly<Record<string, string>>;
7
+ }>;
8
+ /** Version-neutral source for FHIR `Resource.language` and `DomainResource.text`. */
9
+ export type FlatClaimNarrative = Readonly<{
10
+ language: string;
11
+ status: 'generated' | 'extensions' | 'additional' | 'empty';
12
+ xhtml: string;
13
+ }>;
14
+ /** Builds one independent resource entry whose claims all belong to its resource type. */
15
+ export declare function buildFlatClaimResourceEntry(input: Readonly<{
16
+ entryId: string;
17
+ resourceType: string;
18
+ claims: Readonly<Record<string, string>>;
19
+ }>): FlatClaimResourceEntry;
20
+ /**
21
+ * Links contained or referenced resources from the parent flat entry.
22
+ *
23
+ * The comma-separated `<ParentType>.contained-reference-list` is the only
24
+ * canonical graph edge. A child remains an independent entry and therefore
25
+ * never receives `is-contained` or `contained-parent-reference` claims.
26
+ */
27
+ export declare function linkFlatClaimResourceEntries(entries: readonly FlatClaimResourceEntry[], link: Readonly<{
28
+ parentEntryId: string;
29
+ childEntryIds: readonly string[];
30
+ }>): readonly FlatClaimResourceEntry[];
31
+ /** Adds language and XHTML narrative source without materializing native FHIR. */
32
+ export declare function withFlatClaimNarrative(entry: FlatClaimResourceEntry, narrative: FlatClaimNarrative): FlatClaimResourceEntry;
33
+ /** Reads legacy graphs without perpetuating child-owned containment state. */
34
+ export declare function normalizeLegacyContainedClaims(entry: FlatClaimResourceEntry): FlatClaimResourceEntry;
35
+ /**
36
+ * Builds one DocumentReference flat entry for a PDF, JPEG or PNG.
37
+ * Link one or many such entries from any clinical parent with
38
+ * `linkFlatClaimResourceEntries`; attachment bytes are not duplicated.
39
+ */
40
+ export declare function buildDocumentReferenceFlatEntry(input: Readonly<{
41
+ entryId: string;
42
+ identifier: string;
43
+ contentType: 'application/pdf' | 'image/jpeg' | 'image/png';
44
+ contentHash: string;
45
+ title?: string;
46
+ url?: string;
47
+ dataBase64?: string;
48
+ }>): FlatClaimResourceEntry;