vet-data-utils-ts 0.5.18 → 0.5.20
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 +10 -0
- package/dist/index-projection-tags.d.ts +25 -0
- package/dist/index-projection-tags.js +60 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,6 +44,16 @@ already role-authorized GW search result. It removes every clinical field and
|
|
|
44
44
|
`summarizeVeterinaryTaggedIndex(...)` then sorts and counts that minimized copy
|
|
45
45
|
without inspecting resource content.
|
|
46
46
|
|
|
47
|
+
`readVeterinaryBundleTags(...)` is the fail-closed UI reader for that minimized
|
|
48
|
+
shape. It is intentionally separate from the clinical Bundle Reader and rejects
|
|
49
|
+
`meta.claims`, clinical fields, resource types or tags outside the caller's
|
|
50
|
+
already server-authorized policy. Its `cards` output groups repeated Coding
|
|
51
|
+
values by resource-qualified search parameter, so an index-only screen can use
|
|
52
|
+
the same card shell without constructing a clinical-resource view. An
|
|
53
|
+
authorized full Bundle may first be reduced with
|
|
54
|
+
`projectVeterinaryClaimsIndexForUi(...)` when it only needs that summary; the
|
|
55
|
+
full clinical reader itself remains claims-first and unchanged.
|
|
56
|
+
|
|
47
57
|
`buildSmartHealthCardJwsProofReference(...)` serves a different purpose: it
|
|
48
58
|
hashes the exact compact SHC JWS, including its ES256 signature, with a domain
|
|
49
59
|
separator. A product ledger/resolver may use that key to retrieve the existing
|
|
@@ -5,6 +5,13 @@ export type VeterinaryIndexTagPolicy = Readonly<{
|
|
|
5
5
|
export type VeterinaryUiIndexPolicy = VeterinaryIndexTagPolicy & Readonly<{
|
|
6
6
|
allowedResourceTypes: ReadonlySet<string>;
|
|
7
7
|
}>;
|
|
8
|
+
export type VeterinaryIndexCardView = Readonly<{
|
|
9
|
+
fullUrl: string;
|
|
10
|
+
resourceType: string;
|
|
11
|
+
id: string;
|
|
12
|
+
/** Only server-authorized index values; never clinical resource content. */
|
|
13
|
+
tags: Readonly<Record<string, readonly string[]>>;
|
|
14
|
+
}>;
|
|
8
15
|
/**
|
|
9
16
|
* Creates the interoperable message/index copy of a native FHIR Bundle.
|
|
10
17
|
* Only allowlisted, resource-qualified Coding tags are retained or derived;
|
|
@@ -24,4 +31,22 @@ export declare function summarizeVeterinaryTaggedIndex(source: JsonObject, optio
|
|
|
24
31
|
countsByResourceType: Record<string, number>;
|
|
25
32
|
entries: JsonObject[];
|
|
26
33
|
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Strict UI reader for an index-only Bundle. Unlike the clinical Bundle
|
|
36
|
+
* Reader, this refuses claims and every clinical resource field.
|
|
37
|
+
*/
|
|
38
|
+
export declare function readVeterinaryBundleTags(source: JsonObject, options: VeterinaryUiIndexPolicy & Readonly<{
|
|
39
|
+
sortSystem?: string;
|
|
40
|
+
}>): {
|
|
41
|
+
cards: Readonly<{
|
|
42
|
+
fullUrl: string;
|
|
43
|
+
resourceType: string;
|
|
44
|
+
id: string;
|
|
45
|
+
/** Only server-authorized index values; never clinical resource content. */
|
|
46
|
+
tags: Readonly<Record<string, readonly string[]>>;
|
|
47
|
+
}>[];
|
|
48
|
+
total: number;
|
|
49
|
+
countsByResourceType: Record<string, number>;
|
|
50
|
+
entries: JsonObject[];
|
|
51
|
+
};
|
|
27
52
|
export {};
|
|
@@ -79,6 +79,59 @@ export function summarizeVeterinaryTaggedIndex(source, options = {}) {
|
|
|
79
79
|
}
|
|
80
80
|
return { total: entries.length, countsByResourceType, entries };
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Strict UI reader for an index-only Bundle. Unlike the clinical Bundle
|
|
84
|
+
* Reader, this refuses claims and every clinical resource field.
|
|
85
|
+
*/
|
|
86
|
+
export function readVeterinaryBundleTags(source, options) {
|
|
87
|
+
if (source.resourceType !== 'Bundle' || source.type !== 'searchset' || !Array.isArray(source.entry)) {
|
|
88
|
+
throw new TypeError('bundle_tags_reader_searchset_required');
|
|
89
|
+
}
|
|
90
|
+
for (const rawEntry of source.entry) {
|
|
91
|
+
if (!isObject(rawEntry) || !onlyKeys(rawEntry, ['fullUrl', 'resource'])
|
|
92
|
+
|| typeof rawEntry.fullUrl !== 'string' || !isObject(rawEntry.resource)) {
|
|
93
|
+
throw new TypeError('bundle_tags_reader_unsafe_shape');
|
|
94
|
+
}
|
|
95
|
+
const resource = rawEntry.resource;
|
|
96
|
+
if (!onlyKeys(resource, ['resourceType', 'id', 'meta'])
|
|
97
|
+
|| typeof resource.resourceType !== 'string' || typeof resource.id !== 'string'
|
|
98
|
+
|| !isObject(resource.meta) || !onlyKeys(resource.meta, ['tag'])
|
|
99
|
+
|| !Array.isArray(resource.meta.tag)) {
|
|
100
|
+
throw new TypeError('bundle_tags_reader_unsafe_shape');
|
|
101
|
+
}
|
|
102
|
+
if (!options.allowedResourceTypes.has(resource.resourceType)) {
|
|
103
|
+
throw new TypeError('bundle_tags_reader_resource_forbidden');
|
|
104
|
+
}
|
|
105
|
+
for (const rawTag of resource.meta.tag) {
|
|
106
|
+
if (!isObject(rawTag) || !onlyKeys(rawTag, ['system', 'code'])
|
|
107
|
+
|| typeof rawTag.system !== 'string' || typeof rawTag.code !== 'string') {
|
|
108
|
+
throw new TypeError('bundle_tags_reader_unsafe_shape');
|
|
109
|
+
}
|
|
110
|
+
if (!options.allowedTagSystems.has(rawTag.system)
|
|
111
|
+
|| !rawTag.system.startsWith(`${resource.resourceType}.`)) {
|
|
112
|
+
throw new TypeError('bundle_tags_reader_tag_forbidden');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const summary = summarizeVeterinaryTaggedIndex(source, { sortSystem: options.sortSystem });
|
|
117
|
+
const cards = summary.entries.map(entry => {
|
|
118
|
+
const resource = entry.resource;
|
|
119
|
+
const meta = resource.meta;
|
|
120
|
+
const tags = {};
|
|
121
|
+
for (const rawTag of meta.tag) {
|
|
122
|
+
const values = tags[rawTag.system] || [];
|
|
123
|
+
values.push(rawTag.code);
|
|
124
|
+
tags[rawTag.system] = values;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
fullUrl: entry.fullUrl,
|
|
128
|
+
resourceType: resource.resourceType,
|
|
129
|
+
id: resource.id,
|
|
130
|
+
tags,
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
return { ...summary, cards };
|
|
134
|
+
}
|
|
82
135
|
function deriveValues(resource, parameter) {
|
|
83
136
|
switch (`${String(resource.resourceType)}.${parameter}`) {
|
|
84
137
|
case 'Composition.date': return scalar(resource.date);
|
|
@@ -101,6 +154,9 @@ function deriveValues(resource, parameter) {
|
|
|
101
154
|
case 'Condition.severity': return conceptCodes(resource.severity);
|
|
102
155
|
case 'Condition.code': return conceptCodes(resource.code);
|
|
103
156
|
case 'Condition.language': return scalar(resource.language);
|
|
157
|
+
case 'Observation.date': return [...dateChoice(resource, 'effective'), ...scalar(resource.issued)];
|
|
158
|
+
case 'Observation.status': return scalar(resource.status);
|
|
159
|
+
case 'Observation.language': return scalar(resource.language);
|
|
104
160
|
case 'MedicationStatement.effective': return dateChoice(resource, 'effective');
|
|
105
161
|
case 'MedicationStatement.status': return scalar(resource.status);
|
|
106
162
|
case 'MedicationStatement.medication': return conceptCodes(resource.medicationCodeableConcept);
|
|
@@ -181,3 +237,7 @@ function uniqueTags(tags) {
|
|
|
181
237
|
function isObject(value) {
|
|
182
238
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
183
239
|
}
|
|
240
|
+
function onlyKeys(value, allowed) {
|
|
241
|
+
const names = new Set(allowed);
|
|
242
|
+
return Object.keys(value).every(key => names.has(key));
|
|
243
|
+
}
|