gdc-common-utils-ts 2.3.1 → 2.3.3

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 (47) 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/convert-allergy-intolerance.js +2 -1
  11. package/dist/convert/convert-condition.js +2 -1
  12. package/dist/convert/index.d.ts +1 -0
  13. package/dist/convert/index.js +1 -0
  14. package/dist/convert/schemaorg-to-gaia-x.d.ts +115 -0
  15. package/dist/convert/schemaorg-to-gaia-x.js +208 -0
  16. package/dist/models/bundle-editor-types.d.ts +13 -3
  17. package/dist/models/bundle-editor-types.js +9 -1
  18. package/dist/models/gaia-x.d.ts +128 -0
  19. package/dist/models/gaia-x.js +26 -0
  20. package/dist/models/index.d.ts +2 -0
  21. package/dist/models/index.js +2 -0
  22. package/dist/models/jwe.d.ts +25 -0
  23. package/dist/models/subject-identifier-ledger.d.ts +16 -0
  24. package/dist/models/subject-identifier-ledger.js +1 -0
  25. package/dist/utils/bundle-editor-core.d.ts +9 -0
  26. package/dist/utils/bundle-editor-core.js +46 -0
  27. package/dist/utils/bundle-editor.d.ts +2 -0
  28. package/dist/utils/bundle-editor.js +2 -0
  29. package/dist/utils/bundle-entry-editor.d.ts +6 -0
  30. package/dist/utils/bundle-entry-editor.js +20 -0
  31. package/dist/utils/communication-consent-access-editor.d.ts +14 -1
  32. package/dist/utils/communication-consent-access-editor.js +27 -0
  33. package/dist/utils/consent-entry-editor.d.ts +42 -0
  34. package/dist/utils/consent-entry-editor.js +80 -0
  35. package/dist/utils/index.d.ts +1 -0
  36. package/dist/utils/index.js +1 -0
  37. package/dist/utils/individual-identifier.d.ts +14 -0
  38. package/dist/utils/individual-identifier.js +39 -0
  39. package/dist/utils/multibasehash.d.ts +19 -5
  40. package/dist/utils/multibasehash.js +35 -10
  41. package/dist/utils/multiformat-profile.d.ts +4 -2
  42. package/dist/utils/multiformat-profile.js +5 -3
  43. package/dist/utils/related-person-entry-editor.d.ts +32 -0
  44. package/dist/utils/related-person-entry-editor.js +97 -0
  45. package/dist/utils/same-as.d.ts +10 -0
  46. package/dist/utils/same-as.js +28 -3
  47. package/package.json +2 -1
@@ -0,0 +1,208 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { ClaimsOrganizationSchemaorg, ClaimsServiceSchemaorg } from '../constants/schemaorg.js';
3
+ import { GaiaXCredentialAttachmentFormat, GaiaXCredentialAttachmentRole, GaiaXCredentialMediaType, } from '../models/gaia-x.js';
4
+ const W3C_VC_V2_CONTEXT = 'https://www.w3.org/ns/credentials/v2';
5
+ function requiredClaim(claims, key, label) {
6
+ const value = String(claims[key] ?? '').trim();
7
+ if (!value)
8
+ throw new Error(`Missing ${label} required for Gaia-X projection.`);
9
+ return value;
10
+ }
11
+ function optionalClaim(claims, key) {
12
+ const value = String(claims[key] ?? '').trim();
13
+ return value || undefined;
14
+ }
15
+ function requiredInput(value, label) {
16
+ const normalized = value.trim();
17
+ if (!normalized)
18
+ throw new Error(`Missing ${label} required for Gaia-X projection.`);
19
+ return normalized;
20
+ }
21
+ function asObject(value) {
22
+ return value && typeof value === 'object' && !Array.isArray(value)
23
+ ? value
24
+ : undefined;
25
+ }
26
+ function asString(value) {
27
+ return typeof value === 'string' ? value.trim() : '';
28
+ }
29
+ /**
30
+ * Reads the canonical schema.org `PropertyValue` organization identifier.
31
+ * During migration it also accepts the historical nested
32
+ * `identifier.identifier` form. When the structure is absent, `taxID` becomes
33
+ * a VAT/TAX fallback, matching ICA's OrganizationCredential issuance rule.
34
+ */
35
+ export function resolveSchemaOrgOrganizationRegistrationIdentifier(credentialSubject) {
36
+ const outerIdentifier = asObject(credentialSubject.identifier);
37
+ const identifier = asObject(outerIdentifier?.identifier) || outerIdentifier;
38
+ const taxId = asString(credentialSubject.taxID || credentialSubject.taxId);
39
+ const value = asString(identifier?.value) || taxId;
40
+ if (!value)
41
+ throw new Error('Missing OrganizationCredential credentialSubject.identifier.value or taxID.');
42
+ const explicitType = asString(identifier?.additionalType || outerIdentifier?.additionalType);
43
+ return {
44
+ additionalType: explicitType || (/^VAT[A-Z]{2}-/i.test(value) ? 'VAT' : 'TAX'),
45
+ value,
46
+ };
47
+ }
48
+ /**
49
+ * Converts canonical schema.org organization claims into an unsigned Gaia-X
50
+ * ICAM 25.11 LegalPerson VC draft.
51
+ *
52
+ * Semantic boundary:
53
+ * - Gaia-X `LegalPerson` is the juridical organization/legal entity. It is not
54
+ * GDC's natural-person `LegalRepresentativeCredential`.
55
+ * - representative claims are intentionally never projected by this function.
56
+ * - VAT/tax remains in the source schema.org OrganizationCredential. Gaia-X's
57
+ * `gx:legalRegistrationNumber` is a resolvable reference to the separate
58
+ * registration/notary credential, not an assertion synthesized from VAT.
59
+ * - this function is deterministic and does not sign. The authoritative GW
60
+ * signs the returned draft as VC-JWT; ICA caches and verifies that exact JWT.
61
+ *
62
+ * This projection targets the credential structure and semantic model described
63
+ * by Gaia-X ICAM 25.11. Passing the resulting shape tests neither Gaia-X policy
64
+ * compliance nor GXDCH/TCK conformance.
65
+ *
66
+ * @see https://docs.gaia-x.eu/technical-committee/identity-credential-access-management/25.11/gaia-x_credentials/
67
+ * @see https://docs.gaia-x.eu/technical-committee/identity-credential-access-management/25.11/semantic_model/
68
+ */
69
+ export function buildGaiaXLegalPersonCredentialDraft(input) {
70
+ const country = requiredClaim(input.claims, ClaimsOrganizationSchemaorg.addressCountry, 'organization ISO 3166-1 country code').toUpperCase();
71
+ const gaiaXAddress = { 'gx:countryCode': country };
72
+ const website = optionalClaim(input.claims, ClaimsOrganizationSchemaorg.url);
73
+ return {
74
+ '@context': [W3C_VC_V2_CONTEXT],
75
+ type: ['VerifiableCredential', 'LegalPerson'],
76
+ id: requiredInput(input.credentialId, 'credentialId'),
77
+ issuer: requiredInput(input.issuerId, 'issuerId'),
78
+ validFrom: requiredInput(input.validFrom, 'validFrom'),
79
+ credentialSubject: {
80
+ id: requiredInput(input.subjectId, 'subjectId'),
81
+ type: 'gx:LegalPerson',
82
+ 'gx:legalName': requiredClaim(input.claims, ClaimsOrganizationSchemaorg.legalName, 'organization legal name'),
83
+ 'gx:legalRegistrationNumber': {
84
+ id: requiredInput(input.legalRegistrationNumberCredentialId, 'legal registration number credential id'),
85
+ },
86
+ 'gx:headquarterAddress': gaiaXAddress,
87
+ 'gx:legalAddress': gaiaXAddress,
88
+ ...(website ? { 'gx:website': website } : {}),
89
+ },
90
+ };
91
+ }
92
+ /**
93
+ * Projects an ICA/GDC schema.org OrganizationCredential into a Gaia-X
94
+ * LegalPerson draft while preserving the organization `credentialSubject.id`.
95
+ *
96
+ * The source VC and Gaia-X VC are different signed statements, so their
97
+ * credential IDs must not silently collide. By default the Gaia-X credential
98
+ * receives `<source credential id>#gaia-x-legal-person`; callers may provide a
99
+ * separately resolvable ID. `registrationIdentifier` is returned for the
100
+ * notary/GXDCH registration step, while `gx:legalRegistrationNumber` remains a
101
+ * reference to the resulting registration credential.
102
+ */
103
+ export function buildGaiaXLegalPersonProjectionFromOrganizationCredential(input) {
104
+ const sourceCredentialId = requiredInput(asString(input.organizationCredential.id), 'source credential id');
105
+ const subject = asObject(input.organizationCredential.credentialSubject);
106
+ if (!subject)
107
+ throw new Error('OrganizationCredential requires one object credentialSubject.');
108
+ const subjectId = requiredInput(asString(subject.id || subject['@id']), 'organization credentialSubject.id');
109
+ const registrationIdentifier = resolveSchemaOrgOrganizationRegistrationIdentifier(subject);
110
+ const address = asObject(subject.address);
111
+ const claims = {
112
+ [ClaimsOrganizationSchemaorg.legalName]: asString(subject.legalName || subject.name),
113
+ [ClaimsOrganizationSchemaorg.url]: asString(subject.url),
114
+ [ClaimsOrganizationSchemaorg.addressCountry]: input.addressCountryCode || asString(address?.addressCountry),
115
+ [ClaimsOrganizationSchemaorg.identifierType]: registrationIdentifier.additionalType,
116
+ [ClaimsOrganizationSchemaorg.identifierValue]: registrationIdentifier.value,
117
+ };
118
+ return {
119
+ sourceCredentialId,
120
+ registrationIdentifier,
121
+ credential: buildGaiaXLegalPersonCredentialDraft({
122
+ claims,
123
+ credentialId: input.credentialId || `${sourceCredentialId}#gaia-x-legal-person`,
124
+ subjectId,
125
+ issuerId: input.issuerId,
126
+ legalRegistrationNumberCredentialId: input.legalRegistrationNumberCredentialId,
127
+ validFrom: input.validFrom,
128
+ }),
129
+ };
130
+ }
131
+ /**
132
+ * Converts schema.org service claims into an unsigned Gaia-X ServiceOffering
133
+ * VC draft. A service offering is independent from the participant credential:
134
+ * it references the LegalPerson credential through `gx:providedBy` and carries
135
+ * its own mandatory terms reference.
136
+ *
137
+ * The GW must sign this draft as a separate VC-JWT. A DCAT DataService is useful
138
+ * catalog metadata but is not a substitute for this credential.
139
+ */
140
+ export function buildGaiaXServiceOfferingCredentialDraft(input) {
141
+ const name = optionalClaim(input.claims, ClaimsServiceSchemaorg.name);
142
+ const description = optionalClaim(input.claims, ClaimsServiceSchemaorg.description);
143
+ const endpointUrl = optionalClaim(input.claims, ClaimsServiceSchemaorg.url);
144
+ const termsHash = requiredInput(input.termsAndConditionsHash, 'terms and conditions SHA-256 hash');
145
+ if (!/^[a-f0-9]{64}$/i.test(termsHash)) {
146
+ throw new Error('Gaia-X terms and conditions hash must be a 64-character SHA-256 hexadecimal digest of the published document bytes.');
147
+ }
148
+ return {
149
+ '@context': [W3C_VC_V2_CONTEXT],
150
+ type: ['VerifiableCredential', 'ServiceOffering'],
151
+ id: requiredInput(input.credentialId, 'credentialId'),
152
+ issuer: requiredInput(input.issuerId, 'issuerId'),
153
+ validFrom: requiredInput(input.validFrom, 'validFrom'),
154
+ credentialSubject: {
155
+ id: requiredInput(input.subjectId, 'subjectId'),
156
+ type: 'gx:ServiceOffering',
157
+ 'gx:providedBy': { id: requiredInput(input.providedByCredentialId, 'providedBy credential id') },
158
+ 'gx:serviceOfferingTermsAndConditions': [{
159
+ 'gx:url': requiredInput(input.termsAndConditionsUrl, 'terms and conditions URL'),
160
+ 'gx:hash': termsHash.toLowerCase(),
161
+ }],
162
+ ...(name ? { 'gx:name': name } : {}),
163
+ ...(description ? { 'gx:description': description } : {}),
164
+ ...(endpointUrl ? { 'gx:endpoint': [{ 'gx:endpointURL': endpointUrl }] } : {}),
165
+ },
166
+ };
167
+ }
168
+ /** Builds the mandatory first Gaia-X participant attachment for ICA discovery. */
169
+ export function buildGaiaXParticipantAttachment(input) {
170
+ return buildGaiaXVcJwtAttachment({
171
+ ...input,
172
+ role: GaiaXCredentialAttachmentRole.Participant,
173
+ });
174
+ }
175
+ /** Wraps an exact signed Gaia-X VC-JWT without decoding or re-signing it. */
176
+ export function buildGaiaXVcJwtAttachment(input) {
177
+ return {
178
+ id: requiredInput(input.id, 'attachment id'),
179
+ format: GaiaXCredentialAttachmentFormat,
180
+ role: input.role,
181
+ media_type: GaiaXCredentialMediaType.VcJwt,
182
+ data: { json: { jwt: requiredInput(input.jwt, 'VC-JWT') } },
183
+ };
184
+ }
185
+ /**
186
+ * Assembles one ICA member discovery entry and enforces the interoperable
187
+ * ordering contract: schema.org OrganizationCredential first in `vc[]`, and
188
+ * Gaia-X participant VC-JWT first in `attachments[]`.
189
+ *
190
+ * The caller owns signature verification and cache freshness. This helper does
191
+ * not derive or duplicate VAT outside `vc[]` and does not mutate signed data.
192
+ */
193
+ export function buildIcaMemberDiscoveryData(input) {
194
+ if (!input.vc.length) {
195
+ throw new Error('ICA member discovery requires vc[0] OrganizationCredential.');
196
+ }
197
+ if (input.attachments[0]?.role !== GaiaXCredentialAttachmentRole.Participant) {
198
+ throw new Error('ICA member discovery requires the Gaia-X participant VC-JWT as attachments[0].');
199
+ }
200
+ return {
201
+ id: requiredInput(input.id, 'member id'),
202
+ vc: [...input.vc],
203
+ did: { document: input.did.document, meta: { ...input.did.meta } },
204
+ attachments: [...input.attachments],
205
+ ...(input.dcat ? { dcat: input.dcat } : {}),
206
+ ...(input.meta ? { meta: input.meta } : {}),
207
+ };
208
+ }
@@ -1,5 +1,5 @@
1
1
  import { type BundleEntry, type BundleJsonApi, type BundleRequest } from './bundle';
2
- import { EmployeeBundleOperations, type EmployeeClaims } from '../utils/employee';
2
+ import { type EmployeeClaims } from '../utils/employee';
3
3
  import type { BundleEntryEditor } from '../utils/bundle-entry-editor';
4
4
  import type { EmployeeEntryEditor } from '../utils/employee-entry-editor';
5
5
  import type { VitalSignEntryEditor } from '../utils/vital-sign-entry-editor';
@@ -18,11 +18,21 @@ import type { CoverageEntryEditor } from '../utils/coverage-entry-editor';
18
18
  import type { ImmunizationEntryEditor } from '../utils/immunization-entry-editor';
19
19
  import type { ProcedureEntryEditor } from '../utils/procedure-entry-editor';
20
20
  import type { DiagnosticReportEntryEditor } from '../utils/diagnostic-report-entry-editor';
21
- export type BundleOperation = (typeof EmployeeBundleOperations)[keyof typeof EmployeeBundleOperations];
21
+ import type { ConsentEntryEditor } from '../utils/consent-entry-editor';
22
+ import type { RelatedPersonEntryEditor } from '../utils/related-person-entry-editor';
23
+ /** Runtime-neutral business operations staged by `BundleEditor`. */
24
+ export declare const BundleOperations: Readonly<{
25
+ readonly create: "create";
26
+ readonly search: "search";
27
+ readonly disable: "disable";
28
+ readonly purge: "purge";
29
+ }>;
30
+ export type BundleOperation = (typeof BundleOperations)[keyof typeof BundleOperations];
22
31
  /** Resource types that the shared bundle editors currently know how to open as typed entry editors. */
23
32
  export declare const BundleEditableResourceTypes: Readonly<{
24
33
  readonly employee: "Employee";
25
34
  readonly consent: "Consent";
35
+ readonly relatedPerson: "RelatedPerson";
26
36
  readonly observation: "Observation";
27
37
  readonly vitalSign: "Observation";
28
38
  readonly allergyIntolerance: "AllergyIntolerance";
@@ -86,4 +96,4 @@ export type BundleJsonApiShape = BundleJsonApi<BundleEntry>;
86
96
  * resource type they requested, instead of falling back to the generic
87
97
  * `BundleEntryEditor` API.
88
98
  */
89
- export type ResourceTypeEntryEditor<T extends AllowedResourceType> = T extends typeof BundleEditableResourceTypes.employee ? EmployeeEntryEditor : T extends typeof BundleEditableResourceTypes.vitalSign ? VitalSignEntryEditor : T extends typeof BundleEditableResourceTypes.observation ? ObservationEntryEditor : T extends typeof BundleEditableResourceTypes.allergyIntolerance ? AllergyIntoleranceEntryEditor : T extends typeof BundleEditableResourceTypes.condition ? ConditionEntryEditor : T extends typeof BundleEditableResourceTypes.medicationStatement ? MedicationStatementEntryEditor : T extends typeof BundleEditableResourceTypes.documentReference ? DocumentReferenceEntryEditor : T extends typeof BundleEditableResourceTypes.carePlan ? CarePlanEntryEditor : T extends typeof BundleEditableResourceTypes.flag ? FlagEntryEditor : T extends typeof BundleEditableResourceTypes.clinicalImpression ? ClinicalImpressionEntryEditor : T extends typeof BundleEditableResourceTypes.device ? DeviceEntryEditor : T extends typeof BundleEditableResourceTypes.deviceUseStatement ? DeviceUseStatementEntryEditor : T extends typeof BundleEditableResourceTypes.encounter ? EncounterEntryEditor : T extends typeof BundleEditableResourceTypes.coverage ? CoverageEntryEditor : T extends typeof BundleEditableResourceTypes.immunization ? ImmunizationEntryEditor : T extends typeof BundleEditableResourceTypes.procedure ? ProcedureEntryEditor : T extends typeof BundleEditableResourceTypes.diagnosticReport ? DiagnosticReportEntryEditor : BundleEntryEditor;
99
+ export type ResourceTypeEntryEditor<T extends AllowedResourceType> = T extends typeof BundleEditableResourceTypes.employee ? EmployeeEntryEditor : T extends typeof BundleEditableResourceTypes.consent ? ConsentEntryEditor : T extends typeof BundleEditableResourceTypes.relatedPerson ? RelatedPersonEntryEditor : T extends typeof BundleEditableResourceTypes.vitalSign ? VitalSignEntryEditor : T extends typeof BundleEditableResourceTypes.observation ? ObservationEntryEditor : T extends typeof BundleEditableResourceTypes.allergyIntolerance ? AllergyIntoleranceEntryEditor : T extends typeof BundleEditableResourceTypes.condition ? ConditionEntryEditor : T extends typeof BundleEditableResourceTypes.medicationStatement ? MedicationStatementEntryEditor : T extends typeof BundleEditableResourceTypes.documentReference ? DocumentReferenceEntryEditor : T extends typeof BundleEditableResourceTypes.carePlan ? CarePlanEntryEditor : T extends typeof BundleEditableResourceTypes.flag ? FlagEntryEditor : T extends typeof BundleEditableResourceTypes.clinicalImpression ? ClinicalImpressionEntryEditor : T extends typeof BundleEditableResourceTypes.device ? DeviceEntryEditor : T extends typeof BundleEditableResourceTypes.deviceUseStatement ? DeviceUseStatementEntryEditor : T extends typeof BundleEditableResourceTypes.encounter ? EncounterEntryEditor : T extends typeof BundleEditableResourceTypes.coverage ? CoverageEntryEditor : T extends typeof BundleEditableResourceTypes.immunization ? ImmunizationEntryEditor : T extends typeof BundleEditableResourceTypes.procedure ? ProcedureEntryEditor : T extends typeof BundleEditableResourceTypes.diagnosticReport ? DiagnosticReportEntryEditor : BundleEntryEditor;
@@ -5,11 +5,19 @@
5
5
  * - Do not move helper implementations or class logic here.
6
6
  */
7
7
  import { ResourceTypesFhirR4 } from '../constants/fhir-resource-types.js';
8
- import { EmployeeResourceTypes, } from '../utils/employee.js';
8
+ import { EmployeeBundleOperations, EmployeeResourceTypes, } from '../utils/employee.js';
9
+ /** Runtime-neutral business operations staged by `BundleEditor`. */
10
+ export const BundleOperations = Object.freeze({
11
+ create: EmployeeBundleOperations.create,
12
+ search: EmployeeBundleOperations.search,
13
+ disable: EmployeeBundleOperations.disable,
14
+ purge: EmployeeBundleOperations.purge,
15
+ });
9
16
  /** Resource types that the shared bundle editors currently know how to open as typed entry editors. */
10
17
  export const BundleEditableResourceTypes = Object.freeze({
11
18
  employee: EmployeeResourceTypes.employee,
12
19
  consent: ResourceTypesFhirR4.Consent,
20
+ relatedPerson: ResourceTypesFhirR4.RelatedPerson,
13
21
  observation: ResourceTypesFhirR4.Observation,
14
22
  vitalSign: ResourceTypesFhirR4.Observation,
15
23
  allergyIntolerance: ResourceTypesFhirR4.AllergyIntolerance,
@@ -0,0 +1,128 @@
1
+ import type { DidCommAttachment } from './comm';
2
+ import type { DidDocument } from './did';
3
+ export type JsonObject = Record<string, unknown>;
4
+ /** Versioned Gaia-X profile targeted by the schema.org semantic projection. */
5
+ export declare const GaiaXProfile: Readonly<{
6
+ readonly Icam2511: "icam-25.11";
7
+ }>;
8
+ /** Media types used to transport signed Gaia-X credentials without decoding them. */
9
+ export declare const GaiaXCredentialMediaType: Readonly<{
10
+ readonly VcJwt: "application/vc+jwt";
11
+ }>;
12
+ /** DIDComm attachment format identifier used by the existing ICA contract. */
13
+ export declare const GaiaXCredentialAttachmentFormat: "vc+jwt";
14
+ /**
15
+ * Semantic roles for Gaia-X VC-JWT attachments in an ICA member aggregate.
16
+ *
17
+ * `Participant` is deliberately first in a member's `attachments[]`. It is the
18
+ * Gaia-X legal-entity projection corresponding to the same organization
19
+ * represented by the first schema.org `OrganizationCredential` in `vc[]`.
20
+ * They are different signed credentials and MUST NOT be treated as bytewise or
21
+ * schema-equivalent copies.
22
+ */
23
+ export declare const GaiaXCredentialAttachmentRole: Readonly<{
24
+ readonly Participant: "gaia-x-participant-vc-jwt";
25
+ readonly LegalRegistrationNumber: "gaia-x-legal-registration-number-vc-jwt";
26
+ readonly TermsAndConditions: "gaia-x-terms-and-conditions-vc-jwt";
27
+ readonly ServiceOffering: "gaia-x-service-offering-vc-jwt";
28
+ }>;
29
+ export type GaiaXCredentialAttachmentRoleValue = typeof GaiaXCredentialAttachmentRole[keyof typeof GaiaXCredentialAttachmentRole];
30
+ /** DIDComm attachment containing the compact, already-signed Gaia-X VC-JWT. */
31
+ export interface GaiaXVcJwtAttachment extends DidCommAttachment {
32
+ media_type: typeof GaiaXCredentialMediaType.VcJwt;
33
+ format: typeof GaiaXCredentialAttachmentFormat;
34
+ role: GaiaXCredentialAttachmentRoleValue;
35
+ data: {
36
+ json: {
37
+ jwt: string;
38
+ };
39
+ };
40
+ }
41
+ /** Cache metadata kept next to a resolved artifact, never inside that artifact. */
42
+ export interface IcaDiscoveryArtifactMeta {
43
+ sourceUrl?: string;
44
+ fetchedAt: string;
45
+ sourceUpdatedAt?: string;
46
+ verifiedAt?: string;
47
+ expiresAt?: string;
48
+ etag?: string;
49
+ contentHash?: string;
50
+ }
51
+ /** Resolved DID document plus cache provenance, following the old document/meta split. */
52
+ export interface IcaDiscoveredDid {
53
+ document: DidDocument;
54
+ meta: IcaDiscoveryArtifactMeta;
55
+ }
56
+ /** Resolved DCAT document plus cache provenance. */
57
+ export interface IcaDiscoveredDcat {
58
+ document: JsonObject;
59
+ meta: IcaDiscoveryArtifactMeta;
60
+ }
61
+ /**
62
+ * One authorized ICA member's complete discovery record.
63
+ *
64
+ * The VAT is intentionally not repeated at this level. Consumers obtain legal
65
+ * identifiers from the authoritative schema.org OrganizationCredential in
66
+ * `vc[0].credentialSubject.taxID` (or the versioned equivalent). `attachments`
67
+ * contains exact Gaia-X VC-JWT artifacts, while `did.document` and
68
+ * `dcat.document` make discovery possible without a second host request.
69
+ */
70
+ export interface IcaMemberDiscoveryData {
71
+ id: string;
72
+ vc: JsonObject[];
73
+ did: IcaDiscoveredDid;
74
+ attachments: GaiaXVcJwtAttachment[];
75
+ dcat?: IcaDiscoveredDcat;
76
+ meta?: {
77
+ assembledAt: string;
78
+ refreshAfter?: string;
79
+ };
80
+ }
81
+ /** DIDComm/JSON:API-compatible body returned by ICA member autodiscovery. */
82
+ export interface IcaMemberDiscoveryBody {
83
+ data: IcaMemberDiscoveryData[];
84
+ meta: {
85
+ generatedAt: string;
86
+ maxAgeSeconds?: number;
87
+ };
88
+ }
89
+ export interface GaiaXLegalPersonCredentialSubject extends JsonObject {
90
+ id: string;
91
+ type: 'gx:LegalPerson';
92
+ 'gx:legalName': string;
93
+ 'gx:legalRegistrationNumber': {
94
+ id: string;
95
+ };
96
+ 'gx:headquarterAddress': {
97
+ 'gx:countryCode': string;
98
+ };
99
+ 'gx:legalAddress': {
100
+ 'gx:countryCode': string;
101
+ };
102
+ 'gx:website'?: string;
103
+ }
104
+ export interface GaiaXServiceOfferingCredentialSubject extends JsonObject {
105
+ id: string;
106
+ type: 'gx:ServiceOffering';
107
+ 'gx:providedBy': {
108
+ id: string;
109
+ };
110
+ 'gx:serviceOfferingTermsAndConditions': Array<{
111
+ 'gx:url': string;
112
+ 'gx:hash': string;
113
+ }>;
114
+ 'gx:name'?: string;
115
+ 'gx:description'?: string;
116
+ 'gx:endpoint'?: Array<{
117
+ 'gx:endpointURL': string;
118
+ }>;
119
+ }
120
+ /** Unsigned VC Data Model 2.0 draft. Signing and VC-JWT encoding belong to the GW. */
121
+ export interface GaiaXCredentialDraft<TSubject extends JsonObject> extends JsonObject {
122
+ '@context': ['https://www.w3.org/ns/credentials/v2'];
123
+ type: ['VerifiableCredential', 'LegalPerson' | 'ServiceOffering'];
124
+ id: string;
125
+ issuer: string;
126
+ validFrom: string;
127
+ credentialSubject: TSubject;
128
+ }
@@ -0,0 +1,26 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ /** Versioned Gaia-X profile targeted by the schema.org semantic projection. */
3
+ export const GaiaXProfile = Object.freeze({
4
+ Icam2511: 'icam-25.11',
5
+ });
6
+ /** Media types used to transport signed Gaia-X credentials without decoding them. */
7
+ export const GaiaXCredentialMediaType = Object.freeze({
8
+ VcJwt: 'application/vc+jwt',
9
+ });
10
+ /** DIDComm attachment format identifier used by the existing ICA contract. */
11
+ export const GaiaXCredentialAttachmentFormat = 'vc+jwt';
12
+ /**
13
+ * Semantic roles for Gaia-X VC-JWT attachments in an ICA member aggregate.
14
+ *
15
+ * `Participant` is deliberately first in a member's `attachments[]`. It is the
16
+ * Gaia-X legal-entity projection corresponding to the same organization
17
+ * represented by the first schema.org `OrganizationCredential` in `vc[]`.
18
+ * They are different signed credentials and MUST NOT be treated as bytewise or
19
+ * schema-equivalent copies.
20
+ */
21
+ export const GaiaXCredentialAttachmentRole = Object.freeze({
22
+ Participant: 'gaia-x-participant-vc-jwt',
23
+ LegalRegistrationNumber: 'gaia-x-legal-registration-number-vc-jwt',
24
+ TermsAndConditions: 'gaia-x-terms-and-conditions-vc-jwt',
25
+ ServiceOffering: 'gaia-x-service-offering-vc-jwt',
26
+ });
@@ -21,6 +21,7 @@ export * from './dataspace-protocol';
21
21
  export * from './device-license';
22
22
  export * from './did';
23
23
  export * from './fhir-documents';
24
+ export * from './gaia-x';
24
25
  export * from './interoperable-claims';
25
26
  export * from './indexing';
26
27
  export * from './identity-bootstrap';
@@ -46,6 +47,7 @@ export * from './permission-templates';
46
47
  export * from './resource-document';
47
48
  export * from './relationship-access';
48
49
  export * from './response';
50
+ export * from './subject-identifier-ledger';
49
51
  export * from './urlPath';
50
52
  export * from './verifiable-credential';
51
53
  export * from './wallet';
@@ -21,6 +21,7 @@ export * from './dataspace-protocol.js';
21
21
  export * from './device-license.js';
22
22
  export * from './did.js';
23
23
  export * from './fhir-documents.js';
24
+ export * from './gaia-x.js';
24
25
  export * from './interoperable-claims.js';
25
26
  export * from './indexing.js';
26
27
  export * from './identity-bootstrap.js';
@@ -46,6 +47,7 @@ export * from './permission-templates.js';
46
47
  export * from './resource-document.js';
47
48
  export * from './relationship-access.js';
48
49
  export * from './response.js';
50
+ export * from './subject-identifier-ledger.js';
49
51
  export * from './urlPath.js';
50
52
  export * from './verifiable-credential.js';
51
53
  export * from './wallet.js';
@@ -16,6 +16,12 @@ export interface ProtectedHeadersJWE {
16
16
  kid?: string;
17
17
  skid?: string;
18
18
  zip?: string;
19
+ /** Versioned private profile while the JOSE ML-KEM binding remains an IETF draft. */
20
+ gdc_pq_profile?: 'confidential-pqc-v1';
21
+ /** Domain-separated purpose for storage envelopes. */
22
+ gdc_key_purpose?: 'document-at-rest';
23
+ /** Non-reversible binding to the profile/tenant storage owner. */
24
+ gdc_owner_binding?: string;
19
25
  }
20
26
  /**
21
27
  * Unprotected headers that are not integrity protected.
@@ -33,6 +39,25 @@ export interface RecipientDataJWE {
33
39
  kid: string;
34
40
  };
35
41
  }
42
+ /**
43
+ * Opaque recipient payload carried by JWE `encrypted_key` for
44
+ * `confidential-pqc-v1`.
45
+ *
46
+ * The content is encrypted once with a random AES-256-GCM CEK. ML-KEM derives
47
+ * a recipient KEK, and that KEK protects the CEK with AES-256-GCM. Keeping the
48
+ * CEK distinct from the KEM shared secret makes a future General JWE
49
+ * multi-recipient profile possible without re-encrypting the document.
50
+ */
51
+ export interface MlKemWrappedCekV1 {
52
+ v: 'gdc-mlkem-cek-wrap-v1';
53
+ kem: 'ML-KEM-768';
54
+ kdf: 'HKDF-SHA-256';
55
+ wrap: 'A256GCM';
56
+ kemCiphertext: string;
57
+ iv: string;
58
+ ciphertext: string;
59
+ tag: string;
60
+ }
36
61
  /**
37
62
  * Represents the protected (integrity-protected) header of a JWE.
38
63
  * These parameters are combined with the AAD (Additional Authenticated Data)
@@ -0,0 +1,16 @@
1
+ /** Public provider pointer stored for one opaque subject-identifier asset. */
2
+ export type SubjectIdentifierProviderPointer = Readonly<{
3
+ identifier: Readonly<{
4
+ value: string;
5
+ }>;
6
+ /** Bare DNS domain: no URL scheme, path, DID or tenant identifier. */
7
+ url: string;
8
+ }>;
9
+ /** Ledger payload. It deliberately contains no subject DID or raw identifier. */
10
+ export type SubjectIdentifierLedgerPayload = Readonly<{
11
+ provider: SubjectIdentifierProviderPointer;
12
+ }>;
13
+ /** One individual/animal bundle entry expanded into one write per `sameAs` alias. */
14
+ export type SubjectIdentifierLedgerBundleEntry = Readonly<{
15
+ sameAs: string | readonly string[];
16
+ }>;
@@ -0,0 +1 @@
1
+ export {};
@@ -50,6 +50,15 @@ export declare class BundleEditor {
50
50
  setBundleType(type: BundleType): this;
51
51
  /** Returns the declared target bundle shape. */
52
52
  getBundleType(): BundleType;
53
+ /**
54
+ * Replaces staged entries with one previously materialized JSON-API-like
55
+ * Bundle snapshot so a later UI session can reopen, edit or append entries.
56
+ *
57
+ * This is an in-memory authoring operation only. It does not imply that the
58
+ * supplied Bundle came from authoritative GW readback and it performs no
59
+ * Communication wrapping, submission or polling.
60
+ */
61
+ setBundle(bundle: BundleJsonApi<BundleEntry>): this;
53
62
  setCompositionIdentifier(identifier?: string | null): this;
54
63
  /** Returns the normalized `Composition.identifier` staged for the future document root. */
55
64
  getCompositionIdentifier(): string | undefined;
@@ -77,6 +77,52 @@ export class BundleEditor {
77
77
  getBundleType() {
78
78
  return this.bundleType;
79
79
  }
80
+ /**
81
+ * Replaces staged entries with one previously materialized JSON-API-like
82
+ * Bundle snapshot so a later UI session can reopen, edit or append entries.
83
+ *
84
+ * This is an in-memory authoring operation only. It does not imply that the
85
+ * supplied Bundle came from authoritative GW readback and it performs no
86
+ * Communication wrapping, submission or polling.
87
+ */
88
+ setBundle(bundle) {
89
+ if (!bundle || bundle.resourceType !== ResourceTypesFhirR4.Bundle || !Array.isArray(bundle.data)) {
90
+ throw new TypeError('BundleEditor.setBundle requires a Bundle with data[].');
91
+ }
92
+ if (!Object.values(BundleTypes).includes(bundle.type)) {
93
+ throw new TypeError(`BundleEditor.setBundle does not support Bundle.type: ${String(bundle.type || '')}`);
94
+ }
95
+ const operation = this.requireBundleOperation();
96
+ const nextEntries = bundle.data.map((source, index) => {
97
+ const resource = source?.resource;
98
+ const resourceType = normalizeOptionalIdentifier(resource?.resourceType);
99
+ if (!resource || !resourceType) {
100
+ throw new TypeError(`BundleEditor.setBundle requires data[${index}].resource.resourceType.`);
101
+ }
102
+ if (bundle.type !== BundleTypes.document
103
+ && this.allowedResourceType
104
+ && resourceType !== this.allowedResourceType) {
105
+ throw new Error(`BundleEditor cannot mix resource types in ${bundle.type} mode: ${this.allowedResourceType} vs ${resourceType}`);
106
+ }
107
+ const cloned = cloneEntry(source);
108
+ return {
109
+ type: normalizeOptionalIdentifier(cloned.type) || inferGenericEntryType(resourceType, operation),
110
+ request: cloned.request || { method: resolveRequestMethodForOperation(operation) },
111
+ ...(cloned.fullUrl ? { fullUrl: cloned.fullUrl } : {}),
112
+ resource: {
113
+ ...cloned.resource,
114
+ resourceType,
115
+ meta: {
116
+ ...(cloned.resource?.meta || {}),
117
+ claims: { ...(cloned.resource?.meta?.claims || {}) },
118
+ },
119
+ },
120
+ };
121
+ });
122
+ this.bundleType = bundle.type;
123
+ this.entries.splice(0, this.entries.length, ...nextEntries);
124
+ return this;
125
+ }
80
126
  setCompositionIdentifier(identifier) {
81
127
  return this.setCompositionScalarClaim(CompositionClaim.Identifier, identifier);
82
128
  }
@@ -19,3 +19,5 @@ export * from './immunization-entry-editor';
19
19
  export * from './procedure-entry-editor';
20
20
  export * from './diagnostic-report-entry-editor';
21
21
  export * from './employee-entry-editor';
22
+ export * from './consent-entry-editor';
23
+ export * from './related-person-entry-editor';
@@ -19,3 +19,5 @@ export * from './immunization-entry-editor.js';
19
19
  export * from './procedure-entry-editor.js';
20
20
  export * from './diagnostic-report-entry-editor.js';
21
21
  export * from './employee-entry-editor.js';
22
+ export * from './consent-entry-editor.js';
23
+ export * from './related-person-entry-editor.js';
@@ -17,6 +17,8 @@ import type { MedicationStatementEntryEditor } from './medication-statement-entr
17
17
  import type { ObservationEntryEditor } from './observation-entry-editor';
18
18
  import type { ProcedureEntryEditor } from './procedure-entry-editor';
19
19
  import type { VitalSignEntryEditor } from './vital-sign-entry-editor';
20
+ import type { ConsentEntryEditor } from './consent-entry-editor';
21
+ import type { RelatedPersonEntryEditor } from './related-person-entry-editor';
20
22
  import { type EmployeeClaims } from './employee';
21
23
  export declare class BundleEntryEditor {
22
24
  protected readonly bundleEditor: BundleEditor;
@@ -31,6 +33,10 @@ export declare class BundleEntryEditor {
31
33
  asResourceType<T extends AllowedResourceType>(resourceType: T): ResourceTypeEntryEditor<T>;
32
34
  /** Opens the current entry as one employee-specific resource editor. */
33
35
  asEmployee(): EmployeeEntryEditor;
36
+ /** Opens the current entry as one Consent permission editor. */
37
+ asConsent(): ConsentEntryEditor;
38
+ /** Opens the current entry as one RelatedPerson/contact editor. */
39
+ asRelatedPerson(): RelatedPersonEntryEditor;
34
40
  /** Opens the current entry as one vital-sign-specific Observation editor. */
35
41
  asVitalSign(): VitalSignEntryEditor;
36
42
  /** Opens the current entry as one general Observation editor. */
@@ -27,6 +27,10 @@ export class BundleEntryEditor {
27
27
  switch (normalized) {
28
28
  case BundleEditableResourceTypes.employee:
29
29
  return this.asEmployee();
30
+ case BundleEditableResourceTypes.consent:
31
+ return this.asConsent();
32
+ case BundleEditableResourceTypes.relatedPerson:
33
+ return this.asRelatedPerson();
30
34
  case BundleEditableResourceTypes.vitalSign:
31
35
  return this.asVitalSign();
32
36
  case BundleEditableResourceTypes.observation:
@@ -71,6 +75,22 @@ export class BundleEntryEditor {
71
75
  }
72
76
  return createRegisteredBundleEntryEditor(BundleEditableResourceTypes.employee, this.bundleEditor, this.entryIndex);
73
77
  }
78
+ /** Opens the current entry as one Consent permission editor. */
79
+ asConsent() {
80
+ const entry = this.getMutableEntry();
81
+ if (entry.resource?.resourceType !== ResourceTypesFhirR4.Consent) {
82
+ throw new Error(`BundleEntryEditor cannot open this entry as Consent: ${String(entry.resource?.resourceType || '')}`);
83
+ }
84
+ return createRegisteredBundleEntryEditor(BundleEditableResourceTypes.consent, this.bundleEditor, this.entryIndex);
85
+ }
86
+ /** Opens the current entry as one RelatedPerson/contact editor. */
87
+ asRelatedPerson() {
88
+ const entry = this.getMutableEntry();
89
+ if (entry.resource?.resourceType !== ResourceTypesFhirR4.RelatedPerson) {
90
+ throw new Error(`BundleEntryEditor cannot open this entry as RelatedPerson: ${String(entry.resource?.resourceType || '')}`);
91
+ }
92
+ return createRegisteredBundleEntryEditor(BundleEditableResourceTypes.relatedPerson, this.bundleEditor, this.entryIndex);
93
+ }
74
94
  /** Opens the current entry as one vital-sign-specific Observation editor. */
75
95
  asVitalSign() {
76
96
  const entry = this.getMutableEntry();