@tn3w/openage 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 +4 -0
- package/dist/openage.esm.js +135 -11
- package/dist/openage.esm.js.map +1 -1
- package/dist/openage.min.js +1 -1
- package/dist/openage.min.js.map +1 -1
- package/dist/openage.umd.js +135 -11
- package/dist/openage.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/challenge.js +61 -6
- package/src/constants.d.ts +1 -0
- package/src/constants.js +4 -3
- package/src/ui.d.ts +4 -0
- package/src/ui.js +57 -0
- package/src/widget.d.ts +1 -0
- package/src/widget.js +15 -2
package/README.md
CHANGED
|
@@ -95,6 +95,10 @@ OpenAge.execute(widgetId);
|
|
|
95
95
|
await OpenAge.challenge(params);
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
Runtime errors keep the popup open long enough to explain what happened.
|
|
99
|
+
If no camera is available, OpenAge tells the user to plug one in and closes
|
|
100
|
+
the popup automatically after 5 seconds.
|
|
101
|
+
|
|
98
102
|
## Main Params
|
|
99
103
|
|
|
100
104
|
| Param | Values |
|
package/dist/openage.esm.js
CHANGED
|
@@ -893,6 +893,52 @@ const STYLES = `
|
|
|
893
893
|
.oa-result-fail { color: var(--oa-danger); }
|
|
894
894
|
.oa-result-retry { color: var(--oa-warn); }
|
|
895
895
|
|
|
896
|
+
.oa-error-step {
|
|
897
|
+
display: flex;
|
|
898
|
+
flex-direction: column;
|
|
899
|
+
align-items: center;
|
|
900
|
+
gap: 0.7rem;
|
|
901
|
+
padding: 2rem 1.2rem;
|
|
902
|
+
background: var(--oa-surface);
|
|
903
|
+
border: 1px solid var(--oa-border);
|
|
904
|
+
border-radius: var(--oa-radius);
|
|
905
|
+
margin: 10px;
|
|
906
|
+
animation: oa-fade-in 0.4s ease;
|
|
907
|
+
text-align: center;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
.oa-error-step-icon {
|
|
911
|
+
width: 3rem;
|
|
912
|
+
height: 3rem;
|
|
913
|
+
display: flex;
|
|
914
|
+
align-items: center;
|
|
915
|
+
justify-content: center;
|
|
916
|
+
border-radius: 999px;
|
|
917
|
+
background: rgba(239, 68, 68, 0.12);
|
|
918
|
+
color: var(--oa-danger);
|
|
919
|
+
font-size: 1.7rem;
|
|
920
|
+
line-height: 1;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.oa-error-step-title {
|
|
924
|
+
font-size: 1rem;
|
|
925
|
+
font-weight: 700;
|
|
926
|
+
color: var(--oa-text);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
.oa-error-step-message {
|
|
930
|
+
font-size: 0.84rem;
|
|
931
|
+
line-height: 1.5;
|
|
932
|
+
color: var(--oa-text-muted);
|
|
933
|
+
max-width: 260px;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
.oa-error-step-countdown {
|
|
937
|
+
font-size: 0.75rem;
|
|
938
|
+
font-weight: 600;
|
|
939
|
+
color: var(--oa-danger);
|
|
940
|
+
}
|
|
941
|
+
|
|
896
942
|
.oa-hidden { display: none !important; }
|
|
897
943
|
|
|
898
944
|
/* ── Animations ──────────────────────────────── */
|
|
@@ -1019,6 +1065,17 @@ function challengeTemplate() {
|
|
|
1019
1065
|
`;
|
|
1020
1066
|
}
|
|
1021
1067
|
|
|
1068
|
+
function errorStepTemplate(message) {
|
|
1069
|
+
return `
|
|
1070
|
+
<div class="oa-error-step">
|
|
1071
|
+
<div class="oa-error-step-icon">✕</div>
|
|
1072
|
+
<div class="oa-error-step-title">Verification stopped</div>
|
|
1073
|
+
<div class="oa-error-step-message">${message}</div>
|
|
1074
|
+
<div class="oa-error-step-countdown"></div>
|
|
1075
|
+
</div>
|
|
1076
|
+
`;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1022
1079
|
function resultTemplate(outcome, message) {
|
|
1023
1080
|
const icons = {
|
|
1024
1081
|
fail: '✕',
|
|
@@ -1040,7 +1097,7 @@ function resultTemplate(outcome, message) {
|
|
|
1040
1097
|
|
|
1041
1098
|
const VERSION = '1.0.0';
|
|
1042
1099
|
|
|
1043
|
-
const MEDIAPIPE_CDN = 'https://cdn.jsdelivr.net/npm
|
|
1100
|
+
const MEDIAPIPE_CDN = 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.17';
|
|
1044
1101
|
|
|
1045
1102
|
const MEDIAPIPE_WASM = `${MEDIAPIPE_CDN}/wasm`;
|
|
1046
1103
|
|
|
@@ -1052,12 +1109,13 @@ const MEDIAPIPE_MODEL =
|
|
|
1052
1109
|
'face_landmarker.task';
|
|
1053
1110
|
|
|
1054
1111
|
const FACEAPI_CDN =
|
|
1055
|
-
'https://cdn.jsdelivr.net/npm/
|
|
1112
|
+
'https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js';
|
|
1056
1113
|
|
|
1057
1114
|
const FACEAPI_MODEL_CDN =
|
|
1058
|
-
'https://cdn.jsdelivr.net/gh/
|
|
1115
|
+
'https://cdn.jsdelivr.net/gh/justadudewhohacks/face-api.js@master/weights';
|
|
1059
1116
|
|
|
1060
1117
|
const MAX_RETRIES = 3;
|
|
1118
|
+
const ERROR_STEP_SECONDS = 5;
|
|
1061
1119
|
const BURST_FRAMES = 5;
|
|
1062
1120
|
const BURST_INTERVAL_MS = 200;
|
|
1063
1121
|
const POSITION_CHECK_MS = 100;
|
|
@@ -1538,8 +1596,20 @@ class Widget {
|
|
|
1538
1596
|
}
|
|
1539
1597
|
}
|
|
1540
1598
|
|
|
1541
|
-
showError() {
|
|
1542
|
-
this.
|
|
1599
|
+
showError(message) {
|
|
1600
|
+
if (!this.popupElements?.body) return;
|
|
1601
|
+
|
|
1602
|
+
this.popupElements.body.innerHTML = errorStepTemplate(message);
|
|
1603
|
+
this.popupElements.errorCountdown =
|
|
1604
|
+
this.popupElements.body.querySelector('.oa-error-step-countdown');
|
|
1605
|
+
this.hideActions();
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
setErrorCountdown(seconds) {
|
|
1609
|
+
if (!this.popupElements?.errorCountdown) return;
|
|
1610
|
+
|
|
1611
|
+
const unit = seconds === 1 ? 'second' : 'seconds';
|
|
1612
|
+
this.popupElements.errorCountdown.textContent = `Closing in ${seconds} ${unit}…`;
|
|
1543
1613
|
}
|
|
1544
1614
|
|
|
1545
1615
|
clearError() {
|
|
@@ -2717,6 +2787,64 @@ function sleep(ms) {
|
|
|
2717
2787
|
return new Promise((r) => setTimeout(r, ms));
|
|
2718
2788
|
}
|
|
2719
2789
|
|
|
2790
|
+
function resolveChallengeErrorMessage(error) {
|
|
2791
|
+
const name = typeof error?.name === 'string' ? error.name : '';
|
|
2792
|
+
const message = typeof error?.message === 'string' ? error.message : String(error || '');
|
|
2793
|
+
const normalized = `${name} ${message}`.toLowerCase();
|
|
2794
|
+
|
|
2795
|
+
if (
|
|
2796
|
+
name === 'NotFoundError' ||
|
|
2797
|
+
/request is not allowed by the user agent or the platform in the current context/.test(
|
|
2798
|
+
normalized
|
|
2799
|
+
) ||
|
|
2800
|
+
/requested device not found|device not found|no camera|could not start video source/.test(
|
|
2801
|
+
normalized
|
|
2802
|
+
)
|
|
2803
|
+
) {
|
|
2804
|
+
return 'No camera available. Plug in a camera and try again.';
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
if (
|
|
2808
|
+
name === 'NotAllowedError' ||
|
|
2809
|
+
name === 'PermissionDeniedError' ||
|
|
2810
|
+
/permission|camera access|access denied/.test(normalized)
|
|
2811
|
+
) {
|
|
2812
|
+
return 'Camera access was blocked. Allow camera access and try again.';
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
if (/positioning timeout/.test(normalized)) {
|
|
2816
|
+
return 'Face positioning timed out. Reopen the check and try again.';
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
return 'Verification failed. Please try again.';
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
async function showErrorStep(widget, error) {
|
|
2823
|
+
const message = resolveChallengeErrorMessage(error);
|
|
2824
|
+
|
|
2825
|
+
if (!widget.popup) {
|
|
2826
|
+
widget.openPopup?.();
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
widget.showError?.(message);
|
|
2830
|
+
|
|
2831
|
+
for (let seconds = ERROR_STEP_SECONDS; seconds > 0; seconds--) {
|
|
2832
|
+
if (!widget.popup) break;
|
|
2833
|
+
widget.setErrorCountdown?.(seconds);
|
|
2834
|
+
await sleep(1000);
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
widget.closePopup?.();
|
|
2838
|
+
widget.setState?.('retry');
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
async function handleChallengeError(widget, emitter, error) {
|
|
2842
|
+
console.log('Error during challenge:', error);
|
|
2843
|
+
await showErrorStep(widget, error);
|
|
2844
|
+
emitter.emit('error', error, widget.id);
|
|
2845
|
+
widget.params.errorCallback?.(error);
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2720
2848
|
async function runChallenge(widget, emitter) {
|
|
2721
2849
|
const mode = widget.params.mode || 'serverless';
|
|
2722
2850
|
|
|
@@ -2797,9 +2925,7 @@ async function runServerless(widget, emitter) {
|
|
|
2797
2925
|
emitResult(widget, emitter, result);
|
|
2798
2926
|
} catch (error) {
|
|
2799
2927
|
cleanupLocal();
|
|
2800
|
-
widget
|
|
2801
|
-
emitter.emit('error', error, widget.id);
|
|
2802
|
-
params.errorCallback?.(error);
|
|
2928
|
+
await handleChallengeError(widget, emitter, error);
|
|
2803
2929
|
}
|
|
2804
2930
|
}
|
|
2805
2931
|
|
|
@@ -2931,9 +3057,7 @@ async function runServer(widget, emitter) {
|
|
|
2931
3057
|
cleanupVM(transport);
|
|
2932
3058
|
} catch (error) {
|
|
2933
3059
|
cleanupVM();
|
|
2934
|
-
widget
|
|
2935
|
-
emitter.emit('error', error, widget.id);
|
|
2936
|
-
params.errorCallback?.(error);
|
|
3060
|
+
await handleChallengeError(widget, emitter, error);
|
|
2937
3061
|
}
|
|
2938
3062
|
}
|
|
2939
3063
|
|