dcql 0.4.3-alpha-20250706143223 → 0.5.0-alpha-20250724114402

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.js CHANGED
@@ -177,10 +177,19 @@ var v2 = __toESM(require("valibot"));
177
177
  // src/u-dcql.ts
178
178
  var v = __toESM(require("valibot"));
179
179
  var idRegex = /^[a-zA-Z0-9_-]+$/;
180
+ function asNonEmptyArrayOrUndefined(array8) {
181
+ return array8.length > 0 ? array8 : void 0;
182
+ }
183
+ function isNonEmptyArray(array8) {
184
+ return array8.length > 0;
185
+ }
180
186
  var vNonEmptyArray = (item) => {
181
187
  return v.pipe(
182
- v.array(item),
183
- v.custom((input) => input.length > 0)
188
+ v.array(item, (i) => `Expected input to be an array, but received '${i.received}'`),
189
+ v.custom(
190
+ (input) => input.length > 0,
191
+ "Array must be non-empty and have length of at least 1"
192
+ )
184
193
  );
185
194
  };
186
195
  var vIncludesAll = (subset) => {
@@ -255,60 +264,71 @@ var vStringToJson = v.rawTransform(({ dataset, addIssue, NEVER }) => {
255
264
  });
256
265
 
257
266
  // src/dcql-query/m-dcql-trusted-authorities.ts
258
- var getTrustedAuthorityParser = (trustedAuthority) => v2.object(
259
- {
260
- type: v2.literal(
261
- trustedAuthority.type,
262
- (i) => `Expected trusted authority type to be '${trustedAuthority.type}' but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
263
- ),
264
- value: v2.union(
265
- trustedAuthority.values.map(
266
- (value) => v2.literal(
267
- value,
268
- (i) => `Expected trusted authority value to be '${value}' but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
269
- )
267
+ var getTrustedAuthorityParser = (trustedAuthority) => v2.pipe(
268
+ v2.object(
269
+ {
270
+ type: v2.literal(
271
+ trustedAuthority.type,
272
+ (i) => `Expected trusted authority type to be '${trustedAuthority.type}' but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
270
273
  ),
271
- (i) => `Expected trusted authority value to be '${trustedAuthority.values.join("' | '")}' but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
272
- )
273
- },
274
- `Expected trusted authority object with type '${trustedAuthority.type}' to be defined, but received undefined`
274
+ // Some trusted authorities support an array as input type
275
+ values: v2.pipe(
276
+ vNonEmptyArray(v2.string()),
277
+ v2.someItem(
278
+ (item) => trustedAuthority.values.includes(item),
279
+ (i) => `Expected one of the trusted authority values to be '${trustedAuthority.values.join("' | '")}' but received '${i.input.join("' , '")}'`
280
+ )
281
+ )
282
+ },
283
+ `Expected trusted authority object with type '${trustedAuthority.type}' to be defined, but received undefined`
284
+ ),
285
+ v2.transform(({ values, ...rest }) => ({
286
+ ...rest,
287
+ value: values.find((value) => trustedAuthority.values.includes(value))
288
+ }))
275
289
  );
276
290
  var vAuthorityKeyIdentifier = v2.object({
277
291
  type: v2.literal("aki"),
278
- value: v2.pipe(
279
- v2.string(),
280
- vBase64url,
281
- v2.description(
282
- "Contains the KeyIdentifier of the AuthorityKeyIdentifier as defined in Section 4.2.1.1 of [RFC5280], encoded as base64url. The raw byte representation of this element MUST match with the AuthorityKeyIdentifier element of an X.509 certificate in the certificate chain present in the credential (e.g., in the header of an mdoc or SD-JWT). Note that the chain can consist of a single certificate and the credential can include the entire X.509 chain or parts of it."
292
+ values: vNonEmptyArray(
293
+ v2.pipe(
294
+ v2.string("aki trusted authority value must be a string"),
295
+ vBase64url,
296
+ v2.description(
297
+ "Contains a list of KeyIdentifier entries of the AuthorityKeyIdentifier as defined in Section 4.2.1.1 of [RFC5280], encoded as base64url. The raw byte representation of one of the elements MUST match with the AuthorityKeyIdentifier element of an X.509 certificate in the certificate chain present in the credential (e.g., in the header of an mdoc or SD-JWT). Note that the chain can consist of a single certificate and the credential can include the entire X.509 chain or parts of it."
298
+ )
283
299
  )
284
300
  )
285
301
  });
286
302
  var vEtsiTrustedList = v2.object({
287
303
  type: v2.literal("etsi_tl"),
288
- value: v2.pipe(
289
- v2.string(),
290
- v2.url(),
291
- v2.check(
292
- (url2) => url2.startsWith("http://") || url2.startsWith("https://"),
293
- "etsi_tl trusted authority value must be a valid https url"
294
- ),
295
- v2.description(
296
- "The identifier of a Trusted List as specified in ETSI TS 119 612 [ETSI.TL]. An ETSI Trusted List contains references to other Trusted Lists, creating a list of trusted lists, or entries for Trust Service Providers with corresponding service description and X.509 Certificates. The trust chain of a matching Credential MUST contain at least one X.509 Certificate that matches one of the entries of the Trusted List or its cascading Trusted Lists."
304
+ values: vNonEmptyArray(
305
+ v2.pipe(
306
+ v2.string("etsi_tl trusted authority value must be a string"),
307
+ v2.url("etsi_tl trusted authority value must be a valid https url"),
308
+ v2.check(
309
+ (url2) => url2.startsWith("http://") || url2.startsWith("https://"),
310
+ "etsi_tl trusted authority value must be a valid https url"
311
+ ),
312
+ v2.description(
313
+ "The identifier of a Trusted List as specified in ETSI TS 119 612 [ETSI.TL]. An ETSI Trusted List contains references to other Trusted Lists, creating a list of trusted lists, or entries for Trust Service Providers with corresponding service description and X.509 Certificates. The trust chain of a matching Credential MUST contain at least one X.509 Certificate that matches one of the entries of the Trusted List or its cascading Trusted Lists."
314
+ )
297
315
  )
298
316
  )
299
317
  });
300
318
  var vOpenidFederation = v2.object({
301
319
  type: v2.literal("openid_federation"),
302
- value: v2.pipe(
303
- v2.string(),
304
- v2.url(),
305
- // TODO: should we have a config similar to oid4vc-ts to support http for development?
306
- v2.check(
307
- (url2) => url2.startsWith("http://") || url2.startsWith("https://"),
308
- "openid_federation trusted authority value must be a valid https url"
309
- ),
310
- v2.description(
311
- "The Entity Identifier as defined in Section 1 of [OpenID.Federation] that is bound to an entity in a federation. While this Entity Identifier could be any entity in that ecosystem, this entity would usually have the Entity Configuration of a Trust Anchor. A valid trust path, including the given Entity Identifier, must be constructible from a matching credential."
320
+ values: vNonEmptyArray(
321
+ v2.pipe(
322
+ v2.string("openid_federation trusted authority value must be a string"),
323
+ v2.url("openid_federation trusted authority value must be a valid https url"),
324
+ // TODO: should we have a config similar to oid4vc-ts to support http for development?
325
+ v2.check(
326
+ (url2) => url2.startsWith("http://") || url2.startsWith("https://"),
327
+ "openid_federation trusted authority value must be a valid https url"
328
+ ),
329
+ v2.description(
330
+ "The Entity Identifier as defined in Section 1 of [OpenID.Federation] that is bound to an entity in a federation. While this Entity Identifier could be any entity in that ecosystem, this entity would usually have the Entity Configuration of a Trust Anchor. A valid trust path, including the given Entity Identifier, must be constructible from a matching credential."
331
+ )
312
332
  )
313
333
  )
314
334
  });
@@ -324,7 +344,7 @@ var DcqlTrustedAuthoritiesQuery;
324
344
  )
325
345
  ),
326
346
  values: v2.pipe(
327
- vNonEmptyArray(authority.entries.value),
347
+ vNonEmptyArray(authority.entries.values.item),
328
348
  v2.description(
329
349
  "REQUIRED. An array of strings, where each string (value) contains information specific to the used Trusted Authorities Query type that allows to identify an issuer, trust framework, or a federation that an issuer belongs to."
330
350
  )
@@ -710,17 +730,17 @@ var runClaimsQuery = (credentialQuery, ctx) => {
710
730
  if (!credentialQuery.claims) {
711
731
  return {
712
732
  success: true,
713
- valid_claims: [],
714
- failed_claims: [],
733
+ valid_claims: void 0,
734
+ failed_claims: void 0,
715
735
  valid_claim_sets: [
716
736
  {
717
737
  claim_set_index: void 0,
718
738
  output: {},
719
739
  success: true,
720
- valid_claim_indexes: []
740
+ valid_claim_indexes: void 0
721
741
  }
722
742
  ],
723
- failed_claim_sets: []
743
+ failed_claim_sets: void 0
724
744
  };
725
745
  }
726
746
  const failedClaims = [];
@@ -772,7 +792,7 @@ var runClaimsQuery = (credentialQuery, ctx) => {
772
792
  success: true,
773
793
  claim_set_index: claimSetIndex,
774
794
  output,
775
- valid_claim_indexes: claims.map((claim) => claim.claim_index)
795
+ valid_claim_indexes: asNonEmptyArrayOrUndefined(claims.map((claim) => claim.claim_index))
776
796
  });
777
797
  } else {
778
798
  const issues = failedClaims.reduce((merged, claim) => deepMerge(claim.issues, merged), {});
@@ -781,24 +801,26 @@ var runClaimsQuery = (credentialQuery, ctx) => {
781
801
  issues,
782
802
  claim_set_index: claimSetIndex,
783
803
  failed_claim_indexes: claims.filter((claim) => !claim.success).map((claim) => claim.claim_index),
784
- valid_claim_indexes: claims.filter((claim) => claim.success).map((claim) => claim.claim_index)
804
+ valid_claim_indexes: asNonEmptyArrayOrUndefined(
805
+ claims.filter((claim) => claim.success).map((claim) => claim.claim_index)
806
+ )
785
807
  });
786
808
  }
787
809
  }
788
- if (validClaimSets.length === 0) {
810
+ if (isNonEmptyArray(validClaimSets)) {
789
811
  return {
790
- success: false,
791
- failed_claim_sets: failedClaimSets,
792
- failed_claims: failedClaims.map(({ parser, ...rest }) => rest),
793
- valid_claims: validClaims.map(({ parser, ...rest }) => rest)
812
+ success: true,
813
+ failed_claim_sets: asNonEmptyArrayOrUndefined(failedClaimSets),
814
+ valid_claim_sets: validClaimSets,
815
+ valid_claims: asNonEmptyArrayOrUndefined(validClaims.map(({ parser, ...rest }) => rest)),
816
+ failed_claims: asNonEmptyArrayOrUndefined(failedClaims.map(({ parser, ...rest }) => rest))
794
817
  };
795
818
  }
796
819
  return {
797
- success: true,
820
+ success: false,
798
821
  failed_claim_sets: failedClaimSets,
799
- valid_claim_sets: validClaimSets,
800
- valid_claims: validClaims.map(({ parser, ...rest }) => rest),
801
- failed_claims: failedClaims.map(({ parser, ...rest }) => rest)
822
+ failed_claims: failedClaims.map(({ parser, ...rest }) => rest),
823
+ valid_claims: asNonEmptyArrayOrUndefined(validClaims.map(({ parser, ...rest }) => rest))
802
824
  };
803
825
  };
804
826
 
@@ -903,7 +925,7 @@ var runTrustedAuthoritiesQuery = (credentialQuery, credential) => {
903
925
  trusted_authority_index: trustedAuthorityIndex,
904
926
  output: parseResult.output
905
927
  },
906
- failed_trusted_authorities: failedTrustedAuthorities
928
+ failed_trusted_authorities: asNonEmptyArrayOrUndefined(failedTrustedAuthorities)
907
929
  };
908
930
  }
909
931
  const issues = v9.flatten(parseResult.issues);
@@ -923,12 +945,6 @@ var runTrustedAuthoritiesQuery = (credentialQuery, credential) => {
923
945
  // src/dcql-parser/dcql-credential-query-result.ts
924
946
  var runCredentialQuery = (credentialQuery, ctx) => {
925
947
  const { credentials, presentation } = ctx;
926
- if (ctx.credentials.length === 0) {
927
- throw new DcqlError({
928
- message: "Credentials array provided to credential query has length of 0, unable to match credentials against credential query.",
929
- code: "BAD_REQUEST"
930
- });
931
- }
932
948
  const validCredentials = [];
933
949
  const failedCredentials = [];
934
950
  for (const [credentialIndex, credential] of credentials.entries()) {
@@ -953,21 +969,20 @@ var runCredentialQuery = (credentialQuery, ctx) => {
953
969
  });
954
970
  }
955
971
  }
956
- if (!validCredentials.length) {
972
+ if (isNonEmptyArray(validCredentials)) {
957
973
  return {
958
- success: false,
974
+ success: true,
959
975
  credential_query_id: credentialQuery.id,
960
- // We now for sure that there's at least one invalid credential if there's no valid one.
961
- failed_credentials: failedCredentials,
962
- valid_credentials: void 0
976
+ failed_credentials: asNonEmptyArrayOrUndefined(failedCredentials),
977
+ valid_credentials: validCredentials
963
978
  };
964
979
  }
965
980
  return {
966
- success: true,
981
+ success: false,
967
982
  credential_query_id: credentialQuery.id,
968
- failed_credentials: failedCredentials,
969
- // We now for sure that there's at least one valid credential due to the length check
970
- valid_credentials: validCredentials
983
+ // Can be undefined if no credentials were provided to the query
984
+ failed_credentials: asNonEmptyArrayOrUndefined(failedCredentials),
985
+ valid_credentials: void 0
971
986
  };
972
987
  };
973
988
 
@@ -1158,7 +1173,7 @@ var DcqlClaimsResult;
1158
1173
  claim_set_index: v12.union([v12.number(), v12.undefined()]),
1159
1174
  // We use indexes because if there are no claim sets, the ids can be undefined
1160
1175
  // Can be empty array in case there are no claims
1161
- valid_claim_indexes: v12.array(v12.number()),
1176
+ valid_claim_indexes: v12.optional(vNonEmptyArray(v12.number())),
1162
1177
  failed_claim_indexes: v12.optional(v12.undefined()),
1163
1178
  output: vClaimsOutput
1164
1179
  });
@@ -1167,20 +1182,20 @@ var DcqlClaimsResult;
1167
1182
  // Undefined in case of no claim set
1168
1183
  claim_set_index: v12.union([v12.number(), v12.undefined()]),
1169
1184
  // We use indexes because if there are no claim sets, the ids can be undefined
1170
- valid_claim_indexes: v12.array(v12.number()),
1185
+ valid_claim_indexes: v12.optional(vNonEmptyArray(v12.number())),
1171
1186
  failed_claim_indexes: vNonEmptyArray(v12.number()),
1172
1187
  issues: v12.record(v12.string(), v12.unknown())
1173
1188
  });
1174
1189
  DcqlClaimsResult2.vClaimsSuccessResult = v12.object({
1175
1190
  success: v12.literal(true),
1176
- valid_claims: v12.array(DcqlClaimsResult2.vClaimsEntrySuccessResult),
1177
- failed_claims: v12.array(DcqlClaimsResult2.vClaimsEntryFailureResult),
1191
+ valid_claims: v12.optional(vNonEmptyArray(DcqlClaimsResult2.vClaimsEntrySuccessResult)),
1192
+ failed_claims: v12.optional(vNonEmptyArray(DcqlClaimsResult2.vClaimsEntryFailureResult)),
1178
1193
  valid_claim_sets: vNonEmptyArray(DcqlClaimsResult2.vClaimSetSuccessResult),
1179
- failed_claim_sets: v12.array(DcqlClaimsResult2.vClaimSetFailureResult)
1194
+ failed_claim_sets: v12.optional(vNonEmptyArray(DcqlClaimsResult2.vClaimSetFailureResult))
1180
1195
  });
1181
1196
  DcqlClaimsResult2.vClaimsFailureResult = v12.object({
1182
1197
  success: v12.literal(false),
1183
- valid_claims: v12.array(DcqlClaimsResult2.vClaimsEntrySuccessResult),
1198
+ valid_claims: v12.optional(vNonEmptyArray(DcqlClaimsResult2.vClaimsEntrySuccessResult)),
1184
1199
  failed_claims: vNonEmptyArray(DcqlClaimsResult2.vClaimsEntryFailureResult),
1185
1200
  valid_claim_sets: v12.optional(v12.undefined()),
1186
1201
  failed_claim_sets: vNonEmptyArray(DcqlClaimsResult2.vClaimSetFailureResult)
@@ -1215,7 +1230,16 @@ var DcqlTrustedAuthoritiesResult;
1215
1230
  DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntrySuccessResult = v14.object({
1216
1231
  success: v14.literal(true),
1217
1232
  trusted_authority_index: v14.number(),
1218
- output: DcqlCredentialTrustedAuthority.vModel
1233
+ // We map from values (multiple options for a credential/query) to value (the matching option)
1234
+ output: v14.variant(
1235
+ "type",
1236
+ DcqlCredentialTrustedAuthority.vModel.options.map(
1237
+ (o) => v14.object({
1238
+ type: o.entries.type,
1239
+ value: o.entries.values.item
1240
+ })
1241
+ )
1242
+ )
1219
1243
  });
1220
1244
  DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntryFailureResult = v14.object({
1221
1245
  success: v14.literal(false),
@@ -1233,7 +1257,7 @@ var DcqlTrustedAuthoritiesResult;
1233
1257
  v14.object({
1234
1258
  success: v14.literal(true),
1235
1259
  valid_trusted_authority: DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntrySuccessResult,
1236
- failed_trusted_authorities: v14.array(DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntryFailureResult)
1260
+ failed_trusted_authorities: v14.optional(vNonEmptyArray(DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntryFailureResult))
1237
1261
  })
1238
1262
  ]);
1239
1263
  DcqlTrustedAuthoritiesResult2.vTrustedAuthorityFailureResult = v14.object({
@@ -1267,13 +1291,13 @@ var DcqlQueryResult;
1267
1291
  success: v15.literal(true),
1268
1292
  credential_query_id: vIdString,
1269
1293
  valid_credentials: vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialSuccessResult),
1270
- failed_credentials: v15.array(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult)
1294
+ failed_credentials: v15.optional(vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult))
1271
1295
  }),
1272
1296
  v15.object({
1273
1297
  success: v15.literal(false),
1274
1298
  credential_query_id: vIdString,
1275
1299
  valid_credentials: v15.optional(v15.undefined()),
1276
- failed_credentials: vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult)
1300
+ failed_credentials: v15.optional(vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult))
1277
1301
  })
1278
1302
  ]);
1279
1303
  DcqlQueryResult2.vCredentialQueryResult = v15.record(vIdString, DcqlQueryResult2.vCredentialQueryItemResult);
@@ -1349,7 +1373,7 @@ var DcqlPresentationResult;
1349
1373
  const dqclQueryMatched = (
1350
1374
  // We require that all the submitted presentations match with the queries
1351
1375
  // So we must have success for all queries, and we don't allow failed_credentials
1352
- queriesResults.every((result) => result.success && result.failed_credentials.length === 0) && (credentialSetResults ? credentialSetResults.every((set) => !set.required || set.matching_options) : (
1376
+ queriesResults.every((result) => result.success && !result.failed_credentials) && (credentialSetResults ? credentialSetResults.every((set) => !set.required || set.matching_options) : (
1353
1377
  // If not credential_sets are used, we require that at least every credential has a match
1354
1378
  dcqlQuery.credentials.every(
1355
1379
  (credentialQuery) => queriesResults.find((result) => result.credential_query_id === credentialQuery.id)?.success