dcql 0.3.1-alpha-20250624142312 → 0.4.0-alpha-20250704223101
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/README.md +2 -3
- package/dist/index.d.mts +7127 -4169
- package/dist/index.d.ts +7127 -4169
- package/dist/index.js +684 -386
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +684 -386
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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.
|
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
|
-
|
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
|
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
|
-
|
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/
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
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 (
|
495
|
-
|
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
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
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
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
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
|
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 :
|
568
|
+
const elementParser = isLast ? vClaimParser : getJsonClaimParser(claimQuery, { ...ctx, index: index + 1 });
|
527
569
|
if (presentation) {
|
528
|
-
return v7.
|
529
|
-
v7.
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
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(
|
540
|
-
v7.
|
541
|
-
|
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
|
-
|
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
|
550
|
-
}
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
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
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
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
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
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
|
-
|
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
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
const
|
589
|
-
|
590
|
-
doctype
|
591
|
-
|
592
|
-
|
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
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
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 === "
|
614
|
-
return
|
615
|
-
|
616
|
-
|
617
|
-
|
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
|
630
|
-
|
631
|
-
|
632
|
-
|
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
|
-
|
636
|
-
|
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
|
-
|
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
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
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
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
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
|
-
|
863
|
+
trusted_authorities: trustedAuthorityResult,
|
864
|
+
meta: metaResult,
|
865
|
+
claims: claimsResult
|
668
866
|
});
|
669
867
|
}
|
670
|
-
credentialQueryResult.push(claimSetResult);
|
671
868
|
}
|
672
|
-
|
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
|
888
|
+
import * as v15 from "valibot";
|
677
889
|
|
678
890
|
// src/dcql-query/m-dcql-credential-query.ts
|
679
|
-
import * as
|
891
|
+
import * as v10 from "valibot";
|
680
892
|
var DcqlCredentialQuery;
|
681
893
|
((DcqlCredentialQuery2) => {
|
682
|
-
const vBase =
|
683
|
-
id:
|
684
|
-
|
685
|
-
|
686
|
-
|
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
|
-
|
691
|
-
|
692
|
-
|
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:
|
697
|
-
|
698
|
-
|
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 =
|
921
|
+
DcqlCredentialQuery2.vMdoc = v10.object({
|
704
922
|
...vBase.entries,
|
705
|
-
format:
|
706
|
-
|
707
|
-
|
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:
|
710
|
-
|
711
|
-
|
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:
|
714
|
-
|
715
|
-
|
716
|
-
doctype_value:
|
717
|
-
|
718
|
-
|
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
|
-
|
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 =
|
947
|
+
DcqlCredentialQuery2.vSdJwtVc = v10.object({
|
730
948
|
...vBase.entries,
|
731
|
-
format:
|
732
|
-
|
733
|
-
|
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:
|
736
|
-
|
737
|
-
|
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:
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
vct_values:
|
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
|
-
|
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
|
-
|
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 =
|
973
|
+
DcqlCredentialQuery2.vW3cVc = v10.object({
|
756
974
|
...vBase.entries,
|
757
|
-
format:
|
758
|
-
claims:
|
759
|
-
meta:
|
760
|
-
|
761
|
-
|
762
|
-
type_values:
|
763
|
-
|
764
|
-
|
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
|
-
|
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 =
|
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
|
1017
|
+
import * as v11 from "valibot";
|
801
1018
|
var CredentialSetQuery;
|
802
1019
|
((CredentialSetQuery2) => {
|
803
|
-
CredentialSetQuery2.vModel =
|
804
|
-
options:
|
805
|
-
|
806
|
-
|
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:
|
812
|
-
|
813
|
-
|
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:
|
818
|
-
|
819
|
-
|
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.
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
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:
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
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:
|
1199
|
+
matching_options: v15.union([v15.undefined(), vNonEmptyArray(v15.array(v15.string()))])
|
874
1200
|
})
|
875
1201
|
),
|
876
|
-
|
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
|
-
|
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 =
|
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
|
1216
|
+
return v16.parse(DcqlPresentationResult2.vModel, input);
|
911
1217
|
};
|
912
1218
|
DcqlPresentationResult2.fromDcqlPresentation = (dcqlPresentation, ctx) => {
|
913
1219
|
const { dcqlQuery } = ctx;
|
914
|
-
const
|
915
|
-
|
916
|
-
|
917
|
-
|
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 ${
|
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
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
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
|
-
|
949
|
-
|
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(
|
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 =
|
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
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
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.
|
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
|
1285
|
+
import * as v17 from "valibot";
|
982
1286
|
var DcqlPresentation;
|
983
1287
|
((DcqlPresentation2) => {
|
984
|
-
|
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
|
1304
|
+
return v17.parse(v17.pipe(v17.string(), vStringToJson, DcqlPresentation2.vModel), input);
|
988
1305
|
}
|
989
|
-
return
|
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) =>
|
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) :
|
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
|
-
|
1035
|
-
credential_matches:
|
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
|
1340
|
+
import * as v18 from "valibot";
|
1042
1341
|
var DcqlQuery;
|
1043
1342
|
((DcqlQuery2) => {
|
1044
|
-
DcqlQuery2.vModel =
|
1045
|
-
credentials:
|
1046
|
-
|
1047
|
-
|
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:
|
1053
|
-
|
1054
|
-
|
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
|
1366
|
+
return v18.parse(DcqlQuery2.vModel, input);
|
1069
1367
|
};
|
1070
1368
|
})(DcqlQuery || (DcqlQuery = {}));
|
1071
1369
|
var validateUniqueCredentialQueryIds = (query) => {
|