tba-protocol-credential-w3c-vcdm 0.4.0.dev2__tar.gz
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.
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/PKG-INFO +14 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/pyproject.toml +11 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/__init__.py +42 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/bindings.py +9 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/claims.py +12 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/compatibility.py +21 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/contexts.py +21 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/errors.py +8 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/features.py +23 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/migrations.py +19 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/py.typed +1 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/schema_references.py +28 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/schemas.py +100 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/status.py +28 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/validation.py +44 -0
- tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/versions.py +26 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tba-protocol-credential-w3c-vcdm
|
|
3
|
+
Version: 0.4.0.dev2
|
|
4
|
+
Summary:
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.10,<3.15
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: tba-concrete-verifiable-credential (==0.4.0.dev2)
|
|
14
|
+
Requires-Dist: tba-concrete-verifiable-presentation (==0.4.0.dev2)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name="tba-protocol-credential-w3c-vcdm"
|
|
3
|
+
version="0.4.0.dev2"
|
|
4
|
+
requires-python=">=3.10,<3.15"
|
|
5
|
+
license="Apache-2.0"
|
|
6
|
+
dependencies=["tba-concrete-verifiable-credential==0.4.0.dev2","tba-concrete-verifiable-presentation==0.4.0.dev2"]
|
|
7
|
+
[build-system]
|
|
8
|
+
requires=["poetry-core>=1.0.0"]
|
|
9
|
+
build-backend="poetry.core.masonry.api"
|
|
10
|
+
[tool.poetry]
|
|
11
|
+
packages=[{include="tba_protocol_credential_w3c_vcdm",from="src"}]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from .compatibility import VcdmCompatibility, compatibility
|
|
2
|
+
from .contexts import VCDM_2_RECOMMENDATION, VC_CONTEXT_V2, validate_contexts
|
|
3
|
+
from .features import FEATURES_BY_VERSION, supports
|
|
4
|
+
from .errors import VcdmProtocolError
|
|
5
|
+
from .migrations import migrate_document
|
|
6
|
+
from .schemas import parse_verifiable_credential, parse_verifiable_presentation
|
|
7
|
+
from .schema_references import CredentialSchemaReference, parse_credential_schema
|
|
8
|
+
from .status import CredentialStatusEntry, parse_credential_status
|
|
9
|
+
from .validation import validate_verifiable_credential, validate_verifiable_presentation
|
|
10
|
+
from .versions import CURRENT_VERSION, VERSION_HISTORY, VcdmVersion, select_version
|
|
11
|
+
from tba_concrete_verifiable_credential import VerifiableCredential
|
|
12
|
+
from tba_concrete_verifiable_presentation import VerifiablePresentation
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"CURRENT_VERSION",
|
|
16
|
+
"CAPABILITY_REQUIREMENTS",
|
|
17
|
+
"CredentialSchemaReference",
|
|
18
|
+
"CredentialStatusEntry",
|
|
19
|
+
"FEATURES_BY_VERSION",
|
|
20
|
+
"VCDM_2_RECOMMENDATION",
|
|
21
|
+
"VC_CONTEXT_V2",
|
|
22
|
+
"VERSION_HISTORY",
|
|
23
|
+
"VcdmCompatibility",
|
|
24
|
+
"VcdmProtocolError",
|
|
25
|
+
"VcdmVersion",
|
|
26
|
+
"VerifiableCredential",
|
|
27
|
+
"VerifiablePresentation",
|
|
28
|
+
"compatibility",
|
|
29
|
+
"credential_subject_claims",
|
|
30
|
+
"migrate_document",
|
|
31
|
+
"parse_credential_schema",
|
|
32
|
+
"parse_credential_status",
|
|
33
|
+
"parse_verifiable_credential",
|
|
34
|
+
"parse_verifiable_presentation",
|
|
35
|
+
"select_version",
|
|
36
|
+
"supports",
|
|
37
|
+
"validate_contexts",
|
|
38
|
+
"validate_verifiable_credential",
|
|
39
|
+
"validate_verifiable_presentation",
|
|
40
|
+
]
|
|
41
|
+
from .bindings import CAPABILITY_REQUIREMENTS
|
|
42
|
+
from .claims import credential_subject_claims
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Capability bindings required by the W3C VCDM protocol owner."""
|
|
2
|
+
|
|
3
|
+
CAPABILITY_REQUIREMENTS = (
|
|
4
|
+
"credential.issue.verifiable-credential",
|
|
5
|
+
"credential.verify.verifiable-credential",
|
|
6
|
+
"presentation.verify.verifiable-presentation",
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = ["CAPABILITY_REQUIREMENTS"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""W3C VCDM credential-subject claim material."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def credential_subject_claims(value: Mapping[str, object]) -> dict[str, object]:
|
|
7
|
+
"""Normalize credential-subject claims without assigning assurance."""
|
|
8
|
+
|
|
9
|
+
return {str(name): claim for name, claim in value.items()}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__all__ = ["credential_subject_claims"]
|
tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/compatibility.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from .versions import CURRENT_VERSION, VcdmVersion
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True, slots=True)
|
|
7
|
+
class VcdmCompatibility:
|
|
8
|
+
source: str
|
|
9
|
+
target: str
|
|
10
|
+
compatible: bool
|
|
11
|
+
migration_required: bool
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def compatibility(
|
|
15
|
+
source: str, target: str = CURRENT_VERSION.value
|
|
16
|
+
) -> VcdmCompatibility:
|
|
17
|
+
known = source in {version.value for version in VcdmVersion}
|
|
18
|
+
return VcdmCompatibility(source, target, known, known and source != target)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = ["VcdmCompatibility", "compatibility"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
VCDM_2_RECOMMENDATION = "W3C Recommendation 15 May 2025"
|
|
4
|
+
VC_CONTEXT_V2 = "https://www.w3.org/ns/credentials/v2"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def validate_contexts(contexts: object) -> tuple[object, ...]:
|
|
8
|
+
if (
|
|
9
|
+
not isinstance(contexts, Sequence)
|
|
10
|
+
or isinstance(contexts, (str, bytes))
|
|
11
|
+
or not contexts
|
|
12
|
+
):
|
|
13
|
+
raise ValueError("@context must be a non-empty array")
|
|
14
|
+
if contexts[0] != VC_CONTEXT_V2:
|
|
15
|
+
raise ValueError("VCDM 2.0 base context must be first")
|
|
16
|
+
if any(not isinstance(context, (str, dict)) for context in contexts):
|
|
17
|
+
raise ValueError("contexts must be URLs or context objects")
|
|
18
|
+
return tuple(contexts)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = ["VCDM_2_RECOMMENDATION", "VC_CONTEXT_V2", "validate_contexts"]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .versions import CURRENT_VERSION, VcdmVersion
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
FEATURES_BY_VERSION = {
|
|
5
|
+
VcdmVersion.V1_1.value: frozenset({"credential", "presentation", "issuance-date"}),
|
|
6
|
+
VcdmVersion.V2_0.value: frozenset(
|
|
7
|
+
{
|
|
8
|
+
"credential",
|
|
9
|
+
"presentation",
|
|
10
|
+
"valid-from",
|
|
11
|
+
"valid-until",
|
|
12
|
+
"credential-status",
|
|
13
|
+
"credential-schema",
|
|
14
|
+
}
|
|
15
|
+
),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def supports(feature: str, version: VcdmVersion = CURRENT_VERSION) -> bool:
|
|
20
|
+
return feature in FEATURES_BY_VERSION[version.value]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__all__ = ["FEATURES_BY_VERSION", "supports"]
|
tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/migrations.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Mapping
|
|
2
|
+
|
|
3
|
+
from .versions import VcdmVersion
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def migrate_document(
|
|
7
|
+
value: Mapping[str, object], *, source: str, target: str = "2.0"
|
|
8
|
+
) -> dict[str, object]:
|
|
9
|
+
if source == target:
|
|
10
|
+
return dict(value)
|
|
11
|
+
if source != VcdmVersion.V1_1.value or target != VcdmVersion.V2_0.value:
|
|
12
|
+
raise ValueError(f"unsupported VCDM migration: {source} -> {target}")
|
|
13
|
+
migrated = dict(value)
|
|
14
|
+
if "issuanceDate" in migrated and "validFrom" not in migrated:
|
|
15
|
+
migrated["validFrom"] = migrated.pop("issuanceDate")
|
|
16
|
+
return migrated
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = ["migrate_document"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Mapping
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass(frozen=True, slots=True)
|
|
6
|
+
class CredentialSchemaReference:
|
|
7
|
+
identifier: str
|
|
8
|
+
schema_type: str
|
|
9
|
+
properties: Mapping[str, object]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def parse_credential_schema(value: Mapping[str, object]) -> CredentialSchemaReference:
|
|
13
|
+
identifier, schema_type = value.get("id"), value.get("type")
|
|
14
|
+
if (
|
|
15
|
+
not isinstance(identifier, str)
|
|
16
|
+
or not identifier
|
|
17
|
+
or not isinstance(schema_type, str)
|
|
18
|
+
or not schema_type
|
|
19
|
+
):
|
|
20
|
+
raise ValueError("credentialSchema requires id and type")
|
|
21
|
+
return CredentialSchemaReference(
|
|
22
|
+
identifier,
|
|
23
|
+
schema_type,
|
|
24
|
+
{name: item for name, item in value.items() if name not in {"id", "type"}},
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__all__ = ["CredentialSchemaReference", "parse_credential_schema"]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from typing import Mapping, Sequence
|
|
2
|
+
|
|
3
|
+
from tba_concrete_verifiable_credential import VerifiableCredential
|
|
4
|
+
from tba_concrete_verifiable_presentation import VerifiablePresentation
|
|
5
|
+
|
|
6
|
+
from .contexts import validate_contexts
|
|
7
|
+
from .schema_references import parse_credential_schema
|
|
8
|
+
from .status import parse_credential_status
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _objects(value: object, name: str) -> tuple[Mapping[str, object], ...]:
|
|
12
|
+
values = (value,) if isinstance(value, Mapping) else value
|
|
13
|
+
if (
|
|
14
|
+
not isinstance(values, Sequence)
|
|
15
|
+
or isinstance(values, (str, bytes))
|
|
16
|
+
or not values
|
|
17
|
+
or not all(isinstance(item, Mapping) for item in values)
|
|
18
|
+
):
|
|
19
|
+
raise ValueError(f"{name} must be an object or non-empty array of objects")
|
|
20
|
+
return tuple(dict(item) for item in values)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def parse_verifiable_credential(value: Mapping[str, object]) -> VerifiableCredential:
|
|
24
|
+
contexts = validate_contexts(value.get("@context"))
|
|
25
|
+
types, issuer = value.get("type"), value.get("issuer")
|
|
26
|
+
if (
|
|
27
|
+
not isinstance(types, list)
|
|
28
|
+
or "VerifiableCredential" not in types
|
|
29
|
+
or not all(isinstance(item, str) for item in types)
|
|
30
|
+
):
|
|
31
|
+
raise ValueError("type must include VerifiableCredential")
|
|
32
|
+
if not isinstance(issuer, (str, Mapping)) or not issuer:
|
|
33
|
+
raise ValueError("credential requires issuer")
|
|
34
|
+
subjects = _objects(value.get("credentialSubject"), "credentialSubject")
|
|
35
|
+
statuses = (
|
|
36
|
+
tuple(
|
|
37
|
+
parse_credential_status(item)
|
|
38
|
+
for item in _objects(value["credentialStatus"], "credentialStatus")
|
|
39
|
+
)
|
|
40
|
+
if "credentialStatus" in value
|
|
41
|
+
else ()
|
|
42
|
+
)
|
|
43
|
+
schemas = (
|
|
44
|
+
tuple(
|
|
45
|
+
parse_credential_schema(item)
|
|
46
|
+
for item in _objects(value["credentialSchema"], "credentialSchema")
|
|
47
|
+
)
|
|
48
|
+
if "credentialSchema" in value
|
|
49
|
+
else ()
|
|
50
|
+
)
|
|
51
|
+
return VerifiableCredential(
|
|
52
|
+
contexts,
|
|
53
|
+
tuple(types),
|
|
54
|
+
issuer,
|
|
55
|
+
subjects,
|
|
56
|
+
value.get("id") if isinstance(value.get("id"), str) else None,
|
|
57
|
+
value.get("validFrom") if isinstance(value.get("validFrom"), str) else None,
|
|
58
|
+
value.get("validUntil") if isinstance(value.get("validUntil"), str) else None,
|
|
59
|
+
statuses,
|
|
60
|
+
schemas,
|
|
61
|
+
value.get("proof"),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def parse_verifiable_presentation(
|
|
66
|
+
value: Mapping[str, object],
|
|
67
|
+
) -> VerifiablePresentation:
|
|
68
|
+
contexts = validate_contexts(value.get("@context"))
|
|
69
|
+
types = value.get("type")
|
|
70
|
+
if (
|
|
71
|
+
not isinstance(types, list)
|
|
72
|
+
or "VerifiablePresentation" not in types
|
|
73
|
+
or not all(isinstance(item, str) for item in types)
|
|
74
|
+
):
|
|
75
|
+
raise ValueError("type must include VerifiablePresentation")
|
|
76
|
+
credentials = value.get("verifiableCredential", ())
|
|
77
|
+
if isinstance(credentials, (str, Mapping)):
|
|
78
|
+
credentials = (credentials,)
|
|
79
|
+
if (
|
|
80
|
+
not isinstance(credentials, Sequence)
|
|
81
|
+
or isinstance(credentials, bytes)
|
|
82
|
+
or any(not isinstance(item, (str, Mapping)) for item in credentials)
|
|
83
|
+
):
|
|
84
|
+
raise ValueError(
|
|
85
|
+
"verifiableCredential must contain credential objects or strings"
|
|
86
|
+
)
|
|
87
|
+
holder = value.get("holder")
|
|
88
|
+
if holder is not None and not isinstance(holder, (str, Mapping)):
|
|
89
|
+
raise ValueError("holder must be a URL or object")
|
|
90
|
+
return VerifiablePresentation(
|
|
91
|
+
contexts,
|
|
92
|
+
tuple(types),
|
|
93
|
+
tuple(credentials),
|
|
94
|
+
holder,
|
|
95
|
+
value.get("id") if isinstance(value.get("id"), str) else None,
|
|
96
|
+
value.get("proof"),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
__all__ = ["parse_verifiable_credential", "parse_verifiable_presentation"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Mapping
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass(frozen=True, slots=True)
|
|
6
|
+
class CredentialStatusEntry:
|
|
7
|
+
identifier: str
|
|
8
|
+
status_type: str
|
|
9
|
+
properties: Mapping[str, object]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def parse_credential_status(value: Mapping[str, object]) -> CredentialStatusEntry:
|
|
13
|
+
identifier, status_type = value.get("id"), value.get("type")
|
|
14
|
+
if (
|
|
15
|
+
not isinstance(identifier, str)
|
|
16
|
+
or not identifier
|
|
17
|
+
or not isinstance(status_type, str)
|
|
18
|
+
or not status_type
|
|
19
|
+
):
|
|
20
|
+
raise ValueError("credentialStatus requires id and type")
|
|
21
|
+
return CredentialStatusEntry(
|
|
22
|
+
identifier,
|
|
23
|
+
status_type,
|
|
24
|
+
{name: item for name, item in value.items() if name not in {"id", "type"}},
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__all__ = ["CredentialStatusEntry", "parse_credential_status"]
|
tba_protocol_credential_w3c_vcdm-0.4.0.dev2/src/tba_protocol_credential_w3c_vcdm/validation.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import Mapping
|
|
2
|
+
|
|
3
|
+
from tba_concrete_verifiable_credential import VerifiableCredential
|
|
4
|
+
from tba_concrete_verifiable_presentation import VerifiablePresentation
|
|
5
|
+
|
|
6
|
+
from .schemas import parse_verifiable_credential, parse_verifiable_presentation
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _validate_proof_container(proof: object) -> None:
|
|
10
|
+
if proof is None:
|
|
11
|
+
return
|
|
12
|
+
proofs = proof if isinstance(proof, list) else [proof]
|
|
13
|
+
if not proofs or not all(
|
|
14
|
+
isinstance(item, Mapping) and isinstance(item.get("type"), str)
|
|
15
|
+
for item in proofs
|
|
16
|
+
):
|
|
17
|
+
raise ValueError("proof must be an object or array of objects with type")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def validate_verifiable_credential(
|
|
21
|
+
value: Mapping[str, object] | VerifiableCredential,
|
|
22
|
+
) -> None:
|
|
23
|
+
credential = (
|
|
24
|
+
parse_verifiable_credential(value) if isinstance(value, Mapping) else value
|
|
25
|
+
)
|
|
26
|
+
_validate_proof_container(credential.proof)
|
|
27
|
+
if (
|
|
28
|
+
credential.valid_from
|
|
29
|
+
and credential.valid_until
|
|
30
|
+
and credential.valid_from > credential.valid_until
|
|
31
|
+
):
|
|
32
|
+
raise ValueError("validFrom must not follow validUntil")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def validate_verifiable_presentation(
|
|
36
|
+
value: Mapping[str, object] | VerifiablePresentation,
|
|
37
|
+
) -> None:
|
|
38
|
+
presentation = (
|
|
39
|
+
parse_verifiable_presentation(value) if isinstance(value, Mapping) else value
|
|
40
|
+
)
|
|
41
|
+
_validate_proof_container(presentation.proof)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = ["validate_verifiable_credential", "validate_verifiable_presentation"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class VcdmVersion(StrEnum):
|
|
5
|
+
V1_1 = "1.1"
|
|
6
|
+
V2_0 = "2.0"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
CURRENT_VERSION = VcdmVersion.V2_0
|
|
10
|
+
VERSION_HISTORY = {
|
|
11
|
+
VcdmVersion.V1_1.value: {
|
|
12
|
+
"status": "recommendation",
|
|
13
|
+
"specification": "https://www.w3.org/TR/vc-data-model/",
|
|
14
|
+
},
|
|
15
|
+
VcdmVersion.V2_0.value: {
|
|
16
|
+
"status": "recommendation",
|
|
17
|
+
"specification": "https://www.w3.org/TR/vc-data-model-2.0/",
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def select_version(value: str | VcdmVersion) -> VcdmVersion:
|
|
23
|
+
return VcdmVersion(value)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__ = ["CURRENT_VERSION", "VERSION_HISTORY", "VcdmVersion", "select_version"]
|