vet-data-utils-ts 0.3.0 → 0.3.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 +6 -0
- package/dist/digital-twin.d.ts +17 -4
- package/dist/digital-twin.js +48 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,3 +12,9 @@ The catalogue retains longitudinal sections that also apply to animal care,
|
|
|
12
12
|
with animal-facing labels where needed. Human advance directives
|
|
13
13
|
(`LOINC|42348-3`) are deliberately excluded because they express the patient's
|
|
14
14
|
own legal/autonomy decisions, not controller preferences.
|
|
15
|
+
|
|
16
|
+
Digital-twin search input contains one section, one or more resource types
|
|
17
|
+
allowed by that section, display text, `dateFrom` and optional `dateTo`.
|
|
18
|
+
`VeterinaryDigitalTwinSectionSearchProfiles` owns the corresponding
|
|
19
|
+
`code-display` and date claims so browser code never supplies claim names.
|
|
20
|
+
Identifying fields and caller-authored `claim`/`dateClaim` values are rejected.
|
package/dist/digital-twin.d.ts
CHANGED
|
@@ -3,11 +3,24 @@ export declare const VeterinaryDigitalTwinFormats: Readonly<{
|
|
|
3
3
|
readonly R4: "org.hl7.fhir.r4";
|
|
4
4
|
}>;
|
|
5
5
|
export type VeterinaryDigitalTwinFormat = typeof VeterinaryDigitalTwinFormats[keyof typeof VeterinaryDigitalTwinFormats];
|
|
6
|
+
export type VeterinaryDigitalTwinResourceSearchProfile = Readonly<{
|
|
7
|
+
resourceType: string;
|
|
8
|
+
codeDisplayClaim: `${string}.code-display`;
|
|
9
|
+
dateClaim: `${string}.${string}`;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Searchable flat-claim projections for each veterinary section.
|
|
13
|
+
*
|
|
14
|
+
* Native FHIR resources are converted by GW into these governed claims. The
|
|
15
|
+
* browser selects resource families only; it never supplies claim names.
|
|
16
|
+
*/
|
|
17
|
+
export declare const VeterinaryDigitalTwinSectionSearchProfiles: Readonly<Record<string, readonly VeterinaryDigitalTwinResourceSearchProfile[]>>;
|
|
6
18
|
export type VeterinaryDigitalTwinSearch = Readonly<{
|
|
7
19
|
section: string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
resourceTypes: readonly string[];
|
|
21
|
+
text: string;
|
|
22
|
+
dateFrom: string;
|
|
23
|
+
dateTo?: string;
|
|
11
24
|
}>;
|
|
12
|
-
/** Bounds one section-first pseudonymous search and rejects direct identifiers. */
|
|
25
|
+
/** Bounds one section-first pseudonymous search and rejects direct identifiers and caller-authored claims. */
|
|
13
26
|
export declare function parseVeterinaryDigitalTwinSearch(input: unknown): VeterinaryDigitalTwinSearch;
|
package/dist/digital-twin.js
CHANGED
|
@@ -1,24 +1,61 @@
|
|
|
1
|
-
import { isVeterinarySummarySection } from './veterinary-sections.js';
|
|
1
|
+
import { VeterinarySummarySections, isVeterinarySummarySection } from './veterinary-sections.js';
|
|
2
2
|
export const VeterinaryDigitalTwinFormats = Object.freeze({ Api: 'org.hl7.fhir.api', R4: 'org.hl7.fhir.r4' });
|
|
3
|
-
|
|
3
|
+
const profile = (resourceType, dateClaim) => Object.freeze({ resourceType, codeDisplayClaim: `${resourceType}.code-display`, dateClaim });
|
|
4
|
+
/**
|
|
5
|
+
* Searchable flat-claim projections for each veterinary section.
|
|
6
|
+
*
|
|
7
|
+
* Native FHIR resources are converted by GW into these governed claims. The
|
|
8
|
+
* browser selects resource families only; it never supplies claim names.
|
|
9
|
+
*/
|
|
10
|
+
export const VeterinaryDigitalTwinSectionSearchProfiles = Object.freeze({
|
|
11
|
+
[VeterinarySummarySections.Problems.value]: Object.freeze([profile('Condition', 'Condition.onset-datetime')]),
|
|
12
|
+
[VeterinarySummarySections.Allergies.value]: Object.freeze([profile('AllergyIntolerance', 'AllergyIntolerance.onset-datetime')]),
|
|
13
|
+
[VeterinarySummarySections.Medications.value]: Object.freeze([profile('MedicationStatement', 'MedicationStatement.effective')]),
|
|
14
|
+
[VeterinarySummarySections.Immunizations.value]: Object.freeze([profile('Immunization', 'Immunization.date')]),
|
|
15
|
+
[VeterinarySummarySections.Results.value]: Object.freeze([
|
|
16
|
+
profile('Observation', 'Observation.date'),
|
|
17
|
+
profile('DiagnosticReport', 'DiagnosticReport.date'),
|
|
18
|
+
]),
|
|
19
|
+
[VeterinarySummarySections.Procedures.value]: Object.freeze([profile('Procedure', 'Procedure.date')]),
|
|
20
|
+
[VeterinarySummarySections.VitalSigns.value]: Object.freeze([profile('Observation', 'Observation.date')]),
|
|
21
|
+
[VeterinarySummarySections.EnvironmentAndLifestyle.value]: Object.freeze([profile('Observation', 'Observation.date')]),
|
|
22
|
+
[VeterinarySummarySections.FunctionalStatus.value]: Object.freeze([profile('Condition', 'Condition.onset-datetime')]),
|
|
23
|
+
[VeterinarySummarySections.PastIllness.value]: Object.freeze([profile('Condition', 'Condition.onset-datetime')]),
|
|
24
|
+
[VeterinarySummarySections.ReproductiveHistory.value]: Object.freeze([profile('Observation', 'Observation.date')]),
|
|
25
|
+
});
|
|
26
|
+
/** Bounds one section-first pseudonymous search and rejects direct identifiers and caller-authored claims. */
|
|
4
27
|
export function parseVeterinaryDigitalTwinSearch(input) {
|
|
5
28
|
const value = (input || {});
|
|
6
29
|
for (const forbidden of ['name', 'email', 'telephone', 'identifier', 'subjectDid', 'cardNumber']) {
|
|
7
30
|
if (String(value[forbidden] || '').trim())
|
|
8
31
|
throw new TypeError('veterinary_digital_twin_identifying_filter_forbidden');
|
|
9
32
|
}
|
|
33
|
+
if (String(value.claim || '').trim() || String(value.dateClaim || '').trim()) {
|
|
34
|
+
throw new TypeError('veterinary_digital_twin_claim_forbidden');
|
|
35
|
+
}
|
|
10
36
|
const section = String(value.section || '').trim();
|
|
11
|
-
const resourceType = String(value.resourceType || '').trim();
|
|
12
|
-
const claim = String(value.claim || '').trim();
|
|
13
|
-
const filterValue = String(value.value || '').trim();
|
|
14
37
|
if (!isVeterinarySummarySection(section))
|
|
15
38
|
throw new TypeError('veterinary_digital_twin_section_invalid');
|
|
16
|
-
|
|
39
|
+
const allowedProfiles = VeterinaryDigitalTwinSectionSearchProfiles[section] || [];
|
|
40
|
+
if (allowedProfiles.length === 0)
|
|
41
|
+
throw new TypeError('veterinary_digital_twin_section_not_searchable');
|
|
42
|
+
const rawResourceTypes = Array.isArray(value.resourceTypes) ? value.resourceTypes : [value.resourceType];
|
|
43
|
+
const resourceTypes = Array.from(new Set(rawResourceTypes.map(item => String(item || '').trim()).filter(Boolean)));
|
|
44
|
+
if (resourceTypes.length === 0 || resourceTypes.some(resourceType => !allowedProfiles.some(item => item.resourceType === resourceType))) {
|
|
17
45
|
throw new TypeError('veterinary_digital_twin_resource_invalid');
|
|
18
|
-
if (!new RegExp(`^${resourceType.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.[A-Za-z0-9][A-Za-z0-9.-]{0,127}$`).test(claim)) {
|
|
19
|
-
throw new TypeError('veterinary_digital_twin_claim_invalid');
|
|
20
46
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
47
|
+
const text = String(value.text || value.value || '').trim();
|
|
48
|
+
if (!text || text.length > 256)
|
|
49
|
+
throw new TypeError('veterinary_digital_twin_text_invalid');
|
|
50
|
+
const dateFrom = parseIsoDate(value.dateFrom, 'date_from');
|
|
51
|
+
const dateTo = String(value.dateTo || '').trim() ? parseIsoDate(value.dateTo, 'date_to') : undefined;
|
|
52
|
+
if (dateTo && Date.parse(dateTo) < Date.parse(dateFrom))
|
|
53
|
+
throw new TypeError('veterinary_digital_twin_date_range_invalid');
|
|
54
|
+
return Object.freeze({ section, resourceTypes: Object.freeze(resourceTypes), text, dateFrom, ...(dateTo ? { dateTo } : {}) });
|
|
55
|
+
}
|
|
56
|
+
function parseIsoDate(value, field) {
|
|
57
|
+
const normalized = String(value || '').trim();
|
|
58
|
+
if (!normalized || Number.isNaN(Date.parse(normalized)))
|
|
59
|
+
throw new TypeError(`veterinary_digital_twin_${field}_invalid`);
|
|
60
|
+
return normalized;
|
|
24
61
|
}
|