@interop/wallet-core 0.1.0 → 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.
Files changed (103) hide show
  1. package/README.md +18 -2
  2. package/dist/display/alignment.d.ts +49 -0
  3. package/dist/display/alignment.d.ts.map +1 -0
  4. package/dist/display/alignment.js +114 -0
  5. package/dist/display/alignment.js.map +1 -0
  6. package/dist/display/credentialName.d.ts +42 -0
  7. package/dist/display/credentialName.d.ts.map +1 -0
  8. package/dist/display/credentialName.js +106 -0
  9. package/dist/display/credentialName.js.map +1 -0
  10. package/dist/display/displayFields.d.ts +55 -0
  11. package/dist/display/displayFields.d.ts.map +1 -0
  12. package/dist/display/displayFields.js +136 -0
  13. package/dist/display/displayFields.js.map +1 -0
  14. package/dist/display/evidence.d.ts +44 -0
  15. package/dist/display/evidence.d.ts.map +1 -0
  16. package/dist/display/evidence.js +65 -0
  17. package/dist/display/evidence.js.map +1 -0
  18. package/dist/display/image.d.ts +18 -0
  19. package/dist/display/image.d.ts.map +1 -0
  20. package/dist/display/image.js +23 -0
  21. package/dist/display/image.js.map +1 -0
  22. package/dist/display/index.d.ts +38 -0
  23. package/dist/display/index.d.ts.map +1 -0
  24. package/dist/display/index.js +32 -0
  25. package/dist/display/index.js.map +1 -0
  26. package/dist/display/issuer.d.ts +97 -0
  27. package/dist/display/issuer.d.ts.map +1 -0
  28. package/dist/display/issuer.js +126 -0
  29. package/dist/display/issuer.js.map +1 -0
  30. package/dist/display/obv3.d.ts +77 -0
  31. package/dist/display/obv3.d.ts.map +1 -0
  32. package/dist/display/obv3.js +127 -0
  33. package/dist/display/obv3.js.map +1 -0
  34. package/dist/display/parse.d.ts +62 -0
  35. package/dist/display/parse.d.ts.map +1 -0
  36. package/dist/display/parse.js +100 -0
  37. package/dist/display/parse.js.map +1 -0
  38. package/dist/display/subject.d.ts +75 -0
  39. package/dist/display/subject.d.ts.map +1 -0
  40. package/dist/display/subject.js +122 -0
  41. package/dist/display/subject.js.map +1 -0
  42. package/dist/display/text.d.ts +42 -0
  43. package/dist/display/text.d.ts.map +1 -0
  44. package/dist/display/text.js +62 -0
  45. package/dist/display/text.js.map +1 -0
  46. package/dist/display/types.d.ts +55 -0
  47. package/dist/display/types.d.ts.map +1 -0
  48. package/dist/display/types.js +41 -0
  49. package/dist/display/types.js.map +1 -0
  50. package/dist/display/validity.d.ts +51 -0
  51. package/dist/display/validity.d.ts.map +1 -0
  52. package/dist/display/validity.js +51 -0
  53. package/dist/display/validity.js.map +1 -0
  54. package/dist/display/verificationView.d.ts +101 -0
  55. package/dist/display/verificationView.d.ts.map +1 -0
  56. package/dist/display/verificationView.js +199 -0
  57. package/dist/display/verificationView.js.map +1 -0
  58. package/dist/index.d.ts +11 -2
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +11 -2
  61. package/dist/index.js.map +1 -1
  62. package/dist/request/classify.d.ts +112 -0
  63. package/dist/request/classify.d.ts.map +1 -0
  64. package/dist/request/classify.js +210 -0
  65. package/dist/request/classify.js.map +1 -0
  66. package/dist/request/composeVp.d.ts +52 -0
  67. package/dist/request/composeVp.d.ts.map +1 -0
  68. package/dist/request/composeVp.js +177 -0
  69. package/dist/request/composeVp.js.map +1 -0
  70. package/dist/request/exchangeClient.d.ts +165 -0
  71. package/dist/request/exchangeClient.d.ts.map +1 -0
  72. package/dist/request/exchangeClient.js +212 -0
  73. package/dist/request/exchangeClient.js.map +1 -0
  74. package/dist/request/index.d.ts +35 -0
  75. package/dist/request/index.d.ts.map +1 -0
  76. package/dist/request/index.js +35 -0
  77. package/dist/request/index.js.map +1 -0
  78. package/dist/request/interactionUrl.d.ts +53 -0
  79. package/dist/request/interactionUrl.d.ts.map +1 -0
  80. package/dist/request/interactionUrl.js +83 -0
  81. package/dist/request/interactionUrl.js.map +1 -0
  82. package/dist/request/matching.d.ts +69 -0
  83. package/dist/request/matching.d.ts.map +1 -0
  84. package/dist/request/matching.js +183 -0
  85. package/dist/request/matching.js.map +1 -0
  86. package/dist/request/parse.d.ts +68 -0
  87. package/dist/request/parse.d.ts.map +1 -0
  88. package/dist/request/parse.js +114 -0
  89. package/dist/request/parse.js.map +1 -0
  90. package/dist/request/presentationSuite.d.ts +71 -0
  91. package/dist/request/presentationSuite.d.ts.map +1 -0
  92. package/dist/request/presentationSuite.js +139 -0
  93. package/dist/request/presentationSuite.js.map +1 -0
  94. package/dist/request/processRequest.d.ts +48 -0
  95. package/dist/request/processRequest.d.ts.map +1 -0
  96. package/dist/request/processRequest.js +139 -0
  97. package/dist/request/processRequest.js.map +1 -0
  98. package/dist/request/types.d.ts +150 -0
  99. package/dist/request/types.d.ts.map +1 -0
  100. package/dist/request/types.js +2 -0
  101. package/dist/request/types.js.map +1 -0
  102. package/dist/sync/types.d.ts.map +1 -1
  103. package/package.json +23 -3
@@ -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"}
@@ -0,0 +1,53 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * VCALM `interaction:` URL handling: detecting an interaction URL, normalizing
6
+ * it to its inner HTTPS URL, and fetching the protocols map it advertises. An
7
+ * interaction URL is a level of indirection a verifier / issuer hands the wallet
8
+ * (over a deep link or QR code) that resolves to a `protocols` map naming the
9
+ * real exchange endpoints.
10
+ *
11
+ * Ported verbatim from DCW's `app/lib/interactionUrl.ts`, with the network
12
+ * transport injected ({@link FetchLike}, default `globalThis.fetch`).
13
+ */
14
+ import type { FetchLike } from './types.js';
15
+ /**
16
+ * Detects whether a URL is a VCALM interaction URL.
17
+ *
18
+ * Two formats:
19
+ * - `interaction:` scheme: `interaction:https://example.com/path?iuv=1`
20
+ * - HTTPS URL with an `iuv` query param: `https://example.com/path?iuv=1`
21
+ *
22
+ * @param url {string}
23
+ * @returns {boolean}
24
+ */
25
+ export declare function isInteractionUrl(url: string): boolean;
26
+ /**
27
+ * Normalizes any interaction URL to the inner HTTPS interaction URL. Accepts any
28
+ * URL that passed {@link isInteractionUrl}.
29
+ *
30
+ * - `interaction:https://...?iuv=1` strips the prefix
31
+ * - `interaction:https%3A%2F%2F...` strips the prefix and decodes (URL-encoded
32
+ * after `interaction:` is non-standard, but handled for robustness)
33
+ * - `https://...?iuv=1` is returned as-is
34
+ *
35
+ * @param url {string}
36
+ * @returns {string}
37
+ */
38
+ export declare function parseInteractionUrl(url: string): string;
39
+ /**
40
+ * Fetches the interaction protocols response from a VCALM interaction URL. Sends
41
+ * GET with `Accept: application/json` per the VCALM spec, and returns the
42
+ * protocols map. Logs a warning if the `iuv` param value is not `1`; throws if
43
+ * the response is not ok or the `protocols` key is missing.
44
+ *
45
+ * @param interactionUrl {string}
46
+ * @param [options] {object}
47
+ * @param [options.fetch] {FetchLike}
48
+ * @returns {Promise<Record<string, string>>}
49
+ */
50
+ export declare function fetchInteractionProtocols(interactionUrl: string, { fetch }?: {
51
+ fetch?: FetchLike;
52
+ }): Promise<Record<string, string>>;
53
+ //# sourceMappingURL=interactionUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactionUrl.d.ts","sourceRoot":"","sources":["../../src/request/interactionUrl.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAI3C;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAUrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWvD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,yBAAyB,CAC7C,cAAc,EAAE,MAAM,EACtB,EAAE,KAAwB,EAAE,GAAE;IAAE,KAAK,CAAC,EAAE,SAAS,CAAA;CAAO,GACvD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA+BjC"}
@@ -0,0 +1,83 @@
1
+ const INTERACTION_SCHEME_PREFIX = 'interaction:';
2
+ /**
3
+ * Detects whether a URL is a VCALM interaction URL.
4
+ *
5
+ * Two formats:
6
+ * - `interaction:` scheme: `interaction:https://example.com/path?iuv=1`
7
+ * - HTTPS URL with an `iuv` query param: `https://example.com/path?iuv=1`
8
+ *
9
+ * @param url {string}
10
+ * @returns {boolean}
11
+ */
12
+ export function isInteractionUrl(url) {
13
+ if (url.startsWith(INTERACTION_SCHEME_PREFIX)) {
14
+ return true;
15
+ }
16
+ try {
17
+ const parsed = new URL(url);
18
+ return parsed.searchParams.has('iuv');
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ /**
25
+ * Normalizes any interaction URL to the inner HTTPS interaction URL. Accepts any
26
+ * URL that passed {@link isInteractionUrl}.
27
+ *
28
+ * - `interaction:https://...?iuv=1` strips the prefix
29
+ * - `interaction:https%3A%2F%2F...` strips the prefix and decodes (URL-encoded
30
+ * after `interaction:` is non-standard, but handled for robustness)
31
+ * - `https://...?iuv=1` is returned as-is
32
+ *
33
+ * @param url {string}
34
+ * @returns {string}
35
+ */
36
+ export function parseInteractionUrl(url) {
37
+ if (!url.startsWith(INTERACTION_SCHEME_PREFIX)) {
38
+ return url;
39
+ }
40
+ const inner = url.slice(INTERACTION_SCHEME_PREFIX.length);
41
+ // Non-standard: the inner URL may be URL-encoded after the `interaction:`
42
+ // prefix.
43
+ if (inner.startsWith('http%3A') || inner.startsWith('https%3A')) {
44
+ return decodeURIComponent(inner);
45
+ }
46
+ return inner;
47
+ }
48
+ /**
49
+ * Fetches the interaction protocols response from a VCALM interaction URL. Sends
50
+ * GET with `Accept: application/json` per the VCALM spec, and returns the
51
+ * protocols map. Logs a warning if the `iuv` param value is not `1`; throws if
52
+ * the response is not ok or the `protocols` key is missing.
53
+ *
54
+ * @param interactionUrl {string}
55
+ * @param [options] {object}
56
+ * @param [options.fetch] {FetchLike}
57
+ * @returns {Promise<Record<string, string>>}
58
+ */
59
+ export async function fetchInteractionProtocols(interactionUrl, { fetch = globalThis.fetch } = {}) {
60
+ try {
61
+ const parsed = new URL(interactionUrl);
62
+ const iuv = parsed.searchParams.get('iuv');
63
+ if (iuv && iuv !== '1') {
64
+ console.warn(`[fetchInteractionProtocols] Unexpected iuv value "${iuv}" ` +
65
+ '(expected "1"). Proceeding anyway.');
66
+ }
67
+ }
68
+ catch {
69
+ throw new Error(`Invalid interaction URL: ${interactionUrl}`);
70
+ }
71
+ const response = await fetch(interactionUrl, {
72
+ headers: { Accept: 'application/json' }
73
+ });
74
+ if (!response.ok) {
75
+ throw new Error(`Interaction URL fetch failed: ${response.status} ${response.statusText}`);
76
+ }
77
+ const body = await response.json();
78
+ if (!body.protocols || typeof body.protocols !== 'object') {
79
+ throw new Error('Interaction URL response missing "protocols" map.');
80
+ }
81
+ return body.protocols;
82
+ }
83
+ //# sourceMappingURL=interactionUrl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactionUrl.js","sourceRoot":"","sources":["../../src/request/interactionUrl.ts"],"names":[],"mappings":"AAeA,MAAM,yBAAyB,GAAG,cAAc,CAAA;AAEhD;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,GAAG,CAAC,UAAU,CAAC,yBAAyB,CAAC,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,yBAAyB,CAAC,EAAE,CAAC;QAC/C,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAA;IACzD,0EAA0E;IAC1E,UAAU;IACV,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAChE,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,cAAsB,EACtB,EAAE,KAAK,GAAG,UAAU,CAAC,KAAK,KAA4B,EAAE;IAExD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAA;QACtC,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAC1C,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CACV,qDAAqD,GAAG,IAAI;gBAC1D,oCAAoC,CACvC,CAAA;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,4BAA4B,cAAc,EAAE,CAAC,CAAA;IAC/D,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE;QAC3C,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;KACxC,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,iCAAiC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAC1E,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IAElC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACtE,CAAC;IAED,OAAO,IAAI,CAAC,SAAS,CAAA;AACvB,CAAC"}
@@ -0,0 +1,69 @@
1
+ import type { IVerifiableCredential } from './types.js';
2
+ import type { IQueryByExample } from './types.js';
3
+ export { issuerId, typeArray } from '@interop/data-integrity-core/guards';
4
+ /**
5
+ * Whether a credential matches a QueryByExample `example` object, by the DCW
6
+ * deep-matching algorithm: every key of the example is resolved as a JSONPath
7
+ * against the credential and compared. Array example values require the
8
+ * credential to contain (at least) every listed value; object example values
9
+ * recurse; literal example values compare by strict equality. An empty example
10
+ * matches any credential.
11
+ *
12
+ * @param vprExample {Record<string, unknown>} - The QueryByExample `example`.
13
+ * @param credential {IVerifiableCredential} - The stored credential to test.
14
+ * @param [credentialPath] {string} - JSONPath root into the credential
15
+ * (defaults to `$`); used internally when recursing into nested objects.
16
+ * @returns {boolean}
17
+ */
18
+ export declare function credentialMatchesVprExampleQuery(vprExample: Record<string, unknown>, credential: IVerifiableCredential, credentialPath?: string): boolean;
19
+ /**
20
+ * Filters credentials to those matching a `QueryByExample`, using the deep
21
+ * matcher. Each of the query's `credentialQuery` details contributes its
22
+ * `example`; a credential is included when it matches any of them. A malformed
23
+ * query with no example matches nothing.
24
+ *
25
+ * @param credentials {IVerifiableCredential[]}
26
+ * @param query {IQueryByExample}
27
+ * @returns {IVerifiableCredential[]}
28
+ */
29
+ export declare function filterCredentialsByExample(credentials: IVerifiableCredential[], query: IQueryByExample): IVerifiableCredential[];
30
+ /**
31
+ * The credentials matching any of the given QueryByExample queries by the
32
+ * type-and-issuer algorithm. Only queries whose `example` carries a `type`
33
+ * constrain the result; a query with no example type matches nothing here (the
34
+ * caller keeps its list-all behavior when *no* query specifies a type).
35
+ *
36
+ * @param options {object}
37
+ * @param options.credentials {IVerifiableCredential[]}
38
+ * @param options.queries {IQueryByExample[]}
39
+ * @returns {IVerifiableCredential[]}
40
+ */
41
+ export declare function vcMatchesFor({ credentials, queries }: {
42
+ credentials: IVerifiableCredential[];
43
+ queries: IQueryByExample[];
44
+ }): IVerifiableCredential[];
45
+ /**
46
+ * Whether any of the QueryByExample queries pins an example `type` (and so
47
+ * should filter the share list). When false, the caller keeps showing all
48
+ * stored credentials.
49
+ *
50
+ * @param queries {IQueryByExample[]}
51
+ * @returns {boolean}
52
+ */
53
+ export declare function hasTypedExample(queries: IQueryByExample[]): boolean;
54
+ /**
55
+ * Whether any typed example in the query set explicitly lists the given
56
+ * credential `type`. Lets the caller distinguish a request that actually asks
57
+ * for a particular type (e.g. a LoginCredential) from a generic, untyped "any
58
+ * VC" request.
59
+ *
60
+ * @param options {object}
61
+ * @param options.queries {IQueryByExample[]}
62
+ * @param options.type {string}
63
+ * @returns {boolean}
64
+ */
65
+ export declare function requestsCredentialType({ queries, type }: {
66
+ queries: IQueryByExample[];
67
+ type: string;
68
+ }): boolean;
69
+ //# sourceMappingURL=matching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matching.d.ts","sourceRoot":"","sources":["../../src/request/matching.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAoB,eAAe,EAAE,MAAM,YAAY,CAAA;AAMnE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AAEzE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gCAAgC,CAC9C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,UAAU,EAAE,qBAAqB,EACjC,cAAc,SAAM,GACnB,OAAO,CAoCT;AAeD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,qBAAqB,EAAE,EACpC,KAAK,EAAE,eAAe,GACrB,qBAAqB,EAAE,CAgBzB;AAiCD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,EAC3B,WAAW,EACX,OAAO,EACR,EAAE;IACD,WAAW,EAAE,qBAAqB,EAAE,CAAA;IACpC,OAAO,EAAE,eAAe,EAAE,CAAA;CAC3B,GAAG,qBAAqB,EAAE,CAQ1B;AAqBD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,OAAO,CAEnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,OAAO,EACP,IAAI,EACL,EAAE;IACD,OAAO,EAAE,eAAe,EAAE,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,OAAO,CAIV"}