gdc-common-utils-ts 2.3.1 → 2.3.2

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.
Files changed (45) hide show
  1. package/README.md +22 -0
  2. package/dist/CryptographyService.d.ts +3 -0
  3. package/dist/CryptographyService.js +64 -35
  4. package/dist/constants/identity-identifiers.d.ts +60 -7
  5. package/dist/constants/identity-identifiers.js +58 -7
  6. package/dist/constants/schemaorg.d.ts +4 -0
  7. package/dist/constants/schemaorg.js +4 -0
  8. package/dist/constants/urn.d.ts +1 -0
  9. package/dist/constants/urn.js +1 -0
  10. package/dist/convert/index.d.ts +1 -0
  11. package/dist/convert/index.js +1 -0
  12. package/dist/convert/schemaorg-to-gaia-x.d.ts +116 -0
  13. package/dist/convert/schemaorg-to-gaia-x.js +211 -0
  14. package/dist/models/bundle-editor-types.d.ts +13 -3
  15. package/dist/models/bundle-editor-types.js +9 -1
  16. package/dist/models/gaia-x.d.ts +132 -0
  17. package/dist/models/gaia-x.js +26 -0
  18. package/dist/models/index.d.ts +2 -0
  19. package/dist/models/index.js +2 -0
  20. package/dist/models/jwe.d.ts +25 -0
  21. package/dist/models/subject-identifier-ledger.d.ts +16 -0
  22. package/dist/models/subject-identifier-ledger.js +1 -0
  23. package/dist/utils/bundle-editor-core.d.ts +9 -0
  24. package/dist/utils/bundle-editor-core.js +46 -0
  25. package/dist/utils/bundle-editor.d.ts +2 -0
  26. package/dist/utils/bundle-editor.js +2 -0
  27. package/dist/utils/bundle-entry-editor.d.ts +6 -0
  28. package/dist/utils/bundle-entry-editor.js +20 -0
  29. package/dist/utils/communication-consent-access-editor.d.ts +14 -1
  30. package/dist/utils/communication-consent-access-editor.js +27 -0
  31. package/dist/utils/consent-entry-editor.d.ts +42 -0
  32. package/dist/utils/consent-entry-editor.js +80 -0
  33. package/dist/utils/index.d.ts +1 -0
  34. package/dist/utils/index.js +1 -0
  35. package/dist/utils/individual-identifier.d.ts +14 -0
  36. package/dist/utils/individual-identifier.js +39 -0
  37. package/dist/utils/multibasehash.d.ts +19 -5
  38. package/dist/utils/multibasehash.js +35 -10
  39. package/dist/utils/multiformat-profile.d.ts +4 -2
  40. package/dist/utils/multiformat-profile.js +5 -3
  41. package/dist/utils/related-person-entry-editor.d.ts +32 -0
  42. package/dist/utils/related-person-entry-editor.js +97 -0
  43. package/dist/utils/same-as.d.ts +10 -0
  44. package/dist/utils/same-as.js +28 -3
  45. package/package.json +2 -1
package/README.md CHANGED
@@ -209,6 +209,11 @@ The `utils` export exposes reusable helpers for DID and message handling, such a
209
209
  - `utils/content`
210
210
  - `utils/normalize`
211
211
  - `utils/unified-health-id` for provider-scoped Damm control digits over normalized provider + personal numeric sequences
212
+ - `utils/multibasehash` for `encodeMultibaseSha3(input, digestBits = 384)`,
213
+ returning a base58btc multibase-encoded SHA3 multihash
214
+ - `utils/multiformat-profile` for
215
+ `buildRawCidV1FromUtf8String(canonicalValue)`, returning
216
+ `CIDv1(raw, SHA3-384)` by default
212
217
  - `utils/fhir-cid` for recursive FHIR canonicalization + CID generation + `meta.versionId` assignment
213
218
  - `utils/fhir-validator` for adapter-based FHIR validation (`validateFhirResource`, pluggable formal validator)
214
219
  - conversion, formatting, and multibase helpers
@@ -220,8 +225,25 @@ Example:
220
225
  ```ts
221
226
  import { normalizeDidWeb, generateServiceId } from 'gdc-common-utils-ts/utils/did';
222
227
  import { fhirResourceToCid, assignCidToFhirResourceVersionId } from 'gdc-common-utils-ts/utils/fhir-cid';
228
+ import { encodeMultibaseSha3 } from 'gdc-common-utils-ts/utils/multibasehash';
229
+ import { buildRawCidV1FromUtf8String } from 'gdc-common-utils-ts/utils/multiformat-profile';
230
+
231
+ const lookupMultihash = encodeMultibaseSha3('DL|US-CA|D1234567');
232
+ const blockchainAssetId = buildRawCidV1FromUtf8String('DL|US-CA|D1234567');
223
233
  ```
224
234
 
235
+ Individual lookup tokens keep the identifier type and jurisdiction separate:
236
+ `org.hl7.terminology.CodeSystem.v2-0203.NN|ES|12345678Z`. Import
237
+ `buildIndividualIdentifierLedgerAssetId(...)` when a GW must derive the opaque
238
+ SHA3-384 `urn:multibase` key used by the subject-identifier ledger. The raw
239
+ identifier must never be written to that ledger.
240
+
241
+ Both helpers hash the exact UTF-8 bytes supplied by the caller. Identifier,
242
+ FHIR-token or JSON canonicalization belongs to the contract that owns the
243
+ input. A multibase multihash is suitable as a deterministic lookup key; a CID
244
+ is suitable when the key must identify a content-addressed record or
245
+ blockchain asset.
246
+
225
247
  ### Convert
226
248
 
227
249
  The `convert` export exposes resource-specific FHIR R4 conversion helpers such as:
@@ -49,6 +49,9 @@ export declare class CryptographyService implements ICryptography {
49
49
  derivedCekBytes: Uint8Array;
50
50
  }>;
51
51
  decapsulate(encapsulatedBytes: Uint8Array, secretKeyBytes: Uint8Array): Promise<Uint8Array>;
52
+ private wrapCekForRecipient;
53
+ private unwrapRecipientCek;
54
+ private deriveRecipientKek;
52
55
  signBytes(payloadBytes: Uint8Array, secretKeyBytes: Uint8Array, alg: MldsaAlg): Promise<Uint8Array>;
53
56
  verifyBytes(signatureBytes: Uint8Array, dataBytes: Uint8Array, publicKey: PublicJwk): Promise<boolean>;
54
57
  jwsToCompact(jws: DataCompactJWT): string;
@@ -4,6 +4,8 @@ import * as pako from 'pako';
4
4
  import * as jwtUtils from './utils/jwt.js';
5
5
  import { AesManager } from './AesManager.js';
6
6
  import { Content } from './utils/content.js';
7
+ import { hkdf } from '@noble/hashes/hkdf.js';
8
+ import { sha256 } from '@noble/hashes/sha2.js';
7
9
  /**
8
10
  * Implements the ICryptography interface, providing a complete suite of low-level,
9
11
  * stateless cryptographic functions. This service is the "engine" of the security layer,
@@ -113,23 +115,17 @@ export class CryptographyService {
113
115
  }
114
116
  // --- High-Level Workflows ---
115
117
  async encryptJwe(payload, protectedHeader, secretJWKey, recipientsJWKeys) {
116
- // ARCHITECTURAL NOTE: This implementation is currently only suitable for a single recipient.
117
- // A Key Encapsulation Mechanism (KEM) derives a *different* shared secret for each recipient's public key.
118
- // A true multi-recipient JWE requires a single Content Encryption Key (CEK) that is then
119
- // encrypted (wrapped) for each recipient. This code uses the KEM-derived shared secret as the CEK.
120
- // This must be refactored to a key-wrapping approach to support multiple recipients correctly.
118
+ // v1 deliberately exposes one recipient while using a distinct random CEK.
119
+ // The recipient wrap is already compatible with a future General JWE profile.
121
120
  if (recipientsJWKeys.length !== 1) {
122
121
  // Temporarily throw until the architecture is fixed for multi-recipient.
123
122
  throw new Error("CryptographyService.encryptJwe currently only supports a single recipient.");
124
123
  }
125
124
  const recipient = recipientsJWKeys[0];
126
125
  const publicKeyBytes = Content.base64ToBytes(recipient.x);
127
- // Per RFC 9278, we generate a random seed for the KEM. The KEM then derives both the
128
- // final Content Encryption Key (CEK) and the encapsulated key from this seed.
129
- const cekSeedBytes = await this.cryptoHelper.getRandomBytes(32);
130
- const { derivedCekBytes, // This is the actual Content Encryption Key
131
- encapsulatedCekBytes // This is the encrypted key for the recipient
132
- } = await this.encapsulate(cekSeedBytes, secretJWKey.dBytes, publicKeyBytes);
126
+ const cekBytes = await this.cryptoHelper.getRandomBytes(32);
127
+ const kemSeedBytes = await this.cryptoHelper.getRandomBytes(32);
128
+ const { derivedCekBytes: sharedSecret, encapsulatedCekBytes } = await this.encapsulate(kemSeedBytes, secretJWKey.dBytes, publicKeyBytes);
133
129
  // 2. Now, use the *derived* CEK to encrypt the payload with AES.
134
130
  const protectedHeaderB64Url = Content.objectToRawBase64UrlSafe(protectedHeader);
135
131
  let payloadBytes = Content.objectToBytes(payload);
@@ -139,13 +135,13 @@ export class CryptographyService {
139
135
  payloadString = Content.bytesToRawBase64UrlSafe(payloadBytes);
140
136
  }
141
137
  else {
142
- payloadString = Content.bytesToStringASCII(payloadBytes);
138
+ payloadString = Content.bytesToStringUTF8(payloadBytes);
143
139
  }
144
- const encrypted = await this.encrypt(payloadString, derivedCekBytes, protectedHeaderB64Url);
145
- // 3. Assemble the JWE. The `encrypted_key` is the result of the KEM encapsulation.
140
+ const encrypted = await this.encrypt(payloadString, cekBytes, protectedHeaderB64Url);
141
+ const wrappedCek = await this.wrapCekForRecipient(cekBytes, sharedSecret, encapsulatedCekBytes, protectedHeaderB64Url, recipient.kid);
146
142
  const recipientData = [{
147
- header: { alg: recipient.crv, kid: recipient.kid },
148
- encrypted_key: Content.bytesToRawBase64UrlSafe(encapsulatedCekBytes),
143
+ header: { alg: 'ML-KEM-768+HKDF-SHA256+A256GCMKW', kid: recipient.kid },
144
+ encrypted_key: Content.objectToRawBase64UrlSafe(wrappedCek),
149
145
  }];
150
146
  return {
151
147
  protected: protectedHeaderB64Url,
@@ -157,14 +153,16 @@ export class CryptographyService {
157
153
  }
158
154
  async encryptJweToCompact(payload, protectedHeader, secretJWKey, recipientJWKey) {
159
155
  // 1. Construct the complete, final protected header by merging the main and recipient headers.
160
- const recipientHeader = { alg: recipientJWKey.crv, kid: recipientJWKey.kid };
156
+ const recipientHeader = { alg: 'ML-KEM-768+HKDF-SHA256+A256GCMKW', kid: recipientJWKey.kid };
161
157
  const finalProtectedHeader = { ...protectedHeader, ...recipientHeader };
162
158
  const protectedHeaderB64Url = Content.objectToRawBase64UrlSafe(finalProtectedHeader);
163
- // 2. Perform KEM to derive the Content Encryption Key (CEK).
159
+ // 2. Generate a fresh content key, then protect it for the ML-KEM recipient.
164
160
  const publicKeyBytes = Content.base64ToBytes(recipientJWKey.x);
165
- const cekSeedBytes = await this.cryptoHelper.getRandomBytes(32);
166
- const { derivedCekBytes, encapsulatedCekBytes } = await this.encapsulate(cekSeedBytes, secretJWKey.dBytes, publicKeyBytes);
167
- const encapsulatedKeyB64Url = Content.bytesToRawBase64UrlSafe(encapsulatedCekBytes);
161
+ const cekBytes = await this.cryptoHelper.getRandomBytes(32);
162
+ const kemSeedBytes = await this.cryptoHelper.getRandomBytes(32);
163
+ const { derivedCekBytes: sharedSecret, encapsulatedCekBytes } = await this.encapsulate(kemSeedBytes, secretJWKey.dBytes, publicKeyBytes);
164
+ const wrappedCek = await this.wrapCekForRecipient(cekBytes, sharedSecret, encapsulatedCekBytes, protectedHeaderB64Url, recipientJWKey.kid);
165
+ const encryptedKeyB64Url = Content.objectToRawBase64UrlSafe(wrappedCek);
168
166
  // 3. Encrypt the payload using the derived CEK and the *final* protected header as AAD.
169
167
  const payloadBytes = typeof payload === 'string'
170
168
  ? Content.stringToBytesUTF8(payload)
@@ -173,13 +171,13 @@ export class CryptographyService {
173
171
  // Note: Compressing a compact JWS string is often inefficient, but supported.
174
172
  const compressedPayload = pako.deflate(payloadBytes);
175
173
  const payloadString = Content.bytesToRawBase64UrlSafe(compressedPayload);
176
- const encrypted = await this.encrypt(payloadString, derivedCekBytes, protectedHeaderB64Url);
177
- return `${protectedHeaderB64Url}.${encapsulatedKeyB64Url}.${encrypted.iv}.${encrypted.ciphertext}.${encrypted.tag}`;
174
+ const encrypted = await this.encrypt(payloadString, cekBytes, protectedHeaderB64Url);
175
+ return `${protectedHeaderB64Url}.${encryptedKeyB64Url}.${encrypted.iv}.${encrypted.ciphertext}.${encrypted.tag}`;
178
176
  }
179
- const payloadString = Content.bytesToStringASCII(payloadBytes);
180
- const encrypted = await this.encrypt(payloadString, derivedCekBytes, protectedHeaderB64Url);
177
+ const payloadString = Content.bytesToStringUTF8(payloadBytes);
178
+ const encrypted = await this.encrypt(payloadString, cekBytes, protectedHeaderB64Url);
181
179
  // 4. Assemble the 5 parts of the compact JWE.
182
- return `${protectedHeaderB64Url}.${encapsulatedKeyB64Url}.${encrypted.iv}.${encrypted.ciphertext}.${encrypted.tag}`;
180
+ return `${protectedHeaderB64Url}.${encryptedKeyB64Url}.${encrypted.iv}.${encrypted.ciphertext}.${encrypted.tag}`;
183
181
  }
184
182
  async decryptJwe(jwe, secretKeyJwk) {
185
183
  const jweObject = typeof jwe === 'string' ? this.parseCompactJwe(jwe) : jwe;
@@ -187,9 +185,7 @@ export class CryptographyService {
187
185
  if (!recipient || !recipient.encrypted_key) {
188
186
  throw new Error(`JWE does not contain a recipient with kid=${secretKeyJwk.kid}`);
189
187
  }
190
- // Decapsulate to get the CEK
191
- const encapsulatedKeyBytes = Content.base64ToBytes(recipient.encrypted_key);
192
- const cekBytes = await this.decapsulate(encapsulatedKeyBytes, secretKeyJwk.dBytes);
188
+ const cekBytes = await this.unwrapRecipientCek(recipient.encrypted_key, secretKeyJwk, jweObject.protected);
193
189
  // Decrypt the payload
194
190
  const encryptedData = { ciphertext: jweObject.ciphertext, iv: jweObject.iv, tag: jweObject.tag };
195
191
  const decryptedPayloadString = await this.decrypt(encryptedData, cekBytes, jweObject.protected);
@@ -260,12 +256,9 @@ export class CryptographyService {
260
256
  return this.aesManager.decrypt(encryptedData, cekBytes, aad);
261
257
  }
262
258
  async encapsulate(cekSeedBytes, secretKeyBytes, recipientPublicKeyBytes) {
263
- // According to RFC 9278 (JWE with ML-KEM), a seed is used for the KEM encapsulation.
264
- // The KEM then derives a shared secret from this seed. It is this *derived* shared secret
265
- // that is used to encrypt the content, NOT the original seed.
266
- // The `encapsulate` function from the noble library handles this correctly by accepting the
267
- // seed as the second argument. It returns both the encapsulated key (`cipherText`)
268
- // and the derived shared secret, which we must use as the actual AES key.
259
+ // FIPS 203 ML-KEM encapsulation returns a ciphertext and a 32-byte shared secret.
260
+ // `secretKeyBytes` is retained only for public API compatibility; encapsulation
261
+ // requires the recipient public key, not a sender private key.
269
262
  const mlKem = await this.loadMlKem();
270
263
  const { sharedSecret, cipherText } = await mlKem.ml_kem768.encapsulate(recipientPublicKeyBytes, cekSeedBytes);
271
264
  return { derivedCekBytes: sharedSecret, encapsulatedCekBytes: cipherText };
@@ -274,6 +267,42 @@ export class CryptographyService {
274
267
  const mlKem = await this.loadMlKem();
275
268
  return mlKem.ml_kem768.decapsulate(encapsulatedBytes, secretKeyBytes);
276
269
  }
270
+ async wrapCekForRecipient(cekBytes, sharedSecret, kemCiphertext, protectedHeader, recipientKid) {
271
+ const kek = this.deriveRecipientKek(sharedSecret, protectedHeader, recipientKid);
272
+ const wrapped = await this.encrypt(Content.bytesToRawBase64UrlSafe(cekBytes), kek, `${protectedHeader}.${recipientKid}.cek`);
273
+ return {
274
+ v: 'gdc-mlkem-cek-wrap-v1',
275
+ kem: 'ML-KEM-768',
276
+ kdf: 'HKDF-SHA-256',
277
+ wrap: 'A256GCM',
278
+ kemCiphertext: Content.bytesToRawBase64UrlSafe(kemCiphertext),
279
+ iv: wrapped.iv,
280
+ ciphertext: wrapped.ciphertext,
281
+ tag: wrapped.tag,
282
+ };
283
+ }
284
+ async unwrapRecipientCek(encryptedKey, secretKeyJwk, protectedHeader) {
285
+ let wrapped;
286
+ try {
287
+ wrapped = Content.base64UrlSafeToJSON(encryptedKey);
288
+ }
289
+ catch {
290
+ // Legacy v0 used the raw ML-KEM ciphertext and the shared secret as CEK.
291
+ return this.decapsulate(Content.base64ToBytes(encryptedKey), secretKeyJwk.dBytes);
292
+ }
293
+ if (wrapped?.v !== 'gdc-mlkem-cek-wrap-v1' || wrapped.kem !== 'ML-KEM-768' || wrapped.kdf !== 'HKDF-SHA-256' || wrapped.wrap !== 'A256GCM') {
294
+ throw new Error('Unsupported ML-KEM CEK wrap profile.');
295
+ }
296
+ const sharedSecret = await this.decapsulate(Content.base64ToBytes(wrapped.kemCiphertext), secretKeyJwk.dBytes);
297
+ const kek = this.deriveRecipientKek(sharedSecret, protectedHeader, secretKeyJwk.kid);
298
+ const cek = Content.base64ToBytes(await this.decrypt({ iv: wrapped.iv, ciphertext: wrapped.ciphertext, tag: wrapped.tag }, kek, `${protectedHeader}.${secretKeyJwk.kid}.cek`));
299
+ if (cek.byteLength !== 32)
300
+ throw new Error('Invalid wrapped AES-256-GCM CEK.');
301
+ return cek;
302
+ }
303
+ deriveRecipientKek(sharedSecret, protectedHeader, recipientKid) {
304
+ return hkdf(sha256, sharedSecret, Content.stringToBytesUTF8('gdc-confidential-pqc-v1'), Content.stringToBytesUTF8(`${protectedHeader}.${recipientKid}.document-at-rest`), 32);
305
+ }
277
306
  async signBytes(payloadBytes, secretKeyBytes, alg) {
278
307
  const mlDsa = await this.loadMlDsa();
279
308
  switch (alg) {
@@ -15,23 +15,76 @@
15
15
  * Reference:
16
16
  * http://terminology.hl7.org/CodeSystem/v2-0203
17
17
  */
18
+ export declare const HL7_V2_0203_IDENTIFIER_SYSTEM: "http://terminology.hl7.org/CodeSystem/v2-0203";
19
+ export declare const HL7_V2_0203_REVERSE_DNS_PREFIX: "org.hl7.terminology.CodeSystem.v2-0203";
20
+ export declare const HL7_V2_0203_IDENTIFIER_CODES: Readonly<{
21
+ readonly DriverLicense: "DL";
22
+ readonly CitizenshipCard: "CZ";
23
+ readonly NationalNumber: "NN";
24
+ readonly PassportNumber: "PPN";
25
+ readonly JurisdictionalHealthNumber: "JHN";
26
+ readonly WorkPermit: "WP";
27
+ readonly StudyPermit: "SP";
28
+ readonly HealthCard: "HC";
29
+ readonly DonorRecord: "DR";
30
+ readonly PatientIdentifier: "PI";
31
+ }>;
32
+ export type Hl7V20203IdentifierCode = typeof HL7_V2_0203_IDENTIFIER_CODES[keyof typeof HL7_V2_0203_IDENTIFIER_CODES];
33
+ /** Product-neutral document kinds used by subject identifier forms. */
34
+ export declare const INDIVIDUAL_IDENTIFIER_KINDS: Readonly<{
35
+ readonly NationalPerson: "national-person";
36
+ readonly Passport: "passport";
37
+ readonly HealthNumber: "health-number";
38
+ readonly DriverLicense: "driver-license";
39
+ readonly WorkPermit: "work-permit";
40
+ readonly StudyPermit: "study-permit";
41
+ }>;
42
+ export type IndividualIdentifierKind = typeof INDIVIDUAL_IDENTIFIER_KINDS[keyof typeof INDIVIDUAL_IDENTIFIER_KINDS];
43
+ export declare const INDIVIDUAL_IDENTIFIER_KIND_CODES: Readonly<{
44
+ readonly "national-person": "NN";
45
+ readonly passport: "PPN";
46
+ readonly "health-number": "JHN";
47
+ readonly "driver-license": "DL";
48
+ readonly "work-permit": "WP";
49
+ readonly "study-permit": "SP";
50
+ }>;
18
51
  export declare enum IdKind {
19
52
  /** ISO 18013-5 mobile driving licence credential. */
20
53
  MobileDriverLicense = "org.iso.18013.5.1.mDL",
21
54
  /** HL7 driver license identifier. */
22
- PersonalDriverLicense = "org.hl7.terminology.codesystem.v2-0203.DL",
55
+ PersonalDriverLicense = "org.hl7.terminology.CodeSystem.v2-0203.DL",
23
56
  /** HL7 citizenship card identifier. */
24
- PersonalCitizenshipCard = "org.hl7.terminology.codesystem.v2-0203.CZ",
57
+ PersonalCitizenshipCard = "org.hl7.terminology.CodeSystem.v2-0203.CZ",
25
58
  /** HL7 national identity number. */
26
- PersonalNationalNumber = "org.hl7.terminology.codesystem.v2-0203.NN",
59
+ PersonalNationalNumber = "org.hl7.terminology.CodeSystem.v2-0203.NN",
60
+ /** HL7 passport number. */
61
+ PersonalPassportNumber = "org.hl7.terminology.CodeSystem.v2-0203.PPN",
27
62
  /** HL7 jurisdictional health number. */
28
- RegionalHeathCardNumber = "org.hl7.terminology.codesystem.v2-0203.JHN",
63
+ RegionalHealthCardNumber = "org.hl7.terminology.CodeSystem.v2-0203.JHN",
64
+ /** @deprecated Use `RegionalHealthCardNumber`. */
65
+ RegionalHeathCardNumber = "org.hl7.terminology.CodeSystem.v2-0203.JHN",
66
+ /** HL7 work permit. */
67
+ PersonalWorkPermit = "org.hl7.terminology.CodeSystem.v2-0203.WP",
68
+ /** HL7 study permit. */
69
+ PersonalStudyPermit = "org.hl7.terminology.CodeSystem.v2-0203.SP",
29
70
  /** HL7 local/private health card identifier. */
30
- LocalHealthCard = "org.hl7.terminology.codesystem.v2-0203.HC",
71
+ LocalHealthCard = "org.hl7.terminology.CodeSystem.v2-0203.HC",
31
72
  /** HL7 donor record identifier. */
32
- LocalDonorRecord = "org.hl7.terminology.codesystem.v2-0203.DR",
73
+ LocalDonorRecord = "org.hl7.terminology.CodeSystem.v2-0203.DR",
33
74
  /** HL7 local patient identifier. */
34
- LocalPatientIdentifier = "org.hl7.terminology.codesystem.v2-0203.PI"
75
+ LocalPatientIdentifier = "org.hl7.terminology.CodeSystem.v2-0203.PI"
35
76
  }
77
+ export declare const HL7_V2_0203_REVERSE_DNS_TYPES: Readonly<{
78
+ readonly DL: IdKind.PersonalDriverLicense;
79
+ readonly CZ: IdKind.PersonalCitizenshipCard;
80
+ readonly NN: IdKind.PersonalNationalNumber;
81
+ readonly PPN: IdKind.PersonalPassportNumber;
82
+ readonly JHN: IdKind.RegionalHealthCardNumber;
83
+ readonly WP: IdKind.PersonalWorkPermit;
84
+ readonly SP: IdKind.PersonalStudyPermit;
85
+ readonly HC: IdKind.LocalHealthCard;
86
+ readonly DR: IdKind.LocalDonorRecord;
87
+ readonly PI: IdKind.LocalPatientIdentifier;
88
+ }>;
36
89
  /** Union helper for code that wants typed values without depending on the enum object. */
37
90
  export type IdKindValue = typeof IdKind[keyof typeof IdKind];
@@ -15,22 +15,73 @@
15
15
  * Reference:
16
16
  * http://terminology.hl7.org/CodeSystem/v2-0203
17
17
  */
18
+ export const HL7_V2_0203_IDENTIFIER_SYSTEM = 'http://terminology.hl7.org/CodeSystem/v2-0203';
19
+ export const HL7_V2_0203_REVERSE_DNS_PREFIX = 'org.hl7.terminology.CodeSystem.v2-0203';
20
+ export const HL7_V2_0203_IDENTIFIER_CODES = Object.freeze({
21
+ DriverLicense: 'DL',
22
+ CitizenshipCard: 'CZ',
23
+ NationalNumber: 'NN',
24
+ PassportNumber: 'PPN',
25
+ JurisdictionalHealthNumber: 'JHN',
26
+ WorkPermit: 'WP',
27
+ StudyPermit: 'SP',
28
+ HealthCard: 'HC',
29
+ DonorRecord: 'DR',
30
+ PatientIdentifier: 'PI',
31
+ });
32
+ /** Product-neutral document kinds used by subject identifier forms. */
33
+ export const INDIVIDUAL_IDENTIFIER_KINDS = Object.freeze({
34
+ NationalPerson: 'national-person',
35
+ Passport: 'passport',
36
+ HealthNumber: 'health-number',
37
+ DriverLicense: 'driver-license',
38
+ WorkPermit: 'work-permit',
39
+ StudyPermit: 'study-permit',
40
+ });
41
+ export const INDIVIDUAL_IDENTIFIER_KIND_CODES = Object.freeze({
42
+ [INDIVIDUAL_IDENTIFIER_KINDS.NationalPerson]: HL7_V2_0203_IDENTIFIER_CODES.NationalNumber,
43
+ [INDIVIDUAL_IDENTIFIER_KINDS.Passport]: HL7_V2_0203_IDENTIFIER_CODES.PassportNumber,
44
+ [INDIVIDUAL_IDENTIFIER_KINDS.HealthNumber]: HL7_V2_0203_IDENTIFIER_CODES.JurisdictionalHealthNumber,
45
+ [INDIVIDUAL_IDENTIFIER_KINDS.DriverLicense]: HL7_V2_0203_IDENTIFIER_CODES.DriverLicense,
46
+ [INDIVIDUAL_IDENTIFIER_KINDS.WorkPermit]: HL7_V2_0203_IDENTIFIER_CODES.WorkPermit,
47
+ [INDIVIDUAL_IDENTIFIER_KINDS.StudyPermit]: HL7_V2_0203_IDENTIFIER_CODES.StudyPermit,
48
+ });
18
49
  export var IdKind;
19
50
  (function (IdKind) {
20
51
  /** ISO 18013-5 mobile driving licence credential. */
21
52
  IdKind["MobileDriverLicense"] = "org.iso.18013.5.1.mDL";
22
53
  /** HL7 driver license identifier. */
23
- IdKind["PersonalDriverLicense"] = "org.hl7.terminology.codesystem.v2-0203.DL";
54
+ IdKind["PersonalDriverLicense"] = "org.hl7.terminology.CodeSystem.v2-0203.DL";
24
55
  /** HL7 citizenship card identifier. */
25
- IdKind["PersonalCitizenshipCard"] = "org.hl7.terminology.codesystem.v2-0203.CZ";
56
+ IdKind["PersonalCitizenshipCard"] = "org.hl7.terminology.CodeSystem.v2-0203.CZ";
26
57
  /** HL7 national identity number. */
27
- IdKind["PersonalNationalNumber"] = "org.hl7.terminology.codesystem.v2-0203.NN";
58
+ IdKind["PersonalNationalNumber"] = "org.hl7.terminology.CodeSystem.v2-0203.NN";
59
+ /** HL7 passport number. */
60
+ IdKind["PersonalPassportNumber"] = "org.hl7.terminology.CodeSystem.v2-0203.PPN";
28
61
  /** HL7 jurisdictional health number. */
29
- IdKind["RegionalHeathCardNumber"] = "org.hl7.terminology.codesystem.v2-0203.JHN";
62
+ IdKind["RegionalHealthCardNumber"] = "org.hl7.terminology.CodeSystem.v2-0203.JHN";
63
+ /** @deprecated Use `RegionalHealthCardNumber`. */
64
+ IdKind["RegionalHeathCardNumber"] = "org.hl7.terminology.CodeSystem.v2-0203.JHN";
65
+ /** HL7 work permit. */
66
+ IdKind["PersonalWorkPermit"] = "org.hl7.terminology.CodeSystem.v2-0203.WP";
67
+ /** HL7 study permit. */
68
+ IdKind["PersonalStudyPermit"] = "org.hl7.terminology.CodeSystem.v2-0203.SP";
30
69
  /** HL7 local/private health card identifier. */
31
- IdKind["LocalHealthCard"] = "org.hl7.terminology.codesystem.v2-0203.HC";
70
+ IdKind["LocalHealthCard"] = "org.hl7.terminology.CodeSystem.v2-0203.HC";
32
71
  /** HL7 donor record identifier. */
33
- IdKind["LocalDonorRecord"] = "org.hl7.terminology.codesystem.v2-0203.DR";
72
+ IdKind["LocalDonorRecord"] = "org.hl7.terminology.CodeSystem.v2-0203.DR";
34
73
  /** HL7 local patient identifier. */
35
- IdKind["LocalPatientIdentifier"] = "org.hl7.terminology.codesystem.v2-0203.PI";
74
+ IdKind["LocalPatientIdentifier"] = "org.hl7.terminology.CodeSystem.v2-0203.PI";
36
75
  })(IdKind || (IdKind = {}));
76
+ export const HL7_V2_0203_REVERSE_DNS_TYPES = Object.freeze({
77
+ [HL7_V2_0203_IDENTIFIER_CODES.DriverLicense]: IdKind.PersonalDriverLicense,
78
+ [HL7_V2_0203_IDENTIFIER_CODES.CitizenshipCard]: IdKind.PersonalCitizenshipCard,
79
+ [HL7_V2_0203_IDENTIFIER_CODES.NationalNumber]: IdKind.PersonalNationalNumber,
80
+ [HL7_V2_0203_IDENTIFIER_CODES.PassportNumber]: IdKind.PersonalPassportNumber,
81
+ [HL7_V2_0203_IDENTIFIER_CODES.JurisdictionalHealthNumber]: IdKind.RegionalHealthCardNumber,
82
+ [HL7_V2_0203_IDENTIFIER_CODES.WorkPermit]: IdKind.PersonalWorkPermit,
83
+ [HL7_V2_0203_IDENTIFIER_CODES.StudyPermit]: IdKind.PersonalStudyPermit,
84
+ [HL7_V2_0203_IDENTIFIER_CODES.HealthCard]: IdKind.LocalHealthCard,
85
+ [HL7_V2_0203_IDENTIFIER_CODES.DonorRecord]: IdKind.LocalDonorRecord,
86
+ [HL7_V2_0203_IDENTIFIER_CODES.PatientIdentifier]: IdKind.LocalPatientIdentifier,
87
+ });
@@ -3,7 +3,9 @@ export declare enum ClaimsServiceSchemaorg {
3
3
  areaServed = "org.schema.Service.areaServed",
4
4
  additionalType = "org.schema.Service.additionalType",
5
5
  category = "org.schema.Service.category",
6
+ description = "org.schema.Service.description",
6
7
  identifier = "org.schema.Service.identifier",
8
+ name = "org.schema.Service.name",
7
9
  serviceType = "org.schema.Service.serviceType",
8
10
  termsOfService = "org.schema.Service.termsOfService",
9
11
  url = "org.schema.Service.url"
@@ -36,6 +38,8 @@ export declare enum ClaimsSoftwareApplicationSchemaorg {
36
38
  * based on Schema.org vocabulary.
37
39
  */
38
40
  export declare enum ClaimsOrganizationSchemaorg {
41
+ /** Public aliases used for exact organization or individual resolution. */
42
+ sameAs = "org.schema.Organization.sameAs",
39
43
  /** ISO 3166-1 alpha-2 (two-letter country code). The jurisdiction could be the country or the region (county, province or state) */
40
44
  addressCountry = "org.schema.Organization.address.addressCountry",
41
45
  /** ISO 3166-2 code for administrative divisions. The jurisdiction could be the country or the region (county, province or state) */
@@ -5,7 +5,9 @@ export var ClaimsServiceSchemaorg;
5
5
  ClaimsServiceSchemaorg["areaServed"] = "org.schema.Service.areaServed";
6
6
  ClaimsServiceSchemaorg["additionalType"] = "org.schema.Service.additionalType";
7
7
  ClaimsServiceSchemaorg["category"] = "org.schema.Service.category";
8
+ ClaimsServiceSchemaorg["description"] = "org.schema.Service.description";
8
9
  ClaimsServiceSchemaorg["identifier"] = "org.schema.Service.identifier";
10
+ ClaimsServiceSchemaorg["name"] = "org.schema.Service.name";
9
11
  ClaimsServiceSchemaorg["serviceType"] = "org.schema.Service.serviceType";
10
12
  ClaimsServiceSchemaorg["termsOfService"] = "org.schema.Service.termsOfService";
11
13
  ClaimsServiceSchemaorg["url"] = "org.schema.Service.url";
@@ -40,6 +42,8 @@ export var ClaimsSoftwareApplicationSchemaorg;
40
42
  */
41
43
  export var ClaimsOrganizationSchemaorg;
42
44
  (function (ClaimsOrganizationSchemaorg) {
45
+ /** Public aliases used for exact organization or individual resolution. */
46
+ ClaimsOrganizationSchemaorg["sameAs"] = "org.schema.Organization.sameAs";
43
47
  /** ISO 3166-1 alpha-2 (two-letter country code). The jurisdiction could be the country or the region (county, province or state) */
44
48
  ClaimsOrganizationSchemaorg["addressCountry"] = "org.schema.Organization.address.addressCountry";
45
49
  /** ISO 3166-2 code for administrative divisions. The jurisdiction could be the country or the region (county, province or state) */
@@ -7,5 +7,6 @@
7
7
  */
8
8
  export declare const UrnPrefixes: Readonly<{
9
9
  readonly JwkThumbprintSha256KeyId: "urn:ietf:params:oauth:jwk-thumbprint:sha-256:";
10
+ readonly Multibase: "urn:multibase:";
10
11
  }>;
11
12
  export type UrnPrefix = typeof UrnPrefixes[keyof typeof UrnPrefixes];
@@ -8,4 +8,5 @@
8
8
  */
9
9
  export const UrnPrefixes = Object.freeze({
10
10
  JwkThumbprintSha256KeyId: 'urn:ietf:params:oauth:jwk-thumbprint:sha-256:',
11
+ Multibase: 'urn:multibase:',
11
12
  });
@@ -20,3 +20,4 @@ export * from './convert-encounter';
20
20
  export * from './convert-related-person';
21
21
  export * from './convert-coverage';
22
22
  export * from './convert-clinical-impression';
23
+ export * from './schemaorg-to-gaia-x';
@@ -20,3 +20,4 @@ export * from './convert-encounter.js';
20
20
  export * from './convert-related-person.js';
21
21
  export * from './convert-coverage.js';
22
22
  export * from './convert-clinical-impression.js';
23
+ export * from './schemaorg-to-gaia-x.js';
@@ -0,0 +1,116 @@
1
+ import type { ClaimsRecord } from '../models/resource-document';
2
+ import { type GaiaXCredentialDraft, type GaiaXLegalPersonCredentialSubject, type GaiaXServiceOfferingCredentialSubject, type GaiaXVcJwtAttachment, type IcaMemberDiscoveryData } from '../models/gaia-x';
3
+ import type { DidDocument } from '../models/did';
4
+ export interface SchemaOrgOrganizationRegistrationIdentifier {
5
+ additionalType: string;
6
+ value: string;
7
+ }
8
+ /**
9
+ * Reads the canonical schema.org `PropertyValue` organization identifier.
10
+ * During migration it also accepts the historical nested
11
+ * `identifier.identifier` form. When the structure is absent, `taxID` becomes
12
+ * a VAT/TAX fallback, matching ICA's OrganizationCredential issuance rule.
13
+ */
14
+ export declare function resolveSchemaOrgOrganizationRegistrationIdentifier(credentialSubject: Record<string, unknown>): SchemaOrgOrganizationRegistrationIdentifier;
15
+ /**
16
+ * Converts canonical schema.org organization claims into an unsigned Gaia-X
17
+ * ICAM 25.11 LegalPerson VC draft.
18
+ *
19
+ * Semantic boundary:
20
+ * - Gaia-X `LegalPerson` is the juridical organization/legal entity. It is not
21
+ * GDC's natural-person `LegalRepresentativeCredential`.
22
+ * - representative claims are intentionally never projected by this function.
23
+ * - VAT/tax remains in the source schema.org OrganizationCredential. Gaia-X's
24
+ * `gx:legalRegistrationNumber` is a resolvable reference to the separate
25
+ * registration/notary credential, not an assertion synthesized from VAT.
26
+ * - this function is deterministic and does not sign. The authoritative GW
27
+ * signs the returned draft as VC-JWT; ICA caches and verifies that exact JWT.
28
+ *
29
+ * This projection targets the credential structure and semantic model described
30
+ * by Gaia-X ICAM 25.11. Passing the resulting shape tests neither Gaia-X policy
31
+ * compliance nor GXDCH/TCK conformance.
32
+ *
33
+ * @see https://docs.gaia-x.eu/technical-committee/identity-credential-access-management/25.11/gaia-x_credentials/
34
+ * @see https://docs.gaia-x.eu/technical-committee/identity-credential-access-management/25.11/semantic_model/
35
+ */
36
+ export declare function buildGaiaXLegalPersonCredentialDraft(input: Readonly<{
37
+ claims: ClaimsRecord;
38
+ credentialId: string;
39
+ subjectId: string;
40
+ issuerId: string;
41
+ legalRegistrationNumberCredentialId: string;
42
+ validFrom: string;
43
+ }>): GaiaXCredentialDraft<GaiaXLegalPersonCredentialSubject>;
44
+ /**
45
+ * Projects an ICA/GDC schema.org OrganizationCredential into a Gaia-X
46
+ * LegalPerson draft while preserving the organization `credentialSubject.id`.
47
+ *
48
+ * The source VC and Gaia-X VC are different signed statements, so their
49
+ * credential IDs must not silently collide. By default the Gaia-X credential
50
+ * receives `<source credential id>#gaia-x-legal-person`; callers may provide a
51
+ * separately resolvable ID. `registrationIdentifier` is returned for the
52
+ * notary/GXDCH registration step, while `gx:legalRegistrationNumber` remains a
53
+ * reference to the resulting registration credential.
54
+ */
55
+ export declare function buildGaiaXLegalPersonProjectionFromOrganizationCredential(input: Readonly<{
56
+ organizationCredential: Record<string, unknown>;
57
+ credentialId?: string;
58
+ issuerId: string;
59
+ legalRegistrationNumberCredentialId: string;
60
+ addressSubdivisionCode?: string;
61
+ addressCountryCode?: string;
62
+ validFrom: string;
63
+ }>): Readonly<{
64
+ sourceCredentialId: string;
65
+ registrationIdentifier: SchemaOrgOrganizationRegistrationIdentifier;
66
+ credential: GaiaXCredentialDraft<GaiaXLegalPersonCredentialSubject>;
67
+ }>;
68
+ /**
69
+ * Converts schema.org service claims into an unsigned Gaia-X ServiceOffering
70
+ * VC draft. A service offering is independent from the participant credential:
71
+ * it references the LegalPerson credential through `gx:providedBy` and carries
72
+ * its own mandatory terms reference.
73
+ *
74
+ * The GW must sign this draft as a separate VC-JWT. A DCAT DataService is useful
75
+ * catalog metadata but is not a substitute for this credential.
76
+ */
77
+ export declare function buildGaiaXServiceOfferingCredentialDraft(input: Readonly<{
78
+ claims: ClaimsRecord;
79
+ credentialId: string;
80
+ subjectId: string;
81
+ issuerId: string;
82
+ providedByCredentialId: string;
83
+ termsAndConditionsUrl: string;
84
+ termsAndConditionsHash: string;
85
+ validFrom: string;
86
+ }>): GaiaXCredentialDraft<GaiaXServiceOfferingCredentialSubject>;
87
+ /** Builds the mandatory first Gaia-X participant attachment for ICA discovery. */
88
+ export declare function buildGaiaXParticipantAttachment(input: Readonly<{
89
+ id: string;
90
+ jwt: string;
91
+ }>): GaiaXVcJwtAttachment;
92
+ /** Wraps an exact signed Gaia-X VC-JWT without decoding or re-signing it. */
93
+ export declare function buildGaiaXVcJwtAttachment(input: Readonly<{
94
+ id: string;
95
+ jwt: string;
96
+ role: GaiaXVcJwtAttachment['role'];
97
+ }>): GaiaXVcJwtAttachment;
98
+ /**
99
+ * Assembles one ICA member discovery entry and enforces the interoperable
100
+ * ordering contract: schema.org OrganizationCredential first in `vc[]`, and
101
+ * Gaia-X participant VC-JWT first in `attachments[]`.
102
+ *
103
+ * The caller owns signature verification and cache freshness. This helper does
104
+ * not derive or duplicate VAT outside `vc[]` and does not mutate signed data.
105
+ */
106
+ export declare function buildIcaMemberDiscoveryData(input: Readonly<{
107
+ id: string;
108
+ vc: IcaMemberDiscoveryData['vc'];
109
+ did: {
110
+ document: DidDocument;
111
+ meta: IcaMemberDiscoveryData['did']['meta'];
112
+ };
113
+ attachments: GaiaXVcJwtAttachment[];
114
+ dcat?: IcaMemberDiscoveryData['dcat'];
115
+ meta?: IcaMemberDiscoveryData['meta'];
116
+ }>): IcaMemberDiscoveryData;