gdc-common-utils-ts 2.3.0 → 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 (53) 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/examples/shared.d.ts +1 -1
  15. package/dist/examples/shared.js +1 -1
  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 +132 -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/interoperable-claims/invoice-claims.d.ts +1 -1
  23. package/dist/models/interoperable-claims/invoice-claims.js +2 -2
  24. package/dist/models/interoperable-claims/observation-claims.d.ts +2 -2
  25. package/dist/models/interoperable-claims/observation-claims.js +2 -2
  26. package/dist/models/jwe.d.ts +25 -0
  27. package/dist/models/subject-identifier-ledger.d.ts +16 -0
  28. package/dist/models/subject-identifier-ledger.js +1 -0
  29. package/dist/utils/bundle-editor-core.d.ts +9 -0
  30. package/dist/utils/bundle-editor-core.js +46 -0
  31. package/dist/utils/bundle-editor.d.ts +2 -0
  32. package/dist/utils/bundle-editor.js +2 -0
  33. package/dist/utils/bundle-entry-editor.d.ts +6 -0
  34. package/dist/utils/bundle-entry-editor.js +20 -0
  35. package/dist/utils/communication-consent-access-editor.d.ts +14 -1
  36. package/dist/utils/communication-consent-access-editor.js +27 -0
  37. package/dist/utils/consent-entry-editor.d.ts +42 -0
  38. package/dist/utils/consent-entry-editor.js +80 -0
  39. package/dist/utils/did.d.ts +2 -2
  40. package/dist/utils/did.js +3 -3
  41. package/dist/utils/index.d.ts +1 -0
  42. package/dist/utils/index.js +1 -0
  43. package/dist/utils/individual-identifier.d.ts +14 -0
  44. package/dist/utils/individual-identifier.js +39 -0
  45. package/dist/utils/multibasehash.d.ts +19 -5
  46. package/dist/utils/multibasehash.js +35 -10
  47. package/dist/utils/multiformat-profile.d.ts +4 -2
  48. package/dist/utils/multiformat-profile.js +5 -3
  49. package/dist/utils/related-person-entry-editor.d.ts +32 -0
  50. package/dist/utils/related-person-entry-editor.js +97 -0
  51. package/dist/utils/same-as.d.ts +10 -0
  52. package/dist/utils/same-as.js +28 -3
  53. package/package.json +2 -1
@@ -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();
@@ -1,6 +1,6 @@
1
1
  import { type HealthcareActorRoleDescriptor, type HealthcareSectionDescriptor } from '../constants/healthcare';
2
2
  import type { DataspaceSector } from '../constants/sectors';
3
- import type { CommunicationAttachedBundleSessionOptions, ConsentEditorClassifiedActors, ConsentEditorClassifiedPurpose, ConsentEditorClassifiedRoles, ConsentEditorClassifiedTarget, ConsentViewModel } from '../models/communication-attached-bundle-session';
3
+ import type { ActiveEntrySelection, CommunicationAttachedBundleSessionOptions, ConsentEditorClassifiedActors, ConsentEditorClassifiedPurpose, ConsentEditorClassifiedRoles, ConsentEditorClassifiedTarget, ConsentViewModel, UpsertClaimsResourceEntryInput } from '../models/communication-attached-bundle-session';
4
4
  import type { ConsentDuplicateRuleConflict } from './consent-duplicate-rules';
5
5
  import { CommunicationAttachedBundleSession } from './communication-attached-bundle-session';
6
6
  /**
@@ -15,6 +15,19 @@ import { CommunicationAttachedBundleSession } from './communication-attached-bun
15
15
  * by the existing access-rule contract.
16
16
  */
17
17
  export declare class ConsentAccessEditor extends CommunicationAttachedBundleSession {
18
+ /**
19
+ * Adds one authored Consent to the in-memory permission Bundle and selects it.
20
+ *
21
+ * Prefer this semantic name in app code and tutorials. Saving, attaching the
22
+ * completed Bundle to a Communication and transporting it are later steps.
23
+ */
24
+ addConsent(input: UpsertClaimsResourceEntryInput<Record<string, unknown>>): this;
25
+ /** Reopens one Consent already present in the in-memory permission Bundle. */
26
+ openConsent(selection: ActiveEntrySelection): this;
27
+ /** Saves the active Consent and closes its editing selection. */
28
+ saveConsent(): this;
29
+ /** @deprecated Internal compatibility plumbing. Use `addConsent(...)`. */
30
+ upsertActiveConsentEntry(input: UpsertClaimsResourceEntryInput<Record<string, unknown>>): this;
18
31
  /** Returns duplicate atomic consent-rule conflicts across the current bundle. */
19
32
  getConsentRuleDuplicateConflicts(): ConsentDuplicateRuleConflict[];
20
33
  /** Returns duplicate atomic consent-rule conflicts affecting the active Consent entry. */
@@ -18,6 +18,33 @@ import { asTrimmedString, buildClassifiedConsentTarget, buildConsentViewModel, b
18
18
  * by the existing access-rule contract.
19
19
  */
20
20
  export class ConsentAccessEditor extends CommunicationAttachedBundleSession {
21
+ /**
22
+ * Adds one authored Consent to the in-memory permission Bundle and selects it.
23
+ *
24
+ * Prefer this semantic name in app code and tutorials. Saving, attaching the
25
+ * completed Bundle to a Communication and transporting it are later steps.
26
+ */
27
+ addConsent(input) {
28
+ return super.upsertActiveConsentEntry(input);
29
+ }
30
+ /** Reopens one Consent already present in the in-memory permission Bundle. */
31
+ openConsent(selection) {
32
+ this.selectActiveEntry(selection);
33
+ const resourceType = this.getActiveEntry()?.resource?.resourceType;
34
+ if (resourceType !== ResourceTypesFhirR4.Consent) {
35
+ this.clearActiveEntry();
36
+ throw new Error(`ConsentAccessEditor cannot open resource type: ${String(resourceType || '')}`);
37
+ }
38
+ return this;
39
+ }
40
+ /** Saves the active Consent and closes its editing selection. */
41
+ saveConsent() {
42
+ return this.saveAndReleaseActiveEntry();
43
+ }
44
+ /** @deprecated Internal compatibility plumbing. Use `addConsent(...)`. */
45
+ upsertActiveConsentEntry(input) {
46
+ return this.addConsent(input);
47
+ }
21
48
  /** Returns duplicate atomic consent-rule conflicts across the current bundle. */
22
49
  getConsentRuleDuplicateConflicts() {
23
50
  return detectDuplicateConsentRuleConflicts(this.getBundleInMemory().data);
@@ -0,0 +1,42 @@
1
+ /**
2
+ * File discipline note:
3
+ * - Read `ARCHITECTURE.md` and `CONTRIBUTING.md` before changing this module.
4
+ * - This file owns only the typed editor for one staged Consent entry.
5
+ */
6
+ import { type ConsentDecision } from '../models/consent-rule';
7
+ import { BundleEntryEditor } from './bundle-entry-editor';
8
+ /**
9
+ * Typed editor for one Consent permission staged inside a Bundle.
10
+ *
11
+ * It edits semantic permission data only. It does not create a Communication,
12
+ * choose a clinical projection, pack DIDComm, authorize, submit or poll.
13
+ */
14
+ export declare class ConsentEntryEditor extends BundleEntryEditor {
15
+ setIdentifier(value?: string | null): this;
16
+ getIdentifier(): string | undefined;
17
+ ensureIdentifier(): string;
18
+ setSubject(value?: string | null): this;
19
+ getSubject(): string | undefined;
20
+ setDecision(value?: ConsentDecision | null): this;
21
+ getDecision(): string | undefined;
22
+ setActorIdentifierList(values: readonly string[]): this;
23
+ getActorIdentifierList(): string[];
24
+ setActorRoleList(values: readonly string[]): this;
25
+ getActorRoleList(): string[];
26
+ setPurposeList(values: readonly string[]): this;
27
+ getPurposeList(): string[];
28
+ setSectionList(values: readonly string[]): this;
29
+ getSectionList(): string[];
30
+ setResourceTypeList(values: readonly string[]): this;
31
+ getResourceTypeList(): string[];
32
+ setDate(value?: string | null): this;
33
+ getDate(): string | undefined;
34
+ setPeriodStart(value?: string | null): this;
35
+ getPeriodStart(): string | undefined;
36
+ setPeriodEnd(value?: string | null): this;
37
+ getPeriodEnd(): string | undefined;
38
+ private setOptionalText;
39
+ private getOptionalText;
40
+ private setList;
41
+ private getList;
42
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * File discipline note:
3
+ * - Read `ARCHITECTURE.md` and `CONTRIBUTING.md` before changing this module.
4
+ * - This file owns only the typed editor for one staged Consent entry.
5
+ */
6
+ import { ClaimConsent } from '../models/consent-rule.js';
7
+ import { BundleEditableResourceTypes } from '../models/bundle-editor-types.js';
8
+ import { BundleEntryEditor } from './bundle-entry-editor.js';
9
+ import { registerBundleEntryEditor } from './bundle-editor-registry.js';
10
+ function normalizeText(value) {
11
+ const normalized = String(value || '').trim();
12
+ return normalized || undefined;
13
+ }
14
+ function normalizeList(values) {
15
+ return [...new Set(values.map((value) => String(value || '').trim()).filter(Boolean))];
16
+ }
17
+ /**
18
+ * Typed editor for one Consent permission staged inside a Bundle.
19
+ *
20
+ * It edits semantic permission data only. It does not create a Communication,
21
+ * choose a clinical projection, pack DIDComm, authorize, submit or poll.
22
+ */
23
+ export class ConsentEntryEditor extends BundleEntryEditor {
24
+ setIdentifier(value) {
25
+ const normalized = normalizeText(value);
26
+ if (!normalized) {
27
+ this.removeClaim(ClaimConsent.identifier).setResourceId().setFullUrl();
28
+ return this;
29
+ }
30
+ return this.setClaim(ClaimConsent.identifier, normalized)
31
+ .setResourceId(normalized)
32
+ .setFullUrl(normalized);
33
+ }
34
+ getIdentifier() {
35
+ return normalizeText(String(this.getClaim(ClaimConsent.identifier) || this.getResourceId() || this.getFullUrl() || ''));
36
+ }
37
+ ensureIdentifier() {
38
+ const identifier = this.getIdentifier();
39
+ if (!identifier)
40
+ throw new Error('Consent entry requires a generated resource id or explicit identifier.');
41
+ this.setIdentifier(identifier);
42
+ return identifier;
43
+ }
44
+ setSubject(value) { return this.setOptionalText(ClaimConsent.subject, value); }
45
+ getSubject() { return this.getOptionalText(ClaimConsent.subject); }
46
+ setDecision(value) { return this.setOptionalText(ClaimConsent.decision, value); }
47
+ getDecision() { return this.getOptionalText(ClaimConsent.decision); }
48
+ setActorIdentifierList(values) { return this.setList(ClaimConsent.actorIdentifier, values); }
49
+ getActorIdentifierList() { return this.getList(ClaimConsent.actorIdentifier); }
50
+ setActorRoleList(values) { return this.setList(ClaimConsent.actorRole, values); }
51
+ getActorRoleList() { return this.getList(ClaimConsent.actorRole); }
52
+ setPurposeList(values) { return this.setList(ClaimConsent.purpose, values); }
53
+ getPurposeList() { return this.getList(ClaimConsent.purpose); }
54
+ setSectionList(values) { return this.setList(ClaimConsent.action, values); }
55
+ getSectionList() { return this.getList(ClaimConsent.action); }
56
+ setResourceTypeList(values) { return this.setList(ClaimConsent.resourceType, values); }
57
+ getResourceTypeList() { return this.getList(ClaimConsent.resourceType); }
58
+ setDate(value) { return this.setOptionalText(ClaimConsent.date, value); }
59
+ getDate() { return this.getOptionalText(ClaimConsent.date); }
60
+ setPeriodStart(value) { return this.setOptionalText(ClaimConsent.periodStart, value); }
61
+ getPeriodStart() { return this.getOptionalText(ClaimConsent.periodStart); }
62
+ setPeriodEnd(value) { return this.setOptionalText(ClaimConsent.periodEnd, value); }
63
+ getPeriodEnd() { return this.getOptionalText(ClaimConsent.periodEnd); }
64
+ setOptionalText(key, value) {
65
+ const normalized = normalizeText(value);
66
+ return normalized ? this.setClaim(key, normalized) : this.removeClaim(key);
67
+ }
68
+ getOptionalText(key) {
69
+ return normalizeText(String(this.getClaim(key) || ''));
70
+ }
71
+ setList(key, values) {
72
+ const normalized = normalizeList(values);
73
+ return normalized.length ? this.setClaim(key, normalized.join(',')) : this.removeClaim(key);
74
+ }
75
+ getList(key) {
76
+ const value = this.getClaim(key);
77
+ return normalizeList(Array.isArray(value) ? value.map(String) : String(value || '').split(','));
78
+ }
79
+ }
80
+ registerBundleEntryEditor(BundleEditableResourceTypes.consent, ConsentEntryEditor);
@@ -71,7 +71,7 @@ export declare function extractTenantIdFromHostedDidWeb(did: string): string | u
71
71
  * Builds the canonical hosted provider DID root used by hosted tenant services.
72
72
  *
73
73
  * Canonical form:
74
- * `did:web:<host.domain>:<sector>;organization:taxid:<provider-tax-id>`
74
+ * `did:web:<host.domain>:<sector>:organization:taxid:<provider-tax-id>`
75
75
  *
76
76
  * This is the provider-scoped DID root under which hosted individual and member
77
77
  * identities are published. Downstream individual DIDs extend this root with
@@ -167,7 +167,7 @@ export declare function buildProfessionalDidWeb(input: {
167
167
  *
168
168
  * Canonical supported forms:
169
169
  * - hosted:
170
- * `did:web:<host.domain>:<sector>;organization:taxid:<provider-tax-id>:individual:multibase:<individualId>`
170
+ * `did:web:<host.domain>:<sector>:organization:taxid:<provider-tax-id>:individual:multibase:<individualId>`
171
171
  * - external/provider-domain:
172
172
  * `did:web:<sector.provider.domain>:individual:multibase:<individualId>`
173
173
  *
package/dist/utils/did.js CHANGED
@@ -137,7 +137,7 @@ export function extractTenantIdFromHostedDidWeb(did) {
137
137
  * Builds the canonical hosted provider DID root used by hosted tenant services.
138
138
  *
139
139
  * Canonical form:
140
- * `did:web:<host.domain>:<sector>;organization:taxid:<provider-tax-id>`
140
+ * `did:web:<host.domain>:<sector>:organization:taxid:<provider-tax-id>`
141
141
  *
142
142
  * This is the provider-scoped DID root under which hosted individual and member
143
143
  * identities are published. Downstream individual DIDs extend this root with
@@ -153,7 +153,7 @@ export function buildHostedProviderDidWeb(input) {
153
153
  throw new Error('buildHostedProviderDidWeb requires sector.');
154
154
  if (!providerTaxId)
155
155
  throw new Error('buildHostedProviderDidWeb requires providerTaxId.');
156
- return `did:web:${hostDomain}:${sector};organization:taxid:${providerTaxId}`;
156
+ return `did:web:${hostDomain}:${sector}:organization:taxid:${providerTaxId}`;
157
157
  }
158
158
  /**
159
159
  * Builds the canonical public provider DID root used by an external provider domain.
@@ -269,7 +269,7 @@ export function buildProfessionalDidWeb(input) {
269
269
  *
270
270
  * Canonical supported forms:
271
271
  * - hosted:
272
- * `did:web:<host.domain>:<sector>;organization:taxid:<provider-tax-id>:individual:multibase:<individualId>`
272
+ * `did:web:<host.domain>:<sector>:organization:taxid:<provider-tax-id>:individual:multibase:<individualId>`
273
273
  * - external/provider-domain:
274
274
  * `did:web:<sector.provider.domain>:individual:multibase:<individualId>`
275
275
  *
@@ -59,6 +59,7 @@ export * from './clinical-resource-view';
59
59
  export * from './fhir-validator';
60
60
  export * from './family-registration-test-data';
61
61
  export * from './individual-form-pdf';
62
+ export * from './individual-identifier';
62
63
  export * from './individual-smart';
63
64
  export * from './individual-organization-claims';
64
65
  export * from './inter-tenant-access-contract';
@@ -59,6 +59,7 @@ export * from './clinical-resource-view.js';
59
59
  export * from './fhir-validator.js';
60
60
  export * from './family-registration-test-data.js';
61
61
  export * from './individual-form-pdf.js';
62
+ export * from './individual-identifier.js';
62
63
  export * from './individual-smart.js';
63
64
  export * from './individual-organization-claims.js';
64
65
  export * from './inter-tenant-access-contract.js';
@@ -0,0 +1,14 @@
1
+ import { type Hl7V20203IdentifierCode, type IdKindValue } from '../constants/identity-identifiers.js';
2
+ export type IndividualIdentifierInput = Readonly<{
3
+ type: Hl7V20203IdentifierCode | IdKindValue | string;
4
+ jurisdiction: string;
5
+ value: string;
6
+ }>;
7
+ /** Resolves a supported short or reverse-DNS HL7 identifier type to one canonical type. */
8
+ export declare function normalizeIndividualIdentifierType(type: IndividualIdentifierInput['type']): IdKindValue;
9
+ /** Builds the sole canonical input hashed for an external individual identifier alias. */
10
+ export declare function buildIndividualIdentifierToken(input: IndividualIdentifierInput): string;
11
+ /** Returns the deterministic CIDv1/SHA3-384 alias stored in `Organization.sameAs`. */
12
+ export declare function buildIndividualIdentifierCid(input: IndividualIdentifierInput): string;
13
+ /** Returns the SHA3-384 multihash URN used as a ledger lookup asset id. */
14
+ export declare function buildIndividualIdentifierLedgerAssetId(input: IndividualIdentifierInput): string;
@@ -0,0 +1,39 @@
1
+ import { HL7_V2_0203_REVERSE_DNS_PREFIX, HL7_V2_0203_REVERSE_DNS_TYPES, } from '../constants/identity-identifiers.js';
2
+ import { buildRawCidV1FromUtf8String } from './multiformat-profile.js';
3
+ import { encodeMultibaseSha3 } from './multibasehash.js';
4
+ import { UrnPrefixes } from '../constants/urn.js';
5
+ const LEGACY_HL7_V2_0203_REVERSE_DNS_PREFIX = 'org.hl7.terminology.codesystem.v2-0203';
6
+ const ISO_3166_JURISDICTION_PATTERN = /^[A-Z]{2}(?:-[A-Z0-9]{1,3})?$/;
7
+ /** Resolves a supported short or reverse-DNS HL7 identifier type to one canonical type. */
8
+ export function normalizeIndividualIdentifierType(type) {
9
+ const candidate = String(type).trim();
10
+ const shortCode = candidate.startsWith(`${HL7_V2_0203_REVERSE_DNS_PREFIX}.`)
11
+ ? candidate.slice(HL7_V2_0203_REVERSE_DNS_PREFIX.length + 1)
12
+ : candidate.startsWith(`${LEGACY_HL7_V2_0203_REVERSE_DNS_PREFIX}.`)
13
+ ? candidate.slice(LEGACY_HL7_V2_0203_REVERSE_DNS_PREFIX.length + 1)
14
+ : candidate;
15
+ const canonical = HL7_V2_0203_REVERSE_DNS_TYPES[shortCode];
16
+ if (!canonical)
17
+ throw new Error(`Unsupported individual identifier type: ${candidate}`);
18
+ return canonical;
19
+ }
20
+ /** Builds the sole canonical input hashed for an external individual identifier alias. */
21
+ export function buildIndividualIdentifierToken(input) {
22
+ const type = normalizeIndividualIdentifierType(input.type);
23
+ const jurisdiction = input.jurisdiction.trim().toUpperCase();
24
+ const value = input.value.trim().normalize('NFKC').toUpperCase();
25
+ if (!ISO_3166_JURISDICTION_PATTERN.test(jurisdiction)) {
26
+ throw new Error(`Invalid ISO 3166 jurisdiction: ${input.jurisdiction}`);
27
+ }
28
+ if (!value)
29
+ throw new Error('Individual identifier value is required');
30
+ return `${type}|${jurisdiction}|${value}`;
31
+ }
32
+ /** Returns the deterministic CIDv1/SHA3-384 alias stored in `Organization.sameAs`. */
33
+ export function buildIndividualIdentifierCid(input) {
34
+ return buildRawCidV1FromUtf8String(buildIndividualIdentifierToken(input));
35
+ }
36
+ /** Returns the SHA3-384 multihash URN used as a ledger lookup asset id. */
37
+ export function buildIndividualIdentifierLedgerAssetId(input) {
38
+ return `${UrnPrefixes.Multibase}${encodeMultibaseSha3(buildIndividualIdentifierToken(input))}`;
39
+ }
@@ -1,8 +1,22 @@
1
+ export type Sha3DigestBits = 224 | 256 | 384 | 512;
1
2
  /**
2
- * Encodes input into multibase(base58btc(multihash(sha384))).
3
- * - Multihash prefix: 0x15 0x30
4
- * - 0x15: code for SHA-384
5
- * - 0x30: length of SHA-384 = 48 bytes
6
- * - Multibase prefix: 'z'
3
+ * Encodes bytes as `multibase(base58btc(multihash(SHA3-n)))`.
4
+ *
5
+ * Contract:
6
+ * - input strings are hashed as their exact UTF-8 bytes; callers own any
7
+ * higher-level identifier or JSON canonicalization
8
+ * - SHA3-384 is the default profile
9
+ * - the multihash stores the canonical SHA3 multicodec code and digest length
10
+ * - the returned `z...` value is a multibase string, not a CID
11
+ *
12
+ * Use a CID builder when identifying a content-addressed record. Use this
13
+ * helper when the multihash itself is the stable identifier or index key.
14
+ */
15
+ export declare function encodeMultibaseSha3(input: string | Uint8Array, digestBits?: Sha3DigestBits): string;
16
+ /**
17
+ * @deprecated Use `encodeMultibaseSha3(input)` or pass `384` explicitly.
18
+ *
19
+ * This compatibility name now follows its historically intended multihash
20
+ * profile: SHA3-384, not SHA-2 SHA-384 with a mismatched SHA3 prefix.
7
21
  */
8
22
  export declare function encodeMultibaseSha384(input: string | Uint8Array): string;
@@ -1,23 +1,48 @@
1
1
  // Copyright 2025 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  // Use explicit .js subpaths to satisfy package exports in Metro/Node ESM.
3
- import { sha384 } from '@noble/hashes/sha2.js';
3
+ import { sha3_224, sha3_256, sha3_384, sha3_512 } from '@noble/hashes/sha3.js';
4
4
  import { utf8ToBytes } from '@noble/hashes/utils.js';
5
5
  import baseX from 'base-x';
6
6
  const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
7
7
  const base58btc = baseX(BASE58_ALPHABET);
8
+ const SHA3_PROFILES = Object.freeze({
9
+ 224: Object.freeze({ code: 0x17, digestLengthBytes: 28, digest: sha3_224 }),
10
+ 256: Object.freeze({ code: 0x16, digestLengthBytes: 32, digest: sha3_256 }),
11
+ 384: Object.freeze({ code: 0x15, digestLengthBytes: 48, digest: sha3_384 }),
12
+ 512: Object.freeze({ code: 0x14, digestLengthBytes: 64, digest: sha3_512 }),
13
+ });
8
14
  /**
9
- * Encodes input into multibase(base58btc(multihash(sha384))).
10
- * - Multihash prefix: 0x15 0x30
11
- * - 0x15: code for SHA-384
12
- * - 0x30: length of SHA-384 = 48 bytes
13
- * - Multibase prefix: 'z'
15
+ * Encodes bytes as `multibase(base58btc(multihash(SHA3-n)))`.
16
+ *
17
+ * Contract:
18
+ * - input strings are hashed as their exact UTF-8 bytes; callers own any
19
+ * higher-level identifier or JSON canonicalization
20
+ * - SHA3-384 is the default profile
21
+ * - the multihash stores the canonical SHA3 multicodec code and digest length
22
+ * - the returned `z...` value is a multibase string, not a CID
23
+ *
24
+ * Use a CID builder when identifying a content-addressed record. Use this
25
+ * helper when the multihash itself is the stable identifier or index key.
14
26
  */
15
- export function encodeMultibaseSha384(input) {
27
+ export function encodeMultibaseSha3(input, digestBits = 384) {
28
+ const profile = SHA3_PROFILES[digestBits];
29
+ if (!profile) {
30
+ throw new Error(`Unsupported SHA3 digest size: ${String(digestBits)}`);
31
+ }
16
32
  const bytes = typeof input === 'string' ? utf8ToBytes(input) : input;
17
- const hashBytes = sha384(bytes);
33
+ const hashBytes = profile.digest(bytes);
18
34
  const multihashBytes = new Uint8Array(2 + hashBytes.length);
19
- multihashBytes[0] = 0x15; // SHA-384 code
20
- multihashBytes[1] = 0x30; // length = 48
35
+ multihashBytes[0] = profile.code;
36
+ multihashBytes[1] = profile.digestLengthBytes;
21
37
  multihashBytes.set(hashBytes, 2);
22
38
  return 'z' + base58btc.encode(multihashBytes);
23
39
  }
40
+ /**
41
+ * @deprecated Use `encodeMultibaseSha3(input)` or pass `384` explicitly.
42
+ *
43
+ * This compatibility name now follows its historically intended multihash
44
+ * profile: SHA3-384, not SHA-2 SHA-384 with a mismatched SHA3 prefix.
45
+ */
46
+ export function encodeMultibaseSha384(input) {
47
+ return encodeMultibaseSha3(input, 384);
48
+ }
@@ -38,9 +38,11 @@ export declare const SHA3_256_MULTIHASH_PROFILE: MultihashProfile;
38
38
  */
39
39
  export declare const SHA3_384_MULTIHASH_PROFILE: MultihashProfile;
40
40
  /**
41
- * Builds a `CIDv1` over a UTF-8 string using the provided multihash profile.
41
+ * Builds a `CIDv1` over a UTF-8 string using SHA3-384 by default.
42
42
  *
43
43
  * This helper intentionally does not canonicalize the input string for you.
44
44
  * Callers must pass the final canonical logical identifier they want to anchor.
45
+ * A caller may supply another explicit multihash profile when an established
46
+ * external contract requires it.
45
47
  */
46
- export declare function buildRawCidV1FromUtf8String(value: string, profile: MultihashProfile): string;
48
+ export declare function buildRawCidV1FromUtf8String(value: string, profile?: MultihashProfile): string;
@@ -19,7 +19,7 @@ export const MULTICODEC_RAW_CODE = 0x55;
19
19
  */
20
20
  export const SHA3_256_MULTIHASH_PROFILE = Object.freeze({
21
21
  algorithm: 'sha3-256',
22
- code: 0x14,
22
+ code: 0x16,
23
23
  digestLengthBytes: 32,
24
24
  digest: sha3_256,
25
25
  });
@@ -36,12 +36,14 @@ export const SHA3_384_MULTIHASH_PROFILE = Object.freeze({
36
36
  digest: sha3_384,
37
37
  });
38
38
  /**
39
- * Builds a `CIDv1` over a UTF-8 string using the provided multihash profile.
39
+ * Builds a `CIDv1` over a UTF-8 string using SHA3-384 by default.
40
40
  *
41
41
  * This helper intentionally does not canonicalize the input string for you.
42
42
  * Callers must pass the final canonical logical identifier they want to anchor.
43
+ * A caller may supply another explicit multihash profile when an established
44
+ * external contract requires it.
43
45
  */
44
- export function buildRawCidV1FromUtf8String(value, profile) {
46
+ export function buildRawCidV1FromUtf8String(value, profile = SHA3_384_MULTIHASH_PROFILE) {
45
47
  const digest = profile.digest(utf8ToBytes(String(value || '')));
46
48
  const multihash = concatBytes(Uint8Array.from([profile.code, profile.digestLengthBytes]), digest);
47
49
  const cidBytes = concatBytes(encodeVarint(MULTIFORMAT_CID_V1_CODE), encodeVarint(MULTICODEC_RAW_CODE), multihash);
@@ -0,0 +1,32 @@
1
+ import { BundleEntryEditor } from './bundle-entry-editor';
2
+ /**
3
+ * Typed editor for one RelatedPerson/contact staged inside a Bundle.
4
+ *
5
+ * Relationship authoring is separate from access authorization. This editor
6
+ * does not grant Consent, build a Communication, submit or poll.
7
+ */
8
+ export declare class RelatedPersonEntryEditor extends BundleEntryEditor {
9
+ setIdentifier(value?: string | null): this;
10
+ getIdentifier(): string | undefined;
11
+ ensureIdentifier(): string;
12
+ setActive(value?: boolean | null): this;
13
+ getActive(): boolean | undefined;
14
+ setSubject(value?: string | null): this;
15
+ getSubject(): string | undefined;
16
+ setRelationship(value?: string | null): this;
17
+ getRelationship(): string | undefined;
18
+ setRoleList(values: readonly string[]): this;
19
+ getRoleList(): string[];
20
+ setName(value?: string | null): this;
21
+ getName(): string | undefined;
22
+ setTelecom(value?: string | null): this;
23
+ getTelecom(): string | undefined;
24
+ setRelatedEntityType(value?: string | null): this;
25
+ getRelatedEntityType(): string | undefined;
26
+ setActorIdentifierList(values: readonly string[]): this;
27
+ getActorIdentifierList(): string[];
28
+ private setOptionalText;
29
+ private getOptionalText;
30
+ private setList;
31
+ private getList;
32
+ }