gdc-common-utils-ts 1.4.7 → 1.4.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.
@@ -17,10 +17,9 @@
17
17
  * such as registering one organization or creating one employee. It has a strict
18
18
  * structure:
19
19
  * - `type`: A string that defines the business action (e.g., 'Organization-registration-offer-v1.0').
20
- * - `meta`: A **TOP-LEVEL** property containing the original `claims` for the operation.
21
- * This is crucial for both processing and error reporting.
22
- * - `resource`: A FHIR-like resource object that is the subject of the action. It
23
- * contains the structured data derived from the claims.
20
+ * - `resource.meta.claims`: Canonical location for claims in request entries.
21
+ * - `meta.claims`: Legacy compatibility location accepted during migration.
22
+ * - `resource`: A FHIR-like resource object that is the subject of the action.
24
23
  * - `request`/`response`: Contextual objects indicating the operation's details or result.
25
24
  */
26
25
  import { OperationOutcome } from "./operation-outcome";
@@ -50,6 +49,22 @@ export interface BundleEntryMeta {
50
49
  /** The original, flattened claims record for this specific entry. */
51
50
  claims?: Record<string, any>;
52
51
  }
52
+ /**
53
+ * Canonical resource shape for batch-style entries.
54
+ * Claims should be authored in `resource.meta.claims`.
55
+ */
56
+ /**
57
+ * Agnostic resource shape for batch-style entries.
58
+ * Canonical claims location: resource.meta.claims
59
+ * Allows any resource structure, but meta.claims is always present for business logic.
60
+ */
61
+ export interface BundleEntryResource {
62
+ meta?: {
63
+ claims?: Record<string, any>;
64
+ [key: string]: any;
65
+ };
66
+ resourceType?: string;
67
+ }
53
68
  /**
54
69
  * @deprecated Use `BundleEntry` instead. This will be removed in a future version.
55
70
  * Represents a single entry in an INCOMING request Bundle.
@@ -58,7 +73,8 @@ export interface BundleEntryRequest {
58
73
  id?: string;
59
74
  type: string;
60
75
  request: BundleRequest;
61
- resource?: Record<string, any>;
76
+ resource?: BundleEntryResource;
77
+ /** @deprecated Legacy claims location. Prefer resource.meta.claims. */
62
78
  meta?: BundleEntryMeta;
63
79
  }
64
80
  /**
@@ -69,7 +85,8 @@ export interface BundleEntryResponse {
69
85
  id?: string;
70
86
  type: string;
71
87
  response: BundleResponse;
72
- resource?: Record<string, any>;
88
+ resource?: BundleEntryResource;
89
+ /** @deprecated Legacy claims location. Prefer resource.meta.claims. */
73
90
  meta?: BundleEntryMeta;
74
91
  }
75
92
  /**
@@ -100,16 +117,18 @@ export interface ErrorEntry {
100
117
  * This structure is used for both requests and successful responses.
101
118
  *
102
119
  * @property {string} type - A string identifying the business action of the entry.
103
- * @property {BundleEntryMeta} meta - **(TOP-LEVEL PROPERTY)** Contains the original, unprocessed claims.
104
120
  * @property {object} resource - The primary FHIR-like resource that is the subject of the action.
121
+ * Canonical claims location is `resource.meta.claims`.
122
+ * @property {BundleEntryMeta} [meta] - Legacy top-level claims location for backward compatibility.
105
123
  * @property {BundleRequest} [request] - Details of the requested operation (for request bundles).
106
124
  * @property {BundleResponse} [response] - Details of the operation outcome (for response bundles).
107
125
  */
108
126
  export type BundleEntry = {
109
127
  id?: string;
110
128
  type: string;
129
+ /** @deprecated Legacy claims location. Prefer resource.meta.claims. */
111
130
  meta?: BundleEntryMeta;
112
- resource?: Record<string, any>;
131
+ resource?: BundleEntryResource;
113
132
  request?: BundleRequest;
114
133
  response?: BundleResponse;
115
134
  };
@@ -17,10 +17,9 @@
17
17
  * such as registering one organization or creating one employee. It has a strict
18
18
  * structure:
19
19
  * - `type`: A string that defines the business action (e.g., 'Organization-registration-offer-v1.0').
20
- * - `meta`: A **TOP-LEVEL** property containing the original `claims` for the operation.
21
- * This is crucial for both processing and error reporting.
22
- * - `resource`: A FHIR-like resource object that is the subject of the action. It
23
- * contains the structured data derived from the claims.
20
+ * - `resource.meta.claims`: Canonical location for claims in request entries.
21
+ * - `meta.claims`: Legacy compatibility location accepted during migration.
22
+ * - `resource`: A FHIR-like resource object that is the subject of the action.
24
23
  * - `request`/`response`: Contextual objects indicating the operation's details or result.
25
24
  */
26
25
  export {};
@@ -0,0 +1,10 @@
1
+ export type CommunicationMode = 'plain' | 'strict' | 'auto-detect';
2
+ export type DidcommSubmissionCapabilities = {
3
+ hasRecipientEncryptionJwk: boolean;
4
+ };
5
+ export type DidcommSubmissionPlan = {
6
+ mode: CommunicationMode;
7
+ submitKind: 'plain' | 'encrypted';
8
+ reason: 'plain-mode' | 'strict-mode' | 'auto-detect-encrypted' | 'auto-detect-plain';
9
+ };
10
+ export declare function resolveDidcommSubmissionPlan(mode: CommunicationMode, capabilities: DidcommSubmissionCapabilities): DidcommSubmissionPlan;
@@ -0,0 +1,15 @@
1
+ export function resolveDidcommSubmissionPlan(mode, capabilities) {
2
+ if (mode === 'plain') {
3
+ return { mode, submitKind: 'plain', reason: 'plain-mode' };
4
+ }
5
+ if (mode === 'strict') {
6
+ if (!capabilities.hasRecipientEncryptionJwk) {
7
+ throw new Error('strict mode requires recipient encryption JWK.');
8
+ }
9
+ return { mode, submitKind: 'encrypted', reason: 'strict-mode' };
10
+ }
11
+ if (capabilities.hasRecipientEncryptionJwk) {
12
+ return { mode, submitKind: 'encrypted', reason: 'auto-detect-encrypted' };
13
+ }
14
+ return { mode, submitKind: 'plain', reason: 'auto-detect-plain' };
15
+ }
@@ -0,0 +1,34 @@
1
+ import { CommunicationMode } from './didcomm-submit-policy';
2
+ export type DidcommFetchInit = {
3
+ method: 'POST';
4
+ headers: Record<string, string>;
5
+ body: string;
6
+ };
7
+ export type DidcommFetchResponse = {
8
+ status: number;
9
+ headers?: {
10
+ get(name: string): string | null;
11
+ };
12
+ json?: () => Promise<unknown>;
13
+ text?: () => Promise<string>;
14
+ };
15
+ export type DidcommFetchLike = (url: string, init: DidcommFetchInit) => Promise<DidcommFetchResponse>;
16
+ export type DidcommSubmitInput = {
17
+ mode: CommunicationMode;
18
+ url: string;
19
+ payload: Record<string, unknown>;
20
+ defaultHeaders?: Record<string, string>;
21
+ bearerToken?: string;
22
+ recipientEncryptionJwk?: unknown;
23
+ fetcher: DidcommFetchLike;
24
+ signCompactJws?: (claims: Record<string, unknown>) => Promise<string>;
25
+ encryptCompactJwe?: (compactJws: string, recipientEncryptionJwk: unknown) => Promise<string>;
26
+ };
27
+ export type DidcommSubmitResult = {
28
+ status: number;
29
+ location?: string;
30
+ body: unknown;
31
+ submitKind: 'plain' | 'encrypted';
32
+ contentType: 'application/didcomm-plaintext+json' | 'application/didcomm-encrypted+json';
33
+ };
34
+ export declare function submitDidcomm(input: DidcommSubmitInput): Promise<DidcommSubmitResult>;
@@ -0,0 +1,75 @@
1
+ import { resolveDidcommSubmissionPlan } from './didcomm-submit-policy.js';
2
+ function getHeaderValue(headers, name) {
3
+ if (!headers || typeof headers.get !== 'function') {
4
+ return undefined;
5
+ }
6
+ const value = headers.get(name);
7
+ return value == null ? undefined : value;
8
+ }
9
+ async function parseBody(response) {
10
+ if (typeof response.json === 'function') {
11
+ try {
12
+ return await response.json();
13
+ }
14
+ catch {
15
+ // Continue to text fallback.
16
+ }
17
+ }
18
+ if (typeof response.text === 'function') {
19
+ const raw = await response.text();
20
+ if (!raw)
21
+ return undefined;
22
+ try {
23
+ return JSON.parse(raw);
24
+ }
25
+ catch {
26
+ return raw;
27
+ }
28
+ }
29
+ return undefined;
30
+ }
31
+ export async function submitDidcomm(input) {
32
+ const plan = resolveDidcommSubmissionPlan(input.mode, {
33
+ hasRecipientEncryptionJwk: !!input.recipientEncryptionJwk,
34
+ });
35
+ const headers = {
36
+ ...(input.defaultHeaders ?? {}),
37
+ Accept: 'application/json, application/didcomm-plaintext+json, */*',
38
+ };
39
+ let body;
40
+ let contentType;
41
+ if (plan.submitKind === 'plain') {
42
+ contentType = 'application/didcomm-plaintext+json';
43
+ body = JSON.stringify(input.payload);
44
+ }
45
+ else {
46
+ if (!input.signCompactJws) {
47
+ throw new Error('Encrypted DIDComm submission requires signCompactJws callback.');
48
+ }
49
+ if (!input.encryptCompactJwe) {
50
+ throw new Error('Encrypted DIDComm submission requires encryptCompactJwe callback.');
51
+ }
52
+ if (!input.recipientEncryptionJwk) {
53
+ throw new Error('Encrypted DIDComm submission requires recipientEncryptionJwk.');
54
+ }
55
+ const compactJws = await input.signCompactJws(input.payload);
56
+ body = await input.encryptCompactJwe(compactJws, input.recipientEncryptionJwk);
57
+ contentType = 'application/didcomm-encrypted+json';
58
+ }
59
+ headers['Content-Type'] = contentType;
60
+ if (input.bearerToken) {
61
+ headers.Authorization = `Bearer ${input.bearerToken}`;
62
+ }
63
+ const response = await input.fetcher(input.url, {
64
+ method: 'POST',
65
+ headers,
66
+ body,
67
+ });
68
+ return {
69
+ status: response.status,
70
+ location: getHeaderValue(response.headers, 'location'),
71
+ body: await parseBody(response),
72
+ submitKind: plan.submitKind,
73
+ contentType,
74
+ };
75
+ }
@@ -6,6 +6,8 @@ export * from './content';
6
6
  export * from './consent';
7
7
  export * from './did';
8
8
  export * from './didcomm';
9
+ export * from './didcomm-submit';
10
+ export * from './didcomm-submit-policy';
9
11
  export * from './format-converter';
10
12
  export * from './fhir-cid';
11
13
  export * from './jwt';
@@ -15,3 +17,4 @@ export * from './multibasehash';
15
17
  export * from './normalize';
16
18
  export * from './object-convert';
17
19
  export * from './normalize-uuid';
20
+ export * from './vp-token';
@@ -6,6 +6,8 @@ export * from './content.js';
6
6
  export * from './consent.js';
7
7
  export * from './did.js';
8
8
  export * from './didcomm.js';
9
+ export * from './didcomm-submit.js';
10
+ export * from './didcomm-submit-policy.js';
9
11
  export * from './format-converter.js';
10
12
  export * from './fhir-cid.js';
11
13
  export * from './jwt.js';
@@ -15,3 +17,4 @@ export * from './multibasehash.js';
15
17
  export * from './normalize.js';
16
18
  export * from './object-convert.js';
17
19
  export * from './normalize-uuid.js';
20
+ export * from './vp-token.js';
@@ -0,0 +1,37 @@
1
+ export type VpTokenHeader = {
2
+ alg: string;
3
+ typ?: string;
4
+ kid?: string;
5
+ [key: string]: unknown;
6
+ };
7
+ export type VpTokenPayload = {
8
+ iss: string;
9
+ sub?: string;
10
+ aud?: string;
11
+ jti?: string;
12
+ iat?: number;
13
+ exp?: number;
14
+ nonce?: string;
15
+ vp: {
16
+ '@context'?: unknown;
17
+ type?: unknown;
18
+ holder?: string;
19
+ verifiableCredential: string[];
20
+ [key: string]: unknown;
21
+ };
22
+ [key: string]: unknown;
23
+ };
24
+ export declare function generateUuidLike(): string;
25
+ export declare function buildEpochWindow(ttlSeconds?: number): {
26
+ iat: number;
27
+ exp: number;
28
+ };
29
+ export declare function createVP(input?: Partial<VpTokenPayload>): VpTokenPayload;
30
+ export declare function addVC(vpPayload: VpTokenPayload, vcJwt: string): VpTokenPayload;
31
+ export declare function prepareForSignature(header: VpTokenHeader, payload: VpTokenPayload): {
32
+ encodedHeader: string;
33
+ encodedPayload: string;
34
+ signingInput: string;
35
+ };
36
+ export declare function prepareBytesForSignature(header: VpTokenHeader, payload: VpTokenPayload): Uint8Array;
37
+ export declare function buildVpTokenCompact(encodedHeader: string, encodedPayload: string, signatureBase64Url: string): string;
@@ -0,0 +1,60 @@
1
+ import { Content } from './content.js';
2
+ function fallbackId() {
3
+ const rand = Math.random().toString(36).slice(2, 10);
4
+ return `id-${Date.now()}-${rand}`;
5
+ }
6
+ export function generateUuidLike() {
7
+ const fn = globalThis?.crypto?.randomUUID;
8
+ if (typeof fn === 'function')
9
+ return fn.call(globalThis.crypto);
10
+ return fallbackId();
11
+ }
12
+ export function buildEpochWindow(ttlSeconds = 300) {
13
+ const iat = Math.floor(Date.now() / 1000);
14
+ return { iat, exp: iat + Math.max(1, Math.floor(ttlSeconds)) };
15
+ }
16
+ export function createVP(input) {
17
+ const ttl = input?.exp && input?.iat ? undefined : buildEpochWindow(300);
18
+ const jti = input?.jti || generateUuidLike();
19
+ const nonce = input?.nonce || generateUuidLike();
20
+ const base = {
21
+ iss: String(input?.iss || ''),
22
+ sub: input?.sub,
23
+ aud: input?.aud,
24
+ jti,
25
+ iat: input?.iat ?? ttl?.iat,
26
+ exp: input?.exp ?? ttl?.exp,
27
+ nonce,
28
+ vp: {
29
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
30
+ type: ['VerifiablePresentation'],
31
+ holder: input?.vp?.holder || input?.iss || '',
32
+ verifiableCredential: [],
33
+ ...(input?.vp || {}),
34
+ },
35
+ };
36
+ return base;
37
+ }
38
+ export function addVC(vpPayload, vcJwt) {
39
+ const v = String(vcJwt || '').trim();
40
+ if (!v)
41
+ return vpPayload;
42
+ vpPayload.vp.verifiableCredential.push(v);
43
+ return vpPayload;
44
+ }
45
+ export function prepareForSignature(header, payload) {
46
+ const encodedHeader = Content.objectToRawBase64UrlSafe(header);
47
+ const encodedPayload = Content.objectToRawBase64UrlSafe(payload);
48
+ return {
49
+ encodedHeader,
50
+ encodedPayload,
51
+ signingInput: `${encodedHeader}.${encodedPayload}`,
52
+ };
53
+ }
54
+ export function prepareBytesForSignature(header, payload) {
55
+ const { signingInput } = prepareForSignature(header, payload);
56
+ return new TextEncoder().encode(signingInput);
57
+ }
58
+ export function buildVpTokenCompact(encodedHeader, encodedPayload, signatureBase64Url) {
59
+ return `${encodedHeader}.${encodedPayload}.${String(signatureBase64Url || '').trim()}`;
60
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "1.4.7",
3
+ "version": "1.4.10",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },