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.mjs CHANGED
@@ -113,10 +113,19 @@ import * as v2 from "valibot";
113
113
  // src/u-dcql.ts
114
114
  import * as v from "valibot";
115
115
  var idRegex = /^[a-zA-Z0-9_-]+$/;
116
+ function asNonEmptyArrayOrUndefined(array8) {
117
+ return array8.length > 0 ? array8 : void 0;
118
+ }
119
+ function isNonEmptyArray(array8) {
120
+ return array8.length > 0;
121
+ }
116
122
  var vNonEmptyArray = (item) => {
117
123
  return v.pipe(
118
- v.array(item),
119
- v.custom((input) => input.length > 0)
124
+ v.array(item, (i) => `Expected input to be an array, but received '${i.received}'`),
125
+ v.custom(
126
+ (input) => input.length > 0,
127
+ "Array must be non-empty and have length of at least 1"
128
+ )
120
129
  );
121
130
  };
122
131
  var vIncludesAll = (subset) => {
@@ -191,60 +200,71 @@ var vStringToJson = v.rawTransform(({ dataset, addIssue, NEVER }) => {
191
200
  });
192
201
 
193
202
  // src/dcql-query/m-dcql-trusted-authorities.ts
194
- var getTrustedAuthorityParser = (trustedAuthority) => v2.object(
195
- {
196
- type: v2.literal(
197
- trustedAuthority.type,
198
- (i) => `Expected trusted authority type to be '${trustedAuthority.type}' but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
199
- ),
200
- value: v2.union(
201
- trustedAuthority.values.map(
202
- (value) => v2.literal(
203
- value,
204
- (i) => `Expected trusted authority value to be '${value}' but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
205
- )
203
+ var getTrustedAuthorityParser = (trustedAuthority) => v2.pipe(
204
+ v2.object(
205
+ {
206
+ type: v2.literal(
207
+ trustedAuthority.type,
208
+ (i) => `Expected trusted authority type to be '${trustedAuthority.type}' but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
206
209
  ),
207
- (i) => `Expected trusted authority value to be '${trustedAuthority.values.join("' | '")}' but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
208
- )
209
- },
210
- `Expected trusted authority object with type '${trustedAuthority.type}' to be defined, but received undefined`
210
+ // Some trusted authorities support an array as input type
211
+ values: v2.pipe(
212
+ vNonEmptyArray(v2.string()),
213
+ v2.someItem(
214
+ (item) => trustedAuthority.values.includes(item),
215
+ (i) => `Expected one of the trusted authority values to be '${trustedAuthority.values.join("' | '")}' but received '${i.input.join("' , '")}'`
216
+ )
217
+ )
218
+ },
219
+ `Expected trusted authority object with type '${trustedAuthority.type}' to be defined, but received undefined`
220
+ ),
221
+ v2.transform(({ values, ...rest }) => ({
222
+ ...rest,
223
+ value: values.find((value) => trustedAuthority.values.includes(value))
224
+ }))
211
225
  );
212
226
  var vAuthorityKeyIdentifier = v2.object({
213
227
  type: v2.literal("aki"),
214
- value: v2.pipe(
215
- v2.string(),
216
- vBase64url,
217
- v2.description(
218
- "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."
228
+ values: vNonEmptyArray(
229
+ v2.pipe(
230
+ v2.string("aki trusted authority value must be a string"),
231
+ vBase64url,
232
+ v2.description(
233
+ "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."
234
+ )
219
235
  )
220
236
  )
221
237
  });
222
238
  var vEtsiTrustedList = v2.object({
223
239
  type: v2.literal("etsi_tl"),
224
- value: v2.pipe(
225
- v2.string(),
226
- v2.url(),
227
- v2.check(
228
- (url2) => url2.startsWith("http://") || url2.startsWith("https://"),
229
- "etsi_tl trusted authority value must be a valid https url"
230
- ),
231
- v2.description(
232
- "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."
240
+ values: vNonEmptyArray(
241
+ v2.pipe(
242
+ v2.string("etsi_tl trusted authority value must be a string"),
243
+ v2.url("etsi_tl trusted authority value must be a valid https url"),
244
+ v2.check(
245
+ (url2) => url2.startsWith("http://") || url2.startsWith("https://"),
246
+ "etsi_tl trusted authority value must be a valid https url"
247
+ ),
248
+ v2.description(
249
+ "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."
250
+ )
233
251
  )
234
252
  )
235
253
  });
236
254
  var vOpenidFederation = v2.object({
237
255
  type: v2.literal("openid_federation"),
238
- value: v2.pipe(
239
- v2.string(),
240
- v2.url(),
241
- // TODO: should we have a config similar to oid4vc-ts to support http for development?
242
- v2.check(
243
- (url2) => url2.startsWith("http://") || url2.startsWith("https://"),
244
- "openid_federation trusted authority value must be a valid https url"
245
- ),
246
- v2.description(
247
- "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."
256
+ values: vNonEmptyArray(
257
+ v2.pipe(
258
+ v2.string("openid_federation trusted authority value must be a string"),
259
+ v2.url("openid_federation trusted authority value must be a valid https url"),
260
+ // TODO: should we have a config similar to oid4vc-ts to support http for development?
261
+ v2.check(
262
+ (url2) => url2.startsWith("http://") || url2.startsWith("https://"),
263
+ "openid_federation trusted authority value must be a valid https url"
264
+ ),
265
+ v2.description(
266
+ "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."
267
+ )
248
268
  )
249
269
  )
250
270
  });
@@ -260,7 +280,7 @@ var DcqlTrustedAuthoritiesQuery;
260
280
  )
261
281
  ),
262
282
  values: v2.pipe(
263
- vNonEmptyArray(authority.entries.value),
283
+ vNonEmptyArray(authority.entries.values.item),
264
284
  v2.description(
265
285
  "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."
266
286
  )
@@ -646,17 +666,17 @@ var runClaimsQuery = (credentialQuery, ctx) => {
646
666
  if (!credentialQuery.claims) {
647
667
  return {
648
668
  success: true,
649
- valid_claims: [],
650
- failed_claims: [],
669
+ valid_claims: void 0,
670
+ failed_claims: void 0,
651
671
  valid_claim_sets: [
652
672
  {
653
673
  claim_set_index: void 0,
654
674
  output: {},
655
675
  success: true,
656
- valid_claim_indexes: []
676
+ valid_claim_indexes: void 0
657
677
  }
658
678
  ],
659
- failed_claim_sets: []
679
+ failed_claim_sets: void 0
660
680
  };
661
681
  }
662
682
  const failedClaims = [];
@@ -708,7 +728,7 @@ var runClaimsQuery = (credentialQuery, ctx) => {
708
728
  success: true,
709
729
  claim_set_index: claimSetIndex,
710
730
  output,
711
- valid_claim_indexes: claims.map((claim) => claim.claim_index)
731
+ valid_claim_indexes: asNonEmptyArrayOrUndefined(claims.map((claim) => claim.claim_index))
712
732
  });
713
733
  } else {
714
734
  const issues = failedClaims.reduce((merged, claim) => deepMerge(claim.issues, merged), {});
@@ -717,24 +737,26 @@ var runClaimsQuery = (credentialQuery, ctx) => {
717
737
  issues,
718
738
  claim_set_index: claimSetIndex,
719
739
  failed_claim_indexes: claims.filter((claim) => !claim.success).map((claim) => claim.claim_index),
720
- valid_claim_indexes: claims.filter((claim) => claim.success).map((claim) => claim.claim_index)
740
+ valid_claim_indexes: asNonEmptyArrayOrUndefined(
741
+ claims.filter((claim) => claim.success).map((claim) => claim.claim_index)
742
+ )
721
743
  });
722
744
  }
723
745
  }
724
- if (validClaimSets.length === 0) {
746
+ if (isNonEmptyArray(validClaimSets)) {
725
747
  return {
726
- success: false,
727
- failed_claim_sets: failedClaimSets,
728
- failed_claims: failedClaims.map(({ parser, ...rest }) => rest),
729
- valid_claims: validClaims.map(({ parser, ...rest }) => rest)
748
+ success: true,
749
+ failed_claim_sets: asNonEmptyArrayOrUndefined(failedClaimSets),
750
+ valid_claim_sets: validClaimSets,
751
+ valid_claims: asNonEmptyArrayOrUndefined(validClaims.map(({ parser, ...rest }) => rest)),
752
+ failed_claims: asNonEmptyArrayOrUndefined(failedClaims.map(({ parser, ...rest }) => rest))
730
753
  };
731
754
  }
732
755
  return {
733
- success: true,
756
+ success: false,
734
757
  failed_claim_sets: failedClaimSets,
735
- valid_claim_sets: validClaimSets,
736
- valid_claims: validClaims.map(({ parser, ...rest }) => rest),
737
- failed_claims: failedClaims.map(({ parser, ...rest }) => rest)
758
+ failed_claims: failedClaims.map(({ parser, ...rest }) => rest),
759
+ valid_claims: asNonEmptyArrayOrUndefined(validClaims.map(({ parser, ...rest }) => rest))
738
760
  };
739
761
  };
740
762
 
@@ -839,7 +861,7 @@ var runTrustedAuthoritiesQuery = (credentialQuery, credential) => {
839
861
  trusted_authority_index: trustedAuthorityIndex,
840
862
  output: parseResult.output
841
863
  },
842
- failed_trusted_authorities: failedTrustedAuthorities
864
+ failed_trusted_authorities: asNonEmptyArrayOrUndefined(failedTrustedAuthorities)
843
865
  };
844
866
  }
845
867
  const issues = v9.flatten(parseResult.issues);
@@ -859,12 +881,6 @@ var runTrustedAuthoritiesQuery = (credentialQuery, credential) => {
859
881
  // src/dcql-parser/dcql-credential-query-result.ts
860
882
  var runCredentialQuery = (credentialQuery, ctx) => {
861
883
  const { credentials, presentation } = ctx;
862
- if (ctx.credentials.length === 0) {
863
- throw new DcqlError({
864
- message: "Credentials array provided to credential query has length of 0, unable to match credentials against credential query.",
865
- code: "BAD_REQUEST"
866
- });
867
- }
868
884
  const validCredentials = [];
869
885
  const failedCredentials = [];
870
886
  for (const [credentialIndex, credential] of credentials.entries()) {
@@ -889,21 +905,20 @@ var runCredentialQuery = (credentialQuery, ctx) => {
889
905
  });
890
906
  }
891
907
  }
892
- if (!validCredentials.length) {
908
+ if (isNonEmptyArray(validCredentials)) {
893
909
  return {
894
- success: false,
910
+ success: true,
895
911
  credential_query_id: credentialQuery.id,
896
- // We now for sure that there's at least one invalid credential if there's no valid one.
897
- failed_credentials: failedCredentials,
898
- valid_credentials: void 0
912
+ failed_credentials: asNonEmptyArrayOrUndefined(failedCredentials),
913
+ valid_credentials: validCredentials
899
914
  };
900
915
  }
901
916
  return {
902
- success: true,
917
+ success: false,
903
918
  credential_query_id: credentialQuery.id,
904
- failed_credentials: failedCredentials,
905
- // We now for sure that there's at least one valid credential due to the length check
906
- valid_credentials: validCredentials
919
+ // Can be undefined if no credentials were provided to the query
920
+ failed_credentials: asNonEmptyArrayOrUndefined(failedCredentials),
921
+ valid_credentials: void 0
907
922
  };
908
923
  };
909
924
 
@@ -1094,7 +1109,7 @@ var DcqlClaimsResult;
1094
1109
  claim_set_index: v12.union([v12.number(), v12.undefined()]),
1095
1110
  // We use indexes because if there are no claim sets, the ids can be undefined
1096
1111
  // Can be empty array in case there are no claims
1097
- valid_claim_indexes: v12.array(v12.number()),
1112
+ valid_claim_indexes: v12.optional(vNonEmptyArray(v12.number())),
1098
1113
  failed_claim_indexes: v12.optional(v12.undefined()),
1099
1114
  output: vClaimsOutput
1100
1115
  });
@@ -1103,20 +1118,20 @@ var DcqlClaimsResult;
1103
1118
  // Undefined in case of no claim set
1104
1119
  claim_set_index: v12.union([v12.number(), v12.undefined()]),
1105
1120
  // We use indexes because if there are no claim sets, the ids can be undefined
1106
- valid_claim_indexes: v12.array(v12.number()),
1121
+ valid_claim_indexes: v12.optional(vNonEmptyArray(v12.number())),
1107
1122
  failed_claim_indexes: vNonEmptyArray(v12.number()),
1108
1123
  issues: v12.record(v12.string(), v12.unknown())
1109
1124
  });
1110
1125
  DcqlClaimsResult2.vClaimsSuccessResult = v12.object({
1111
1126
  success: v12.literal(true),
1112
- valid_claims: v12.array(DcqlClaimsResult2.vClaimsEntrySuccessResult),
1113
- failed_claims: v12.array(DcqlClaimsResult2.vClaimsEntryFailureResult),
1127
+ valid_claims: v12.optional(vNonEmptyArray(DcqlClaimsResult2.vClaimsEntrySuccessResult)),
1128
+ failed_claims: v12.optional(vNonEmptyArray(DcqlClaimsResult2.vClaimsEntryFailureResult)),
1114
1129
  valid_claim_sets: vNonEmptyArray(DcqlClaimsResult2.vClaimSetSuccessResult),
1115
- failed_claim_sets: v12.array(DcqlClaimsResult2.vClaimSetFailureResult)
1130
+ failed_claim_sets: v12.optional(vNonEmptyArray(DcqlClaimsResult2.vClaimSetFailureResult))
1116
1131
  });
1117
1132
  DcqlClaimsResult2.vClaimsFailureResult = v12.object({
1118
1133
  success: v12.literal(false),
1119
- valid_claims: v12.array(DcqlClaimsResult2.vClaimsEntrySuccessResult),
1134
+ valid_claims: v12.optional(vNonEmptyArray(DcqlClaimsResult2.vClaimsEntrySuccessResult)),
1120
1135
  failed_claims: vNonEmptyArray(DcqlClaimsResult2.vClaimsEntryFailureResult),
1121
1136
  valid_claim_sets: v12.optional(v12.undefined()),
1122
1137
  failed_claim_sets: vNonEmptyArray(DcqlClaimsResult2.vClaimSetFailureResult)
@@ -1151,7 +1166,16 @@ var DcqlTrustedAuthoritiesResult;
1151
1166
  DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntrySuccessResult = v14.object({
1152
1167
  success: v14.literal(true),
1153
1168
  trusted_authority_index: v14.number(),
1154
- output: DcqlCredentialTrustedAuthority.vModel
1169
+ // We map from values (multiple options for a credential/query) to value (the matching option)
1170
+ output: v14.variant(
1171
+ "type",
1172
+ DcqlCredentialTrustedAuthority.vModel.options.map(
1173
+ (o) => v14.object({
1174
+ type: o.entries.type,
1175
+ value: o.entries.values.item
1176
+ })
1177
+ )
1178
+ )
1155
1179
  });
1156
1180
  DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntryFailureResult = v14.object({
1157
1181
  success: v14.literal(false),
@@ -1169,7 +1193,7 @@ var DcqlTrustedAuthoritiesResult;
1169
1193
  v14.object({
1170
1194
  success: v14.literal(true),
1171
1195
  valid_trusted_authority: DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntrySuccessResult,
1172
- failed_trusted_authorities: v14.array(DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntryFailureResult)
1196
+ failed_trusted_authorities: v14.optional(vNonEmptyArray(DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntryFailureResult))
1173
1197
  })
1174
1198
  ]);
1175
1199
  DcqlTrustedAuthoritiesResult2.vTrustedAuthorityFailureResult = v14.object({
@@ -1203,13 +1227,13 @@ var DcqlQueryResult;
1203
1227
  success: v15.literal(true),
1204
1228
  credential_query_id: vIdString,
1205
1229
  valid_credentials: vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialSuccessResult),
1206
- failed_credentials: v15.array(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult)
1230
+ failed_credentials: v15.optional(vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult))
1207
1231
  }),
1208
1232
  v15.object({
1209
1233
  success: v15.literal(false),
1210
1234
  credential_query_id: vIdString,
1211
1235
  valid_credentials: v15.optional(v15.undefined()),
1212
- failed_credentials: vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult)
1236
+ failed_credentials: v15.optional(vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult))
1213
1237
  })
1214
1238
  ]);
1215
1239
  DcqlQueryResult2.vCredentialQueryResult = v15.record(vIdString, DcqlQueryResult2.vCredentialQueryItemResult);
@@ -1285,7 +1309,7 @@ var DcqlPresentationResult;
1285
1309
  const dqclQueryMatched = (
1286
1310
  // We require that all the submitted presentations match with the queries
1287
1311
  // So we must have success for all queries, and we don't allow failed_credentials
1288
- queriesResults.every((result) => result.success && result.failed_credentials.length === 0) && (credentialSetResults ? credentialSetResults.every((set) => !set.required || set.matching_options) : (
1312
+ queriesResults.every((result) => result.success && !result.failed_credentials) && (credentialSetResults ? credentialSetResults.every((set) => !set.required || set.matching_options) : (
1289
1313
  // If not credential_sets are used, we require that at least every credential has a match
1290
1314
  dcqlQuery.credentials.every(
1291
1315
  (credentialQuery) => queriesResults.find((result) => result.credential_query_id === credentialQuery.id)?.success