dcql 0.3.1-alpha-20250624142312 → 0.4.0-alpha-20250704223931

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,8 +113,11 @@ 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
- var vNonEmptyArray = () => {
117
- return v.custom((input) => input.length > 0);
116
+ var vNonEmptyArray = (item) => {
117
+ return v.pipe(
118
+ v.array(item),
119
+ v.custom((input) => input.length > 0)
120
+ );
118
121
  };
119
122
  var vIncludesAll = (subset) => {
120
123
  return v.custom(
@@ -188,6 +191,24 @@ var vStringToJson = v.rawTransform(({ dataset, addIssue, NEVER }) => {
188
191
  });
189
192
 
190
193
  // 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
+ )
206
+ ),
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`
211
+ );
191
212
  var vAuthorityKeyIdentifier = v2.object({
192
213
  type: v2.literal("aki"),
193
214
  value: v2.pipe(
@@ -239,8 +260,7 @@ var DcqlTrustedAuthoritiesQuery;
239
260
  )
240
261
  ),
241
262
  values: v2.pipe(
242
- v2.array(authority.entries.value),
243
- vNonEmptyArray(),
263
+ vNonEmptyArray(authority.entries.value),
244
264
  v2.description(
245
265
  "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."
246
266
  )
@@ -333,22 +353,6 @@ var DcqlCredential;
333
353
  DcqlSdJwtVcCredential.vModel,
334
354
  DcqlW3cVcCredential.vModel
335
355
  ]);
336
- DcqlCredential2.vParseSuccess = v4.object({
337
- success: v4.literal(true),
338
- typed: v4.literal(true),
339
- issues: v4.optional(v4.undefined()),
340
- input_credential_index: v4.number(),
341
- claim_set_index: v4.union([v4.number(), v4.undefined()]),
342
- output: DcqlCredential2.vModel
343
- });
344
- DcqlCredential2.vParseFailure = v4.object({
345
- success: v4.literal(false),
346
- typed: v4.boolean(),
347
- output: v4.unknown(),
348
- issues: v4.pipe(v4.array(v4.unknown()), vNonEmptyArray()),
349
- input_credential_index: v4.number(),
350
- claim_set_index: v4.union([v4.number(), v4.undefined()])
351
- });
352
356
  DcqlCredential2.model = new Model({ vModel: DcqlCredential2.vModel });
353
357
  })(DcqlCredential || (DcqlCredential = {}));
354
358
 
@@ -380,10 +384,7 @@ var DcqlCredentialPresentation;
380
384
  })(DcqlCredentialPresentation || (DcqlCredentialPresentation = {}));
381
385
 
382
386
  // src/dcql-presentation/m-dcql-presentation-result.ts
383
- import * as v12 from "valibot";
384
-
385
- // src/dcql-parser/dcql-credential-query-result.ts
386
- import * as v8 from "valibot";
387
+ import * as v16 from "valibot";
387
388
 
388
389
  // src/dcql-parser/dcql-claims-query-result.ts
389
390
  import * as v7 from "valibot";
@@ -402,8 +403,7 @@ var DcqlClaimsQuery;
402
403
  )
403
404
  ),
404
405
  path: v6.pipe(
405
- v6.array(DcqlClaimsQuery2.vPath),
406
- vNonEmptyArray(),
406
+ vNonEmptyArray(DcqlClaimsQuery2.vPath),
407
407
  v6.description(
408
408
  "A non-empty array representing a claims path pointer that specifies the path to a claim within the Verifiable Credential."
409
409
  )
@@ -476,304 +476,521 @@ var DcqlClaimsQuery;
476
476
  DcqlClaimsQuery2.vModel = v6.union([DcqlClaimsQuery2.vMdoc, DcqlClaimsQuery2.vW3cSdJwtVc]);
477
477
  })(DcqlClaimsQuery || (DcqlClaimsQuery = {}));
478
478
 
479
- // src/dcql-parser/dcql-claims-query-result.ts
480
- var getTrustedAuthorityValue = (credentialQuery) => v7.object({
481
- authority: credentialQuery.trusted_authorities ? v7.variant(
482
- "type",
483
- credentialQuery.trusted_authorities.map(
484
- (t) => v7.object({ type: v7.literal(t.type), value: v7.union(t.values.map((value) => v7.literal(value))) })
485
- ),
486
- `Credential query '${credentialQuery.id}' requires the credential to be issued by a trusted authority of type ${credentialQuery.trusted_authorities.map((t) => t.type).join(" | ")}, but none of the type or values match.`
487
- ) : v7.optional(DcqlCredentialTrustedAuthority.vModel)
488
- });
489
- var getClaimParser = (input) => {
490
- const { value, values } = input;
491
- if (value) {
492
- return vWithJT(v7.literal(value));
479
+ // src/util/deep-merge.ts
480
+ function deepMerge(source, target) {
481
+ let newTarget = target;
482
+ if (Object.getPrototypeOf(source) !== Object.prototype && !Array.isArray(source)) {
483
+ throw new DcqlError({
484
+ message: "source value provided to deepMerge is neither an array or object.",
485
+ code: "PARSE_ERROR"
486
+ });
493
487
  }
494
- if (values) {
495
- return vWithJT(v7.union(values.map((val) => v7.literal(val))));
488
+ if (Object.getPrototypeOf(target) !== Object.prototype && !Array.isArray(target)) {
489
+ throw new DcqlError({
490
+ message: "target value provided to deepMerge is neither an array or object.",
491
+ code: "PARSE_ERROR"
492
+ });
496
493
  }
497
- return v7.nonNullish(v7.any());
498
- };
499
- var getNamespacesParser = (claimsQueries) => {
500
- const claimsForNamespace = {};
501
- for (const claimQuery of claimsQueries) {
502
- const mdocPathQuery = v7.is(DcqlClaimsQuery.vMdocNamespace, claimQuery) ? {
503
- id: claimQuery.id,
504
- path: [claimQuery.namespace, claimQuery.claim_name],
505
- values: claimQuery.values
506
- } : claimQuery;
507
- const namespace = mdocPathQuery.path[0];
508
- if (claimsForNamespace[namespace]) {
509
- claimsForNamespace[namespace]?.push({ ...mdocPathQuery });
510
- } else {
511
- claimsForNamespace[namespace] = [{ ...mdocPathQuery }];
494
+ for (const [key, val] of Object.entries(source)) {
495
+ if (val !== null && typeof val === "object" && (Object.getPrototypeOf(val) === Object.prototype || Array.isArray(val))) {
496
+ const newValue = deepMerge(
497
+ val,
498
+ newTarget[key] ?? new (Object.getPrototypeOf(val)).constructor()
499
+ );
500
+ newTarget = setValue(newTarget, key, newValue);
501
+ } else if (val != null) {
502
+ newTarget = setValue(newTarget, key, val);
512
503
  }
513
504
  }
514
- const parsersForNamespaces = Object.entries(claimsForNamespace).map(([namespace, claims]) => {
515
- const claimParsers = Object.fromEntries(claims.map((claim) => [claim.path[1], getClaimParser(claim)]));
516
- return [namespace, v7.object(claimParsers)];
517
- });
518
- return v7.object(Object.fromEntries(parsersForNamespaces));
505
+ return newTarget;
506
+ }
507
+ function setValue(target, key, value) {
508
+ let newTarget = target;
509
+ if (Array.isArray(newTarget)) {
510
+ newTarget = [...newTarget];
511
+ newTarget[key] = value;
512
+ } else if (Object.getPrototypeOf(newTarget) === Object.prototype) {
513
+ newTarget = { ...newTarget, [key]: value };
514
+ } else {
515
+ throw new DcqlError({
516
+ message: "Unsupported type for deep merge. Only primitive types or Array and Object are supported",
517
+ code: "INTERNAL_SERVER_ERROR"
518
+ });
519
+ }
520
+ return newTarget;
521
+ }
522
+
523
+ // src/dcql-parser/dcql-claims-query-result.ts
524
+ var pathToString = (path) => path.map((item) => typeof item === "string" ? `'${item}'` : `${item}`).join(".");
525
+ var getClaimParser = (path, values) => {
526
+ if (values) {
527
+ return v7.union(
528
+ values.map(
529
+ (val) => v7.literal(
530
+ val,
531
+ (i) => `Expected claim ${pathToString(path)} to be ${typeof val === "string" ? `'${val}'` : val} but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
532
+ )
533
+ ),
534
+ (i) => `Expected claim ${pathToString(path)} to be ${values.map((v19) => typeof v19 === "string" ? `'${v19}'` : v19).join(" | ")} but received ${typeof i.input === "string" ? `'${i.input}'` : i.input}`
535
+ );
536
+ }
537
+ return v7.pipe(
538
+ v7.unknown(),
539
+ v7.check((value) => value !== null && value !== void 0, `Expected claim '${path.join("'.'")}' to be defined`)
540
+ );
541
+ };
542
+ var getMdocClaimParser = (claimQuery) => {
543
+ const mdocPathQuery = v7.is(DcqlClaimsQuery.vMdocNamespace, claimQuery) ? {
544
+ id: claimQuery.id,
545
+ path: [claimQuery.namespace, claimQuery.claim_name],
546
+ values: claimQuery.values
547
+ } : claimQuery;
548
+ const namespace = mdocPathQuery.path[0];
549
+ const field = mdocPathQuery.path[1];
550
+ return v7.object(
551
+ {
552
+ [namespace]: v7.object(
553
+ {
554
+ [field]: getClaimParser(mdocPathQuery.path, claimQuery.values)
555
+ },
556
+ `Expected claim ${pathToString(mdocPathQuery.path)} to be defined`
557
+ )
558
+ },
559
+ `Expected claim ${pathToString(mdocPathQuery.path)} to be defined`
560
+ );
519
561
  };
520
- var getClaimQueryParser = (claimQuery, ctx) => {
562
+ var getJsonClaimParser = (claimQuery, ctx) => {
521
563
  const { index, presentation } = ctx;
522
564
  const pathElement = claimQuery.path[index];
523
565
  const isLast = index === claimQuery.path.length - 1;
524
- const vClaimParser = getClaimParser(claimQuery);
566
+ const vClaimParser = getClaimParser(claimQuery.path, claimQuery.values);
525
567
  if (typeof pathElement === "number") {
526
- const elementParser = isLast ? vClaimParser : getClaimQueryParser(claimQuery, { ...ctx, index: index + 1 });
568
+ const elementParser = isLast ? vClaimParser : getJsonClaimParser(claimQuery, { ...ctx, index: index + 1 });
527
569
  if (presentation) {
528
- return v7.union([
529
- v7.pipe(
530
- v7.array(vJson),
531
- v7.length(1),
532
- v7.transform((input) => input[0]),
533
- elementParser
534
- ),
535
- elementParser
536
- ]);
570
+ return v7.pipe(
571
+ v7.array(v7.any(), `Expected path ${pathToString(claimQuery.path.slice(0, index + 1))} to be an array`),
572
+ v7.rawTransform(({ dataset, addIssue }) => {
573
+ const issues = [];
574
+ for (const item of dataset.value) {
575
+ const itemResult = v7.safeParse(elementParser, item);
576
+ if (itemResult.success) {
577
+ return dataset.value;
578
+ }
579
+ issues.push(itemResult.issues[0]);
580
+ }
581
+ addIssue({
582
+ ...issues[0],
583
+ message: isLast ? issues[0].message : `Expected any element in array ${pathToString(claimQuery.path.slice(0, index + 1))} to match sub requirement but none matched: ${issues[0].message}`
584
+ });
585
+ return dataset.value;
586
+ })
587
+ );
537
588
  }
538
589
  return v7.pipe(
539
- v7.array(vJson),
540
- v7.transform((input) => input[pathElement]),
541
- elementParser
590
+ v7.array(v7.any(), `Expected path ${pathToString(claimQuery.path.slice(0, index + 1))} to be an array`),
591
+ v7.rawTransform(({ addIssue, dataset, NEVER }) => {
592
+ const result = v7.safeParse(elementParser, dataset.value[pathElement]);
593
+ if (!result.success) {
594
+ addIssue(result.issues[0]);
595
+ return NEVER;
596
+ }
597
+ return [...dataset.value.slice(0, pathElement).map(() => null), result.output];
598
+ })
542
599
  );
543
600
  }
544
601
  if (typeof pathElement === "string") {
545
- return v7.object({
546
- [pathElement]: isLast ? vClaimParser : getClaimQueryParser(claimQuery, { ...ctx, index: index + 1 })
547
- });
602
+ return v7.object(
603
+ {
604
+ [pathElement]: isLast ? vClaimParser : getJsonClaimParser(claimQuery, { ...ctx, index: index + 1 })
605
+ },
606
+ `Expected claim ${pathToString(claimQuery.path)} to be defined`
607
+ );
548
608
  }
549
- return isLast ? v7.array(vClaimParser) : v7.array(getClaimQueryParser(claimQuery, { ...ctx, index: index + 1 }));
550
- };
551
- var getJsonClaimsParser = (claimsQueries, ctx) => {
552
- const claimParser = v7.intersect(
553
- claimsQueries.map(
554
- (claimQuery) => getClaimQueryParser(claimQuery, {
555
- ...ctx,
556
- index: 0
557
- })
558
- )
609
+ return v7.pipe(
610
+ v7.array(v7.any(), `Expected path ${pathToString(claimQuery.path.slice(0, index + 1))} to be an array`),
611
+ v7.rawTransform(({ addIssue, dataset, NEVER }) => {
612
+ const mapped = dataset.value.map((item) => {
613
+ const parsed = v7.safeParse(
614
+ isLast ? vClaimParser : getJsonClaimParser(claimQuery, { ...ctx, index: index + 1 }),
615
+ item
616
+ );
617
+ return parsed;
618
+ });
619
+ if (mapped.every((parsed) => !parsed.success)) {
620
+ for (const parsed of mapped) {
621
+ for (const issue of parsed.issues) {
622
+ addIssue(issue);
623
+ }
624
+ }
625
+ return NEVER;
626
+ }
627
+ return mapped.map((parsed) => parsed.success ? parsed.output : null);
628
+ })
559
629
  );
560
- return claimParser;
561
630
  };
562
- var getMdocClaimsQueriesForClaimSet = (claimsQueries, claimSet) => {
563
- return claimSet.map((credential_id) => {
564
- const query = claimsQueries.find((query2) => query2.id === credential_id);
565
- if (!query) {
566
- throw new DcqlInvalidClaimsQueryIdError({
567
- message: `Claims-query with id '${credential_id}' not found.`
631
+ var runClaimsQuery = (credentialQuery, ctx) => {
632
+ if (!credentialQuery.claims) {
633
+ return {
634
+ success: true,
635
+ valid_claims: [],
636
+ failed_claims: [],
637
+ valid_claim_sets: [
638
+ {
639
+ claim_set_index: void 0,
640
+ output: {},
641
+ success: true,
642
+ valid_claim_indexes: []
643
+ }
644
+ ],
645
+ failed_claim_sets: []
646
+ };
647
+ }
648
+ const failedClaims = [];
649
+ const validClaims = [];
650
+ for (const [claimIndex, claimQuery] of credentialQuery.claims.entries()) {
651
+ const parser = credentialQuery.format === "mso_mdoc" ? getMdocClaimParser(claimQuery) : getJsonClaimParser(claimQuery, {
652
+ index: 0,
653
+ presentation: ctx.presentation
654
+ });
655
+ const parseResult = v7.safeParse(
656
+ parser,
657
+ ctx.credential.credential_format === "mso_mdoc" ? ctx.credential.namespaces : ctx.credential.claims
658
+ );
659
+ if (parseResult.success) {
660
+ validClaims.push({
661
+ success: true,
662
+ claim_index: claimIndex,
663
+ claim_id: claimQuery.id,
664
+ output: parseResult.output,
665
+ parser
666
+ });
667
+ } else {
668
+ const flattened = v7.flatten(parseResult.issues);
669
+ failedClaims.push({
670
+ success: false,
671
+ issues: flattened.nested ?? flattened,
672
+ claim_index: claimIndex,
673
+ claim_id: claimQuery.id,
674
+ output: parseResult.output,
675
+ parser
568
676
  });
569
677
  }
570
- return query;
571
- });
572
- };
573
- var getJsonClaimsQueriesForClaimSet = (claimsQueries, claimSet) => {
574
- return claimSet.map((credential_id) => {
575
- const query = claimsQueries.find((query2) => query2.id === credential_id);
576
- if (!query) {
577
- throw new DcqlInvalidClaimsQueryIdError({
578
- message: `Claims-query with id '${credential_id}' not found.`
678
+ }
679
+ const failedClaimSets = [];
680
+ const validClaimSets = [];
681
+ for (const [claimSetIndex, claimSet] of credentialQuery.claim_sets?.entries() ?? [[void 0, void 0]]) {
682
+ const claims = claimSet?.map((id) => {
683
+ const claim = validClaims.find((claim2) => claim2.claim_id === id) ?? failedClaims.find((claim2) => claim2.claim_id === id);
684
+ if (!claim) {
685
+ throw new DcqlParseError({
686
+ message: `Claim with id '${id}' in query '${credentialQuery.id}' from claim set with index '${claimSetIndex}' not found in claims of claim`
687
+ });
688
+ }
689
+ return claim;
690
+ }) ?? [...validClaims, ...failedClaims];
691
+ if (claims.every((claim) => claim.success)) {
692
+ const output = claims.reduce((merged, claim) => deepMerge(claim.output, merged), {});
693
+ validClaimSets.push({
694
+ success: true,
695
+ claim_set_index: claimSetIndex,
696
+ output,
697
+ valid_claim_indexes: validClaims.map((claim) => claim.claim_index)
698
+ });
699
+ } else {
700
+ const issues = failedClaims.reduce((merged, claim) => deepMerge(claim.issues, merged), {});
701
+ failedClaimSets.push({
702
+ success: false,
703
+ issues,
704
+ claim_set_index: claimSetIndex,
705
+ failed_claim_indexes: failedClaims.map((claim) => claim.claim_index),
706
+ valid_claim_indexes: validClaims.map((claim) => claim.claim_index)
579
707
  });
580
708
  }
581
- return query;
582
- });
709
+ }
710
+ if (validClaimSets.length === 0) {
711
+ return {
712
+ success: false,
713
+ failed_claim_sets: failedClaimSets,
714
+ failed_claims: failedClaims.map(({ parser, ...rest }) => rest),
715
+ valid_claims: validClaims.map(({ parser, ...rest }) => rest)
716
+ };
717
+ }
718
+ return {
719
+ success: true,
720
+ failed_claim_sets: failedClaimSets,
721
+ valid_claim_sets: validClaimSets,
722
+ valid_claims: validClaims.map(({ parser, ...rest }) => rest),
723
+ failed_claims: failedClaims.map(({ parser, ...rest }) => rest)
724
+ };
583
725
  };
584
- var getMdocParser = (credentialQuery, ctx) => {
585
- const { claimSet } = ctx;
586
- const vDoctype = credentialQuery.meta?.doctype_value ? v7.literal(credentialQuery.meta.doctype_value) : v7.string();
587
- const claimSetQueries = credentialQuery.claims && claimSet ? getMdocClaimsQueriesForClaimSet(credentialQuery.claims, claimSet) : credentialQuery.claims;
588
- const credentialParser = v7.object({
589
- credential_format: v7.literal("mso_mdoc"),
590
- doctype: vDoctype,
591
- namespaces: claimSetQueries ? getNamespacesParser(claimSetQueries) : v7.record(v7.string(), v7.record(v7.string(), v7.unknown())),
592
- ...getTrustedAuthorityValue(credentialQuery).entries
726
+
727
+ // src/dcql-parser/dcql-meta-query-result.ts
728
+ import * as v8 from "valibot";
729
+ var getMdocMetaParser = (credentialQuery) => {
730
+ const vDoctype = credentialQuery.meta?.doctype_value ? v8.literal(
731
+ credentialQuery.meta.doctype_value,
732
+ (i) => `Expected doctype to be '${credentialQuery.meta?.doctype_value}' but received '${i.input}'`
733
+ ) : v8.string("Expected doctype to be defined");
734
+ const credentialParser = v8.object({
735
+ credential_format: v8.literal(
736
+ "mso_mdoc",
737
+ (i) => `Expected credential format to be 'mso_mdoc' but received '${i.input}'`
738
+ ),
739
+ doctype: vDoctype
593
740
  });
594
741
  return credentialParser;
595
742
  };
596
- var getW3cVcSdJwtVcParser = (credentialQuery, ctx) => {
597
- const { claimSet } = ctx;
598
- const claimSetQueries = credentialQuery.claims && claimSet ? getJsonClaimsQueriesForClaimSet(credentialQuery.claims, claimSet) : credentialQuery.claims;
599
- if (credentialQuery.format === "vc+sd-jwt" || credentialQuery.format === "dc+sd-jwt") {
600
- return v7.object({
601
- credential_format: v7.literal(credentialQuery.format),
602
- vct: credentialQuery.meta?.vct_values ? v7.picklist(credentialQuery.meta.vct_values) : v7.string(),
603
- claims: claimSetQueries ? getJsonClaimsParser(claimSetQueries, ctx) : vJsonRecord,
604
- // TODO: we should split up the:
605
- // - vct
606
- // - claim value
607
- // - trusted authorities
608
- // into separate validations so we can make the match result richer with specifically
609
- // which steps failed, also we should look at showing exactly which claim paths failed.
610
- ...getTrustedAuthorityValue(credentialQuery).entries
611
- });
743
+ var getSdJwtVcMetaParser = (credentialQuery) => {
744
+ return v8.object({
745
+ credential_format: v8.literal(
746
+ credentialQuery.format,
747
+ (i) => `Expected credential format to be '${credentialQuery.format}' but received '${i.input}'`
748
+ ),
749
+ vct: credentialQuery.meta?.vct_values ? v8.picklist(
750
+ credentialQuery.meta.vct_values,
751
+ (i) => `Expected vct to be '${credentialQuery.meta?.vct_values?.join("' | '")}' but received '${i.input}'`
752
+ ) : v8.string("Expected vct to be defined")
753
+ });
754
+ };
755
+ var getW3cVcMetaParser = (credentialQuery) => {
756
+ return v8.object({
757
+ credential_format: v8.literal(
758
+ credentialQuery.format,
759
+ (i) => `Expected credential format to be '${credentialQuery.format}' but received '${i.input}'`
760
+ ),
761
+ type: credentialQuery.meta?.type_values ? v8.union(
762
+ credentialQuery.meta.type_values.map((values) => vIncludesAll(values)),
763
+ `Expected type to include all values from one of the following subsets: ${credentialQuery.meta.type_values.map((values) => `[${values.join(", ")}]`).join(" | ")}`
764
+ ) : vNonEmptyArray(v8.string())
765
+ });
766
+ };
767
+ var getMetaParser = (credentialQuery) => {
768
+ if (credentialQuery.format === "mso_mdoc") {
769
+ return getMdocMetaParser(credentialQuery);
612
770
  }
613
- if (credentialQuery.format === "jwt_vc_json" || credentialQuery.format === "ldp_vc") {
614
- return v7.object({
615
- credential_format: v7.literal(credentialQuery.format),
616
- claims: claimSetQueries ? getJsonClaimsParser(claimSetQueries, ctx) : vJsonRecord,
617
- type: credentialQuery.meta?.type_values ? v7.union(
618
- credentialQuery.meta.type_values.map((values) => vIncludesAll(values)),
619
- `Type must include at least all values from one of the following subsets: ${credentialQuery.meta.type_values.map((values) => `[${values.join(", ")}]`).join(" | ")}`
620
- ) : v7.array(v7.string()),
621
- ...getTrustedAuthorityValue(credentialQuery).entries
622
- });
771
+ if (credentialQuery.format === "dc+sd-jwt" || credentialQuery.format === "vc+sd-jwt") {
772
+ return getSdJwtVcMetaParser(credentialQuery);
773
+ }
774
+ if (credentialQuery.format === "ldp_vc" || credentialQuery.format === "jwt_vc_json") {
775
+ return getW3cVcMetaParser(credentialQuery);
623
776
  }
624
777
  throw new DcqlError({
625
778
  code: "NOT_IMPLEMENTED",
626
779
  message: `Usupported format '${credentialQuery.format}'`
627
780
  });
628
781
  };
629
- var getCredentialQueryParser = (credentialQuery, ctx) => {
630
- if (credentialQuery.claim_sets && !ctx.claimSet) {
631
- throw new DcqlMissingClaimSetParseError({
632
- message: "credentialQuery specifies claim_sets but no claim_set for parsing is provided."
633
- });
782
+ var runMetaQuery = (credentialQuery, credential) => {
783
+ const metaParser = getMetaParser(credentialQuery);
784
+ const parseResult = v8.safeParse(metaParser, credential);
785
+ if (!parseResult.success) {
786
+ const issues = v8.flatten(parseResult.issues);
787
+ return {
788
+ success: false,
789
+ issues: issues.nested ?? issues,
790
+ output: parseResult.output
791
+ };
634
792
  }
635
- if (credentialQuery.format === "mso_mdoc") {
636
- return getMdocParser(credentialQuery, ctx);
793
+ return {
794
+ success: true,
795
+ output: parseResult.output
796
+ };
797
+ };
798
+
799
+ // src/dcql-parser/dcql-trusted-authorities-result.ts
800
+ import * as v9 from "valibot";
801
+ var runTrustedAuthoritiesQuery = (credentialQuery, credential) => {
802
+ if (!credentialQuery.trusted_authorities) {
803
+ return {
804
+ success: true
805
+ };
637
806
  }
638
- return getW3cVcSdJwtVcParser(credentialQuery, ctx);
807
+ const failedTrustedAuthorities = [];
808
+ for (const [trustedAuthorityIndex, trustedAuthority] of credentialQuery.trusted_authorities.entries()) {
809
+ const trustedAuthorityParser = getTrustedAuthorityParser(trustedAuthority);
810
+ const parseResult = v9.safeParse(trustedAuthorityParser, credential.authority);
811
+ if (parseResult.success) {
812
+ return {
813
+ success: true,
814
+ valid_trusted_authority: {
815
+ success: true,
816
+ trusted_authority_index: trustedAuthorityIndex,
817
+ output: parseResult.output
818
+ },
819
+ failed_trusted_authorities: failedTrustedAuthorities
820
+ };
821
+ }
822
+ const issues = v9.flatten(parseResult.issues);
823
+ failedTrustedAuthorities.push({
824
+ success: false,
825
+ trusted_authority_index: trustedAuthorityIndex,
826
+ issues: issues.nested ?? issues,
827
+ output: parseResult.output
828
+ });
829
+ }
830
+ return {
831
+ success: false,
832
+ failed_trusted_authorities: failedTrustedAuthorities
833
+ };
639
834
  };
640
835
 
641
836
  // src/dcql-parser/dcql-credential-query-result.ts
642
837
  var runCredentialQuery = (credentialQuery, ctx) => {
643
838
  const { credentials, presentation } = ctx;
644
- const claimSets = credentialQuery.claim_sets ?? [void 0];
645
- const credentialQueryResult = [];
646
- for (const [claimSetIndex, claim_set] of claimSets.entries()) {
647
- const credentialParser = getCredentialQueryParser(credentialQuery, {
648
- claimSet: claim_set,
649
- presentation
839
+ if (ctx.credentials.length === 0) {
840
+ throw new DcqlError({
841
+ message: "Credentials array provided to credential query has length of 0, unable to match credentials against credential query.",
842
+ code: "BAD_REQUEST"
650
843
  });
651
- const claimSetResult = [];
652
- for (const [credentialIndex, credential] of credentials.entries()) {
653
- if (claimSetIndex > 0) {
654
- const previous = credentialQueryResult[claimSetIndex - 1][credentialIndex];
655
- if (previous?.success || !previous) {
656
- claimSetResult[credentialIndex] = void 0;
657
- continue;
658
- }
659
- }
660
- const parseResult = v8.safeParse(credentialParser, credential);
661
- claimSetResult.push({
662
- ...parseResult,
663
- ...parseResult.issues && {
664
- flattened: v8.flatten(parseResult.issues)
665
- },
844
+ }
845
+ const validCredentials = [];
846
+ const failedCredentials = [];
847
+ for (const [credentialIndex, credential] of credentials.entries()) {
848
+ const trustedAuthorityResult = runTrustedAuthoritiesQuery(credentialQuery, credential);
849
+ const claimsResult = runClaimsQuery(credentialQuery, { credential, presentation });
850
+ const metaResult = runMetaQuery(credentialQuery, credential);
851
+ if (claimsResult.success && trustedAuthorityResult.success && metaResult.success) {
852
+ validCredentials.push({
853
+ success: true,
854
+ input_credential_index: credentialIndex,
855
+ trusted_authorities: trustedAuthorityResult,
856
+ meta: metaResult,
857
+ claims: claimsResult
858
+ });
859
+ } else {
860
+ failedCredentials.push({
861
+ success: false,
666
862
  input_credential_index: credentialIndex,
667
- claim_set_index: credentialQuery.claim_sets ? claimSetIndex : void 0
863
+ trusted_authorities: trustedAuthorityResult,
864
+ meta: metaResult,
865
+ claims: claimsResult
668
866
  });
669
867
  }
670
- credentialQueryResult.push(claimSetResult);
671
868
  }
672
- return credentialQueryResult;
869
+ if (!validCredentials.length) {
870
+ return {
871
+ success: false,
872
+ credential_query_id: credentialQuery.id,
873
+ // We now for sure that there's at least one invalid credential if there's no valid one.
874
+ failed_credentials: failedCredentials,
875
+ valid_credentials: void 0
876
+ };
877
+ }
878
+ return {
879
+ success: true,
880
+ credential_query_id: credentialQuery.id,
881
+ failed_credentials: failedCredentials,
882
+ // We now for sure that there's at least one valid credential due to the length check
883
+ valid_credentials: validCredentials
884
+ };
673
885
  };
674
886
 
675
887
  // src/dcql-query-result/m-dcql-query-result.ts
676
- import * as v11 from "valibot";
888
+ import * as v15 from "valibot";
677
889
 
678
890
  // src/dcql-query/m-dcql-credential-query.ts
679
- import * as v9 from "valibot";
891
+ import * as v10 from "valibot";
680
892
  var DcqlCredentialQuery;
681
893
  ((DcqlCredentialQuery2) => {
682
- const vBase = v9.object({
683
- id: v9.pipe(
684
- v9.string(),
685
- v9.regex(idRegex),
686
- v9.description(
894
+ const vBase = v10.object({
895
+ id: v10.pipe(
896
+ v10.string(),
897
+ v10.regex(idRegex),
898
+ v10.description(
687
899
  `REQUIRED. A string identifying the Credential in the response and, if provided, the constraints in 'credential_sets'.`
688
900
  )
689
901
  ),
690
- claim_sets: v9.pipe(
691
- v9.optional(v9.pipe(v9.array(v9.pipe(v9.array(vIdString), vNonEmptyArray())), vNonEmptyArray())),
692
- v9.description(
902
+ multiple: v10.pipe(
903
+ v10.optional(v10.boolean(), false),
904
+ v10.description(
905
+ "OPTIONAL. A boolean which indicates whether multiple Credentials can be returned for this Credential Query. If omitted, the default value is false."
906
+ )
907
+ ),
908
+ claim_sets: v10.pipe(
909
+ v10.optional(vNonEmptyArray(vNonEmptyArray(vIdString))),
910
+ v10.description(
693
911
  `OPTIONAL. A non-empty array containing arrays of identifiers for elements in 'claims' that specifies which combinations of 'claims' for the Credential are requested.`
694
912
  )
695
913
  ),
696
- trusted_authorities: v9.pipe(
697
- v9.optional(v9.pipe(v9.array(DcqlTrustedAuthoritiesQuery.vModel), vNonEmptyArray())),
698
- v9.description(
914
+ trusted_authorities: v10.pipe(
915
+ v10.optional(vNonEmptyArray(DcqlTrustedAuthoritiesQuery.vModel)),
916
+ v10.description(
699
917
  "OPTIONAL. A non-empty array of objects as defined in Section 6.1.1 that specifies expected authorities or trust frameworks that certify Issuers, that the Verifier will accept. Every Credential returned by the Wallet SHOULD match at least one of the conditions present in the corresponding trusted_authorities array if present."
700
918
  )
701
919
  )
702
920
  });
703
- DcqlCredentialQuery2.vMdoc = v9.object({
921
+ DcqlCredentialQuery2.vMdoc = v10.object({
704
922
  ...vBase.entries,
705
- format: v9.pipe(
706
- v9.literal("mso_mdoc"),
707
- v9.description("REQUIRED. A string that specifies the format of the requested Verifiable Credential.")
923
+ format: v10.pipe(
924
+ v10.literal("mso_mdoc"),
925
+ v10.description("REQUIRED. A string that specifies the format of the requested Verifiable Credential.")
708
926
  ),
709
- claims: v9.pipe(
710
- v9.optional(v9.pipe(v9.array(DcqlClaimsQuery.vMdoc), vNonEmptyArray())),
711
- v9.description("OPTIONAL. A non-empty array of objects as that specifies claims in the requested Credential.")
927
+ claims: v10.pipe(
928
+ v10.optional(vNonEmptyArray(DcqlClaimsQuery.vMdoc)),
929
+ v10.description("OPTIONAL. A non-empty array of objects as that specifies claims in the requested Credential.")
712
930
  ),
713
- meta: v9.pipe(
714
- v9.optional(
715
- v9.object({
716
- doctype_value: v9.pipe(
717
- v9.optional(v9.string()),
718
- v9.description(
931
+ meta: v10.pipe(
932
+ v10.optional(
933
+ v10.object({
934
+ doctype_value: v10.pipe(
935
+ v10.optional(v10.string()),
936
+ v10.description(
719
937
  "OPTIONAL. String that specifies an allowed value for the doctype of the requested Verifiable Credential."
720
938
  )
721
939
  )
722
940
  })
723
941
  ),
724
- v9.description(
942
+ v10.description(
725
943
  "OPTIONAL. An object defining additional properties requested by the Verifier that apply to the metadata and validity data of the Credential."
726
944
  )
727
945
  )
728
946
  });
729
- DcqlCredentialQuery2.vSdJwtVc = v9.object({
947
+ DcqlCredentialQuery2.vSdJwtVc = v10.object({
730
948
  ...vBase.entries,
731
- format: v9.pipe(
732
- v9.picklist(["vc+sd-jwt", "dc+sd-jwt"]),
733
- v9.description("REQUIRED. A string that specifies the format of the requested Verifiable Credential.")
949
+ format: v10.pipe(
950
+ v10.picklist(["vc+sd-jwt", "dc+sd-jwt"]),
951
+ v10.description("REQUIRED. A string that specifies the format of the requested Verifiable Credential.")
734
952
  ),
735
- claims: v9.pipe(
736
- v9.optional(v9.pipe(v9.array(DcqlClaimsQuery.vW3cSdJwtVc), vNonEmptyArray())),
737
- v9.description("OPTIONAL. A non-empty array of objects as that specifies claims in the requested Credential.")
953
+ claims: v10.pipe(
954
+ v10.optional(vNonEmptyArray(DcqlClaimsQuery.vW3cSdJwtVc)),
955
+ v10.description("OPTIONAL. A non-empty array of objects as that specifies claims in the requested Credential.")
738
956
  ),
739
- meta: v9.pipe(
740
- v9.optional(
741
- v9.pipe(
742
- v9.object({
743
- vct_values: v9.optional(v9.array(v9.string()))
957
+ meta: v10.pipe(
958
+ v10.optional(
959
+ v10.pipe(
960
+ v10.object({
961
+ vct_values: v10.optional(v10.array(v10.string()))
744
962
  }),
745
- v9.description(
963
+ v10.description(
746
964
  "OPTIONAL. An array of strings that specifies allowed values for the type of the requested Verifiable Credential."
747
965
  )
748
966
  )
749
967
  ),
750
- v9.description(
968
+ v10.description(
751
969
  "OPTIONAL. An object defining additional properties requested by the Verifier that apply to the metadata and validity data of the Credential."
752
970
  )
753
971
  )
754
972
  });
755
- DcqlCredentialQuery2.vW3cVc = v9.object({
973
+ DcqlCredentialQuery2.vW3cVc = v10.object({
756
974
  ...vBase.entries,
757
- format: v9.picklist(["jwt_vc_json", "ldp_vc"]),
758
- claims: v9.optional(v9.pipe(v9.array(DcqlClaimsQuery.vW3cSdJwtVc), vNonEmptyArray())),
759
- meta: v9.pipe(
760
- v9.pipe(
761
- v9.object({
762
- type_values: v9.pipe(
763
- v9.array(v9.pipe(v9.array(v9.string()), vNonEmptyArray())),
764
- vNonEmptyArray(),
765
- v9.description(
975
+ format: v10.picklist(["jwt_vc_json", "ldp_vc"]),
976
+ claims: v10.optional(vNonEmptyArray(DcqlClaimsQuery.vW3cSdJwtVc)),
977
+ meta: v10.pipe(
978
+ v10.pipe(
979
+ v10.object({
980
+ type_values: v10.pipe(
981
+ vNonEmptyArray(vNonEmptyArray(v10.string())),
982
+ v10.description(
766
983
  "REQUIRED. An array of string arrays that specifies the fully expanded types (IRIs) after the @context was applied that the Verifier accepts to be presented in the Presentation. Each of the top-level arrays specifies one alternative to match the type values of the Verifiable Credential against. Each inner array specifies a set of fully expanded types that MUST be present in the type property of the Verifiable Credential, regardless of order or the presence of additional types."
767
984
  )
768
985
  )
769
986
  })
770
987
  ),
771
- v9.description(
988
+ v10.description(
772
989
  "REQUIRED. An object defining additional properties requested by the Verifier that apply to the metadata and validity data of the Credential."
773
990
  )
774
991
  )
775
992
  });
776
- DcqlCredentialQuery2.vModel = v9.variant("format", [DcqlCredentialQuery2.vMdoc, DcqlCredentialQuery2.vSdJwtVc, DcqlCredentialQuery2.vW3cVc]);
993
+ DcqlCredentialQuery2.vModel = v10.variant("format", [DcqlCredentialQuery2.vMdoc, DcqlCredentialQuery2.vSdJwtVc, DcqlCredentialQuery2.vW3cVc]);
777
994
  DcqlCredentialQuery2.validate = (credentialQuery) => {
778
995
  claimSetIdsAreDefined(credentialQuery);
779
996
  };
@@ -797,177 +1014,264 @@ var claimSetIdsAreDefined = (credentialQuery) => {
797
1014
  };
798
1015
 
799
1016
  // src/dcql-query/m-dcql-credential-set-query.ts
800
- import * as v10 from "valibot";
1017
+ import * as v11 from "valibot";
801
1018
  var CredentialSetQuery;
802
1019
  ((CredentialSetQuery2) => {
803
- CredentialSetQuery2.vModel = v10.object({
804
- options: v10.pipe(
805
- v10.array(v10.array(vIdString)),
806
- vNonEmptyArray(),
807
- v10.description(
1020
+ CredentialSetQuery2.vModel = v11.object({
1021
+ options: v11.pipe(
1022
+ vNonEmptyArray(v11.array(vIdString)),
1023
+ v11.description(
808
1024
  "REQUIRED. A non-empty array, where each value in the array is a list of Credential Query identifiers representing one set of Credentials that satisfies the use case."
809
1025
  )
810
1026
  ),
811
- required: v10.pipe(
812
- v10.optional(v10.boolean(), true),
813
- v10.description(
1027
+ required: v11.pipe(
1028
+ v11.optional(v11.boolean(), true),
1029
+ v11.description(
814
1030
  `OPTIONAL. Boolean which indicates whether this set of Credentials is required to satisfy the particular use case at the Verifier. If omitted, the default value is 'true'.`
815
1031
  )
816
1032
  ),
817
- purpose: v10.pipe(
818
- v10.optional(v10.union([v10.string(), v10.number(), v10.record(v10.string(), v10.unknown())])),
819
- v10.description("OPTIONAL. A string, number or object specifying the purpose of the query.")
1033
+ purpose: v11.pipe(
1034
+ v11.optional(v11.union([v11.string(), v11.number(), v11.record(v11.string(), v11.unknown())])),
1035
+ v11.description("OPTIONAL. A string, number or object specifying the purpose of the query.")
820
1036
  )
821
1037
  });
822
1038
  })(CredentialSetQuery || (CredentialSetQuery = {}));
823
1039
 
1040
+ // src/dcql-query-result/m-claims-result.ts
1041
+ import * as v12 from "valibot";
1042
+ var DcqlClaimsResult;
1043
+ ((DcqlClaimsResult2) => {
1044
+ const vClaimsOutput = v12.union([
1045
+ DcqlMdocCredential.vModel.entries.namespaces,
1046
+ DcqlSdJwtVcCredential.vModel.entries.claims,
1047
+ DcqlW3cVcCredential.vModel.entries.claims
1048
+ ]);
1049
+ DcqlClaimsResult2.vClaimsEntrySuccessResult = v12.object({
1050
+ success: v12.literal(true),
1051
+ claim_index: v12.number(),
1052
+ claim_id: v12.optional(vIdString),
1053
+ output: vClaimsOutput
1054
+ });
1055
+ DcqlClaimsResult2.vClaimsEntryFailureResult = v12.object({
1056
+ success: v12.literal(false),
1057
+ claim_index: v12.number(),
1058
+ claim_id: v12.optional(vIdString),
1059
+ issues: v12.record(v12.string(), v12.unknown()),
1060
+ output: v12.unknown()
1061
+ });
1062
+ DcqlClaimsResult2.vClaimSetSuccessResult = v12.object({
1063
+ success: v12.literal(true),
1064
+ // Undefined in case of no claim set
1065
+ claim_set_index: v12.union([v12.number(), v12.undefined()]),
1066
+ // We use indexes because if there are no claim sets, the ids can be undefined
1067
+ // Can be empty array in case there are no claims
1068
+ valid_claim_indexes: v12.array(v12.number()),
1069
+ failed_claim_indexes: v12.optional(v12.undefined()),
1070
+ output: vClaimsOutput
1071
+ });
1072
+ DcqlClaimsResult2.vClaimSetFailureResult = v12.object({
1073
+ success: v12.literal(false),
1074
+ // Undefined in case of no claim set
1075
+ claim_set_index: v12.union([v12.number(), v12.undefined()]),
1076
+ // We use indexes because if there are no claim sets, the ids can be undefined
1077
+ valid_claim_indexes: v12.array(v12.number()),
1078
+ failed_claim_indexes: vNonEmptyArray(v12.number()),
1079
+ issues: v12.record(v12.string(), v12.unknown())
1080
+ });
1081
+ DcqlClaimsResult2.vClaimsSuccessResult = v12.object({
1082
+ success: v12.literal(true),
1083
+ valid_claims: v12.array(DcqlClaimsResult2.vClaimsEntrySuccessResult),
1084
+ failed_claims: v12.array(DcqlClaimsResult2.vClaimsEntryFailureResult),
1085
+ valid_claim_sets: vNonEmptyArray(DcqlClaimsResult2.vClaimSetSuccessResult),
1086
+ failed_claim_sets: v12.array(DcqlClaimsResult2.vClaimSetFailureResult)
1087
+ });
1088
+ DcqlClaimsResult2.vClaimsFailureResult = v12.object({
1089
+ success: v12.literal(false),
1090
+ valid_claims: v12.array(DcqlClaimsResult2.vClaimsEntrySuccessResult),
1091
+ failed_claims: vNonEmptyArray(DcqlClaimsResult2.vClaimsEntryFailureResult),
1092
+ valid_claim_sets: v12.optional(v12.undefined()),
1093
+ failed_claim_sets: vNonEmptyArray(DcqlClaimsResult2.vClaimSetFailureResult)
1094
+ });
1095
+ DcqlClaimsResult2.vModel = v12.union([DcqlClaimsResult2.vClaimsSuccessResult, DcqlClaimsResult2.vClaimsFailureResult]);
1096
+ })(DcqlClaimsResult || (DcqlClaimsResult = {}));
1097
+
1098
+ // src/dcql-query-result/m-meta-result.ts
1099
+ import * as v13 from "valibot";
1100
+ var DcqlMetaResult;
1101
+ ((DcqlMetaResult2) => {
1102
+ DcqlMetaResult2.vMetaSuccessResult = v13.object({
1103
+ success: v13.literal(true),
1104
+ // TODO: This needs to be format specific
1105
+ output: v13.object({
1106
+ credential_format: v13.string()
1107
+ })
1108
+ });
1109
+ DcqlMetaResult2.vMetaFailureResult = v13.object({
1110
+ success: v13.literal(false),
1111
+ issues: v13.record(v13.string(), v13.unknown()),
1112
+ output: v13.unknown()
1113
+ });
1114
+ DcqlMetaResult2.vModel = v13.union([DcqlMetaResult2.vMetaSuccessResult, DcqlMetaResult2.vMetaFailureResult]);
1115
+ })(DcqlMetaResult || (DcqlMetaResult = {}));
1116
+
1117
+ // src/dcql-query-result/m-trusted-authorities-result.ts
1118
+ import * as v14 from "valibot";
1119
+ var DcqlTrustedAuthoritiesResult;
1120
+ ((DcqlTrustedAuthoritiesResult2) => {
1121
+ DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntrySuccessResult = v14.object({
1122
+ success: v14.literal(true),
1123
+ trusted_authority_index: v14.number(),
1124
+ output: DcqlCredentialTrustedAuthority.vModel
1125
+ });
1126
+ DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntryFailureResult = v14.object({
1127
+ success: v14.literal(false),
1128
+ trusted_authority_index: v14.number(),
1129
+ issues: v14.record(v14.string(), v14.unknown()),
1130
+ output: v14.unknown()
1131
+ });
1132
+ DcqlTrustedAuthoritiesResult2.vTrustedAuthoritySuccessResult = v14.union([
1133
+ // In this case there is no trusted authority on the query
1134
+ v14.object({
1135
+ success: v14.literal(true),
1136
+ valid_trusted_authority: v14.optional(v14.undefined()),
1137
+ failed_trusted_authorities: v14.optional(v14.undefined())
1138
+ }),
1139
+ v14.object({
1140
+ success: v14.literal(true),
1141
+ valid_trusted_authority: DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntrySuccessResult,
1142
+ failed_trusted_authorities: v14.array(DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntryFailureResult)
1143
+ })
1144
+ ]);
1145
+ DcqlTrustedAuthoritiesResult2.vTrustedAuthorityFailureResult = v14.object({
1146
+ success: v14.literal(false),
1147
+ valid_trusted_authority: v14.optional(v14.undefined()),
1148
+ failed_trusted_authorities: vNonEmptyArray(DcqlTrustedAuthoritiesResult2.vTrustedAuthorityEntryFailureResult)
1149
+ });
1150
+ DcqlTrustedAuthoritiesResult2.vModel = v14.union([...DcqlTrustedAuthoritiesResult2.vTrustedAuthoritySuccessResult.options, DcqlTrustedAuthoritiesResult2.vTrustedAuthorityFailureResult]);
1151
+ })(DcqlTrustedAuthoritiesResult || (DcqlTrustedAuthoritiesResult = {}));
1152
+
824
1153
  // src/dcql-query-result/m-dcql-query-result.ts
825
1154
  var DcqlQueryResult;
826
1155
  ((DcqlQueryResult2) => {
827
- DcqlQueryResult2.vCredentialQueryResult = v11.pipe(
828
- v11.array(v11.array(v11.union([v11.undefined(), DcqlCredential.vParseSuccess, DcqlCredential.vParseFailure]))),
829
- vNonEmptyArray()
830
- );
831
- DcqlQueryResult2.vModel = v11.object({
832
- credentials: v11.pipe(
833
- v11.array(DcqlCredentialQuery.vModel),
834
- vNonEmptyArray(),
835
- v11.description(
1156
+ DcqlQueryResult2.vCredentialQueryItemCredentialSuccessResult = v15.object({
1157
+ success: v15.literal(true),
1158
+ input_credential_index: v15.number(),
1159
+ trusted_authorities: DcqlTrustedAuthoritiesResult.vTrustedAuthoritySuccessResult,
1160
+ // TODO: format specific (we should probably add format to this object, to differentiate?)
1161
+ claims: DcqlClaimsResult.vClaimsSuccessResult,
1162
+ meta: DcqlMetaResult.vMetaSuccessResult
1163
+ });
1164
+ DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult = v15.object({
1165
+ success: v15.literal(false),
1166
+ input_credential_index: v15.number(),
1167
+ trusted_authorities: DcqlTrustedAuthoritiesResult.vModel,
1168
+ claims: DcqlClaimsResult.vModel,
1169
+ meta: DcqlMetaResult.vModel
1170
+ });
1171
+ DcqlQueryResult2.vCredentialQueryItemResult = v15.union([
1172
+ v15.object({
1173
+ success: v15.literal(true),
1174
+ credential_query_id: vIdString,
1175
+ valid_credentials: vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialSuccessResult),
1176
+ failed_credentials: v15.array(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult)
1177
+ }),
1178
+ v15.object({
1179
+ success: v15.literal(false),
1180
+ credential_query_id: vIdString,
1181
+ valid_credentials: v15.optional(v15.undefined()),
1182
+ failed_credentials: vNonEmptyArray(DcqlQueryResult2.vCredentialQueryItemCredentialFailureResult)
1183
+ })
1184
+ ]);
1185
+ DcqlQueryResult2.vCredentialQueryResult = v15.record(vIdString, DcqlQueryResult2.vCredentialQueryItemResult);
1186
+ DcqlQueryResult2.vModel = v15.object({
1187
+ credentials: v15.pipe(
1188
+ vNonEmptyArray(DcqlCredentialQuery.vModel),
1189
+ v15.description(
836
1190
  "REQUIRED. A non-empty array of Credential Queries that specify the requested Verifiable Credentials."
837
1191
  )
838
1192
  ),
839
- credential_matches: v11.record(
840
- v11.pipe(vIdString),
841
- v11.union([
842
- v11.object({
843
- ...DcqlCredential.vParseSuccess.entries,
844
- all: v11.pipe(
845
- v11.array(
846
- v11.pipe(
847
- v11.array(v11.union([v11.undefined(), DcqlCredential.vParseSuccess, DcqlCredential.vParseFailure])),
848
- vNonEmptyArray()
849
- )
850
- ),
851
- vNonEmptyArray()
852
- )
853
- }),
854
- v11.object({
855
- success: DcqlCredential.vParseFailure.entries.success,
856
- all: v11.pipe(
857
- v11.array(
858
- v11.pipe(
859
- v11.array(v11.union([v11.undefined(), DcqlCredential.vParseSuccess, DcqlCredential.vParseFailure])),
860
- vNonEmptyArray()
861
- )
862
- ),
863
- vNonEmptyArray()
864
- )
865
- })
866
- ])
867
- ),
868
- credential_sets: v11.optional(
869
- v11.pipe(
870
- v11.array(
871
- v11.object({
1193
+ credential_matches: DcqlQueryResult2.vCredentialQueryResult,
1194
+ credential_sets: v15.optional(
1195
+ v15.pipe(
1196
+ vNonEmptyArray(
1197
+ v15.object({
872
1198
  ...CredentialSetQuery.vModel.entries,
873
- matching_options: v11.union([v11.undefined(), v11.pipe(v11.array(v11.array(v11.string())), vNonEmptyArray())])
1199
+ matching_options: v15.union([v15.undefined(), vNonEmptyArray(v15.array(v15.string()))])
874
1200
  })
875
1201
  ),
876
- vNonEmptyArray(),
877
- v11.description(
1202
+ v15.description(
878
1203
  "OPTIONAL. A non-empty array of credential set queries that specifies additional constraints on which of the requested Verifiable Credentials to return."
879
1204
  )
880
1205
  )
881
1206
  ),
882
- canBeSatisfied: v11.boolean()
1207
+ can_be_satisfied: v15.boolean()
883
1208
  });
884
1209
  })(DcqlQueryResult || (DcqlQueryResult = {}));
885
1210
 
886
1211
  // src/dcql-presentation/m-dcql-presentation-result.ts
887
1212
  var DcqlPresentationResult;
888
1213
  ((DcqlPresentationResult2) => {
889
- DcqlPresentationResult2.vModel = v12.object({
890
- ...v12.omit(DcqlQueryResult.vModel, ["credential_matches"]).entries,
891
- invalid_matches: v12.union([
892
- v12.record(
893
- v12.pipe(vIdString),
894
- v12.object({
895
- ...v12.omit(DcqlCredential.vParseFailure, ["input_credential_index"]).entries,
896
- presentation_id: v12.pipe(vIdString)
897
- })
898
- ),
899
- v12.undefined()
900
- ]),
901
- valid_matches: v12.record(
902
- v12.pipe(vIdString),
903
- v12.object({
904
- ...v12.omit(DcqlCredential.vParseSuccess, ["issues", "input_credential_index"]).entries,
905
- presentation_id: v12.pipe(vIdString)
906
- })
907
- )
908
- });
1214
+ DcqlPresentationResult2.vModel = v16.omit(DcqlQueryResult.vModel, ["credentials"]);
909
1215
  DcqlPresentationResult2.parse = (input) => {
910
- return v12.parse(DcqlPresentationResult2.vModel, input);
1216
+ return v16.parse(DcqlPresentationResult2.vModel, input);
911
1217
  };
912
1218
  DcqlPresentationResult2.fromDcqlPresentation = (dcqlPresentation, ctx) => {
913
1219
  const { dcqlQuery } = ctx;
914
- const presentationQueriesResults = Object.fromEntries(
915
- Object.entries(dcqlPresentation).map(([queryId, presentation]) => {
916
- const credentialQuery = dcqlQuery.credentials.find((c) => c.id === queryId);
917
- if (!credentialQuery) {
1220
+ const queriesResults = Object.entries(dcqlPresentation).map(([credentialQueryId, presentations]) => {
1221
+ const credentialQuery = dcqlQuery.credentials.find((c) => c.id === credentialQueryId);
1222
+ if (!credentialQuery) {
1223
+ throw new DcqlPresentationResultError({
1224
+ message: `Query ${credentialQueryId} not found in the dcql query. Cannot validate presentation.`
1225
+ });
1226
+ }
1227
+ if (Array.isArray(presentations)) {
1228
+ if (presentations.length === 0) {
918
1229
  throw new DcqlPresentationResultError({
919
- message: `Query ${queryId} not found in the dcql query. Cannot validate presentation.`
1230
+ message: `Query credential '${credentialQueryId}' is present in the presentations but the value is an empty array. Each entry must at least provide one presentation.`
920
1231
  });
921
1232
  }
922
- return [
923
- queryId,
924
- runCredentialQuery(credentialQuery, {
925
- presentation: true,
926
- credentials: [presentation]
927
- })
928
- ];
929
- })
930
- );
931
- let invalidMatches = {};
932
- const validMatches = {};
933
- for (const [queryId, presentationQueryResult] of Object.entries(presentationQueriesResults)) {
934
- for (const presentationQueryResultForClaimSet of presentationQueryResult) {
935
- const result = presentationQueryResultForClaimSet[0];
936
- if (result?.success) {
937
- const { issues, input_credential_index, ...rest } = result;
938
- validMatches[queryId] = { ...rest, presentation_id: queryId };
939
- } else if (result?.success === false) {
940
- const { input_credential_index, ...rest } = result;
941
- invalidMatches[queryId] = {
942
- ...rest,
943
- presentation_id: queryId
944
- };
1233
+ if (!credentialQuery.multiple && presentations.length > 1) {
1234
+ throw new DcqlPresentationResultError({
1235
+ message: `Query credential '${credentialQueryId}' has not enabled 'multiple', but multiple presentations were provided. Only a single presentation is allowed for each query credential when 'multiple' is not enabled on the query.`
1236
+ });
945
1237
  }
946
1238
  }
947
- }
948
- invalidMatches = Object.fromEntries(
949
- Object.entries(invalidMatches ?? {}).filter(([queryId]) => validMatches[queryId] === void 0)
950
- );
1239
+ return runCredentialQuery(credentialQuery, {
1240
+ presentation: true,
1241
+ credentials: presentations ? Array.isArray(presentations) ? presentations : [presentations] : []
1242
+ });
1243
+ });
951
1244
  const credentialSetResults = dcqlQuery.credential_sets?.map((set) => {
952
1245
  const matchingOptions = set.options.filter(
953
- (option) => option.every((credentialQueryId) => validMatches[credentialQueryId]?.success)
1246
+ (option) => option.every(
1247
+ (credentialQueryId) => queriesResults.find((result) => result.credential_query_id === credentialQueryId)?.success
1248
+ )
954
1249
  );
955
1250
  return {
956
1251
  ...set,
957
1252
  matching_options: matchingOptions.length > 0 ? matchingOptions : void 0
958
1253
  };
959
1254
  });
960
- const dqclQueryMatched = credentialSetResults ? credentialSetResults.every((set) => !set.required || set.matching_options) : Object.keys(validMatches).length === ctx.dcqlQuery.credentials.length;
1255
+ const dqclQueryMatched = (
1256
+ // We require that all the submitted presentations match with the queries
1257
+ // So we must have success for all queries, and we don't allow failed_credentials
1258
+ queriesResults.every((result) => result.success && result.failed_credentials.length === 0) && (credentialSetResults ? credentialSetResults.every((set) => !set.required || set.matching_options) : (
1259
+ // If not credential_sets are used, we require that at least every credential has a match
1260
+ dcqlQuery.credentials.every(
1261
+ (credentialQuery) => queriesResults.find((result) => result.credential_query_id === credentialQuery.id)?.success
1262
+ )
1263
+ ))
1264
+ );
961
1265
  return {
962
- ...dcqlQuery,
963
- canBeSatisfied: dqclQueryMatched,
964
- valid_matches: validMatches,
965
- invalid_matches: Object.keys(invalidMatches).length === 0 ? void 0 : invalidMatches,
966
- credential_sets: credentialSetResults
1266
+ // NOTE: can_be_satisfied is maybe not the best term, because we return false if it can be
1267
+ // satisfied, but one of the provided presentations did not match
1268
+ can_be_satisfied: dqclQueryMatched,
1269
+ credential_sets: credentialSetResults,
1270
+ credential_matches: Object.fromEntries(queriesResults.map((result) => [result.credential_query_id, result]))
967
1271
  };
968
1272
  };
969
1273
  DcqlPresentationResult2.validate = (dcqlQueryResult) => {
970
- if (!dcqlQueryResult.canBeSatisfied) {
1274
+ if (!dcqlQueryResult.can_be_satisfied) {
971
1275
  throw new DcqlInvalidPresentationRecordError({
972
1276
  message: "Invalid Presentation record",
973
1277
  cause: dcqlQueryResult
@@ -978,15 +1282,28 @@ var DcqlPresentationResult;
978
1282
  })(DcqlPresentationResult || (DcqlPresentationResult = {}));
979
1283
 
980
1284
  // src/dcql-presentation/m-dcql-presentation.ts
981
- import * as v13 from "valibot";
1285
+ import * as v17 from "valibot";
982
1286
  var DcqlPresentation;
983
1287
  ((DcqlPresentation2) => {
984
- DcqlPresentation2.vModel = v13.record(vIdString, v13.union([v13.string(), vJsonRecord]));
1288
+ const vPresentationEntry = v17.union([v17.string(), vJsonRecord]);
1289
+ DcqlPresentation2.vModel = v17.pipe(
1290
+ v17.union([
1291
+ v17.record(vIdString, vNonEmptyArray(vPresentationEntry)),
1292
+ v17.record(
1293
+ vIdString,
1294
+ // We support presentation entry directly (not as array) to support older draft of DCQL
1295
+ vPresentationEntry
1296
+ )
1297
+ ]),
1298
+ v17.description(
1299
+ "REQUIRED. This is a JSON-encoded object containing entries where the key is the id value used for a Credential Query in the DCQL query and the value is an array of one or more Presentations that match the respective Credential Query. When multiple is omitted, or set to false, the array MUST contain only one Presentation. There MUST NOT be any entry in the JSON-encoded object for optional Credential Queries when there are no matching Credentials for the respective Credential Query. Each Presentation is represented as a string or object, depending on the format as defined in Appendix B. The same rules as above apply for encoding the Presentations."
1300
+ )
1301
+ );
985
1302
  DcqlPresentation2.parse = (input) => {
986
1303
  if (typeof input === "string") {
987
- return v13.parse(v13.pipe(v13.string(), vStringToJson, DcqlPresentation2.vModel), input);
1304
+ return v17.parse(v17.pipe(v17.string(), vStringToJson, DcqlPresentation2.vModel), input);
988
1305
  }
989
- return v13.parse(DcqlPresentation2.vModel, input);
1306
+ return v17.parse(DcqlPresentation2.vModel, input);
990
1307
  };
991
1308
  DcqlPresentation2.encode = (input) => {
992
1309
  return JSON.stringify(input);
@@ -998,60 +1315,41 @@ var runDcqlQuery = (dcqlQuery, ctx) => {
998
1315
  const credentialQueriesResults = Object.fromEntries(
999
1316
  dcqlQuery.credentials.map((credentialQuery) => [credentialQuery.id, runCredentialQuery(credentialQuery, ctx)])
1000
1317
  );
1001
- const credentialMatches = Object.fromEntries(
1002
- Object.entries(credentialQueriesResults).map(([key, credentialQueryResult]) => {
1003
- let bestMatch = void 0;
1004
- for (const credentialParseResult of credentialQueryResult) {
1005
- const bestMatchForCredential = credentialParseResult.find((result) => result?.success === true);
1006
- if (!bestMatch && bestMatchForCredential) {
1007
- const { issues, ...matchWithoutIssues } = bestMatchForCredential;
1008
- bestMatch = matchWithoutIssues;
1009
- continue;
1010
- }
1011
- if (bestMatchForCredential && bestMatchForCredential.claim_set_index < bestMatch?.claim_set_index) {
1012
- const { issues, ...matchWithoutIssues } = bestMatchForCredential;
1013
- bestMatch = matchWithoutIssues;
1014
- }
1015
- }
1016
- return [
1017
- key,
1018
- bestMatch ? { ...bestMatch, all: credentialQueryResult } : { success: false, all: credentialQueryResult }
1019
- ];
1020
- })
1021
- );
1022
1318
  const credentialSetResults = dcqlQuery.credential_sets?.map((set) => {
1023
1319
  const matchingOptions = set.options.filter(
1024
- (option) => option.every((credentialQueryId) => credentialMatches[credentialQueryId]?.success)
1320
+ (option) => option.every((credentialQueryId) => credentialQueriesResults[credentialQueryId].success)
1025
1321
  );
1026
1322
  return {
1027
1323
  ...set,
1028
1324
  matching_options: matchingOptions.length > 0 ? matchingOptions : void 0
1029
1325
  };
1030
1326
  });
1031
- const dqclQueryMatched = credentialSetResults ? credentialSetResults.every((set) => !set.required || set.matching_options) : Object.values(credentialMatches).every((query) => query.success);
1327
+ const dqclQueryMatched = credentialSetResults ? credentialSetResults.every((set) => !set.required || set.matching_options) : (
1328
+ // If not credential_sets are used, we require that at least every credential has a match
1329
+ dcqlQuery.credentials.every(({ id }) => credentialQueriesResults[id].success === true)
1330
+ );
1032
1331
  return {
1033
1332
  ...dcqlQuery,
1034
- canBeSatisfied: dqclQueryMatched,
1035
- credential_matches: credentialMatches,
1333
+ can_be_satisfied: dqclQueryMatched,
1334
+ credential_matches: credentialQueriesResults,
1036
1335
  credential_sets: credentialSetResults
1037
1336
  };
1038
1337
  };
1039
1338
 
1040
1339
  // src/dcql-query/m-dcql-query.ts
1041
- import * as v14 from "valibot";
1340
+ import * as v18 from "valibot";
1042
1341
  var DcqlQuery;
1043
1342
  ((DcqlQuery2) => {
1044
- DcqlQuery2.vModel = v14.object({
1045
- credentials: v14.pipe(
1046
- v14.array(DcqlCredentialQuery.vModel),
1047
- vNonEmptyArray(),
1048
- v14.description(
1343
+ DcqlQuery2.vModel = v18.object({
1344
+ credentials: v18.pipe(
1345
+ vNonEmptyArray(DcqlCredentialQuery.vModel),
1346
+ v18.description(
1049
1347
  "REQUIRED. A non-empty array of Credential Queries that specify the requested Verifiable Credentials."
1050
1348
  )
1051
1349
  ),
1052
- credential_sets: v14.pipe(
1053
- v14.optional(v14.pipe(v14.array(CredentialSetQuery.vModel), vNonEmptyArray())),
1054
- v14.description(
1350
+ credential_sets: v18.pipe(
1351
+ v18.optional(vNonEmptyArray(CredentialSetQuery.vModel)),
1352
+ v18.description(
1055
1353
  "OPTIONAL. A non-empty array of credential set queries that specifies additional constraints on which of the requested Verifiable Credentials to return."
1056
1354
  )
1057
1355
  )
@@ -1065,7 +1363,7 @@ var DcqlQuery;
1065
1363
  return runDcqlQuery(dcqlQuery, { credentials, presentation: false });
1066
1364
  };
1067
1365
  DcqlQuery2.parse = (input) => {
1068
- return v14.parse(DcqlQuery2.vModel, input);
1366
+ return v18.parse(DcqlQuery2.vModel, input);
1069
1367
  };
1070
1368
  })(DcqlQuery || (DcqlQuery = {}));
1071
1369
  var validateUniqueCredentialQueryIds = (query) => {