smartcomply-web-sdk 1.0.61 → 1.0.62
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/dist/camera/CameraManager.d.ts +8 -0
- package/dist/camera/CameraManager.d.ts.map +1 -1
- package/dist/camera/CameraManager.js +13 -2
- package/dist/camera/CameraManager.js.map +1 -1
- package/dist/camera/Permission.d.ts +11 -0
- package/dist/camera/Permission.d.ts.map +1 -1
- package/dist/camera/Permission.js +23 -0
- package/dist/camera/Permission.js.map +1 -1
- package/dist/esm/camera/CameraManager.d.ts +8 -0
- package/dist/esm/camera/CameraManager.d.ts.map +1 -1
- package/dist/esm/camera/CameraManager.js +11 -1
- package/dist/esm/camera/CameraManager.js.map +1 -1
- package/dist/esm/camera/Permission.d.ts +11 -0
- package/dist/esm/camera/Permission.d.ts.map +1 -1
- package/dist/esm/camera/Permission.js +22 -0
- package/dist/esm/camera/Permission.js.map +1 -1
- package/dist/esm/flow/DocumentCapture.d.ts +1 -4
- package/dist/esm/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/esm/flow/DocumentCapture.js +47 -130
- package/dist/esm/flow/DocumentCapture.js.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +8 -13
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/utils/constants.d.ts +11 -3
- package/dist/esm/utils/constants.d.ts.map +1 -1
- package/dist/esm/utils/constants.js +11 -3
- package/dist/esm/utils/constants.js.map +1 -1
- package/dist/esm/utils/documentRules.d.ts +15 -0
- package/dist/esm/utils/documentRules.d.ts.map +1 -0
- package/dist/esm/utils/documentRules.js +27 -0
- package/dist/esm/utils/documentRules.js.map +1 -0
- package/dist/flow/DocumentCapture.d.ts +1 -4
- package/dist/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/flow/DocumentCapture.js +47 -130
- package/dist/flow/DocumentCapture.js.map +1 -1
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +11 -16
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/smartcomply.browser.js +60 -111
- package/dist/smartcomply.browser.js.map +4 -4
- package/dist/utils/constants.d.ts +11 -3
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +11 -3
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/documentRules.d.ts +15 -0
- package/dist/utils/documentRules.d.ts.map +1 -0
- package/dist/utils/documentRules.js +31 -0
- package/dist/utils/documentRules.js.map +1 -0
- package/package.json +6 -3
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { PermissionStatus } from "./Permission";
|
|
2
|
+
/** Error thrown by CameraManager.open() — carries the classified permission
|
|
3
|
+
* status alongside the friendly message, so callers can decide whether a
|
|
4
|
+
* failure is worth retrying without re-deriving it from the raw DOMException. */
|
|
5
|
+
export declare class CameraAccessError extends Error {
|
|
6
|
+
readonly status: PermissionStatus;
|
|
7
|
+
constructor(message: string, status: PermissionStatus);
|
|
8
|
+
}
|
|
1
9
|
export declare class CameraManager {
|
|
2
10
|
private stream;
|
|
3
11
|
private onInterrupted;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../src/camera/CameraManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErE;;iFAEiF;AACjF,qBAAa,iBAAkB,SAAQ,KAAK;aACG,MAAM,EAAE,gBAAgB;gBAAzD,OAAO,EAAE,MAAM,EAAkB,MAAM,EAAE,gBAAgB;CAItE;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,kBAAkB,CAAyB;IAEnD;;;OAGG;IACG,IAAI,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IAqBtE,SAAS,IAAI,WAAW,GAAG,IAAI;IAI/B;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAKpC;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAI/C,OAAO,CAAC,YAAY;IAiBpB,IAAI,IAAI,IAAI;CAQb"}
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CameraManager = void 0;
|
|
3
|
+
exports.CameraManager = exports.CameraAccessError = void 0;
|
|
4
4
|
const Permission_1 = require("./Permission");
|
|
5
|
+
/** Error thrown by CameraManager.open() — carries the classified permission
|
|
6
|
+
* status alongside the friendly message, so callers can decide whether a
|
|
7
|
+
* failure is worth retrying without re-deriving it from the raw DOMException. */
|
|
8
|
+
class CameraAccessError extends Error {
|
|
9
|
+
constructor(message, status) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.name = "CameraAccessError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.CameraAccessError = CameraAccessError;
|
|
5
16
|
class CameraManager {
|
|
6
17
|
constructor() {
|
|
7
18
|
this.stream = null;
|
|
@@ -23,7 +34,7 @@ class CameraManager {
|
|
|
23
34
|
};
|
|
24
35
|
const { stream, permission } = await (0, Permission_1.requestCameraAccess)(constraints || defaults);
|
|
25
36
|
if (!stream) {
|
|
26
|
-
throw new
|
|
37
|
+
throw new CameraAccessError(permission.reason || "Unable to access the camera.", permission.status);
|
|
27
38
|
}
|
|
28
39
|
this.stream = stream;
|
|
29
40
|
this._watchTracks();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraManager.js","sourceRoot":"","sources":["../../src/camera/CameraManager.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"CameraManager.js","sourceRoot":"","sources":["../../src/camera/CameraManager.ts"],"names":[],"mappings":";;;AAAA,6CAAqE;AAErE;;iFAEiF;AACjF,MAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAe,EAAkB,MAAwB;QACnE,KAAK,CAAC,OAAO,CAAC,CAAC;QAD4B,WAAM,GAAN,MAAM,CAAkB;QAEnE,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AALD,8CAKC;AAED,MAAa,aAAa;IAA1B;QACU,WAAM,GAAuB,IAAI,CAAC;QAClC,kBAAa,GAAwB,IAAI,CAAC;QAC1C,uBAAkB,GAAsB,EAAE,CAAC;IA+ErD,CAAC;IA7EC;;;OAGG;IACH,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,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,IAAA,gCAAmB,EAAC,WAAW,IAAI,QAAQ,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,iBAAiB,CAAC,UAAU,CAAC,MAAM,IAAI,8BAA8B,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACtG,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,MAAmB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAoB;QACtC,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;IAChC,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACxC,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,qEAAqE;gBACrE,iEAAiE;gBACjE,mEAAmE;gBACnE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAChB,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;gBACzB,CAAC;YACH,CAAC,CAAC;YACF,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,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;QACD,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;IAC/B,CAAC;CACF;AAlFD,sCAkFC"}
|
|
@@ -30,6 +30,17 @@ export declare function requestCameraAccess(constraints?: MediaStreamConstraints
|
|
|
30
30
|
stream: MediaStream;
|
|
31
31
|
permission: CameraPermissionResult;
|
|
32
32
|
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Whether a failed camera request is worth retrying. "denied" (user
|
|
35
|
+
* explicitly refused) and "unsupported" (no camera device at all) can never
|
|
36
|
+
* be fixed by retrying — surfacing the error immediately instead of burning
|
|
37
|
+
* several seconds of backoff first. Everything else (busy camera,
|
|
38
|
+
* unsupported constraints, anything unrecognized) is treated as
|
|
39
|
+
* potentially transient and retried — failing open avoids turning a
|
|
40
|
+
* recoverable hiccup into a hard failure on browsers that throw
|
|
41
|
+
* non-standard error names (notably Safari/iOS).
|
|
42
|
+
*/
|
|
43
|
+
export declare function isRetryableCameraError(status: PermissionStatus): boolean;
|
|
33
44
|
/**
|
|
34
45
|
* Stop all tracks on a MediaStream and release the camera.
|
|
35
46
|
* Safe to call with null/undefined.
|
|
@@ -1 +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,
|
|
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,CA4DtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAExE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAGhF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.queryCameraPermission = queryCameraPermission;
|
|
11
11
|
exports.requestCameraAccess = requestCameraAccess;
|
|
12
|
+
exports.isRetryableCameraError = isRetryableCameraError;
|
|
12
13
|
exports.releaseCameraStream = releaseCameraStream;
|
|
13
14
|
exports.isCameraSupported = isCameraSupported;
|
|
14
15
|
/**
|
|
@@ -78,6 +79,15 @@ async function requestCameraAccess(constraints) {
|
|
|
78
79
|
},
|
|
79
80
|
};
|
|
80
81
|
}
|
|
82
|
+
if (name === "OverconstrainedError") {
|
|
83
|
+
return {
|
|
84
|
+
stream: null,
|
|
85
|
+
permission: {
|
|
86
|
+
status: "unknown",
|
|
87
|
+
reason: "Camera constraints not supported. Trying with different settings...",
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
81
91
|
return {
|
|
82
92
|
stream: null,
|
|
83
93
|
permission: {
|
|
@@ -87,6 +97,19 @@ async function requestCameraAccess(constraints) {
|
|
|
87
97
|
};
|
|
88
98
|
}
|
|
89
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Whether a failed camera request is worth retrying. "denied" (user
|
|
102
|
+
* explicitly refused) and "unsupported" (no camera device at all) can never
|
|
103
|
+
* be fixed by retrying — surfacing the error immediately instead of burning
|
|
104
|
+
* several seconds of backoff first. Everything else (busy camera,
|
|
105
|
+
* unsupported constraints, anything unrecognized) is treated as
|
|
106
|
+
* potentially transient and retried — failing open avoids turning a
|
|
107
|
+
* recoverable hiccup into a hard failure on browsers that throw
|
|
108
|
+
* non-standard error names (notably Safari/iOS).
|
|
109
|
+
*/
|
|
110
|
+
function isRetryableCameraError(status) {
|
|
111
|
+
return status !== "denied" && status !== "unsupported";
|
|
112
|
+
}
|
|
90
113
|
/**
|
|
91
114
|
* Stop all tracks on a MediaStream and release the camera.
|
|
92
115
|
* Safe to call with null/undefined.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../src/camera/Permission.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAqBH,sDAeC;AAUD,
|
|
1
|
+
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../src/camera/Permission.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAqBH,sDAeC;AAUD,kDA8DC;AAYD,wDAEC;AAMD,kDAGC;AAKD,8CAMC;AA/HD;;;;;GAKG;AACI,KAAK,UAAU,qBAAqB;IACzC,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC;QAChE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,gDAAgD,EAAE,CAAC;IAC7F,CAAC;IAED,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC;QAClC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC/B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,QAA0B,EAAE,CAAC,CAAC;QACvF,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAyB,EAAE,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,mBAAmB,CACvC,WAAoC;IAEpC,MAAM,QAAQ,GAA2B;QACvC,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC5E,KAAK,EAAE,KAAK;KACb,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,WAAW,IAAI,QAAQ,CAAC,CAAC;QAClF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;IACvD,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,MAAM,IAAI,GAAW,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;QAErC,IAAI,IAAI,KAAK,iBAAiB,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;YACnE,OAAO;gBACL,MAAM,EAAE,IAAW;gBACnB,UAAU,EAAE;oBACV,MAAM,EAAE,QAAQ;oBAChB,MAAM,EAAE,mGAAmG;iBAC5G;aACF,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,KAAK,eAAe,IAAI,IAAI,KAAK,sBAAsB,EAAE,CAAC;YAChE,OAAO;gBACL,MAAM,EAAE,IAAW;gBACnB,UAAU,EAAE;oBACV,MAAM,EAAE,aAAa;oBACrB,MAAM,EAAE,6DAA6D;iBACtE;aACF,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,KAAK,kBAAkB,IAAI,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC9D,OAAO;gBACL,MAAM,EAAE,IAAW;gBACnB,UAAU,EAAE;oBACV,MAAM,EAAE,SAAS;oBACjB,MAAM,EAAE,iFAAiF;iBAC1F;aACF,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,KAAK,sBAAsB,EAAE,CAAC;YACpC,OAAO;gBACL,MAAM,EAAE,IAAW;gBACnB,UAAU,EAAE;oBACV,MAAM,EAAE,SAAS;oBACjB,MAAM,EAAE,qEAAqE;iBAC9E;aACF,CAAC;QACJ,CAAC;QAED,OAAO;YACL,MAAM,EAAE,IAAW;YACnB,UAAU,EAAE;gBACV,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,GAAG,EAAE,OAAO,IAAI,8BAA8B;aACvD;SACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,sBAAsB,CAAC,MAAwB;IAC7D,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,aAAa,CAAC;AACzD,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,MAAsC;IACxE,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB;IAC/B,OAAO,CACL,OAAO,SAAS,KAAK,WAAW;QAChC,CAAC,CAAC,SAAS,CAAC,YAAY,EAAE,YAAY;QACtC,OAAO,aAAa,KAAK,WAAW,CACrC,CAAC;AACJ,CAAC"}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { PermissionStatus } from "./Permission";
|
|
2
|
+
/** Error thrown by CameraManager.open() — carries the classified permission
|
|
3
|
+
* status alongside the friendly message, so callers can decide whether a
|
|
4
|
+
* failure is worth retrying without re-deriving it from the raw DOMException. */
|
|
5
|
+
export declare class CameraAccessError extends Error {
|
|
6
|
+
readonly status: PermissionStatus;
|
|
7
|
+
constructor(message: string, status: PermissionStatus);
|
|
8
|
+
}
|
|
1
9
|
export declare class CameraManager {
|
|
2
10
|
private stream;
|
|
3
11
|
private onInterrupted;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErE;;iFAEiF;AACjF,qBAAa,iBAAkB,SAAQ,KAAK;aACG,MAAM,EAAE,gBAAgB;gBAAzD,OAAO,EAAE,MAAM,EAAkB,MAAM,EAAE,gBAAgB;CAItE;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,kBAAkB,CAAyB;IAEnD;;;OAGG;IACG,IAAI,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IAqBtE,SAAS,IAAI,WAAW,GAAG,IAAI;IAI/B;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAKpC;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAI/C,OAAO,CAAC,YAAY;IAiBpB,IAAI,IAAI,IAAI;CAQb"}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { requestCameraAccess } from "./Permission";
|
|
2
|
+
/** Error thrown by CameraManager.open() — carries the classified permission
|
|
3
|
+
* status alongside the friendly message, so callers can decide whether a
|
|
4
|
+
* failure is worth retrying without re-deriving it from the raw DOMException. */
|
|
5
|
+
export class CameraAccessError extends Error {
|
|
6
|
+
constructor(message, status) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.name = "CameraAccessError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
2
12
|
export class CameraManager {
|
|
3
13
|
constructor() {
|
|
4
14
|
this.stream = null;
|
|
@@ -20,7 +30,7 @@ export class CameraManager {
|
|
|
20
30
|
};
|
|
21
31
|
const { stream, permission } = await requestCameraAccess(constraints || defaults);
|
|
22
32
|
if (!stream) {
|
|
23
|
-
throw new
|
|
33
|
+
throw new CameraAccessError(permission.reason || "Unable to access the camera.", permission.status);
|
|
24
34
|
}
|
|
25
35
|
this.stream = stream;
|
|
26
36
|
this._watchTracks();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraManager.js","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"CameraManager.js","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAoB,MAAM,cAAc,CAAC;AAErE;;iFAEiF;AACjF,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAe,EAAkB,MAAwB;QACnE,KAAK,CAAC,OAAO,CAAC,CAAC;QAD4B,WAAM,GAAN,MAAM,CAAkB;QAEnE,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,MAAM,OAAO,aAAa;IAA1B;QACU,WAAM,GAAuB,IAAI,CAAC;QAClC,kBAAa,GAAwB,IAAI,CAAC;QAC1C,uBAAkB,GAAsB,EAAE,CAAC;IA+ErD,CAAC;IA7EC;;;OAGG;IACH,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,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,mBAAmB,CAAC,WAAW,IAAI,QAAQ,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,iBAAiB,CAAC,UAAU,CAAC,MAAM,IAAI,8BAA8B,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACtG,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,MAAmB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAoB;QACtC,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;IAChC,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACxC,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,qEAAqE;gBACrE,iEAAiE;gBACjE,mEAAmE;gBACnE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAChB,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;gBACzB,CAAC;YACH,CAAC,CAAC;YACF,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,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;QACD,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;IAC/B,CAAC;CACF"}
|
|
@@ -30,6 +30,17 @@ export declare function requestCameraAccess(constraints?: MediaStreamConstraints
|
|
|
30
30
|
stream: MediaStream;
|
|
31
31
|
permission: CameraPermissionResult;
|
|
32
32
|
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Whether a failed camera request is worth retrying. "denied" (user
|
|
35
|
+
* explicitly refused) and "unsupported" (no camera device at all) can never
|
|
36
|
+
* be fixed by retrying — surfacing the error immediately instead of burning
|
|
37
|
+
* several seconds of backoff first. Everything else (busy camera,
|
|
38
|
+
* unsupported constraints, anything unrecognized) is treated as
|
|
39
|
+
* potentially transient and retried — failing open avoids turning a
|
|
40
|
+
* recoverable hiccup into a hard failure on browsers that throw
|
|
41
|
+
* non-standard error names (notably Safari/iOS).
|
|
42
|
+
*/
|
|
43
|
+
export declare function isRetryableCameraError(status: PermissionStatus): boolean;
|
|
33
44
|
/**
|
|
34
45
|
* Stop all tracks on a MediaStream and release the camera.
|
|
35
46
|
* Safe to call with null/undefined.
|
|
@@ -1 +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,
|
|
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,CA4DtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAExE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAGhF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C"}
|
|
@@ -72,6 +72,15 @@ export async function requestCameraAccess(constraints) {
|
|
|
72
72
|
},
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
+
if (name === "OverconstrainedError") {
|
|
76
|
+
return {
|
|
77
|
+
stream: null,
|
|
78
|
+
permission: {
|
|
79
|
+
status: "unknown",
|
|
80
|
+
reason: "Camera constraints not supported. Trying with different settings...",
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
75
84
|
return {
|
|
76
85
|
stream: null,
|
|
77
86
|
permission: {
|
|
@@ -81,6 +90,19 @@ export async function requestCameraAccess(constraints) {
|
|
|
81
90
|
};
|
|
82
91
|
}
|
|
83
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Whether a failed camera request is worth retrying. "denied" (user
|
|
95
|
+
* explicitly refused) and "unsupported" (no camera device at all) can never
|
|
96
|
+
* be fixed by retrying — surfacing the error immediately instead of burning
|
|
97
|
+
* several seconds of backoff first. Everything else (busy camera,
|
|
98
|
+
* unsupported constraints, anything unrecognized) is treated as
|
|
99
|
+
* potentially transient and retried — failing open avoids turning a
|
|
100
|
+
* recoverable hiccup into a hard failure on browsers that throw
|
|
101
|
+
* non-standard error names (notably Safari/iOS).
|
|
102
|
+
*/
|
|
103
|
+
export function isRetryableCameraError(status) {
|
|
104
|
+
return status !== "denied" && status !== "unsupported";
|
|
105
|
+
}
|
|
84
106
|
/**
|
|
85
107
|
* Stop all tracks on a MediaStream and release the camera.
|
|
86
108
|
* Safe to call with null/undefined.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../../src/camera/Permission.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../../src/camera/Permission.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@ export interface DocumentCaptureResult {
|
|
|
15
15
|
export declare class DocumentCapture {
|
|
16
16
|
private root;
|
|
17
17
|
private videoEl;
|
|
18
|
-
private
|
|
18
|
+
private cameraManager;
|
|
19
19
|
private resolveCapture;
|
|
20
20
|
private rejectCapture;
|
|
21
21
|
private frontBlob;
|
|
@@ -28,14 +28,12 @@ export declare class DocumentCapture {
|
|
|
28
28
|
*/
|
|
29
29
|
capture(container: HTMLElement, theme: ThemeColors, documentType: string, requiresBack?: boolean): Promise<DocumentCaptureResult>;
|
|
30
30
|
destroy(): void;
|
|
31
|
-
private requiresBackSide;
|
|
32
31
|
private shouldCaptureBackSide;
|
|
33
32
|
private renderChoiceScreen;
|
|
34
33
|
/** Returns an appropriate emoji icon for the document type. */
|
|
35
34
|
private _docIcon;
|
|
36
35
|
private renderCameraView;
|
|
37
36
|
private openCamera;
|
|
38
|
-
private getCameraErrorMessage;
|
|
39
37
|
private captureFrame;
|
|
40
38
|
/**
|
|
41
39
|
* Runs the local sharpness/glare/contrast/brightness check against a
|
|
@@ -51,7 +49,6 @@ export declare class DocumentCapture {
|
|
|
51
49
|
private validateFile;
|
|
52
50
|
private handleFileUpload;
|
|
53
51
|
private stopCamera;
|
|
54
|
-
private createEnhancedButton;
|
|
55
52
|
private showLoadingState;
|
|
56
53
|
private showError;
|
|
57
54
|
private createButton;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentCapture.d.ts","sourceRoot":"","sources":["../../../src/flow/DocumentCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"DocumentCapture.d.ts","sourceRoot":"","sources":["../../../src/flow/DocumentCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAOtC;;;;;;;;GAQG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAA+B;IAC3C,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,cAAc,CAA0D;IAChF,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,oBAAoB,CAAkC;IAE9D;;;OAGG;IACH,OAAO,CACL,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,WAAW,EAClB,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,OAAO,GACrB,OAAO,CAAC,qBAAqB,CAAC;IAgCjC,OAAO,IAAI,IAAI;IAuBf,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,kBAAkB;IAoK1B,+DAA+D;IAC/D,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,gBAAgB;YAuEV,UAAU;IAoExB,OAAO,CAAC,YAAY;IAkCpB;;;;OAIG;YACW,mBAAmB;IAYjC,+EAA+E;YACjE,kBAAkB;IAsBhC,OAAO,CAAC,WAAW;IAkHnB,OAAO,CAAC,qBAAqB;YAqCf,wBAAwB;IAUtC,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,gBAAgB;IAwDxB,OAAO,CAAC,SAAS;IAgCjB,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,aAAa;IA4ErB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAuC5B,OAAO,CAAC,gBAAgB;CASzB"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { assessQuality } from "../utils/ImageQuality";
|
|
2
|
+
import { requiresBackSide, computeScaleRatio } from "../utils/documentRules";
|
|
3
|
+
import { CameraManager, CameraAccessError } from "../camera/CameraManager";
|
|
4
|
+
import { isRetryableCameraError } from "../camera/Permission";
|
|
5
|
+
import { DOC_CAMERA_IDEAL_WIDTH, DOC_CAMERA_IDEAL_HEIGHT } from "../utils/constants";
|
|
2
6
|
export class DocumentCapture {
|
|
3
7
|
constructor() {
|
|
4
8
|
this.root = null;
|
|
5
9
|
this.videoEl = null;
|
|
6
|
-
this.
|
|
10
|
+
this.cameraManager = new CameraManager();
|
|
7
11
|
this.resolveCapture = null;
|
|
8
12
|
this.rejectCapture = null;
|
|
9
13
|
this.frontBlob = null;
|
|
@@ -63,26 +67,11 @@ export class DocumentCapture {
|
|
|
63
67
|
this.backBlob = null;
|
|
64
68
|
this.currentSide = 'front';
|
|
65
69
|
}
|
|
66
|
-
requiresBackSide(documentType) {
|
|
67
|
-
// Only request back-side capture for documents where the backend OCR
|
|
68
|
-
// actually needs the reverse face. NIN cards are single-sided for OCR
|
|
69
|
-
// purposes — do NOT include "nin" here.
|
|
70
|
-
const type = documentType.toLowerCase();
|
|
71
|
-
return type.includes("voter") ||
|
|
72
|
-
type.includes("driver") ||
|
|
73
|
-
type.includes("license") ||
|
|
74
|
-
type.includes("national id") ||
|
|
75
|
-
type.includes("national id card") ||
|
|
76
|
-
type.includes("ghana");
|
|
77
|
-
// Passport: back side is the machine-readable zone but OCR reads from front only.
|
|
78
|
-
// NIN / NIN with face: front only needed.
|
|
79
|
-
// Voter's card / Driver's license / National ID card: back required.
|
|
80
|
-
}
|
|
81
70
|
shouldCaptureBackSide(documentType) {
|
|
82
|
-
// Prefer the backend-provided flag; fall back to
|
|
71
|
+
// Prefer the backend-provided flag; fall back to the shared heuristic only when absent.
|
|
83
72
|
return this.requiresBackOverride !== undefined
|
|
84
73
|
? this.requiresBackOverride
|
|
85
|
-
:
|
|
74
|
+
: requiresBackSide(documentType);
|
|
86
75
|
}
|
|
87
76
|
renderChoiceScreen(container, theme, documentType) {
|
|
88
77
|
this.root = document.createElement("div");
|
|
@@ -309,28 +298,32 @@ export class DocumentCapture {
|
|
|
309
298
|
return;
|
|
310
299
|
}
|
|
311
300
|
const maxRetries = 3;
|
|
312
|
-
|
|
313
|
-
|
|
301
|
+
// Retries only transient/negotiable failures (busy camera, unsupported
|
|
302
|
+
// constraints, anything unrecognized) — a hard permission denial or "no
|
|
303
|
+
// camera exists" can never be fixed by retrying, so those short-circuit
|
|
304
|
+
// to an immediate error instead of burning ~6s of backoff first.
|
|
305
|
+
const attemptCameraAccess = async (constraints, retryCount = 0) => {
|
|
314
306
|
try {
|
|
315
|
-
return await
|
|
307
|
+
return await this.cameraManager.open(constraints);
|
|
316
308
|
}
|
|
317
309
|
catch (err) {
|
|
318
310
|
console.warn(`Camera access attempt ${retryCount + 1} failed:`, err);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount)); // Exponential backoff
|
|
322
|
-
return attemptCameraAccess(constraints);
|
|
311
|
+
const status = err instanceof CameraAccessError ? err.status : "unknown";
|
|
312
|
+
if (isRetryableCameraError(status) && retryCount < maxRetries - 1) {
|
|
313
|
+
await new Promise((resolve) => setTimeout(resolve, 1000 * (retryCount + 1))); // Exponential backoff
|
|
314
|
+
return attemptCameraAccess(constraints, retryCount + 1);
|
|
323
315
|
}
|
|
324
316
|
throw err;
|
|
325
317
|
}
|
|
326
318
|
};
|
|
319
|
+
let stream;
|
|
327
320
|
try {
|
|
328
321
|
// Try rear camera first
|
|
329
|
-
|
|
322
|
+
stream = await attemptCameraAccess({
|
|
330
323
|
video: {
|
|
331
324
|
facingMode: { ideal: "environment" },
|
|
332
|
-
width: { ideal:
|
|
333
|
-
height: { ideal:
|
|
325
|
+
width: { ideal: DOC_CAMERA_IDEAL_WIDTH },
|
|
326
|
+
height: { ideal: DOC_CAMERA_IDEAL_HEIGHT },
|
|
334
327
|
},
|
|
335
328
|
});
|
|
336
329
|
}
|
|
@@ -338,48 +331,37 @@ export class DocumentCapture {
|
|
|
338
331
|
console.warn("Rear camera failed, trying any camera:", err);
|
|
339
332
|
// Fallback to any camera
|
|
340
333
|
try {
|
|
341
|
-
|
|
342
|
-
video: { width: { ideal:
|
|
334
|
+
stream = await attemptCameraAccess({
|
|
335
|
+
video: { width: { ideal: DOC_CAMERA_IDEAL_WIDTH } },
|
|
343
336
|
});
|
|
344
337
|
}
|
|
345
338
|
catch (fallbackErr) {
|
|
346
|
-
|
|
347
|
-
this.rejectCapture?.(new Error(errorMessage));
|
|
339
|
+
this.rejectCapture?.(new Error(fallbackErr?.message || "Camera access failed. Please check your camera permissions and try again."));
|
|
348
340
|
return;
|
|
349
341
|
}
|
|
350
342
|
}
|
|
343
|
+
// A capture that's already resolved (photo taken, camera intentionally
|
|
344
|
+
// stopped) must not surface a stale interruption from the old stream.
|
|
345
|
+
this.cameraManager.onStreamInterrupted(() => {
|
|
346
|
+
if (this.resolveCapture || this.rejectCapture) {
|
|
347
|
+
this.rejectCapture?.(new Error("Camera connection was lost. Please check your camera and try again."));
|
|
348
|
+
}
|
|
349
|
+
});
|
|
351
350
|
if (this.videoEl) {
|
|
352
|
-
this.videoEl.srcObject =
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
getCameraErrorMessage(error) {
|
|
356
|
-
if (error.name === 'NotAllowedError') {
|
|
357
|
-
return "Camera access denied. Please allow camera permissions and try again.";
|
|
358
|
-
}
|
|
359
|
-
else if (error.name === 'NotFoundError') {
|
|
360
|
-
return "No camera found. Please ensure your device has a working camera.";
|
|
361
|
-
}
|
|
362
|
-
else if (error.name === 'NotReadableError') {
|
|
363
|
-
return "Camera is already in use by another application. Please close other apps using the camera and try again.";
|
|
364
|
-
}
|
|
365
|
-
else if (error.name === 'OverconstrainedError') {
|
|
366
|
-
return "Camera constraints not supported. Trying with different settings...";
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
return "Camera access failed. Please check your camera permissions and try again.";
|
|
351
|
+
this.videoEl.srcObject = stream;
|
|
370
352
|
}
|
|
371
353
|
}
|
|
372
354
|
captureFrame(container, theme, documentType) {
|
|
373
355
|
if (!this.videoEl)
|
|
374
356
|
return;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
357
|
+
// Draw at the video's native resolution — the downstream compressImage()
|
|
358
|
+
// call (see proceedWithPhoto) is the single place resolution gets capped
|
|
359
|
+
// (via computeScaleRatio, which never upscales). Resizing here too used
|
|
360
|
+
// to cap captures at 1280x960 before compressImage ever ran, silently
|
|
361
|
+
// undermining its higher resolution/quality target.
|
|
380
362
|
const canvas = document.createElement("canvas");
|
|
381
|
-
canvas.width =
|
|
382
|
-
canvas.height =
|
|
363
|
+
canvas.width = this.videoEl.videoWidth;
|
|
364
|
+
canvas.height = this.videoEl.videoHeight;
|
|
383
365
|
const ctx = canvas.getContext("2d");
|
|
384
366
|
if (!ctx)
|
|
385
367
|
return;
|
|
@@ -387,14 +369,15 @@ export class DocumentCapture {
|
|
|
387
369
|
canvas.toBlob(async (blob) => {
|
|
388
370
|
if (blob) {
|
|
389
371
|
this.stopCamera();
|
|
390
|
-
// Quality analysis runs synchronously over
|
|
391
|
-
// show a loading state so the capture->preview
|
|
392
|
-
// looks like a frozen/unresponsive tap on
|
|
372
|
+
// Quality analysis runs synchronously over the captured frame's
|
|
373
|
+
// pixel data — show a loading state so the capture->preview
|
|
374
|
+
// transition never looks like a frozen/unresponsive tap on
|
|
375
|
+
// slower devices.
|
|
393
376
|
this.showLoadingState("Analyzing photo...", theme);
|
|
394
377
|
const quality = await this._assessImageQuality(canvas, ctx);
|
|
395
378
|
this.showPreview(container, theme, blob, documentType, quality);
|
|
396
379
|
}
|
|
397
|
-
}, "image/jpeg", 0.
|
|
380
|
+
}, "image/jpeg", 0.92);
|
|
398
381
|
}
|
|
399
382
|
/**
|
|
400
383
|
* Runs the local sharpness/glare/contrast/brightness check against a
|
|
@@ -587,78 +570,12 @@ export class DocumentCapture {
|
|
|
587
570
|
input.click();
|
|
588
571
|
}
|
|
589
572
|
stopCamera() {
|
|
590
|
-
|
|
591
|
-
this.stream.getTracks().forEach((t) => t.stop());
|
|
592
|
-
this.stream = null;
|
|
593
|
-
}
|
|
573
|
+
this.cameraManager.stop();
|
|
594
574
|
if (this.videoEl) {
|
|
595
575
|
this.videoEl.srcObject = null;
|
|
596
576
|
this.videoEl = null;
|
|
597
577
|
}
|
|
598
578
|
}
|
|
599
|
-
createEnhancedButton(label, theme, isPrimary, ariaLabel) {
|
|
600
|
-
const btn = document.createElement("button");
|
|
601
|
-
btn.textContent = label;
|
|
602
|
-
btn.setAttribute("aria-label", ariaLabel);
|
|
603
|
-
btn.style.cssText = `
|
|
604
|
-
padding:18px 24px;border-radius:16px;font-size:16px;font-weight:600;
|
|
605
|
-
cursor:pointer;transition:all 0.3s ease;border:none;width:100%;
|
|
606
|
-
min-height:56px;touch-action:manipulation;-webkit-tap-highlight-color:transparent;
|
|
607
|
-
position:relative;overflow:hidden;
|
|
608
|
-
${isPrimary
|
|
609
|
-
? `background:linear-gradient(135deg, ${theme.primary}, ${theme.primary}DD);color:#fff;box-shadow:0 8px 24px ${theme.primary}25, 0 4px 12px ${theme.primary}15;`
|
|
610
|
-
: `background:${theme.inputBg};color:${theme.text};border:2px solid ${theme.border};box-shadow:0 2px 8px ${theme.border}20;`}
|
|
611
|
-
`;
|
|
612
|
-
// Add ripple effect
|
|
613
|
-
btn.addEventListener("click", (e) => {
|
|
614
|
-
const ripple = document.createElement("span");
|
|
615
|
-
const rect = btn.getBoundingClientRect();
|
|
616
|
-
const size = Math.max(rect.width, rect.height);
|
|
617
|
-
const x = e.clientX - rect.left - size / 2;
|
|
618
|
-
const y = e.clientY - rect.top - size / 2;
|
|
619
|
-
ripple.style.cssText = `
|
|
620
|
-
position:absolute;width:${size}px;height:${size}px;
|
|
621
|
-
left:${x}px;top:${y}px;border-radius:50%;
|
|
622
|
-
background:${isPrimary ? 'rgba(255,255,255,0.3)' : theme.primary + '20'};
|
|
623
|
-
transform:scale(0);animation:ripple 0.6s ease-out;
|
|
624
|
-
pointer-events:none;
|
|
625
|
-
`;
|
|
626
|
-
btn.appendChild(ripple);
|
|
627
|
-
setTimeout(() => ripple.remove(), 600);
|
|
628
|
-
});
|
|
629
|
-
btn.addEventListener("mouseenter", () => {
|
|
630
|
-
btn.style.transform = "translateY(-2px)";
|
|
631
|
-
if (isPrimary) {
|
|
632
|
-
btn.style.boxShadow = `0 12px 32px ${theme.primary}30, 0 6px 16px ${theme.primary}20`;
|
|
633
|
-
}
|
|
634
|
-
else {
|
|
635
|
-
btn.style.boxShadow = `0 4px 16px ${theme.border}30, 0 2px 8px ${theme.border}20`;
|
|
636
|
-
btn.style.borderColor = theme.primary;
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
btn.addEventListener("mouseleave", () => {
|
|
640
|
-
btn.style.transform = "translateY(0)";
|
|
641
|
-
if (isPrimary) {
|
|
642
|
-
btn.style.boxShadow = `0 8px 24px ${theme.primary}25, 0 4px 12px ${theme.primary}15`;
|
|
643
|
-
}
|
|
644
|
-
else {
|
|
645
|
-
btn.style.boxShadow = `0 2px 8px ${theme.border}20`;
|
|
646
|
-
btn.style.borderColor = theme.border;
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
// Add ripple animation
|
|
650
|
-
if (!document.head.querySelector('style[data-ripple-animation]')) {
|
|
651
|
-
const style = document.createElement('style');
|
|
652
|
-
style.setAttribute('data-ripple-animation', 'true');
|
|
653
|
-
style.textContent = `
|
|
654
|
-
@keyframes ripple {
|
|
655
|
-
to { transform: scale(4); opacity: 0; }
|
|
656
|
-
}
|
|
657
|
-
`;
|
|
658
|
-
document.head.appendChild(style);
|
|
659
|
-
}
|
|
660
|
-
return btn;
|
|
661
|
-
}
|
|
662
579
|
showLoadingState(message, theme) {
|
|
663
580
|
if (!this.root)
|
|
664
581
|
return;
|
|
@@ -774,7 +691,7 @@ export class DocumentCapture {
|
|
|
774
691
|
const swapped = orientation >= 5 && orientation <= 8;
|
|
775
692
|
const srcW = swapped ? h : w;
|
|
776
693
|
const srcH = swapped ? w : h;
|
|
777
|
-
const ratio =
|
|
694
|
+
const ratio = computeScaleRatio(srcW, srcH, maxWidth, maxHeight);
|
|
778
695
|
const outW = Math.round(srcW * ratio);
|
|
779
696
|
const outH = Math.round(srcH * ratio);
|
|
780
697
|
const canvas = document.createElement("canvas");
|
|
@@ -821,7 +738,7 @@ export class DocumentCapture {
|
|
|
821
738
|
imgEl.onload = () => {
|
|
822
739
|
URL.revokeObjectURL(url);
|
|
823
740
|
let { naturalWidth: w, naturalHeight: h } = imgEl;
|
|
824
|
-
const ratio =
|
|
741
|
+
const ratio = computeScaleRatio(w, h, maxWidth, maxHeight);
|
|
825
742
|
const canvas = document.createElement("canvas");
|
|
826
743
|
canvas.width = Math.round(w * ratio);
|
|
827
744
|
canvas.height = Math.round(h * ratio);
|