sos-sdk-node-ts 0.1.15 → 0.1.16
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.16 - 2026-10-22
|
|
6
|
+
|
|
7
|
+
- Recover checksum-valid historical animal cards through the provider's exact
|
|
8
|
+
stored alias and explicit taxonomy claim, while returning only the current
|
|
9
|
+
species-bound card identity to portals. New writes still reject the retired
|
|
10
|
+
identifier form.
|
|
11
|
+
|
|
5
12
|
## 0.1.15 - 2026-10-22
|
|
6
13
|
|
|
7
14
|
- Add product-neutral verification and resolution for an existing professional
|
|
@@ -17,6 +17,8 @@ export type AuthorizedIndividualSubjectDirectoryInput = Readonly<{
|
|
|
17
17
|
}>;
|
|
18
18
|
export type AuthorizedIndividualSubject = Readonly<{
|
|
19
19
|
subjectDid: string;
|
|
20
|
+
/** Historical provider lookup alias retained only while its record is migrated. */
|
|
21
|
+
sourceSubjectDid?: string;
|
|
20
22
|
organizationId?: string;
|
|
21
23
|
role?: string;
|
|
22
24
|
grantClaims: Readonly<Record<string, unknown>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { parseAnimalCardDidWeb } from 'sos-data-utils-ts/animal-card-interoperability';
|
|
2
|
+
import { migrateHistoricalAnimalCardDidWeb, parseAnimalCardDidWeb, parseNcbiTaxonomyUri, } from 'sos-data-utils-ts/animal-card-interoperability';
|
|
3
3
|
import { buildGatewayApplicationHeaders } from 'sos-data-utils-ts/gateway-application-identity';
|
|
4
4
|
const CLAIM_AUDIENCE_TYPE = 'org.schema.IndividualProduct.audience.audienceType';
|
|
5
5
|
const CLAIM_ITEM_CONDITION = 'org.schema.IndividualProduct.itemCondition';
|
|
@@ -10,6 +10,7 @@ const CLAIM_ORGANIZATION_OWNER_EMAIL = 'org.schema.Organization.owner.email';
|
|
|
10
10
|
const CLAIM_ORGANIZATION_OWNER_TELEPHONE = 'org.schema.Organization.owner.telephone';
|
|
11
11
|
const CLAIM_ORGANIZATION_OWNER_IDENTIFIER = 'org.schema.Organization.owner.identifier.value';
|
|
12
12
|
const CLAIM_ORGANIZATION_MEMBER_ROLE = 'org.schema.Organization.member.role';
|
|
13
|
+
const CLAIM_ORGANIZATION_MEMBER_ADDITIONAL_TYPE = 'org.schema.Organization.member.additionalType';
|
|
13
14
|
const CLAIM_RELATED_PERSON_ROLE = 'RelatedPerson.role';
|
|
14
15
|
const CLAIM_OCCUPATION_IDENTIFIER = 'org.schema.Person.hasOccupation.identifier.value';
|
|
15
16
|
/**
|
|
@@ -93,8 +94,10 @@ export class SosGatewaySubjectDirectoryClient {
|
|
|
93
94
|
const organization = readFirstOrganization(organizationResult.body);
|
|
94
95
|
if (!publicAliases(organization.claims[CLAIM_ORGANIZATION_SAME_AS]).includes(license.subjectDid))
|
|
95
96
|
continue;
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
const resolvedDid = resolveSubjectCardDid(license.subjectDid, context, organization.claims);
|
|
98
|
+
subjectsByDid.set(resolvedDid.subjectDid, Object.freeze({
|
|
99
|
+
subjectDid: resolvedDid.subjectDid,
|
|
100
|
+
...(resolvedDid.sourceSubjectDid ? { sourceSubjectDid: resolvedDid.sourceSubjectDid } : {}),
|
|
98
101
|
...(organization.organizationId ? { organizationId: organization.organizationId } : {}),
|
|
99
102
|
...(license.role ? { role: license.role } : {}),
|
|
100
103
|
grantClaims: license.grantClaims,
|
|
@@ -167,15 +170,17 @@ function readOwnedSubjects(body, context, verified) {
|
|
|
167
170
|
const ownerTelephone = String(claims[CLAIM_ORGANIZATION_OWNER_TELEPHONE] || '').trim();
|
|
168
171
|
if (!((verified.email && ownerEmail === verified.email) || (verified.telephone && ownerTelephone === verified.telephone)))
|
|
169
172
|
return [];
|
|
170
|
-
const
|
|
171
|
-
if (!
|
|
173
|
+
const sourceSubjectDid = selectSubjectCardAlias(claims[CLAIM_ORGANIZATION_SAME_AS], context);
|
|
174
|
+
if (!sourceSubjectDid)
|
|
172
175
|
return [];
|
|
176
|
+
const resolvedDid = resolveSubjectCardDid(sourceSubjectDid, context, claims);
|
|
173
177
|
const role = isAnimalSector(context.sector)
|
|
174
178
|
? 'RESPRSN'
|
|
175
179
|
: String(claims[CLAIM_ORGANIZATION_MEMBER_ROLE] || '').trim();
|
|
176
180
|
const relatedPersonId = String(claims[CLAIM_ORGANIZATION_OWNER_IDENTIFIER] || '').trim();
|
|
177
181
|
return [Object.freeze({
|
|
178
|
-
subjectDid,
|
|
182
|
+
subjectDid: resolvedDid.subjectDid,
|
|
183
|
+
...(resolvedDid.sourceSubjectDid ? { sourceSubjectDid: resolvedDid.sourceSubjectDid } : {}),
|
|
179
184
|
...(String(resource?.id || '').trim() ? { organizationId: String(resource.id).trim() } : {}),
|
|
180
185
|
...(role ? { role } : {}),
|
|
181
186
|
...(relatedPersonId ? { relatedPersonId } : {}),
|
|
@@ -195,7 +200,6 @@ function readAcceptedLicenses(body, context) {
|
|
|
195
200
|
return [];
|
|
196
201
|
if (!isSubjectCardCandidate(subjectDid, context))
|
|
197
202
|
return [];
|
|
198
|
-
requireCanonicalSubjectDid(subjectDid, context);
|
|
199
203
|
const claims = objectRecord(meta.claims);
|
|
200
204
|
const relatedRoles = String(claims[CLAIM_RELATED_PERSON_ROLE] || '')
|
|
201
205
|
.split(',').map((value) => value.trim().toUpperCase()).filter(Boolean);
|
|
@@ -229,9 +233,29 @@ function selectSubjectCardAlias(value, context) {
|
|
|
229
233
|
: alias.includes(':card:uhc:'));
|
|
230
234
|
if (!candidate)
|
|
231
235
|
return undefined;
|
|
232
|
-
|
|
236
|
+
if (!isAnimalSector(context.sector))
|
|
237
|
+
requireCanonicalSubjectDid(candidate, context);
|
|
233
238
|
return candidate;
|
|
234
239
|
}
|
|
240
|
+
function resolveSubjectCardDid(sourceSubjectDid, context, claims) {
|
|
241
|
+
if (!isAnimalSector(context.sector)) {
|
|
242
|
+
requireCanonicalSubjectDid(sourceSubjectDid, context);
|
|
243
|
+
return Object.freeze({ subjectDid: sourceSubjectDid });
|
|
244
|
+
}
|
|
245
|
+
if (parseAnimalCardDidWeb(sourceSubjectDid)) {
|
|
246
|
+
return Object.freeze({ subjectDid: sourceSubjectDid });
|
|
247
|
+
}
|
|
248
|
+
const taxonomyId = parseNcbiTaxonomyUri(claims[CLAIM_ORGANIZATION_MEMBER_ADDITIONAL_TYPE]);
|
|
249
|
+
const migrated = taxonomyId
|
|
250
|
+
? migrateHistoricalAnimalCardDidWeb(sourceSubjectDid, taxonomyId)
|
|
251
|
+
: undefined;
|
|
252
|
+
if (!migrated)
|
|
253
|
+
throw new Error('animal_card_did_invalid');
|
|
254
|
+
return Object.freeze({
|
|
255
|
+
subjectDid: migrated.cardDid,
|
|
256
|
+
sourceSubjectDid: migrated.sourceCardDid,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
235
259
|
function requireCanonicalSubjectDid(subjectDid, context) {
|
|
236
260
|
if (isAnimalSector(context.sector) && !parseAnimalCardDidWeb(subjectDid)) {
|
|
237
261
|
throw new Error('animal_card_did_invalid');
|
|
@@ -15,10 +15,14 @@ The client submits and polls these SOS GW routes:
|
|
|
15
15
|
confidential resource. SOS GW indexes every normalized non-empty alias as its
|
|
16
16
|
own protected indexed attribute. Exact resolution therefore searches one
|
|
17
17
|
alias; a returned CSV is accepted only when it contains that exact alias.
|
|
18
|
-
Animal entries
|
|
19
|
-
seven-digit taxonomy context precedes the complete
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
Animal entries returned to consumers use only the current species-bound card
|
|
19
|
+
profile, in which a seven-digit taxonomy context precedes the complete
|
|
20
|
+
21-digit card number. A retired split `jurisdiction5:animalId16` alias can be
|
|
21
|
+
used only to find an existing provider record: its historical checksum and the
|
|
22
|
+
record's explicit animal taxonomy claim must validate before the client derives
|
|
23
|
+
the current identifier. The client retains the old alias separately for exact
|
|
24
|
+
provider lookup and never accepts it for a new write. The exact profile literal
|
|
25
|
+
remains owned and documented by the gateway that issues the card.
|
|
22
26
|
|
|
23
27
|
Discovery is not authorization. A signed OpenID token is verified by the BFF;
|
|
24
28
|
the SDK receives only the verified contact and uses the token as server-side
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sos-sdk-node-ts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Product-neutral Node adapters shared by SOSChain, Vet and UHC runtimes",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Connecting Solution & Applications Ltd",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"@simplewebauthn/server": "13.3.2",
|
|
81
81
|
"gdc-common-utils-ts": "2.9.30",
|
|
82
82
|
"gdc-sdk-node-ts": "2.9.32",
|
|
83
|
-
"sos-data-utils-ts": "0.6.
|
|
83
|
+
"sos-data-utils-ts": "0.6.16",
|
|
84
84
|
"sos-sdk-core-ts": "0.5.2"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|