gdc-common-utils-ts 2.4.1 → 2.5.0
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 +12 -5
- package/dist/constants/schemaorg.d.ts +2 -0
- package/dist/constants/schemaorg.js +2 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/models/subject-identifier-ledger.d.ts +13 -2
- package/dist/models/subject-identity.d.ts +36 -0
- package/dist/models/subject-identity.js +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/subject-identity.d.ts +30 -0
- package/dist/utils/subject-identity.js +127 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -243,11 +243,18 @@ const lookupMultihash = encodeMultibaseSha3('DL|US-CA|D1234567');
|
|
|
243
243
|
const blockchainAssetId = buildRawCidV1FromUtf8String('DL|US-CA|D1234567');
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
`org.hl7.terminology.CodeSystem.v2-0203.NN
|
|
248
|
-
`
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
Canonical Subject-collection lookup tokens use `codingSystem|codeValue`, for
|
|
247
|
+
example `org.hl7.terminology.CodeSystem.v2-0203.NN.ES|12345678Z` or
|
|
248
|
+
`urn:iso:std:iso:11784-11785|981020000123456`. Import
|
|
249
|
+
`buildSubjectIdentifierAssetId(...)` when a GW must derive the opaque SHA3-384
|
|
250
|
+
`urn:multibase` key used by the distributed subject index. The semantic
|
|
251
|
+
`Person`/`Animal` entry keeps the private claims and its `sameAs` points to the
|
|
252
|
+
stable public unified card; the raw identifier must never be written to the
|
|
253
|
+
ledger.
|
|
254
|
+
|
|
255
|
+
`buildIndividualIdentifierLedgerAssetId(...)` remains the older
|
|
256
|
+
type/jurisdiction/value compatibility helper. Do not use its former
|
|
257
|
+
`Organization.sameAs` convention for new Subject collection writes.
|
|
251
258
|
|
|
252
259
|
Both helpers hash the exact UTF-8 bytes supplied by the caller. Identifier,
|
|
253
260
|
FHIR-token or JSON canonicalization belongs to the contract that owns the
|
|
@@ -175,6 +175,8 @@ export declare enum ClaimsPersonSchemaorg {
|
|
|
175
175
|
identifier = "org.schema.Person.identifier",// the URN (composed by the provider)
|
|
176
176
|
identifierType = "org.schema.Person.identifier.additionalType",// retrieved from a form
|
|
177
177
|
identifierValue = "org.schema.Person.identifier.value",// retrieved from a form
|
|
178
|
+
/** Stable public unified-card URI shared by private identity representations. */
|
|
179
|
+
sameAs = "org.schema.Person.sameAs",
|
|
178
180
|
/** ICAO transliteration of official given name (including middlenames), family name and addtional surname */
|
|
179
181
|
name = "org.schema.Person.name",
|
|
180
182
|
memberOf = "org.schema.Person.memberOf",// for employees
|
|
@@ -183,6 +183,8 @@ export var ClaimsPersonSchemaorg;
|
|
|
183
183
|
ClaimsPersonSchemaorg["identifier"] = "org.schema.Person.identifier";
|
|
184
184
|
ClaimsPersonSchemaorg["identifierType"] = "org.schema.Person.identifier.additionalType";
|
|
185
185
|
ClaimsPersonSchemaorg["identifierValue"] = "org.schema.Person.identifier.value";
|
|
186
|
+
/** Stable public unified-card URI shared by private identity representations. */
|
|
187
|
+
ClaimsPersonSchemaorg["sameAs"] = "org.schema.Person.sameAs";
|
|
186
188
|
/** ICAO transliteration of official given name (including middlenames), family name and addtional surname */
|
|
187
189
|
ClaimsPersonSchemaorg["name"] = "org.schema.Person.name";
|
|
188
190
|
ClaimsPersonSchemaorg["memberOf"] = "org.schema.Person.memberOf";
|
package/dist/models/index.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export * from './resource-document';
|
|
|
51
51
|
export * from './relationship-access';
|
|
52
52
|
export * from './response';
|
|
53
53
|
export * from './subject-identifier-ledger';
|
|
54
|
+
export * from './subject-identity';
|
|
54
55
|
export * from './subject-identity-binding';
|
|
55
56
|
export * from './terminology';
|
|
56
57
|
export * from './urlPath';
|
package/dist/models/index.js
CHANGED
|
@@ -51,6 +51,7 @@ export * from './resource-document.js';
|
|
|
51
51
|
export * from './relationship-access.js';
|
|
52
52
|
export * from './response.js';
|
|
53
53
|
export * from './subject-identifier-ledger.js';
|
|
54
|
+
export * from './subject-identity.js';
|
|
54
55
|
export * from './subject-identity-binding.js';
|
|
55
56
|
export * from './terminology.js';
|
|
56
57
|
export * from './urlPath.js';
|
|
@@ -6,11 +6,22 @@ export type SubjectIdentifierProviderPointer = Readonly<{
|
|
|
6
6
|
/** Bare DNS domain: no URL scheme, path, DID or tenant identifier. */
|
|
7
7
|
url: string;
|
|
8
8
|
}>;
|
|
9
|
-
/**
|
|
9
|
+
/** Public unified-card pointer resolved from an exact known identifier. */
|
|
10
|
+
export type SubjectIdentifierCardPointer = Readonly<{
|
|
11
|
+
identifier: Readonly<{
|
|
12
|
+
value: string;
|
|
13
|
+
}>;
|
|
14
|
+
subjectKind: 'person' | 'animal' | 'property';
|
|
15
|
+
}>;
|
|
16
|
+
/** Ledger payload. It deliberately contains no raw private identifier. */
|
|
10
17
|
export type SubjectIdentifierLedgerPayload = Readonly<{
|
|
18
|
+
card: SubjectIdentifierCardPointer;
|
|
11
19
|
provider: SubjectIdentifierProviderPointer;
|
|
12
20
|
}>;
|
|
13
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated Parse canonical Person/Animal/Place Bundle entries with
|
|
23
|
+
* `readSubjectIdentityBundleEntry` instead. Kept for 2.x compatibility only.
|
|
24
|
+
*/
|
|
14
25
|
export type SubjectIdentifierLedgerBundleEntry = Readonly<{
|
|
15
26
|
sameAs: string | readonly string[];
|
|
16
27
|
}>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { BundleEntry } from './bundle';
|
|
2
|
+
/** Subject categories supported by the neutral identity collection. */
|
|
3
|
+
export type SubjectKind = 'person' | 'animal' | 'property';
|
|
4
|
+
/** Semantic resource types stored inside the neutral Subject collection. */
|
|
5
|
+
export type SubjectIdentityResourceType = 'Person' | 'Animal' | 'Place';
|
|
6
|
+
/** Exact private identifier input linked to one public unified card. */
|
|
7
|
+
export type SubjectIdentityInput = Readonly<{
|
|
8
|
+
subjectKind: SubjectKind;
|
|
9
|
+
cardId: string;
|
|
10
|
+
/** Canonical identifier type/coding system. */
|
|
11
|
+
codingSystem: string;
|
|
12
|
+
/** ISO jurisdiction for scoped identifiers; empty for a global identifier. */
|
|
13
|
+
jurisdiction: string;
|
|
14
|
+
codeValue: string;
|
|
15
|
+
/** Additional encrypted claims; canonical identifier and sameAs keys cannot be overridden. */
|
|
16
|
+
additionalClaims?: Readonly<Record<string, unknown>>;
|
|
17
|
+
}>;
|
|
18
|
+
/** Normalized association recovered from one Subject collection entry. */
|
|
19
|
+
export type SubjectIdentityAssociation = SubjectIdentityInput & Readonly<{
|
|
20
|
+
resourceType: SubjectIdentityResourceType;
|
|
21
|
+
assetId: string;
|
|
22
|
+
}>;
|
|
23
|
+
/** Bundle entry used to write one semantic identity into the Subject collection. */
|
|
24
|
+
export type SubjectIdentityBundleEntry = BundleEntry & Readonly<{
|
|
25
|
+
type: 'Subject-identity-link-v1.0';
|
|
26
|
+
resource: NonNullable<BundleEntry['resource']> & Readonly<{
|
|
27
|
+
resourceType: SubjectIdentityResourceType;
|
|
28
|
+
meta: {
|
|
29
|
+
claims: Record<string, unknown>;
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
request: {
|
|
33
|
+
method: 'POST';
|
|
34
|
+
url: 'Subject';
|
|
35
|
+
};
|
|
36
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -102,6 +102,7 @@ export * from './permission-templates';
|
|
|
102
102
|
export * from './smart-scope';
|
|
103
103
|
export * from './service-act-reasons';
|
|
104
104
|
export * from './same-as';
|
|
105
|
+
export * from './subject-identity';
|
|
105
106
|
export * from './subject-identity-binding';
|
|
106
107
|
export * from './activation-request';
|
|
107
108
|
export * from './actor-identifier';
|
package/dist/utils/index.js
CHANGED
|
@@ -102,6 +102,7 @@ export * from './permission-templates.js';
|
|
|
102
102
|
export * from './smart-scope.js';
|
|
103
103
|
export * from './service-act-reasons.js';
|
|
104
104
|
export * from './same-as.js';
|
|
105
|
+
export * from './subject-identity.js';
|
|
105
106
|
export * from './subject-identity-binding.js';
|
|
106
107
|
export * from './activation-request.js';
|
|
107
108
|
export * from './actor-identifier.js';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { BundleEntry } from '../models/bundle';
|
|
2
|
+
import type { SubjectIdentityAssociation, SubjectIdentityBundleEntry, SubjectIdentityInput } from '../models/subject-identity';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the exact UTF-8 token used by the distributed Subject lookup.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - canonical input is `codingSystem|jurisdiction-or-empty|codeValue`;
|
|
8
|
+
* - an empty jurisdiction is explicit, producing `||` for global identifiers;
|
|
9
|
+
* - values use the existing individual-identifier uppercase normalization;
|
|
10
|
+
* - `|` is forbidden in every component to keep the encoding unambiguous.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildSubjectIdentifierToken(input: Pick<SubjectIdentityInput, 'codingSystem' | 'jurisdiction' | 'codeValue'>): string;
|
|
13
|
+
/**
|
|
14
|
+
* Returns `urn:multibase:<base58btc multihash>` using SHA3-384.
|
|
15
|
+
*
|
|
16
|
+
* The value is a deterministic public lookup key, not an authorization proof.
|
|
17
|
+
* The raw identifier remains in the encrypted Subject collection and is never
|
|
18
|
+
* included in the Fabric payload.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildSubjectIdentifierAssetId(input: Pick<SubjectIdentityInput, 'codingSystem' | 'jurisdiction' | 'codeValue'>): string;
|
|
21
|
+
/**
|
|
22
|
+
* Builds one semantic identity resource for the neutral Subject collection.
|
|
23
|
+
*
|
|
24
|
+
* `sameAs` always identifies the stable public unified card. The private
|
|
25
|
+
* coding system and code value remain claims of Person, Animal or Place; the
|
|
26
|
+
* collection name never replaces that semantic resource type.
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildSubjectIdentityBundleEntry(input: SubjectIdentityInput): SubjectIdentityBundleEntry;
|
|
29
|
+
/** Reads and validates one Person/Animal/Place entry from the Subject collection. */
|
|
30
|
+
export declare function readSubjectIdentityBundleEntry(entry: BundleEntry): SubjectIdentityAssociation;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { UrnPrefixes } from '../constants/urn.js';
|
|
2
|
+
import { encodeMultibaseSha3 } from './multibasehash.js';
|
|
3
|
+
import { normalizeIndividualIdentifierType } from './individual-identifier.js';
|
|
4
|
+
const SUBJECT_RESOURCE_BY_KIND = {
|
|
5
|
+
person: 'Person',
|
|
6
|
+
animal: 'Animal',
|
|
7
|
+
property: 'Place',
|
|
8
|
+
};
|
|
9
|
+
const SUBJECT_KIND_BY_RESOURCE = {
|
|
10
|
+
Person: 'person',
|
|
11
|
+
Animal: 'animal',
|
|
12
|
+
Place: 'property',
|
|
13
|
+
};
|
|
14
|
+
const STABLE_CARD_ID_PATTERN = /^(?:did|urn|https):\S+$/i;
|
|
15
|
+
const ISO_3166_JURISDICTION_PATTERN = /^[A-Z]{2}(?:-[A-Z0-9]{1,3})?$/;
|
|
16
|
+
/**
|
|
17
|
+
* Builds the exact UTF-8 token used by the distributed Subject lookup.
|
|
18
|
+
*
|
|
19
|
+
* Contract:
|
|
20
|
+
* - canonical input is `codingSystem|jurisdiction-or-empty|codeValue`;
|
|
21
|
+
* - an empty jurisdiction is explicit, producing `||` for global identifiers;
|
|
22
|
+
* - values use the existing individual-identifier uppercase normalization;
|
|
23
|
+
* - `|` is forbidden in every component to keep the encoding unambiguous.
|
|
24
|
+
*/
|
|
25
|
+
export function buildSubjectIdentifierToken(input) {
|
|
26
|
+
const codingSystem = normalizeTokenPart(input.codingSystem, 'codingSystem');
|
|
27
|
+
const jurisdiction = normalizeJurisdiction(input.jurisdiction);
|
|
28
|
+
const codeValue = normalizeTokenPart(input.codeValue, 'codeValue').toUpperCase();
|
|
29
|
+
return `${codingSystem}|${jurisdiction}|${codeValue}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns `urn:multibase:<base58btc multihash>` using SHA3-384.
|
|
33
|
+
*
|
|
34
|
+
* The value is a deterministic public lookup key, not an authorization proof.
|
|
35
|
+
* The raw identifier remains in the encrypted Subject collection and is never
|
|
36
|
+
* included in the Fabric payload.
|
|
37
|
+
*/
|
|
38
|
+
export function buildSubjectIdentifierAssetId(input) {
|
|
39
|
+
return `${UrnPrefixes.Multibase}${encodeMultibaseSha3(buildSubjectIdentifierToken(input), 384)}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Builds one semantic identity resource for the neutral Subject collection.
|
|
43
|
+
*
|
|
44
|
+
* `sameAs` always identifies the stable public unified card. The private
|
|
45
|
+
* coding system and code value remain claims of Person, Animal or Place; the
|
|
46
|
+
* collection name never replaces that semantic resource type.
|
|
47
|
+
*/
|
|
48
|
+
export function buildSubjectIdentityBundleEntry(input) {
|
|
49
|
+
const resourceType = SUBJECT_RESOURCE_BY_KIND[input.subjectKind];
|
|
50
|
+
if (!resourceType)
|
|
51
|
+
throw new TypeError(`Unsupported subject kind: ${input.subjectKind}`);
|
|
52
|
+
const cardId = input.cardId.trim();
|
|
53
|
+
if (!STABLE_CARD_ID_PATTERN.test(cardId)) {
|
|
54
|
+
throw new TypeError('Subject identity cardId must be a stable URI (did:, urn: or https:).');
|
|
55
|
+
}
|
|
56
|
+
const codingSystem = input.subjectKind === 'person'
|
|
57
|
+
? normalizeIndividualIdentifierType(input.codingSystem)
|
|
58
|
+
: normalizeTokenPart(input.codingSystem, 'codingSystem');
|
|
59
|
+
const jurisdiction = normalizeJurisdiction(input.jurisdiction);
|
|
60
|
+
if (input.subjectKind === 'person' && !jurisdiction) {
|
|
61
|
+
throw new TypeError('Person identity jurisdiction is required.');
|
|
62
|
+
}
|
|
63
|
+
const codeValue = normalizeTokenPart(input.codeValue, 'codeValue').toUpperCase();
|
|
64
|
+
const assetId = buildSubjectIdentifierAssetId({ codingSystem, jurisdiction, codeValue });
|
|
65
|
+
const claimPrefix = `org.schema.${resourceType}`;
|
|
66
|
+
return {
|
|
67
|
+
id: assetId,
|
|
68
|
+
fullUrl: assetId,
|
|
69
|
+
type: 'Subject-identity-link-v1.0',
|
|
70
|
+
resource: {
|
|
71
|
+
resourceType,
|
|
72
|
+
id: assetId,
|
|
73
|
+
meta: {
|
|
74
|
+
claims: {
|
|
75
|
+
...(input.additionalClaims || {}),
|
|
76
|
+
[`${claimPrefix}.identifier`]: assetId,
|
|
77
|
+
[`${claimPrefix}.identifier.additionalType`]: codingSystem,
|
|
78
|
+
[`${claimPrefix}.identifier.jurisdiction`]: jurisdiction,
|
|
79
|
+
[`${claimPrefix}.identifier.value`]: codeValue,
|
|
80
|
+
[`${claimPrefix}.sameAs`]: cardId,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
request: { method: 'POST', url: 'Subject' },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Reads and validates one Person/Animal/Place entry from the Subject collection. */
|
|
88
|
+
export function readSubjectIdentityBundleEntry(entry) {
|
|
89
|
+
const resourceType = String(entry.resource?.resourceType || '');
|
|
90
|
+
const subjectKind = SUBJECT_KIND_BY_RESOURCE[resourceType];
|
|
91
|
+
if (!subjectKind) {
|
|
92
|
+
throw new TypeError(`Unsupported Subject identity resourceType: ${resourceType || '(missing)'}; expected Person, Animal or Place.`);
|
|
93
|
+
}
|
|
94
|
+
const claims = entry.resource?.meta?.claims || {};
|
|
95
|
+
const prefix = `org.schema.${resourceType}`;
|
|
96
|
+
const cardId = String(claims[`${prefix}.sameAs`] || '').trim();
|
|
97
|
+
const codingSystem = String(claims[`${prefix}.identifier.additionalType`] || '').trim();
|
|
98
|
+
const jurisdiction = normalizeJurisdiction(String(claims[`${prefix}.identifier.jurisdiction`] || ''));
|
|
99
|
+
const codeValue = String(claims[`${prefix}.identifier.value`] || '').trim();
|
|
100
|
+
if (!STABLE_CARD_ID_PATTERN.test(cardId))
|
|
101
|
+
throw new TypeError('Subject identity sameAs must contain one stable card URI.');
|
|
102
|
+
if (subjectKind === 'person' && !jurisdiction)
|
|
103
|
+
throw new TypeError('Person identity jurisdiction is required.');
|
|
104
|
+
const assetId = buildSubjectIdentifierAssetId({ codingSystem, jurisdiction, codeValue });
|
|
105
|
+
const claimedAssetId = String(claims[`${prefix}.identifier`] || entry.resource?.id || entry.id || '').trim();
|
|
106
|
+
if (claimedAssetId && claimedAssetId !== assetId) {
|
|
107
|
+
throw new TypeError('Subject identity identifier does not match codingSystem|jurisdiction|codeValue.');
|
|
108
|
+
}
|
|
109
|
+
return { subjectKind, resourceType, cardId, codingSystem, jurisdiction, codeValue, assetId };
|
|
110
|
+
}
|
|
111
|
+
function normalizeTokenPart(value, field) {
|
|
112
|
+
const normalized = String(value || '').trim().normalize('NFKC');
|
|
113
|
+
if (!normalized)
|
|
114
|
+
throw new TypeError(`${field} is required`);
|
|
115
|
+
if (normalized.includes('|'))
|
|
116
|
+
throw new TypeError(`${field} must not contain the '|' delimiter`);
|
|
117
|
+
return normalized;
|
|
118
|
+
}
|
|
119
|
+
function normalizeJurisdiction(value) {
|
|
120
|
+
const normalized = String(value || '').trim().normalize('NFKC').toUpperCase();
|
|
121
|
+
if (normalized.includes('|'))
|
|
122
|
+
throw new TypeError("jurisdiction must not contain the '|' delimiter");
|
|
123
|
+
if (normalized && !ISO_3166_JURISDICTION_PATTERN.test(normalized)) {
|
|
124
|
+
throw new TypeError(`Invalid ISO 3166 jurisdiction: ${value}`);
|
|
125
|
+
}
|
|
126
|
+
return normalized;
|
|
127
|
+
}
|