@eudiplo/sdk-core 1.14.0-main.07a91c4 → 1.14.0-main.0934ac3

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 CHANGED
@@ -82,6 +82,158 @@ await waitForCompletion();
82
82
  | `verifyAndWait(options)` | One-liner: create request + wait for result |
83
83
  | `issueAndWait(options)` | One-liner: create offer + wait for result |
84
84
 
85
+ ### Digital Credentials API (Browser Native)
86
+
87
+ The SDK includes utilities for the [Digital Credentials API](https://wicg.github.io/digital-credentials/), enabling browser-native credential presentation without QR codes.
88
+
89
+ ```typescript
90
+ import { isDcApiAvailable, verifyWithDcApi } from '@eudiplo/sdk-core';
91
+
92
+ // Check if browser supports DC API
93
+ if (isDcApiAvailable()) {
94
+ const result = await verifyWithDcApi({
95
+ baseUrl: 'https://eudiplo.example.com',
96
+ clientId: 'my-demo',
97
+ clientSecret: 'secret',
98
+ configId: 'age-over-18',
99
+ });
100
+
101
+ console.log('Verified!', result.session.credentials);
102
+ } else {
103
+ // Fall back to QR code flow
104
+ const session = await verifyAndWait({...});
105
+ }
106
+ ```
107
+
108
+ #### DC API Functions
109
+
110
+ | Function | Description |
111
+ | ---------------------- | --------------------------------------------------- |
112
+ | `isDcApiAvailable()` | Check if browser supports Digital Credentials API |
113
+ | `verifyWithDcApi()` | Complete verification flow using browser-native API |
114
+ | `createDcApiRequest()` | Create a `DigitalCredentialRequestOptions` object |
115
+
116
+ #### Lower-level DC API Usage
117
+
118
+ ```typescript
119
+ import { createDcApiRequest, EudiploClient } from '@eudiplo/sdk-core';
120
+
121
+ const client = new EudiploClient({...});
122
+
123
+ // Create presentation request
124
+ const { uri, sessionId } = await client.createPresentationRequest({
125
+ configId: 'age-over-18',
126
+ responseType: 'dc-api',
127
+ });
128
+
129
+ // Create the browser request object
130
+ const request = createDcApiRequest(uri);
131
+
132
+ // Call the browser API directly
133
+ const credential = await navigator.credentials.get(request);
134
+
135
+ // Submit the response and get verified session
136
+ const session = await client.submitDcApiResponse(sessionId, credential);
137
+ ```
138
+
139
+ #### Secure Server/Client Deployment (Recommended for Production)
140
+
141
+ When deploying to production, you should **never expose your client credentials to the browser**. The SDK provides helper functions to split the DC API flow between your server (where credentials are safe) and the browser (where the DC API runs).
142
+
143
+ **Server-side Functions:**
144
+
145
+ | Function | Description |
146
+ | -------------------------------- | ------------------------------------------------------ |
147
+ | `createDcApiRequestForBrowser()` | Create request on server, return safe data for browser |
148
+ | `submitDcApiWalletResponse()` | Submit wallet response to EUDIPLO from server |
149
+
150
+ **Browser-side Functions:**
151
+
152
+ | Function | Description |
153
+ | ------------- | ------------------------------------------------------ |
154
+ | `callDcApi()` | Call the native DC API with a request from your server |
155
+
156
+ ##### Example: Express.js Backend + Browser Frontend
157
+
158
+ **Server (Express.js / Next.js API route):**
159
+
160
+ ```typescript
161
+ import {
162
+ createDcApiRequestForBrowser,
163
+ submitDcApiWalletResponse,
164
+ } from '@eudiplo/sdk-core';
165
+
166
+ // POST /api/start-verification
167
+ app.post('/api/start-verification', async (req, res) => {
168
+ const requestData = await createDcApiRequestForBrowser({
169
+ baseUrl: process.env.EUDIPLO_URL,
170
+ clientId: process.env.EUDIPLO_CLIENT_ID, // ✅ Safe on server
171
+ clientSecret: process.env.EUDIPLO_SECRET, // ✅ Safe on server
172
+ configId: 'age-over-18',
173
+ });
174
+
175
+ // Only safe data is sent to browser (no secrets)
176
+ res.json(requestData);
177
+ });
178
+
179
+ // POST /api/complete-verification
180
+ app.post('/api/complete-verification', async (req, res) => {
181
+ const { responseUri, walletResponse } = req.body;
182
+
183
+ const result = await submitDcApiWalletResponse({
184
+ responseUri,
185
+ walletResponse,
186
+ sendResponse: true, // Get verified claims back
187
+ });
188
+
189
+ // result.credentials contains the verified data
190
+ res.json(result);
191
+ });
192
+ ```
193
+
194
+ **Browser (React / vanilla JS):**
195
+
196
+ ```typescript
197
+ import { callDcApi, isDcApiAvailable } from '@eudiplo/sdk-core';
198
+
199
+ async function verifyAge() {
200
+ // 1. Get the request from your server (credentials stay on server)
201
+ const requestData = await fetch('/api/start-verification', {
202
+ method: 'POST',
203
+ }).then((r) => r.json());
204
+
205
+ // 2. Check DC API support and call it locally
206
+ if (!isDcApiAvailable()) {
207
+ throw new Error('Digital Credentials API not supported');
208
+ }
209
+
210
+ const walletResponse = await callDcApi(requestData.requestObject);
211
+
212
+ // 3. Send the wallet response back to your server for verification
213
+ const result = await fetch('/api/complete-verification', {
214
+ method: 'POST',
215
+ headers: { 'Content-Type': 'application/json' },
216
+ body: JSON.stringify({
217
+ responseUri: requestData.responseUri,
218
+ walletResponse,
219
+ }),
220
+ }).then((r) => r.json());
221
+
222
+ console.log('Verified!', result.credentials);
223
+ return result;
224
+ }
225
+ ```
226
+
227
+ **What stays where:**
228
+
229
+ | Data | Location | Safe to expose? |
230
+ | ------------------------------ | ---------------- | ------------------- |
231
+ | `clientId` / `clientSecret` | Server only | ❌ Never expose |
232
+ | `requestObject` (signed JWT) | Server → Browser | ✅ Yes |
233
+ | `responseUri` | Server → Browser | ✅ Yes |
234
+ | Wallet response (encrypted VP) | Browser → Server | ✅ Yes |
235
+ | Verified credentials | Server only | Depends on use case |
236
+
85
237
  ### Class-based API (More Control)
86
238
 
87
239
  ```typescript
@@ -175,6 +327,43 @@ const session = await client.waitForSession(sessionId, {
175
327
  });
176
328
  ```
177
329
 
330
+ ### `subscribeToSession(sessionId, options)`
331
+
332
+ Subscribe to real-time session status updates via Server-Sent Events (SSE).
333
+ This is more efficient than polling and provides instant updates.
334
+
335
+ ```typescript
336
+ const subscription = await client.subscribeToSession(sessionId, {
337
+ onStatusChange: (event) => {
338
+ console.log(`Status: ${event.status}`);
339
+ if (['completed', 'expired', 'failed'].includes(event.status)) {
340
+ subscription.close();
341
+ }
342
+ },
343
+ onError: (error) => console.error('SSE error:', error),
344
+ onOpen: () => console.log('Connected'),
345
+ });
346
+
347
+ // Later, to close the connection:
348
+ subscription.close();
349
+ ```
350
+
351
+ ### `waitForSessionWithSse(sessionId, options)`
352
+
353
+ Wait for session completion using SSE instead of polling. Returns a Promise
354
+ that resolves when the session completes.
355
+
356
+ ```typescript
357
+ try {
358
+ const finalStatus = await client.waitForSessionWithSse(sessionId, {
359
+ onStatusChange: (event) => console.log('Status:', event.status),
360
+ });
361
+ console.log('Session completed:', finalStatus);
362
+ } catch (error) {
363
+ console.error('Session failed:', error);
364
+ }
365
+ ```
366
+
178
367
  ## Examples
179
368
 
180
369
  ### Age Verification in a Web Shop
@@ -1,4 +1,4 @@
1
- import { C as Config, a as Client } from '../../types.gen-CIiveH8G.mjs';
1
+ import { b as Config, C as Client } from '../../types.gen-D8LjzWc0.mjs';
2
2
 
3
3
  declare const createClient: (config?: Config) => Client;
4
4
 
@@ -1,4 +1,4 @@
1
- import { C as Config, a as Client } from '../../types.gen-CIiveH8G.js';
1
+ import { b as Config, C as Client } from '../../types.gen-D8LjzWc0.js';
2
2
 
3
3
  declare const createClient: (config?: Config) => Client;
4
4
 
@@ -719,10 +719,14 @@ var createClient = (config = {}) => {
719
719
  case "arrayBuffer":
720
720
  case "blob":
721
721
  case "formData":
722
- case "json":
723
722
  case "text":
724
723
  data = await response[parseAs]();
725
724
  break;
725
+ case "json": {
726
+ const text = await response.text();
727
+ data = text ? JSON.parse(text) : {};
728
+ break;
729
+ }
726
730
  case "stream":
727
731
  return opts.responseStyle === "data" ? response.body : {
728
732
  data: response.body,
@@ -717,10 +717,14 @@ var createClient = (config = {}) => {
717
717
  case "arrayBuffer":
718
718
  case "blob":
719
719
  case "formData":
720
- case "json":
721
720
  case "text":
722
721
  data = await response[parseAs]();
723
722
  break;
723
+ case "json": {
724
+ const text = await response.text();
725
+ data = text ? JSON.parse(text) : {};
726
+ break;
727
+ }
724
728
  case "stream":
725
729
  return opts.responseStyle === "data" ? response.body : {
726
730
  data: response.body,
@@ -1,4 +1,4 @@
1
- export { A as Auth, a as Client, b as ClientOptions, C as Config, c as CreateClientConfig, O as Options, Q as QuerySerializerOptions, R as RequestOptions, d as RequestResult, e as ResolvedRequestOptions, f as ResponseStyle, T as TDataShape, g as createConfig, h as formDataBodySerializer, j as jsonBodySerializer, m as mergeHeaders, u as urlSearchParamsBodySerializer } from '../../types.gen-CIiveH8G.mjs';
1
+ export { A as Auth, C as Client, a as ClientOptions, b as Config, c as CreateClientConfig, O as Options, Q as QuerySerializerOptions, R as RequestOptions, d as RequestResult, e as ResolvedRequestOptions, f as ResponseStyle, T as TDataShape, g as createConfig, h as formDataBodySerializer, j as jsonBodySerializer, m as mergeHeaders, u as urlSearchParamsBodySerializer } from '../../types.gen-D8LjzWc0.mjs';
2
2
  export { createClient } from './client.gen.mjs';
3
3
 
4
4
  type Slot = "body" | "headers" | "path" | "query";
@@ -1,4 +1,4 @@
1
- export { A as Auth, a as Client, b as ClientOptions, C as Config, c as CreateClientConfig, O as Options, Q as QuerySerializerOptions, R as RequestOptions, d as RequestResult, e as ResolvedRequestOptions, f as ResponseStyle, T as TDataShape, g as createConfig, h as formDataBodySerializer, j as jsonBodySerializer, m as mergeHeaders, u as urlSearchParamsBodySerializer } from '../../types.gen-CIiveH8G.js';
1
+ export { A as Auth, C as Client, a as ClientOptions, b as Config, c as CreateClientConfig, O as Options, Q as QuerySerializerOptions, R as RequestOptions, d as RequestResult, e as ResolvedRequestOptions, f as ResponseStyle, T as TDataShape, g as createConfig, h as formDataBodySerializer, j as jsonBodySerializer, m as mergeHeaders, u as urlSearchParamsBodySerializer } from '../../types.gen-D8LjzWc0.js';
2
2
  export { createClient } from './client.gen.js';
3
3
 
4
4
  type Slot = "body" | "headers" | "path" | "query";
@@ -88,7 +88,7 @@ var buildKeyMap = (fields, map) => {
88
88
  };
89
89
  var stripEmptySlots = (params) => {
90
90
  for (const [slot, value] of Object.entries(params)) {
91
- if (value && typeof value === "object" && !Object.keys(value).length) {
91
+ if (value && typeof value === "object" && !Array.isArray(value) && !Object.keys(value).length) {
92
92
  delete params[slot];
93
93
  }
94
94
  }
@@ -941,10 +941,14 @@ var createClient = (config = {}) => {
941
941
  case "arrayBuffer":
942
942
  case "blob":
943
943
  case "formData":
944
- case "json":
945
944
  case "text":
946
945
  data = await response[parseAs]();
947
946
  break;
947
+ case "json": {
948
+ const text = await response.text();
949
+ data = text ? JSON.parse(text) : {};
950
+ break;
951
+ }
948
952
  case "stream":
949
953
  return opts.responseStyle === "data" ? response.body : {
950
954
  data: response.body,
@@ -86,7 +86,7 @@ var buildKeyMap = (fields, map) => {
86
86
  };
87
87
  var stripEmptySlots = (params) => {
88
88
  for (const [slot, value] of Object.entries(params)) {
89
- if (value && typeof value === "object" && !Object.keys(value).length) {
89
+ if (value && typeof value === "object" && !Array.isArray(value) && !Object.keys(value).length) {
90
90
  delete params[slot];
91
91
  }
92
92
  }
@@ -939,10 +939,14 @@ var createClient = (config = {}) => {
939
939
  case "arrayBuffer":
940
940
  case "blob":
941
941
  case "formData":
942
- case "json":
943
942
  case "text":
944
943
  data = await response[parseAs]();
945
944
  break;
945
+ case "json": {
946
+ const text = await response.text();
947
+ data = text ? JSON.parse(text) : {};
948
+ break;
949
+ }
946
950
  case "stream":
947
951
  return opts.responseStyle === "data" ? response.body : {
948
952
  data: response.body,
@@ -1 +1 @@
1
- export { a as Client, b as ClientOptions, C as Config, c as CreateClientConfig, O as Options, R as RequestOptions, d as RequestResult, e as ResolvedRequestOptions, f as ResponseStyle, T as TDataShape } from '../../types.gen-CIiveH8G.mjs';
1
+ export { C as Client, a as ClientOptions, b as Config, c as CreateClientConfig, O as Options, R as RequestOptions, d as RequestResult, e as ResolvedRequestOptions, f as ResponseStyle, T as TDataShape } from '../../types.gen-D8LjzWc0.mjs';
@@ -1 +1 @@
1
- export { a as Client, b as ClientOptions, C as Config, c as CreateClientConfig, O as Options, R as RequestOptions, d as RequestResult, e as ResolvedRequestOptions, f as ResponseStyle, T as TDataShape } from '../../types.gen-CIiveH8G.js';
1
+ export { C as Client, a as ClientOptions, b as Config, c as CreateClientConfig, O as Options, R as RequestOptions, d as RequestResult, e as ResolvedRequestOptions, f as ResponseStyle, T as TDataShape } from '../../types.gen-D8LjzWc0.js';
@@ -1,2 +1,15 @@
1
- import '../types.gen-CIiveH8G.mjs';
2
- export { ed as CreateClientConfig, ec as client } from '../client.gen-CU56lLgT.mjs';
1
+ import { C as Client, a as ClientOptions, b as Config } from '../types.gen-D8LjzWc0.mjs';
2
+ import { ae as ClientOptions$1 } from '../types.gen-sNmRQvUI.mjs';
3
+
4
+ /**
5
+ * The `createClientConfig()` function will be called on client initialization
6
+ * and the returned object will become the client's initial configuration.
7
+ *
8
+ * You may want to initialize your client this way instead of calling
9
+ * `setConfig()`. This is useful for example if you're using Next.js
10
+ * to ensure your client always has the correct values.
11
+ */
12
+ type CreateClientConfig<T extends ClientOptions = ClientOptions$1> = (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T> | Promise<Config<Required<ClientOptions> & T>>;
13
+ declare const client: Client;
14
+
15
+ export { type CreateClientConfig, client };
@@ -1,2 +1,15 @@
1
- import '../types.gen-CIiveH8G.js';
2
- export { ed as CreateClientConfig, ec as client } from '../client.gen-CsfHl8pY.js';
1
+ import { C as Client, a as ClientOptions, b as Config } from '../types.gen-D8LjzWc0.js';
2
+ import { ae as ClientOptions$1 } from '../types.gen-sNmRQvUI.js';
3
+
4
+ /**
5
+ * The `createClientConfig()` function will be called on client initialization
6
+ * and the returned object will become the client's initial configuration.
7
+ *
8
+ * You may want to initialize your client this way instead of calling
9
+ * `setConfig()`. This is useful for example if you're using Next.js
10
+ * to ensure your client always has the correct values.
11
+ */
12
+ type CreateClientConfig<T extends ClientOptions = ClientOptions$1> = (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T> | Promise<Config<Required<ClientOptions> & T>>;
13
+ declare const client: Client;
14
+
15
+ export { type CreateClientConfig, client };
@@ -719,10 +719,14 @@ var createClient = (config = {}) => {
719
719
  case "arrayBuffer":
720
720
  case "blob":
721
721
  case "formData":
722
- case "json":
723
722
  case "text":
724
723
  data = await response[parseAs]();
725
724
  break;
725
+ case "json": {
726
+ const text = await response.text();
727
+ data = text ? JSON.parse(text) : {};
728
+ break;
729
+ }
726
730
  case "stream":
727
731
  return opts.responseStyle === "data" ? response.body : {
728
732
  data: response.body,
@@ -717,10 +717,14 @@ var createClient = (config = {}) => {
717
717
  case "arrayBuffer":
718
718
  case "blob":
719
719
  case "formData":
720
- case "json":
721
720
  case "text":
722
721
  data = await response[parseAs]();
723
722
  break;
723
+ case "json": {
724
+ const text = await response.text();
725
+ data = text ? JSON.parse(text) : {};
726
+ break;
727
+ }
724
728
  case "stream":
725
729
  return opts.responseStyle === "data" ? response.body : {
726
730
  data: response.body,