@sekyro/web 0.0.0-stage → 0.1.0-rc.2
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/CHANGELOG.md +17 -0
- package/LICENSE +20 -0
- package/README.md +92 -2
- package/dist/capture-constraints-BujkZcfb.d.cts +21 -0
- package/dist/capture-constraints-BujkZcfb.d.ts +21 -0
- package/dist/capture-session-v3-BJ8ciaWl.d.ts +457 -0
- package/dist/capture-session-v3-CAoWM8iT.d.cts +457 -0
- package/dist/chunk-BPYVZUJA.js +852 -0
- package/dist/chunk-BPYVZUJA.js.map +1 -0
- package/dist/chunk-LOTAJDRE.js +1094 -0
- package/dist/chunk-LOTAJDRE.js.map +1 -0
- package/dist/chunk-MWNFSNMP.js +52 -0
- package/dist/chunk-MWNFSNMP.js.map +1 -0
- package/dist/chunk-NILNKLK6.js +50 -0
- package/dist/chunk-NILNKLK6.js.map +1 -0
- package/dist/chunk-NWF46H7K.js +359 -0
- package/dist/chunk-NWF46H7K.js.map +1 -0
- package/dist/chunk-WY6U7AIK.js +193 -0
- package/dist/chunk-WY6U7AIK.js.map +1 -0
- package/dist/chunk-YVFF4Q4G.js +327 -0
- package/dist/chunk-YVFF4Q4G.js.map +1 -0
- package/dist/client/index.cjs +357 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +39 -0
- package/dist/client/index.d.ts +39 -0
- package/dist/client/index.js +15 -0
- package/dist/client/index.js.map +1 -0
- package/dist/core/index.cjs +1605 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +87 -0
- package/dist/core/index.d.ts +87 -0
- package/dist/core/index.js +139 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/v3/detect-worker.cjs +392 -0
- package/dist/core/v3/detect-worker.cjs.map +1 -0
- package/dist/core/v3/detect-worker.d.cts +1 -0
- package/dist/core/v3/detect-worker.d.ts +1 -0
- package/dist/core/v3/detect-worker.js +13 -0
- package/dist/core/v3/detect-worker.js.map +1 -0
- package/dist/create-verification-CjLnJ0w6.d.ts +348 -0
- package/dist/create-verification-EnZ13uY4.d.cts +348 -0
- package/dist/detect-worker-CKXHlXx9.d.cts +105 -0
- package/dist/detect-worker-CKXHlXx9.d.ts +105 -0
- package/dist/detector-scanic-4C6IEW2K.js +787 -0
- package/dist/detector-scanic-4C6IEW2K.js.map +1 -0
- package/dist/hosted-client-BpvBqu9f.d.cts +408 -0
- package/dist/hosted-client-BpvBqu9f.d.ts +408 -0
- package/dist/index.cjs +2636 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/index.cjs +4166 -0
- package/dist/internal/index.cjs.map +1 -0
- package/dist/internal/index.d.cts +431 -0
- package/dist/internal/index.d.ts +431 -0
- package/dist/internal/index.js +647 -0
- package/dist/internal/index.js.map +1 -0
- package/dist/popup/index.cjs +76 -0
- package/dist/popup/index.cjs.map +1 -0
- package/dist/popup/index.d.cts +66 -0
- package/dist/popup/index.d.ts +66 -0
- package/dist/popup/index.js +11 -0
- package/dist/popup/index.js.map +1 -0
- package/dist/scanic-ml-stub-S2OZ6HEF.js +10 -0
- package/dist/scanic-ml-stub-S2OZ6HEF.js.map +1 -0
- package/package.json +118 -4
|
@@ -0,0 +1,852 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CaptureSessionV3,
|
|
3
|
+
detectIsMobile,
|
|
4
|
+
isMediaRecorderSupported
|
|
5
|
+
} from "./chunk-LOTAJDRE.js";
|
|
6
|
+
import {
|
|
7
|
+
HostedApiError,
|
|
8
|
+
HostedClient,
|
|
9
|
+
stripMimeParams
|
|
10
|
+
} from "./chunk-YVFF4Q4G.js";
|
|
11
|
+
|
|
12
|
+
// src/verification/emitter.ts
|
|
13
|
+
function createEmitter() {
|
|
14
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
15
|
+
return {
|
|
16
|
+
on(event, handler) {
|
|
17
|
+
let set = handlers.get(event);
|
|
18
|
+
if (set === void 0) {
|
|
19
|
+
set = /* @__PURE__ */ new Set();
|
|
20
|
+
handlers.set(event, set);
|
|
21
|
+
}
|
|
22
|
+
const h = handler;
|
|
23
|
+
set.add(h);
|
|
24
|
+
return () => {
|
|
25
|
+
set?.delete(h);
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
emit(event, payload) {
|
|
29
|
+
const set = handlers.get(event);
|
|
30
|
+
if (set === void 0) return;
|
|
31
|
+
for (const handler of [...set]) {
|
|
32
|
+
handler(payload);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
clear() {
|
|
36
|
+
handlers.clear();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/capture/create-liveness-capture.ts
|
|
42
|
+
function mapGuidanceHint(key) {
|
|
43
|
+
switch (key) {
|
|
44
|
+
case "fcv3.no_face":
|
|
45
|
+
case "fcv3.center_face":
|
|
46
|
+
case "fcv3.straighten":
|
|
47
|
+
return "center";
|
|
48
|
+
case "fcv3.come_closer":
|
|
49
|
+
return "closer";
|
|
50
|
+
case "fcv3.move_back":
|
|
51
|
+
return "farther";
|
|
52
|
+
case "fcv3.more_light":
|
|
53
|
+
case "fcv3.too_bright":
|
|
54
|
+
return "light";
|
|
55
|
+
case "fcv3.hold_still":
|
|
56
|
+
case "fcv3.steady_camera":
|
|
57
|
+
return "still";
|
|
58
|
+
default:
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function mapEngineState(state) {
|
|
63
|
+
switch (state) {
|
|
64
|
+
case "preflight":
|
|
65
|
+
case "acquiring":
|
|
66
|
+
return "starting";
|
|
67
|
+
case "positioning":
|
|
68
|
+
return "gating";
|
|
69
|
+
case "idle":
|
|
70
|
+
case "recording":
|
|
71
|
+
case "captured":
|
|
72
|
+
case "error":
|
|
73
|
+
return state;
|
|
74
|
+
default: {
|
|
75
|
+
const unhandled = state;
|
|
76
|
+
throw new Error(`mapEngineState: unhandled state ${unhandled}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function mapErrorKind(kind) {
|
|
81
|
+
switch (kind) {
|
|
82
|
+
case "permission":
|
|
83
|
+
case "notfound":
|
|
84
|
+
case "inuse":
|
|
85
|
+
case "insecure":
|
|
86
|
+
case "unsupported":
|
|
87
|
+
return kind;
|
|
88
|
+
case "overconstrained":
|
|
89
|
+
case "generic":
|
|
90
|
+
case "models":
|
|
91
|
+
case "cliptoolarge":
|
|
92
|
+
case "recorder":
|
|
93
|
+
return "generic";
|
|
94
|
+
default: {
|
|
95
|
+
const unhandled = kind;
|
|
96
|
+
throw new Error(`mapErrorKind: unhandled kind ${unhandled}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function createLivenessCaptureInternal(options, internals = {}) {
|
|
101
|
+
const now = internals.now ?? (() => performance.now());
|
|
102
|
+
const emitter = createEmitter();
|
|
103
|
+
const degraded = !isMediaRecorderSupported();
|
|
104
|
+
const engineOptions = {
|
|
105
|
+
video: options.video,
|
|
106
|
+
mode: degraded ? "photo" : "clip",
|
|
107
|
+
...options.facing !== void 0 ? { facingMode: options.facing } : {},
|
|
108
|
+
...options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {},
|
|
109
|
+
...internals.engineOptions ?? {}
|
|
110
|
+
};
|
|
111
|
+
const engine = internals.createEngine?.(engineOptions) ?? new CaptureSessionV3(engineOptions);
|
|
112
|
+
let state = "idle";
|
|
113
|
+
let hint = null;
|
|
114
|
+
let progress = 0;
|
|
115
|
+
let faceDetected = false;
|
|
116
|
+
let animations = "full";
|
|
117
|
+
let recordingStartedAt = null;
|
|
118
|
+
let frozenAt = null;
|
|
119
|
+
const setState = (next) => {
|
|
120
|
+
state = next;
|
|
121
|
+
emitter.emit("state", next);
|
|
122
|
+
};
|
|
123
|
+
const emitGate = () => emitter.emit("gate", { progress, hint, faceDetected });
|
|
124
|
+
const syncProfile = () => {
|
|
125
|
+
const next = engine.animations ?? "full";
|
|
126
|
+
if (next === animations) return;
|
|
127
|
+
animations = next;
|
|
128
|
+
emitter.emit("profile", { animations });
|
|
129
|
+
};
|
|
130
|
+
const unsubscribes = [
|
|
131
|
+
engine.on("statechange", ({ state: engineState }) => {
|
|
132
|
+
const next = mapEngineState(engineState);
|
|
133
|
+
if (next === "starting" && state !== "starting") {
|
|
134
|
+
hint = null;
|
|
135
|
+
progress = 0;
|
|
136
|
+
faceDetected = false;
|
|
137
|
+
recordingStartedAt = null;
|
|
138
|
+
frozenAt = null;
|
|
139
|
+
}
|
|
140
|
+
setState(next);
|
|
141
|
+
syncProfile();
|
|
142
|
+
if (next === "recording") {
|
|
143
|
+
recordingStartedAt = now();
|
|
144
|
+
hint = "hold";
|
|
145
|
+
progress = 1;
|
|
146
|
+
emitGate();
|
|
147
|
+
}
|
|
148
|
+
}),
|
|
149
|
+
engine.on("checks", (payload) => {
|
|
150
|
+
progress = payload.progress;
|
|
151
|
+
faceDetected = payload.factors.presence > 0;
|
|
152
|
+
emitGate();
|
|
153
|
+
syncProfile();
|
|
154
|
+
}),
|
|
155
|
+
engine.on("guidance", ({ key }) => {
|
|
156
|
+
hint = mapGuidanceHint(key);
|
|
157
|
+
emitGate();
|
|
158
|
+
}),
|
|
159
|
+
engine.on("frozen", ({ previewDataUrl }) => {
|
|
160
|
+
frozenAt = now();
|
|
161
|
+
emitter.emit("frozen", { previewDataUrl });
|
|
162
|
+
}),
|
|
163
|
+
engine.on("artifacts", (artifacts) => {
|
|
164
|
+
const clipMs = recordingStartedAt !== null && frozenAt !== null ? Math.max(0, Math.round(frozenAt - recordingStartedAt)) : void 0;
|
|
165
|
+
emitter.emit("captured", {
|
|
166
|
+
keyframe: artifacts.keyframe,
|
|
167
|
+
...artifacts.clip !== void 0 ? { clip: artifacts.clip } : {},
|
|
168
|
+
...artifacts.clipMimeType !== void 0 ? { clipMime: artifacts.clipMimeType } : {},
|
|
169
|
+
width: options.video.videoWidth,
|
|
170
|
+
height: options.video.videoHeight,
|
|
171
|
+
...clipMs !== void 0 ? { clipMs } : {},
|
|
172
|
+
captureChannel: detectIsMobile() ? "mobile" : "desktop",
|
|
173
|
+
...degraded ? { degraded: "NO_RECORDER" } : {}
|
|
174
|
+
});
|
|
175
|
+
}),
|
|
176
|
+
engine.on("error", ({ kind, retryable }) => {
|
|
177
|
+
emitter.emit("error", { kind: mapErrorKind(kind), retryable });
|
|
178
|
+
})
|
|
179
|
+
];
|
|
180
|
+
const handle = {
|
|
181
|
+
get state() {
|
|
182
|
+
return state;
|
|
183
|
+
},
|
|
184
|
+
on: (event, handler) => emitter.on(event, handler),
|
|
185
|
+
start: () => engine.start(),
|
|
186
|
+
retry: () => engine.retry(),
|
|
187
|
+
switchCamera: () => engine.switchCamera(),
|
|
188
|
+
destroy: () => {
|
|
189
|
+
for (const unsubscribe of unsubscribes) unsubscribe();
|
|
190
|
+
engine.destroy();
|
|
191
|
+
emitter.clear();
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
handle,
|
|
196
|
+
setFlowState: (next) => {
|
|
197
|
+
if (state !== next) setState(next);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function createLivenessCapture(options) {
|
|
202
|
+
return createLivenessCaptureInternal(options).handle;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/verification/result.ts
|
|
206
|
+
var RETRYABLE_REASONS = /* @__PURE__ */ new Set([
|
|
207
|
+
"no_face",
|
|
208
|
+
"media_unreadable",
|
|
209
|
+
"clip_too_short",
|
|
210
|
+
"no_face_source",
|
|
211
|
+
"side_missing",
|
|
212
|
+
"unreadable",
|
|
213
|
+
"registry_unavailable",
|
|
214
|
+
"otp_mismatch",
|
|
215
|
+
"otp_expired",
|
|
216
|
+
"otp_send_failed"
|
|
217
|
+
]);
|
|
218
|
+
var BIOMETRIC_CHECKS = /* @__PURE__ */ new Set(["liveness", "face_match"]);
|
|
219
|
+
function isExecutable(step) {
|
|
220
|
+
return step !== void 0 && (step.state === "pending" || step.state === "input_required");
|
|
221
|
+
}
|
|
222
|
+
function isTerminalView(view) {
|
|
223
|
+
return view.status !== "created" && view.status !== "in_progress";
|
|
224
|
+
}
|
|
225
|
+
function frontierStep(view) {
|
|
226
|
+
return view.steps.find((step) => isExecutable(step));
|
|
227
|
+
}
|
|
228
|
+
function stepOf(view, checkType) {
|
|
229
|
+
return view.steps.find((step) => step.checkType === checkType);
|
|
230
|
+
}
|
|
231
|
+
function mergeSessionView(prev, next, submitted) {
|
|
232
|
+
const byType = new Map(prev.steps.map((step) => [step.checkType, step]));
|
|
233
|
+
const steps = next.steps.map((step) => {
|
|
234
|
+
const before = byType.get(step.checkType);
|
|
235
|
+
const attemptsBefore = before?.attemptsRemaining ?? 0;
|
|
236
|
+
const merged = {
|
|
237
|
+
...before ?? {},
|
|
238
|
+
checkType: step.checkType,
|
|
239
|
+
state: step.state,
|
|
240
|
+
attemptsRemaining: step.checkType === submitted ? Math.max(0, attemptsBefore - 1) : attemptsBefore
|
|
241
|
+
};
|
|
242
|
+
if (step.reason !== void 0) merged.reason = step.reason;
|
|
243
|
+
else delete merged.reason;
|
|
244
|
+
return merged;
|
|
245
|
+
});
|
|
246
|
+
return { ...prev, status: next.status, outcome: next.outcome, steps, expiresAt: next.expiresAt };
|
|
247
|
+
}
|
|
248
|
+
function unavailable(sessionId, reason) {
|
|
249
|
+
return reason === void 0 ? { sessionId, status: "unavailable" } : { sessionId, status: "unavailable", reason };
|
|
250
|
+
}
|
|
251
|
+
function withReason(sessionId, status, reason) {
|
|
252
|
+
return reason === void 0 ? { sessionId, status } : { sessionId, status, reason };
|
|
253
|
+
}
|
|
254
|
+
function mapSessionResult(view, opts) {
|
|
255
|
+
const { sessionId } = view;
|
|
256
|
+
switch (view.status) {
|
|
257
|
+
case "completed":
|
|
258
|
+
if (view.outcome === "approved") return { sessionId, status: "approved" };
|
|
259
|
+
if (view.outcome === "review") return withReason(sessionId, "review", reasonIn(view, "review"));
|
|
260
|
+
return withReason(sessionId, "rejected", reasonIn(view, "failed"));
|
|
261
|
+
case "failed":
|
|
262
|
+
return withReason(sessionId, "rejected", reasonIn(view, "failed"));
|
|
263
|
+
case "review_pending":
|
|
264
|
+
return withReason(sessionId, "review", reasonIn(view, "review"));
|
|
265
|
+
case "expired":
|
|
266
|
+
return unavailable(sessionId);
|
|
267
|
+
case "created":
|
|
268
|
+
case "in_progress":
|
|
269
|
+
break;
|
|
270
|
+
default: {
|
|
271
|
+
const unhandled = view.status;
|
|
272
|
+
throw new Error(`mapSessionResult: unhandled status ${unhandled}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const parked = view.steps.find(
|
|
276
|
+
(step) => BIOMETRIC_CHECKS.has(step.checkType) && step.state === "input_required" && step.reason !== void 0
|
|
277
|
+
);
|
|
278
|
+
if (parked?.reason !== void 0) {
|
|
279
|
+
const canRetry = opts.autoRetry && RETRYABLE_REASONS.has(parked.reason) && parked.attemptsRemaining > 0;
|
|
280
|
+
return {
|
|
281
|
+
sessionId,
|
|
282
|
+
status: canRetry ? "retry" : "rejected",
|
|
283
|
+
reason: parked.reason,
|
|
284
|
+
attemptsRemaining: parked.attemptsRemaining
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
if (frontierStep(view) === void 0 && view.steps.some((step) => step.state === "review")) {
|
|
288
|
+
return withReason(sessionId, "review", reasonIn(view, "review"));
|
|
289
|
+
}
|
|
290
|
+
return unavailable(sessionId);
|
|
291
|
+
}
|
|
292
|
+
function reasonIn(view, state) {
|
|
293
|
+
return view.steps.find((step) => step.state === state && step.reason !== void 0)?.reason;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/verification/liveness-flow.ts
|
|
297
|
+
var CLIP_MIME_ALLOWLIST = ["video/webm", "video/mp4"];
|
|
298
|
+
var KEYFRAME_MIME = "image/jpeg";
|
|
299
|
+
var TRANSPORT_REPLAY_ATTEMPTS = 2;
|
|
300
|
+
var TRANSPORT_REPLAY_DELAY_MS = 1e3;
|
|
301
|
+
var MAX_RETRY_AFTER_MS = 1e4;
|
|
302
|
+
var RESYNC_REENTRY_MAX = 3;
|
|
303
|
+
var TRANSPORT_CODES = /* @__PURE__ */ new Set([
|
|
304
|
+
"network_error",
|
|
305
|
+
"timeout",
|
|
306
|
+
"upload_failed",
|
|
307
|
+
"unknown_error",
|
|
308
|
+
"rate_limit_exceeded"
|
|
309
|
+
]);
|
|
310
|
+
var RESYNC_CODES = /* @__PURE__ */ new Set([
|
|
311
|
+
"session_not_active",
|
|
312
|
+
"check_not_found",
|
|
313
|
+
"check_already_finalized",
|
|
314
|
+
"check_out_of_order"
|
|
315
|
+
]);
|
|
316
|
+
var AttestationError = class extends Error {
|
|
317
|
+
constructor(message) {
|
|
318
|
+
super(message);
|
|
319
|
+
this.name = "AttestationError";
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
async function runSubmission(rt, executor, hooks = {}) {
|
|
323
|
+
let key = rt.uuid();
|
|
324
|
+
let consentRecovered = false;
|
|
325
|
+
let attestationRetried = false;
|
|
326
|
+
let transportReplays = 0;
|
|
327
|
+
for (; ; ) {
|
|
328
|
+
try {
|
|
329
|
+
return await executor(rt.client, key);
|
|
330
|
+
} catch (err) {
|
|
331
|
+
if (err instanceof AttestationError) {
|
|
332
|
+
if (attestationRetried) throw err;
|
|
333
|
+
attestationRetried = true;
|
|
334
|
+
hooks.onAttestationInvalid?.();
|
|
335
|
+
key = rt.uuid();
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (!(err instanceof HostedApiError)) throw err;
|
|
339
|
+
if (err.code === "consent_required" && !consentRecovered) {
|
|
340
|
+
consentRecovered = true;
|
|
341
|
+
await rt.ensureConsent({ force: true });
|
|
342
|
+
key = rt.uuid();
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if (err.code === "attestation_invalid" && !attestationRetried) {
|
|
346
|
+
attestationRetried = true;
|
|
347
|
+
hooks.onAttestationInvalid?.();
|
|
348
|
+
key = rt.uuid();
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (TRANSPORT_CODES.has(err.code) && transportReplays < TRANSPORT_REPLAY_ATTEMPTS) {
|
|
352
|
+
transportReplays += 1;
|
|
353
|
+
const retryAfterMs = err.retryAfterSeconds !== void 0 ? err.retryAfterSeconds * 1e3 : void 0;
|
|
354
|
+
await rt.sleep(
|
|
355
|
+
retryAfterMs !== void 0 ? Math.min(retryAfterMs, MAX_RETRY_AFTER_MS) : TRANSPORT_REPLAY_DELAY_MS
|
|
356
|
+
);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
throw err;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
var LIVENESS_CHAINABLE = /* @__PURE__ */ new Set(["passed", "review", "processing", "skipped"]);
|
|
364
|
+
async function runLivenessFlow(deps) {
|
|
365
|
+
const { artifacts } = deps;
|
|
366
|
+
const clip = artifacts.clip;
|
|
367
|
+
const bareMime = stripMimeParams(artifacts.clipMime ?? "");
|
|
368
|
+
if (artifacts.degraded === "NO_RECORDER" || clip === void 0 || !CLIP_MIME_ALLOWLIST.includes(bareMime)) {
|
|
369
|
+
return { kind: "unavailable", reason: "no_recorder" };
|
|
370
|
+
}
|
|
371
|
+
const submitLiveness = buildLivenessSubmit(deps, clip, bareMime);
|
|
372
|
+
const submitFaceMatch = buildFaceMatchSubmit(deps, artifacts.keyframe);
|
|
373
|
+
let livenessSubmitted = false;
|
|
374
|
+
let faceMatchSubmitted = false;
|
|
375
|
+
let livenessChainable = true;
|
|
376
|
+
let resyncs = 0;
|
|
377
|
+
for (; ; ) {
|
|
378
|
+
if (deps.isCancelled()) return { kind: "cancelled" };
|
|
379
|
+
const view = deps.getView();
|
|
380
|
+
if (isTerminalView(view)) return { kind: "settled" };
|
|
381
|
+
const frontier = frontierStep(view)?.checkType;
|
|
382
|
+
const next = frontier === "liveness" && !livenessSubmitted ? "liveness" : frontier === "face_match" && !faceMatchSubmitted && livenessChainable ? "face_match" : null;
|
|
383
|
+
if (next === null) return { kind: "settled" };
|
|
384
|
+
try {
|
|
385
|
+
if (next === "liveness") {
|
|
386
|
+
livenessSubmitted = true;
|
|
387
|
+
const response = await submitLiveness();
|
|
388
|
+
if (deps.isCancelled()) return { kind: "cancelled" };
|
|
389
|
+
deps.applySubmit(response, "liveness");
|
|
390
|
+
const state = stepOf(deps.getView(), "liveness")?.state;
|
|
391
|
+
livenessChainable = state === void 0 || LIVENESS_CHAINABLE.has(state);
|
|
392
|
+
} else {
|
|
393
|
+
faceMatchSubmitted = true;
|
|
394
|
+
const response = await submitFaceMatch();
|
|
395
|
+
if (deps.isCancelled()) return { kind: "cancelled" };
|
|
396
|
+
deps.applySubmit(response, "face_match");
|
|
397
|
+
}
|
|
398
|
+
} catch (err) {
|
|
399
|
+
if (deps.isCancelled()) return { kind: "cancelled" };
|
|
400
|
+
const outcome = classifyFailure(err);
|
|
401
|
+
if (outcome.kind !== "resync" || resyncs >= RESYNC_REENTRY_MAX) return outcome;
|
|
402
|
+
resyncs += 1;
|
|
403
|
+
await deps.refreshView();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function buildLivenessSubmit(deps, clip, bareMime) {
|
|
408
|
+
let clipKey = null;
|
|
409
|
+
let attestation = null;
|
|
410
|
+
return () => runSubmission(
|
|
411
|
+
deps,
|
|
412
|
+
async (client, idempotencyKey) => {
|
|
413
|
+
if (clipKey === null) {
|
|
414
|
+
deps.onPhase("uploading");
|
|
415
|
+
const presigned = await client.presign("clip", bareMime, clip.size);
|
|
416
|
+
await client.uploadToSpaces(presigned.uploadUrl, clip, bareMime);
|
|
417
|
+
clipKey = (await client.completeMedia(presigned.key)).key;
|
|
418
|
+
}
|
|
419
|
+
if (attestation === null) {
|
|
420
|
+
try {
|
|
421
|
+
attestation = await deps.getAttestation();
|
|
422
|
+
} catch (err) {
|
|
423
|
+
throw new AttestationError(err instanceof Error ? err.message : String(err));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
deps.onPhase("checking");
|
|
427
|
+
return client.submitCheck(
|
|
428
|
+
"liveness",
|
|
429
|
+
{ clipKey, attestation, captureChannel: deps.artifacts.captureChannel },
|
|
430
|
+
{ idempotencyKey }
|
|
431
|
+
);
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
onAttestationInvalid: () => {
|
|
435
|
+
attestation = null;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
function buildFaceMatchSubmit(deps, keyframe) {
|
|
441
|
+
let imageKey = null;
|
|
442
|
+
return () => runSubmission(deps, async (client, idempotencyKey) => {
|
|
443
|
+
if (imageKey === null) {
|
|
444
|
+
deps.onPhase("uploading");
|
|
445
|
+
const presigned = await client.presign("image", KEYFRAME_MIME, keyframe.size);
|
|
446
|
+
await client.uploadToSpaces(presigned.uploadUrl, keyframe, KEYFRAME_MIME);
|
|
447
|
+
imageKey = (await client.completeMedia(presigned.key)).key;
|
|
448
|
+
}
|
|
449
|
+
deps.onPhase("checking");
|
|
450
|
+
return client.submitCheck("face_match", { imageKey }, { idempotencyKey });
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
function classifyFailure(err) {
|
|
454
|
+
if (err instanceof AttestationError) return { kind: "unavailable", reason: "attestation" };
|
|
455
|
+
if (err instanceof HostedApiError) {
|
|
456
|
+
if (err.code === "attestation_invalid") return { kind: "unavailable", reason: "attestation" };
|
|
457
|
+
if (RESYNC_CODES.has(err.code)) return { kind: "resync" };
|
|
458
|
+
}
|
|
459
|
+
return { kind: "unavailable", reason: "network" };
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// src/verification/turnstile.ts
|
|
463
|
+
var TURNSTILE_SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js";
|
|
464
|
+
var TURNSTILE_EXECUTE_TIMEOUT_MS = 3e4;
|
|
465
|
+
function installedApi() {
|
|
466
|
+
return window.turnstile;
|
|
467
|
+
}
|
|
468
|
+
var loaderPromise = null;
|
|
469
|
+
function resetTurnstileForTests() {
|
|
470
|
+
loaderPromise = null;
|
|
471
|
+
}
|
|
472
|
+
function loadTurnstile() {
|
|
473
|
+
loaderPromise ?? (loaderPromise = new Promise((resolve, reject) => {
|
|
474
|
+
const present = installedApi();
|
|
475
|
+
if (present) {
|
|
476
|
+
resolve(present);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const script = document.createElement("script");
|
|
480
|
+
script.src = TURNSTILE_SCRIPT_SRC;
|
|
481
|
+
script.async = true;
|
|
482
|
+
script.onload = () => {
|
|
483
|
+
const api = installedApi();
|
|
484
|
+
if (api) {
|
|
485
|
+
resolve(api);
|
|
486
|
+
} else {
|
|
487
|
+
loaderPromise = null;
|
|
488
|
+
reject(new Error("Turnstile script loaded but exposed no API"));
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
script.onerror = () => {
|
|
492
|
+
loaderPromise = null;
|
|
493
|
+
script.remove();
|
|
494
|
+
reject(new Error("Turnstile script failed to load"));
|
|
495
|
+
};
|
|
496
|
+
document.head.appendChild(script);
|
|
497
|
+
}));
|
|
498
|
+
return loaderPromise;
|
|
499
|
+
}
|
|
500
|
+
function abortError() {
|
|
501
|
+
return new Error("Turnstile challenge aborted");
|
|
502
|
+
}
|
|
503
|
+
async function executeTurnstile(siteKey, opts = {}) {
|
|
504
|
+
const timeoutMs = opts.timeoutMs ?? TURNSTILE_EXECUTE_TIMEOUT_MS;
|
|
505
|
+
const signal = opts.signal;
|
|
506
|
+
if (signal?.aborted) throw abortError();
|
|
507
|
+
const api = await loadTurnstile();
|
|
508
|
+
if (signal?.aborted) throw abortError();
|
|
509
|
+
const container = document.createElement("div");
|
|
510
|
+
document.body.appendChild(container);
|
|
511
|
+
let widgetId;
|
|
512
|
+
let timer;
|
|
513
|
+
let onAbort;
|
|
514
|
+
try {
|
|
515
|
+
return await new Promise((resolve, reject) => {
|
|
516
|
+
timer = setTimeout(
|
|
517
|
+
() => reject(new Error(`Turnstile challenge timed out (${timeoutMs} ms)`)),
|
|
518
|
+
timeoutMs
|
|
519
|
+
);
|
|
520
|
+
if (signal !== void 0) {
|
|
521
|
+
onAbort = () => reject(abortError());
|
|
522
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
523
|
+
}
|
|
524
|
+
widgetId = api.render(container, {
|
|
525
|
+
sitekey: siteKey,
|
|
526
|
+
execution: "execute",
|
|
527
|
+
appearance: "execute",
|
|
528
|
+
callback: resolve,
|
|
529
|
+
"error-callback": (code) => {
|
|
530
|
+
reject(new Error(`Turnstile challenge failed${code ? ` (${code})` : ""}`));
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
if (widgetId === void 0) {
|
|
534
|
+
reject(new Error("Turnstile render returned no widget id"));
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
api.execute(widgetId);
|
|
538
|
+
});
|
|
539
|
+
} finally {
|
|
540
|
+
clearTimeout(timer);
|
|
541
|
+
if (signal !== void 0 && onAbort !== void 0) signal.removeEventListener("abort", onAbort);
|
|
542
|
+
if (widgetId !== void 0) {
|
|
543
|
+
try {
|
|
544
|
+
api.remove(widgetId);
|
|
545
|
+
} catch {
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
container.remove();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/verification/create-verification.ts
|
|
553
|
+
var DEFAULT_API_ORIGIN = "https://api.sekyro.ai";
|
|
554
|
+
var REGISTRY_RETRY_DELAY_MS = 5e3;
|
|
555
|
+
var PROCESSING_POLL_DELAY_MS = 1500;
|
|
556
|
+
var PROCESSING_POLL_MAX = 10;
|
|
557
|
+
function defaultSleep(ms) {
|
|
558
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
559
|
+
}
|
|
560
|
+
function randomUuid() {
|
|
561
|
+
const c = globalThis.crypto;
|
|
562
|
+
if (c !== void 0 && typeof c.randomUUID === "function") return c.randomUUID();
|
|
563
|
+
const bytes = new Uint8Array(16);
|
|
564
|
+
if (c !== void 0 && typeof c.getRandomValues === "function") c.getRandomValues(bytes);
|
|
565
|
+
else for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);
|
|
566
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
567
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
568
|
+
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
569
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
570
|
+
}
|
|
571
|
+
function createVerification(options) {
|
|
572
|
+
return createVerificationInternal(options, {});
|
|
573
|
+
}
|
|
574
|
+
function createVerificationInternal(options, internals) {
|
|
575
|
+
const apiOrigin = options.apiOrigin ?? DEFAULT_API_ORIGIN;
|
|
576
|
+
const autoRetry = options.autoRetry ?? true;
|
|
577
|
+
const rearm = options.rearm ?? "auto";
|
|
578
|
+
const client = internals.client ?? new HostedClient({
|
|
579
|
+
apiOrigin,
|
|
580
|
+
sessionToken: options.clientToken,
|
|
581
|
+
...options.fetch !== void 0 ? { fetch: options.fetch } : {}
|
|
582
|
+
});
|
|
583
|
+
const sleep = internals.sleep ?? defaultSleep;
|
|
584
|
+
const uuid = internals.uuid ?? randomUuid;
|
|
585
|
+
const getTurnstileToken = internals.getTurnstileToken ?? executeTurnstile;
|
|
586
|
+
const emitter = createEmitter();
|
|
587
|
+
let destroyed = false;
|
|
588
|
+
const lifetime = new AbortController();
|
|
589
|
+
let view = null;
|
|
590
|
+
let bootstrap = null;
|
|
591
|
+
const setView = (next) => {
|
|
592
|
+
view = next;
|
|
593
|
+
emitter.emit("view", next);
|
|
594
|
+
};
|
|
595
|
+
const ensureView = () => {
|
|
596
|
+
if (view !== null) return Promise.resolve(view);
|
|
597
|
+
bootstrap ?? (bootstrap = client.getSession().then((next) => {
|
|
598
|
+
setView(next);
|
|
599
|
+
return next;
|
|
600
|
+
}).finally(() => {
|
|
601
|
+
bootstrap = null;
|
|
602
|
+
}));
|
|
603
|
+
return bootstrap;
|
|
604
|
+
};
|
|
605
|
+
const refreshView = async () => {
|
|
606
|
+
const next = await client.getSession();
|
|
607
|
+
setView(next);
|
|
608
|
+
return next;
|
|
609
|
+
};
|
|
610
|
+
const applySubmit = (response, submitted) => {
|
|
611
|
+
if (view !== null) setView(mergeSessionView(view, response, submitted));
|
|
612
|
+
};
|
|
613
|
+
let consentInFlight = null;
|
|
614
|
+
const recordConsent = async (force) => {
|
|
615
|
+
const current = await ensureView();
|
|
616
|
+
if (!force && current.consentRecorded) return;
|
|
617
|
+
await client.recordConsent(options.locale);
|
|
618
|
+
if (view !== null) setView({ ...view, consentRecorded: true });
|
|
619
|
+
};
|
|
620
|
+
const ensureConsent = (opts) => {
|
|
621
|
+
if (opts?.force === true) return recordConsent(true);
|
|
622
|
+
consentInFlight ?? (consentInFlight = recordConsent(false).finally(() => {
|
|
623
|
+
consentInFlight = null;
|
|
624
|
+
}));
|
|
625
|
+
return consentInFlight;
|
|
626
|
+
};
|
|
627
|
+
const runtime = {
|
|
628
|
+
client,
|
|
629
|
+
ensureConsent: (opts) => ensureConsent(opts),
|
|
630
|
+
sleep,
|
|
631
|
+
uuid
|
|
632
|
+
};
|
|
633
|
+
let terminalEmitted = false;
|
|
634
|
+
const emitResult = (result) => {
|
|
635
|
+
if (result.status === "approved" || result.status === "rejected" || result.status === "review") {
|
|
636
|
+
if (terminalEmitted) return;
|
|
637
|
+
terminalEmitted = true;
|
|
638
|
+
}
|
|
639
|
+
emitter.emit("result", result);
|
|
640
|
+
};
|
|
641
|
+
let registry = null;
|
|
642
|
+
let registryFailed = false;
|
|
643
|
+
let registryUnbound = false;
|
|
644
|
+
const submitRegistry = () => runSubmission(
|
|
645
|
+
runtime,
|
|
646
|
+
(c, idempotencyKey) => (
|
|
647
|
+
// `{}` input: the session is bound to a SERVER-provided NNI (Plan A Task 2).
|
|
648
|
+
c.submitCheck("population_registry", {}, { idempotencyKey })
|
|
649
|
+
)
|
|
650
|
+
);
|
|
651
|
+
const runRegistry = async () => {
|
|
652
|
+
try {
|
|
653
|
+
const current = await ensureView();
|
|
654
|
+
if (isTerminalView(current) || frontierStep(current)?.checkType !== "population_registry") {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
if (current.nniBound !== true) {
|
|
658
|
+
registryUnbound = true;
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
await ensureConsent();
|
|
662
|
+
applySubmit(await submitRegistry(), "population_registry");
|
|
663
|
+
const step = view === null ? void 0 : stepOf(view, "population_registry");
|
|
664
|
+
if (step?.state === "input_required" && step.reason === "registry_unavailable" && step.attemptsRemaining > 0) {
|
|
665
|
+
await sleep(REGISTRY_RETRY_DELAY_MS);
|
|
666
|
+
if (destroyed) return;
|
|
667
|
+
applySubmit(await submitRegistry(), "population_registry");
|
|
668
|
+
}
|
|
669
|
+
} catch {
|
|
670
|
+
registryFailed = true;
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (view !== null && isTerminalView(view)) emitResult(mapSessionResult(view, { autoRetry }));
|
|
674
|
+
};
|
|
675
|
+
const startRegistry = () => {
|
|
676
|
+
registry ?? (registry = runRegistry());
|
|
677
|
+
return registry;
|
|
678
|
+
};
|
|
679
|
+
const getAttestation = async () => {
|
|
680
|
+
const current = await ensureView();
|
|
681
|
+
const siteKey = current.turnstileSiteKey;
|
|
682
|
+
if (siteKey === null) return { sandbox: true };
|
|
683
|
+
return {
|
|
684
|
+
channel: "hosted_web",
|
|
685
|
+
siteKey,
|
|
686
|
+
turnstileToken: await getTurnstileToken(siteKey, { signal: lifetime.signal })
|
|
687
|
+
};
|
|
688
|
+
};
|
|
689
|
+
let active = null;
|
|
690
|
+
let chain = Promise.resolve();
|
|
691
|
+
let chainDepth = 0;
|
|
692
|
+
const settleProcessing = async () => {
|
|
693
|
+
let current = await ensureView();
|
|
694
|
+
for (let i = 0; i < PROCESSING_POLL_MAX && !destroyed && !isTerminalView(current) && frontierStep(current) === void 0 && current.steps.some((step) => step.state === "processing"); i += 1) {
|
|
695
|
+
await sleep(PROCESSING_POLL_DELAY_MS);
|
|
696
|
+
if (destroyed) break;
|
|
697
|
+
current = await refreshView();
|
|
698
|
+
}
|
|
699
|
+
return current;
|
|
700
|
+
};
|
|
701
|
+
const runFlow = async (bound, artifacts, capturedAttempt) => {
|
|
702
|
+
const { capture } = bound;
|
|
703
|
+
const isCancelled = () => destroyed;
|
|
704
|
+
const ownsHandle = () => bound.alive && bound.attempt === capturedAttempt;
|
|
705
|
+
const setPhase = (phase) => {
|
|
706
|
+
if (ownsHandle()) capture.setFlowState(phase);
|
|
707
|
+
};
|
|
708
|
+
const finish = (result) => {
|
|
709
|
+
if (isCancelled()) return;
|
|
710
|
+
emitResult(result);
|
|
711
|
+
if (!ownsHandle()) return;
|
|
712
|
+
if (result.status === "retry" && rearm === "auto") {
|
|
713
|
+
capture.handle.retry();
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
capture.setFlowState("done");
|
|
717
|
+
};
|
|
718
|
+
if (artifacts.degraded === "NO_RECORDER") {
|
|
719
|
+
const sessionId = view?.sessionId ?? (await ensureView().catch(() => null))?.sessionId ?? "";
|
|
720
|
+
finish(unavailable(sessionId, "no_recorder"));
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
setPhase("uploading");
|
|
724
|
+
try {
|
|
725
|
+
await startRegistry();
|
|
726
|
+
if (isCancelled()) return;
|
|
727
|
+
let current = await ensureView();
|
|
728
|
+
if (!isTerminalView(current)) {
|
|
729
|
+
if (frontierStep(current)?.checkType === "population_registry") {
|
|
730
|
+
finish(
|
|
731
|
+
unavailable(
|
|
732
|
+
current.sessionId,
|
|
733
|
+
registryUnbound ? "nni_unbound" : registryFailed ? "network" : "registry_unavailable"
|
|
734
|
+
)
|
|
735
|
+
);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
await ensureConsent();
|
|
739
|
+
if (isCancelled()) return;
|
|
740
|
+
const outcome = await runLivenessFlow({
|
|
741
|
+
...runtime,
|
|
742
|
+
artifacts,
|
|
743
|
+
getAttestation,
|
|
744
|
+
getView: () => view ?? current,
|
|
745
|
+
refreshView,
|
|
746
|
+
applySubmit,
|
|
747
|
+
onPhase: setPhase,
|
|
748
|
+
isCancelled
|
|
749
|
+
});
|
|
750
|
+
if (isCancelled()) return;
|
|
751
|
+
if (outcome.kind === "unavailable") {
|
|
752
|
+
finish(unavailable(current.sessionId, outcome.reason));
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
if (outcome.kind === "resync") await refreshView();
|
|
756
|
+
current = await settleProcessing();
|
|
757
|
+
}
|
|
758
|
+
finish(mapSessionResult(current, { autoRetry }));
|
|
759
|
+
} catch {
|
|
760
|
+
if (isCancelled()) return;
|
|
761
|
+
finish(unavailable(view?.sessionId ?? "", "network"));
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
const enqueueFlow = (bound, artifacts) => {
|
|
765
|
+
const capturedAttempt = bound.attempt;
|
|
766
|
+
if (chainDepth > 0 && bound.alive) {
|
|
767
|
+
bound.capture.setFlowState("uploading");
|
|
768
|
+
}
|
|
769
|
+
chainDepth += 1;
|
|
770
|
+
chain = chain.then(() => runFlow(bound, artifacts, capturedAttempt)).finally(() => {
|
|
771
|
+
chainDepth -= 1;
|
|
772
|
+
}).catch(() => void 0);
|
|
773
|
+
};
|
|
774
|
+
const liveness = (captureOptions) => {
|
|
775
|
+
active?.capture.handle.destroy();
|
|
776
|
+
if (active !== null) active.alive = false;
|
|
777
|
+
const capture = createLivenessCaptureInternal(captureOptions, internals.capture);
|
|
778
|
+
const bound = { capture, alive: true, attempt: 0 };
|
|
779
|
+
active = bound;
|
|
780
|
+
capture.handle.on("state", (state) => {
|
|
781
|
+
if (state === "starting") bound.attempt += 1;
|
|
782
|
+
});
|
|
783
|
+
capture.handle.on("captured", (artifacts) => {
|
|
784
|
+
void Promise.resolve().then(() => enqueueFlow(bound, artifacts));
|
|
785
|
+
});
|
|
786
|
+
const inner = capture.handle;
|
|
787
|
+
return {
|
|
788
|
+
get state() {
|
|
789
|
+
return inner.state;
|
|
790
|
+
},
|
|
791
|
+
on: (event, handler) => inner.on(event, handler),
|
|
792
|
+
start: () => inner.start(),
|
|
793
|
+
retry: () => inner.retry(),
|
|
794
|
+
switchCamera: () => inner.switchCamera(),
|
|
795
|
+
destroy: () => {
|
|
796
|
+
bound.alive = false;
|
|
797
|
+
if (active === bound) active = null;
|
|
798
|
+
inner.destroy();
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
};
|
|
802
|
+
return {
|
|
803
|
+
consent: () => ensureConsent(),
|
|
804
|
+
startRegistry,
|
|
805
|
+
liveness,
|
|
806
|
+
on: (event, handler) => emitter.on(event, handler),
|
|
807
|
+
destroy: () => {
|
|
808
|
+
destroyed = true;
|
|
809
|
+
lifetime.abort();
|
|
810
|
+
if (active !== null) {
|
|
811
|
+
active.alive = false;
|
|
812
|
+
active.capture.handle.destroy();
|
|
813
|
+
active = null;
|
|
814
|
+
}
|
|
815
|
+
emitter.clear();
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
export {
|
|
821
|
+
mapGuidanceHint,
|
|
822
|
+
mapEngineState,
|
|
823
|
+
mapErrorKind,
|
|
824
|
+
createLivenessCaptureInternal,
|
|
825
|
+
createLivenessCapture,
|
|
826
|
+
RETRYABLE_REASONS,
|
|
827
|
+
isExecutable,
|
|
828
|
+
isTerminalView,
|
|
829
|
+
frontierStep,
|
|
830
|
+
stepOf,
|
|
831
|
+
mergeSessionView,
|
|
832
|
+
mapSessionResult,
|
|
833
|
+
CLIP_MIME_ALLOWLIST,
|
|
834
|
+
TRANSPORT_REPLAY_ATTEMPTS,
|
|
835
|
+
TRANSPORT_REPLAY_DELAY_MS,
|
|
836
|
+
AttestationError,
|
|
837
|
+
runSubmission,
|
|
838
|
+
runLivenessFlow,
|
|
839
|
+
TURNSTILE_SCRIPT_SRC,
|
|
840
|
+
TURNSTILE_EXECUTE_TIMEOUT_MS,
|
|
841
|
+
resetTurnstileForTests,
|
|
842
|
+
loadTurnstile,
|
|
843
|
+
executeTurnstile,
|
|
844
|
+
DEFAULT_API_ORIGIN,
|
|
845
|
+
REGISTRY_RETRY_DELAY_MS,
|
|
846
|
+
PROCESSING_POLL_DELAY_MS,
|
|
847
|
+
PROCESSING_POLL_MAX,
|
|
848
|
+
randomUuid,
|
|
849
|
+
createVerification,
|
|
850
|
+
createVerificationInternal
|
|
851
|
+
};
|
|
852
|
+
//# sourceMappingURL=chunk-BPYVZUJA.js.map
|