sos-sdk-core-ts 0.1.5
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 +37 -0
- package/README.md +31 -0
- package/dist/emergency-authorization.d.ts +31 -0
- package/dist/emergency-authorization.js +19 -0
- package/dist/emergency-group.d.ts +46 -0
- package/dist/emergency-group.js +48 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/place-card-association.d.ts +35 -0
- package/dist/place-card-association.js +30 -0
- package/package.json +49 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- Pin the first registry release to `uhc-sdk-core-ts@0.6.0` so SOSChain
|
|
6
|
+
consumers can remove the historical vendored package from staging builds.
|
|
7
|
+
|
|
8
|
+
## 0.1.5
|
|
9
|
+
|
|
10
|
+
- Add the short-lived `incident-reporter` evidence relationship used when a
|
|
11
|
+
verified person scans an SOSChain/UHC/VetChain card at an incident without
|
|
12
|
+
claiming to be the subject's guardian, controller or professional.
|
|
13
|
+
|
|
14
|
+
## 0.1.4
|
|
15
|
+
|
|
16
|
+
- Add the deliberate public deterministic SHA3-384/CID target hash used by
|
|
17
|
+
smart contracts to return an existing Place or vehicle instead of duplicating it.
|
|
18
|
+
|
|
19
|
+
## 0.1.3
|
|
20
|
+
|
|
21
|
+
- Add flat claims for UHC/VetChain card associations to canonical SOSChain
|
|
22
|
+
Place or vehicle identities and explicit created/existing match results.
|
|
23
|
+
|
|
24
|
+
## 0.1.2
|
|
25
|
+
|
|
26
|
+
- Replace premature FHIR resource objects with canonical flat
|
|
27
|
+
`resource.meta.claims` contracts using kebab-case FHIR claim names.
|
|
28
|
+
|
|
29
|
+
## 0.1.1
|
|
30
|
+
|
|
31
|
+
- Add fail-closed emergency authorization evidence and regional/local delivery target contracts.
|
|
32
|
+
- Name emergency groups explicitly.
|
|
33
|
+
|
|
34
|
+
## 0.1.0
|
|
35
|
+
|
|
36
|
+
- Add opaque SOSChain emergency-group claims.
|
|
37
|
+
- Add the FHIR R5 `ServiceRequest` projection for group assistance.
|
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# SOSChain SDK Core
|
|
2
|
+
|
|
3
|
+
Runtime-neutral SOSChain extension of `uhc-sdk-core-ts`.
|
|
4
|
+
|
|
5
|
+
It consumes the exact published `uhc-sdk-core-ts` contract. Git, workspace,
|
|
6
|
+
vendored and `file:` dependencies are forbidden in committed or deployed
|
|
7
|
+
dependency state.
|
|
8
|
+
|
|
9
|
+
The emergency-group contract treats `emergencyGroupId`, `subjectId` and
|
|
10
|
+
`callerId` as opaque business identifiers. A group assistance request is one
|
|
11
|
+
flat `ServiceRequest.*` claims under `resource.meta.claims`. A FHIR R5, R6 or
|
|
12
|
+
other resource is projected only at an integration boundary.
|
|
13
|
+
|
|
14
|
+
Groups are named collections. A scanned card is attached to one selected group
|
|
15
|
+
before that group's identifiers are projected into a single service request.
|
|
16
|
+
|
|
17
|
+
`authorizeSoschainEmergencyAction` separately checks trusted evidence for every
|
|
18
|
+
member and for `sos:call` or `sos:request`. Identity verification alone never
|
|
19
|
+
grants an emergency capability. Regional/local delivery is represented by
|
|
20
|
+
`SoschainEmergencyDeliveryTarget`; transport and tenant routing stay outside FHIR.
|
|
21
|
+
The `incident-reporter` relationship represents a short-lived grant issued by
|
|
22
|
+
a trusted scan resolver to an already verified person at the scene; it does
|
|
23
|
+
not imply guardianship, control or professional status.
|
|
24
|
+
|
|
25
|
+
`buildSoschainPlaceCardAssociationClaims` records a UHC/VetChain card link to
|
|
26
|
+
a canonical SOSChain Place or vehicle without merging either identity.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install
|
|
30
|
+
npm run check
|
|
31
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type SoschainEmergencyCapability = 'sos:call' | 'sos:request';
|
|
2
|
+
export type SoschainEvidenceDomain = 'SOS' | 'Health' | 'TrainingAndEducation';
|
|
3
|
+
/** A verified evidence reference; the source VC/PDF stays outside call and audit logs. */
|
|
4
|
+
export type SoschainEmergencyAuthorizationEvidence = Readonly<{
|
|
5
|
+
evidenceId: string;
|
|
6
|
+
issuerId: string;
|
|
7
|
+
holderId: string;
|
|
8
|
+
subjectIds: readonly string[];
|
|
9
|
+
capabilities: readonly SoschainEmergencyCapability[];
|
|
10
|
+
domains: readonly SoschainEvidenceDomain[];
|
|
11
|
+
relationship: 'self' | 'guardian' | 'professional' | 'institution' | 'incident-reporter';
|
|
12
|
+
verifiedAt: string;
|
|
13
|
+
validUntil?: string;
|
|
14
|
+
}>;
|
|
15
|
+
export type SoschainEmergencyDeliveryTarget = Readonly<{
|
|
16
|
+
tenantId: string;
|
|
17
|
+
jurisdiction: string;
|
|
18
|
+
locality?: string;
|
|
19
|
+
endpointId: string;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Requires current trusted evidence for every affected subject and capability.
|
|
23
|
+
* Identifier syntax and the legal meaning of each issuer remain policy concerns.
|
|
24
|
+
*/
|
|
25
|
+
export declare function authorizeSoschainEmergencyAction(input: Readonly<{
|
|
26
|
+
callerId: string;
|
|
27
|
+
memberSubjectIds: readonly string[];
|
|
28
|
+
capability: SoschainEmergencyCapability;
|
|
29
|
+
evidence: readonly SoschainEmergencyAuthorizationEvidence[];
|
|
30
|
+
now?: string;
|
|
31
|
+
}>): readonly string[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requires current trusted evidence for every affected subject and capability.
|
|
3
|
+
* Identifier syntax and the legal meaning of each issuer remain policy concerns.
|
|
4
|
+
*/
|
|
5
|
+
export function authorizeSoschainEmergencyAction(input) {
|
|
6
|
+
const at = Date.parse(input.now ?? new Date().toISOString());
|
|
7
|
+
const evidenceIds = new Set();
|
|
8
|
+
for (const subjectId of input.memberSubjectIds) {
|
|
9
|
+
const accepted = input.evidence.find((item) => item.holderId === input.callerId &&
|
|
10
|
+
item.subjectIds.includes(subjectId) &&
|
|
11
|
+
item.capabilities.includes(input.capability) &&
|
|
12
|
+
Number.isFinite(Date.parse(item.verifiedAt)) && Date.parse(item.verifiedAt) <= at &&
|
|
13
|
+
(!item.validUntil || Date.parse(item.validUntil) >= at));
|
|
14
|
+
if (!accepted)
|
|
15
|
+
throw new Error(`emergency_authorization_evidence_required:${subjectId}:${input.capability}`);
|
|
16
|
+
evidenceIds.add(accepted.evidenceId);
|
|
17
|
+
}
|
|
18
|
+
return [...evidenceIds];
|
|
19
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const SOSCHAIN_FHIR_CLAIMS_CONTEXT: "org.hl7.fhir.api";
|
|
2
|
+
export declare const SOSCHAIN_EMERGENCY_ASSISTANCE_CODE_SYSTEM = "https://soschain.org/fhir/CodeSystem/emergency-assistance";
|
|
3
|
+
/** Canonical claims-first group record stored under resource.meta.claims. */
|
|
4
|
+
export type SoschainEmergencyGroupClaims = Readonly<{
|
|
5
|
+
'@context': typeof SOSCHAIN_FHIR_CLAIMS_CONTEXT;
|
|
6
|
+
'Group.identifier': string;
|
|
7
|
+
'Group.name': string;
|
|
8
|
+
'Group.member-subject': readonly string[];
|
|
9
|
+
'Group.soschain-caller': string;
|
|
10
|
+
'Group.period-start': string;
|
|
11
|
+
}>;
|
|
12
|
+
/** Canonical claims-first request; FHIR version projection happens later. */
|
|
13
|
+
export type SoschainEmergencyServiceRequestClaims = Readonly<{
|
|
14
|
+
'@context': typeof SOSCHAIN_FHIR_CLAIMS_CONTEXT;
|
|
15
|
+
'ServiceRequest.identifier': string;
|
|
16
|
+
'ServiceRequest.status': 'active';
|
|
17
|
+
'ServiceRequest.intent': 'order';
|
|
18
|
+
'ServiceRequest.code': string;
|
|
19
|
+
'ServiceRequest.code-system': typeof SOSCHAIN_EMERGENCY_ASSISTANCE_CODE_SYSTEM;
|
|
20
|
+
'ServiceRequest.code-display'?: string;
|
|
21
|
+
'ServiceRequest.subject': string;
|
|
22
|
+
'ServiceRequest.subject-type': 'Group';
|
|
23
|
+
'ServiceRequest.requester': string;
|
|
24
|
+
'ServiceRequest.authored-on': string;
|
|
25
|
+
'ServiceRequest.soschain-member-subject': readonly string[];
|
|
26
|
+
}>;
|
|
27
|
+
/** Builds flat, version-neutral FHIR claims without interpreting identifier syntax. */
|
|
28
|
+
export declare function buildSoschainEmergencyGroupClaims(input: Readonly<{
|
|
29
|
+
emergencyGroupId: string;
|
|
30
|
+
displayName: string;
|
|
31
|
+
memberSubjectIds: readonly string[];
|
|
32
|
+
callerId: string;
|
|
33
|
+
createdAt?: string;
|
|
34
|
+
}>): SoschainEmergencyGroupClaims;
|
|
35
|
+
/**
|
|
36
|
+
* Builds one flat ServiceRequest claim set for all members of one emergency
|
|
37
|
+
* group. It deliberately does not construct a FHIR R5/R6 resource.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildSoschainEmergencyServiceRequestClaims(input: Readonly<{
|
|
40
|
+
requestId: string;
|
|
41
|
+
groupClaims: SoschainEmergencyGroupClaims;
|
|
42
|
+
assistanceCode?: string;
|
|
43
|
+
assistanceDisplay?: string;
|
|
44
|
+
authoredOn?: string;
|
|
45
|
+
}>): SoschainEmergencyServiceRequestClaims;
|
|
46
|
+
export declare function readSoschainEmergencySubjectIds(claims: SoschainEmergencyServiceRequestClaims): readonly string[];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const SOSCHAIN_FHIR_CLAIMS_CONTEXT = 'org.hl7.fhir.api';
|
|
2
|
+
export const SOSCHAIN_EMERGENCY_ASSISTANCE_CODE_SYSTEM = 'https://soschain.org/fhir/CodeSystem/emergency-assistance';
|
|
3
|
+
function requiredIdentifier(value, name) {
|
|
4
|
+
const normalized = String(value).trim();
|
|
5
|
+
if (!normalized)
|
|
6
|
+
throw new TypeError(`${name} is required.`);
|
|
7
|
+
return normalized;
|
|
8
|
+
}
|
|
9
|
+
function uniqueIdentifiers(values) {
|
|
10
|
+
const identifiers = [...new Set(values.map((value) => requiredIdentifier(value, 'memberSubjectId')))];
|
|
11
|
+
if (identifiers.length === 0)
|
|
12
|
+
throw new TypeError('At least one memberSubjectId is required.');
|
|
13
|
+
return identifiers;
|
|
14
|
+
}
|
|
15
|
+
/** Builds flat, version-neutral FHIR claims without interpreting identifier syntax. */
|
|
16
|
+
export function buildSoschainEmergencyGroupClaims(input) {
|
|
17
|
+
return {
|
|
18
|
+
'@context': SOSCHAIN_FHIR_CLAIMS_CONTEXT,
|
|
19
|
+
'Group.identifier': requiredIdentifier(input.emergencyGroupId, 'emergencyGroupId'),
|
|
20
|
+
'Group.name': requiredIdentifier(input.displayName, 'displayName'),
|
|
21
|
+
'Group.member-subject': uniqueIdentifiers(input.memberSubjectIds),
|
|
22
|
+
'Group.soschain-caller': requiredIdentifier(input.callerId, 'callerId'),
|
|
23
|
+
'Group.period-start': input.createdAt ?? new Date().toISOString(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Builds one flat ServiceRequest claim set for all members of one emergency
|
|
28
|
+
* group. It deliberately does not construct a FHIR R5/R6 resource.
|
|
29
|
+
*/
|
|
30
|
+
export function buildSoschainEmergencyServiceRequestClaims(input) {
|
|
31
|
+
return {
|
|
32
|
+
'@context': SOSCHAIN_FHIR_CLAIMS_CONTEXT,
|
|
33
|
+
'ServiceRequest.identifier': requiredIdentifier(input.requestId, 'requestId'),
|
|
34
|
+
'ServiceRequest.status': 'active',
|
|
35
|
+
'ServiceRequest.intent': 'order',
|
|
36
|
+
'ServiceRequest.code': input.assistanceCode?.trim() || 'emergency-assistance',
|
|
37
|
+
'ServiceRequest.code-system': SOSCHAIN_EMERGENCY_ASSISTANCE_CODE_SYSTEM,
|
|
38
|
+
...(input.assistanceDisplay ? { 'ServiceRequest.code-display': input.assistanceDisplay } : {}),
|
|
39
|
+
'ServiceRequest.subject': input.groupClaims['Group.identifier'],
|
|
40
|
+
'ServiceRequest.subject-type': 'Group',
|
|
41
|
+
'ServiceRequest.requester': input.groupClaims['Group.soschain-caller'],
|
|
42
|
+
'ServiceRequest.authored-on': input.authoredOn ?? new Date().toISOString(),
|
|
43
|
+
'ServiceRequest.soschain-member-subject': uniqueIdentifiers(input.groupClaims['Group.member-subject']),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function readSoschainEmergencySubjectIds(claims) {
|
|
47
|
+
return claims['ServiceRequest.soschain-member-subject'];
|
|
48
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SOSCHAIN_FHIR_CLAIMS_CONTEXT } from './emergency-group.js';
|
|
2
|
+
export type SoschainAssociationTargetType = 'Place' | 'Vehicle';
|
|
3
|
+
export type SoschainAssociationRole = 'controller' | 'occupant' | 'employee' | 'guardian';
|
|
4
|
+
/** Flat association record stored under resource.meta.claims. */
|
|
5
|
+
export type SoschainPlaceCardAssociationClaims = Readonly<{
|
|
6
|
+
'@context': typeof SOSCHAIN_FHIR_CLAIMS_CONTEXT;
|
|
7
|
+
'Basic.identifier': string;
|
|
8
|
+
'Basic.code': 'soschain-card-target-association';
|
|
9
|
+
'Basic.subject': string;
|
|
10
|
+
'Basic.soschain-target': string;
|
|
11
|
+
'Basic.soschain-target-hash': string;
|
|
12
|
+
'Basic.soschain-target-type': SoschainAssociationTargetType;
|
|
13
|
+
'Basic.soschain-role': SoschainAssociationRole;
|
|
14
|
+
'Basic.period-start': string;
|
|
15
|
+
}>;
|
|
16
|
+
export type SoschainCanonicalTargetMatch = Readonly<{
|
|
17
|
+
status: 'created' | 'existing';
|
|
18
|
+
canonicalTargetId: string;
|
|
19
|
+
targetType: SoschainAssociationTargetType;
|
|
20
|
+
}>;
|
|
21
|
+
export declare function buildSoschainPlaceCardAssociationClaims(input: Readonly<{
|
|
22
|
+
associationId: string;
|
|
23
|
+
cardId: string;
|
|
24
|
+
canonicalTargetId: string;
|
|
25
|
+
canonicalTargetHash: string;
|
|
26
|
+
targetType: SoschainAssociationTargetType;
|
|
27
|
+
role: SoschainAssociationRole;
|
|
28
|
+
createdAt?: string;
|
|
29
|
+
}>): SoschainPlaceCardAssociationClaims;
|
|
30
|
+
/** Public deterministic SHA3-384/CID lookup key for smart-contract deduplication. */
|
|
31
|
+
export declare function buildSoschainCanonicalTargetHash(input: Readonly<{
|
|
32
|
+
targetType: SoschainAssociationTargetType;
|
|
33
|
+
countryCode: string;
|
|
34
|
+
canonicalValue: string;
|
|
35
|
+
}>): string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { SOSCHAIN_FHIR_CLAIMS_CONTEXT } from './emergency-group.js';
|
|
2
|
+
import { buildRawCidV1FromUtf8String } from 'gdc-common-utils-ts/utils/multiformat-profile';
|
|
3
|
+
function required(value, name) {
|
|
4
|
+
const normalized = String(value).trim();
|
|
5
|
+
if (!normalized)
|
|
6
|
+
throw new TypeError(`${name} is required.`);
|
|
7
|
+
return normalized;
|
|
8
|
+
}
|
|
9
|
+
export function buildSoschainPlaceCardAssociationClaims(input) {
|
|
10
|
+
return {
|
|
11
|
+
'@context': SOSCHAIN_FHIR_CLAIMS_CONTEXT,
|
|
12
|
+
'Basic.identifier': required(input.associationId, 'associationId'),
|
|
13
|
+
'Basic.code': 'soschain-card-target-association',
|
|
14
|
+
'Basic.subject': required(input.cardId, 'cardId'),
|
|
15
|
+
'Basic.soschain-target': required(input.canonicalTargetId, 'canonicalTargetId'),
|
|
16
|
+
'Basic.soschain-target-hash': required(input.canonicalTargetHash, 'canonicalTargetHash'),
|
|
17
|
+
'Basic.soschain-target-type': input.targetType,
|
|
18
|
+
'Basic.soschain-role': input.role,
|
|
19
|
+
'Basic.period-start': input.createdAt ?? new Date().toISOString(),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Public deterministic SHA3-384/CID lookup key for smart-contract deduplication. */
|
|
23
|
+
export function buildSoschainCanonicalTargetHash(input) {
|
|
24
|
+
const canonical = [
|
|
25
|
+
input.targetType,
|
|
26
|
+
required(input.countryCode, 'countryCode').toUpperCase(),
|
|
27
|
+
required(input.canonicalValue, 'canonicalValue').normalize('NFC').trim().replace(/\s+/g, ' ').toLowerCase(),
|
|
28
|
+
].join('|');
|
|
29
|
+
return buildRawCidV1FromUtf8String(canonical);
|
|
30
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sos-sdk-core-ts",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Runtime-neutral SOSChain contracts extending uhc-sdk-core-ts",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Connecting Solution & Applications Ltd",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Fundacion-UNID/sos-sdk-core-ts.git"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./emergency-group": {
|
|
20
|
+
"types": "./dist/emergency-group.d.ts",
|
|
21
|
+
"default": "./dist/emergency-group.js"
|
|
22
|
+
},
|
|
23
|
+
"./emergency-authorization": {
|
|
24
|
+
"types": "./dist/emergency-authorization.d.ts",
|
|
25
|
+
"default": "./dist/emergency-authorization.js"
|
|
26
|
+
},
|
|
27
|
+
"./place-card-association": {
|
|
28
|
+
"types": "./dist/place-card-association.d.ts",
|
|
29
|
+
"default": "./dist/place-card-association.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": ["dist", "README.md", "CHANGELOG.md"],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
35
|
+
"build": "npm run clean && tsc -p tsconfig.json",
|
|
36
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
37
|
+
"test": "npm run build && node --test tests/*.test.mjs",
|
|
38
|
+
"check": "npm run typecheck && npm test",
|
|
39
|
+
"prepack": "npm run check"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"gdc-common-utils-ts": "^2.3.28",
|
|
43
|
+
"uhc-sdk-core-ts": "0.6.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^22.5.0",
|
|
47
|
+
"typescript": "^5.5.4"
|
|
48
|
+
}
|
|
49
|
+
}
|