vet-sdk-core-ts 0.4.36 → 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 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
- * Projects one controller-authorized animal to the GW Organization envelope.
23
+ * Creates the claims-first administrative draft for one animal.
22
24
  *
23
- * Species remains separate governed metadata. It validates the Damm digit with
24
- * jurisdiction and animalNumericId15 but is never decoded from cardNumber21.
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 buildVetChainAnimalOnboardingClaims(input) {
29
+ export function buildVetChainAnimalDraftClaims(input) {
27
30
  const subjectId = required(input.subjectId, 'vet_animal_subject_id_invalid');
28
- const alternateName = required(input.alternateName, 'vet_animal_name_invalid');
29
- const card = parseVetChainAnimalCardDid(input.cardDidWeb);
30
- const ncbiTaxonomyId = normalizeVetChainNcbiTaxonomyId(input.ncbiTaxonomyId);
31
- const expectedAnimalId16 = buildAnimalIdentifier16({
32
- speciesId7: buildSpeciesId7(ncbiTaxonomyId),
33
- jurisdictionCode5: card.jurisdictionCode5,
34
- animalNumericId15: card.animalNumericId15,
35
- });
36
- if (expectedAnimalId16 !== card.animalId16) {
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
- [claim.alternateName]: alternateName,
49
+ ...(alternateName ? {
50
+ [claim.alternateName]: alternateName,
51
+ [claim.memberName]: alternateName,
52
+ } : {}),
49
53
  ...(legalName ? { [claim.legalName]: legalName } : {}),
50
- [claim.sameAs]: card.did,
51
- [claim.memberName]: alternateName,
52
- [claim.memberAdditionalType]: `http://purl.obolibrary.org/obo/NCBITaxon_${ncbiTaxonomyId}`,
53
- [claim.memberRole]: 'RESPRSN',
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vet-sdk-core-ts",
3
- "version": "0.4.36",
3
+ "version": "0.4.38",
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",
@@ -89,6 +89,6 @@
89
89
  "@noble/hashes": "^2.2.0",
90
90
  "@noble/post-quantum": "0.5.4",
91
91
  "gdc-common-utils-ts": "2.9.10",
92
- "vet-data-utils-ts": "0.5.27"
92
+ "vet-data-utils-ts": "0.5.28"
93
93
  }
94
94
  }