vet-sdk-core-ts 0.4.37 → 0.4.38
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 +13 -0
- package/dist/animal-onboarding.d.ts +36 -0
- package/dist/animal-onboarding.js +69 -20
- package/docs/101-ANIMAL-INDIVIDUAL-ONBOARDING.md +37 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# VetChain Core SDK
|
|
2
2
|
|
|
3
|
+
## Animal Individual Organization identity
|
|
4
|
+
|
|
5
|
+
An animal is the first member of its own Individual Organization, so
|
|
6
|
+
`Organization.member.role` is always the HL7 code `ONESELF`. The authenticated
|
|
7
|
+
human that creates it is `Organization.owner`, the controller by default;
|
|
8
|
+
`RESPRSN` must never be copied into the animal member.
|
|
9
|
+
|
|
10
|
+
Use `buildVetChainAnimalDraftClaims(...)` while the name, exact NCBI species or
|
|
11
|
+
public Card is missing. Only a draft for which
|
|
12
|
+
`readVetChainAnimalOnboardingReadiness(...)` returns `ready: true` may be
|
|
13
|
+
activated and issued a Card. See
|
|
14
|
+
[`docs/101-ANIMAL-INDIVIDUAL-ONBOARDING.md`](docs/101-ANIMAL-INDIVIDUAL-ONBOARDING.md).
|
|
15
|
+
|
|
3
16
|
`vet-sdk-core-ts/place-service-directory` is the VetChain-only bundle boundary
|
|
4
17
|
for claims-first Schema.org `Place` and `Service` resources. It builds GW VET
|
|
5
18
|
batch/search envelopes and joins authorized directory search responses for the
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** Canonical member relationship for the animal SELF subject. */
|
|
2
|
+
export declare const VetChainAnimalMemberRoles: Readonly<{
|
|
3
|
+
readonly Self: "ONESELF";
|
|
4
|
+
}>;
|
|
1
5
|
export type VetChainAnimalOnboardingInput = Readonly<{
|
|
2
6
|
subjectId: string;
|
|
3
7
|
cardDidWeb: string;
|
|
@@ -11,6 +15,23 @@ export type VetChainAnimalOnboardingInput = Readonly<{
|
|
|
11
15
|
controllerTelephone?: string;
|
|
12
16
|
sector?: string;
|
|
13
17
|
}>;
|
|
18
|
+
export type VetChainAnimalDraftInput = Readonly<{
|
|
19
|
+
subjectId: string;
|
|
20
|
+
cardDidWeb?: string;
|
|
21
|
+
alternateName?: string;
|
|
22
|
+
legalName?: string;
|
|
23
|
+
birthDate?: string;
|
|
24
|
+
birthYear?: number;
|
|
25
|
+
gender?: 'female' | 'male' | 'other' | 'unknown';
|
|
26
|
+
ncbiTaxonomyId?: string;
|
|
27
|
+
controllerEmail?: string;
|
|
28
|
+
controllerTelephone?: string;
|
|
29
|
+
sector?: string;
|
|
30
|
+
}>;
|
|
31
|
+
export type VetChainAnimalOnboardingReadiness = Readonly<{
|
|
32
|
+
ready: boolean;
|
|
33
|
+
missing: ReadonlyArray<'alternateName' | 'species' | 'card'>;
|
|
34
|
+
}>;
|
|
14
35
|
/** Canonical schema.org claim names for the VetChain animal registration. */
|
|
15
36
|
export declare const VetChainAnimalOnboardingClaimNames: Readonly<{
|
|
16
37
|
readonly identifierValue: "org.schema.Organization.identifier.value";
|
|
@@ -27,9 +48,24 @@ export declare const VetChainAnimalOnboardingClaimNames: Readonly<{
|
|
|
27
48
|
readonly ownerTelephone: "org.schema.Organization.owner.telephone";
|
|
28
49
|
readonly serviceCategory: "org.schema.Service.category";
|
|
29
50
|
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Creates the claims-first administrative draft for one animal.
|
|
53
|
+
*
|
|
54
|
+
* A draft always identifies its private subject and the creating controller,
|
|
55
|
+
* but name, NCBI species and public Card DID may remain absent. The animal is
|
|
56
|
+
* still its own `ONESELF` member; `Organization.owner` is the controller.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildVetChainAnimalDraftClaims(input: VetChainAnimalDraftInput): Readonly<Record<string, string>>;
|
|
59
|
+
/** Reports whether an animal draft can be activated and issued a public Card. */
|
|
60
|
+
export declare function readVetChainAnimalOnboardingReadiness(claims: Readonly<Record<string, unknown>>): VetChainAnimalOnboardingReadiness;
|
|
30
61
|
/**
|
|
31
62
|
* Projects one controller-authorized animal to the GW Organization envelope.
|
|
32
63
|
*
|
|
64
|
+
* The animal is the envelope's first member and therefore has the HL7
|
|
65
|
+
* `ONESELF` relationship. The authenticated human that creates it is kept in
|
|
66
|
+
* `Organization.owner`; controller authority such as `RESPRSN` is never
|
|
67
|
+
* copied into `Organization.member.role`.
|
|
68
|
+
*
|
|
33
69
|
* Species remains separate governed metadata. It validates the Damm digit with
|
|
34
70
|
* jurisdiction and animalNumericId15 but is never decoded from cardNumber21.
|
|
35
71
|
*/
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { parseVetChainAnimalCardDid } from 'vet-data-utils-ts';
|
|
2
2
|
import { buildAnimalIdentifier16, buildSpeciesId7 } from './animal-identity.js';
|
|
3
3
|
import { normalizeVetChainNcbiTaxonomyId } from './card-issuance.js';
|
|
4
|
+
/** Canonical member relationship for the animal SELF subject. */
|
|
5
|
+
export const VetChainAnimalMemberRoles = Object.freeze({ Self: 'ONESELF' });
|
|
4
6
|
/** Canonical schema.org claim names for the VetChain animal registration. */
|
|
5
7
|
export const VetChainAnimalOnboardingClaimNames = Object.freeze({
|
|
6
8
|
identifierValue: 'org.schema.Organization.identifier.value',
|
|
@@ -18,24 +20,23 @@ export const VetChainAnimalOnboardingClaimNames = Object.freeze({
|
|
|
18
20
|
serviceCategory: 'org.schema.Service.category',
|
|
19
21
|
});
|
|
20
22
|
/**
|
|
21
|
-
*
|
|
23
|
+
* Creates the claims-first administrative draft for one animal.
|
|
22
24
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
+
* A draft always identifies its private subject and the creating controller,
|
|
26
|
+
* but name, NCBI species and public Card DID may remain absent. The animal is
|
|
27
|
+
* still its own `ONESELF` member; `Organization.owner` is the controller.
|
|
25
28
|
*/
|
|
26
|
-
export function
|
|
29
|
+
export function buildVetChainAnimalDraftClaims(input) {
|
|
27
30
|
const subjectId = required(input.subjectId, 'vet_animal_subject_id_invalid');
|
|
28
|
-
const alternateName =
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
throw new TypeError('vet_animal_card_species_checksum_invalid');
|
|
38
|
-
}
|
|
31
|
+
const alternateName = String(input.alternateName || '').trim();
|
|
32
|
+
const ncbiTaxonomyId = String(input.ncbiTaxonomyId || '').trim()
|
|
33
|
+
? normalizeVetChainNcbiTaxonomyId(String(input.ncbiTaxonomyId))
|
|
34
|
+
: '';
|
|
35
|
+
const cardDidWeb = String(input.cardDidWeb || '').trim();
|
|
36
|
+
if (cardDidWeb && !ncbiTaxonomyId)
|
|
37
|
+
throw new TypeError('vet_animal_card_species_required');
|
|
38
|
+
if (cardDidWeb)
|
|
39
|
+
assertCardSpecies(cardDidWeb, ncbiTaxonomyId);
|
|
39
40
|
const birthDate = String(input.birthDate || '').trim()
|
|
40
41
|
|| (Number.isInteger(input.birthYear) ? String(input.birthYear) : '');
|
|
41
42
|
const legalName = String(input.legalName || '').trim();
|
|
@@ -45,12 +46,16 @@ export function buildVetChainAnimalOnboardingClaims(input) {
|
|
|
45
46
|
'@context': 'org.schema',
|
|
46
47
|
[claim.identifierValue]: subjectId,
|
|
47
48
|
[claim.additionalType]: 'animal',
|
|
48
|
-
|
|
49
|
+
...(alternateName ? {
|
|
50
|
+
[claim.alternateName]: alternateName,
|
|
51
|
+
[claim.memberName]: alternateName,
|
|
52
|
+
} : {}),
|
|
49
53
|
...(legalName ? { [claim.legalName]: legalName } : {}),
|
|
50
|
-
[claim.sameAs]:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
...(cardDidWeb ? { [claim.sameAs]: cardDidWeb } : {}),
|
|
55
|
+
...(ncbiTaxonomyId ? {
|
|
56
|
+
[claim.memberAdditionalType]: `http://purl.obolibrary.org/obo/NCBITaxon_${ncbiTaxonomyId}`,
|
|
57
|
+
} : {}),
|
|
58
|
+
[claim.memberRole]: VetChainAnimalMemberRoles.Self,
|
|
54
59
|
...(birthDate ? { [claim.memberBirthDate]: birthDate } : {}),
|
|
55
60
|
...(gender ? { [claim.memberGender]: gender } : {}),
|
|
56
61
|
[claim.ownerEmail]: String(input.controllerEmail || '').trim(),
|
|
@@ -58,6 +63,50 @@ export function buildVetChainAnimalOnboardingClaims(input) {
|
|
|
58
63
|
[claim.serviceCategory]: String(input.sector || '').trim() || 'animal-care',
|
|
59
64
|
});
|
|
60
65
|
}
|
|
66
|
+
/** Reports whether an animal draft can be activated and issued a public Card. */
|
|
67
|
+
export function readVetChainAnimalOnboardingReadiness(claims) {
|
|
68
|
+
const claim = VetChainAnimalOnboardingClaimNames;
|
|
69
|
+
const missing = [];
|
|
70
|
+
if (!String(claims[claim.alternateName] || '').trim())
|
|
71
|
+
missing.push('alternateName');
|
|
72
|
+
if (!/^http:\/\/purl\.obolibrary\.org\/obo\/NCBITaxon_[1-9]\d*$/.test(String(claims[claim.memberAdditionalType] || '').trim())) {
|
|
73
|
+
missing.push('species');
|
|
74
|
+
}
|
|
75
|
+
if (!String(claims[claim.sameAs] || '').trim())
|
|
76
|
+
missing.push('card');
|
|
77
|
+
return Object.freeze({ ready: missing.length === 0, missing: Object.freeze(missing) });
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Projects one controller-authorized animal to the GW Organization envelope.
|
|
81
|
+
*
|
|
82
|
+
* The animal is the envelope's first member and therefore has the HL7
|
|
83
|
+
* `ONESELF` relationship. The authenticated human that creates it is kept in
|
|
84
|
+
* `Organization.owner`; controller authority such as `RESPRSN` is never
|
|
85
|
+
* copied into `Organization.member.role`.
|
|
86
|
+
*
|
|
87
|
+
* Species remains separate governed metadata. It validates the Damm digit with
|
|
88
|
+
* jurisdiction and animalNumericId15 but is never decoded from cardNumber21.
|
|
89
|
+
*/
|
|
90
|
+
export function buildVetChainAnimalOnboardingClaims(input) {
|
|
91
|
+
const alternateName = required(input.alternateName, 'vet_animal_name_invalid');
|
|
92
|
+
const ncbiTaxonomyId = normalizeVetChainNcbiTaxonomyId(input.ncbiTaxonomyId);
|
|
93
|
+
const claims = buildVetChainAnimalDraftClaims({ ...input, alternateName, ncbiTaxonomyId });
|
|
94
|
+
const readiness = readVetChainAnimalOnboardingReadiness(claims);
|
|
95
|
+
if (!readiness.ready)
|
|
96
|
+
throw new TypeError(`vet_animal_onboarding_incomplete:${readiness.missing.join(',')}`);
|
|
97
|
+
return claims;
|
|
98
|
+
}
|
|
99
|
+
function assertCardSpecies(cardDidWeb, ncbiTaxonomyId) {
|
|
100
|
+
const card = parseVetChainAnimalCardDid(cardDidWeb);
|
|
101
|
+
const expectedAnimalId16 = buildAnimalIdentifier16({
|
|
102
|
+
speciesId7: buildSpeciesId7(ncbiTaxonomyId),
|
|
103
|
+
jurisdictionCode5: card.jurisdictionCode5,
|
|
104
|
+
animalNumericId15: card.animalNumericId15,
|
|
105
|
+
});
|
|
106
|
+
if (expectedAnimalId16 !== card.animalId16) {
|
|
107
|
+
throw new TypeError('vet_animal_card_species_checksum_invalid');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
61
110
|
function required(value, error) {
|
|
62
111
|
const normalized = String(value || '').trim();
|
|
63
112
|
if (!normalized)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Animal Individual Organization onboarding 101
|
|
2
|
+
|
|
3
|
+
The animal and the human controller are two different identities:
|
|
4
|
+
|
|
5
|
+
- `Organization.member` is the indexed animal itself. Its relationship is
|
|
6
|
+
`ONESELF`.
|
|
7
|
+
- `Organization.owner` is the authenticated human that creates and controls
|
|
8
|
+
the Individual Organization by default. Controller authority may be
|
|
9
|
+
represented as `RESPRSN`, but that value never becomes the animal's member
|
|
10
|
+
role.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import {
|
|
14
|
+
buildVetChainAnimalDraftClaims,
|
|
15
|
+
readVetChainAnimalOnboardingReadiness,
|
|
16
|
+
} from 'vet-sdk-core-ts/animal-onboarding'
|
|
17
|
+
|
|
18
|
+
// Step 1. The controller starts a private draft using a verified contact.
|
|
19
|
+
const draft = buildVetChainAnimalDraftClaims({
|
|
20
|
+
subjectId: crypto.randomUUID(),
|
|
21
|
+
controllerTelephone: '+12365162385',
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Step 2. The draft has member=SELF/ONESELF and owner=controller.
|
|
25
|
+
draft['org.schema.Organization.member.role'] // ONESELF
|
|
26
|
+
draft['org.schema.Organization.owner.telephone'] // verified controller
|
|
27
|
+
|
|
28
|
+
// Step 3. Do not issue or display a Card while required data is missing.
|
|
29
|
+
readVetChainAnimalOnboardingReadiness(draft)
|
|
30
|
+
// { ready: false, missing: ['alternateName', 'species', 'card'] }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The BFF later rebuilds the same subject draft with its name and exact NCBI
|
|
34
|
+
taxonomy, issues the public Card, and resubmits it under the same private
|
|
35
|
+
`subjectId`. GW VET alone performs the `pending -> active` transition. A draft
|
|
36
|
+
does not need a clinical `Composition`; the active clinical index is a
|
|
37
|
+
separate document lifecycle.
|
package/package.json
CHANGED