nervoscan-js-sdk 1.0.3 → 1.0.4
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/.env.production +3 -2
- package/README.md +298 -73
- package/dist/nervoscan-js-sdk.js +101 -101
- package/dist/nervoscan-js-sdk.mjs +2778 -2740
- package/dist/nervoscan-js-sdk.umd.js +104 -104
- package/package.json +3 -2
- package/src/api/Client.ts +53 -6
- package/src/api/utils/backend_repository/backendRepository.ts +8 -4
- package/src/api/utils/general_utils.ts +14 -0
- package/src/api/utils/streaming/server/server_streaming.ts +160 -0
package/.env.production
CHANGED
package/README.md
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
+
# NervoScan JS SDK
|
|
4
|
+
|
|
3
5
|
[](https://www.npmjs.com/package/nervoscan-js-sdk)
|
|
6
|
+
[](https://opensource.org/licenses/ISC)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
4
8
|
|
|
5
9
|
## Overview
|
|
6
10
|
|
|
7
|
-
The **NervoScan JS SDK** is a
|
|
8
|
-
|
|
9
|
-
It provides a singleton `Client` interface and a full suite of custom error classes for advanced control and debugging.
|
|
11
|
+
The **NervoScan JS SDK** is a comprehensive JavaScript/TypeScript library that enables seamless integration with the NervoScan contactless health analysis platform. This SDK provides real-time video streaming capabilities, live health metric results, and robust error handling for building sophisticated health monitoring applications.
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
### Key Features
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
15
|
+
- 🎥 **Real-time Video Streaming** - Stream video directly from webcam using WebRTC
|
|
16
|
+
- 📊 **Live Results** - Receive health metrics in real-time via Firebase
|
|
17
|
+
- 🔄 **Dual Processing Modes** - Support for both batch video upload and live streaming
|
|
18
|
+
- 🏗️ **Multiple Backend Types** - Server and serverless deployment options
|
|
19
|
+
- 🛡️ **Type-Safe** - Full TypeScript support with comprehensive type definitions
|
|
20
|
+
- 🎯 **Smart Error Handling** - Detailed error classes for face positioning and scan quality
|
|
21
|
+
- ⚡ **Framework Agnostic** - Works with React, Vue, Angular, or vanilla JS
|
|
22
|
+
- 📦 **Multiple Module Formats** - ESM, CommonJS, and UMD builds included
|
|
21
23
|
|
|
22
24
|
---
|
|
23
25
|
|
|
@@ -27,35 +29,71 @@ It provides a singleton `Client` interface and a full suite of custom error clas
|
|
|
27
29
|
npm install nervoscan-js-sdk
|
|
28
30
|
```
|
|
29
31
|
|
|
32
|
+
Or with yarn:
|
|
33
|
+
```bash
|
|
34
|
+
yarn add nervoscan-js-sdk
|
|
35
|
+
```
|
|
36
|
+
|
|
30
37
|
---
|
|
31
38
|
|
|
32
|
-
##
|
|
39
|
+
## Quick Start
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
### Basic Video Upload
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { Client } from 'nervoscan-js-sdk';
|
|
36
45
|
|
|
37
|
-
// Get the singleton instance
|
|
38
46
|
const client = Client.getInstance();
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
// Initialize with credentials
|
|
49
|
+
await client.initialize('username', 'password');
|
|
50
|
+
|
|
51
|
+
// Upload a video file
|
|
52
|
+
const videoBlob = new Blob([videoData], { type: 'video/mp4' });
|
|
53
|
+
const jobId = await client.uploadVideo(videoBlob);
|
|
54
|
+
|
|
55
|
+
console.log('Video uploaded! Job ID:', jobId);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Real-time Streaming with Live Results
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { Client } from 'nervoscan-js-sdk';
|
|
62
|
+
|
|
63
|
+
const client = Client.getInstance();
|
|
64
|
+
|
|
65
|
+
async function startHealthMonitoring() {
|
|
66
|
+
// Initialize client
|
|
67
|
+
await client.initialize('username', 'password');
|
|
68
|
+
|
|
69
|
+
// Set up result callbacks
|
|
70
|
+
client.setOnWindowResults((results) => {
|
|
71
|
+
console.log('New window results:', results);
|
|
72
|
+
// Update UI with real-time metrics
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
client.setOnFinalResults((results) => {
|
|
76
|
+
console.log('Final averaged results:', results);
|
|
77
|
+
// Display final health report
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
client.setOnError((error) => {
|
|
81
|
+
console.error('Scan error:', error);
|
|
82
|
+
// Handle scanning errors
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Get webcam stream
|
|
86
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
87
|
+
video: { width: 1280, height: 720 }
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Initialize streaming
|
|
91
|
+
const videoElement = document.getElementById('video') as HTMLVideoElement;
|
|
92
|
+
client.initializeStreaming(stream, videoElement);
|
|
93
|
+
|
|
94
|
+
// Start streaming
|
|
95
|
+
const jobId = await client.startStreaming();
|
|
96
|
+
console.log('Streaming started! Job ID:', jobId);
|
|
59
97
|
}
|
|
60
98
|
```
|
|
61
99
|
|
|
@@ -63,73 +101,260 @@ async function submitScan(videoBlob: Blob) {
|
|
|
63
101
|
|
|
64
102
|
## API Reference
|
|
65
103
|
|
|
66
|
-
###
|
|
104
|
+
### Client Class
|
|
105
|
+
|
|
106
|
+
The `Client` class follows a singleton pattern to ensure consistent state management across your application.
|
|
67
107
|
|
|
68
|
-
####
|
|
69
|
-
|
|
70
|
-
|
|
108
|
+
#### Getting the Instance
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const client = Client.getInstance();
|
|
71
112
|
```
|
|
72
|
-
Returns the singleton instance of the Client class.
|
|
73
113
|
|
|
74
|
-
#### Methods
|
|
114
|
+
#### Core Methods
|
|
115
|
+
|
|
116
|
+
##### `initialize(username: string, password: string, serverType?: string): void`
|
|
117
|
+
Initializes the client with authentication credentials.
|
|
118
|
+
|
|
119
|
+
- `username` - Your NervoScan account username
|
|
120
|
+
- `password` - Your NervoScan account password
|
|
121
|
+
- `serverType` - Backend type: `'server'` (default) or `'serverless'`
|
|
122
|
+
|
|
123
|
+
##### `uploadVideo(videoBlob: Blob): Promise<string>`
|
|
124
|
+
Uploads a video for processing and returns the job ID.
|
|
125
|
+
|
|
126
|
+
- `videoBlob` - Video file as a Blob object
|
|
127
|
+
- Returns: Promise resolving to the job ID string
|
|
128
|
+
|
|
129
|
+
##### `initializeStreaming(videoStream: MediaStream, videoElement: HTMLVideoElement): void`
|
|
130
|
+
Sets up real-time video streaming.
|
|
131
|
+
|
|
132
|
+
- `videoStream` - MediaStream from getUserMedia
|
|
133
|
+
- `videoElement` - HTML video element for preview
|
|
134
|
+
|
|
135
|
+
##### `startStreaming(): Promise<string>`
|
|
136
|
+
Begins streaming video to the server for real-time analysis.
|
|
137
|
+
|
|
138
|
+
- Returns: Promise resolving to the job ID string
|
|
139
|
+
|
|
140
|
+
##### `stopStreaming(): void`
|
|
141
|
+
Stops the active streaming session.
|
|
142
|
+
|
|
143
|
+
#### Callback Methods
|
|
75
144
|
|
|
76
|
-
|
|
77
|
-
|
|
145
|
+
##### `setOnWindowResults(callback: (results: any) => void): void`
|
|
146
|
+
Sets callback for receiving real-time window-based results.
|
|
78
147
|
|
|
79
|
-
|
|
80
|
-
|
|
148
|
+
##### `setOnFinalResults(callback: (results: any) => void): void`
|
|
149
|
+
Sets callback for receiving final averaged results.
|
|
81
150
|
|
|
82
|
-
|
|
83
|
-
|
|
151
|
+
##### `setOnError(callback: (error: any) => void): void`
|
|
152
|
+
Sets callback for handling scan errors.
|
|
84
153
|
|
|
85
|
-
|
|
86
|
-
|
|
154
|
+
##### `setOnDisconnection(callback: () => void): void`
|
|
155
|
+
Sets callback for handling connection loss.
|
|
156
|
+
|
|
157
|
+
#### Deprecated Methods
|
|
158
|
+
|
|
159
|
+
⚠️ The following methods are deprecated and will be removed in future versions:
|
|
160
|
+
|
|
161
|
+
- `checkResults(jobID: string)` - Use callback methods instead
|
|
162
|
+
- `getResults(jobID: string)` - Use callback methods instead
|
|
87
163
|
|
|
88
164
|
---
|
|
89
165
|
|
|
90
166
|
## Error Handling
|
|
91
167
|
|
|
92
|
-
The SDK provides
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
import { Errors } from 'nervoscan-js-sdk';
|
|
168
|
+
The SDK provides comprehensive error classes for granular error control:
|
|
96
169
|
|
|
170
|
+
### Authentication Errors
|
|
171
|
+
```typescript
|
|
97
172
|
try {
|
|
98
173
|
await client.initialize('username', 'password');
|
|
99
|
-
} catch (
|
|
100
|
-
if (
|
|
101
|
-
console.error('
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (error instanceof Errors.InvalidUsernameError) {
|
|
176
|
+
console.error('Invalid username');
|
|
177
|
+
} else if (error instanceof Errors.InvalidPasswordError) {
|
|
178
|
+
console.error('Invalid password');
|
|
102
179
|
}
|
|
103
180
|
}
|
|
104
181
|
```
|
|
105
182
|
|
|
106
|
-
###
|
|
183
|
+
### Scan Quality Errors
|
|
184
|
+
```typescript
|
|
185
|
+
client.setOnError((error) => {
|
|
186
|
+
if (error instanceof Errors.FaceTooFarError) {
|
|
187
|
+
showMessage('Please move closer to the camera');
|
|
188
|
+
} else if (error instanceof Errors.FaceTooCloseError) {
|
|
189
|
+
showMessage('Please move further from the camera');
|
|
190
|
+
} else if (error instanceof Errors.FaceNotCenteredError) {
|
|
191
|
+
showMessage('Please center your face in the frame');
|
|
192
|
+
} else if (error instanceof Errors.LowFPSError) {
|
|
193
|
+
showMessage('Poor video quality - check your lighting');
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
```
|
|
107
197
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
198
|
+
### Complete Error Reference
|
|
199
|
+
|
|
200
|
+
| Error Class | Description |
|
|
201
|
+
|-------------|-------------|
|
|
202
|
+
| `NotInitializedError` | Client not initialized |
|
|
203
|
+
| `EmptyVideoError` | Video blob is empty |
|
|
204
|
+
| `VideoTypeError` | Invalid video type |
|
|
205
|
+
| `InvalidUsernameError` | Invalid username |
|
|
206
|
+
| `InvalidPasswordError` | Invalid password |
|
|
207
|
+
| `InvalidAccessTokenError` | Invalid or expired token |
|
|
208
|
+
| `NoScansAvailableError` | No scans left in plan |
|
|
209
|
+
| `NoScanDataError` | No data for job ID |
|
|
210
|
+
| `InvalidServerTypeError` | Invalid server type |
|
|
211
|
+
| `LowFPSError` | Video FPS too low |
|
|
212
|
+
| `FaceNotCenteredError` | Face not centered |
|
|
213
|
+
| `FaceTooFarError` | Face too far away |
|
|
214
|
+
| `FaceTooCloseError` | Face too close |
|
|
215
|
+
| `FaceLookingLeftError` | Face turned left |
|
|
216
|
+
| `FaceLookingRightError` | Face turned right |
|
|
217
|
+
| `UnhandledScanError` | Other scan errors |
|
|
116
218
|
|
|
117
219
|
---
|
|
118
220
|
|
|
119
|
-
##
|
|
221
|
+
## Real-World Examples
|
|
222
|
+
|
|
223
|
+
### React Component with Live Monitoring
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
227
|
+
import { Client, Errors } from 'nervoscan-js-sdk';
|
|
228
|
+
|
|
229
|
+
function HealthMonitor() {
|
|
230
|
+
const videoRef = useRef<HTMLVideoElement>(null);
|
|
231
|
+
const [isScanning, setIsScanning] = useState(false);
|
|
232
|
+
const [heartRate, setHeartRate] = useState<number | null>(null);
|
|
233
|
+
const [message, setMessage] = useState('');
|
|
234
|
+
|
|
235
|
+
const client = Client.getInstance();
|
|
236
|
+
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
// Setup callbacks
|
|
239
|
+
client.setOnWindowResults((results) => {
|
|
240
|
+
setHeartRate(results.heartRate);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
client.setOnError((error) => {
|
|
244
|
+
if (error instanceof Errors.FaceNotCenteredError) {
|
|
245
|
+
setMessage('Center your face in the frame');
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return () => {
|
|
250
|
+
client.stopStreaming();
|
|
251
|
+
};
|
|
252
|
+
}, []);
|
|
253
|
+
|
|
254
|
+
const startScan = async () => {
|
|
255
|
+
try {
|
|
256
|
+
await client.initialize('user@example.com', 'password');
|
|
257
|
+
|
|
258
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
259
|
+
video: { facingMode: 'user' }
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (videoRef.current) {
|
|
263
|
+
client.initializeStreaming(stream, videoRef.current);
|
|
264
|
+
await client.startStreaming();
|
|
265
|
+
setIsScanning(true);
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
console.error('Failed to start scan:', error);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div>
|
|
274
|
+
<video ref={videoRef} autoPlay playsInline />
|
|
275
|
+
{heartRate && <p>Heart Rate: {heartRate} BPM</p>}
|
|
276
|
+
<p>{message}</p>
|
|
277
|
+
<button onClick={startScan} disabled={isScanning}>
|
|
278
|
+
{isScanning ? 'Scanning...' : 'Start Scan'}
|
|
279
|
+
</button>
|
|
280
|
+
</div>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Vue 3 Composition API Example
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
<template>
|
|
289
|
+
<div>
|
|
290
|
+
<video ref="videoEl" autoplay playsinline></video>
|
|
291
|
+
<div v-if="metrics">
|
|
292
|
+
<p>Heart Rate: {{ metrics.heartRate }} BPM</p>
|
|
293
|
+
<p>Stress Level: {{ metrics.stressLevel }}</p>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
</template>
|
|
120
297
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
298
|
+
<script setup lang="ts">
|
|
299
|
+
import { ref, onMounted, onUnmounted } from 'vue';
|
|
300
|
+
import { Client } from 'nervoscan-js-sdk';
|
|
301
|
+
|
|
302
|
+
const videoEl = ref<HTMLVideoElement>();
|
|
303
|
+
const metrics = ref(null);
|
|
304
|
+
|
|
305
|
+
const client = Client.getInstance();
|
|
306
|
+
|
|
307
|
+
onMounted(async () => {
|
|
308
|
+
await client.initialize(import.meta.env.VITE_NERVO_USER,
|
|
309
|
+
import.meta.env.VITE_NERVO_PASS);
|
|
310
|
+
|
|
311
|
+
client.setOnFinalResults((results) => {
|
|
312
|
+
metrics.value = results;
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
316
|
+
if (videoEl.value) {
|
|
317
|
+
client.initializeStreaming(stream, videoEl.value);
|
|
318
|
+
await client.startStreaming();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
onUnmounted(() => {
|
|
323
|
+
client.stopStreaming();
|
|
324
|
+
});
|
|
325
|
+
</script>
|
|
326
|
+
```
|
|
124
327
|
|
|
125
328
|
---
|
|
126
329
|
|
|
127
|
-
##
|
|
330
|
+
## Requirements
|
|
128
331
|
|
|
129
|
-
|
|
332
|
+
- **Node.js**: v14 or higher
|
|
333
|
+
- **Browser Support**: Chrome 80+, Safari 14+, Firefox 78+, Edge 80+
|
|
334
|
+
- **Network**: Stable internet connection for streaming
|
|
335
|
+
- **Camera**: HD webcam (720p or higher recommended)
|
|
336
|
+
- **NervoScan Account**: Valid credentials with active subscription
|
|
130
337
|
|
|
131
338
|
---
|
|
132
339
|
|
|
133
|
-
##
|
|
340
|
+
## TypeScript Support
|
|
341
|
+
|
|
342
|
+
The SDK is written in TypeScript and includes comprehensive type definitions:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { Client, Errors, NervoscanError } from 'nervoscan-js-sdk';
|
|
346
|
+
|
|
347
|
+
// All methods are fully typed
|
|
348
|
+
const client: Client = Client.getInstance();
|
|
349
|
+
|
|
350
|
+
// Error types are available
|
|
351
|
+
function handleError(error: unknown) {
|
|
352
|
+
if (error instanceof NervoscanError) {
|
|
353
|
+
// Handle NervoScan specific errors
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
134
359
|
|
|
135
|
-
Built and maintained by the NervoScan
|
|
360
|
+
Built and maintained by the NervoScan team
|