@animo-id/eudi-wallet-functionality 0.0.0-alpha-20260112191822 → 0.1.0

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/dist/index.mjs CHANGED
@@ -17,16 +17,152 @@ var Ts12IntegrityError = class Ts12IntegrityError extends EudiWalletExtensionsEr
17
17
  }
18
18
  };
19
19
 
20
+ //#endregion
21
+ //#region src/merge-json.ts
22
+ /**
23
+ * Default validator that enforces:
24
+ * 1. Non-null/undefined values cannot be set to null/undefined.
25
+ * 2. Non-null/undefined types must match (e.g. cannot change string to number, or object to array).
26
+ */
27
+ const defaultValidator = (path, target, source) => {
28
+ if (source === void 0) return;
29
+ if (target === void 0) return;
30
+ if (source === null) {
31
+ if (target !== null) throw new Error(`Invalid value change at path "${path}": cannot set non-nullable value to null`);
32
+ return;
33
+ }
34
+ if (target === null) return;
35
+ const targetType = getType(target);
36
+ const sourceType = getType(source);
37
+ if (targetType !== sourceType) throw new Error(`Type mismatch at path "${path}": expected ${targetType}, got ${sourceType}`);
38
+ if (targetType === "primitive") {
39
+ if (typeof target !== typeof source) throw new Error(`Type mismatch at path "${path}": expected ${typeof target}, got ${typeof source}`);
40
+ }
41
+ };
42
+ /**
43
+ * Merges two JSON values based on a configuration.
44
+ *
45
+ * @param target The original object (will not be mutated).
46
+ * @param source The object to merge into the target.
47
+ * @param config Configuration for the merge behavior.
48
+ * @returns The merged object.
49
+ */
50
+ function mergeJson(target, source, config = {}) {
51
+ return mergeRecursive(target, source, config, {
52
+ objectStrategy: config.objectStrategy,
53
+ arrayStrategy: config.arrayStrategy,
54
+ validator: config.validator ?? defaultValidator
55
+ }, "");
56
+ }
57
+ function mergeRecursive(target, source, nodeConfig, parentDefaults, path) {
58
+ if (source === void 0) return target;
59
+ const currentDefaults = {
60
+ objectStrategy: nodeConfig?.objectStrategy ?? parentDefaults.objectStrategy,
61
+ arrayStrategy: nodeConfig?.arrayStrategy ?? parentDefaults.arrayStrategy,
62
+ validator: nodeConfig?.validator ?? parentDefaults.validator
63
+ };
64
+ if (nodeConfig?.validate) nodeConfig.validate(target, source);
65
+ if (currentDefaults.validator) currentDefaults.validator(path, target, source);
66
+ if (target === void 0) return source;
67
+ if (source === null) return null;
68
+ if (target === null) return source;
69
+ const targetType = getType(target);
70
+ const sourceType = getType(source);
71
+ if (targetType !== sourceType) return source;
72
+ if (targetType === "primitive") {
73
+ if (typeof target !== typeof source) return source;
74
+ return source;
75
+ }
76
+ const strategy = nodeConfig?.strategy;
77
+ if (sourceType === "array") {
78
+ const targetArray = target;
79
+ const sourceArray = source;
80
+ const arrayStrategy = strategy || currentDefaults.arrayStrategy || "replace";
81
+ if (arrayStrategy === "replace") return [...sourceArray];
82
+ if (arrayStrategy === "append") return [...targetArray, ...sourceArray];
83
+ if (arrayStrategy === "merge") return mergeArrays(targetArray, sourceArray, nodeConfig, currentDefaults, path, nodeConfig?.arrayDiscriminant);
84
+ }
85
+ if (sourceType === "object") {
86
+ const targetObj = target;
87
+ const sourceObj = source;
88
+ const objectStrategy = strategy || currentDefaults.objectStrategy || "merge";
89
+ if (objectStrategy === "replace") return { ...sourceObj };
90
+ if (objectStrategy === "merge") {
91
+ const result = { ...targetObj };
92
+ const keys = new Set([...Object.keys(targetObj), ...Object.keys(sourceObj)]);
93
+ for (const key of keys) {
94
+ const keyPart = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? `.${key}` : `["${key}"]`;
95
+ const newPath = path ? `${path}${keyPart}` : key;
96
+ const specificConfig = nodeConfig?.fields?.[key];
97
+ const wildcardConfig = nodeConfig?.items;
98
+ const childNodeConfig = specificConfig && wildcardConfig ? {
99
+ ...wildcardConfig,
100
+ ...specificConfig
101
+ } : specificConfig ?? wildcardConfig;
102
+ result[key] = mergeRecursive(targetObj[key], sourceObj[key], childNodeConfig, currentDefaults, newPath);
103
+ }
104
+ return result;
105
+ }
106
+ }
107
+ return source;
108
+ }
109
+ function mergeArrays(target, source, arrayNodeConfig, defaults, path, discriminant) {
110
+ const itemNodeConfig = arrayNodeConfig?.items;
111
+ if (!discriminant) {
112
+ const result$1 = [...target];
113
+ for (let i = 0; i < source.length; i++) if (i < result$1.length) result$1[i] = mergeRecursive(result$1[i], source[i], itemNodeConfig, defaults, `${path}[${i}]`);
114
+ else result$1.push(source[i]);
115
+ return result$1;
116
+ }
117
+ const result = [...target];
118
+ const discriminants = Array.isArray(discriminant) ? discriminant : [discriminant];
119
+ for (const sourceItem of source) {
120
+ const matchIndex = result.findIndex((targetItem) => {
121
+ if (getType(targetItem) !== "object" || getType(sourceItem) !== "object") return false;
122
+ const t = targetItem;
123
+ const s = sourceItem;
124
+ return discriminants.every((d) => deepEqual(t[d], s[d]));
125
+ });
126
+ if (matchIndex !== -1) result[matchIndex] = mergeRecursive(result[matchIndex], sourceItem, itemNodeConfig, defaults, `${path}[${matchIndex}]`);
127
+ else result.push(sourceItem);
128
+ }
129
+ return result;
130
+ }
131
+ function getType(value) {
132
+ if (Array.isArray(value)) return "array";
133
+ if (value !== null && typeof value === "object") return "object";
134
+ return "primitive";
135
+ }
136
+ function deepEqual(a, b) {
137
+ if (a === b) return true;
138
+ const typeA = getType(a);
139
+ if (typeA !== getType(b)) return false;
140
+ if (typeA === "array") {
141
+ const arrA = a;
142
+ const arrB = b;
143
+ if (arrA.length !== arrB.length) return false;
144
+ for (let i = 0; i < arrA.length; i++) if (!deepEqual(arrA[i], arrB[i])) return false;
145
+ return true;
146
+ }
147
+ if (typeA === "object") {
148
+ const objA = a;
149
+ const objB = b;
150
+ const keysA = Object.keys(objA);
151
+ const keysB = Object.keys(objB);
152
+ if (keysA.length !== keysB.length) return false;
153
+ for (const key of keysA) {
154
+ if (!Object.hasOwn(objB, key)) return false;
155
+ if (!deepEqual(objA[key], objB[key])) return false;
156
+ }
157
+ return true;
158
+ }
159
+ return false;
160
+ }
161
+
20
162
  //#endregion
21
163
  //#region src/validation/z-sca-attestation-ext.ts
22
164
  const zScaTransactionDataTypeClaims = z.array(z.object({
23
165
  path: z.array(z.string()),
24
- visualisation: z.union([
25
- z.literal(1),
26
- z.literal(2),
27
- z.literal(3),
28
- z.literal(4)
29
- ]).default(3),
30
166
  display: z.array(z.object({
31
167
  name: z.string(),
32
168
  locale: z.string().optional(),
@@ -35,23 +171,23 @@ const zScaTransactionDataTypeClaims = z.array(z.object({
35
171
  }));
36
172
  const zScaTransactionDataTypeUiLabels = z.object({
37
173
  affirmative_action_label: z.array(z.object({
38
- lang: z.string(),
174
+ locale: z.string(),
39
175
  value: z.string().max(30)
40
176
  })).min(1),
41
177
  denial_action_label: z.array(z.object({
42
- lang: z.string(),
178
+ locale: z.string(),
43
179
  value: z.string().max(30)
44
180
  })).min(1).optional(),
45
181
  transaction_title: z.array(z.object({
46
- lang: z.string(),
182
+ locale: z.string(),
47
183
  value: z.string().max(50)
48
184
  })).min(1).optional(),
49
185
  security_hint: z.array(z.object({
50
- lang: z.string(),
186
+ locale: z.string(),
51
187
  value: z.string().max(250)
52
188
  })).min(1).optional()
53
189
  }).catchall(z.array(z.object({
54
- lang: z.string(),
190
+ locale: z.string(),
55
191
  value: z.string()
56
192
  })));
57
193
  /**
@@ -63,10 +199,11 @@ const zScaTransactionDataTypeUiLabels = z.object({
63
199
  */
64
200
  const zScaAttestationExt = z.object({
65
201
  category: z.string().optional(),
66
- transaction_data_types: z.record(z.string().describe("Transaction Type URI (e.g., urn:eudi:sca:payment:1). Must be collision resistant."), z.intersection(z.union([z.object({ schema: z.string() }), z.object({
67
- schema_uri: z.url(),
68
- "schema_uri#integrity": z.string().optional()
69
- })]), z.intersection(z.union([z.object({ claims: zScaTransactionDataTypeClaims }), z.object({
202
+ transaction_data_types: z.array(z.intersection(z.object({
203
+ type: z.string(),
204
+ "type#integrity": z.string().optional(),
205
+ subtype: z.string().optional()
206
+ }), z.intersection(z.union([z.object({ claims: zScaTransactionDataTypeClaims }), z.object({
70
207
  claims_uri: z.url(),
71
208
  "claims_uri#integrity": z.string().optional()
72
209
  })]), z.union([z.object({ ui_labels: zScaTransactionDataTypeUiLabels }), z.object({
@@ -167,61 +304,40 @@ const zPaymentPayload = z.object({
167
304
  path: ["execution_date"]
168
305
  });
169
306
  /**
170
- * **TS12 Login / Risk Payload**
307
+ * **TS12 Generic Payload**
171
308
  * * @see EUDI TS12 Section 4.3.2
172
309
  */
173
- const zLoginPayload = z.object({
310
+ const zGenericPayload = z.object({
174
311
  transaction_id: z.string().min(1).max(36),
175
- date_time: z.iso.datetime().optional(),
176
- service: z.string().max(100).optional(),
177
- action: z.string().max(140).describe("Description of the action to be authorized")
312
+ payment_payload: zPaymentPayload.optional()
313
+ }).catchall(z.string().max(40).nullable()).refine((data) => {
314
+ return Object.keys(data).length <= 11;
315
+ }, { message: "Total number of properties is limited to 11" });
316
+ const URN_SCA_PAYMENT = "urn:eudi:sca:payment:1";
317
+ const URN_SCA_GENERIC = "urn:eudi:sca:generic:1";
318
+ const zTs12PaymentTransaction = zBaseTransaction.extend({
319
+ type: z.literal(URN_SCA_PAYMENT),
320
+ subtype: z.undefined(),
321
+ payload: zPaymentPayload
178
322
  });
179
- /**
180
- * **TS12 Account Access Payload**
181
- * * @see EUDI TS12 Section 4.3.3
182
- */
183
- const zAccountAccessPayload = z.object({
184
- transaction_id: z.string().min(1).max(36),
185
- date_time: z.iso.datetime().optional(),
186
- aisp: z.object({
187
- legal_name: z.string(),
188
- brand_name: z.string(),
189
- domain_name: z.string()
190
- }).optional(),
191
- description: z.string().max(140).optional()
323
+ const zTs12GenericTransaction = zBaseTransaction.extend({
324
+ type: z.literal(URN_SCA_GENERIC),
325
+ subtype: z.string(),
326
+ payload: zGenericPayload
192
327
  });
193
- /**
194
- * **TS12 E-Mandate Payload**
195
- * * @see EUDI TS12 Section 4.3.4
196
- */
197
- const zEMandatePayload = z.object({
198
- transaction_id: z.string().min(1).max(36),
199
- date_time: z.iso.datetime().optional(),
200
- start_date: z.iso.datetime().optional(),
201
- end_date: z.iso.datetime().optional(),
202
- reference_number: z.string().min(1).max(50).optional(),
203
- creditor_id: z.string().min(1).max(50).optional(),
204
- purpose: z.string().max(1e3).optional(),
205
- payment_payload: zPaymentPayload.optional()
206
- }).refine((data) => data.payment_payload || data.purpose, {
207
- message: "Purpose is required if payment_payload is missing",
208
- path: ["purpose"]
328
+ const zTs12FallbackTransaction = zBaseTransaction.extend({
329
+ subtype: z.string().optional(),
330
+ payload: z.unknown()
209
331
  });
210
- const URN_SCA_PAYMENT = "urn:eudi:sca:payment:1";
211
- const URN_SCA_LOGIN_RISK = "urn:eudi:sca:login_risk_transaction:1";
212
- const URN_SCA_ACCOUNT_ACCESS = "urn:eudi:sca:account_access:1";
213
- const URN_SCA_EMANDATE = "urn:eudi:sca:emandate:1";
214
332
  /**
215
333
  * **TS12 Transaction**
216
334
  * @see TS12 Section 4.3
217
335
  */
218
- const zTs12Transaction = zBaseTransaction.extend({ payload: z.union([
219
- zPaymentPayload,
220
- zLoginPayload,
221
- zAccountAccessPayload,
222
- zEMandatePayload,
223
- z.unknown()
224
- ]) });
336
+ const zTs12Transaction = z.union([
337
+ zTs12PaymentTransaction,
338
+ zTs12GenericTransaction,
339
+ zTs12FallbackTransaction
340
+ ]);
225
341
 
226
342
  //#endregion
227
343
  //#region src/validation/z-transaction-data.ts
@@ -229,9 +345,7 @@ const zTransactionDataEntry = zTs12Transaction.or(zFunkeQesTransaction);
229
345
  const zTransactionData = z.array(zTransactionDataEntry);
230
346
  const ts12BuiltinSchemaValidators = {
231
347
  [URN_SCA_PAYMENT]: zPaymentPayload,
232
- [URN_SCA_LOGIN_RISK]: zLoginPayload,
233
- [URN_SCA_ACCOUNT_ACCESS]: zAccountAccessPayload,
234
- [URN_SCA_EMANDATE]: zEMandatePayload
348
+ [URN_SCA_GENERIC]: zGenericPayload
235
349
  };
236
350
 
237
351
  //#endregion
@@ -242,14 +356,13 @@ async function fetchVerified(uri, schema, integrity, validateIntegrity) {
242
356
  if (integrity && validateIntegrity && !validateIntegrity(await response.clone().arrayBuffer(), integrity)) throw new Ts12IntegrityError(uri, integrity);
243
357
  return schema.parse(await response.json());
244
358
  }
245
- async function resolveTs12TransactionDisplayMetadata(metadata, type, validateIntegrity) {
246
- if (!metadata.transaction_data_types || !metadata.transaction_data_types[type]) return;
247
- const typeMetadata = metadata.transaction_data_types[type];
359
+ async function resolveTs12TransactionDisplayMetadata(metadata, type, subtype, validateIntegrity) {
360
+ if (!metadata.transaction_data_types) return;
361
+ const typeMetadata = metadata.transaction_data_types.find((t) => t.type === type && t.subtype === subtype);
362
+ if (!typeMetadata) return;
248
363
  const resolved = {};
249
- if ("schema" in typeMetadata && typeMetadata.schema) {
250
- if (!(typeMetadata.schema in ts12BuiltinSchemaValidators)) throw new Error(`unknown builtin schema: ${typeMetadata.schema}`);
251
- resolved.schema = typeMetadata.schema;
252
- } else if ("schema_uri" in typeMetadata && typeMetadata.schema_uri) resolved.schema = await fetchVerified(typeMetadata.schema_uri, z.object({}), typeMetadata["schema_uri#integrity"], validateIntegrity);
364
+ if (typeMetadata.type in ts12BuiltinSchemaValidators) resolved.schema = typeMetadata.type;
365
+ else if (typeMetadata.type.startsWith("http")) resolved.schema = await fetchVerified(typeMetadata.type, z.object({}), typeMetadata["type#integrity"], validateIntegrity);
253
366
  else throw new Error(`Unknown schema type for ${typeMetadata}`);
254
367
  if ("claims" in typeMetadata && typeMetadata.claims) resolved.claims = typeMetadata.claims;
255
368
  else if ("claims_uri" in typeMetadata && typeMetadata.claims_uri) resolved.claims = await fetchVerified(typeMetadata.claims_uri, zScaTransactionDataTypeClaims, typeMetadata["claims_uri#integrity"], validateIntegrity);
@@ -259,6 +372,44 @@ async function resolveTs12TransactionDisplayMetadata(metadata, type, validateInt
259
372
  else throw new Error(`Unknown ui_labels for ${typeMetadata}`);
260
373
  return resolved;
261
374
  }
375
+ const baseMergeConfig = { fields: {
376
+ display: { strategy: "replace" },
377
+ claims: {
378
+ strategy: "merge",
379
+ arrayDiscriminant: "path",
380
+ items: { fields: {
381
+ sd: { validate: (target, source) => {
382
+ if (target === "always" && source !== "always") throw new Error("Constraint violation: 'sd' cannot change from 'always'");
383
+ if (target === "never" && source !== "never") throw new Error("Constraint violation: 'sd' cannot change from 'never'");
384
+ } },
385
+ mandatory: { validate: (target, source) => {
386
+ if (target === true && source !== true) throw new Error("Constraint violation: 'mandatory' cannot change from true to false");
387
+ } }
388
+ } }
389
+ }
390
+ } };
391
+ const ts12MergeConfig = mergeJson(baseMergeConfig, { fields: { transaction_data_types: {
392
+ arrayStrategy: "append",
393
+ strategy: "merge",
394
+ arrayDiscriminant: ["type", "subtype"],
395
+ items: { fields: {
396
+ claims: {
397
+ strategy: "merge",
398
+ arrayDiscriminant: "path",
399
+ items: { fields: { display: {
400
+ strategy: "merge",
401
+ arrayDiscriminant: "locale"
402
+ } } }
403
+ },
404
+ ui_labels: {
405
+ strategy: "merge",
406
+ items: {
407
+ strategy: "merge",
408
+ arrayDiscriminant: "locale"
409
+ }
410
+ }
411
+ } }
412
+ } } });
262
413
 
263
414
  //#endregion
264
415
  //#region src/isDcqlQueryEqualOrSubset.ts
@@ -337,9 +488,9 @@ const verifyOpenid4VpAuthorizationRequest = async (agentContext, { resolvedAutho
337
488
  const registrationCertificateHeaderSchema = z$1.object({
338
489
  typ: z$1.literal("rc-rp+jwt"),
339
490
  alg: z$1.string(),
340
- x5u: z$1.string().url().optional(),
491
+ x5u: z$1.url().optional(),
341
492
  "x5t#s256": z$1.string().optional()
342
- }).passthrough();
493
+ }).loose();
343
494
  const registrationCertificatePayloadSchema = z$1.object({
344
495
  credentials: z$1.array(z$1.object({
345
496
  format: z$1.string(),
@@ -361,8 +512,8 @@ const verifyOpenid4VpAuthorizationRequest = async (agentContext, { resolvedAutho
361
512
  claim_sets: z$1.array(z$1.array(z$1.string())).nonempty().optional()
362
513
  })),
363
514
  contact: z$1.object({
364
- website: z$1.string().url(),
365
- "e-mail": z$1.string().email(),
515
+ website: z$1.url(),
516
+ "e-mail": z$1.email(),
366
517
  phone: z$1.string()
367
518
  }),
368
519
  sub: z$1.string(),
@@ -376,7 +527,7 @@ const verifyOpenid4VpAuthorizationRequest = async (agentContext, { resolvedAutho
376
527
  format: z$1.string(),
377
528
  meta: z$1.any()
378
529
  })).optional(),
379
- privacy_policy: z$1.string().url(),
530
+ privacy_policy: z$1.url(),
380
531
  iat: z$1.number().optional(),
381
532
  exp: z$1.number().optional(),
382
533
  purpose: z$1.array(z$1.object({
@@ -385,7 +536,7 @@ const verifyOpenid4VpAuthorizationRequest = async (agentContext, { resolvedAutho
385
536
  name: z$1.string()
386
537
  })).optional(),
387
538
  status: z$1.any()
388
- }).passthrough();
539
+ }).loose();
389
540
  registrationCertificateHeaderSchema.parse(jwt.header);
390
541
  const parsedPayload = registrationCertificatePayloadSchema.parse(jwt.payload.toJson());
391
542
  const [rpCertEncoded] = signedAuthorizationRequest.signer.x5c;
@@ -405,5 +556,5 @@ const verifyOpenid4VpAuthorizationRequest = async (agentContext, { resolvedAutho
405
556
  };
406
557
 
407
558
  //#endregion
408
- export { EudiWalletExtensionsError, Ts12IntegrityError, URN_SCA_ACCOUNT_ACCESS, URN_SCA_EMANDATE, URN_SCA_LOGIN_RISK, URN_SCA_PAYMENT, resolveTs12TransactionDisplayMetadata, ts12BuiltinSchemaValidators, verifyOpenid4VpAuthorizationRequest, zAccountAccessPayload, zEMandatePayload, zFunkeQesTransaction, zLoginPayload, zPaymentPayload, zScaAttestationExt, zScaTransactionDataTypeClaims, zScaTransactionDataTypeUiLabels, zTransactionData, zTransactionDataEntry, zTs12Transaction };
559
+ export { EudiWalletExtensionsError, Ts12IntegrityError, URN_SCA_GENERIC, URN_SCA_PAYMENT, baseMergeConfig, defaultValidator, mergeJson, resolveTs12TransactionDisplayMetadata, ts12BuiltinSchemaValidators, ts12MergeConfig, verifyOpenid4VpAuthorizationRequest, zFunkeQesTransaction, zGenericPayload, zPaymentPayload, zScaAttestationExt, zScaTransactionDataTypeClaims, zScaTransactionDataTypeUiLabels, zTransactionData, zTransactionDataEntry, zTs12FallbackTransaction, zTs12GenericTransaction, zTs12PaymentTransaction, zTs12Transaction };
409
560
  //# sourceMappingURL=index.mjs.map