gdc-common-utils-ts 2.0.8 → 2.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -136,6 +136,7 @@ If you need the canonical explanation of how DIDComm envelope, batch body,
136
136
  entry types, FHIR-like resources, and `resource.meta.claims` fit together,
137
137
  read first:
138
138
 
139
+ - [`docs/101-ID_TOKEN.md`](docs/101-ID_TOKEN.md)
139
140
  - [`docs/101-COMMUNICATION_LAYERING.md`](docs/101-COMMUNICATION_LAYERING.md)
140
141
  - [`docs/101-BUNDLE_EDITOR_READER.md`](docs/101-BUNDLE_EDITOR_READER.md)
141
142
  - [`docs/101-CLINICAL-IPS.md`](docs/101-CLINICAL-IPS.md)
@@ -127,7 +127,26 @@ export declare const EXAMPLE_EMPLOYEE_DEVICE_ACTIVATION_INPUT: {
127
127
  readonly activationCode: "ACT-001";
128
128
  readonly idToken: "employee-id-token-001";
129
129
  readonly dcrPayload: {
130
- readonly application_type: "web";
130
+ readonly application_type: "native";
131
+ readonly client_name: "Acme Controller App";
132
+ readonly redirect_uris: readonly ["acme-controller://callback"];
133
+ readonly jwks: {
134
+ readonly keys: readonly [{
135
+ readonly kid: "controller-didcomm-enc-001";
136
+ readonly kty: "EC";
137
+ readonly crv: "P-384";
138
+ readonly x: "<enc-x>";
139
+ readonly y: "<enc-y>";
140
+ readonly use: "enc";
141
+ readonly purposes: readonly ["didcomm-enc"];
142
+ }];
143
+ };
144
+ readonly ext_device_info: {
145
+ readonly push_token: "ExponentPushToken[example-controller]";
146
+ readonly push_provider: "expo";
147
+ readonly device_id: "device-controller-001";
148
+ readonly device_name: "Controller iPhone";
149
+ };
131
150
  };
132
151
  };
133
152
  export declare const EXAMPLE_EMPLOYEE_DEVICE_EXCHANGE_RESPONSE: {
@@ -79,7 +79,16 @@ export const EXAMPLE_EMPLOYEE_DEVICE_ACTIVATION_INPUT = {
79
79
  activationCode: EXAMPLE_EMPLOYEE_ACTIVATION_CODE,
80
80
  idToken: 'employee-id-token-001',
81
81
  dcrPayload: {
82
- application_type: 'web',
82
+ application_type: 'native',
83
+ client_name: 'Acme Controller App',
84
+ redirect_uris: ['acme-controller://callback'],
85
+ jwks: EXAMPLE_CONTROLLER_BINDING.jwks,
86
+ ext_device_info: {
87
+ push_token: 'ExponentPushToken[example-controller]',
88
+ push_provider: 'expo',
89
+ device_id: 'device-controller-001',
90
+ device_name: 'Controller iPhone',
91
+ },
83
92
  },
84
93
  };
85
94
  export const EXAMPLE_EMPLOYEE_DEVICE_EXCHANGE_RESPONSE = {
@@ -41,6 +41,11 @@ export declare const ObservationClaim: {
41
41
  readonly CodeValue: "Observation.code-value";
42
42
  /** Local-language label used by UI/forms. Example: `Heart rate`. */
43
43
  readonly CodeText: "Observation.code-text";
44
+ /**
45
+ * Naming alias for `Observation.code-text` when the caller wants the
46
+ * setter/getter name to make the local-text intent explicit.
47
+ */
48
+ readonly CodeTextLocal: "Observation.code-text";
44
49
  /** Canonical English/international display. Example: `Heart rate`. */
45
50
  readonly CodeDisplay: "Observation.code-display";
46
51
  /** Observation or component code as `system|code`. Example: `http://loinc.org|8867-4`. */
@@ -43,6 +43,11 @@ export const ObservationClaim = {
43
43
  CodeValue: 'Observation.code-value',
44
44
  /** Local-language label used by UI/forms. Example: `Heart rate`. */
45
45
  CodeText: 'Observation.code-text',
46
+ /**
47
+ * Naming alias for `Observation.code-text` when the caller wants the
48
+ * setter/getter name to make the local-text intent explicit.
49
+ */
50
+ CodeTextLocal: 'Observation.code-text',
46
51
  /** Canonical English/international display. Example: `Heart rate`. */
47
52
  CodeDisplay: 'Observation.code-display',
48
53
  /** Observation or component code as `system|code`. Example: `http://loinc.org|8867-4`. */
@@ -235,8 +235,23 @@ export declare class ObservationComponentEntryEditor extends BundleEntryEditor {
235
235
  getCodeValue(): string | undefined;
236
236
  setCodeDisplay(display: string): this;
237
237
  getCodeDisplay(): string | undefined;
238
- /** Local-language label intended for forms and local UI. */
238
+ /**
239
+ * Stores the local-language label used by forms and local UI copy.
240
+ *
241
+ * Keep this distinct from `setCodeDisplay(...)`, which is the canonical
242
+ * English/international display carried by the coded concept.
243
+ */
244
+ setCodeTextLocal(text: string): this;
245
+ /**
246
+ * Returns the local-language label used by forms and local UI copy.
247
+ *
248
+ * Keep this distinct from `getCodeDisplay()`, which returns the canonical
249
+ * English/international display when present.
250
+ */
251
+ getCodeTextLocal(): string | undefined;
252
+ /** Compatibility alias for older examples/tests. Prefer `setCodeTextLocal(...)`. */
239
253
  setLocalText(text: string): this;
254
+ /** Compatibility alias for older examples/tests. Prefer `getCodeTextLocal()`. */
240
255
  getLocalText(): string | undefined;
241
256
  setValueQuantityNumber(value: number): this;
242
257
  getValueQuantityNumber(): number | undefined;
@@ -724,13 +724,32 @@ export class ObservationComponentEntryEditor extends BundleEntryEditor {
724
724
  getCodeDisplay() {
725
725
  return normalizeOptionalIdentifier(this.getClaim(ObservationClaim.CodeDisplay));
726
726
  }
727
- /** Local-language label intended for forms and local UI. */
728
- setLocalText(text) {
727
+ /**
728
+ * Stores the local-language label used by forms and local UI copy.
729
+ *
730
+ * Keep this distinct from `setCodeDisplay(...)`, which is the canonical
731
+ * English/international display carried by the coded concept.
732
+ */
733
+ setCodeTextLocal(text) {
729
734
  return this.setClaim(ObservationClaim.CodeText, String(text).trim());
730
735
  }
731
- getLocalText() {
736
+ /**
737
+ * Returns the local-language label used by forms and local UI copy.
738
+ *
739
+ * Keep this distinct from `getCodeDisplay()`, which returns the canonical
740
+ * English/international display when present.
741
+ */
742
+ getCodeTextLocal() {
732
743
  return normalizeOptionalIdentifier(this.getClaim(ObservationClaim.CodeText));
733
744
  }
745
+ /** Compatibility alias for older examples/tests. Prefer `setCodeTextLocal(...)`. */
746
+ setLocalText(text) {
747
+ return this.setCodeTextLocal(text);
748
+ }
749
+ /** Compatibility alias for older examples/tests. Prefer `getCodeTextLocal()`. */
750
+ getLocalText() {
751
+ return this.getCodeTextLocal();
752
+ }
734
753
  setValueQuantityNumber(value) {
735
754
  return this.setClaim(ObservationClaim.ValueQuantityNumber, String(value));
736
755
  }
@@ -841,7 +860,7 @@ export class VitalSignEntryEditor extends ObservationComponentEntryEditor {
841
860
  this.setCodeValue(code.code);
842
861
  if (code.display) {
843
862
  this.setCodeDisplay(code.display);
844
- this.setLocalText(code.display);
863
+ this.setCodeTextLocal(code.display);
845
864
  }
846
865
  if (unit) {
847
866
  this.setValueQuantityUnit(unit);
@@ -142,20 +142,32 @@ export declare class CommunicationAttachedBundleSession {
142
142
  getBundleInMemory(): BundleJsonApi<BundleEntry>;
143
143
  /** Returns the active entry index, or null when no entry is selected. */
144
144
  getActiveEntryIndex(): number | null;
145
+ /** Alias of `getActiveEntryIndex()` with entry-selection wording. */
146
+ getSelectedEntryIndex(): number | null;
145
147
  /** Returns a deep copy of the active entry when selected. */
146
148
  getActiveEntry(): BundleEntry | null;
149
+ /** Alias of `getActiveEntry()` with entry-selection wording. */
150
+ getSelectedEntry(): BundleEntry | null;
147
151
  /** Returns one claim from the currently selected active entry. */
148
152
  getActiveEntryClaim(key: string): unknown;
153
+ /** Alias of `getActiveEntryClaim()` with entry-selection wording. */
154
+ getSelectedEntryClaim(key: string): unknown;
149
155
  /** Returns whether the currently selected active entry carries one claim key. */
150
156
  hasActiveEntryClaim(key: string): boolean;
157
+ /** Alias of `hasActiveEntryClaim()` with entry-selection wording. */
158
+ hasSelectedEntryClaim(key: string): boolean;
151
159
  /** Sets one claim on the currently selected active entry and syncs the bundle attachment. */
152
160
  setActiveEntryClaim(key: string, value: unknown): this;
161
+ /** Alias of `setActiveEntryClaim()` with entry-selection wording. */
162
+ setSelectedEntryClaim(key: string, value: unknown): this;
153
163
  /** Appends one claim value on the currently selected active entry and syncs the bundle attachment. */
154
164
  addActiveEntryClaim(key: string, value: unknown): this;
155
165
  /** Removes one claim from the currently selected active entry and syncs the bundle attachment. */
156
166
  removeActiveEntryClaim(key: string): this;
157
167
  /** Selects an active entry by index or fullUrl. */
158
168
  selectActiveEntry(selection: ActiveEntrySelection): this;
169
+ /** Alias of `selectActiveEntry()` with entry-selection wording. */
170
+ selectEntry(selection: ActiveEntrySelection): this;
159
171
  /** Clears active entry selection from memory. */
160
172
  clearActiveEntry(): this;
161
173
  /**
@@ -64,6 +64,10 @@ export class CommunicationAttachedBundleSession {
64
64
  getActiveEntryIndex() {
65
65
  return this.activeEntryIndex;
66
66
  }
67
+ /** Alias of `getActiveEntryIndex()` with entry-selection wording. */
68
+ getSelectedEntryIndex() {
69
+ return this.getActiveEntryIndex();
70
+ }
67
71
  /** Returns a deep copy of the active entry when selected. */
68
72
  getActiveEntry() {
69
73
  if (this.activeEntryIndex === null) {
@@ -71,16 +75,28 @@ export class CommunicationAttachedBundleSession {
71
75
  }
72
76
  return cloneEntry(this.bundleInMemory.data[this.activeEntryIndex]);
73
77
  }
78
+ /** Alias of `getActiveEntry()` with entry-selection wording. */
79
+ getSelectedEntry() {
80
+ return this.getActiveEntry();
81
+ }
74
82
  /** Returns one claim from the currently selected active entry. */
75
83
  getActiveEntryClaim(key) {
76
84
  const claims = this.getRequiredActiveEntryClaims();
77
85
  return cloneUnknownValue(claims[key]);
78
86
  }
87
+ /** Alias of `getActiveEntryClaim()` with entry-selection wording. */
88
+ getSelectedEntryClaim(key) {
89
+ return this.getActiveEntryClaim(key);
90
+ }
79
91
  /** Returns whether the currently selected active entry carries one claim key. */
80
92
  hasActiveEntryClaim(key) {
81
93
  const claims = this.getRequiredActiveEntryClaims();
82
94
  return Object.prototype.hasOwnProperty.call(claims, key);
83
95
  }
96
+ /** Alias of `hasActiveEntryClaim()` with entry-selection wording. */
97
+ hasSelectedEntryClaim(key) {
98
+ return this.hasActiveEntryClaim(key);
99
+ }
84
100
  /** Sets one claim on the currently selected active entry and syncs the bundle attachment. */
85
101
  setActiveEntryClaim(key, value) {
86
102
  const current = cloneEntry(this.getRequiredActiveEntry());
@@ -95,6 +111,10 @@ export class CommunicationAttachedBundleSession {
95
111
  this.syncAttachmentFromBundle();
96
112
  return this;
97
113
  }
114
+ /** Alias of `setActiveEntryClaim()` with entry-selection wording. */
115
+ setSelectedEntryClaim(key, value) {
116
+ return this.setActiveEntryClaim(key, value);
117
+ }
98
118
  /** Appends one claim value on the currently selected active entry and syncs the bundle attachment. */
99
119
  addActiveEntryClaim(key, value) {
100
120
  const current = cloneEntry(this.getRequiredActiveEntry());
@@ -152,6 +172,10 @@ export class CommunicationAttachedBundleSession {
152
172
  }
153
173
  throw new Error('selectActiveEntry requires either index or fullUrl.');
154
174
  }
175
+ /** Alias of `selectActiveEntry()` with entry-selection wording. */
176
+ selectEntry(selection) {
177
+ return this.selectActiveEntry(selection);
178
+ }
155
179
  /** Clears active entry selection from memory. */
156
180
  clearActiveEntry() {
157
181
  this.activeEntryIndex = null;
@@ -51,6 +51,53 @@ export declare function encodeSignature(signatureBytes?: Uint8Array): string;
51
51
  * @returns A compact JWT string.
52
52
  */
53
53
  export declare function compactJWT(header: object, payload: object, signatureBytes?: Uint8Array): Promise<string>;
54
+ /**
55
+ * Prepares the JOSE compact-signing input for an externally signed JWT/JWS.
56
+ *
57
+ * This helper is intended for BFF, wallet, Expo/native, or server-side flows
58
+ * where the signing key lives in an external KMS/HSM and the application must:
59
+ *
60
+ * 1. build the protected header and JWT payload locally
61
+ * 2. obtain the canonical `base64url(header).base64url(payload)` string
62
+ * 3. sign that exact byte sequence with the external signer
63
+ * 4. assemble the final compact JWS/JWT by appending the returned signature
64
+ *
65
+ * References:
66
+ * - RFC 7515 (JWS), Compact Serialization
67
+ * - RFC 7519 (JWT)
68
+ * - OpenID Connect Core 1.0 (`id_token` profile claims such as `email`)
69
+ *
70
+ * Note:
71
+ * - this helper only prepares the compact signing input
72
+ * - it does not verify that the payload is a particular profile such as
73
+ * `vp_token`, `id_token`, or `private_key_jwt`
74
+ */
75
+ export declare function prepareJwtForSignature(header: object, payload: object): {
76
+ encodedHeader: string;
77
+ encodedPayload: string;
78
+ signingInput: string;
79
+ };
80
+ /**
81
+ * Returns the UTF-8 bytes of the canonical compact JWT/JWS signing input.
82
+ *
83
+ * This is the exact byte sequence that an external signer must sign before the
84
+ * caller assembles the final compact JWT string with
85
+ * `buildJwtCompact(...)`.
86
+ */
87
+ export declare function prepareJwtBytesForSignature(header: object, payload: object): Uint8Array;
88
+ /**
89
+ * Assembles the final compact JWT/JWS once the caller already has:
90
+ *
91
+ * - the base64url-encoded protected header
92
+ * - the base64url-encoded payload
93
+ * - the detached signature returned by the external signer, also base64url-encoded
94
+ *
95
+ * This helper is profile-agnostic. It can be used for:
96
+ * - OpenID Connect `id_token`
97
+ * - `vp_token`
98
+ * - other compact JWS/JWT profiles that follow RFC 7515 / RFC 7519
99
+ */
100
+ export declare function buildJwtCompact(encodedHeader: string, encodedPayload: string, signatureBase64Url: string): string;
54
101
  /**
55
102
  * Builds an unsigned compact JWT with `alg=none`.
56
103
  *
package/dist/utils/jwt.js CHANGED
@@ -152,6 +152,62 @@ export async function compactJWT(header, payload, signatureBytes) {
152
152
  const encodedSignature = encodeSignature(signatureBytes);
153
153
  return `${encodedHeader}.${encodedPayload}.${encodedSignature}`;
154
154
  }
155
+ /**
156
+ * Prepares the JOSE compact-signing input for an externally signed JWT/JWS.
157
+ *
158
+ * This helper is intended for BFF, wallet, Expo/native, or server-side flows
159
+ * where the signing key lives in an external KMS/HSM and the application must:
160
+ *
161
+ * 1. build the protected header and JWT payload locally
162
+ * 2. obtain the canonical `base64url(header).base64url(payload)` string
163
+ * 3. sign that exact byte sequence with the external signer
164
+ * 4. assemble the final compact JWS/JWT by appending the returned signature
165
+ *
166
+ * References:
167
+ * - RFC 7515 (JWS), Compact Serialization
168
+ * - RFC 7519 (JWT)
169
+ * - OpenID Connect Core 1.0 (`id_token` profile claims such as `email`)
170
+ *
171
+ * Note:
172
+ * - this helper only prepares the compact signing input
173
+ * - it does not verify that the payload is a particular profile such as
174
+ * `vp_token`, `id_token`, or `private_key_jwt`
175
+ */
176
+ export function prepareJwtForSignature(header, payload) {
177
+ const encodedHeader = encodeHeader(header);
178
+ const encodedPayload = Content.objectToRawBase64UrlSafe(payload);
179
+ return {
180
+ encodedHeader,
181
+ encodedPayload,
182
+ signingInput: `${encodedHeader}.${encodedPayload}`,
183
+ };
184
+ }
185
+ /**
186
+ * Returns the UTF-8 bytes of the canonical compact JWT/JWS signing input.
187
+ *
188
+ * This is the exact byte sequence that an external signer must sign before the
189
+ * caller assembles the final compact JWT string with
190
+ * `buildJwtCompact(...)`.
191
+ */
192
+ export function prepareJwtBytesForSignature(header, payload) {
193
+ const { signingInput } = prepareJwtForSignature(header, payload);
194
+ return new TextEncoder().encode(signingInput);
195
+ }
196
+ /**
197
+ * Assembles the final compact JWT/JWS once the caller already has:
198
+ *
199
+ * - the base64url-encoded protected header
200
+ * - the base64url-encoded payload
201
+ * - the detached signature returned by the external signer, also base64url-encoded
202
+ *
203
+ * This helper is profile-agnostic. It can be used for:
204
+ * - OpenID Connect `id_token`
205
+ * - `vp_token`
206
+ * - other compact JWS/JWT profiles that follow RFC 7515 / RFC 7519
207
+ */
208
+ export function buildJwtCompact(encodedHeader, encodedPayload, signatureBase64Url) {
209
+ return `${encodedHeader}.${encodedPayload}.${String(signatureBase64Url || '').trim()}`;
210
+ }
155
211
  /**
156
212
  * Builds an unsigned compact JWT with `alg=none`.
157
213
  *
@@ -1,5 +1,4 @@
1
- // Always create JSDoc, do not use strings inline in keys nor values, use types instead, and reuse the data test examples.
2
- import { Content } from './content.js';
1
+ import { buildJwtCompact, prepareJwtBytesForSignature, prepareJwtForSignature } from './jwt.js';
3
2
  import { ORGANIZATION_ACTIVATION_VC_TYPES, REPRESENTATIVE_ACTIVATION_VC_TYPES, W3cCredentialContexts, W3cCredentialTypes, } from '../constants/verifiable-credentials.js';
4
3
  function fallbackId() {
5
4
  const rand = Math.random().toString(36).slice(2, 10);
@@ -224,13 +223,7 @@ export function addLegalRepresentativeCredential(vpPayload, vc) {
224
223
  return addTypedVC(vpPayload, vc, [...REPRESENTATIVE_ACTIVATION_VC_TYPES], 'LegalRepresentative');
225
224
  }
226
225
  export function prepareForSignature(header, payload) {
227
- const encodedHeader = Content.objectToRawBase64UrlSafe(header);
228
- const encodedPayload = Content.objectToRawBase64UrlSafe(payload);
229
- return {
230
- encodedHeader,
231
- encodedPayload,
232
- signingInput: `${encodedHeader}.${encodedPayload}`,
233
- };
226
+ return prepareJwtForSignature(header, payload);
234
227
  }
235
228
  /**
236
229
  * Returns the UTF-8 bytes of the canonical `base64url(header).base64url(payload)`
@@ -241,8 +234,7 @@ export function prepareForSignature(header, payload) {
241
234
  * `buildVpTokenCompact(...)`.
242
235
  */
243
236
  export function prepareBytesForSignature(header, payload) {
244
- const { signingInput } = prepareForSignature(header, payload);
245
- return new TextEncoder().encode(signingInput);
237
+ return prepareJwtBytesForSignature(header, payload);
246
238
  }
247
239
  /**
248
240
  * Assembles the final compact VP JWT once the caller already has:
@@ -252,5 +244,5 @@ export function prepareBytesForSignature(header, payload) {
252
244
  * - the detached signature returned by the external signer, also base64url-encoded
253
245
  */
254
246
  export function buildVpTokenCompact(encodedHeader, encodedPayload, signatureBase64Url) {
255
- return `${encodedHeader}.${encodedPayload}.${String(signatureBase64Url || '').trim()}`;
247
+ return buildJwtCompact(encodedHeader, encodedPayload, signatureBase64Url);
256
248
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "2.0.8",
3
+ "version": "2.0.10",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },