smartcomply-web-sdk 1.0.3 → 1.0.5
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 +43 -0
- package/dist/esm/camera/ActionDetector.d.ts +26 -0
- package/dist/esm/camera/ActionDetector.d.ts.map +1 -0
- package/dist/esm/camera/ActionDetector.js +110 -0
- package/dist/esm/camera/ActionDetector.js.map +1 -0
- package/dist/esm/camera/CameraManager.d.ts +7 -0
- package/dist/esm/camera/CameraManager.d.ts.map +1 -0
- package/dist/esm/camera/CameraManager.js +27 -0
- package/dist/esm/camera/CameraManager.js.map +1 -0
- package/dist/esm/camera/FaceDetector.d.ts +12 -0
- package/dist/esm/camera/FaceDetector.d.ts.map +1 -0
- package/dist/esm/camera/FaceDetector.js +51 -0
- package/dist/esm/camera/FaceDetector.js.map +1 -0
- package/dist/esm/camera/Permission.d.ts +42 -0
- package/dist/esm/camera/Permission.d.ts.map +1 -0
- package/dist/esm/camera/Permission.js +101 -0
- package/dist/esm/camera/Permission.js.map +1 -0
- package/dist/esm/camera/VideoRecorder.d.ts +12 -0
- package/dist/esm/camera/VideoRecorder.d.ts.map +1 -0
- package/dist/esm/camera/VideoRecorder.js +47 -0
- package/dist/esm/camera/VideoRecorder.js.map +1 -0
- package/dist/esm/client/Config.d.ts +9 -0
- package/dist/esm/client/Config.d.ts.map +1 -0
- package/dist/esm/client/Config.js +5 -0
- package/dist/esm/client/Config.js.map +1 -0
- package/dist/esm/client/HttpClient.d.ts +44 -0
- package/dist/esm/client/HttpClient.d.ts.map +1 -0
- package/dist/esm/client/HttpClient.js +145 -0
- package/dist/esm/client/HttpClient.js.map +1 -0
- package/dist/esm/client/Smartcomply.d.ts +50 -0
- package/dist/esm/client/Smartcomply.d.ts.map +1 -0
- package/dist/esm/client/Smartcomply.js +73 -0
- package/dist/esm/client/Smartcomply.js.map +1 -0
- package/dist/esm/components/CameraView.d.ts +59 -0
- package/dist/esm/components/CameraView.d.ts.map +1 -0
- package/dist/esm/components/CameraView.js +238 -0
- package/dist/esm/components/CameraView.js.map +1 -0
- package/dist/esm/components/DocumentUpload.d.ts +21 -0
- package/dist/esm/components/DocumentUpload.d.ts.map +1 -0
- package/dist/esm/components/DocumentUpload.js +216 -0
- package/dist/esm/components/DocumentUpload.js.map +1 -0
- package/dist/esm/errors/AuthError.d.ts +8 -0
- package/dist/esm/errors/AuthError.d.ts.map +1 -0
- package/dist/esm/errors/AuthError.js +11 -0
- package/dist/esm/errors/AuthError.js.map +1 -0
- package/dist/esm/errors/NetworkError.d.ts +8 -0
- package/dist/esm/errors/NetworkError.d.ts.map +1 -0
- package/dist/esm/errors/NetworkError.js +11 -0
- package/dist/esm/errors/NetworkError.js.map +1 -0
- package/dist/esm/errors/SDKError.d.ts +7 -0
- package/dist/esm/errors/SDKError.d.ts.map +1 -0
- package/dist/esm/errors/SDKError.js +10 -0
- package/dist/esm/errors/SDKError.js.map +1 -0
- package/dist/esm/flow/DocumentCapture.d.ts +54 -0
- package/dist/esm/flow/DocumentCapture.d.ts.map +1 -0
- package/dist/esm/flow/DocumentCapture.js +797 -0
- package/dist/esm/flow/DocumentCapture.js.map +1 -0
- package/dist/esm/flow/SmartComplyFlow.d.ts +104 -0
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -0
- package/dist/esm/flow/SmartComplyFlow.js +1722 -0
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -0
- package/dist/esm/flow/theme.d.ts +30 -0
- package/dist/esm/flow/theme.d.ts.map +1 -0
- package/dist/esm/flow/theme.js +99 -0
- package/dist/esm/flow/theme.js.map +1 -0
- package/dist/esm/index.d.ts +20 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/modules/liveness/LivenessUI.d.ts +46 -0
- package/dist/esm/modules/liveness/LivenessUI.d.ts.map +1 -0
- package/dist/esm/modules/liveness/LivenessUI.js +605 -0
- package/dist/esm/modules/liveness/LivenessUI.js.map +1 -0
- package/dist/esm/modules/liveness/index.d.ts +5 -0
- package/dist/esm/modules/liveness/index.d.ts.map +1 -0
- package/dist/esm/modules/liveness/index.js +4 -0
- package/dist/esm/modules/liveness/index.js.map +1 -0
- package/dist/esm/modules/liveness/liveness.d.ts +85 -0
- package/dist/esm/modules/liveness/liveness.d.ts.map +1 -0
- package/dist/esm/modules/liveness/liveness.js +421 -0
- package/dist/esm/modules/liveness/liveness.js.map +1 -0
- package/dist/esm/modules/liveness/uploader.d.ts +34 -0
- package/dist/esm/modules/liveness/uploader.d.ts.map +1 -0
- package/dist/esm/modules/liveness/uploader.js +102 -0
- package/dist/esm/modules/liveness/uploader.js.map +1 -0
- package/dist/esm/modules/onboarding/index.d.ts +2 -0
- package/dist/esm/modules/onboarding/index.d.ts.map +1 -0
- package/dist/esm/modules/onboarding/index.js +2 -0
- package/dist/esm/modules/onboarding/index.js.map +1 -0
- package/dist/esm/modules/onboarding/onboarding.d.ts +25 -0
- package/dist/esm/modules/onboarding/onboarding.d.ts.map +1 -0
- package/dist/esm/modules/onboarding/onboarding.js +29 -0
- package/dist/esm/modules/onboarding/onboarding.js.map +1 -0
- package/dist/esm/types/common.d.ts +27 -0
- package/dist/esm/types/common.d.ts.map +1 -0
- package/dist/esm/types/common.js +2 -0
- package/dist/esm/types/common.js.map +1 -0
- package/dist/esm/types/liveness.d.ts +142 -0
- package/dist/esm/types/liveness.d.ts.map +1 -0
- package/dist/esm/types/liveness.js +2 -0
- package/dist/esm/types/liveness.js.map +1 -0
- package/dist/esm/types/onboarding.d.ts +53 -0
- package/dist/esm/types/onboarding.d.ts.map +1 -0
- package/dist/esm/types/onboarding.js +2 -0
- package/dist/esm/types/onboarding.js.map +1 -0
- package/dist/esm/types/quality.d.ts +23 -0
- package/dist/esm/types/quality.d.ts.map +1 -0
- package/dist/esm/types/quality.js +5 -0
- package/dist/esm/types/quality.js.map +1 -0
- package/dist/esm/utils/ImageQuality.d.ts +48 -0
- package/dist/esm/utils/ImageQuality.d.ts.map +1 -0
- package/dist/esm/utils/ImageQuality.js +177 -0
- package/dist/esm/utils/ImageQuality.js.map +1 -0
- package/dist/esm/utils/constants.d.ts +59 -0
- package/dist/esm/utils/constants.d.ts.map +1 -0
- package/dist/esm/utils/constants.js +67 -0
- package/dist/esm/utils/constants.js.map +1 -0
- package/dist/esm/utils/device.d.ts +14 -0
- package/dist/esm/utils/device.d.ts.map +1 -0
- package/dist/esm/utils/device.js +52 -0
- package/dist/esm/utils/device.js.map +1 -0
- package/dist/esm/utils/logger.d.ts +28 -0
- package/dist/esm/utils/logger.d.ts.map +1 -0
- package/dist/esm/utils/logger.js +64 -0
- package/dist/esm/utils/logger.js.map +1 -0
- package/dist/esm/utils/validator.d.ts +35 -0
- package/dist/esm/utils/validator.d.ts.map +1 -0
- package/dist/esm/utils/validator.js +102 -0
- package/dist/esm/utils/validator.js.map +1 -0
- package/package.json +13 -5
package/README.md
CHANGED
|
@@ -61,6 +61,20 @@ The SDK verifies that a person is **real**, **alive**, and **who they claim to b
|
|
|
61
61
|
npm install smartcomply-web-sdk
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
### Alternative: CDN (No Build Step)
|
|
65
|
+
|
|
66
|
+
```html
|
|
67
|
+
<script src="https://cdn.jsdelivr.net/npm/smartcomply-web-sdk@1.0.3/dist/smartcomply.browser.js"></script>
|
|
68
|
+
<script>
|
|
69
|
+
// SDK available as window.SmartComplySDK
|
|
70
|
+
SmartComplySDK.SmartComplyFlow.open({
|
|
71
|
+
apiKey: "your_api_key",
|
|
72
|
+
clientId: "your_client_id",
|
|
73
|
+
onComplete: (result) => console.log("Done:", result),
|
|
74
|
+
});
|
|
75
|
+
</script>
|
|
76
|
+
```
|
|
77
|
+
|
|
64
78
|
---
|
|
65
79
|
|
|
66
80
|
## Option 1: Drop-in Widget (Recommended)
|
|
@@ -477,6 +491,35 @@ import type {
|
|
|
477
491
|
|
|
478
492
|
---
|
|
479
493
|
|
|
494
|
+
## Quick Start (Test in 2 Minutes)
|
|
495
|
+
|
|
496
|
+
**For production, always use `npm install`. Clone only for testing/contributing.**
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
# 1. Install globally (for testing)
|
|
500
|
+
npm install -g smartcomply-web-sdk
|
|
501
|
+
|
|
502
|
+
# 2. Or clone and run demo (testing only)
|
|
503
|
+
git clone https://github.com/386konsult/smartcomply-web-sdk.git
|
|
504
|
+
cd smartcomply-web-sdk
|
|
505
|
+
npm install
|
|
506
|
+
npm run demo
|
|
507
|
+
# Opens http://localhost:3000 with test page
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## Testing
|
|
511
|
+
|
|
512
|
+
Test the SDK locally before integrating:
|
|
513
|
+
|
|
514
|
+
```bash
|
|
515
|
+
cd smartcomply-web-sdk
|
|
516
|
+
npm run demo
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
This starts a mock API server and opens a browser test page. Use these test credentials:
|
|
520
|
+
- **API Key**: Any string works in demo mode
|
|
521
|
+
- **Client ID**: Create one in your Adhere dashboard
|
|
522
|
+
|
|
480
523
|
## License
|
|
481
524
|
|
|
482
525
|
ISC
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FaceDetectionResult } from "./FaceDetector";
|
|
2
|
+
export interface ActionState {
|
|
3
|
+
action: string;
|
|
4
|
+
detected: boolean;
|
|
5
|
+
active: boolean;
|
|
6
|
+
confidence: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Sequential action detector aligned with backend's 5 UPPERCASE actions:
|
|
10
|
+
* BLINK, TURN_LEFT, TURN_RIGHT, TURN_HEAD, OPEN_MOUTH
|
|
11
|
+
*/
|
|
12
|
+
export declare class ActionDetector {
|
|
13
|
+
private requiredActions;
|
|
14
|
+
private completedActions;
|
|
15
|
+
private holdCounters;
|
|
16
|
+
constructor(requiredActions: string[]);
|
|
17
|
+
check(result: FaceDetectionResult): ActionState[];
|
|
18
|
+
getCurrentAction(): string | null;
|
|
19
|
+
private buildStates;
|
|
20
|
+
private getConfidence;
|
|
21
|
+
private getHeadYaw;
|
|
22
|
+
allCompleted(): boolean;
|
|
23
|
+
completedCount(): number;
|
|
24
|
+
reset(): void;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=ActionDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionDetector.d.ts","sourceRoot":"","sources":["../../../src/camera/ActionDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAoBD;;;GAGG;AACH,qBAAa,cAAc;IAIb,OAAO,CAAC,eAAe;IAHnC,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,YAAY,CAA6B;gBAE7B,eAAe,EAAE,MAAM,EAAE;IAE7C,KAAK,CAAC,MAAM,EAAE,mBAAmB,GAAG,WAAW,EAAE;IAgCjD,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAOjC,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,aAAa;IAoCrB,OAAO,CAAC,UAAU;IAKlB,YAAY,IAAI,OAAO;IAIvB,cAAc,IAAI,MAAM;IAIxB,KAAK,IAAI,IAAI;CAId"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-action detection tuning.
|
|
3
|
+
* - threshold : minimum confidence score required to start counting.
|
|
4
|
+
* - holdFrames: consecutive frames above threshold before action is confirmed.
|
|
5
|
+
*
|
|
6
|
+
* BLINK is a fast reflex (~100-200ms) so we use a low threshold and only 2
|
|
7
|
+
* frames. Head-turn and mouth-open are sustained movements so they need a
|
|
8
|
+
* slightly longer hold to avoid false positives.
|
|
9
|
+
*/
|
|
10
|
+
const ACTION_CONFIGS = {
|
|
11
|
+
BLINK: { threshold: 0.30, holdFrames: 2 },
|
|
12
|
+
TURN_LEFT: { threshold: 0.45, holdFrames: 6 },
|
|
13
|
+
TURN_RIGHT: { threshold: 0.45, holdFrames: 6 },
|
|
14
|
+
TURN_HEAD: { threshold: 0.45, holdFrames: 6 },
|
|
15
|
+
OPEN_MOUTH: { threshold: 0.40, holdFrames: 3 },
|
|
16
|
+
};
|
|
17
|
+
const DEFAULT_CONFIG = { threshold: 0.55, holdFrames: 8 };
|
|
18
|
+
/**
|
|
19
|
+
* Sequential action detector aligned with backend's 5 UPPERCASE actions:
|
|
20
|
+
* BLINK, TURN_LEFT, TURN_RIGHT, TURN_HEAD, OPEN_MOUTH
|
|
21
|
+
*/
|
|
22
|
+
export class ActionDetector {
|
|
23
|
+
constructor(requiredActions) {
|
|
24
|
+
this.requiredActions = requiredActions;
|
|
25
|
+
this.completedActions = new Set();
|
|
26
|
+
this.holdCounters = new Map();
|
|
27
|
+
}
|
|
28
|
+
check(result) {
|
|
29
|
+
const currentAction = this.getCurrentAction();
|
|
30
|
+
if (!result.faceDetected) {
|
|
31
|
+
return this.buildStates(currentAction, 0);
|
|
32
|
+
}
|
|
33
|
+
const bs = result.blendshapes;
|
|
34
|
+
// Only check the current active action (sequential flow)
|
|
35
|
+
if (currentAction && !this.completedActions.has(currentAction)) {
|
|
36
|
+
const conf = this.getConfidence(currentAction, bs, result.transformMatrix);
|
|
37
|
+
const cfg = ACTION_CONFIGS[currentAction] ?? DEFAULT_CONFIG;
|
|
38
|
+
if (conf >= cfg.threshold) {
|
|
39
|
+
const count = (this.holdCounters.get(currentAction) || 0) + 1;
|
|
40
|
+
this.holdCounters.set(currentAction, count);
|
|
41
|
+
if (count >= cfg.holdFrames) {
|
|
42
|
+
this.completedActions.add(currentAction);
|
|
43
|
+
this.holdCounters.delete(currentAction);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.holdCounters.set(currentAction, 0);
|
|
48
|
+
}
|
|
49
|
+
return this.buildStates(currentAction, conf);
|
|
50
|
+
}
|
|
51
|
+
return this.buildStates(currentAction, 0);
|
|
52
|
+
}
|
|
53
|
+
getCurrentAction() {
|
|
54
|
+
for (const action of this.requiredActions) {
|
|
55
|
+
if (!this.completedActions.has(action))
|
|
56
|
+
return action;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
buildStates(currentAction, currentConf) {
|
|
61
|
+
return this.requiredActions.map((action) => ({
|
|
62
|
+
action,
|
|
63
|
+
detected: this.completedActions.has(action),
|
|
64
|
+
active: action === currentAction,
|
|
65
|
+
confidence: action === currentAction ? currentConf : 0,
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
getConfidence(action, bs, matrix) {
|
|
69
|
+
switch (action) {
|
|
70
|
+
case "BLINK": {
|
|
71
|
+
const left = bs.get("eyeBlinkLeft") || 0;
|
|
72
|
+
const right = bs.get("eyeBlinkRight") || 0;
|
|
73
|
+
return (left + right) / 2;
|
|
74
|
+
}
|
|
75
|
+
case "TURN_LEFT": {
|
|
76
|
+
const yaw = this.getHeadYaw(matrix);
|
|
77
|
+
return yaw < -12 ? Math.min(1, Math.abs(yaw) / 30) : 0;
|
|
78
|
+
}
|
|
79
|
+
case "TURN_RIGHT": {
|
|
80
|
+
const yaw = this.getHeadYaw(matrix);
|
|
81
|
+
return yaw > 12 ? Math.min(1, yaw / 30) : 0;
|
|
82
|
+
}
|
|
83
|
+
case "TURN_HEAD": {
|
|
84
|
+
// Any significant yaw (left OR right) counts
|
|
85
|
+
const yaw = Math.abs(this.getHeadYaw(matrix));
|
|
86
|
+
return yaw > 15 ? Math.min(1, yaw / 30) : 0;
|
|
87
|
+
}
|
|
88
|
+
case "OPEN_MOUTH":
|
|
89
|
+
return bs.get("jawOpen") || 0;
|
|
90
|
+
default:
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
getHeadYaw(matrix) {
|
|
95
|
+
if (!matrix || matrix.length < 16)
|
|
96
|
+
return 0;
|
|
97
|
+
return Math.atan2(matrix[8], matrix[0]) * (180 / Math.PI);
|
|
98
|
+
}
|
|
99
|
+
allCompleted() {
|
|
100
|
+
return this.requiredActions.every((a) => this.completedActions.has(a));
|
|
101
|
+
}
|
|
102
|
+
completedCount() {
|
|
103
|
+
return this.completedActions.size;
|
|
104
|
+
}
|
|
105
|
+
reset() {
|
|
106
|
+
this.completedActions.clear();
|
|
107
|
+
this.holdCounters.clear();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=ActionDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionDetector.js","sourceRoot":"","sources":["../../../src/camera/ActionDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAEpC,IAAI,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IAiBtE,SAAS,IAAI,WAAW,GAAG,IAAI;IAI/B,IAAI,IAAI,IAAI;CAMb"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class CameraManager {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.stream = null;
|
|
4
|
+
}
|
|
5
|
+
async open(constraints) {
|
|
6
|
+
const defaults = {
|
|
7
|
+
video: {
|
|
8
|
+
facingMode: "user",
|
|
9
|
+
width: { ideal: 1280 },
|
|
10
|
+
height: { ideal: 720 },
|
|
11
|
+
},
|
|
12
|
+
audio: false,
|
|
13
|
+
};
|
|
14
|
+
this.stream = await navigator.mediaDevices.getUserMedia(constraints || defaults);
|
|
15
|
+
return this.stream;
|
|
16
|
+
}
|
|
17
|
+
getStream() {
|
|
18
|
+
return this.stream;
|
|
19
|
+
}
|
|
20
|
+
stop() {
|
|
21
|
+
if (this.stream) {
|
|
22
|
+
this.stream.getTracks().forEach((track) => track.stop());
|
|
23
|
+
this.stream = null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=CameraManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CameraManager.js","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,aAAa;IAA1B;QACU,WAAM,GAAuB,IAAI,CAAC;IA6B5C,CAAC;IA3BC,KAAK,CAAC,IAAI,CAAC,WAAoC;QAC7C,MAAM,QAAQ,GAA2B;YACvC,KAAK,EAAE;gBACL,UAAU,EAAE,MAAM;gBAClB,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;gBACtB,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;aACvB;YACD,KAAK,EAAE,KAAK;SACb,CAAC;QAEF,IAAI,CAAC,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CACrD,WAAW,IAAI,QAAQ,CACxB,CAAC;QAEF,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface FaceDetectionResult {
|
|
2
|
+
faceDetected: boolean;
|
|
3
|
+
blendshapes: Map<string, number>;
|
|
4
|
+
transformMatrix: number[] | null;
|
|
5
|
+
}
|
|
6
|
+
export declare class FaceDetectorEngine {
|
|
7
|
+
private landmarker;
|
|
8
|
+
init(): Promise<void>;
|
|
9
|
+
detect(video: HTMLVideoElement, timestamp: number): FaceDetectionResult;
|
|
10
|
+
destroy(): void;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=FaceDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FaceDetector.d.ts","sourceRoot":"","sources":["../../../src/camera/FaceDetector.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAClC;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,UAAU,CAA+B;IAE3C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,MAAM,CAAC,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,GAAG,mBAAmB;IA4BvE,OAAO,IAAI,IAAI;CAMhB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { FilesetResolver, FaceLandmarker, } from "@mediapipe/tasks-vision";
|
|
2
|
+
const WASM_CDN = "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm";
|
|
3
|
+
const MODEL_URL = "https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task";
|
|
4
|
+
export class FaceDetectorEngine {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.landmarker = null;
|
|
7
|
+
}
|
|
8
|
+
async init() {
|
|
9
|
+
const vision = await FilesetResolver.forVisionTasks(WASM_CDN);
|
|
10
|
+
// Fetch model ourselves to avoid MediaPipe internal path resolution issues
|
|
11
|
+
const modelResponse = await fetch(MODEL_URL);
|
|
12
|
+
if (!modelResponse.ok) {
|
|
13
|
+
throw new Error(`Failed to download face model (${modelResponse.status})`);
|
|
14
|
+
}
|
|
15
|
+
const modelBuffer = await modelResponse.arrayBuffer();
|
|
16
|
+
this.landmarker = await FaceLandmarker.createFromOptions(vision, {
|
|
17
|
+
baseOptions: {
|
|
18
|
+
modelAssetBuffer: new Uint8Array(modelBuffer),
|
|
19
|
+
},
|
|
20
|
+
runningMode: "VIDEO",
|
|
21
|
+
outputFaceBlendshapes: true,
|
|
22
|
+
outputFacialTransformationMatrixes: true,
|
|
23
|
+
numFaces: 1,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
detect(video, timestamp) {
|
|
27
|
+
if (!this.landmarker) {
|
|
28
|
+
return { faceDetected: false, blendshapes: new Map(), transformMatrix: null };
|
|
29
|
+
}
|
|
30
|
+
const result = this.landmarker.detectForVideo(video, timestamp);
|
|
31
|
+
if (!result.faceBlendshapes || result.faceBlendshapes.length === 0) {
|
|
32
|
+
return { faceDetected: false, blendshapes: new Map(), transformMatrix: null };
|
|
33
|
+
}
|
|
34
|
+
const blendshapes = new Map();
|
|
35
|
+
for (const bs of result.faceBlendshapes[0].categories) {
|
|
36
|
+
blendshapes.set(bs.categoryName, bs.score);
|
|
37
|
+
}
|
|
38
|
+
const transformMatrix = result.facialTransformationMatrixes &&
|
|
39
|
+
result.facialTransformationMatrixes.length > 0
|
|
40
|
+
? Array.from(result.facialTransformationMatrixes[0].data)
|
|
41
|
+
: null;
|
|
42
|
+
return { faceDetected: true, blendshapes, transformMatrix };
|
|
43
|
+
}
|
|
44
|
+
destroy() {
|
|
45
|
+
if (this.landmarker) {
|
|
46
|
+
this.landmarker.close();
|
|
47
|
+
this.landmarker = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=FaceDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FaceDetector.js","sourceRoot":"","sources":["../../../src/camera/FaceDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,cAAc,GAEf,MAAM,yBAAyB,CAAC;AAEjC,MAAM,QAAQ,GACZ,kEAAkE,CAAC;AACrE,MAAM,SAAS,GACb,qHAAqH,CAAC;AAQxH,MAAM,OAAO,kBAAkB;IAA/B;QACU,eAAU,GAA0B,IAAI,CAAC;IAyDnD,CAAC;IAvDC,KAAK,CAAC,IAAI;QACR,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAE9D,2EAA2E;QAC3E,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,kCAAkC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,WAAW,EAAE,CAAC;QAEtD,IAAI,CAAC,UAAU,GAAG,MAAM,cAAc,CAAC,iBAAiB,CAAC,MAAM,EAAE;YAC/D,WAAW,EAAE;gBACX,gBAAgB,EAAE,IAAI,UAAU,CAAC,WAAW,CAAC;aAC9C;YACD,WAAW,EAAE,OAAO;YACpB,qBAAqB,EAAE,IAAI;YAC3B,kCAAkC,EAAE,IAAI;YACxC,QAAQ,EAAE,CAAC;SACZ,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,KAAuB,EAAE,SAAiB;QAC/C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,GAAG,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;QAChF,CAAC;QAED,MAAM,MAAM,GAAyB,IAAI,CAAC,UAAU,CAAC,cAAc,CACjE,KAAK,EACL,SAAS,CACV,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,GAAG,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;QAChF,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC9C,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;YACtD,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC;QAED,MAAM,eAAe,GACnB,MAAM,CAAC,4BAA4B;YACnC,MAAM,CAAC,4BAA4B,CAAC,MAAM,GAAG,CAAC;YAC5C,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAa;YACrE,CAAC,CAAC,IAAI,CAAC;QAEX,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;IAC9D,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Camera permission utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provides helpers to check, request and handle camera permissions in a
|
|
5
|
+
* browser-consistent way. All functions are purely async and do NOT throw —
|
|
6
|
+
* they return a typed result so callers can handle each case explicitly.
|
|
7
|
+
*/
|
|
8
|
+
export type PermissionStatus = "granted" | "prompt" | "denied" | "unsupported" | "unknown";
|
|
9
|
+
export interface CameraPermissionResult {
|
|
10
|
+
status: PermissionStatus;
|
|
11
|
+
/** Human-readable reason (populated on denied / unsupported). */
|
|
12
|
+
reason?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Query the current camera permission state without prompting the user.
|
|
16
|
+
*
|
|
17
|
+
* Uses the Permissions API when available, falls back to "unknown" on older
|
|
18
|
+
* browsers (e.g. Safari < 16).
|
|
19
|
+
*/
|
|
20
|
+
export declare function queryCameraPermission(): Promise<CameraPermissionResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Request camera access and return the resulting MediaStream.
|
|
23
|
+
*
|
|
24
|
+
* Maps browser-specific errors onto actionable `CameraPermissionResult`
|
|
25
|
+
* values so callers can show the right UI instead of catching raw DOMExceptions.
|
|
26
|
+
*
|
|
27
|
+
* @param constraints - Optional MediaStreamConstraints override.
|
|
28
|
+
*/
|
|
29
|
+
export declare function requestCameraAccess(constraints?: MediaStreamConstraints): Promise<{
|
|
30
|
+
stream: MediaStream;
|
|
31
|
+
permission: CameraPermissionResult;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Stop all tracks on a MediaStream and release the camera.
|
|
35
|
+
* Safe to call with null/undefined.
|
|
36
|
+
*/
|
|
37
|
+
export declare function releaseCameraStream(stream: MediaStream | null | undefined): void;
|
|
38
|
+
/**
|
|
39
|
+
* Returns true if the browser supports the camera APIs needed by the SDK.
|
|
40
|
+
*/
|
|
41
|
+
export declare function isCameraSupported(): boolean;
|
|
42
|
+
//# sourceMappingURL=Permission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Permission.d.ts","sourceRoot":"","sources":["../../../src/camera/Permission.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,gBAAgB,CAAC;IACzB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAe7E;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,CAAC,EAAE,sBAAsB,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,sBAAsB,CAAA;CAAE,CAAC,CAkDtE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAGhF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Camera permission utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provides helpers to check, request and handle camera permissions in a
|
|
5
|
+
* browser-consistent way. All functions are purely async and do NOT throw —
|
|
6
|
+
* they return a typed result so callers can handle each case explicitly.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Query the current camera permission state without prompting the user.
|
|
10
|
+
*
|
|
11
|
+
* Uses the Permissions API when available, falls back to "unknown" on older
|
|
12
|
+
* browsers (e.g. Safari < 16).
|
|
13
|
+
*/
|
|
14
|
+
export async function queryCameraPermission() {
|
|
15
|
+
if (typeof navigator === "undefined" || !navigator.mediaDevices) {
|
|
16
|
+
return { status: "unsupported", reason: "getUserMedia is not supported in this browser." };
|
|
17
|
+
}
|
|
18
|
+
if (!navigator.permissions?.query) {
|
|
19
|
+
return { status: "unknown" };
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const result = await navigator.permissions.query({ name: "camera" });
|
|
23
|
+
return { status: result.state };
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { status: "unknown" };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Request camera access and return the resulting MediaStream.
|
|
31
|
+
*
|
|
32
|
+
* Maps browser-specific errors onto actionable `CameraPermissionResult`
|
|
33
|
+
* values so callers can show the right UI instead of catching raw DOMExceptions.
|
|
34
|
+
*
|
|
35
|
+
* @param constraints - Optional MediaStreamConstraints override.
|
|
36
|
+
*/
|
|
37
|
+
export async function requestCameraAccess(constraints) {
|
|
38
|
+
const defaults = {
|
|
39
|
+
video: { facingMode: "user", width: { ideal: 640 }, height: { ideal: 480 } },
|
|
40
|
+
audio: false,
|
|
41
|
+
};
|
|
42
|
+
try {
|
|
43
|
+
const stream = await navigator.mediaDevices.getUserMedia(constraints ?? defaults);
|
|
44
|
+
return { stream, permission: { status: "granted" } };
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const name = err?.name ?? "";
|
|
48
|
+
if (name === "NotAllowedError" || name === "PermissionDeniedError") {
|
|
49
|
+
return {
|
|
50
|
+
stream: null,
|
|
51
|
+
permission: {
|
|
52
|
+
status: "denied",
|
|
53
|
+
reason: "Camera access was denied. Please allow camera permissions in your browser settings and try again.",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (name === "NotFoundError" || name === "DevicesNotFoundError") {
|
|
58
|
+
return {
|
|
59
|
+
stream: null,
|
|
60
|
+
permission: {
|
|
61
|
+
status: "unsupported",
|
|
62
|
+
reason: "No camera was found. Please connect a camera and try again.",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (name === "NotReadableError" || name === "TrackStartError") {
|
|
67
|
+
return {
|
|
68
|
+
stream: null,
|
|
69
|
+
permission: {
|
|
70
|
+
status: "unknown",
|
|
71
|
+
reason: "Camera is already in use by another application. Please close it and try again.",
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
stream: null,
|
|
77
|
+
permission: {
|
|
78
|
+
status: "unknown",
|
|
79
|
+
reason: err?.message ?? "Unable to access the camera.",
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Stop all tracks on a MediaStream and release the camera.
|
|
86
|
+
* Safe to call with null/undefined.
|
|
87
|
+
*/
|
|
88
|
+
export function releaseCameraStream(stream) {
|
|
89
|
+
if (!stream)
|
|
90
|
+
return;
|
|
91
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Returns true if the browser supports the camera APIs needed by the SDK.
|
|
95
|
+
*/
|
|
96
|
+
export function isCameraSupported() {
|
|
97
|
+
return (typeof navigator !== "undefined" &&
|
|
98
|
+
!!navigator.mediaDevices?.getUserMedia &&
|
|
99
|
+
typeof MediaRecorder !== "undefined");
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=Permission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../../src/camera/Permission.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class VideoRecorder {
|
|
2
|
+
private recorder;
|
|
3
|
+
private chunks;
|
|
4
|
+
private startedAt;
|
|
5
|
+
start(stream: MediaStream): void;
|
|
6
|
+
stop(): Promise<{
|
|
7
|
+
blob: Blob;
|
|
8
|
+
durationSeconds: number;
|
|
9
|
+
}>;
|
|
10
|
+
isRecording(): boolean;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=VideoRecorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../../src/camera/VideoRecorder.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAa;IAE9B,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAsBhC,IAAI,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IA0BxD,WAAW,IAAI,OAAO;CAGvB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class VideoRecorder {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.recorder = null;
|
|
4
|
+
this.chunks = [];
|
|
5
|
+
this.startedAt = 0;
|
|
6
|
+
}
|
|
7
|
+
start(stream) {
|
|
8
|
+
this.chunks = [];
|
|
9
|
+
const mimeType = MediaRecorder.isTypeSupported("video/webm;codecs=vp9")
|
|
10
|
+
? "video/webm;codecs=vp9"
|
|
11
|
+
: "video/webm";
|
|
12
|
+
this.recorder = new MediaRecorder(stream, {
|
|
13
|
+
mimeType,
|
|
14
|
+
videoBitsPerSecond: 250000, // ~1.4 MB for 45s — keeps file under server upload limit
|
|
15
|
+
});
|
|
16
|
+
this.recorder.ondataavailable = (e) => {
|
|
17
|
+
if (e.data.size > 0) {
|
|
18
|
+
this.chunks.push(e.data);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
this.startedAt = Date.now();
|
|
22
|
+
this.recorder.start();
|
|
23
|
+
}
|
|
24
|
+
stop() {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
if (!this.recorder || this.recorder.state === "inactive") {
|
|
27
|
+
reject(new Error("Recorder is not active"));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const stoppedAt = Date.now();
|
|
31
|
+
this.recorder.onstop = () => {
|
|
32
|
+
const blob = new Blob(this.chunks, { type: "video/webm" });
|
|
33
|
+
const durationSeconds = parseFloat(((stoppedAt - this.startedAt) / 1000).toFixed(2));
|
|
34
|
+
this.chunks = [];
|
|
35
|
+
resolve({ blob, durationSeconds });
|
|
36
|
+
};
|
|
37
|
+
this.recorder.onerror = () => {
|
|
38
|
+
reject(new Error("Recording failed"));
|
|
39
|
+
};
|
|
40
|
+
this.recorder.stop();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
isRecording() {
|
|
44
|
+
return this.recorder?.state === "recording";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=VideoRecorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoRecorder.js","sourceRoot":"","sources":["../../../src/camera/VideoRecorder.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,aAAa;IAA1B;QACU,aAAQ,GAAyB,IAAI,CAAC;QACtC,WAAM,GAAW,EAAE,CAAC;QACpB,cAAS,GAAW,CAAC,CAAC;IAqDhC,CAAC;IAnDC,KAAK,CAAC,MAAmB;QACvB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QAEjB,MAAM,QAAQ,GAAG,aAAa,CAAC,eAAe,CAAC,uBAAuB,CAAC;YACrE,CAAC,CAAC,uBAAuB;YACzB,CAAC,CAAC,YAAY,CAAC;QAEjB,IAAI,CAAC,QAAQ,GAAG,IAAI,aAAa,CAAC,MAAM,EAAE;YACxC,QAAQ;YACR,kBAAkB,EAAE,MAAO,EAAE,yDAAyD;SACvF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,eAAe,GAAG,CAAC,CAAC,EAAE,EAAE;YACpC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC,CAAC;QAEF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,IAAI;QACF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBACzD,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;gBAC5C,OAAO;YACT,CAAC;YAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAE7B,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE;gBAC1B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;gBAC3D,MAAM,eAAe,GAAG,UAAU,CAChC,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CACjD,CAAC;gBACF,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;gBACjB,OAAO,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;YACrC,CAAC,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,OAAO,GAAG,GAAG,EAAE;gBAC3B,MAAM,CAAC,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC;YACxC,CAAC,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,EAAE,KAAK,KAAK,WAAW,CAAC;IAC9C,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type Environment = "sandbox" | "production";
|
|
2
|
+
export declare const BASE_URLS: Record<Environment, string>;
|
|
3
|
+
export interface SDKConfig {
|
|
4
|
+
apiKey: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
environment?: Environment;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=Config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../../src/client/Config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,YAAY,CAAC;AAEnD,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAGjD,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Config.js","sourceRoot":"","sources":["../../../src/client/Config.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,SAAS,GAAgC;IACpD,OAAO,EAAE,oCAAoC;IAC7C,UAAU,EAAE,6BAA6B;CAC1C,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { SDKConfig } from "./Config";
|
|
2
|
+
/**
|
|
3
|
+
* HTTP client aligned with Adhere backend contract.
|
|
4
|
+
*
|
|
5
|
+
* Two auth modes:
|
|
6
|
+
* - request() → Authorization: Bearer <apiKey> (session creation)
|
|
7
|
+
* - sessionRequest() → x-access-token: <sessionToken> (all other endpoints)
|
|
8
|
+
* - uploadWithSession() → multipart + x-access-token (file uploads)
|
|
9
|
+
*/
|
|
10
|
+
export declare class HttpClient {
|
|
11
|
+
private baseUrl;
|
|
12
|
+
private apiKey;
|
|
13
|
+
private timeout;
|
|
14
|
+
private sessionToken;
|
|
15
|
+
constructor(config: SDKConfig);
|
|
16
|
+
setSessionToken(token: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* API-key authenticated request (used for session creation only).
|
|
19
|
+
* Header: Authorization: Bearer <apiKey>
|
|
20
|
+
*/
|
|
21
|
+
request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Session-token authenticated request (used for all endpoints after session creation).
|
|
24
|
+
* Header: x-access-token: <sessionToken>
|
|
25
|
+
*/
|
|
26
|
+
sessionRequest<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
27
|
+
/**
|
|
28
|
+
* Multipart file upload with session-token auth.
|
|
29
|
+
* Header: x-access-token: <sessionToken>
|
|
30
|
+
* NOTE: Do NOT set Content-Type — browser sets it with boundary automatically.
|
|
31
|
+
*/
|
|
32
|
+
uploadWithSession<T>(path: string, formData: FormData): Promise<T>;
|
|
33
|
+
/**
|
|
34
|
+
* Unauthenticated GET — used for public endpoints (e.g. status polling after session revoke).
|
|
35
|
+
*/
|
|
36
|
+
publicGet<T>(path: string): Promise<T>;
|
|
37
|
+
/**
|
|
38
|
+
* Legacy upload with API key (kept for backward compatibility if needed).
|
|
39
|
+
*/
|
|
40
|
+
upload<T>(path: string, formData: FormData): Promise<T>;
|
|
41
|
+
private requireToken;
|
|
42
|
+
private _fetch;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=HttpClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpClient.d.ts","sourceRoot":"","sources":["../../../src/client/HttpClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,UAAU,CAAC;AAMhD;;;;;;;GAOG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAuB;gBAE/B,MAAM,EAAE,SAAS;IAM7B,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC;;;OAGG;IACG,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC;IAWb;;;OAGG;IACG,cAAc,CAAC,CAAC,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC;IAab;;;;OAIG;IACG,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC;IAYxE;;OAEG;IACG,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAO5C;;OAEG;IACG,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC;IAY7D,OAAO,CAAC,YAAY;YASN,MAAM;CAyErB"}
|