@interop/wallet-core 0.1.1 → 0.2.0
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 +18 -2
- package/dist/display/alignment.d.ts +49 -0
- package/dist/display/alignment.d.ts.map +1 -0
- package/dist/display/alignment.js +114 -0
- package/dist/display/alignment.js.map +1 -0
- package/dist/display/credentialName.d.ts +42 -0
- package/dist/display/credentialName.d.ts.map +1 -0
- package/dist/display/credentialName.js +106 -0
- package/dist/display/credentialName.js.map +1 -0
- package/dist/display/displayFields.d.ts +55 -0
- package/dist/display/displayFields.d.ts.map +1 -0
- package/dist/display/displayFields.js +136 -0
- package/dist/display/displayFields.js.map +1 -0
- package/dist/display/evidence.d.ts +44 -0
- package/dist/display/evidence.d.ts.map +1 -0
- package/dist/display/evidence.js +65 -0
- package/dist/display/evidence.js.map +1 -0
- package/dist/display/image.d.ts +18 -0
- package/dist/display/image.d.ts.map +1 -0
- package/dist/display/image.js +23 -0
- package/dist/display/image.js.map +1 -0
- package/dist/display/index.d.ts +38 -0
- package/dist/display/index.d.ts.map +1 -0
- package/dist/display/index.js +32 -0
- package/dist/display/index.js.map +1 -0
- package/dist/display/issuer.d.ts +97 -0
- package/dist/display/issuer.d.ts.map +1 -0
- package/dist/display/issuer.js +126 -0
- package/dist/display/issuer.js.map +1 -0
- package/dist/display/obv3.d.ts +77 -0
- package/dist/display/obv3.d.ts.map +1 -0
- package/dist/display/obv3.js +127 -0
- package/dist/display/obv3.js.map +1 -0
- package/dist/display/parse.d.ts +62 -0
- package/dist/display/parse.d.ts.map +1 -0
- package/dist/display/parse.js +100 -0
- package/dist/display/parse.js.map +1 -0
- package/dist/display/subject.d.ts +75 -0
- package/dist/display/subject.d.ts.map +1 -0
- package/dist/display/subject.js +122 -0
- package/dist/display/subject.js.map +1 -0
- package/dist/display/text.d.ts +42 -0
- package/dist/display/text.d.ts.map +1 -0
- package/dist/display/text.js +62 -0
- package/dist/display/text.js.map +1 -0
- package/dist/display/types.d.ts +55 -0
- package/dist/display/types.d.ts.map +1 -0
- package/dist/display/types.js +41 -0
- package/dist/display/types.js.map +1 -0
- package/dist/display/validity.d.ts +51 -0
- package/dist/display/validity.d.ts.map +1 -0
- package/dist/display/validity.js +51 -0
- package/dist/display/validity.js.map +1 -0
- package/dist/display/verificationView.d.ts +101 -0
- package/dist/display/verificationView.d.ts.map +1 -0
- package/dist/display/verificationView.js +199 -0
- package/dist/display/verificationView.js.map +1 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/request/classify.d.ts +112 -0
- package/dist/request/classify.d.ts.map +1 -0
- package/dist/request/classify.js +210 -0
- package/dist/request/classify.js.map +1 -0
- package/dist/request/composeVp.d.ts +52 -0
- package/dist/request/composeVp.d.ts.map +1 -0
- package/dist/request/composeVp.js +177 -0
- package/dist/request/composeVp.js.map +1 -0
- package/dist/request/exchangeClient.d.ts +165 -0
- package/dist/request/exchangeClient.d.ts.map +1 -0
- package/dist/request/exchangeClient.js +212 -0
- package/dist/request/exchangeClient.js.map +1 -0
- package/dist/request/index.d.ts +35 -0
- package/dist/request/index.d.ts.map +1 -0
- package/dist/request/index.js +35 -0
- package/dist/request/index.js.map +1 -0
- package/dist/request/interactionUrl.d.ts +53 -0
- package/dist/request/interactionUrl.d.ts.map +1 -0
- package/dist/request/interactionUrl.js +83 -0
- package/dist/request/interactionUrl.js.map +1 -0
- package/dist/request/matching.d.ts +69 -0
- package/dist/request/matching.d.ts.map +1 -0
- package/dist/request/matching.js +183 -0
- package/dist/request/matching.js.map +1 -0
- package/dist/request/parse.d.ts +68 -0
- package/dist/request/parse.d.ts.map +1 -0
- package/dist/request/parse.js +114 -0
- package/dist/request/parse.js.map +1 -0
- package/dist/request/presentationSuite.d.ts +71 -0
- package/dist/request/presentationSuite.d.ts.map +1 -0
- package/dist/request/presentationSuite.js +139 -0
- package/dist/request/presentationSuite.js.map +1 -0
- package/dist/request/processRequest.d.ts +48 -0
- package/dist/request/processRequest.d.ts.map +1 -0
- package/dist/request/processRequest.js +139 -0
- package/dist/request/processRequest.js.map +1 -0
- package/dist/request/types.d.ts +150 -0
- package/dist/request/types.d.ts.map +1 -0
- package/dist/request/types.js +2 -0
- package/dist/request/types.js.map +1 -0
- package/dist/sync/types.d.ts.map +1 -1
- package/package.json +21 -2
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { typeArray } from '@interop/data-integrity-core/guards';
|
|
2
|
+
const VC_1_CONTEXT_URL = 'https://www.w3.org/2018/credentials/v1';
|
|
3
|
+
const VC_2_CONTEXT_URL = 'https://www.w3.org/ns/credentials/v2';
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a bare Verifiable Credential in an unsigned Verifiable Presentation,
|
|
6
|
+
* matching the credential's VC data model version so the presentation's
|
|
7
|
+
* `@context` stays coherent with the credential it carries.
|
|
8
|
+
*
|
|
9
|
+
* @param credential {IVerifiableCredential}
|
|
10
|
+
* @returns {IVerifiablePresentation}
|
|
11
|
+
*/
|
|
12
|
+
function presentationWrapping(credential) {
|
|
13
|
+
const contexts = credential['@context'];
|
|
14
|
+
const contextArray = Array.isArray(contexts) ? contexts : [contexts];
|
|
15
|
+
const isV2 = contextArray.includes(VC_2_CONTEXT_URL);
|
|
16
|
+
return {
|
|
17
|
+
'@context': [isV2 ? VC_2_CONTEXT_URL : VC_1_CONTEXT_URL],
|
|
18
|
+
type: ['VerifiablePresentation'],
|
|
19
|
+
verifiableCredential: [credential]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The offered payload as a Verifiable Presentation: passed through when the
|
|
24
|
+
* issuer already offered one, and wrapped when it offered a bare Verifiable
|
|
25
|
+
* Credential.
|
|
26
|
+
*
|
|
27
|
+
* @param credential {CHAPIStoreEvent['credential']}
|
|
28
|
+
* @returns {IVerifiablePresentation}
|
|
29
|
+
*/
|
|
30
|
+
function offeredPresentation({ dataType, data }) {
|
|
31
|
+
const types = typeArray(data?.type);
|
|
32
|
+
const isPresentation = dataType === 'VerifiablePresentation' ||
|
|
33
|
+
types.includes('VerifiablePresentation') ||
|
|
34
|
+
'verifiableCredential' in (data ?? {});
|
|
35
|
+
if (isPresentation) {
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
if (dataType === 'VerifiableCredential' ||
|
|
39
|
+
types.includes('VerifiableCredential')) {
|
|
40
|
+
return presentationWrapping(data);
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`CHAPI store event offered an unrecognized payload (dataType: ${dataType ?? 'undefined'}, type: ${JSON.stringify(types)}).`);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The Verifiable Credentials carried by a presentation, normalized to an array.
|
|
46
|
+
*
|
|
47
|
+
* @param presentation {IVerifiablePresentation}
|
|
48
|
+
* @returns {IVerifiableCredential[]}
|
|
49
|
+
*/
|
|
50
|
+
export function credentialsOf(presentation) {
|
|
51
|
+
const { verifiableCredential } = presentation;
|
|
52
|
+
if (!verifiableCredential) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
return Array.isArray(verifiableCredential)
|
|
56
|
+
? verifiableCredential
|
|
57
|
+
: [verifiableCredential];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Wraps a CHAPI get event as an `IVPRequest`.
|
|
61
|
+
*
|
|
62
|
+
* @param event {CHAPIGetEvent}
|
|
63
|
+
* @returns {IVPRequest}
|
|
64
|
+
*/
|
|
65
|
+
export function classifyCHAPIGetEvent(event) {
|
|
66
|
+
const verifiablePresentationRequest = event.credentialRequestOptions?.web?.VerifiablePresentation;
|
|
67
|
+
if (!verifiablePresentationRequest) {
|
|
68
|
+
throw new Error('CHAPI get event is missing a VerifiablePresentation request.');
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
verifiablePresentationRequest,
|
|
72
|
+
credentialRequestOrigin: event.credentialRequestOrigin
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Wraps a CHAPI store event as an `IVPOffer`. A bare offered credential is
|
|
77
|
+
* wrapped in an unsigned presentation, so downstream code always sees a VP.
|
|
78
|
+
*
|
|
79
|
+
* @param event {CHAPIStoreEvent}
|
|
80
|
+
* @returns {IVPOffer}
|
|
81
|
+
*/
|
|
82
|
+
export function classifyCHAPIStoreEvent(event) {
|
|
83
|
+
return {
|
|
84
|
+
verifiablePresentation: offeredPresentation(event.credential),
|
|
85
|
+
credentialRequestOrigin: event.credentialRequestOrigin
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Returns true if the query set contains a `DIDAuthentication` query. Throws if
|
|
90
|
+
* more than one is present -- a single DID-Auth proof answers the request.
|
|
91
|
+
*
|
|
92
|
+
* @param options {object}
|
|
93
|
+
* @param options.queries {IVPRQuery[]}
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
96
|
+
export function isDIDAuthRequested({ queries }) {
|
|
97
|
+
const didAuthRequests = queries.filter(q => q.type === 'DIDAuthentication');
|
|
98
|
+
if (didAuthRequests.length > 1) {
|
|
99
|
+
throw new Error('More than one DIDAuthentication request found, exiting.');
|
|
100
|
+
}
|
|
101
|
+
return didAuthRequests.length === 1;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Normalizes a VPR's `query` (which may be a single object or an array) to an
|
|
105
|
+
* array, dropping anything that is not a typed query object. A VPR body can
|
|
106
|
+
* legitimately carry no queries at all -- a CHAPI request that names a
|
|
107
|
+
* `protocols` exchange sends an empty body -- so callers get an empty array
|
|
108
|
+
* rather than an array holding `undefined`.
|
|
109
|
+
*
|
|
110
|
+
* @param request {IVPRDetails}
|
|
111
|
+
* @returns {IVPRQuery[]}
|
|
112
|
+
*/
|
|
113
|
+
export function queriesOf(request) {
|
|
114
|
+
const { query } = request;
|
|
115
|
+
const queries = Array.isArray(query) ? query : [query];
|
|
116
|
+
return queries.filter((entry) => !!entry &&
|
|
117
|
+
typeof entry === 'object' &&
|
|
118
|
+
typeof entry.type === 'string');
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Normalizes a `QueryByExample`'s `credentialQuery` (a single detail object or
|
|
122
|
+
* an array of them) to an array.
|
|
123
|
+
*
|
|
124
|
+
* @param query {IQueryByExample}
|
|
125
|
+
* @returns {ICredentialQuery[]}
|
|
126
|
+
*/
|
|
127
|
+
export function credentialQueriesOf(query) {
|
|
128
|
+
const { credentialQuery } = query;
|
|
129
|
+
if (!credentialQuery) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
return Array.isArray(credentialQuery) ? credentialQuery : [credentialQuery];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Collects the requested capabilities from a query set: filters the two zcap
|
|
136
|
+
* query type strings (`AuthorizationCapabilityQuery` canonical, `ZcapQuery`
|
|
137
|
+
* legacy alias), normalizes each `capabilityQuery` (object or array) to an
|
|
138
|
+
* array, and flattens. A zcap query whose `capabilityQuery` is missing or not
|
|
139
|
+
* an object is malformed -- there is nothing to ask consent for -- so it throws
|
|
140
|
+
* rather than letting an `undefined` descriptor reach grant resolution;
|
|
141
|
+
* classification-time callers surface the throw as a malformed-request state.
|
|
142
|
+
*
|
|
143
|
+
* @param queries {IVPRQuery[]}
|
|
144
|
+
* @returns {ICapabilityQueryDetail[]}
|
|
145
|
+
*/
|
|
146
|
+
export function zcapQueriesOf(queries) {
|
|
147
|
+
return queries
|
|
148
|
+
.filter((query) => query.type === 'AuthorizationCapabilityQuery' ||
|
|
149
|
+
query.type === 'ZcapQuery')
|
|
150
|
+
.flatMap(({ type, capabilityQuery }) => {
|
|
151
|
+
const detailEntries = Array.isArray(capabilityQuery)
|
|
152
|
+
? capabilityQuery
|
|
153
|
+
: [capabilityQuery];
|
|
154
|
+
for (const detail of detailEntries) {
|
|
155
|
+
if (!detail || typeof detail !== 'object') {
|
|
156
|
+
throw new Error(`A "${type}" query is missing its capabilityQuery detail.`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return detailEntries;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Classifies a VPR body onto the independent axes the consent screen and
|
|
164
|
+
* response assembly work from: whether DID Authentication is requested, and
|
|
165
|
+
* separately the credential (`QueryByExample`) and capability
|
|
166
|
+
* (`AuthorizationCapabilityQuery` / `ZcapQuery`) content asked for. Any
|
|
167
|
+
* combination is valid, including zcap-only.
|
|
168
|
+
*
|
|
169
|
+
* @param request {IVPRDetails}
|
|
170
|
+
* @returns {WalletRequestProfile}
|
|
171
|
+
*/
|
|
172
|
+
export function classifyRequest(request) {
|
|
173
|
+
const queries = queriesOf(request);
|
|
174
|
+
return {
|
|
175
|
+
didAuth: isDIDAuthRequested({ queries }),
|
|
176
|
+
vcQueries: queries.filter((query) => query.type === 'QueryByExample'),
|
|
177
|
+
zcapRequests: zcapQueriesOf(queries)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Whether a classified request is DID-Authentication *only*: it asks the wallet
|
|
182
|
+
* to prove control of its DID and nothing else (no credential queries, no
|
|
183
|
+
* capability requests). Derived from the profile so a popup's restore fast-path
|
|
184
|
+
* and its render both dispatch on the one predicate.
|
|
185
|
+
*
|
|
186
|
+
* @param profile {WalletRequestProfile}
|
|
187
|
+
* @returns {boolean}
|
|
188
|
+
*/
|
|
189
|
+
export function isDidAuthOnly(profile) {
|
|
190
|
+
return (profile.didAuth &&
|
|
191
|
+
profile.vcQueries.length === 0 &&
|
|
192
|
+
profile.zcapRequests.length === 0);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Returns true if the wallet can satisfy the DID method a `DIDAuthentication`
|
|
196
|
+
* query constrains to. A wallet holding only `did:key` can satisfy a request
|
|
197
|
+
* that lists `key` among `acceptedMethods` or omits the constraint entirely.
|
|
198
|
+
*
|
|
199
|
+
* @param queries {IVPRQuery[]}
|
|
200
|
+
* @returns {boolean}
|
|
201
|
+
*/
|
|
202
|
+
export function didAuthMethodSupported(queries) {
|
|
203
|
+
const didAuth = queries.find(query => query.type === 'DIDAuthentication');
|
|
204
|
+
const acceptedMethods = didAuth?.acceptedMethods;
|
|
205
|
+
if (!acceptedMethods || acceptedMethods.length === 0) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
return acceptedMethods.some(({ method }) => method === 'key');
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/request/classify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { IDocumentLoader } from '@interop/data-integrity-core';
|
|
2
|
+
import type { IVerifiablePresentation, IVerifiableCredential, IZcap, PresentationSigner } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Shared JSON-LD document loader for presentation and credential signing.
|
|
5
|
+
* Exported so single-VC issuance paths reuse the same context resolution the VP
|
|
6
|
+
* compose path uses.
|
|
7
|
+
*/
|
|
8
|
+
export declare const documentLoader: IDocumentLoader;
|
|
9
|
+
/**
|
|
10
|
+
* Creates a Verifiable Presentation for the requester.
|
|
11
|
+
*
|
|
12
|
+
* @param options {object}
|
|
13
|
+
* @param options.presentationSigner {PresentationSigner} - The authentication
|
|
14
|
+
* signer and the holder DID to name on a signed VP.
|
|
15
|
+
* @param [options.selectedVcs] {IVerifiableCredential[]} - VCs the user chose to
|
|
16
|
+
* share (empty for a DID-Auth-only or zcap-only response).
|
|
17
|
+
* @param [options.challenge] {string} - Required when DID Auth is requested.
|
|
18
|
+
* @param [options.domain] {string} - Signed into the proof when present;
|
|
19
|
+
* optional per the VPR spec.
|
|
20
|
+
* @param options.didAuthRequested {boolean} - Whether to sign the VP.
|
|
21
|
+
* @param [options.cryptosuite] {string} - Negotiated cryptosuite; falls back to
|
|
22
|
+
* the wallet default (Ed25519Signature2020) when absent.
|
|
23
|
+
* @param [options.zcaps] {IZcap[]} - Delegated capabilities to embed as the
|
|
24
|
+
* VP's `zcap` array (before signing, so a DIDAuth proof covers them).
|
|
25
|
+
* @param [options.appConnect] {{ firstRun: boolean }} - App Connect response
|
|
26
|
+
* marker to embed (before signing, like the grants).
|
|
27
|
+
* @param [options.vocabBaseIri] {string} - Vocabulary base IRI for the embedded
|
|
28
|
+
* term definitions; defaults to Freewallet's value.
|
|
29
|
+
* @param [options.documentLoader] {IDocumentLoader} - JSON-LD loader; defaults
|
|
30
|
+
* to the shared security loader.
|
|
31
|
+
* @returns {Promise<IVerifiablePresentation>}
|
|
32
|
+
*/
|
|
33
|
+
export declare function composeVp({ presentationSigner, selectedVcs, challenge, domain, didAuthRequested, cryptosuite, zcaps, appConnect, vocabBaseIri, documentLoader: loader }: {
|
|
34
|
+
presentationSigner: PresentationSigner;
|
|
35
|
+
selectedVcs?: IVerifiableCredential[];
|
|
36
|
+
challenge?: string;
|
|
37
|
+
domain?: string;
|
|
38
|
+
didAuthRequested: boolean;
|
|
39
|
+
cryptosuite?: string;
|
|
40
|
+
zcaps?: IZcap[];
|
|
41
|
+
appConnect?: {
|
|
42
|
+
firstRun: boolean;
|
|
43
|
+
};
|
|
44
|
+
vocabBaseIri?: string;
|
|
45
|
+
documentLoader?: IDocumentLoader;
|
|
46
|
+
}): Promise<IVerifiablePresentation>;
|
|
47
|
+
/**
|
|
48
|
+
* @deprecated Use {@link composeVp}. Retained for Freewallet, which imports the
|
|
49
|
+
* `composeVP` spelling.
|
|
50
|
+
*/
|
|
51
|
+
export declare const composeVP: typeof composeVp;
|
|
52
|
+
//# sourceMappingURL=composeVp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composeVp.d.ts","sourceRoot":"","sources":["../../src/request/composeVp.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAEnE,OAAO,KAAK,EACV,uBAAuB,EACvB,qBAAqB,EACrB,KAAK,EACL,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAEnB;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,eAEnB,CAAA;AAyGV;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,SAAS,CAAC,EAC9B,kBAAkB,EAClB,WAAgB,EAChB,SAAS,EACT,MAAM,EACN,gBAAgB,EAChB,WAAW,EACX,KAAU,EACV,UAAU,EACV,YAAqC,EACrC,cAAc,EAAE,MAAuB,EACxC,EAAE;IACD,kBAAkB,EAAE,kBAAkB,CAAA;IACtC,WAAW,CAAC,EAAE,qBAAqB,EAAE,CAAA;IACrC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gBAAgB,EAAE,OAAO,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,KAAK,EAAE,CAAA;IACf,UAAU,CAAC,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,eAAe,CAAA;CACjC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAoDnC;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,kBAAY,CAAA"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Composes a Verifiable Presentation to send back to a requester. The VP is
|
|
6
|
+
* signed when DID Authentication was requested (proving control of the holder's
|
|
7
|
+
* DID over the request's `challenge`, and `domain` when the verifier sends
|
|
8
|
+
* one), and unsigned otherwise.
|
|
9
|
+
*
|
|
10
|
+
* Merged from DCW's `app/lib/composeVp.ts` and Freewallet's
|
|
11
|
+
* `src/lib/walletRequest/composeVP.ts`. The signer and holder DID are injected
|
|
12
|
+
* as a {@link PresentationSigner} -- each app resolves them from its own key
|
|
13
|
+
* material -- and the optional zcap / appConnect embedding (grants ride inside
|
|
14
|
+
* the VP, added before signing so a DIDAuth proof covers them) is carried over
|
|
15
|
+
* from Freewallet. The DIDAuth guard follows DCW: `challenge` is required,
|
|
16
|
+
* `domain` is optional (a wallet that needs the stricter "domain always
|
|
17
|
+
* present" invariant enforces it in its own wrapper before calling here).
|
|
18
|
+
*/
|
|
19
|
+
import * as vc from '@interop/vc';
|
|
20
|
+
import { securityLoader } from '@interop/security-document-loader';
|
|
21
|
+
import { presentationSuiteFor } from './presentationSuite.js';
|
|
22
|
+
/**
|
|
23
|
+
* Shared JSON-LD document loader for presentation and credential signing.
|
|
24
|
+
* Exported so single-VC issuance paths reuse the same context resolution the VP
|
|
25
|
+
* compose path uses.
|
|
26
|
+
*/
|
|
27
|
+
export const documentLoader = securityLoader({
|
|
28
|
+
fetchRemoteContexts: true
|
|
29
|
+
}).build();
|
|
30
|
+
/**
|
|
31
|
+
* The default JSON-LD vocabulary base IRI for the embedded-grant term
|
|
32
|
+
* definitions. This IRI is canonicalized into the DIDAuth proof, so it must be
|
|
33
|
+
* byte-stable: the default is Freewallet's value, keeping Freewallet's signed
|
|
34
|
+
* output byte-identical after extraction. A wallet that embeds grants under a
|
|
35
|
+
* different vocabulary passes its own `vocabBaseIri`.
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_VOCAB_BASE_IRI = 'urn:freewallet:vocab#';
|
|
38
|
+
/** Reads a presentation's `@context` as a mutable array of entries. */
|
|
39
|
+
function contextEntries(presentation) {
|
|
40
|
+
const base = presentation['@context'];
|
|
41
|
+
return Array.isArray(base) ? [...base] : [base];
|
|
42
|
+
}
|
|
43
|
+
/** Writes a presentation's `@context` from an array of entries. */
|
|
44
|
+
function setContext(presentation, entries) {
|
|
45
|
+
;
|
|
46
|
+
presentation['@context'] = entries;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The bare `zcap` term definition appended to the VP `@context` when grants are
|
|
50
|
+
* embedded. Only the top-level term is defined (mapped to `${vocabBaseIri}zcap`);
|
|
51
|
+
* the zcap sub-contexts are *not* hoisted -- each embedded zcap self-describes
|
|
52
|
+
* via its own `@context`. Defining the term is what lets JSON-LD safe-mode
|
|
53
|
+
* canonicalization include (rather than reject) the grants, so the
|
|
54
|
+
* authentication proof genuinely covers them.
|
|
55
|
+
*/
|
|
56
|
+
function zcapTermContext(vocabBaseIri) {
|
|
57
|
+
return {
|
|
58
|
+
'@protected': true,
|
|
59
|
+
zcap: { '@id': `${vocabBaseIri}zcap`, '@container': '@set' }
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The `appConnect` term definition appended to the VP `@context` when an App
|
|
64
|
+
* Connect response marker is embedded. The member is a JSON literal
|
|
65
|
+
* (`@type: '@json'`) so its `firstRun` boolean canonicalizes as one opaque
|
|
66
|
+
* value; embedding happens before signing, so the DIDAuth proof covers the
|
|
67
|
+
* marker the same way it covers the grants.
|
|
68
|
+
*/
|
|
69
|
+
function appConnectTermContext(vocabBaseIri) {
|
|
70
|
+
return {
|
|
71
|
+
'@protected': true,
|
|
72
|
+
appConnect: { '@id': `${vocabBaseIri}appConnect`, '@type': '@json' }
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Embeds the delegated capabilities on the presentation and adds the bare
|
|
77
|
+
* `zcap` term to its `@context`.
|
|
78
|
+
*/
|
|
79
|
+
function embedZcaps(presentation, zcaps, vocabBaseIri) {
|
|
80
|
+
if (zcaps.length === 0) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
setContext(presentation, [
|
|
84
|
+
...contextEntries(presentation),
|
|
85
|
+
zcapTermContext(vocabBaseIri)
|
|
86
|
+
]);
|
|
87
|
+
presentation.zcap = zcaps;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Embeds the App Connect response marker (the wallet-provided `firstRun`
|
|
91
|
+
* signal) on the presentation and adds the `appConnect` term to its `@context`.
|
|
92
|
+
*/
|
|
93
|
+
function embedAppConnect(presentation, appConnect, vocabBaseIri) {
|
|
94
|
+
if (!appConnect) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
setContext(presentation, [
|
|
98
|
+
...contextEntries(presentation),
|
|
99
|
+
appConnectTermContext(vocabBaseIri)
|
|
100
|
+
]);
|
|
101
|
+
presentation.appConnect = appConnect;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Creates a Verifiable Presentation for the requester.
|
|
105
|
+
*
|
|
106
|
+
* @param options {object}
|
|
107
|
+
* @param options.presentationSigner {PresentationSigner} - The authentication
|
|
108
|
+
* signer and the holder DID to name on a signed VP.
|
|
109
|
+
* @param [options.selectedVcs] {IVerifiableCredential[]} - VCs the user chose to
|
|
110
|
+
* share (empty for a DID-Auth-only or zcap-only response).
|
|
111
|
+
* @param [options.challenge] {string} - Required when DID Auth is requested.
|
|
112
|
+
* @param [options.domain] {string} - Signed into the proof when present;
|
|
113
|
+
* optional per the VPR spec.
|
|
114
|
+
* @param options.didAuthRequested {boolean} - Whether to sign the VP.
|
|
115
|
+
* @param [options.cryptosuite] {string} - Negotiated cryptosuite; falls back to
|
|
116
|
+
* the wallet default (Ed25519Signature2020) when absent.
|
|
117
|
+
* @param [options.zcaps] {IZcap[]} - Delegated capabilities to embed as the
|
|
118
|
+
* VP's `zcap` array (before signing, so a DIDAuth proof covers them).
|
|
119
|
+
* @param [options.appConnect] {{ firstRun: boolean }} - App Connect response
|
|
120
|
+
* marker to embed (before signing, like the grants).
|
|
121
|
+
* @param [options.vocabBaseIri] {string} - Vocabulary base IRI for the embedded
|
|
122
|
+
* term definitions; defaults to Freewallet's value.
|
|
123
|
+
* @param [options.documentLoader] {IDocumentLoader} - JSON-LD loader; defaults
|
|
124
|
+
* to the shared security loader.
|
|
125
|
+
* @returns {Promise<IVerifiablePresentation>}
|
|
126
|
+
*/
|
|
127
|
+
export async function composeVp({ presentationSigner, selectedVcs = [], challenge, domain, didAuthRequested, cryptosuite, zcaps = [], appConnect, vocabBaseIri = DEFAULT_VOCAB_BASE_IRI, documentLoader: loader = documentLoader }) {
|
|
128
|
+
if (!didAuthRequested && selectedVcs.length === 0 && zcaps.length === 0) {
|
|
129
|
+
throw new Error('A VP requires credentials, capabilities, or a DID Auth request.');
|
|
130
|
+
}
|
|
131
|
+
if (didAuthRequested && !challenge) {
|
|
132
|
+
throw new Error('A "challenge" is required for DID Auth.');
|
|
133
|
+
}
|
|
134
|
+
if (!didAuthRequested) {
|
|
135
|
+
// Return an unsigned VP. verify: false skips per-VC validation (including
|
|
136
|
+
// expiration checks). A zcap-only response rides here: the grants are
|
|
137
|
+
// individually signed and controller-bound, so they need no VP proof.
|
|
138
|
+
const presentation = vc.createPresentation({
|
|
139
|
+
verifiableCredential: selectedVcs.length > 0 ? selectedVcs : undefined,
|
|
140
|
+
verify: false,
|
|
141
|
+
version: 1.0
|
|
142
|
+
});
|
|
143
|
+
embedZcaps(presentation, zcaps, vocabBaseIri);
|
|
144
|
+
embedAppConnect(presentation, appConnect, vocabBaseIri);
|
|
145
|
+
return presentation;
|
|
146
|
+
}
|
|
147
|
+
const { signer, holder } = presentationSigner;
|
|
148
|
+
// Sign with the cryptosuite the verifier requested (via VCALM
|
|
149
|
+
// `acceptedCryptosuites`), falling back to the wallet default. The suite
|
|
150
|
+
// dictates the VC data model version: eddsa-rdfc-2022 proofs require VC 2.0,
|
|
151
|
+
// the default Ed25519Signature2020 proof uses VC 1.0.
|
|
152
|
+
const { suite, version } = presentationSuiteFor({ signer, cryptosuite });
|
|
153
|
+
const presentation = vc.createPresentation({
|
|
154
|
+
holder,
|
|
155
|
+
verifiableCredential: selectedVcs.length > 0 ? selectedVcs : undefined,
|
|
156
|
+
verify: false,
|
|
157
|
+
version
|
|
158
|
+
});
|
|
159
|
+
// Embed the grants (and any App Connect marker) before signing so the
|
|
160
|
+
// authentication proof covers them; the grants additionally self-authenticate
|
|
161
|
+
// via their own delegation proofs and carry their own `@context`.
|
|
162
|
+
embedZcaps(presentation, zcaps, vocabBaseIri);
|
|
163
|
+
embedAppConnect(presentation, appConnect, vocabBaseIri);
|
|
164
|
+
return (await vc.signPresentation({
|
|
165
|
+
presentation,
|
|
166
|
+
challenge,
|
|
167
|
+
...(domain !== undefined && { domain }),
|
|
168
|
+
documentLoader: loader,
|
|
169
|
+
suite
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* @deprecated Use {@link composeVp}. Retained for Freewallet, which imports the
|
|
174
|
+
* `composeVP` spelling.
|
|
175
|
+
*/
|
|
176
|
+
export const composeVP = composeVp;
|
|
177
|
+
//# sourceMappingURL=composeVp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composeVp.js","sourceRoot":"","sources":["../../src/request/composeVp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* VC API exchange client. A verifier may hand the wallet a CHAPI request whose
|
|
6
|
+
* `VerifiablePresentation` body is empty and whose `protocols.vcapi` names an
|
|
7
|
+
* exchange URL instead: the real Verifiable Presentation Request lives on the
|
|
8
|
+
* verifier's exchange endpoint, and the wallet POSTs its response back there
|
|
9
|
+
* rather than (only) over the CHAPI channel. vcplayground.org's verifier does
|
|
10
|
+
* exactly this whenever it mints an exchange, which is always.
|
|
11
|
+
*
|
|
12
|
+
* Two calls make up the wallet's side of the exchange: `startExchange` (POST an
|
|
13
|
+
* empty body, receive the VPR) and `submitPresentation` (POST the composed VP
|
|
14
|
+
* to the VPR's presentation service endpoint, defaulting to the exchange URL).
|
|
15
|
+
* An issuance exchange runs the same two calls in the other direction.
|
|
16
|
+
*
|
|
17
|
+
* Ported from Freewallet's `src/lib/walletRequest/vcApiExchange.ts`, with the
|
|
18
|
+
* network transport injected ({@link FetchLike}, default `globalThis.fetch`) and
|
|
19
|
+
* DCW's `sendToExchanger` (the whole-`WalletResponse` POST, envelope carrying
|
|
20
|
+
* `zcap`) added alongside `submitPresentation` (VP-only body).
|
|
21
|
+
*
|
|
22
|
+
* @see https://w3c-ccg.github.io/vc-api/#exchange-examples
|
|
23
|
+
*/
|
|
24
|
+
import type { CHAPIProtocols, FetchLike, IVerifiablePresentation, IVPRDetails, IZcap, VCAPIExchangeResponse } from './types.js';
|
|
25
|
+
/**
|
|
26
|
+
* The exchange URL a CHAPI request defers to, if any. Present when the verifier
|
|
27
|
+
* or issuer chose an exchange-based protocol, in which case the request's VPR
|
|
28
|
+
* (or store) body is empty and everything of substance lives behind this URL.
|
|
29
|
+
*
|
|
30
|
+
* Two protocol handles carry such a URL: the classic `vcapi` key, and the
|
|
31
|
+
* `interact` key of the newer `chapi.interact()` API (a "meta" protocol whose
|
|
32
|
+
* URL is opaque -- the underlying exchange is negotiated behind it, exactly as
|
|
33
|
+
* with `vcapi`). Both are plain HTTP exchange endpoints the wallet POSTs to, so
|
|
34
|
+
* they are handled identically here; `interact` is preferred when a request
|
|
35
|
+
* carries both.
|
|
36
|
+
*
|
|
37
|
+
* @param options {object}
|
|
38
|
+
* @param [options.protocols] {CHAPIProtocols}
|
|
39
|
+
* @returns {string | undefined}
|
|
40
|
+
*/
|
|
41
|
+
export declare function vcApiExchangeUrl({ protocols }: {
|
|
42
|
+
protocols?: CHAPIProtocols;
|
|
43
|
+
}): string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Opens the exchange and retrieves the Verifiable Presentation Request the
|
|
46
|
+
* verifier is actually asking for. The wallet begins an exchange by POSTing an
|
|
47
|
+
* empty body; the reply carries the VPR.
|
|
48
|
+
*
|
|
49
|
+
* @param options {object}
|
|
50
|
+
* @param options.exchangeUrl {string}
|
|
51
|
+
* @param [options.fetch] {FetchLike}
|
|
52
|
+
* @returns {Promise<IVPRDetails>}
|
|
53
|
+
*/
|
|
54
|
+
export declare function startExchange({ exchangeUrl, fetch }: {
|
|
55
|
+
exchangeUrl: string;
|
|
56
|
+
fetch?: FetchLike;
|
|
57
|
+
}): Promise<IVPRDetails>;
|
|
58
|
+
/**
|
|
59
|
+
* Opens an exchange: the wallet's first message is always an empty JSON body,
|
|
60
|
+
* whether the exchange goes on to request a presentation (a verifier) or to
|
|
61
|
+
* offer one (an issuer).
|
|
62
|
+
*
|
|
63
|
+
* @param options {object}
|
|
64
|
+
* @param options.exchangeUrl {string}
|
|
65
|
+
* @param [options.fetch] {FetchLike}
|
|
66
|
+
* @returns {Promise<VCAPIExchangeResponse>}
|
|
67
|
+
*/
|
|
68
|
+
export declare function beginExchange({ exchangeUrl, fetch }: {
|
|
69
|
+
exchangeUrl: string;
|
|
70
|
+
fetch?: FetchLike;
|
|
71
|
+
}): Promise<VCAPIExchangeResponse>;
|
|
72
|
+
/**
|
|
73
|
+
* Delivers the wallet's composed presentation to a verifier's exchange and
|
|
74
|
+
* confirms the exchange finished. The exchange, not the CHAPI channel, is the
|
|
75
|
+
* verifier's system of record, so an unfinished (multi-step) reply is a failed
|
|
76
|
+
* delivery.
|
|
77
|
+
*
|
|
78
|
+
* @param options {object}
|
|
79
|
+
* @param options.request {IVPRDetails} - The VPR the exchange handed back.
|
|
80
|
+
* @param options.exchangeUrl {string}
|
|
81
|
+
* @param options.verifiablePresentation {IVerifiablePresentation}
|
|
82
|
+
* @param [options.fetch] {FetchLike}
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
*/
|
|
85
|
+
export declare function deliverPresentation({ request, exchangeUrl, verifiablePresentation, fetch }: {
|
|
86
|
+
request: IVPRDetails;
|
|
87
|
+
exchangeUrl: string;
|
|
88
|
+
verifiablePresentation: IVerifiablePresentation;
|
|
89
|
+
fetch?: FetchLike;
|
|
90
|
+
}): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Answers an issuance exchange's holder-binding step: POSTs the wallet's
|
|
93
|
+
* DID-Auth presentation and collects the credentials the issuer hands back in
|
|
94
|
+
* return. A reply carrying yet another `verifiablePresentationRequest` means a
|
|
95
|
+
* further round this wallet does not answer.
|
|
96
|
+
*
|
|
97
|
+
* @param options {object}
|
|
98
|
+
* @param options.request {IVPRDetails} - The VPR the exchange opened with.
|
|
99
|
+
* @param options.exchangeUrl {string}
|
|
100
|
+
* @param options.verifiablePresentation {IVerifiablePresentation} - The signed
|
|
101
|
+
* DID-Auth presentation proving control of the holder DID.
|
|
102
|
+
* @param [options.fetch] {FetchLike}
|
|
103
|
+
* @returns {Promise<IVerifiablePresentation>} The offered presentation.
|
|
104
|
+
*/
|
|
105
|
+
export declare function collectIssuedPresentation({ request, exchangeUrl, verifiablePresentation, fetch }: {
|
|
106
|
+
request: IVPRDetails;
|
|
107
|
+
exchangeUrl: string;
|
|
108
|
+
verifiablePresentation: IVerifiablePresentation;
|
|
109
|
+
fetch?: FetchLike;
|
|
110
|
+
}): Promise<IVerifiablePresentation>;
|
|
111
|
+
/**
|
|
112
|
+
* Where to POST the composed presentation: the VPR's unmediated HTTP
|
|
113
|
+
* presentation service, when it names one, else the exchange URL itself (which
|
|
114
|
+
* every exchange accepts, and which is all vcplayground.org's VPR offers).
|
|
115
|
+
*
|
|
116
|
+
* @param options {object}
|
|
117
|
+
* @param options.request {IVPRDetails}
|
|
118
|
+
* @param options.exchangeUrl {string}
|
|
119
|
+
* @returns {string}
|
|
120
|
+
*/
|
|
121
|
+
export declare function presentationEndpointFor({ request, exchangeUrl }: {
|
|
122
|
+
request: IVPRDetails;
|
|
123
|
+
exchangeUrl: string;
|
|
124
|
+
}): string;
|
|
125
|
+
/**
|
|
126
|
+
* Delivers the wallet's composed presentation to the exchange and returns the
|
|
127
|
+
* raw reply. The exchange is complete unless the reply carries a further
|
|
128
|
+
* `verifiablePresentationRequest`; the finalize helpers (`deliverPresentation`,
|
|
129
|
+
* `collectIssuedPresentation`) inspect the reply via `assertExchangeComplete`.
|
|
130
|
+
*
|
|
131
|
+
* @param options {object}
|
|
132
|
+
* @param options.request {IVPRDetails} - The VPR the exchange handed back.
|
|
133
|
+
* @param options.exchangeUrl {string}
|
|
134
|
+
* @param options.verifiablePresentation {IVerifiablePresentation}
|
|
135
|
+
* @param [options.fetch] {FetchLike}
|
|
136
|
+
* @returns {Promise<VCAPIExchangeResponse>}
|
|
137
|
+
*/
|
|
138
|
+
export declare function submitPresentation({ request, exchangeUrl, verifiablePresentation, fetch }: {
|
|
139
|
+
request: IVPRDetails;
|
|
140
|
+
exchangeUrl: string;
|
|
141
|
+
verifiablePresentation: IVerifiablePresentation;
|
|
142
|
+
fetch?: FetchLike;
|
|
143
|
+
}): Promise<VCAPIExchangeResponse>;
|
|
144
|
+
/**
|
|
145
|
+
* Sends the whole Wallet Response object -- a VP and/or delegated `zcap`s -- to
|
|
146
|
+
* an exchanger endpoint (DCW's response envelope, which threads the grants
|
|
147
|
+
* *beside* the VP rather than embedding them inside it). Returns the parsed JSON
|
|
148
|
+
* reply, or `null` for an empty body. Kept alongside `submitPresentation` (whose
|
|
149
|
+
* envelope carries only the VP) because the two apps POST different shapes.
|
|
150
|
+
*
|
|
151
|
+
* @param options {object}
|
|
152
|
+
* @param options.exchangeUrl {string}
|
|
153
|
+
* @param options.payload {{ verifiablePresentation?, zcap? }}
|
|
154
|
+
* @param [options.fetch] {FetchLike}
|
|
155
|
+
* @returns {Promise<unknown>}
|
|
156
|
+
*/
|
|
157
|
+
export declare function sendToExchanger({ exchangeUrl, payload, fetch }: {
|
|
158
|
+
exchangeUrl: string;
|
|
159
|
+
payload: {
|
|
160
|
+
verifiablePresentation?: IVerifiablePresentation;
|
|
161
|
+
zcap?: IZcap[];
|
|
162
|
+
};
|
|
163
|
+
fetch?: FetchLike;
|
|
164
|
+
}): Promise<unknown>;
|
|
165
|
+
//# sourceMappingURL=exchangeClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exchangeClient.d.ts","sourceRoot":"","sources":["../../src/request/exchangeClient.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,uBAAuB,EACvB,WAAW,EACX,KAAK,EACL,qBAAqB,EACtB,MAAM,YAAY,CAAA;AAQnB;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACV,EAAE;IACD,SAAS,CAAC,EAAE,cAAc,CAAA;CAC3B,GAAG,MAAM,GAAG,SAAS,CAGrB;AA6CD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,EAClC,WAAW,EACX,KAAwB,EACzB,EAAE;IACD,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,GAAG,OAAO,CAAC,WAAW,CAAC,CAYvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,EAClC,WAAW,EACX,KAAwB,EACzB,EAAE;IACD,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAEjC;AAwBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,OAAO,EACP,WAAW,EACX,sBAAsB,EACtB,KAAwB,EACzB,EAAE;IACD,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,sBAAsB,EAAE,uBAAuB,CAAA;IAC/C,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,yBAAyB,CAAC,EAC9C,OAAO,EACP,WAAW,EACX,sBAAsB,EACtB,KAAwB,EACzB,EAAE;IACD,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,sBAAsB,EAAE,uBAAuB,CAAA;IAC/C,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAcnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACP,WAAW,EACZ,EAAE;IACD,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;CACpB,GAAG,MAAM,CAOT;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,OAAO,EACP,WAAW,EACX,sBAAsB,EACtB,KAAwB,EACzB,EAAE;IACD,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,sBAAsB,EAAE,uBAAuB,CAAA;IAC/C,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAMjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CAAC,EACpC,WAAW,EACX,OAAO,EACP,KAAwB,EACzB,EAAE;IACD,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE;QACP,sBAAsB,CAAC,EAAE,uBAAuB,CAAA;QAChD,IAAI,CAAC,EAAE,KAAK,EAAE,CAAA;KACf,CAAA;IACD,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,GAAG,OAAO,CAAC,OAAO,CAAC,CAQnB"}
|