vet-sdk-core-ts 0.4.21 → 0.4.23
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 +9 -0
- package/dist/health-card-issuance.d.ts +23 -0
- package/dist/health-card-issuance.js +22 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/scheduling.d.ts +52 -0
- package/dist/scheduling.js +88 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -9,6 +9,15 @@ UHC SDK packages and must not import them.
|
|
|
9
9
|
The SDK consumes governed browser-safe values from `vet-data-utils-ts` and
|
|
10
10
|
owns gateway request construction. GW VET remains the policy authority.
|
|
11
11
|
|
|
12
|
+
## Veterinary scheduling bundles
|
|
13
|
+
|
|
14
|
+
The `vet-sdk-core-ts/scheduling` entrypoint extends the generic GW bundle
|
|
15
|
+
boundary without changing `gdc-*`. It builds claims-first JSON:API batches,
|
|
16
|
+
reads canonical `body.data[].resource` search matches and reconciles removed
|
|
17
|
+
occupied Slots as pending Appointments plus durable rescheduling
|
|
18
|
+
Communications. The same product extension can later be promoted for SOSChain
|
|
19
|
+
or UHC/UNID after their resource policy is defined.
|
|
20
|
+
|
|
12
21
|
## Veterinary health-card issuance
|
|
13
22
|
|
|
14
23
|
`issueVeterinaryHealthCardCredential(...)` accepts only an authoritative
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type PostQuantumCompanionProofQrOptions, type SmartHealthCardPayloadReferences } from 'vet-data-utils-ts/shc';
|
|
2
2
|
import { type InternationalHealthCardPrintData } from 'vet-data-utils-ts/international-health-card';
|
|
3
|
+
import { type ClinicalBundleReaderOptions } from 'vet-data-utils-ts/clinical-bundle-reader';
|
|
4
|
+
import type { SmartHealthCardPayload } from 'vet-data-utils-ts/shc';
|
|
3
5
|
export type VeterinaryHealthCardAttester = Readonly<{
|
|
4
6
|
/** Signs the exact uncompressed, deterministic SHC payload as RFC 7797 JWS. */
|
|
5
7
|
signDetachedPqc(payloadBytes: Uint8Array): Promise<string>;
|
|
@@ -44,3 +46,24 @@ export declare function verifyVeterinaryHealthCardCredential(input: Readonly<{
|
|
|
44
46
|
pqcCompanionProofs: readonly boolean[];
|
|
45
47
|
valid: boolean;
|
|
46
48
|
}>>;
|
|
49
|
+
/**
|
|
50
|
+
* Verifies every scanned proof before exposing the SHC Bundle to the shared
|
|
51
|
+
* IPS reader. Terminology enrichment runs only on a presentation copy after
|
|
52
|
+
* verification, so neither signature bytes nor payload hashes can change.
|
|
53
|
+
*/
|
|
54
|
+
export declare function readVerifiedVeterinaryHealthCardCredential(input: Readonly<{
|
|
55
|
+
shcQr: readonly string[];
|
|
56
|
+
pqcQr: readonly (readonly string[])[];
|
|
57
|
+
language: string;
|
|
58
|
+
verifyEs256(compactJws: string): Promise<boolean>;
|
|
59
|
+
verifyDetachedPqc(payloadBytes: Uint8Array, detachedJws: string): Promise<boolean>;
|
|
60
|
+
resolveTerminology: ClinicalBundleReaderOptions['resolveTerminology'];
|
|
61
|
+
}>): Promise<Readonly<{
|
|
62
|
+
verification: Readonly<{
|
|
63
|
+
shc: boolean;
|
|
64
|
+
pqcCompanionProofs: readonly boolean[];
|
|
65
|
+
valid: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
payload: SmartHealthCardPayload;
|
|
68
|
+
bundle: Record<string, unknown>;
|
|
69
|
+
}>>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assembleSmartHealthCardJws, buildSmartHealthCardPayloadReferences, decodePostQuantumCompanionProofQr, decodeSmartHealthCardPayloadBytes, decodeSmartHealthCardQr, encodePostQuantumCompanionProofUri, encodePostQuantumCompanionProofQr, encodeSmartHealthCardQr, prepareSmartHealthCard, } from 'vet-data-utils-ts/shc';
|
|
2
2
|
import { buildInternationalHealthCardPrintData, } from 'vet-data-utils-ts/international-health-card';
|
|
3
|
+
import { projectClinicalBundleForReading, } from 'vet-data-utils-ts/clinical-bundle-reader';
|
|
3
4
|
/**
|
|
4
5
|
* Issues transport values only after the caller supplies GW's authoritative
|
|
5
6
|
* readback Bundle. The tenant issuer owns the standard ES256 signature. Every
|
|
@@ -54,3 +55,24 @@ export async function verifyVeterinaryHealthCardCredential(input) {
|
|
|
54
55
|
valid: shc && pqcCompanionProofs.length > 0 && pqcCompanionProofs.every(Boolean),
|
|
55
56
|
};
|
|
56
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Verifies every scanned proof before exposing the SHC Bundle to the shared
|
|
60
|
+
* IPS reader. Terminology enrichment runs only on a presentation copy after
|
|
61
|
+
* verification, so neither signature bytes nor payload hashes can change.
|
|
62
|
+
*/
|
|
63
|
+
export async function readVerifiedVeterinaryHealthCardCredential(input) {
|
|
64
|
+
const compactJws = decodeSmartHealthCardQr(input.shcQr);
|
|
65
|
+
const payloadBytes = decodeSmartHealthCardPayloadBytes(compactJws);
|
|
66
|
+
const shc = await input.verifyEs256(compactJws);
|
|
67
|
+
const pqcCompanionProofs = await Promise.all(input.pqcQr.map(qrSet => (input.verifyDetachedPqc(payloadBytes, decodePostQuantumCompanionProofQr(qrSet)))));
|
|
68
|
+
const verification = {
|
|
69
|
+
shc,
|
|
70
|
+
pqcCompanionProofs,
|
|
71
|
+
valid: shc && pqcCompanionProofs.length > 0 && pqcCompanionProofs.every(Boolean),
|
|
72
|
+
};
|
|
73
|
+
if (!verification.valid)
|
|
74
|
+
throw new TypeError('veterinary_health_card_proof_invalid');
|
|
75
|
+
const payload = JSON.parse(new TextDecoder().decode(payloadBytes));
|
|
76
|
+
const bundle = await projectClinicalBundleForReading(payload.vc.credentialSubject.fhirBundle, { language: input.language, resolveTerminology: input.resolveTerminology });
|
|
77
|
+
return { verification, payload, bundle };
|
|
78
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type VeterinarySchedulingFlatClaimsResource, type VeterinarySchedulingResourceType } from 'vet-data-utils-ts/scheduling';
|
|
2
|
+
export type VeterinarySchedulingBatchMethod = 'POST' | 'PUT';
|
|
3
|
+
export type VeterinarySchedulingBatch = Readonly<{
|
|
4
|
+
data: readonly Readonly<{
|
|
5
|
+
type: `${VeterinarySchedulingResourceType}-v5.0.0`;
|
|
6
|
+
resource: VeterinarySchedulingFlatClaimsResource;
|
|
7
|
+
request: Readonly<{
|
|
8
|
+
method: VeterinarySchedulingBatchMethod;
|
|
9
|
+
url: string;
|
|
10
|
+
}>;
|
|
11
|
+
}>[];
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* Extends the generic GW JSON:API batch boundary for VetChain scheduling.
|
|
15
|
+
* Resources are revalidated at this product boundary and remain claims-only.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildVeterinarySchedulingBatch(resources: readonly unknown[], method?: VeterinarySchedulingBatchMethod): VeterinarySchedulingBatch;
|
|
18
|
+
/** Builds the governed claims-first search envelope accepted by GW VET. */
|
|
19
|
+
export declare function buildVeterinarySchedulingSearch(resourceType: VeterinarySchedulingResourceType, claims: Readonly<Record<string, unknown>>): Readonly<{
|
|
20
|
+
data: readonly Readonly<{
|
|
21
|
+
resource: Readonly<{
|
|
22
|
+
resourceType: VeterinarySchedulingResourceType;
|
|
23
|
+
id: string;
|
|
24
|
+
meta: Readonly<{
|
|
25
|
+
claims: Readonly<Record<string, readonly string[]>>;
|
|
26
|
+
}>;
|
|
27
|
+
}>;
|
|
28
|
+
}>[];
|
|
29
|
+
}>;
|
|
30
|
+
/** Reads canonical GW search matches from `body.data[].resource`. */
|
|
31
|
+
export declare function readVeterinarySchedulingSearch(body: unknown, expectedResourceType: VeterinarySchedulingResourceType): readonly VeterinarySchedulingFlatClaimsResource[];
|
|
32
|
+
export type VeterinaryScheduleReconciliation = Readonly<{
|
|
33
|
+
appointments: readonly VeterinarySchedulingFlatClaimsResource[];
|
|
34
|
+
notifications: readonly Readonly<{
|
|
35
|
+
resourceType: 'Communication';
|
|
36
|
+
id: string;
|
|
37
|
+
meta: Readonly<{
|
|
38
|
+
claims: Readonly<Record<string, readonly string[]>>;
|
|
39
|
+
}>;
|
|
40
|
+
}>[];
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Produces recoverable updates for bookings whose slots disappeared.
|
|
44
|
+
* It deliberately does not cancel: the patient receives a durable
|
|
45
|
+
* Communication and may choose another slot before a final decline.
|
|
46
|
+
*/
|
|
47
|
+
export declare function reconcileVeterinaryAppointmentsAfterSlotChange(input: Readonly<{
|
|
48
|
+
appointments: readonly unknown[];
|
|
49
|
+
availableSlotReferences: ReadonlySet<string>;
|
|
50
|
+
subjectReferenceFor: (appointment: VeterinarySchedulingFlatClaimsResource) => string;
|
|
51
|
+
reason: string;
|
|
52
|
+
}>): VeterinaryScheduleReconciliation;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { buildVeterinaryAppointmentNotificationResource, normalizeVeterinarySchedulingFlatClaimsResource, } from 'vet-data-utils-ts/scheduling';
|
|
2
|
+
const asArrayClaims = (claims) => Object.freeze(Object.fromEntries(Object.entries(claims).map(([name, raw]) => [
|
|
3
|
+
name,
|
|
4
|
+
Object.freeze((Array.isArray(raw) ? raw : [raw]).map(value => String(value))),
|
|
5
|
+
])));
|
|
6
|
+
/**
|
|
7
|
+
* Extends the generic GW JSON:API batch boundary for VetChain scheduling.
|
|
8
|
+
* Resources are revalidated at this product boundary and remain claims-only.
|
|
9
|
+
*/
|
|
10
|
+
export function buildVeterinarySchedulingBatch(resources, method = 'POST') {
|
|
11
|
+
return Object.freeze({
|
|
12
|
+
data: Object.freeze(resources.map(candidate => {
|
|
13
|
+
const resource = normalizeVeterinarySchedulingFlatClaimsResource(candidate);
|
|
14
|
+
return Object.freeze({
|
|
15
|
+
type: `${resource.resourceType}-v5.0.0`,
|
|
16
|
+
resource,
|
|
17
|
+
request: Object.freeze({ method, url: `${resource.resourceType}/${resource.id}` }),
|
|
18
|
+
});
|
|
19
|
+
})),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Builds the governed claims-first search envelope accepted by GW VET. */
|
|
23
|
+
export function buildVeterinarySchedulingSearch(resourceType, claims) {
|
|
24
|
+
const resource = normalizeVeterinarySchedulingFlatClaimsResource({
|
|
25
|
+
resourceType,
|
|
26
|
+
id: 'search',
|
|
27
|
+
meta: { claims },
|
|
28
|
+
});
|
|
29
|
+
return Object.freeze({ data: Object.freeze([Object.freeze({ resource })]) });
|
|
30
|
+
}
|
|
31
|
+
/** Reads canonical GW search matches from `body.data[].resource`. */
|
|
32
|
+
export function readVeterinarySchedulingSearch(body, expectedResourceType) {
|
|
33
|
+
if (!body || typeof body !== 'object' || !Array.isArray(body.data)) {
|
|
34
|
+
throw new TypeError('veterinary_scheduling_search_invalid');
|
|
35
|
+
}
|
|
36
|
+
return Object.freeze(body.data.map(entry => {
|
|
37
|
+
if (!entry || typeof entry !== 'object' || !('resource' in entry)) {
|
|
38
|
+
throw new TypeError('veterinary_scheduling_search_resource_invalid');
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const resource = normalizeVeterinarySchedulingFlatClaimsResource(entry.resource);
|
|
42
|
+
if (resource.resourceType !== expectedResourceType)
|
|
43
|
+
throw new Error('wrong type');
|
|
44
|
+
return resource;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new TypeError('veterinary_scheduling_search_resource_invalid');
|
|
48
|
+
}
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Produces recoverable updates for bookings whose slots disappeared.
|
|
53
|
+
* It deliberately does not cancel: the patient receives a durable
|
|
54
|
+
* Communication and may choose another slot before a final decline.
|
|
55
|
+
*/
|
|
56
|
+
export function reconcileVeterinaryAppointmentsAfterSlotChange(input) {
|
|
57
|
+
const appointments = [];
|
|
58
|
+
const notifications = [];
|
|
59
|
+
for (const candidate of input.appointments) {
|
|
60
|
+
const appointment = normalizeVeterinarySchedulingFlatClaimsResource(candidate);
|
|
61
|
+
if (appointment.resourceType !== 'Appointment')
|
|
62
|
+
throw new TypeError('veterinary_scheduling_appointment_required');
|
|
63
|
+
const slots = appointment.meta.claims['Appointment.slot'] ?? [];
|
|
64
|
+
if (slots.length > 0 && slots.every(slot => input.availableSlotReferences.has(slot)))
|
|
65
|
+
continue;
|
|
66
|
+
const updated = normalizeVeterinarySchedulingFlatClaimsResource({
|
|
67
|
+
...appointment,
|
|
68
|
+
meta: { claims: {
|
|
69
|
+
...appointment.meta.claims,
|
|
70
|
+
'Appointment.status': ['pending'],
|
|
71
|
+
'VeterinaryAppointment.reschedulingReason': [input.reason],
|
|
72
|
+
} },
|
|
73
|
+
});
|
|
74
|
+
appointments.push(updated);
|
|
75
|
+
const notification = buildVeterinaryAppointmentNotificationResource({
|
|
76
|
+
id: `${appointment.id}-schedule-change`,
|
|
77
|
+
appointmentReference: `Appointment/${appointment.id}`,
|
|
78
|
+
subjectReference: input.subjectReferenceFor(appointment),
|
|
79
|
+
reason: input.reason,
|
|
80
|
+
});
|
|
81
|
+
notifications.push(Object.freeze({
|
|
82
|
+
resourceType: 'Communication',
|
|
83
|
+
id: notification.id,
|
|
84
|
+
meta: Object.freeze({ claims: asArrayClaims(notification.meta.claims) }),
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
return Object.freeze({ appointments: Object.freeze(appointments), notifications: Object.freeze(notifications) });
|
|
88
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vet-sdk-core-ts",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.23",
|
|
4
4
|
"description": "Browser-safe VetChain core contracts and governed animal species identifiers",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Connecting Solution & Applications Ltd",
|
|
@@ -47,6 +47,10 @@
|
|
|
47
47
|
"./health-card-issuance": {
|
|
48
48
|
"types": "./dist/health-card-issuance.d.ts",
|
|
49
49
|
"default": "./dist/health-card-issuance.js"
|
|
50
|
+
},
|
|
51
|
+
"./scheduling": {
|
|
52
|
+
"types": "./dist/scheduling.d.ts",
|
|
53
|
+
"default": "./dist/scheduling.js"
|
|
50
54
|
}
|
|
51
55
|
},
|
|
52
56
|
"files": [
|
|
@@ -72,6 +76,6 @@
|
|
|
72
76
|
"dependencies": {
|
|
73
77
|
"@noble/hashes": "^2.2.0",
|
|
74
78
|
"gdc-common-utils-ts": "2.9.10",
|
|
75
|
-
"vet-data-utils-ts": "0.5.
|
|
79
|
+
"vet-data-utils-ts": "0.5.10"
|
|
76
80
|
}
|
|
77
81
|
}
|