fhir-data-utils-ts 0.6.20 → 0.6.22

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 (31) hide show
  1. package/.codex/skills/govern-fhir-data-utilities/SKILL.md +8 -11
  2. package/.codex/skills/govern-fhir-observation-components/SKILL.md +11 -13
  3. package/.codex/skills/preserve-fhir-flat-coding-review/SKILL.md +5 -5
  4. package/CHANGELOG.md +31 -13
  5. package/README.md +18 -26
  6. package/dist/builders/immunization-reminders.d.ts +1 -1
  7. package/dist/builders/immunization-reminders.js +1 -1
  8. package/dist/claims/communication-request-claims.d.ts +2 -0
  9. package/dist/claims/communication-request-claims.js +4 -0
  10. package/dist/claims/index.d.ts +1 -0
  11. package/dist/claims/index.js +1 -0
  12. package/dist/claims/us-core-patient-demographics-claims.d.ts +19 -0
  13. package/dist/claims/us-core-patient-demographics-claims.js +26 -0
  14. package/dist/clinical-terminology.d.ts +1 -1
  15. package/dist/converters/convert-communication-request.js +23 -4
  16. package/dist/converters/convert-us-core-patient-demographics.d.ts +29 -0
  17. package/dist/converters/convert-us-core-patient-demographics.js +114 -0
  18. package/dist/converters/index.d.ts +1 -0
  19. package/dist/converters/index.js +1 -0
  20. package/dist/data/communication-request-form-schema.data.d.ts +24 -0
  21. package/dist/data/communication-request-form-schema.data.js +28 -4
  22. package/dist/fhir-search-parameters.d.ts +1 -1
  23. package/dist/fhir-search-parameters.js +1 -1
  24. package/dist/imaging-types.d.ts +1 -1
  25. package/dist/search-models/index.d.ts +1 -1
  26. package/dist/search-models/index.js +1 -1
  27. package/docs/CARE_EMERGENCY_ALERT_CONTRACTS.md +2 -2
  28. package/docs/CLINICAL_TYPED_ACCESSOR_BOUNDARY.md +6 -11
  29. package/docs/COMMUNICATION_BUNDLE_CONTRACT.md +6 -7
  30. package/package.json +1 -1
  31. package/docs/UHC_FHIR_UTILS_MIGRATION.md +0 -181
@@ -1,25 +1,23 @@
1
1
  ---
2
2
  name: govern-fhir-data-utilities
3
- description: Complete or audit migrations, claim catalogs, converters, builders, tests and releases in fhir-data-utils-ts. Use for shared FHIR types or utilities copied from frozen gdc-common-utils, former UHC utilities, SOS, Vet or UHC packages; raw flat-claim literals; duplicate root and family modules; Observation components; Immunization graphs; Group or ResearchStudy projections; package exports; coverage gates; or downstream promotion.
3
+ description: Complete or audit migrations, claim catalogs, converters, builders, tests and releases in fhir-data-utils-ts. Use for shared FHIR types or utilities recovered from standards-based compatibility evidence; raw flat-claim literals; duplicate root and family modules; Observation components; Immunization graphs; Group or ResearchStudy projections; package exports; coverage gates; or downstream promotion.
4
4
  ---
5
5
 
6
6
  # Govern FHIR Data Utilities
7
7
 
8
8
  ## Keep ownership explicit
9
9
 
10
- 1. Read `docs/UHC_FHIR_UTILS_MIGRATION.md` and inventory every relevant source
11
- family before declaring a migration complete.
10
+ 1. Inventory every relevant standards-based source family before declaring a
11
+ migration complete.
12
12
  2. Treat `gdc-*` repositories as read-only evidence unless the user explicitly
13
13
  changes that scope. Never patch them as part of this migration.
14
14
  3. Keep neutral FHIR data semantics here. Keep HTTP, SMART, DIDComm, storage,
15
15
  authorization and product policy in their owning services or sector packages.
16
16
  4. Define every flat key once in its owning named catalog. Import that symbol in
17
17
  converters, builders, tests and examples; never repeat a claim-path literal.
18
- 5. For SHC and DGC archaeology, treat `uhc-helpers-typescript` as the early
19
- flow source, the later UHC common models/data/common/crypto split as the
20
- preferred implementation evidence, and `gdc-common-utils-ts` as its later
21
- consolidation. Do not copy an earlier helper when a later governed contract
22
- exists.
18
+ 5. For SHC and DGC archaeology, prefer the latest governed standards-based
19
+ implementation evidence. Do not copy an earlier helper when a later
20
+ governed contract exists.
23
21
 
24
22
  ## Separate catalogs, adapters and builders
25
23
 
@@ -68,9 +66,8 @@ description: Complete or audit migrations, claim catalogs, converters, builders,
68
66
  subscriber persistence, authentication or product notification policy.
69
67
  - Validate and project the canonical outer `Bundle[type=batch]` ->
70
68
  `Communication` -> inner `Bundle[type=document|batch|collection]` structure
71
- here, but keep the cross-product high-level editor/reader in `sos-sdk-core`
72
- and its data contracts in `sos-data-utils`. Neither replacement package may
73
- import frozen `gdc-*`. Arbitrary files are DocumentReference entries in the
69
+ here, but keep high-level editors/readers and application data contracts in
70
+ downstream libraries. Arbitrary files are DocumentReference entries in the
74
71
  inner Bundle, never a second direct Communication attachment. Direct
75
72
  flat-claim CRUD is an internal persistence boundary, not this wire format.
76
73
  - Keep ImagingStudy searchable metadata, clinical references and protected
@@ -1,6 +1,6 @@
1
1
  ---
2
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.
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 neutral compatibility code.
4
4
  ---
5
5
 
6
6
  # Govern FHIR Observation Components
@@ -9,12 +9,12 @@ description: Implement or audit neutral FHIR Observation claim catalogs, vital-s
9
9
 
10
10
  1. Treat native FHIR import/export as an adapter boundary. Keep flat claims as
11
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.
12
+ 2. Read `src/observation-claims.ts`, `src/vital-sign-observations.ts` and their
13
+ tests before changing behavior.
14
+ 3. Treat external compatibility repositories as read-only evidence unless the
15
+ user explicitly changes that scope.
16
+ 4. Keep product policies downstream. This package owns only neutral FHIR data
17
+ semantics.
18
18
 
19
19
  ## Preserve the component contract
20
20
 
@@ -79,15 +79,13 @@ description: Implement or audit neutral FHIR Observation claim catalogs, vital-s
79
79
 
80
80
  ## Audit migrations completely
81
81
 
82
- - Inventory the old UHC package, `gdc-common-utils-ts`, `sos-data-utils-ts`,
83
- `vet-data-utils-ts`, and the UHC/Vet/SOS SDKs before calling a migration
84
- complete.
82
+ - Inventory the relevant standards-based source and compatibility evidence
83
+ before calling a migration complete.
85
84
  - Move only neutral FHIR data types, claim catalogs, conversion rules and pure
86
85
  helpers here. Keep transport clients and sector authorization/policy in
87
86
  their owning package.
88
- - Record every migrated, retired or intentionally retained family in
89
- `docs/UHC_FHIR_UTILS_MIGRATION.md`; an unmentioned file is an incomplete
90
- audit.
87
+ - Record every migrated, retired or intentionally retained family in neutral
88
+ repository documentation; an unmentioned file is an incomplete audit.
91
89
  - Expose each supported family through a stable package subpath and prove the
92
90
  packed artifact, not only root source imports.
93
91
 
@@ -1,6 +1,6 @@
1
1
  ---
2
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.
3
+ description: Preserve the neutral flat-claim contract for imported local clinical text, terminology proposals and confirmed codings. Use when changing ingestion, ResearchSubject batches, contained clinical resources, coding-review UI/API contracts, examples, fixtures or tests.
4
4
  ---
5
5
 
6
6
  # Preserve FHIR Flat Coding Review
@@ -24,7 +24,7 @@ redeclare governed claim paths, code systems, UUIDs or example values.
24
24
  `userSelected` as a draft or promotion flag.
25
25
  - Group review UI with the package-owned clinical families: allergies and
26
26
  intolerances, procedures, diagnostics and immunizations. Do not duplicate
27
- resource-type lists in Vet, UHC or DataConv consumers.
27
+ resource-type lists in downstream consumers.
28
28
  - A localized generated label may use a language-qualified `-text_<BCP47>`
29
29
  companion. It must not overwrite the imported local text before explicit
30
30
  review.
@@ -60,9 +60,9 @@ redeclare governed claim paths, code systems, UUIDs or example values.
60
60
  ## Boundaries
61
61
 
62
62
  - This is FHIR-neutral shared data behavior. It belongs in
63
- `fhir-data-utils-ts`, not SOSChain, Vet, UHC or frozen `gdc-*` packages.
64
- - DataConv applies the contract during import. Vet and UHC consume the same
65
- contract and may add sector policy without forking its claim shape.
63
+ `fhir-data-utils-ts`, not in downstream or frozen compatibility packages.
64
+ - Importers and sector applications consume the same contract and may add
65
+ policy without forking its claim shape.
66
66
  - Do not put HTTP clients, gateway policy, product authorization or terminology
67
67
  catalogs in this package.
68
68
  - Tests and examples import package-owned fixtures/builders. Do not scatter raw
package/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Re-establish the public repository from a clean source snapshot while
6
+ retaining this changelog as the package release record.
7
+
8
+ ## 0.6.22 - 2026-10-22
9
+
10
+ - Add FHIR-owned flat claims and a lossless R4 extension round trip for the
11
+ optional US Core Patient race and ethnicity extensions. Preserve OMB and
12
+ detailed Coding tokens separately from the required human-readable text;
13
+ derive the official `Patient.race` and `Patient.ethnicity` token-search
14
+ claims instead of accepting them from a browser; and do not expose these
15
+ jurisdictional fields as universal demographics or org.schema properties.
16
+
17
+ ## 0.6.21 - 2026-10-21
18
+
19
+ - Add the shared CommunicationRequest form, flat-claim and materialization
20
+ fields needed for categorized public alerts, related attachments and bounded
21
+ occurrence periods without introducing a proprietary alert resource.
22
+
5
23
  ## 0.6.20 - 2026-10-12
6
24
 
7
25
  - Export the deduplicated official LOINC Document Ontology subject-matter-
@@ -58,8 +76,8 @@
58
76
  - Give every reusable IPS authoring schema canonical initial claims so portal
59
77
  components do not invent resource status defaults.
60
78
 
61
- - Recover the useful locale-aware intent of the historical UHC
62
- internationalization helpers as strict FHIR temporal presentation tools.
79
+ - Recover useful locale-aware behavior from historical compatibility helpers
80
+ as strict FHIR temporal presentation tools.
63
81
  Date-only values retain their calendar day, instants render with the user's
64
82
  locale and IANA time zone, editor date/time fields compose a canonical FHIR
65
83
  instant with optional seconds defaulted to `00`, and inclusive ranges treat
@@ -86,8 +104,8 @@
86
104
  document so portals can update the Composition without duplicating LOINC
87
105
  section codes.
88
106
 
89
- - Recover neutral SHC and EU DGC QR prefix classification from the later UHC
90
- data-utility lineage, with exported repository-safe `*.data.ts` examples.
107
+ - Recover neutral SHC and EU DGC QR prefix classification from compatibility
108
+ evidence, with exported repository-safe `*.data.ts` examples.
91
109
  Recognition explicitly provides no decoding, verification or authorization.
92
110
 
93
111
  - Export one repository-safe, telephone-free IPS demo document with all sixteen
@@ -99,8 +117,8 @@
99
117
  historical source modules remain compatibility reexports.
100
118
  - Remove notification-channel policy from the general root and `builders`
101
119
  surfaces. The historical reminder subpath and non-standard `Task.channel`
102
- claim remain deprecated compatibility only while product packages migrate;
103
- SOS/Vet/UHC own channel choices and dispatch behavior.
120
+ claim remain deprecated compatibility only while consumers migrate;
121
+ downstream applications own channel choices and dispatch behavior.
104
122
 
105
123
  ## 0.6.2 - 2026-09-30
106
124
 
@@ -123,7 +141,7 @@
123
141
  the frozen shared implementation: outer batch Bundle, one FHIR Bundle
124
142
  attachment per Communication, and validated document/batch/collection inner
125
143
  Bundles. Reject direct clinical or binary Communication payloads while
126
- leaving high-level mutable editors to `sos-sdk-core`.
144
+ leaving high-level mutable editors downstream.
127
145
  - Add a typed Appointment exchange projection that preserves start/end,
128
146
  participants, slots and supporting information while keeping an ICS calendar
129
147
  file as an independent DocumentReference flat-claim resource.
@@ -254,9 +272,9 @@
254
272
  modifying that frozen source repository.
255
273
  - Recover strict FHIR primitives, value/search/token utilities, Bundle readers,
256
274
  resource search/template types, terminology model types, RelatedPerson and
257
- telecom helpers, and DICOMDIR structural types from the retired UHC package.
275
+ telecom helpers, and DICOMDIR structural types from compatibility evidence.
258
276
  - Move cross-sector terminology, Immunization graph/reminder, Group R5 and
259
- ResearchStudy R5 contracts that existed only in SOS or Vet data packages.
277
+ ResearchStudy R5 contracts into their neutral standards-based owner.
260
278
  - Require component Observations to use independently indexable graph entries;
261
279
  deprecated JSON-array component claims remain read-only compatibility input.
262
280
  - Add stable package subpaths for every migrated contract family.
@@ -293,12 +311,12 @@
293
311
  JSON repeatable-component encoding, generic component index projection and
294
312
  explicit R4/R5 projection boundary. Historical component CSV and `bp-*`
295
313
  aliases remain read-only compatibility inputs.
296
- - Record the reuse/rewrite/retire audit of the original
297
- `uhc-fhir-utils-typescript` architecture without adding its obsolete product,
298
- transport, SHA-1, DICOM or build dependencies to the neutral package.
314
+ - Record the reuse/rewrite/retire audit of historical compatibility utilities
315
+ without adding obsolete product, transport, SHA-1, DICOM or build
316
+ dependencies to the neutral package.
299
317
  - Add the neutral contained-resource coding-review contract: canonical imported
300
318
  `*-text`, resource-scoped `meta.codingProposals[]`, batch traversal, candidate
301
- confirmation and reusable governed fixtures for DataConv, Vet and UHC.
319
+ confirmation and reusable governed fixtures for downstream consumers.
302
320
  - Add channel-neutral FHIR-like vital-sign Observation builders and lossless device-batch normalization while preserving device source without inventing professional attestation.
303
321
  - Introduce product-neutral flat-claim resource graphs, parent-owned
304
322
  `contained-reference-list`, narrative `language`/`xhtml`, and governed
package/README.md CHANGED
@@ -2,26 +2,24 @@
2
2
 
3
3
  FHIR-neutral data contracts for flat claims, governed SearchParameter names,
4
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.
5
+ network clients and no dependency on downstream product packages.
6
6
 
7
7
  Release order is local-first: local tarball consumers, complete local matrix,
8
8
  merge and push `main`, publish bottom-up, install exact registry versions, then
9
9
  staging. Never commit `file:`, Git or workspace dependencies.
10
10
 
11
- ## Where this package sits
11
+ ## Public source history
12
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
13
+ The public repository begins from a clean 2026 source snapshot. The retained
14
+ [`CHANGELOG.md`](CHANGELOG.md) records package releases without republishing
15
+ the former Git push history.
21
16
 
22
- terminology-api-node-ts reads the neutral FHIR and shared SOS contracts.
23
- It never depends on Vet or UHC product packages.
24
- ```
17
+ ## Ownership boundary
18
+
19
+ This package owns only standards-based FHIR SearchParameters, flat-claim
20
+ graphs, validation and explicit R4/R5/R6 projection rules. Sector terminology,
21
+ product policy, transport, authorization, mutable editors, network clients and
22
+ application workflows belong downstream and are deliberately unnamed here.
25
23
 
26
24
  FHIR resource graphs use independent entries. A parent links children only
27
25
  through `<ResourceType>.contained-reference-list`; children do not carry
@@ -48,8 +46,8 @@ Cross-participant data always uses an outer FHIR batch Bundle containing
48
46
  Communication entries. Each Communication carries exactly one
49
47
  `application/fhir+json` inner document, batch or collection Bundle; arbitrary
50
48
  files are DocumentReference resources inside that Bundle. This package owns
51
- strict parsing and projection tools; the shared high-level editor/reader belongs
52
- to `sos-sdk-core`, with cross-sector contracts in `sos-data-utils`. See
49
+ strict parsing and projection tools; mutable editors, transport and
50
+ cross-sector policy belong downstream. See
53
51
  [Communication Bundle contract](docs/COMMUNICATION_BUNDLE_CONTRACT.md).
54
52
 
55
53
  Reservable equipment remains a `Device` actor of its service-specific
@@ -70,10 +68,10 @@ The root export and stable subpaths provide:
70
68
  - complete flat-claim catalogs and claim helpers for the resource families
71
69
  formerly scattered through `gdc-common-utils-ts`;
72
70
  - explicit R4/R5 conversion adapters, including Consent and PractitionerRole;
73
- - the recovered UHC resource search/template types, terminology model types,
71
+ - resource search/template types, terminology model types,
74
72
  RelatedPerson/telecom helpers and type-only DICOMDIR tree contract;
75
73
  - cross-sector terminology, Immunization graphs/reminders, and FHIR R5 Group
76
- and ResearchStudy contracts previously located only in SOS or Vet packages.
74
+ and ResearchStudy contracts;
77
75
  - native FHIR R5 SubscriptionTopic/Subscription configuration builders,
78
76
  official search catalogs and a strict notification-Bundle parser; terminal
79
77
  asynchronous job messages remain claims-first Communication resources.
@@ -156,18 +154,13 @@ top-level Observations with a professional attester. Ledger hashing, index
156
154
  updates, Digital Twin and research eligibility remain owning-service policy.
157
155
  See [Daily vital-sign document lifecycle](docs/VITAL_SIGN_DAILY_DOCUMENT.md).
158
156
 
159
- See [the UHC FHIR utilities migration](docs/UHC_FHIR_UTILS_MIGRATION.md) for the
160
- reuse/rewrite/retire inventory and the rule for moving frozen contracts without
161
- changing any `gdc-*` repository.
162
-
163
157
  See [Clinical typed accessor boundary](docs/CLINICAL_TYPED_ACCESSOR_BOUNDARY.md)
164
158
  for the exact division between canonical clinical claims/converters here,
165
- shared SOS multichannel schemas and the mutable Bundle getters/setters exposed
166
- by runtime SDKs.
159
+ downstream schemas and mutable Bundle getters/setters.
167
160
 
168
161
  ## Coding review for imported clinical text
169
162
 
170
- DataConv, Vet and UHC share one neutral review contract from
163
+ Coding review uses one neutral contract from
171
164
  `fhir-data-utils-ts/coding-review-flat-claims`:
172
165
 
173
166
  - imported wording is the canonical `<ResourceType>.<code-field>-text` claim;
@@ -209,8 +202,7 @@ packed artifact and installs it in an isolated consumer before publication.
209
202
 
210
203
  ## Care, emergency and population-alert graphs
211
204
 
212
- The package provides the neutral FHIR foundation for the three SOSChain
213
- journeys proven first in VetChain/PetChain: Location-linked Encounter care
205
+ The package provides the neutral FHIR foundation for Location-linked Encounter care
214
206
  journeys, emergency ServiceRequest plus triage Task, and cohort
215
207
  CommunicationRequest against a frozen Group snapshot. See
216
208
  [the contract guide](docs/CARE_EMERGENCY_ALERT_CONTRACTS.md). Product policy,
@@ -7,7 +7,7 @@ export declare const ImmunizationReminderTaskValues: Readonly<{
7
7
  }>;
8
8
  /**
9
9
  * @deprecated Compatibility only. Notification-channel policy belongs to the
10
- * consuming SOS/Vet/UHC data package, not to the general FHIR data package.
10
+ * consuming downstream package, not to the general FHIR data package.
11
11
  */
12
12
  export declare const ImmunizationReminderChannels: readonly ["push", "email", "sms", "whatsapp", "telephone"];
13
13
  export type ImmunizationReminderChannel = (typeof ImmunizationReminderChannels)[number];
@@ -10,7 +10,7 @@ export const ImmunizationReminderTaskValues = Object.freeze({
10
10
  });
11
11
  /**
12
12
  * @deprecated Compatibility only. Notification-channel policy belongs to the
13
- * consuming SOS/Vet/UHC data package, not to the general FHIR data package.
13
+ * consuming downstream package, not to the general FHIR data package.
14
14
  */
15
15
  export const ImmunizationReminderChannels = Object.freeze([
16
16
  'push',
@@ -20,6 +20,8 @@ export declare const CommunicationRequestClaim: Readonly<{
20
20
  readonly InformationProvider: "CommunicationRequest.information-provider";
21
21
  readonly AuthoredOn: "CommunicationRequest.authored-on";
22
22
  readonly Occurrence: "CommunicationRequest.occurrence";
23
+ readonly OccurrencePeriodStart: "CommunicationRequest.occurrence-period-start";
24
+ readonly OccurrencePeriodEnd: "CommunicationRequest.occurrence-period-end";
23
25
  readonly ReasonCode: "CommunicationRequest.reason-code";
24
26
  readonly ReasonReference: "CommunicationRequest.reason-reference";
25
27
  readonly PayloadContent: "CommunicationRequest.payload-content";
@@ -19,6 +19,8 @@ export const CommunicationRequestClaim = Object.freeze({
19
19
  InformationProvider: 'CommunicationRequest.information-provider',
20
20
  AuthoredOn: 'CommunicationRequest.authored-on',
21
21
  Occurrence: 'CommunicationRequest.occurrence',
22
+ OccurrencePeriodStart: 'CommunicationRequest.occurrence-period-start',
23
+ OccurrencePeriodEnd: 'CommunicationRequest.occurrence-period-end',
22
24
  ReasonCode: 'CommunicationRequest.reason-code',
23
25
  ReasonReference: 'CommunicationRequest.reason-reference',
24
26
  PayloadContent: 'CommunicationRequest.payload-content',
@@ -48,6 +50,8 @@ export const CommunicationRequestClaimSpecs = Object.freeze([
48
50
  { key: CommunicationRequestClaim.InformationProvider, meaning: 'Actors expected to provide or review message information.', example: 'PractitionerRole/doctor-1' },
49
51
  { key: CommunicationRequestClaim.AuthoredOn, meaning: 'Request authoring time.', example: '2026-09-26T11:00:00Z' },
50
52
  { key: CommunicationRequestClaim.Occurrence, meaning: 'Requested delivery time.', example: '2026-09-26T11:30:00Z' },
53
+ { key: CommunicationRequestClaim.OccurrencePeriodStart, meaning: 'Start of the requested delivery period.', example: '2026-09-26T11:30:00Z' },
54
+ { key: CommunicationRequestClaim.OccurrencePeriodEnd, meaning: 'End of the requested delivery period.', example: '2026-09-27T11:30:00Z' },
51
55
  { key: CommunicationRequestClaim.ReasonCode, meaning: 'Coded reasons for the request.', example: 'http://snomed.info/sct|246205007' },
52
56
  { key: CommunicationRequestClaim.ReasonReference, meaning: 'Resource references supporting the request reason.', example: 'Condition/condition-1' },
53
57
  { key: CommunicationRequestClaim.PayloadContent, meaning: 'R4 string payload compatibility value.', example: 'Family update ready for review.' },
@@ -35,3 +35,4 @@ export * from './regulated-authorization-claims.js';
35
35
  export * from './service-request-claims.js';
36
36
  export * from './schedule-slot-claims.js';
37
37
  export * from './task-claims.js';
38
+ export * from './us-core-patient-demographics-claims.js';
@@ -35,3 +35,4 @@ export * from './regulated-authorization-claims.js';
35
35
  export * from './service-request-claims.js';
36
36
  export * from './schedule-slot-claims.js';
37
37
  export * from './task-claims.js';
38
+ export * from './us-core-patient-demographics-claims.js';
@@ -0,0 +1,19 @@
1
+ import type { ClaimSpec } from './claim-types.js';
2
+ /** Canonical claims for the optional US Core Patient race and ethnicity extensions. */
3
+ export declare const UsCorePatientDemographicClaim: Readonly<{
4
+ readonly RaceOmbCategory: "Patient.extension:us-core-race.extension:ombCategory";
5
+ readonly RaceDetailed: "Patient.extension:us-core-race.extension:detailed";
6
+ readonly RaceText: "Patient.extension:us-core-race.extension:text";
7
+ readonly EthnicityOmbCategory: "Patient.extension:us-core-ethnicity.extension:ombCategory";
8
+ readonly EthnicityDetailed: "Patient.extension:us-core-ethnicity.extension:detailed";
9
+ readonly EthnicityText: "Patient.extension:us-core-ethnicity.extension:text";
10
+ }>;
11
+ /** Derived claims backed by the two official optional US Core SearchParameters. */
12
+ export declare const UsCorePatientDemographicSearchClaim: Readonly<{
13
+ readonly Race: "Patient.race";
14
+ readonly Ethnicity: "Patient.ethnicity";
15
+ }>;
16
+ export type UsCorePatientDemographicClaimKey = typeof UsCorePatientDemographicClaim[keyof typeof UsCorePatientDemographicClaim];
17
+ export type UsCorePatientDemographicSearchClaimKey = typeof UsCorePatientDemographicSearchClaim[keyof typeof UsCorePatientDemographicSearchClaim];
18
+ export declare const UsCorePatientDemographicClaimSpecs: readonly ClaimSpec[];
19
+ export declare const UsCorePatientDemographicSearchClaimSpecs: readonly ClaimSpec[];
@@ -0,0 +1,26 @@
1
+ /** Canonical claims for the optional US Core Patient race and ethnicity extensions. */
2
+ export const UsCorePatientDemographicClaim = Object.freeze({
3
+ RaceOmbCategory: 'Patient.extension:us-core-race.extension:ombCategory',
4
+ RaceDetailed: 'Patient.extension:us-core-race.extension:detailed',
5
+ RaceText: 'Patient.extension:us-core-race.extension:text',
6
+ EthnicityOmbCategory: 'Patient.extension:us-core-ethnicity.extension:ombCategory',
7
+ EthnicityDetailed: 'Patient.extension:us-core-ethnicity.extension:detailed',
8
+ EthnicityText: 'Patient.extension:us-core-ethnicity.extension:text',
9
+ });
10
+ /** Derived claims backed by the two official optional US Core SearchParameters. */
11
+ export const UsCorePatientDemographicSearchClaim = Object.freeze({
12
+ Race: 'Patient.race',
13
+ Ethnicity: 'Patient.ethnicity',
14
+ });
15
+ export const UsCorePatientDemographicClaimSpecs = Object.freeze([
16
+ { key: UsCorePatientDemographicClaim.RaceOmbCategory, meaning: 'Comma-separated OMB race Coding tokens.', example: 'urn:oid:2.16.840.1.113883.6.238|2028-9' },
17
+ { key: UsCorePatientDemographicClaim.RaceDetailed, meaning: 'Comma-separated detailed race Coding tokens.', example: 'urn:oid:2.16.840.1.113883.6.238|2036-2' },
18
+ { key: UsCorePatientDemographicClaim.RaceText, meaning: 'Patient-supplied race text required when the US Core race extension is present.', example: 'Asian' },
19
+ { key: UsCorePatientDemographicClaim.EthnicityOmbCategory, meaning: 'OMB ethnicity Coding token.', example: 'urn:oid:2.16.840.1.113883.6.238|2135-2' },
20
+ { key: UsCorePatientDemographicClaim.EthnicityDetailed, meaning: 'Comma-separated detailed ethnicity Coding tokens.', example: 'urn:oid:2.16.840.1.113883.6.238|2148-5' },
21
+ { key: UsCorePatientDemographicClaim.EthnicityText, meaning: 'Patient-supplied ethnicity text required when the US Core ethnicity extension is present.', example: 'Hispanic or Latino' },
22
+ ]);
23
+ export const UsCorePatientDemographicSearchClaimSpecs = Object.freeze([
24
+ { key: UsCorePatientDemographicSearchClaim.Race, meaning: 'Derived token list for the official optional US Core race SearchParameter.', example: 'urn:oid:2.16.840.1.113883.6.238|2028-9' },
25
+ { key: UsCorePatientDemographicSearchClaim.Ethnicity, meaning: 'Derived token list for the official optional US Core ethnicity SearchParameter.', example: 'urn:oid:2.16.840.1.113883.6.238|2135-2' },
26
+ ]);
@@ -18,7 +18,7 @@ export declare const ClinicalTerminologySourceScopes: Readonly<{
18
18
  readonly AtcVet: "ATCVET";
19
19
  }>;
20
20
  export type ClinicalTerminologySourceScope = typeof ClinicalTerminologySourceScopes[keyof typeof ClinicalTerminologySourceScopes];
21
- /** Product-neutral terminology candidate returned to SOS, Vet or UHC channels. */
21
+ /** Product-neutral terminology candidate returned to downstream channels. */
22
22
  export type ClinicalTerminologyCandidate = Readonly<{
23
23
  /** Governed source-scope identifier returned by the deployment capability. */
24
24
  source: string;
@@ -18,7 +18,7 @@ export function communicationRequestFlatToFhirR4(claims) {
18
18
  requester: claims[CommunicationRequestClaim.Requester] ? { reference: claims[CommunicationRequestClaim.Requester] } : undefined,
19
19
  sender: firstReference(claims[CommunicationRequestClaim.InformationProvider]),
20
20
  recipient: referenceCsv(claims[CommunicationRequestClaim.Recipient]), authoredOn: claims[CommunicationRequestClaim.AuthoredOn],
21
- occurrenceDateTime: claims[CommunicationRequestClaim.Occurrence],
21
+ ...occurrenceToFhir(claims),
22
22
  reasonCode: claims[CommunicationRequestClaim.ReasonCode] ? [{ coding: codingListFromCsv(claims[CommunicationRequestClaim.ReasonCode]) }] : undefined,
23
23
  reasonReference: referenceCsv(claims[CommunicationRequestClaim.ReasonReference]),
24
24
  payload: payloadR4(claims),
@@ -44,7 +44,8 @@ export function communicationRequestFhirR4ToFlat(resource) {
44
44
  [CommunicationRequestClaim.Requester]: referenceToValue(resource.requester),
45
45
  [CommunicationRequestClaim.InformationProvider]: referenceToValue(resource.sender),
46
46
  [CommunicationRequestClaim.Recipient]: referenceListToCsv(resource.recipient),
47
- [CommunicationRequestClaim.AuthoredOn]: resource.authoredOn, [CommunicationRequestClaim.Occurrence]: resource.occurrenceDateTime,
47
+ [CommunicationRequestClaim.AuthoredOn]: resource.authoredOn,
48
+ ...occurrenceToFlat(resource),
48
49
  [CommunicationRequestClaim.ReasonCode]: codingListToCsv(resource.reasonCode?.flatMap(value => value.coding ?? [])),
49
50
  [CommunicationRequestClaim.ReasonReference]: referenceListToCsv(resource.reasonReference),
50
51
  ...payloadToFlat(resource),
@@ -82,7 +83,7 @@ function communicationRequestFlatToFhirR5Resource(claims) {
82
83
  requester: claims[CommunicationRequestClaim.Requester] ? { reference: claims[CommunicationRequestClaim.Requester] } : undefined,
83
84
  recipient: referenceCsv(claims[CommunicationRequestClaim.Recipient]),
84
85
  informationProvider: referenceCsv(claims[CommunicationRequestClaim.InformationProvider]),
85
- authoredOn: claims[CommunicationRequestClaim.AuthoredOn], occurrenceDateTime: claims[CommunicationRequestClaim.Occurrence],
86
+ authoredOn: claims[CommunicationRequestClaim.AuthoredOn], ...occurrenceToFhir(claims),
86
87
  reason: reasonsR5(claims), payload: payloadR5(claims), note: annotation(claims[CommunicationRequestClaim.Note]),
87
88
  };
88
89
  }
@@ -107,7 +108,7 @@ function communicationRequestFhirR5ResourceToFlat(resource) {
107
108
  [CommunicationRequestClaim.Recipient]: referenceListToCsv(resource.recipient),
108
109
  [CommunicationRequestClaim.InformationProvider]: referenceListToCsv(resource.informationProvider),
109
110
  [CommunicationRequestClaim.AuthoredOn]: resource.authoredOn,
110
- [CommunicationRequestClaim.Occurrence]: resource.occurrenceDateTime,
111
+ ...occurrenceToFlat(resource),
111
112
  [CommunicationRequestClaim.ReasonCode]: codingListToCsv(reasons?.flatMap(value => value.concept?.coding ?? [])),
112
113
  [CommunicationRequestClaim.ReasonReference]: reasons?.map(value => value.reference?.reference).filter((value) => Boolean(value)).join(',') || undefined,
113
114
  ...payloadToFlat(resource), [CommunicationRequestClaim.Note]: annotationText(resource.note),
@@ -134,6 +135,24 @@ function optionalBoolean(value) {
134
135
  }
135
136
  function annotation(value) { return value ? [{ text: value }] : undefined; }
136
137
  function annotationText(value) { return value?.[0]?.text; }
138
+ function occurrenceToFhir(claims) {
139
+ const occurrenceDateTime = claims[CommunicationRequestClaim.Occurrence];
140
+ const start = claims[CommunicationRequestClaim.OccurrencePeriodStart];
141
+ const end = claims[CommunicationRequestClaim.OccurrencePeriodEnd];
142
+ if (occurrenceDateTime && (start || end))
143
+ throw new TypeError('communication_request_occurrence_choice_invalid');
144
+ return occurrenceDateTime ? { occurrenceDateTime } : start || end ? { occurrencePeriod: compact({ start, end }) } : {};
145
+ }
146
+ function occurrenceToFlat(resource) {
147
+ const period = resource.occurrencePeriod;
148
+ if (resource.occurrenceDateTime && period)
149
+ throw new TypeError('communication_request_occurrence_choice_invalid');
150
+ return compact({
151
+ [CommunicationRequestClaim.Occurrence]: resource.occurrenceDateTime,
152
+ [CommunicationRequestClaim.OccurrencePeriodStart]: period?.start,
153
+ [CommunicationRequestClaim.OccurrencePeriodEnd]: period?.end,
154
+ });
155
+ }
137
156
  function payloadR4(claims) {
138
157
  if (claims[CommunicationRequestClaim.PayloadReference])
139
158
  return [{ contentReference: { reference: claims[CommunicationRequestClaim.PayloadReference] } }];
@@ -0,0 +1,29 @@
1
+ import type { UsCorePatientDemographicSearchClaimKey } from '../claims/us-core-patient-demographics-claims.js';
2
+ import type { FhirCoding, FhirResource } from '../fhir-types.js';
3
+ import type { FlatClaims } from './convert-shared.js';
4
+ export declare const UsCorePatientRaceExtensionUrl = "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race";
5
+ export declare const UsCorePatientEthnicityExtensionUrl = "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity";
6
+ export type UsCorePatientSearchParameterDefinition = Readonly<{
7
+ canonicalUrl: string;
8
+ code: 'race' | 'ethnicity';
9
+ base: 'Patient';
10
+ type: 'token';
11
+ expression: string;
12
+ claim: UsCorePatientDemographicSearchClaimKey;
13
+ sourceClaims: readonly string[];
14
+ multipleOr: true;
15
+ multipleAnd: true;
16
+ }>;
17
+ export declare const UsCorePatientDemographicSearchParameters: readonly UsCorePatientSearchParameterDefinition[];
18
+ export type FhirExtension = Readonly<{
19
+ url: string;
20
+ extension?: readonly FhirExtension[];
21
+ valueCoding?: FhirCoding;
22
+ valueString?: string;
23
+ }>;
24
+ /** Materializes only the optional US Core extensions enabled by canonical claims. */
25
+ export declare function usCorePatientDemographicsFlatToFhirR4Extensions(claims: FlatClaims): readonly FhirExtension[];
26
+ /** Reads the two governed US Core extensions without treating them as universal demographics. */
27
+ export declare function usCorePatientDemographicsFhirR4ToFlat(resource: FhirResource): FlatClaims;
28
+ /** Derives the official token-search claims; callers never submit these independently. */
29
+ export declare function deriveUsCorePatientDemographicSearchClaims(claims: FlatClaims): FlatClaims;
@@ -0,0 +1,114 @@
1
+ import { UsCorePatientDemographicClaim, UsCorePatientDemographicSearchClaim } from '../claims/us-core-patient-demographics-claims.js';
2
+ import { codingListFromCsv, codingListToCsv } from './convert-shared.js';
3
+ export const UsCorePatientRaceExtensionUrl = 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-race';
4
+ export const UsCorePatientEthnicityExtensionUrl = 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity';
5
+ export const UsCorePatientDemographicSearchParameters = Object.freeze([
6
+ Object.freeze({
7
+ canonicalUrl: 'http://hl7.org/fhir/us/core/SearchParameter/us-core-race',
8
+ code: 'race', base: 'Patient', type: 'token',
9
+ expression: `Patient.extension.where(url = '${UsCorePatientRaceExtensionUrl}').extension.value.code`,
10
+ claim: UsCorePatientDemographicSearchClaim.Race,
11
+ sourceClaims: Object.freeze([UsCorePatientDemographicClaim.RaceOmbCategory, UsCorePatientDemographicClaim.RaceDetailed]),
12
+ multipleOr: true, multipleAnd: true,
13
+ }),
14
+ Object.freeze({
15
+ canonicalUrl: 'http://hl7.org/fhir/us/core/SearchParameter/us-core-ethnicity',
16
+ code: 'ethnicity', base: 'Patient', type: 'token',
17
+ expression: `Patient.extension.where(url = '${UsCorePatientEthnicityExtensionUrl}').extension.value.code`,
18
+ claim: UsCorePatientDemographicSearchClaim.Ethnicity,
19
+ sourceClaims: Object.freeze([UsCorePatientDemographicClaim.EthnicityOmbCategory, UsCorePatientDemographicClaim.EthnicityDetailed]),
20
+ multipleOr: true, multipleAnd: true,
21
+ }),
22
+ ]);
23
+ /** Materializes only the optional US Core extensions enabled by canonical claims. */
24
+ export function usCorePatientDemographicsFlatToFhirR4Extensions(claims) {
25
+ return [
26
+ buildExtension({
27
+ url: UsCorePatientRaceExtensionUrl,
28
+ omb: claims[UsCorePatientDemographicClaim.RaceOmbCategory],
29
+ detailed: claims[UsCorePatientDemographicClaim.RaceDetailed],
30
+ text: claims[UsCorePatientDemographicClaim.RaceText],
31
+ textError: 'us_core_patient_race_text_required',
32
+ }),
33
+ buildExtension({
34
+ url: UsCorePatientEthnicityExtensionUrl,
35
+ omb: claims[UsCorePatientDemographicClaim.EthnicityOmbCategory],
36
+ detailed: claims[UsCorePatientDemographicClaim.EthnicityDetailed],
37
+ text: claims[UsCorePatientDemographicClaim.EthnicityText],
38
+ textError: 'us_core_patient_ethnicity_text_required',
39
+ singleOmbCategory: true,
40
+ }),
41
+ ].filter((extension) => Boolean(extension));
42
+ }
43
+ /** Reads the two governed US Core extensions without treating them as universal demographics. */
44
+ export function usCorePatientDemographicsFhirR4ToFlat(resource) {
45
+ if (resource.resourceType !== 'Patient')
46
+ throw new TypeError('us_core_patient_resource_required');
47
+ const extensions = Array.isArray(resource.extension) ? resource.extension : [];
48
+ return {
49
+ ...readExtension(extensions, UsCorePatientRaceExtensionUrl, {
50
+ omb: UsCorePatientDemographicClaim.RaceOmbCategory,
51
+ detailed: UsCorePatientDemographicClaim.RaceDetailed,
52
+ text: UsCorePatientDemographicClaim.RaceText,
53
+ textError: 'us_core_patient_race_text_required',
54
+ }),
55
+ ...readExtension(extensions, UsCorePatientEthnicityExtensionUrl, {
56
+ omb: UsCorePatientDemographicClaim.EthnicityOmbCategory,
57
+ detailed: UsCorePatientDemographicClaim.EthnicityDetailed,
58
+ text: UsCorePatientDemographicClaim.EthnicityText,
59
+ textError: 'us_core_patient_ethnicity_text_required',
60
+ singleOmbCategory: true,
61
+ }),
62
+ };
63
+ }
64
+ /** Derives the official token-search claims; callers never submit these independently. */
65
+ export function deriveUsCorePatientDemographicSearchClaims(claims) {
66
+ return {
67
+ [UsCorePatientDemographicSearchClaim.Race]: mergeTokenClaims(claims[UsCorePatientDemographicClaim.RaceOmbCategory], claims[UsCorePatientDemographicClaim.RaceDetailed]),
68
+ [UsCorePatientDemographicSearchClaim.Ethnicity]: mergeTokenClaims(claims[UsCorePatientDemographicClaim.EthnicityOmbCategory], claims[UsCorePatientDemographicClaim.EthnicityDetailed]),
69
+ };
70
+ }
71
+ function buildExtension(input) {
72
+ const omb = codingListFromCsv(input.omb) ?? [];
73
+ const detailed = codingListFromCsv(input.detailed) ?? [];
74
+ const text = String(input.text || '').trim();
75
+ if (!omb.length && !detailed.length && !text)
76
+ return undefined;
77
+ if (!text)
78
+ throw new TypeError(input.textError);
79
+ if (input.singleOmbCategory && omb.length > 1)
80
+ throw new TypeError('us_core_patient_ethnicity_omb_category_cardinality');
81
+ return Object.freeze({
82
+ url: input.url,
83
+ extension: Object.freeze([
84
+ ...omb.map((valueCoding) => Object.freeze({ url: 'ombCategory', valueCoding })),
85
+ ...detailed.map((valueCoding) => Object.freeze({ url: 'detailed', valueCoding })),
86
+ Object.freeze({ url: 'text', valueString: text }),
87
+ ]),
88
+ });
89
+ }
90
+ function readExtension(extensions, url, keys) {
91
+ const matches = extensions.filter((extension) => extension.url === url);
92
+ if (!matches.length)
93
+ return {};
94
+ if (matches.length > 1)
95
+ throw new TypeError('us_core_patient_extension_cardinality');
96
+ const children = matches[0]?.extension ?? [];
97
+ const omb = children.filter((extension) => extension.url === 'ombCategory' && extension.valueCoding).map((extension) => extension.valueCoding);
98
+ const detailed = children.filter((extension) => extension.url === 'detailed' && extension.valueCoding).map((extension) => extension.valueCoding);
99
+ const texts = children.filter((extension) => extension.url === 'text' && extension.valueString).map((extension) => extension.valueString.trim()).filter(Boolean);
100
+ if (texts.length !== 1)
101
+ throw new TypeError(keys.textError);
102
+ if (keys.singleOmbCategory && omb.length > 1)
103
+ throw new TypeError('us_core_patient_ethnicity_omb_category_cardinality');
104
+ return {
105
+ [keys.omb]: codingListToCsv(omb),
106
+ [keys.detailed]: codingListToCsv(detailed),
107
+ [keys.text]: texts[0],
108
+ };
109
+ }
110
+ function mergeTokenClaims(...values) {
111
+ const tokens = values.flatMap((value) => String(value || '').split(',')).map((value) => value.trim()).filter(Boolean);
112
+ const unique = [...new Set(tokens)];
113
+ return unique.length ? unique.join(',') : undefined;
114
+ }
@@ -32,3 +32,4 @@ export * from './convert-related-person.js';
32
32
  export * from './convert-service-request.js';
33
33
  export * from './convert-schedule-slot.js';
34
34
  export * from './materialize-claims-first-resource.js';
35
+ export * from './convert-us-core-patient-demographics.js';
@@ -32,3 +32,4 @@ export * from './convert-related-person.js';
32
32
  export * from './convert-service-request.js';
33
33
  export * from './convert-schedule-slot.js';
34
34
  export * from './materialize-claims-first-resource.js';
35
+ export * from './convert-us-core-patient-demographics.js';
@@ -7,13 +7,17 @@ export type CommunicationRequestFormData = Readonly<{
7
7
  status: typeof CommunicationRequestStatuses[number];
8
8
  intent: typeof CommunicationRequestIntents[number];
9
9
  priority?: typeof CommunicationRequestPriorities[number];
10
+ category?: string;
10
11
  subject: string;
12
+ about?: readonly string[];
11
13
  encounter?: string;
12
14
  requester: string;
13
15
  recipients: readonly string[];
14
16
  informationProviders?: readonly string[];
15
17
  authoredOn?: string;
16
18
  occurrence?: string;
19
+ occurrencePeriodStart?: string;
20
+ occurrencePeriodEnd?: string;
17
21
  payloadReference?: string;
18
22
  payloadAttachmentContentType?: string;
19
23
  payloadAttachmentData?: string;
@@ -45,10 +49,22 @@ export declare const FHIR_COMMUNICATION_REQUEST_FORM_SCHEMA: Readonly<{
45
49
  type: "string";
46
50
  enum: readonly ["routine", "urgent", "asap", "stat"];
47
51
  }>;
52
+ category: Readonly<{
53
+ type: "string";
54
+ pattern: "^https?://[^|,]+\\|[^|,]+$";
55
+ }>;
48
56
  subject: Readonly<{
49
57
  type: "string";
50
58
  pattern: "^[A-Za-z][A-Za-z0-9]+/[A-Za-z0-9.-]{1,64}$";
51
59
  }>;
60
+ about: Readonly<{
61
+ type: "array";
62
+ uniqueItems: true;
63
+ items: Readonly<{
64
+ type: "string";
65
+ pattern: "^[A-Za-z][A-Za-z0-9]+/[A-Za-z0-9.-]{1,64}$";
66
+ }>;
67
+ }>;
52
68
  encounter: Readonly<{
53
69
  type: "string";
54
70
  pattern: "^[A-Za-z][A-Za-z0-9]+/[A-Za-z0-9.-]{1,64}$";
@@ -82,6 +98,14 @@ export declare const FHIR_COMMUNICATION_REQUEST_FORM_SCHEMA: Readonly<{
82
98
  type: "string";
83
99
  format: "date-time";
84
100
  }>;
101
+ occurrencePeriodStart: Readonly<{
102
+ type: "string";
103
+ format: "date-time";
104
+ }>;
105
+ occurrencePeriodEnd: Readonly<{
106
+ type: "string";
107
+ format: "date-time";
108
+ }>;
85
109
  payloadReference: Readonly<{
86
110
  type: "string";
87
111
  pattern: "^[A-Za-z][A-Za-z0-9]+/[A-Za-z0-9.-]{1,64}$";
@@ -20,10 +20,14 @@ export const FHIR_COMMUNICATION_REQUEST_FORM_SCHEMA = Object.freeze({
20
20
  status: Object.freeze({ type: 'string', enum: CommunicationRequestStatuses }),
21
21
  intent: Object.freeze({ type: 'string', enum: CommunicationRequestIntents }),
22
22
  priority: Object.freeze({ type: 'string', enum: CommunicationRequestPriorities }),
23
- subject: referenceSchema(), encounter: referenceSchema(), requester: referenceSchema(),
23
+ category: codingSchema(),
24
+ subject: referenceSchema(),
25
+ about: Object.freeze({ type: 'array', uniqueItems: true, items: referenceSchema() }),
26
+ encounter: referenceSchema(), requester: referenceSchema(),
24
27
  recipients: Object.freeze({ type: 'array', minItems: 1, uniqueItems: true, items: referenceSchema() }),
25
28
  informationProviders: Object.freeze({ type: 'array', uniqueItems: true, items: referenceSchema() }),
26
- authoredOn: dateTimeSchema(), occurrence: dateTimeSchema(), payloadReference: referenceSchema(),
29
+ authoredOn: dateTimeSchema(), occurrence: dateTimeSchema(),
30
+ occurrencePeriodStart: dateTimeSchema(), occurrencePeriodEnd: dateTimeSchema(), payloadReference: referenceSchema(),
27
31
  payloadAttachmentContentType: Object.freeze({ type: 'string', minLength: 1 }),
28
32
  payloadAttachmentData: Object.freeze({ type: 'string', minLength: 1, contentEncoding: 'base64' }),
29
33
  payloadAttachmentTitle: Object.freeze({ type: 'string', minLength: 1 }),
@@ -38,21 +42,34 @@ export function communicationRequestFormDataToFlatClaims(input) {
38
42
  const recipients = references(input.recipients, 'communication_request_recipient_required');
39
43
  const providers = input.informationProviders?.length
40
44
  ? references(input.informationProviders, 'communication_request_information_provider_invalid') : undefined;
45
+ const about = input.about?.length ? references(input.about, 'communication_request_about_invalid') : undefined;
41
46
  if (input.payloadAttachmentData && !input.payloadAttachmentContentType) {
42
47
  throw new TypeError('communication_request_attachment_content_type_required');
43
48
  }
49
+ const occurrence = dateTime(input.occurrence);
50
+ const occurrencePeriodStart = dateTime(input.occurrencePeriodStart);
51
+ const occurrencePeriodEnd = dateTime(input.occurrencePeriodEnd);
52
+ if (occurrence && (occurrencePeriodStart || occurrencePeriodEnd))
53
+ throw new TypeError('communication_request_occurrence_choice_invalid');
54
+ if (occurrencePeriodStart && occurrencePeriodEnd && Date.parse(occurrencePeriodEnd) <= Date.parse(occurrencePeriodStart)) {
55
+ throw new TypeError('communication_request_occurrence_period_invalid');
56
+ }
44
57
  return compact({
45
58
  [CommunicationRequestClaim.Identifier]: optional(input.identifier),
46
59
  [CommunicationRequestClaim.Status]: input.status,
47
60
  [CommunicationRequestClaim.Intent]: input.intent,
48
61
  [CommunicationRequestClaim.Priority]: optional(input.priority),
62
+ [CommunicationRequestClaim.Category]: coding(input.category),
49
63
  [CommunicationRequestClaim.Subject]: reference(input.subject, 'communication_request_subject_invalid'),
64
+ [CommunicationRequestClaim.About]: about?.join(','),
50
65
  [CommunicationRequestClaim.Encounter]: input.encounter ? reference(input.encounter, 'communication_request_encounter_invalid') : undefined,
51
66
  [CommunicationRequestClaim.Requester]: reference(input.requester, 'communication_request_requester_invalid'),
52
67
  [CommunicationRequestClaim.Recipient]: recipients.join(','),
53
68
  [CommunicationRequestClaim.InformationProvider]: providers?.join(','),
54
69
  [CommunicationRequestClaim.AuthoredOn]: dateTime(input.authoredOn),
55
- [CommunicationRequestClaim.Occurrence]: dateTime(input.occurrence),
70
+ [CommunicationRequestClaim.Occurrence]: occurrence,
71
+ [CommunicationRequestClaim.OccurrencePeriodStart]: occurrencePeriodStart,
72
+ [CommunicationRequestClaim.OccurrencePeriodEnd]: occurrencePeriodEnd,
56
73
  [CommunicationRequestClaim.PayloadReference]: input.payloadReference ? reference(input.payloadReference, 'communication_request_payload_reference_invalid') : undefined,
57
74
  [CommunicationRequestClaim.PayloadAttachmentContentType]: optional(input.payloadAttachmentContentType),
58
75
  [CommunicationRequestClaim.PayloadAttachmentData]: optional(input.payloadAttachmentData),
@@ -64,10 +81,14 @@ export function communicationRequestFlatClaimsToFormData(claims) {
64
81
  return compact({
65
82
  identifier: claims[CommunicationRequestClaim.Identifier], status: claims[CommunicationRequestClaim.Status],
66
83
  intent: claims[CommunicationRequestClaim.Intent], priority: claims[CommunicationRequestClaim.Priority],
67
- subject: claims[CommunicationRequestClaim.Subject], encounter: claims[CommunicationRequestClaim.Encounter],
84
+ category: claims[CommunicationRequestClaim.Category],
85
+ subject: claims[CommunicationRequestClaim.Subject], about: splitOptional(claims[CommunicationRequestClaim.About]),
86
+ encounter: claims[CommunicationRequestClaim.Encounter],
68
87
  requester: claims[CommunicationRequestClaim.Requester], recipients: split(claims[CommunicationRequestClaim.Recipient]),
69
88
  informationProviders: splitOptional(claims[CommunicationRequestClaim.InformationProvider]),
70
89
  authoredOn: claims[CommunicationRequestClaim.AuthoredOn], occurrence: claims[CommunicationRequestClaim.Occurrence],
90
+ occurrencePeriodStart: claims[CommunicationRequestClaim.OccurrencePeriodStart],
91
+ occurrencePeriodEnd: claims[CommunicationRequestClaim.OccurrencePeriodEnd],
71
92
  payloadReference: claims[CommunicationRequestClaim.PayloadReference],
72
93
  payloadAttachmentContentType: claims[CommunicationRequestClaim.PayloadAttachmentContentType],
73
94
  payloadAttachmentData: claims[CommunicationRequestClaim.PayloadAttachmentData],
@@ -75,12 +96,15 @@ export function communicationRequestFlatClaimsToFormData(claims) {
75
96
  });
76
97
  }
77
98
  function referenceSchema() { return Object.freeze({ type: 'string', pattern: '^[A-Za-z][A-Za-z0-9]+/[A-Za-z0-9.-]{1,64}$' }); }
99
+ function codingSchema() { return Object.freeze({ type: 'string', pattern: '^https?://[^|,]+\\|[^|,]+$' }); }
78
100
  function dateTimeSchema() { return Object.freeze({ type: 'string', format: 'date-time' }); }
79
101
  function optional(value) { const text = String(value ?? '').trim(); return text || undefined; }
80
102
  function dateTime(value) { const text = optional(value); if (text && Number.isNaN(Date.parse(text)))
81
103
  throw new TypeError('communication_request_datetime_invalid'); return text; }
82
104
  function reference(value, error) { const text = optional(value); if (!text || !/^[A-Za-z][A-Za-z0-9]+\/[A-Za-z0-9.-]{1,64}$/u.test(text))
83
105
  throw new TypeError(error); return text; }
106
+ function coding(value) { const text = optional(value); if (text && !/^https?:\/\/[^|,]+\|[^|,]+$/u.test(text))
107
+ throw new TypeError('communication_request_category_invalid'); return text; }
84
108
  function references(values, error) { if (!Array.isArray(values) || !values.length)
85
109
  throw new TypeError(error); const result = [...new Set(values.map(value => reference(value, error)))]; if (result.length !== values.length)
86
110
  throw new TypeError(error); return result; }
@@ -8,7 +8,7 @@ export type FhirSearchParameter = Readonly<{
8
8
  system?: string;
9
9
  unit?: string;
10
10
  }>;
11
- /** Parses one typed FHIR query value without the lossy integer coercion of the retired UHC helper. */
11
+ /** Parses one typed FHIR query value without lossy integer coercion. */
12
12
  export declare function parseFhirSearchParameter(input: Readonly<{
13
13
  name: string;
14
14
  type: FhirSearchParameterType;
@@ -1,5 +1,5 @@
1
1
  const PREFIX = /^(eq|ne|gt|lt|ge|le|sa|eb|ap)/;
2
- /** Parses one typed FHIR query value without the lossy integer coercion of the retired UHC helper. */
2
+ /** Parses one typed FHIR query value without lossy integer coercion. */
3
3
  export function parseFhirSearchParameter(input) {
4
4
  const name = required(input.name, 'fhir_search_name_required');
5
5
  const raw = required(input.value, 'fhir_search_value_required');
@@ -1,4 +1,4 @@
1
- /** Type-only DICOMDIR tree contract recovered from the retired UHC package. */
1
+ /** Type-only DICOMDIR tree contract. */
2
2
  export interface DicomDirectoryNode {
3
3
  id: string;
4
4
  uid: string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Resource-specific search models recovered from the original UHC package.
2
+ * Resource-specific FHIR search models.
3
3
  * Runtime parsing and serialization use `fhir-search-parameters`; these are
4
4
  * compile-time compatibility surfaces, not a second query implementation.
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Resource-specific search models recovered from the original UHC package.
2
+ * Resource-specific FHIR search models.
3
3
  * Runtime parsing and serialization use `fhir-search-parameters`; these are
4
4
  * compile-time compatibility surfaces, not a second query implementation.
5
5
  */
@@ -1,7 +1,7 @@
1
1
  # Care, emergency and population-alert FHIR contracts
2
2
 
3
- This package owns only the product-neutral FHIR representation used by the
4
- three SOSChain journeys that are first proven in VetChain and PetChain.
3
+ This package owns only the product-neutral FHIR representation used by three
4
+ care and emergency journeys.
5
5
  Authorization, controller discovery, encryption, transport, storage and UI
6
6
  policy remain in their owning product or service layers.
7
7
 
@@ -24,19 +24,14 @@ The layer split is:
24
24
  fhir-data-utils-ts
25
25
  canonical claims + R4/R5/R6 import/export
26
26
  ↓
27
- sos-data-utils-ts
28
- product-neutral multichannel field schemas
29
- ↓
30
- sos-sdk-core-ts / sos-sdk-node-ts
31
- shared orchestration and runtime adapters
32
- ↓
33
- vet-* / uhc-*
34
- product forms, portal editors and telephone/chat dialogue
27
+ downstream libraries
28
+ multichannel schemas, mutable editors, orchestration and product policy
35
29
  ```
36
30
 
37
- When Vet or UHC adds a visual, chat or telephone editor for one of these
38
- fields, it must consume the canonical claim key from this package. It must not
39
- maintain a second literal, converter or incompatible getter/setter model.
31
+ When a downstream application adds a visual, chat or telephone editor for one
32
+ of these fields, it must consume the canonical claim key from this package. It
33
+ must not maintain a second literal, converter or incompatible getter/setter
34
+ model.
40
35
 
41
36
  The mutable GDC Bundle API remains documented separately in
42
37
  [`gdc-common-utils-ts`](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/docs/101-CLINICAL-ENTRY-GETTERS-SETTERS.md)
@@ -1,9 +1,9 @@
1
1
  # Communication Bundle contract
2
2
 
3
3
  Every cross-participant clinical or administrative exchange uses the same
4
- two-level FHIR envelope. This is the portable contract for Vet, UHC and
5
- SOSChain channels; HTTP, DIDComm, encryption and recipient resolution remain
6
- in their owning SDK or service.
4
+ two-level FHIR envelope. This is the portable contract for downstream
5
+ channels; HTTP, DIDComm, encryption and recipient resolution remain in their
6
+ owning library or service.
7
7
 
8
8
  ```text
9
9
  outer Bundle (type=batch)
@@ -45,10 +45,9 @@ The validator fails closed when the outer resource is not a batch Bundle, an out
45
45
  batch entry lacks its request, the Communication has anything other than one
46
46
  FHIR Bundle attachment, or the inner Bundle violates its declared type.
47
47
 
48
- `sos-sdk-core` owns the high-level Bundle and Communication editor/reader used
49
- by SOS, Vet and UHC. `sos-data-utils` owns its cross-sector input contracts and
50
- fixtures. The historical `gdc-*` editors remain compatibility surfaces until
51
- their consumers migrate; this package neither imports nor wraps them.
48
+ Downstream libraries own high-level Bundle and Communication editors/readers,
49
+ cross-sector input contracts, fixtures, transport and product policy. This
50
+ package neither imports nor wraps those consumer surfaces.
52
51
 
53
52
  At the GW persistence boundary, the validated native resources are projected
54
53
  to independent `resource.meta.claims` rows. Direct flat-claim CRUD is an
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fhir-data-utils-ts",
3
- "version": "0.6.20",
3
+ "version": "0.6.22",
4
4
  "description": "FHIR-version-neutral flat-claim and SearchParameter data utilities",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,181 +0,0 @@
1
- # UHC FHIR utilities migration
2
-
3
- `uhc-fhir-utils-typescript` is an archaeological source, not a dependency of
4
- this package. Its useful behavior must be recovered behind neutral, typed and
5
- version-explicit contracts. Its product branding, obsolete security choices
6
- and transport assumptions must not be copied.
7
-
8
- The wider historical lineage began with early helpers in
9
- `uhc-helpers-typescript`, was then split and improved across
10
- `uhc-common-models-typescript`, `uhc-common-utils-typescript`,
11
- `data-utils-typescript` and the crypto utilities, and later contributed to
12
- `gdc-common-utils-ts`. For SHC and DGC, the later split packages are the
13
- preferred behavioural and type evidence; the early helper remains UI-flow
14
- evidence only.
15
-
16
- ## Migration rules
17
-
18
- 1. Add the smallest failing contract test here before porting behavior.
19
- 2. Use official FHIR R4/R5/R6 structures at import/export boundaries and flat
20
- claims as the version-neutral representation between those boundaries.
21
- 3. Copy no raw claim, terminology, identifier or profile literal into a
22
- consumer. Define it once in its owning neutral or sector data package.
23
- 4. Preserve historical inputs with explicit read adapters. New writers emit
24
- only the current canonical representation.
25
- 5. Do not change `gdc-*` while those repositories are frozen. They are
26
- read-only compatibility evidence until every consumer uses this package.
27
-
28
- ## Reuse, rewrite or retire
29
-
30
- | Old area | Decision | Current destination or gate |
31
- | --- | --- | --- |
32
- | `Bundle` and `Composition` traversal | Rewrite from tests | Neutral graph and explicit R4/R5/R6 adapters; preserve author, attester and section boundaries |
33
- | `gdc-common-utils-ts` Communication attached-Bundle editor/reader | Preserve as read-only migration evidence | Neutral validation/projection tools live here; the replacement high-level editor/reader belongs to `sos-sdk-core`, with shared input contracts in `sos-data-utils` |
34
- | `CodeableConcept`, `Identifier`, `Reference`, `Quantity` | Rewrite and reuse behavior | Small typed value modules with round-trip tests |
35
- | `Observation` and resource parameter models | Rewrite | Canonical claim catalogs plus version-specific projection adapters |
36
- | `Parameters` conversion | Rewrite | A typed FHIR Parameters module; never a transport-specific envelope |
37
- | Anonymization field inventory | Test input only | A separately reviewed policy contract; old field lists do not become policy automatically |
38
- | ATC, LOINC, SNOMED and EMA JSON | Validate before reuse | Terminology service fixtures, never authoritative embedded catalogs |
39
- | DICOM parsing | Extract later | Separate imaging package; do not add `dicom-parser` to the neutral core |
40
- | ImagingStudy metadata | Rewrite | Neutral flat claims and R4 projection for subject, encounter, modality, procedure and protected endpoint references; DICOM bytes remain outside |
41
- | SHA-1 attachment hashing | Retire | No compatibility writer; historical read/verification belongs in a versioned adapter |
42
- | `MessageHeader` transport conventions | Retire | Transport is outside the FHIR data contract |
43
- | UHC-branded templates and defaults | Retire or move downstream | Product policy belongs in UHC packages, not here |
44
- | Babel/Yarn/Jest build plumbing and broad `any` models | Retire | Node 24, TypeScript strict mode and `node:test` |
45
-
46
- ## Completed source inventory
47
-
48
- | Source | Migrated here | Deliberately remains outside |
49
- | --- | --- | --- |
50
- | `gdc-common-utils-ts` interoperable claims | All product-neutral claim catalogs, specs and claim helpers, including Consent and the complete SOS Task surface | Schema.org/Gaia-X mapping and runtime policy |
51
- | `gdc-common-utils-ts` converters | Every product-neutral R4/R5 converter present there; Observation component writing was corrected to use a resource graph | Network, persistence and authorization orchestration |
52
- | `uhc-fhir-utils-typescript` models | ATC/FHIR/LOINC/SNOMED model types, all resource search parameter enums and template interfaces, strict replacement FHIR primitives | Embedded terminology JSON is not authoritative data |
53
- | `uhc-fhir-utils-typescript` helpers | Coding/token, Identifier, Reference, Quantity, telecom, RelatedPerson search projection and non-mutating Bundle traversal/classification/reference lookup | Message transport, SHA-1 writers, COVID-era product presets and mutation-heavy manager facades |
54
- | Later UHC data/common packages | SHC and EU DGC QR prefix classification plus repository-safe examples | Signature verification, issuer trust, credential import and authorization remain separate future contracts |
55
- | `uhc-fhir-utils-typescript` imaging | DICOMDIR structural types only | Byte parsing and pixel extraction require a separately versioned imaging package |
56
- | `sos-data-utils-ts` | Cross-sector terminology contract and fixtures, Immunization graph and reminder planning | SOS authorization, repository and emergency policy |
57
- | `vet-data-utils-ts` | Product-neutral FHIR R5 Group and ResearchStudy contracts/search projections | Animal identity, veterinary sections, species, insurance, payment and other Vet policy |
58
- | Provider catalogs | R4 HealthcareService claims/converter, geocoded Location claims, one-department professional Group normalization and reviewed C80-to-LOINC candidate mappings | Schema.org images/logo storage, marketplace sector policy, endpoint authorization and communication routing |
59
- | UHC/Vet SDKs | No transport client was copied | SMART clients, Oracle clients, BFF calls and product workflows remain SDK responsibilities |
60
-
61
- The frozen `gdc-*` implementation remains compatibility evidence. New Vet,
62
- UHC and SOS code uses the replacement high-level API in `sos-sdk-core`, which
63
- composes the neutral tools exported here. Neither shared replacement package
64
- imports `gdc-*`. DIDComm wrapping, post-quantum encryption, endpoint selection
65
- and delivery retries remain in the SDK/service layer.
66
-
67
- The migration surface is executable rather than documentary only:
68
-
69
- - every migrated claim family has a named catalog and shared list helpers;
70
- - every migrated product-neutral converter participates in the converter
71
- round-trip matrix, with rich optional-field fixtures for complex resources;
72
- - Encounter retains its originating Appointment and ImagingStudy retains
73
- searchable metadata without embedding DICOM object bytes;
74
- - FHIR primitives, typed searches, Bundle traversal and fallback conversion
75
- have strict success and rejection tests;
76
- - Communication exchange is authored and read only as outer batch Bundle to
77
- Communication to inner document/batch/collection Bundle, with files modeled
78
- as inner DocumentReference resources;
79
- - Immunization graphs and reminders live once under `src/builders/`; the former
80
- root modules are deprecated re-export shims;
81
- - Group and ResearchStudy expose named claim constants, preserve member/party
82
- correlation as separate rows and reject authorization-shaped data;
83
- - Organization departments remain distinct from offered HealthcareServices;
84
- every independently schedulable service keeps its own stable identity and
85
- Location references, while professional Group ownership remains explicit;
86
- - `buildServiceScheduleFlatGraph(...)` and the R5 Schedule/Slot adapters keep
87
- per-service planning horizons and slot intervals shared between Vet and UHC;
88
- - schedulable child Locations preserve `partOf`, while Schedule actors can
89
- combine the service with Location, Device and PractitionerRole capacity;
90
- and
91
- - `npm run check` enforces minimum line, function and branch coverage across
92
- converters, builders and core FHIR modules.
93
-
94
- This inventory is a boundary decision, not permission to leave another copy in
95
- new code. Consumers must move to this package bottom-up after its immutable
96
- release. Existing packages may retain compatibility re-exports only during
97
- that promotion.
98
-
99
- ## Observation and component contract
100
-
101
- FHIR blood pressure is one native `Observation` with systolic and diastolic
102
- entries in `Observation.component[]`. Canonical flat storage is instead a
103
- resource graph: the primary Observation owns `Observation.has-member`, and
104
- each referenced reduced Observation owns its identifier, subject, category,
105
- date, component code, numeric value and unit. Reduced entries deliberately
106
- carry no `Observation.status`, so normal top-level listings ignore them while
107
- component-level indexes can query their ordinary Observation claims.
108
-
109
- The R4/R5 export adapter resolves the primary entry's members and reconstructs
110
- one native `Observation.component[]`; it does not export three independent
111
- native Observations. Component entries do not use `is-contained`,
112
- `contained-parent-reference` or `contained-reference-list`.
113
-
114
- Diagnostic laboratory authoring is owned here by
115
- `buildDiagnosticReportFlatGraph(...)`: `DiagnosticReport.code` identifies the
116
- report/panel, independent Observation entries carry atomic results, and
117
- independent DocumentReference entries carry PDF/image evidence. Sector roles,
118
- confirmation, ledger eligibility, consent and research policy remain outside
119
- this package.
120
-
121
- Aligned JSON arrays and the older comma-separated `Observation.component-*`
122
- claims are accepted only by the migration reader. No current writer emits
123
- either representation.
124
-
125
- The historical `Observation.bp-systolic-number` and
126
- `Observation.bp-diastolic-number` fields are accepted only when reading data
127
- that has no component graph or historical component claims. New writers and
128
- generic component indexes never emit them. This prevents a new custom claim
129
- family for every assessment scale.
130
-
131
- ## Consumer promotion sequence
132
-
133
- 1. Release this package, pin the immutable version in the lowest downstream
134
- package, and run that consumer's complete local matrix.
135
- 2. Remove the corresponding frozen dependency only after no consumer imports
136
- that family from it.
137
- 3. Migrate imaging/DICOM byte parsing and terminology payloads as separate changes because
138
- they have different dependencies, security rules and release gates.
139
-
140
- ## Ownership map for new work
141
-
142
- | Change | Owning surface |
143
- | --- | --- |
144
- | Add or rename a flat storage key | `src/claims/<resource>-claims.ts` |
145
- | Convert a native R4/R5 resource | `src/converters/convert-<resource>.ts` |
146
- | Author several correlated resources | `src/builders/` |
147
- | Preserve an obsolete input | Explicit read-only compatibility adapter |
148
- | Add transport, SMART or persistence behavior | Owning SDK/service, never this package |
149
- | Add sector terminology or policy | SOS, Vet or UHC data package |
150
- | Configure an R5 SubscriptionTopic/Subscription | Native builder and official search catalog in `subscription-r5`; registration remains in the owning service |
151
- | Persist or deliver a subscription event | GW/BFF transport and storage, never this package |
152
-
153
- Converters and builders import catalog symbols. Tests may spell native FHIR
154
- wire fields, but do not duplicate flat-claim paths except when proving that a
155
- malformed or invented path is rejected.
156
-
157
- ## Schema-driven editor boundary
158
-
159
- `clinicalArtifactFhirR4ToFlat(...)` and
160
- `clinicalArtifactEditorClaimsFromFhirR4(...)` are import/read adapters. They
161
- turn a native FHIR R4 clinical resource into canonical flat claims and never
162
- own a portal form, authorization or transport decision.
163
-
164
- The cross-channel JSON Schema and the `BundleEditor`/`BundleReader` round trip
165
- belong to `sos-data-utils-ts`. A shared React component consumes both layers:
166
- FHIR conversion for native imports, then the SOS JSON Schema to select and
167
- validate editable values and create the claims-first changes Bundle. Portals
168
- must not add resource-specific claim mappers between those shared layers.
169
-
170
- ## Subscription notification boundary
171
-
172
- FHIR R5 `SubscriptionTopic` and `Subscription` are native configuration
173
- resources, not flat clinical persistence. This package owns their strict typed
174
- builders, the official R5 resource-specific SearchParameter catalogs and the
175
- parser for `Bundle[type=subscription-notification]`. It also owns the minimal
176
- claims-first terminal Task `Communication` shape so producers and receivers do
177
- not duplicate claim paths.
178
-
179
- HTTP delivery, authentication, durable outbox/inbox storage and browser or
180
- email/push fan-out remain in GW, DataConv and the product BFFs. The neutral
181
- package contains no HTTP client and no gateway policy.