@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/package.json
CHANGED
package/src/challenge.js
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
import {
|
|
28
28
|
BURST_FRAMES,
|
|
29
29
|
BURST_INTERVAL_MS,
|
|
30
|
+
ERROR_STEP_SECONDS,
|
|
30
31
|
MAX_RETRIES,
|
|
31
32
|
MOTION_CAPTURE_MS,
|
|
32
33
|
MOTION_SAMPLE_MS,
|
|
@@ -110,6 +111,64 @@ function sleep(ms) {
|
|
|
110
111
|
return new Promise((r) => setTimeout(r, ms));
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
function resolveChallengeErrorMessage(error) {
|
|
115
|
+
const name = typeof error?.name === 'string' ? error.name : '';
|
|
116
|
+
const message = typeof error?.message === 'string' ? error.message : String(error || '');
|
|
117
|
+
const normalized = `${name} ${message}`.toLowerCase();
|
|
118
|
+
|
|
119
|
+
if (
|
|
120
|
+
name === 'NotFoundError' ||
|
|
121
|
+
/request is not allowed by the user agent or the platform in the current context/.test(
|
|
122
|
+
normalized
|
|
123
|
+
) ||
|
|
124
|
+
/requested device not found|device not found|no camera|could not start video source/.test(
|
|
125
|
+
normalized
|
|
126
|
+
)
|
|
127
|
+
) {
|
|
128
|
+
return 'No camera available. Plug in a camera and try again.';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (
|
|
132
|
+
name === 'NotAllowedError' ||
|
|
133
|
+
name === 'PermissionDeniedError' ||
|
|
134
|
+
/permission|camera access|access denied/.test(normalized)
|
|
135
|
+
) {
|
|
136
|
+
return 'Camera access was blocked. Allow camera access and try again.';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (/positioning timeout/.test(normalized)) {
|
|
140
|
+
return 'Face positioning timed out. Reopen the check and try again.';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return 'Verification failed. Please try again.';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function showErrorStep(widget, error) {
|
|
147
|
+
const message = resolveChallengeErrorMessage(error);
|
|
148
|
+
|
|
149
|
+
if (!widget.popup) {
|
|
150
|
+
widget.openPopup?.();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
widget.showError?.(message);
|
|
154
|
+
|
|
155
|
+
for (let seconds = ERROR_STEP_SECONDS; seconds > 0; seconds--) {
|
|
156
|
+
if (!widget.popup) break;
|
|
157
|
+
widget.setErrorCountdown?.(seconds);
|
|
158
|
+
await sleep(1000);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
widget.closePopup?.();
|
|
162
|
+
widget.setState?.('retry');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function handleChallengeError(widget, emitter, error) {
|
|
166
|
+
console.log('Error during challenge:', error);
|
|
167
|
+
await showErrorStep(widget, error);
|
|
168
|
+
emitter.emit('error', error, widget.id);
|
|
169
|
+
widget.params.errorCallback?.(error);
|
|
170
|
+
}
|
|
171
|
+
|
|
113
172
|
export async function runChallenge(widget, emitter) {
|
|
114
173
|
const mode = widget.params.mode || 'serverless';
|
|
115
174
|
|
|
@@ -190,9 +249,7 @@ async function runServerless(widget, emitter) {
|
|
|
190
249
|
emitResult(widget, emitter, result);
|
|
191
250
|
} catch (error) {
|
|
192
251
|
cleanupLocal();
|
|
193
|
-
widget
|
|
194
|
-
emitter.emit('error', error, widget.id);
|
|
195
|
-
params.errorCallback?.(error);
|
|
252
|
+
await handleChallengeError(widget, emitter, error);
|
|
196
253
|
}
|
|
197
254
|
}
|
|
198
255
|
|
|
@@ -324,9 +381,7 @@ async function runServer(widget, emitter) {
|
|
|
324
381
|
cleanupVM(transport);
|
|
325
382
|
} catch (error) {
|
|
326
383
|
cleanupVM();
|
|
327
|
-
widget
|
|
328
|
-
emitter.emit('error', error, widget.id);
|
|
329
|
-
params.errorCallback?.(error);
|
|
384
|
+
await handleChallengeError(widget, emitter, error);
|
|
330
385
|
}
|
|
331
386
|
}
|
|
332
387
|
|
package/src/constants.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export declare const FACEAPI_MODEL_CDN: string;
|
|
|
8
8
|
export declare const DEFAULT_MIN_AGE: number;
|
|
9
9
|
export declare const FAIL_FLOOR: number;
|
|
10
10
|
export declare const MAX_RETRIES: number;
|
|
11
|
+
export declare const ERROR_STEP_SECONDS: number;
|
|
11
12
|
export declare const BURST_FRAMES: number;
|
|
12
13
|
export declare const BURST_INTERVAL_MS: number;
|
|
13
14
|
export declare const POSITION_CHECK_MS: number;
|
package/src/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const VERSION = '1.0.0';
|
|
2
2
|
|
|
3
|
-
export const MEDIAPIPE_CDN = 'https://cdn.jsdelivr.net/npm
|
|
3
|
+
export const MEDIAPIPE_CDN = 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.17';
|
|
4
4
|
|
|
5
5
|
export const MEDIAPIPE_WASM = `${MEDIAPIPE_CDN}/wasm`;
|
|
6
6
|
|
|
@@ -12,12 +12,13 @@ export const MEDIAPIPE_MODEL =
|
|
|
12
12
|
'face_landmarker.task';
|
|
13
13
|
|
|
14
14
|
export const FACEAPI_CDN =
|
|
15
|
-
'https://cdn.jsdelivr.net/npm/
|
|
15
|
+
'https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js';
|
|
16
16
|
|
|
17
17
|
export const FACEAPI_MODEL_CDN =
|
|
18
|
-
'https://cdn.jsdelivr.net/gh/
|
|
18
|
+
'https://cdn.jsdelivr.net/gh/justadudewhohacks/face-api.js@master/weights';
|
|
19
19
|
|
|
20
20
|
export const MAX_RETRIES = 3;
|
|
21
|
+
export const ERROR_STEP_SECONDS = 5;
|
|
21
22
|
export const BURST_FRAMES = 5;
|
|
22
23
|
export const BURST_INTERVAL_MS = 200;
|
|
23
24
|
export const POSITION_CHECK_MS = 100;
|
package/src/ui.d.ts
CHANGED
|
@@ -17,6 +17,10 @@ export declare function heroTemplate(
|
|
|
17
17
|
|
|
18
18
|
export declare function challengeTemplate(): string;
|
|
19
19
|
|
|
20
|
+
export declare function errorStepTemplate(
|
|
21
|
+
message: string
|
|
22
|
+
): string;
|
|
23
|
+
|
|
20
24
|
export declare function resultTemplate(
|
|
21
25
|
outcome: "fail" | "retry",
|
|
22
26
|
message: string
|
package/src/ui.js
CHANGED
|
@@ -893,6 +893,52 @@ export 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 @@ export function challengeTemplate() {
|
|
|
1019
1065
|
`;
|
|
1020
1066
|
}
|
|
1021
1067
|
|
|
1068
|
+
export 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
|
export function resultTemplate(outcome, message) {
|
|
1023
1080
|
const icons = {
|
|
1024
1081
|
fail: '✕',
|
package/src/widget.d.ts
CHANGED
package/src/widget.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
checkboxTemplate,
|
|
4
4
|
heroTemplate,
|
|
5
5
|
challengeTemplate,
|
|
6
|
+
errorStepTemplate,
|
|
6
7
|
resultTemplate,
|
|
7
8
|
resolveTheme,
|
|
8
9
|
watchTheme,
|
|
@@ -476,8 +477,20 @@ export class Widget {
|
|
|
476
477
|
}
|
|
477
478
|
}
|
|
478
479
|
|
|
479
|
-
showError() {
|
|
480
|
-
this.
|
|
480
|
+
showError(message) {
|
|
481
|
+
if (!this.popupElements?.body) return;
|
|
482
|
+
|
|
483
|
+
this.popupElements.body.innerHTML = errorStepTemplate(message);
|
|
484
|
+
this.popupElements.errorCountdown =
|
|
485
|
+
this.popupElements.body.querySelector('.oa-error-step-countdown');
|
|
486
|
+
this.hideActions();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
setErrorCountdown(seconds) {
|
|
490
|
+
if (!this.popupElements?.errorCountdown) return;
|
|
491
|
+
|
|
492
|
+
const unit = seconds === 1 ? 'second' : 'seconds';
|
|
493
|
+
this.popupElements.errorCountdown.textContent = `Closing in ${seconds} ${unit}…`;
|
|
481
494
|
}
|
|
482
495
|
|
|
483
496
|
clearError() {
|