@interop/wallet-core 0.1.1 → 0.3.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 +27 -3
- 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/identity/agents.d.ts +68 -0
- package/dist/identity/agents.d.ts.map +1 -0
- package/dist/identity/agents.js +95 -0
- package/dist/identity/agents.js.map +1 -0
- package/dist/identity/index.d.ts +20 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +19 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/keyResolver.d.ts +30 -0
- package/dist/identity/keyResolver.d.ts.map +1 -0
- package/dist/identity/keyResolver.js +25 -0
- package/dist/identity/keyResolver.js.map +1 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -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 +31 -3
|
@@ -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"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `interact.service` type naming an endpoint that accepts a Verifiable
|
|
3
|
+
* Presentation over plain HTTP POST, with no mediator in between.
|
|
4
|
+
*/
|
|
5
|
+
const PRESENTATION_SERVICE_TYPE = 'UnmediatedHttpPresentationService2021';
|
|
6
|
+
/**
|
|
7
|
+
* The exchange URL a CHAPI request defers to, if any. Present when the verifier
|
|
8
|
+
* or issuer chose an exchange-based protocol, in which case the request's VPR
|
|
9
|
+
* (or store) body is empty and everything of substance lives behind this URL.
|
|
10
|
+
*
|
|
11
|
+
* Two protocol handles carry such a URL: the classic `vcapi` key, and the
|
|
12
|
+
* `interact` key of the newer `chapi.interact()` API (a "meta" protocol whose
|
|
13
|
+
* URL is opaque -- the underlying exchange is negotiated behind it, exactly as
|
|
14
|
+
* with `vcapi`). Both are plain HTTP exchange endpoints the wallet POSTs to, so
|
|
15
|
+
* they are handled identically here; `interact` is preferred when a request
|
|
16
|
+
* carries both.
|
|
17
|
+
*
|
|
18
|
+
* @param options {object}
|
|
19
|
+
* @param [options.protocols] {CHAPIProtocols}
|
|
20
|
+
* @returns {string | undefined}
|
|
21
|
+
*/
|
|
22
|
+
export function vcApiExchangeUrl({ protocols }) {
|
|
23
|
+
const candidates = [protocols?.interact, protocols?.vcapi];
|
|
24
|
+
return candidates.find(url => typeof url === 'string' && url.length > 0);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* POSTs a JSON body to an exchange endpoint and parses the reply. A 2xx with an
|
|
28
|
+
* empty body is normal (a completed exchange), and yields `{}`.
|
|
29
|
+
*
|
|
30
|
+
* @param options {object}
|
|
31
|
+
* @param options.url {string}
|
|
32
|
+
* @param options.body {object}
|
|
33
|
+
* @param options.fetch {FetchLike}
|
|
34
|
+
* @returns {Promise<VCAPIExchangeResponse>}
|
|
35
|
+
*/
|
|
36
|
+
async function postToExchange({ url, body, fetch }) {
|
|
37
|
+
const response = await fetch(url, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
|
40
|
+
body: JSON.stringify(body)
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(`The exchange at ${url} responded ${response.status} ` +
|
|
44
|
+
`${response.statusText}.`);
|
|
45
|
+
}
|
|
46
|
+
const text = await response.text();
|
|
47
|
+
if (!text) {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(text);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
throw new Error(`The exchange at ${url} returned malformed JSON.`, {
|
|
55
|
+
cause: err
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Opens the exchange and retrieves the Verifiable Presentation Request the
|
|
61
|
+
* verifier is actually asking for. The wallet begins an exchange by POSTing an
|
|
62
|
+
* empty body; the reply carries the VPR.
|
|
63
|
+
*
|
|
64
|
+
* @param options {object}
|
|
65
|
+
* @param options.exchangeUrl {string}
|
|
66
|
+
* @param [options.fetch] {FetchLike}
|
|
67
|
+
* @returns {Promise<IVPRDetails>}
|
|
68
|
+
*/
|
|
69
|
+
export async function startExchange({ exchangeUrl, fetch = globalThis.fetch }) {
|
|
70
|
+
const { verifiablePresentationRequest } = await beginExchange({
|
|
71
|
+
exchangeUrl,
|
|
72
|
+
fetch
|
|
73
|
+
});
|
|
74
|
+
if (!verifiablePresentationRequest) {
|
|
75
|
+
throw new Error(`The exchange at ${exchangeUrl} did not return a ` +
|
|
76
|
+
'verifiablePresentationRequest.');
|
|
77
|
+
}
|
|
78
|
+
return verifiablePresentationRequest;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Opens an exchange: the wallet's first message is always an empty JSON body,
|
|
82
|
+
* whether the exchange goes on to request a presentation (a verifier) or to
|
|
83
|
+
* offer one (an issuer).
|
|
84
|
+
*
|
|
85
|
+
* @param options {object}
|
|
86
|
+
* @param options.exchangeUrl {string}
|
|
87
|
+
* @param [options.fetch] {FetchLike}
|
|
88
|
+
* @returns {Promise<VCAPIExchangeResponse>}
|
|
89
|
+
*/
|
|
90
|
+
export async function beginExchange({ exchangeUrl, fetch = globalThis.fetch }) {
|
|
91
|
+
return postToExchange({ url: exchangeUrl, body: {}, fetch });
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Reply inspection shared by both exchange directions. This wallet answers a
|
|
95
|
+
* single round, so a reply carrying a further `verifiablePresentationRequest` is
|
|
96
|
+
* a multi-step exchange it cannot continue: throw rather than pretend the
|
|
97
|
+
* exchange closed. A reply without one is a completed round and returns
|
|
98
|
+
* normally.
|
|
99
|
+
*/
|
|
100
|
+
function assertExchangeComplete({ reply, exchangeUrl }) {
|
|
101
|
+
if (reply.verifiablePresentationRequest) {
|
|
102
|
+
throw new Error(`The exchange at ${exchangeUrl} asked for a further presentation; ` +
|
|
103
|
+
'multi-step exchanges are not supported.');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Delivers the wallet's composed presentation to a verifier's exchange and
|
|
108
|
+
* confirms the exchange finished. The exchange, not the CHAPI channel, is the
|
|
109
|
+
* verifier's system of record, so an unfinished (multi-step) reply is a failed
|
|
110
|
+
* delivery.
|
|
111
|
+
*
|
|
112
|
+
* @param options {object}
|
|
113
|
+
* @param options.request {IVPRDetails} - The VPR the exchange handed back.
|
|
114
|
+
* @param options.exchangeUrl {string}
|
|
115
|
+
* @param options.verifiablePresentation {IVerifiablePresentation}
|
|
116
|
+
* @param [options.fetch] {FetchLike}
|
|
117
|
+
* @returns {Promise<void>}
|
|
118
|
+
*/
|
|
119
|
+
export async function deliverPresentation({ request, exchangeUrl, verifiablePresentation, fetch = globalThis.fetch }) {
|
|
120
|
+
const reply = await submitPresentation({
|
|
121
|
+
request,
|
|
122
|
+
exchangeUrl,
|
|
123
|
+
verifiablePresentation,
|
|
124
|
+
fetch
|
|
125
|
+
});
|
|
126
|
+
assertExchangeComplete({ reply, exchangeUrl });
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Answers an issuance exchange's holder-binding step: POSTs the wallet's
|
|
130
|
+
* DID-Auth presentation and collects the credentials the issuer hands back in
|
|
131
|
+
* return. A reply carrying yet another `verifiablePresentationRequest` means a
|
|
132
|
+
* further round this wallet does not answer.
|
|
133
|
+
*
|
|
134
|
+
* @param options {object}
|
|
135
|
+
* @param options.request {IVPRDetails} - The VPR the exchange opened with.
|
|
136
|
+
* @param options.exchangeUrl {string}
|
|
137
|
+
* @param options.verifiablePresentation {IVerifiablePresentation} - The signed
|
|
138
|
+
* DID-Auth presentation proving control of the holder DID.
|
|
139
|
+
* @param [options.fetch] {FetchLike}
|
|
140
|
+
* @returns {Promise<IVerifiablePresentation>} The offered presentation.
|
|
141
|
+
*/
|
|
142
|
+
export async function collectIssuedPresentation({ request, exchangeUrl, verifiablePresentation, fetch = globalThis.fetch }) {
|
|
143
|
+
const reply = await submitPresentation({
|
|
144
|
+
request,
|
|
145
|
+
exchangeUrl,
|
|
146
|
+
verifiablePresentation,
|
|
147
|
+
fetch
|
|
148
|
+
});
|
|
149
|
+
if (reply.verifiablePresentation) {
|
|
150
|
+
return reply.verifiablePresentation;
|
|
151
|
+
}
|
|
152
|
+
assertExchangeComplete({ reply, exchangeUrl });
|
|
153
|
+
throw new Error(`The exchange at ${exchangeUrl} offered no verifiablePresentation.`);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Where to POST the composed presentation: the VPR's unmediated HTTP
|
|
157
|
+
* presentation service, when it names one, else the exchange URL itself (which
|
|
158
|
+
* every exchange accepts, and which is all vcplayground.org's VPR offers).
|
|
159
|
+
*
|
|
160
|
+
* @param options {object}
|
|
161
|
+
* @param options.request {IVPRDetails}
|
|
162
|
+
* @param options.exchangeUrl {string}
|
|
163
|
+
* @returns {string}
|
|
164
|
+
*/
|
|
165
|
+
export function presentationEndpointFor({ request, exchangeUrl }) {
|
|
166
|
+
const services = request.interact?.service ?? [];
|
|
167
|
+
const unmediated = services.find(({ type, serviceEndpoint }) => type === PRESENTATION_SERVICE_TYPE && !!serviceEndpoint);
|
|
168
|
+
return unmediated?.serviceEndpoint ?? exchangeUrl;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Delivers the wallet's composed presentation to the exchange and returns the
|
|
172
|
+
* raw reply. The exchange is complete unless the reply carries a further
|
|
173
|
+
* `verifiablePresentationRequest`; the finalize helpers (`deliverPresentation`,
|
|
174
|
+
* `collectIssuedPresentation`) inspect the reply via `assertExchangeComplete`.
|
|
175
|
+
*
|
|
176
|
+
* @param options {object}
|
|
177
|
+
* @param options.request {IVPRDetails} - The VPR the exchange handed back.
|
|
178
|
+
* @param options.exchangeUrl {string}
|
|
179
|
+
* @param options.verifiablePresentation {IVerifiablePresentation}
|
|
180
|
+
* @param [options.fetch] {FetchLike}
|
|
181
|
+
* @returns {Promise<VCAPIExchangeResponse>}
|
|
182
|
+
*/
|
|
183
|
+
export async function submitPresentation({ request, exchangeUrl, verifiablePresentation, fetch = globalThis.fetch }) {
|
|
184
|
+
return postToExchange({
|
|
185
|
+
url: presentationEndpointFor({ request, exchangeUrl }),
|
|
186
|
+
body: { verifiablePresentation },
|
|
187
|
+
fetch
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Sends the whole Wallet Response object -- a VP and/or delegated `zcap`s -- to
|
|
192
|
+
* an exchanger endpoint (DCW's response envelope, which threads the grants
|
|
193
|
+
* *beside* the VP rather than embedding them inside it). Returns the parsed JSON
|
|
194
|
+
* reply, or `null` for an empty body. Kept alongside `submitPresentation` (whose
|
|
195
|
+
* envelope carries only the VP) because the two apps POST different shapes.
|
|
196
|
+
*
|
|
197
|
+
* @param options {object}
|
|
198
|
+
* @param options.exchangeUrl {string}
|
|
199
|
+
* @param options.payload {{ verifiablePresentation?, zcap? }}
|
|
200
|
+
* @param [options.fetch] {FetchLike}
|
|
201
|
+
* @returns {Promise<unknown>}
|
|
202
|
+
*/
|
|
203
|
+
export async function sendToExchanger({ exchangeUrl, payload, fetch = globalThis.fetch }) {
|
|
204
|
+
const response = await fetch(exchangeUrl, {
|
|
205
|
+
method: 'POST',
|
|
206
|
+
headers: { 'Content-Type': 'application/json' },
|
|
207
|
+
body: JSON.stringify(payload)
|
|
208
|
+
});
|
|
209
|
+
const text = await response.text();
|
|
210
|
+
return text ? JSON.parse(text) : null;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=exchangeClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exchangeClient.js","sourceRoot":"","sources":["../../src/request/exchangeClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The `@interop/wallet-core/request` subpath: wallet-request / exchange-protocol
|
|
6
|
+
* handling shared by the Interop wallet apps (DCW and Freewallet).
|
|
7
|
+
*
|
|
8
|
+
* - `types` re-exports the VPR message vocabulary from
|
|
9
|
+
* `@interop/data-integrity-core` and declares the request-local types (CHAPI
|
|
10
|
+
* events, the classified-request profile, and the `PresentationSigner` /
|
|
11
|
+
* `FetchLike` / `RequestProcessors` injection seams).
|
|
12
|
+
* - `parse` / `classify` turn a raw URL, JSON string, or CHAPI event into a
|
|
13
|
+
* typed message and dispatch on what it asks for.
|
|
14
|
+
* - `matching` filters stored credentials against a QueryByExample (both the
|
|
15
|
+
* jsonpath deep matcher and the type/issuer matcher).
|
|
16
|
+
* - `presentationSuite` negotiates the response cryptosuite; `composeVp` builds
|
|
17
|
+
* the (optionally signed, optionally grant-embedding) response VP.
|
|
18
|
+
* - `exchangeClient` is the fetch-injectable VC-API exchange client;
|
|
19
|
+
* `interactionUrl` resolves VCALM `interaction:` URLs.
|
|
20
|
+
* - `processRequest` is the pure request-to-response pipeline, with the
|
|
21
|
+
* app-side side effects injected.
|
|
22
|
+
*
|
|
23
|
+
* The signing / document-loader dependency graph lives entirely behind this
|
|
24
|
+
* subpath; `@interop/wallet-core`'s plaintext consumers never pull it in.
|
|
25
|
+
*/
|
|
26
|
+
export * from './types.js';
|
|
27
|
+
export * from './parse.js';
|
|
28
|
+
export * from './classify.js';
|
|
29
|
+
export * from './matching.js';
|
|
30
|
+
export * from './presentationSuite.js';
|
|
31
|
+
export * from './composeVp.js';
|
|
32
|
+
export * from './exchangeClient.js';
|
|
33
|
+
export * from './interactionUrl.js';
|
|
34
|
+
export * from './processRequest.js';
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/request/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The `@interop/wallet-core/request` subpath: wallet-request / exchange-protocol
|
|
6
|
+
* handling shared by the Interop wallet apps (DCW and Freewallet).
|
|
7
|
+
*
|
|
8
|
+
* - `types` re-exports the VPR message vocabulary from
|
|
9
|
+
* `@interop/data-integrity-core` and declares the request-local types (CHAPI
|
|
10
|
+
* events, the classified-request profile, and the `PresentationSigner` /
|
|
11
|
+
* `FetchLike` / `RequestProcessors` injection seams).
|
|
12
|
+
* - `parse` / `classify` turn a raw URL, JSON string, or CHAPI event into a
|
|
13
|
+
* typed message and dispatch on what it asks for.
|
|
14
|
+
* - `matching` filters stored credentials against a QueryByExample (both the
|
|
15
|
+
* jsonpath deep matcher and the type/issuer matcher).
|
|
16
|
+
* - `presentationSuite` negotiates the response cryptosuite; `composeVp` builds
|
|
17
|
+
* the (optionally signed, optionally grant-embedding) response VP.
|
|
18
|
+
* - `exchangeClient` is the fetch-injectable VC-API exchange client;
|
|
19
|
+
* `interactionUrl` resolves VCALM `interaction:` URLs.
|
|
20
|
+
* - `processRequest` is the pure request-to-response pipeline, with the
|
|
21
|
+
* app-side side effects injected.
|
|
22
|
+
*
|
|
23
|
+
* The signing / document-loader dependency graph lives entirely behind this
|
|
24
|
+
* subpath; `@interop/wallet-core`'s plaintext consumers never pull it in.
|
|
25
|
+
*/
|
|
26
|
+
export * from './types.js';
|
|
27
|
+
export * from './parse.js';
|
|
28
|
+
export * from './classify.js';
|
|
29
|
+
export * from './matching.js';
|
|
30
|
+
export * from './presentationSuite.js';
|
|
31
|
+
export * from './composeVp.js';
|
|
32
|
+
export * from './exchangeClient.js';
|
|
33
|
+
export * from './interactionUrl.js';
|
|
34
|
+
export * from './processRequest.js';
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/request/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA"}
|