vet-data-utils-ts 0.6.0 → 0.7.1

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.
package/README.md CHANGED
@@ -26,9 +26,10 @@ The product-local `place-service-directory` export models persistent public
26
26
  departments as claims-only Schema.org `Organization` resources, their sites as
27
27
  `Place`, and their offerings as `Service` or `Product`. A department has one
28
28
  parent legal organization, one or more Places and several offered Services.
29
- `projectDepartmentToFhirR5HealthcareService(...)` aggregates those Services
30
- into exactly one stable FHIR `HealthcareService` per department; an offered
31
- Service is not a second department or communication sender. A care organization
29
+ Each offered Service projects through
30
+ `projectServiceToFhirR5HealthcareService(...)` to its own stable FHIR
31
+ `HealthcareService`; the department remains the provider Organization and
32
+ communication sender. The old aggregate department projection fails closed. A care organization
32
33
  publishes services; an insurance organization publishes products in a
33
34
  Schema.org `OfferCatalog`. Logos, images, accessibility, coordinates, address
34
35
  and opening-hours specifications remain neutral indexed claims. Native FHIR R5
@@ -126,6 +127,25 @@ and applies weekly or monthly-ordinal exceptions that restrict or extend one or
126
127
  more consultation Locations. It converts clinic-local times through an IANA
127
128
  time zone, including daylight-saving changes.
128
129
 
130
+ Every Schedule identifies exactly one HealthcareService and the resources that
131
+ must be available together. A child Location is the independently reservable
132
+ capacity unit; `PractitionerRole` and `Device` actors are optional and included
133
+ only when that professional or particular machine constrains capacity. Slot duration comes only from its start/end instants.
134
+ Schedule and Slot business identity is independent from those mutable actors.
135
+ Callers supply one canonical service-owned Schedule URN for each reservable
136
+ Location. `Schedule.identifier` preserves that URN and `Schedule.id` is its
137
+ SHA3-256 base58btc multihash. `Slot.identifier` appends one
138
+ `<startEpochSeconds>-<endEpochSeconds>` interval segment and `Slot.id` is
139
+ derived the same way. Do not concatenate practitioner, Location, Device or DID
140
+ values into either identity.
141
+ Reservable rooms, booths, chairs and beds are child Locations; machines remain
142
+ Devices. The neutral builders and canonical Schedule/Slot claim catalogs come
143
+ from `fhir-data-utils-ts` rather than being duplicated here.
144
+
145
+ An equipment failure is not itself an appointment cancellation. The equipment
146
+ record captures the fault or availability change; scheduling separately marks
147
+ only the affected future Slots unavailable and reconciles their appointments.
148
+
129
149
  The official searchable facts remain resource-qualified flat claims. The
130
150
  exported `VeterinarySlotSearchParameters` catalogue identifies the supported
131
151
  FHIR R5 Slot query codes; generated resources include `Slot.identifier`,
@@ -0,0 +1,182 @@
1
+ export declare const VeterinaryAnimalIdentityCredentialTypes: Readonly<{
2
+ Assurance: "VeterinaryAnimalIdentityAssuranceCredential";
3
+ }>;
4
+ export type VeterinaryAnimalControllerRelationship = 'owner' | 'caregiver';
5
+ export type VeterinaryIdentityDocumentDigest = Readonly<{
6
+ contentType: 'application/pdf' | 'image/jpeg' | 'image/png';
7
+ /** SHA3-384 multihash encoded as base58btc. Raw document bytes never belong in the credential. */
8
+ sha3_384: string;
9
+ }>;
10
+ export type VeterinaryAnimalIdentityAssuranceCredential = Readonly<{
11
+ '@context': readonly ['https://www.w3.org/ns/credentials/v2', 'https://schema.org'];
12
+ id: string;
13
+ type: readonly ['VerifiableCredential', 'VeterinaryAnimalIdentityAssuranceCredential'];
14
+ issuer: string;
15
+ validFrom: string;
16
+ validUntil: string;
17
+ credentialSubject: Readonly<{
18
+ id: string;
19
+ '@type': 'Person';
20
+ holdsAnimalIdentity: Readonly<{
21
+ id: string;
22
+ '@type': 'Animal';
23
+ name: string;
24
+ species: Readonly<{
25
+ '@type': 'Taxon';
26
+ taxonRank: 'species';
27
+ identifier: string;
28
+ }>;
29
+ identifier: Readonly<{
30
+ '@type': 'PropertyValue';
31
+ propertyID: string;
32
+ value: string;
33
+ }>;
34
+ }>;
35
+ relationshipToAnimal: VeterinaryAnimalControllerRelationship;
36
+ verified_claims: Readonly<{
37
+ verification: Readonly<{
38
+ trust_framework: 'veterinarian_in_person_document_check';
39
+ time: string;
40
+ verification_process: string;
41
+ evidence: readonly [
42
+ Readonly<{
43
+ type: 'document';
44
+ method: 'physical_in_person_check';
45
+ verifier: Readonly<{
46
+ organization: string;
47
+ professional: string;
48
+ role: 'ISCO-08|2250';
49
+ }>;
50
+ time: string;
51
+ check_details: readonly [Readonly<{
52
+ check_method: 'physical_in_person_check';
53
+ time: string;
54
+ }>];
55
+ attachments: Readonly<{
56
+ content_type: string;
57
+ digest: Readonly<{
58
+ alg: 'sha3-384';
59
+ value: string;
60
+ }>;
61
+ }>;
62
+ }>
63
+ ];
64
+ }>;
65
+ claims: Readonly<{
66
+ name: string;
67
+ identifier: Readonly<{
68
+ type: string;
69
+ value: string;
70
+ country: string;
71
+ }>;
72
+ }>;
73
+ }>;
74
+ }>;
75
+ evidence: readonly [
76
+ Readonly<{
77
+ type: 'VeterinaryAnimalIdentityEvidence';
78
+ verifier: string;
79
+ time: string;
80
+ content_type: string;
81
+ digest: Readonly<{
82
+ alg: 'sha3-384';
83
+ value: string;
84
+ }>;
85
+ }>,
86
+ Readonly<{
87
+ type: 'AnimalControllerRelationshipEvidence';
88
+ relationship: VeterinaryAnimalControllerRelationship;
89
+ verifier: string;
90
+ time: string;
91
+ content_type: string;
92
+ digest: Readonly<{
93
+ alg: 'sha3-384';
94
+ value: string;
95
+ }>;
96
+ }>
97
+ ];
98
+ credentialStatus: Readonly<{
99
+ id: string;
100
+ type: 'SimpleCredentialStatus2026';
101
+ }>;
102
+ proof?: unknown;
103
+ }>;
104
+ export type VeterinaryAnimalIdentityAssuranceInput = Readonly<{
105
+ credentialId: string;
106
+ issuerDid: string;
107
+ veterinarianOrganizationId: string;
108
+ veterinarianRole: 'ISCO-08|2250';
109
+ holderDid: string;
110
+ validFrom: string;
111
+ validUntil: string;
112
+ animal: Readonly<{
113
+ id: string;
114
+ name: string;
115
+ speciesTaxonomyId: string;
116
+ identifier: Readonly<{
117
+ system: string;
118
+ value: string;
119
+ }>;
120
+ }>;
121
+ controller: Readonly<{
122
+ id: string;
123
+ legalName: string;
124
+ identifier: Readonly<{
125
+ type: string;
126
+ value: string;
127
+ country: string;
128
+ }>;
129
+ }>;
130
+ relationship: VeterinaryAnimalControllerRelationship;
131
+ evidence: Readonly<{
132
+ animalDocument: VeterinaryIdentityDocumentDigest;
133
+ controllerDocument: VeterinaryIdentityDocumentDigest;
134
+ relationshipDocument: VeterinaryIdentityDocumentDigest;
135
+ checkedAt: string;
136
+ }>;
137
+ }>;
138
+ /**
139
+ * Builds the unsigned W3C VC 2.0 payload attested by a veterinarian.
140
+ *
141
+ * OpenID Identity Assurance `verified_claims` is deliberately scoped to the
142
+ * natural person. Animal identity and the owner/caregiver relationship use
143
+ * separate veterinary evidence entries. All physical files are hash-only.
144
+ */
145
+ export declare function buildVeterinaryAnimalIdentityAssuranceCredential(input: VeterinaryAnimalIdentityAssuranceInput): VeterinaryAnimalIdentityAssuranceCredential;
146
+ /** Validates the governed credential shape before wallet storage or presentation. */
147
+ export declare function parseVeterinaryAnimalIdentityAssuranceCredential(value: unknown): VeterinaryAnimalIdentityAssuranceCredential;
148
+ /** Builds the bounded OpenID4VP 1.0 request used by airline, police and insurer verifiers. */
149
+ export declare function buildVeterinaryAnimalIdentityOpenId4VpRequest(input: Readonly<{
150
+ clientId: string;
151
+ responseUri: string;
152
+ nonce: string;
153
+ state: string;
154
+ animalId: string;
155
+ }>): Readonly<{
156
+ client_id: string;
157
+ response_uri: string;
158
+ response_type: "vp_token";
159
+ response_mode: "direct_post";
160
+ nonce: string;
161
+ state: string;
162
+ presentation_definition: Readonly<{
163
+ id: `vetchain-animal-identity-${string}`;
164
+ input_descriptors: readonly Readonly<{
165
+ id: "veterinary-animal-identity-assurance";
166
+ format: Readonly<{
167
+ jwt_vc_json: Readonly<{
168
+ alg: readonly string[];
169
+ }>;
170
+ }>;
171
+ constraints: Readonly<{
172
+ fields: readonly Readonly<{
173
+ path: readonly string[];
174
+ filter: Readonly<{
175
+ type: "string";
176
+ const: string;
177
+ }>;
178
+ }>[];
179
+ }>;
180
+ }>[];
181
+ }>;
182
+ }>;
@@ -0,0 +1,231 @@
1
+ export const VeterinaryAnimalIdentityCredentialTypes = Object.freeze({
2
+ Assurance: 'VeterinaryAnimalIdentityAssuranceCredential',
3
+ });
4
+ function record(value) {
5
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
6
+ }
7
+ function firstRecord(value) {
8
+ return Array.isArray(value) ? value.map(record).find(Boolean) : undefined;
9
+ }
10
+ function required(value, error) {
11
+ const text = typeof value === 'string' ? value.trim() : '';
12
+ if (!text)
13
+ throw new TypeError(error);
14
+ return text;
15
+ }
16
+ function did(value, error) {
17
+ const text = required(value, error);
18
+ if (!text.startsWith('did:'))
19
+ throw new TypeError(error);
20
+ return text;
21
+ }
22
+ function instant(value, error) {
23
+ const text = required(value, error);
24
+ if (!Number.isFinite(Date.parse(text)))
25
+ throw new TypeError(error);
26
+ return text;
27
+ }
28
+ function digest(value, error) {
29
+ const contentType = required(value?.contentType, error);
30
+ if (!['application/pdf', 'image/jpeg', 'image/png'].includes(contentType))
31
+ throw new TypeError(error);
32
+ return Object.freeze({
33
+ contentType: contentType,
34
+ sha3_384: required(value?.sha3_384, error),
35
+ });
36
+ }
37
+ /**
38
+ * Builds the unsigned W3C VC 2.0 payload attested by a veterinarian.
39
+ *
40
+ * OpenID Identity Assurance `verified_claims` is deliberately scoped to the
41
+ * natural person. Animal identity and the owner/caregiver relationship use
42
+ * separate veterinary evidence entries. All physical files are hash-only.
43
+ */
44
+ export function buildVeterinaryAnimalIdentityAssuranceCredential(input) {
45
+ const credentialId = required(input.credentialId, 'animal_identity_credential_id_required');
46
+ const issuerDid = did(input.issuerDid, 'animal_identity_credential_issuer_invalid');
47
+ const holderDid = did(input.holderDid, 'animal_identity_credential_holder_invalid');
48
+ const controllerId = did(input.controller?.id, 'animal_identity_controller_id_invalid');
49
+ if (holderDid !== controllerId)
50
+ throw new TypeError('holder_controller_identity_mismatch');
51
+ const animalId = did(input.animal?.id, 'animal_identity_subject_invalid');
52
+ const validFrom = instant(input.validFrom, 'animal_identity_credential_valid_from_invalid');
53
+ const validUntil = instant(input.validUntil, 'animal_identity_credential_valid_until_invalid');
54
+ if (Date.parse(validFrom) > Date.parse(validUntil))
55
+ throw new TypeError('animal_identity_credential_validity_invalid');
56
+ if (input.relationship !== 'owner' && input.relationship !== 'caregiver') {
57
+ throw new TypeError('animal_controller_relationship_invalid');
58
+ }
59
+ if (input.veterinarianRole !== 'ISCO-08|2250')
60
+ throw new TypeError('veterinarian_role_invalid');
61
+ const organization = required(input.veterinarianOrganizationId, 'veterinarian_organization_required');
62
+ const checkedAt = instant(input.evidence?.checkedAt, 'animal_identity_evidence_time_invalid');
63
+ const animalDocument = digest(input.evidence?.animalDocument, 'animal_identity_evidence_digest_required');
64
+ const controllerDocument = digest(input.evidence?.controllerDocument, 'controller_identity_evidence_digest_required');
65
+ const relationshipDocument = digest(input.evidence?.relationshipDocument, 'relationship_evidence_digest_required');
66
+ const statusId = `${credentialId}#status`;
67
+ return Object.freeze({
68
+ '@context': Object.freeze(['https://www.w3.org/ns/credentials/v2', 'https://schema.org']),
69
+ id: credentialId,
70
+ type: Object.freeze(['VerifiableCredential', VeterinaryAnimalIdentityCredentialTypes.Assurance]),
71
+ issuer: issuerDid,
72
+ validFrom,
73
+ validUntil,
74
+ credentialSubject: Object.freeze({
75
+ id: holderDid,
76
+ '@type': 'Person',
77
+ holdsAnimalIdentity: Object.freeze({
78
+ id: animalId,
79
+ '@type': 'Animal',
80
+ name: required(input.animal?.name, 'animal_identity_name_required'),
81
+ species: Object.freeze({
82
+ '@type': 'Taxon',
83
+ taxonRank: 'species',
84
+ identifier: required(input.animal?.speciesTaxonomyId, 'animal_identity_species_required'),
85
+ }),
86
+ identifier: Object.freeze({
87
+ '@type': 'PropertyValue',
88
+ propertyID: required(input.animal?.identifier?.system, 'animal_identity_identifier_system_required'),
89
+ value: required(input.animal?.identifier?.value, 'animal_identity_identifier_value_required'),
90
+ }),
91
+ }),
92
+ relationshipToAnimal: input.relationship,
93
+ verified_claims: Object.freeze({
94
+ verification: Object.freeze({
95
+ trust_framework: 'veterinarian_in_person_document_check',
96
+ time: checkedAt,
97
+ verification_process: credentialId,
98
+ evidence: Object.freeze([Object.freeze({
99
+ type: 'document',
100
+ method: 'physical_in_person_check',
101
+ verifier: Object.freeze({ organization, professional: issuerDid, role: input.veterinarianRole }),
102
+ time: checkedAt,
103
+ check_details: Object.freeze([Object.freeze({ check_method: 'physical_in_person_check', time: checkedAt })]),
104
+ attachments: Object.freeze({
105
+ content_type: controllerDocument.contentType,
106
+ digest: Object.freeze({ alg: 'sha3-384', value: controllerDocument.sha3_384 }),
107
+ }),
108
+ })]),
109
+ }),
110
+ claims: Object.freeze({
111
+ name: required(input.controller?.legalName, 'controller_identity_name_required'),
112
+ identifier: Object.freeze({
113
+ type: required(input.controller?.identifier?.type, 'controller_identity_identifier_type_required'),
114
+ value: required(input.controller?.identifier?.value, 'controller_identity_identifier_value_required'),
115
+ country: required(input.controller?.identifier?.country, 'controller_identity_identifier_country_required').toUpperCase(),
116
+ }),
117
+ }),
118
+ }),
119
+ }),
120
+ evidence: Object.freeze([
121
+ Object.freeze({
122
+ type: 'VeterinaryAnimalIdentityEvidence',
123
+ verifier: issuerDid,
124
+ time: checkedAt,
125
+ content_type: animalDocument.contentType,
126
+ digest: Object.freeze({ alg: 'sha3-384', value: animalDocument.sha3_384 }),
127
+ }),
128
+ Object.freeze({
129
+ type: 'AnimalControllerRelationshipEvidence',
130
+ relationship: input.relationship,
131
+ verifier: issuerDid,
132
+ time: checkedAt,
133
+ content_type: relationshipDocument.contentType,
134
+ digest: Object.freeze({ alg: 'sha3-384', value: relationshipDocument.sha3_384 }),
135
+ }),
136
+ ]),
137
+ credentialStatus: Object.freeze({ id: statusId, type: 'SimpleCredentialStatus2026' }),
138
+ });
139
+ }
140
+ /** Validates the governed credential shape before wallet storage or presentation. */
141
+ export function parseVeterinaryAnimalIdentityAssuranceCredential(value) {
142
+ const credential = record(value);
143
+ const subject = record(credential?.credentialSubject);
144
+ const animal = record(subject?.holdsAnimalIdentity);
145
+ const humanEvidence = firstRecord(record(record(subject?.verified_claims)?.verification)?.evidence);
146
+ const humanVerifier = record(humanEvidence?.verifier);
147
+ const types = Array.isArray(credential?.type) ? credential.type : [];
148
+ if (!types.includes('VerifiableCredential') || !types.includes(VeterinaryAnimalIdentityCredentialTypes.Assurance)) {
149
+ throw new TypeError('animal_identity_credential_type_invalid');
150
+ }
151
+ const rebuilt = buildVeterinaryAnimalIdentityAssuranceCredential({
152
+ credentialId: required(credential?.id, 'animal_identity_credential_id_required'),
153
+ issuerDid: required(credential?.issuer, 'animal_identity_credential_issuer_invalid'),
154
+ veterinarianOrganizationId: required(humanVerifier?.organization, 'veterinarian_organization_required'),
155
+ veterinarianRole: 'ISCO-08|2250',
156
+ holderDid: required(subject?.id, 'animal_identity_credential_holder_invalid'),
157
+ validFrom: required(credential?.validFrom, 'animal_identity_credential_valid_from_invalid'),
158
+ validUntil: required(credential?.validUntil, 'animal_identity_credential_valid_until_invalid'),
159
+ animal: {
160
+ id: required(animal?.id, 'animal_identity_subject_invalid'),
161
+ name: required(animal?.name, 'animal_identity_name_required'),
162
+ speciesTaxonomyId: required(record(animal?.species)?.identifier, 'animal_identity_species_required'),
163
+ identifier: {
164
+ system: required(record(animal?.identifier)?.propertyID, 'animal_identity_identifier_system_required'),
165
+ value: required(record(animal?.identifier)?.value, 'animal_identity_identifier_value_required'),
166
+ },
167
+ },
168
+ controller: {
169
+ id: required(subject?.id, 'animal_identity_controller_id_invalid'),
170
+ legalName: required(record(record(subject?.verified_claims)?.claims)?.name, 'controller_identity_name_required'),
171
+ identifier: record(record(record(subject?.verified_claims)?.claims)?.identifier),
172
+ },
173
+ relationship: subject?.relationshipToAnimal,
174
+ evidence: parseCredentialEvidence(credential, subject),
175
+ });
176
+ return Object.freeze({ ...rebuilt, ...(credential?.proof !== undefined ? { proof: credential.proof } : {}) });
177
+ }
178
+ function parseCredentialEvidence(credential, subject) {
179
+ const evidence = Array.isArray(credential.evidence) ? credential.evidence.map(record).filter(Boolean) : [];
180
+ const animal = evidence.find(item => item.type === 'VeterinaryAnimalIdentityEvidence');
181
+ const relationship = evidence.find(item => item.type === 'AnimalControllerRelationshipEvidence');
182
+ const human = firstRecord(record(record(subject.verified_claims)?.verification)?.evidence);
183
+ const humanAttachment = record(human?.attachments);
184
+ const humanDigest = record(humanAttachment?.digest);
185
+ const animalDigest = record(animal?.digest);
186
+ const relationshipDigest = record(relationship?.digest);
187
+ return {
188
+ animalDocument: {
189
+ contentType: animal?.content_type,
190
+ sha3_384: required(animalDigest?.value, 'animal_identity_evidence_digest_required'),
191
+ },
192
+ controllerDocument: {
193
+ contentType: humanAttachment?.content_type,
194
+ sha3_384: required(humanDigest?.value, 'controller_identity_evidence_digest_required'),
195
+ },
196
+ relationshipDocument: {
197
+ contentType: relationship?.content_type,
198
+ sha3_384: required(relationshipDigest?.value, 'relationship_evidence_digest_required'),
199
+ },
200
+ checkedAt: required(animal?.time, 'animal_identity_evidence_time_invalid'),
201
+ };
202
+ }
203
+ /** Builds the bounded OpenID4VP 1.0 request used by airline, police and insurer verifiers. */
204
+ export function buildVeterinaryAnimalIdentityOpenId4VpRequest(input) {
205
+ const clientId = required(input.clientId, 'animal_identity_verifier_client_id_required');
206
+ const responseUri = required(input.responseUri, 'animal_identity_verifier_response_uri_required');
207
+ const nonce = required(input.nonce, 'animal_identity_presentation_nonce_required');
208
+ const state = required(input.state, 'animal_identity_presentation_state_required');
209
+ const animalId = did(input.animalId, 'animal_identity_subject_invalid');
210
+ return Object.freeze({
211
+ client_id: clientId,
212
+ response_uri: responseUri,
213
+ response_type: 'vp_token',
214
+ response_mode: 'direct_post',
215
+ nonce,
216
+ state,
217
+ presentation_definition: Object.freeze({
218
+ id: `vetchain-animal-identity-${state}`,
219
+ input_descriptors: Object.freeze([Object.freeze({
220
+ id: 'veterinary-animal-identity-assurance',
221
+ format: Object.freeze({ jwt_vc_json: Object.freeze({ alg: Object.freeze(['ES256', 'ML-DSA-65']) }) }),
222
+ constraints: Object.freeze({
223
+ fields: Object.freeze([
224
+ Object.freeze({ path: Object.freeze(['$.type']), filter: Object.freeze({ type: 'string', const: VeterinaryAnimalIdentityCredentialTypes.Assurance }) }),
225
+ Object.freeze({ path: Object.freeze(['$.credentialSubject.holdsAnimalIdentity.id']), filter: Object.freeze({ type: 'string', const: animalId }) }),
226
+ ]),
227
+ }),
228
+ })]),
229
+ }),
230
+ });
231
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './animal-card.js';
2
+ export * from './animal-identity-assurance.js';
2
3
  export * from './animal-behavior.js';
3
4
  export * from './assistant.js';
4
5
  export * from './communication.js';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './animal-card.js';
2
+ export * from './animal-identity-assurance.js';
2
3
  export * from './animal-behavior.js';
3
4
  export * from './assistant.js';
4
5
  export * from './communication.js';
@@ -197,9 +197,9 @@ export declare const DataspaceProtocol2025: Readonly<{
197
197
  /**
198
198
  * Builds the claims-only public department Organization.
199
199
  *
200
- * One department is one Schema.org/FHIR Organization and owns one stable
201
- * HealthcareService projection whose id is the same department id. Offered
202
- * Services remain separate directory records linked through providerIdentifier.
200
+ * One department is one Schema.org/FHIR Organization. Each offered Service
201
+ * remains a separate directory record and owns its own HealthcareService
202
+ * projection through providerIdentifier.
203
203
  */
204
204
  export declare function buildDepartmentDirectoryResource(input: DepartmentDirectoryInput): DirectoryResource;
205
205
  /** Builds the claims-only public Place authored by an organization. */
@@ -239,49 +239,14 @@ export declare function projectDepartmentToFhirR5Organization(department: Direct
239
239
  name: string;
240
240
  }>;
241
241
  /**
242
- * Projects all offered Services of one department into its single stable
243
- * HealthcareService. The Services supply types; the department supplies the
244
- * provider identity and allowed Locations.
242
+ * @deprecated Aggregate department HealthcareService projection is forbidden.
243
+ * Project each offered Service independently with
244
+ * projectServiceToFhirR5HealthcareService(...).
245
245
  */
246
246
  export declare function projectDepartmentToFhirR5HealthcareService(department: DirectoryResource | DirectoryFlatClaimsResource, input: Readonly<{
247
247
  services: readonly (DirectoryResource | DirectoryFlatClaimsResource)[];
248
248
  places: readonly (DirectoryResource | DirectoryFlatClaimsResource)[];
249
- }>): Readonly<{
250
- meta: {
251
- claims: {
252
- 'HealthcareService.service-category'?: string[] | undefined;
253
- 'HealthcareService.identifier': string;
254
- 'HealthcareService.organization': string;
255
- 'HealthcareService.name': string | undefined;
256
- 'HealthcareService.location': string[];
257
- 'HealthcareService.service-type': string[];
258
- };
259
- };
260
- photo?: {
261
- url: string;
262
- } | undefined;
263
- resourceType: "HealthcareService";
264
- id: string;
265
- active: true;
266
- providedBy: {
267
- reference: string;
268
- };
269
- name: string | undefined;
270
- comment: string | undefined;
271
- category: {
272
- coding: {
273
- code: string;
274
- }[];
275
- }[];
276
- type: {
277
- coding: {
278
- code: string;
279
- }[];
280
- }[];
281
- location: {
282
- reference: string;
283
- }[];
284
- }>;
249
+ }>): void;
285
250
  /** Explicitly projects one neutral Place to native FHIR R5 Location for export or interop. */
286
251
  export declare function projectPlaceToFhirR5Location(place: DirectoryResource | DirectoryFlatClaimsResource): Readonly<{
287
252
  resourceType: "Location";
@@ -169,9 +169,9 @@ function encodeSpecialOpeningHours(value) {
169
169
  /**
170
170
  * Builds the claims-only public department Organization.
171
171
  *
172
- * One department is one Schema.org/FHIR Organization and owns one stable
173
- * HealthcareService projection whose id is the same department id. Offered
174
- * Services remain separate directory records linked through providerIdentifier.
172
+ * One department is one Schema.org/FHIR Organization. Each offered Service
173
+ * remains a separate directory record and owns its own HealthcareService
174
+ * projection through providerIdentifier.
175
175
  */
176
176
  export function buildDepartmentDirectoryResource(input) {
177
177
  const id = required(input.id, 'department_id_required');
@@ -421,54 +421,14 @@ export function projectDepartmentToFhirR5Organization(department) {
421
421
  });
422
422
  }
423
423
  /**
424
- * Projects all offered Services of one department into its single stable
425
- * HealthcareService. The Services supply types; the department supplies the
426
- * provider identity and allowed Locations.
424
+ * @deprecated Aggregate department HealthcareService projection is forbidden.
425
+ * Project each offered Service independently with
426
+ * projectServiceToFhirR5HealthcareService(...).
427
427
  */
428
428
  export function projectDepartmentToFhirR5HealthcareService(department, input) {
429
- if (department.resourceType !== 'Organization')
430
- throw new TypeError('department_resource_required');
431
- const departmentLocations = values(department, ClaimsDirectoryOrganizationSchemaorg.locationIdentifier);
432
- const places = departmentLocations.map(identifier => {
433
- const place = input.places.find(candidate => candidate.resourceType === 'Place' && candidate.id === identifier);
434
- if (!place)
435
- throw new TypeError('department_place_not_found');
436
- return place;
437
- });
438
- const services = input.services.filter(service => {
439
- if (service.resourceType !== 'Service')
440
- throw new TypeError('service_resource_required');
441
- return first(service, ClaimsDirectoryServiceSchemaorg.providerIdentifier) === department.id;
442
- });
443
- if (!services.length)
444
- throw new TypeError('department_service_required');
445
- for (const service of services) {
446
- if (values(service, ClaimsDirectoryServiceSchemaorg.placeIdentifier).some(identifier => !departmentLocations.includes(identifier))) {
447
- throw new TypeError('department_service_place_invalid');
448
- }
449
- }
450
- const serviceTypes = [...new Set(services.flatMap(service => values(service, ClaimsDirectoryServiceSchemaorg.serviceType)))];
451
- const categories = [...new Set(services.flatMap(service => values(service, ClaimsDirectoryServiceSchemaorg.category)))];
452
- const concept = (code) => ({ coding: [{ code }] });
453
- const photoUrl = first(department, ClaimsDirectoryOrganizationSchemaorg.logoContentUrl)
454
- || places.map(place => first(place, ClaimsPlaceSchemaorg.photoContentUrl)).find(Boolean);
455
- return Object.freeze({
456
- resourceType: 'HealthcareService', id: department.id, active: true,
457
- providedBy: { reference: `Organization/${department.id}` },
458
- name: first(department, ClaimsDirectoryOrganizationSchemaorg.name),
459
- comment: first(department, ClaimsDirectoryOrganizationSchemaorg.description),
460
- category: categories.map(concept), type: serviceTypes.map(concept),
461
- location: places.map(place => ({ reference: `Location/${place.id}` })),
462
- ...(photoUrl ? { photo: { url: photoUrl } } : {}),
463
- meta: { claims: {
464
- 'HealthcareService.identifier': department.id,
465
- 'HealthcareService.organization': `Organization/${department.id}`,
466
- 'HealthcareService.name': first(department, ClaimsDirectoryOrganizationSchemaorg.name),
467
- 'HealthcareService.location': places.map(place => `Location/${place.id}`),
468
- 'HealthcareService.service-type': serviceTypes,
469
- ...(categories.length ? { 'HealthcareService.service-category': categories } : {}),
470
- } },
471
- });
429
+ void department;
430
+ void input;
431
+ throw new TypeError('department_aggregate_healthcare_service_forbidden');
472
432
  }
473
433
  /** Explicitly projects one neutral Place to native FHIR R5 Location for export or interop. */
474
434
  export function projectPlaceToFhirR5Location(place) {
@@ -519,7 +479,8 @@ export function projectServiceToFhirR5HealthcareService(service, input) {
519
479
  });
520
480
  const serviceTypes = values(service, ClaimsDirectoryServiceSchemaorg.serviceType);
521
481
  const categories = values(service, ClaimsDirectoryServiceSchemaorg.category);
522
- const organization = required(first(service, ClaimsDirectoryServiceSchemaorg.providerIdentifier), 'service_organization_required');
482
+ const rawOrganization = required(first(service, ClaimsDirectoryServiceSchemaorg.providerIdentifier), 'service_organization_required');
483
+ const organization = rawOrganization.startsWith('Organization/') ? rawOrganization : `Organization/${rawOrganization}`;
523
484
  const photoUrl = places.map(place => first(place, ClaimsPlaceSchemaorg.photoContentUrl)).find(Boolean);
524
485
  const concept = (code) => ({ coding: [{ code }] });
525
486
  return Object.freeze({
@@ -1,3 +1,4 @@
1
+ import { buildSchedulableLocationFlatResource } from 'fhir-data-utils-ts';
1
2
  /** FHIR R5 Slot statuses accepted by reusable scheduling. */
2
3
  export declare const SlotStatuses: Readonly<{
3
4
  readonly Busy: "busy";
@@ -28,12 +29,12 @@ export declare const AppointmentStatuses: Readonly<{
28
29
  readonly Waitlist: "waitlist";
29
30
  }>;
30
31
  /** Official FHIR R5 Slot resource-specific SearchParameter codes. */
31
- export declare const SlotSearchParameters: readonly ["appointment-type", "identifier", "schedule", "service-category", "service-type", "service-type-reference", "specialty", "start", "status"];
32
+ export declare const SlotSearchParameters: readonly string[];
32
33
  /** Official FHIR R5 SearchParameter codes used by reusable scheduling. */
33
34
  export declare const SchedulingSearchParameterCatalog: Readonly<{
34
35
  readonly Location: readonly string[];
35
36
  readonly Schedule: readonly string[];
36
- readonly Slot: readonly ["appointment-type", "identifier", "schedule", "service-category", "service-type", "service-type-reference", "specialty", "start", "status"];
37
+ readonly Slot: readonly string[];
37
38
  readonly Appointment: readonly string[];
38
39
  readonly AppointmentResponse: readonly string[];
39
40
  }>;
@@ -47,7 +48,7 @@ export type SchedulingFlatClaimsResource = Readonly<Record<string, unknown> & {
47
48
  claims: Readonly<Record<string, readonly string[]>>;
48
49
  }>;
49
50
  }>;
50
- /** Validates native FHIR scheduling data and normalizes indexed SearchParameter claims to strings. */
51
+ /** Rejects nested native FHIR and normalizes governed flat claims to strings. */
51
52
  export declare function normalizeSchedulingFlatClaimsResource(input: unknown): SchedulingFlatClaimsResource;
52
53
  export type ClaimsFirstResource = Readonly<Record<string, unknown> & {
53
54
  resourceType: string;
@@ -68,15 +69,18 @@ export declare function buildLocationResource(input: Readonly<{
68
69
  characteristicCodes?: readonly string[];
69
70
  photoUrl?: string;
70
71
  }>): ClaimsFirstResource;
71
- /** Builds the claims-first FHIR R5 Schedule joining one practitioner and consultation Location. */
72
+ /** Builds one service-specific Schedule through the neutral FHIR claims graph. */
72
73
  export declare function buildScheduleResource(input: Readonly<{
73
- id: string;
74
+ scheduleUrn: string;
74
75
  name: string;
75
- practitionerReference: string;
76
- locationReference: string;
76
+ healthcareServiceReference: string;
77
+ resourceActorReferences: readonly string[];
78
+ serviceType: string;
77
79
  planningHorizonStart: string;
78
80
  planningHorizonEnd: string;
79
81
  }>): ClaimsFirstResource;
82
+ /** Builds one independently reservable child Location; equipment remains Device. */
83
+ export declare const buildSchedulableLocationResource: typeof buildSchedulableLocationFlatResource;
80
84
  export type AvailabilityPeriod = Readonly<{
81
85
  daysOfWeek: readonly number[];
82
86
  startTime: string;
@@ -101,11 +105,13 @@ export type ExpandedSlot = Readonly<{
101
105
  locationReference: string;
102
106
  resource: ClaimsFirstResource;
103
107
  }>;
104
- /** Produces the stable Schedule id for one practitioner/location availability stream. */
105
- export declare function scheduleIdForLocation(baseScheduleId: string, locationReference: string): string;
106
108
  /** Expands bounded weekly availability into claims-first Slots, applying recurring restrictions or expansions. */
107
109
  export declare function expandAvailability(input: Readonly<{
108
- scheduleId: string;
110
+ /** One service-owned Schedule URN for each independently reservable Location. */
111
+ scheduleUrnsByLocation: Readonly<Record<string, string>>;
112
+ healthcareServiceReference: string;
113
+ serviceType: string;
114
+ resourceActorReferencesByLocation: Readonly<Record<string, readonly string[]>>;
109
115
  startDate: string;
110
116
  endDate: string;
111
117
  timeZone: string;
@@ -161,7 +167,7 @@ export declare function buildAppointmentNotificationResource(input: Readonly<{
161
167
  reason: string;
162
168
  }>): ClaimsFirstResource;
163
169
  /** @deprecated Use {@link SlotSearchParameters}. */
164
- export declare const VeterinarySlotSearchParameters: readonly ["appointment-type", "identifier", "schedule", "service-category", "service-type", "service-type-reference", "specialty", "start", "status"];
170
+ export declare const VeterinarySlotSearchParameters: readonly string[];
165
171
  /** @deprecated Use {@link SchedulingFlatClaimCatalog}. */
166
172
  export declare const VeterinarySchedulingFlatClaimCatalog: Readonly<Record<"Appointment" | "AppointmentResponse" | "Location" | "Schedule" | "Slot", readonly string[]>>;
167
173
  /** @deprecated Use {@link SchedulingResourceType}. */
@@ -1,3 +1,5 @@
1
+ import { ScheduleR5SearchParameterCatalog, SlotR5SearchParameterCatalog, buildSchedulableLocationFlatResource, buildServiceScheduleFlatGraph, } from 'fhir-data-utils-ts';
2
+ import { buildScheduleSlotIdentity, deriveFhirResourceIdFromUrn, } from 'sos-data-utils-ts/organization-scoped-resource-identity';
1
3
  /** FHIR R5 Slot statuses accepted by reusable scheduling. */
2
4
  export const SlotStatuses = Object.freeze({ Busy: 'busy', Free: 'free', BusyUnavailable: 'busy-unavailable', BusyTentative: 'busy-tentative', EnteredInError: 'entered-in-error' });
3
5
  /** FHIR R5 AppointmentResponse participant statuses. */
@@ -8,11 +10,11 @@ export const AppointmentStatuses = Object.freeze({
8
10
  Cancelled: 'cancelled', NoShow: 'noshow', EnteredInError: 'entered-in-error', CheckedIn: 'checked-in', Waitlist: 'waitlist',
9
11
  });
10
12
  /** Official FHIR R5 Slot resource-specific SearchParameter codes. */
11
- export const SlotSearchParameters = Object.freeze(['appointment-type', 'identifier', 'schedule', 'service-category', 'service-type', 'service-type-reference', 'specialty', 'start', 'status']);
13
+ export const SlotSearchParameters = Object.freeze(SlotR5SearchParameterCatalog.map(definition => definition.claim.slice('Slot.'.length)));
12
14
  /** Official FHIR R5 SearchParameter codes used by reusable scheduling. */
13
15
  export const SchedulingSearchParameterCatalog = Object.freeze({
14
16
  Location: Object.freeze(['characteristic', 'identifier', 'name', 'near', 'organization', 'status', 'type']),
15
- Schedule: Object.freeze(['active', 'actor', 'date', 'identifier', 'name', 'service-category', 'service-type', 'specialty']),
17
+ Schedule: Object.freeze(ScheduleR5SearchParameterCatalog.map(definition => definition.claim.slice('Schedule.'.length))),
16
18
  Slot: SlotSearchParameters,
17
19
  Appointment: Object.freeze(['actor', 'appointment-type', 'based-on', 'date', 'identifier', 'location', 'part-status', 'patient', 'practitioner', 'reason-code', 'reason-reference', 'service-category', 'service-type', 'slot', 'specialty', 'status', 'subject', 'supporting-info']),
18
20
  AppointmentResponse: Object.freeze(['_lastUpdated', 'actor', 'appointment', 'group', 'identifier', 'location', 'part-status', 'patient', 'practitioner']),
@@ -22,17 +24,12 @@ export const SchedulingFlatClaimCatalog = Object.freeze(Object.fromEntries(Objec
22
24
  resourceType,
23
25
  Object.freeze([
24
26
  ...parameters.map(parameter => `${resourceType}.${parameter}`),
27
+ ...(resourceType === 'Schedule' ? ['Schedule.planning-horizon-start', 'Schedule.planning-horizon-end'] : []),
28
+ ...(resourceType === 'Slot' ? ['Slot.end'] : []),
25
29
  ...(resourceType === 'Appointment' ? ['Appointment.user-selected'] : []),
26
30
  ]),
27
31
  ])));
28
- const nativeFieldsByResource = Object.freeze({
29
- Location: new Set(['identifier', 'status', 'mode', 'name', 'description', 'type', 'telecom', 'address', 'physicalType', 'position', 'managingOrganization', 'characteristic', 'contained']),
30
- Schedule: new Set(['identifier', 'active', 'serviceCategory', 'serviceType', 'specialty', 'name', 'actor', 'planningHorizon', 'comment']),
31
- Slot: new Set(['identifier', 'serviceCategory', 'serviceType', 'specialty', 'appointmentType', 'schedule', 'status', 'start', 'end', 'overbooked', 'comment']),
32
- Appointment: new Set(['identifier', 'status', 'serviceCategory', 'serviceType', 'specialty', 'appointmentType', 'reason', 'description', 'start', 'end', 'slot', 'participant', 'subject', 'note', 'supportingInformation', 'contained']),
33
- AppointmentResponse: new Set(['identifier', 'appointment', 'proposedNewTime', 'start', 'end', 'participantType', 'actor', 'participantStatus', 'comment', 'recurring', 'occurrenceDate', 'recurrenceId', 'contained']),
34
- });
35
- /** Validates native FHIR scheduling data and normalizes indexed SearchParameter claims to strings. */
32
+ /** Rejects nested native FHIR and normalizes governed flat claims to strings. */
36
33
  export function normalizeSchedulingFlatClaimsResource(input) {
37
34
  if (!input || typeof input !== 'object' || Array.isArray(input))
38
35
  throw new TypeError('scheduling_flat_resource_invalid');
@@ -40,7 +37,7 @@ export function normalizeSchedulingFlatClaimsResource(input) {
40
37
  if (typeof value.resourceType !== 'string' || !(value.resourceType in SchedulingFlatClaimCatalog))
41
38
  throw new TypeError('scheduling_flat_resource_invalid');
42
39
  const resourceType = value.resourceType;
43
- if (Object.keys(value).some(key => !['resourceType', 'id', 'meta'].includes(key) && !nativeFieldsByResource[resourceType].has(key)))
40
+ if (Object.keys(value).some(key => !['resourceType', 'id', 'meta'].includes(key)))
44
41
  throw new TypeError('scheduling_flat_resource_invalid');
45
42
  const id = required(String(value.id || ''), 'scheduling_flat_resource_invalid');
46
43
  if (!/^[A-Za-z0-9\-.]{1,64}$/.test(id))
@@ -90,31 +87,25 @@ export function buildLocationResource(input) {
90
87
  'Location.near': `${input.latitude}|${input.longitude}`,
91
88
  'Location.organization': organizationReference,
92
89
  ...(characteristicCodes.length ? { 'Location.characteristic': characteristicCodes } : {}),
93
- }, {
94
- identifier: [{ value: input.id }], status: 'active', mode: 'instance', name,
95
- ...(input.description ? { description: input.description.trim() } : {}),
96
- position: { latitude: input.latitude, longitude: input.longitude, ...(input.altitude === undefined ? {} : { altitude: input.altitude }) },
97
- managingOrganization: { reference: organizationReference },
98
- ...(characteristicCodes.length ? { characteristic: characteristicCodes.map(code => ({ coding: [{ code }] })) } : {}),
99
- ...(input.photoUrl ? { contained: [{ resourceType: 'DocumentReference', id: 'location-photo', status: 'current', content: [{ attachment: { url: input.photoUrl } }] }] } : {}),
100
90
  });
101
91
  }
102
- /** Builds the claims-first FHIR R5 Schedule joining one practitioner and consultation Location. */
92
+ /** Builds one service-specific Schedule through the neutral FHIR claims graph. */
103
93
  export function buildScheduleResource(input) {
104
- const start = assertDate(input.planningHorizonStart);
105
- const end = assertDate(input.planningHorizonEnd);
106
- if (start > end)
107
- throw new Error('schedule_horizon_invalid');
108
- const name = required(input.name, 'schedule_name_required');
109
- const actors = Object.freeze([required(input.practitionerReference, 'schedule_practitioner_required'), required(input.locationReference, 'schedule_location_required')]);
110
- return claimsResource('Schedule', input.id, {
111
- 'Schedule.identifier': input.id, 'Schedule.active': true, 'Schedule.name': name,
112
- 'Schedule.actor': actors, 'Schedule.date': Object.freeze([start, end]),
113
- }, {
114
- identifier: [{ value: input.id }], active: true, name,
115
- actor: actors.map(reference => ({ reference })), planningHorizon: { start, end },
94
+ const id = deriveFhirResourceIdFromUrn(input.scheduleUrn);
95
+ const graph = buildServiceScheduleFlatGraph({
96
+ scheduleId: id,
97
+ scheduleIdentifier: input.scheduleUrn,
98
+ name: input.name,
99
+ healthcareServiceReference: input.healthcareServiceReference,
100
+ resourceActorReferences: input.resourceActorReferences,
101
+ serviceType: input.serviceType,
102
+ planningHorizon: { start: input.planningHorizonStart, end: input.planningHorizonEnd },
103
+ slots: [],
116
104
  });
105
+ return graph[0];
117
106
  }
107
+ /** Builds one independently reservable child Location; equipment remains Device. */
108
+ export const buildSchedulableLocationResource = buildSchedulableLocationFlatResource;
118
109
  const minutes = (value) => { const [hour, minute] = assertTime(value).split(':').map(Number); return hour * 60 + minute; };
119
110
  const timeFromMinutes = (value) => `${String(Math.floor(value / 60)).padStart(2, '0')}:${String(value % 60).padStart(2, '0')}`;
120
111
  const dates = (start, end) => { const result = []; const cursor = new Date(`${start}T00:00:00Z`); const last = new Date(`${end}T00:00:00Z`); while (cursor <= last) {
@@ -140,12 +131,6 @@ const matchesExceptionDate = (date, recurrence) => { const [, , day] = dateParts
140
131
  const validateRange = (startTime, endTime, code) => { const start = minutes(startTime); const end = minutes(endTime); if (start >= end)
141
132
  throw new Error(code); return [start, end]; };
142
133
  const slotKey = (location, date, start, end) => `${location}|${date}|${start}|${end}`;
143
- /** Produces the stable Schedule id for one practitioner/location availability stream. */
144
- export function scheduleIdForLocation(baseScheduleId, locationReference) {
145
- const base = required(baseScheduleId, 'schedule_id_required');
146
- const locationId = required(locationReference, 'availability_location_required').split('/').at(-1);
147
- return `${base}-${locationId}`;
148
- }
149
134
  /** Expands bounded weekly availability into claims-first Slots, applying recurring restrictions or expansions. */
150
135
  export function expandAvailability(input) {
151
136
  const startDate = assertDate(input.startDate);
@@ -201,11 +186,33 @@ export function expandAvailability(input) {
201
186
  return Object.freeze([...base.values()].sort((left, right) => slotKey(left.locationReference, left.localDate, left.localStart, left.localEnd).localeCompare(slotKey(right.locationReference, right.localDate, right.localStart, right.localEnd))).map(slot => {
202
187
  const start = zonedInstant(slot.localDate, slot.localStart, input.timeZone);
203
188
  const end = zonedInstant(slot.localDate, slot.localEnd, input.timeZone);
204
- const id = `${input.scheduleId}-${slot.localDate}-${slot.localStart.replace(':', '')}-${slot.locationReference.split('/').at(-1)}`;
205
- const scheduleReference = `Schedule/${scheduleIdForLocation(input.scheduleId, slot.locationReference)}`;
206
- return Object.freeze({ ...slot, resource: claimsResource('Slot', id, {
207
- 'Slot.identifier': id, 'Slot.schedule': scheduleReference, 'Slot.status': SlotStatuses.Free, 'Slot.start': start,
208
- }, { identifier: [{ value: id }], schedule: { reference: scheduleReference }, status: SlotStatuses.Free, start, end }) });
189
+ const scheduleUrn = required(input.scheduleUrnsByLocation[slot.locationReference] || '', 'availability_schedule_urn_required');
190
+ const scheduleResourceId = deriveFhirResourceIdFromUrn(scheduleUrn);
191
+ const slotIdentity = buildScheduleSlotIdentity({ scheduleUrn, start, end });
192
+ const scheduleReference = `Schedule/${scheduleResourceId}`;
193
+ const actors = input.resourceActorReferencesByLocation[slot.locationReference];
194
+ if (!actors?.includes(slot.locationReference))
195
+ throw new Error('availability_resource_actors_required');
196
+ const graph = buildServiceScheduleFlatGraph({
197
+ scheduleId: scheduleReference.slice('Schedule/'.length),
198
+ scheduleIdentifier: scheduleReference,
199
+ name: scheduleReference,
200
+ healthcareServiceReference: input.healthcareServiceReference,
201
+ resourceActorReferences: actors,
202
+ serviceType: input.serviceType,
203
+ planningHorizon: {
204
+ start: zonedInstant(startDate, '00:00', input.timeZone),
205
+ end: new Date(Date.parse(zonedInstant(endDate, '23:59', input.timeZone)) + 60_000).toISOString(),
206
+ },
207
+ slots: [{
208
+ slotId: slotIdentity.id,
209
+ slotIdentifier: slotIdentity.identifier,
210
+ status: SlotStatuses.Free,
211
+ start,
212
+ end,
213
+ }],
214
+ });
215
+ return Object.freeze({ ...slot, resource: graph[1] });
209
216
  }));
210
217
  }
211
218
  /** Builds a claims-first response that confirms, declines or proposes a new appointment time. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vet-data-utils-ts",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Browser-safe governed VetChain data contracts",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Connecting Solution & Applications Ltd",
@@ -16,6 +16,10 @@
16
16
  "types": "./dist/animal-card.d.ts",
17
17
  "default": "./dist/animal-card.js"
18
18
  },
19
+ "./animal-identity-assurance": {
20
+ "types": "./dist/animal-identity-assurance.d.ts",
21
+ "default": "./dist/animal-identity-assurance.js"
22
+ },
19
23
  "./animal-behavior": {
20
24
  "types": "./dist/animal-behavior.d.ts",
21
25
  "default": "./dist/animal-behavior.js"
@@ -157,10 +161,10 @@
157
161
  "typescript": "^5.5.4"
158
162
  },
159
163
  "dependencies": {
160
- "fhir-data-utils-ts": "0.2.4",
164
+ "fhir-data-utils-ts": "0.6.0",
161
165
  "gdc-common-utils-ts": "2.9.12",
162
166
  "pako": "^2.2.0",
163
- "sos-data-utils-ts": "0.3.7"
167
+ "sos-data-utils-ts": "0.5.1"
164
168
  },
165
169
  "engines": {
166
170
  "node": ">=20"