@sekyro/web 0.1.0-rc.5 → 0.1.0-rc.7
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 +37 -0
- package/LICENSE +3 -3
- package/README.md +4 -4
- package/dist/chunk-B45J4KC3.js +1 -0
- package/dist/chunk-BMA36W2W.js +1 -0
- package/dist/chunk-FPXL63KZ.js +1 -0
- package/dist/chunk-IYUOYINS.js +1 -0
- package/dist/chunk-P7ECRN47.js +1 -0
- package/dist/chunk-SXZCL5NO.js +1 -0
- package/dist/chunk-YOB4ZTYN.js +1 -0
- package/dist/chunk-YQBK6OIM.js +1 -0
- package/dist/client/index.cjs +1 -357
- package/dist/client/index.d.cts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +1 -15
- package/dist/core/index.cjs +1 -1639
- package/dist/core/index.js +1 -139
- package/dist/core/v3/detect-worker.cjs +1 -392
- package/dist/core/v3/detect-worker.js +1 -13
- package/dist/{create-verification-CA-3szPI.d.cts → create-verification-C2JhOO6Y.d.cts} +100 -15
- package/dist/{create-verification-c98uAxbl.d.ts → create-verification-CGazwtAh.d.ts} +100 -15
- package/dist/detector-scanic-VQFFXKTL.js +1 -0
- package/dist/{hosted-client-DWzCRYJK.d.ts → hosted-client-kBeq-mnJ.d.cts} +188 -7
- package/dist/{hosted-client-DWzCRYJK.d.cts → hosted-client-kBeq-mnJ.d.ts} +188 -7
- package/dist/index.cjs +1 -3020
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -85
- package/dist/internal/index.cjs +1 -4429
- package/dist/internal/index.d.cts +10 -7
- package/dist/internal/index.d.ts +10 -7
- package/dist/internal/index.js +1 -657
- package/dist/popup/index.cjs +1 -76
- package/dist/popup/index.js +1 -11
- package/dist/scanic-ml-stub-KW7IZHUT.js +1 -0
- package/package.json +5 -8
- package/sbom.cdx.json +195 -0
- package/dist/chunk-MWNFSNMP.js +0 -52
- package/dist/chunk-MWNFSNMP.js.map +0 -1
- package/dist/chunk-NILNKLK6.js +0 -50
- package/dist/chunk-NILNKLK6.js.map +0 -1
- package/dist/chunk-NWF46H7K.js +0 -359
- package/dist/chunk-NWF46H7K.js.map +0 -1
- package/dist/chunk-SGDXLAXR.js +0 -1163
- package/dist/chunk-SGDXLAXR.js.map +0 -1
- package/dist/chunk-TG5IE552.js +0 -1128
- package/dist/chunk-TG5IE552.js.map +0 -1
- package/dist/chunk-WY6U7AIK.js +0 -193
- package/dist/chunk-WY6U7AIK.js.map +0 -1
- package/dist/chunk-YVFF4Q4G.js +0 -327
- package/dist/chunk-YVFF4Q4G.js.map +0 -1
- package/dist/client/index.cjs.map +0 -1
- package/dist/client/index.js.map +0 -1
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/v3/detect-worker.cjs.map +0 -1
- package/dist/core/v3/detect-worker.js.map +0 -1
- package/dist/detector-scanic-4C6IEW2K.js +0 -787
- package/dist/detector-scanic-4C6IEW2K.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal/index.cjs.map +0 -1
- package/dist/internal/index.js.map +0 -1
- package/dist/popup/index.cjs.map +0 -1
- package/dist/popup/index.js.map +0 -1
- package/dist/scanic-ml-stub-S2OZ6HEF.js +0 -10
- package/dist/scanic-ml-stub-S2OZ6HEF.js.map +0 -1
package/dist/internal/index.cjs
CHANGED
|
@@ -1,4429 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __esm = (fn, res) => function __init() {
|
|
9
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
-
};
|
|
11
|
-
var __export = (target, all) => {
|
|
12
|
-
for (var name in all)
|
|
13
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
-
};
|
|
15
|
-
var __copyProps = (to, from, except, desc) => {
|
|
16
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
-
for (let key of __getOwnPropNames(from))
|
|
18
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
-
}
|
|
21
|
-
return to;
|
|
22
|
-
};
|
|
23
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
-
mod
|
|
30
|
-
));
|
|
31
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
-
|
|
33
|
-
// src/core/doc/doc-catalog.ts
|
|
34
|
-
function aspectBand(format) {
|
|
35
|
-
return ASPECT_BANDS[format];
|
|
36
|
-
}
|
|
37
|
-
var ASPECT_BANDS, MR_DOC_CATALOG;
|
|
38
|
-
var init_doc_catalog = __esm({
|
|
39
|
-
"src/core/doc/doc-catalog.ts"() {
|
|
40
|
-
"use strict";
|
|
41
|
-
ASPECT_BANDS = {
|
|
42
|
-
"ID-1": { center: 1.586, tolerance: 0.15 },
|
|
43
|
-
"ID-3": { center: 1.42, tolerance: 0.2 }
|
|
44
|
-
};
|
|
45
|
-
MR_DOC_CATALOG = [
|
|
46
|
-
{
|
|
47
|
-
docType: "carte_identite",
|
|
48
|
-
format: "ID-1",
|
|
49
|
-
sides: ["recto", "verso"],
|
|
50
|
-
mrz: "td1",
|
|
51
|
-
// verso; carries the NNI with ICAO check digits (findings §6)
|
|
52
|
-
portraitSide: "recto",
|
|
53
|
-
aspectRatio: 1.586,
|
|
54
|
-
identifierSource: "mrz"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
docType: "passport",
|
|
58
|
-
format: "ID-3",
|
|
59
|
-
sides: ["single"],
|
|
60
|
-
mrz: "td3",
|
|
61
|
-
portraitSide: "single",
|
|
62
|
-
// data page
|
|
63
|
-
aspectRatio: 1.42,
|
|
64
|
-
identifierSource: "mrz"
|
|
65
|
-
}
|
|
66
|
-
];
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// src/core/v3/frame-quality.ts
|
|
71
|
-
function lumaAt(data, index) {
|
|
72
|
-
return 0.299 * (data[index] ?? 0) + 0.587 * (data[index + 1] ?? 0) + 0.114 * (data[index + 2] ?? 0);
|
|
73
|
-
}
|
|
74
|
-
function meanLuma(image) {
|
|
75
|
-
const { data, width, height } = image;
|
|
76
|
-
const pixels = width * height;
|
|
77
|
-
if (pixels === 0) return 0;
|
|
78
|
-
let sum = 0;
|
|
79
|
-
for (let i2 = 0; i2 < pixels; i2 += 1) sum += lumaAt(data, i2 * 4);
|
|
80
|
-
return sum / pixels;
|
|
81
|
-
}
|
|
82
|
-
function sharpness(image) {
|
|
83
|
-
const { data, width, height } = image;
|
|
84
|
-
if (width < 3 || height < 3) return 0;
|
|
85
|
-
const gray = new Float32Array(width * height);
|
|
86
|
-
for (let i2 = 0; i2 < gray.length; i2 += 1) gray[i2] = lumaAt(data, i2 * 4);
|
|
87
|
-
let sum = 0;
|
|
88
|
-
let sumSquares = 0;
|
|
89
|
-
let count = 0;
|
|
90
|
-
for (let y2 = 1; y2 < height - 1; y2 += 1) {
|
|
91
|
-
for (let x = 1; x < width - 1; x += 1) {
|
|
92
|
-
const i2 = y2 * width + x;
|
|
93
|
-
const response = (gray[i2 - width] ?? 0) + (gray[i2 - 1] ?? 0) + (gray[i2 + 1] ?? 0) + (gray[i2 + width] ?? 0) - 4 * (gray[i2] ?? 0);
|
|
94
|
-
sum += response;
|
|
95
|
-
sumSquares += response * response;
|
|
96
|
-
count += 1;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (count === 0) return 0;
|
|
100
|
-
const mean = sum / count;
|
|
101
|
-
const variance = sumSquares / count - mean * mean;
|
|
102
|
-
return Math.min(1, Math.max(0, variance) / SHARP_REF);
|
|
103
|
-
}
|
|
104
|
-
function exposure(image) {
|
|
105
|
-
const mean = meanLuma(image);
|
|
106
|
-
const [low, high] = EXPOSURE_BAND;
|
|
107
|
-
const distance = mean < low ? low - mean : mean > high ? mean - high : 0;
|
|
108
|
-
return Math.max(0, 1 - distance / EXPOSURE_FALLOFF);
|
|
109
|
-
}
|
|
110
|
-
var SHARP_REF, EXPOSURE_BAND, EXPOSURE_FALLOFF;
|
|
111
|
-
var init_frame_quality = __esm({
|
|
112
|
-
"src/core/v3/frame-quality.ts"() {
|
|
113
|
-
"use strict";
|
|
114
|
-
SHARP_REF = 180;
|
|
115
|
-
EXPOSURE_BAND = [100, 190];
|
|
116
|
-
EXPOSURE_FALLOFF = 64;
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// src/core/doc/doc-gate-v1.ts
|
|
121
|
-
function quadAreaFrac(quad, frameWidth, frameHeight) {
|
|
122
|
-
const pts = [quad.topLeft, quad.topRight, quad.bottomRight, quad.bottomLeft];
|
|
123
|
-
let doubled = 0;
|
|
124
|
-
for (let i2 = 0; i2 < 4; i2++) {
|
|
125
|
-
const a2 = pts[i2];
|
|
126
|
-
const b = pts[(i2 + 1) % 4];
|
|
127
|
-
doubled += a2.x * b.y - b.x * a2.y;
|
|
128
|
-
}
|
|
129
|
-
const frameArea = frameWidth * frameHeight;
|
|
130
|
-
return frameArea > 0 ? Math.abs(doubled / 2) / frameArea : 0;
|
|
131
|
-
}
|
|
132
|
-
function quadIou(a2, b) {
|
|
133
|
-
const boxA = bbox(a2);
|
|
134
|
-
const boxB = bbox(b);
|
|
135
|
-
const x1 = Math.max(boxA.x1, boxB.x1);
|
|
136
|
-
const y1 = Math.max(boxA.y1, boxB.y1);
|
|
137
|
-
const x2 = Math.min(boxA.x2, boxB.x2);
|
|
138
|
-
const y2 = Math.min(boxA.y2, boxB.y2);
|
|
139
|
-
const inter = Math.max(0, x2 - x1) * Math.max(0, y2 - y1);
|
|
140
|
-
const areaA = (boxA.x2 - boxA.x1) * (boxA.y2 - boxA.y1);
|
|
141
|
-
const areaB = (boxB.x2 - boxB.x1) * (boxB.y2 - boxB.y1);
|
|
142
|
-
const union = areaA + areaB - inter;
|
|
143
|
-
return union > 0 ? inter / union : 0;
|
|
144
|
-
}
|
|
145
|
-
function quadBounds(quad) {
|
|
146
|
-
const box = bbox(quad);
|
|
147
|
-
return { width: box.x2 - box.x1, height: box.y2 - box.y1 };
|
|
148
|
-
}
|
|
149
|
-
function bbox(quad) {
|
|
150
|
-
const pts = [quad.topLeft, quad.topRight, quad.bottomRight, quad.bottomLeft];
|
|
151
|
-
return {
|
|
152
|
-
x1: Math.min(...pts.map((p2) => p2.x)),
|
|
153
|
-
y1: Math.min(...pts.map((p2) => p2.y)),
|
|
154
|
-
x2: Math.max(...pts.map((p2) => p2.x)),
|
|
155
|
-
y2: Math.max(...pts.map((p2) => p2.y))
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
function quadAspect(quad) {
|
|
159
|
-
const top = Math.hypot(quad.topRight.x - quad.topLeft.x, quad.topRight.y - quad.topLeft.y);
|
|
160
|
-
const bottom = Math.hypot(
|
|
161
|
-
quad.bottomRight.x - quad.bottomLeft.x,
|
|
162
|
-
quad.bottomRight.y - quad.bottomLeft.y
|
|
163
|
-
);
|
|
164
|
-
const left = Math.hypot(quad.bottomLeft.x - quad.topLeft.x, quad.bottomLeft.y - quad.topLeft.y);
|
|
165
|
-
const right = Math.hypot(
|
|
166
|
-
quad.bottomRight.x - quad.topRight.x,
|
|
167
|
-
quad.bottomRight.y - quad.topRight.y
|
|
168
|
-
);
|
|
169
|
-
const horizontal = (top + bottom) / 2;
|
|
170
|
-
const vertical = (left + right) / 2;
|
|
171
|
-
if (horizontal <= 0 || vertical <= 0) return 0;
|
|
172
|
-
return Math.max(horizontal, vertical) / Math.min(horizontal, vertical);
|
|
173
|
-
}
|
|
174
|
-
var DOC_DETECT_WIDTH, DOC_GATE_V1_DEFAULTS, clamp01, DocGateV1;
|
|
175
|
-
var init_doc_gate_v1 = __esm({
|
|
176
|
-
"src/core/doc/doc-gate-v1.ts"() {
|
|
177
|
-
"use strict";
|
|
178
|
-
init_frame_quality();
|
|
179
|
-
init_doc_catalog();
|
|
180
|
-
DOC_DETECT_WIDTH = 480;
|
|
181
|
-
DOC_GATE_V1_DEFAULTS = Object.freeze({
|
|
182
|
-
MIN_AREA_FRAC: 0.15,
|
|
183
|
-
EDGE_CLIP_MARGIN_PX: 4,
|
|
184
|
-
STABLE_TICKS: 3,
|
|
185
|
-
IOU_STABLE_MIN: 0.85,
|
|
186
|
-
RISE_PER_S: 0.9,
|
|
187
|
-
FALL_PER_S: 0.25,
|
|
188
|
-
GUIDE_EXPOSURE: 0.6,
|
|
189
|
-
GUIDE_SHARPNESS: 0.35,
|
|
190
|
-
ID3_BAND_MIN_WIDTH_FRAC: 0.55,
|
|
191
|
-
ID3_BAND_ASPECT_MIN: 4,
|
|
192
|
-
ID3_BAND_ASPECT_MAX: 40
|
|
193
|
-
});
|
|
194
|
-
clamp01 = (value) => Math.min(1, Math.max(0, value));
|
|
195
|
-
DocGateV1 = class {
|
|
196
|
-
constructor(format, overrides = {}) {
|
|
197
|
-
this.p = 0;
|
|
198
|
-
this.lastTickAt = null;
|
|
199
|
-
this.lastQuad = null;
|
|
200
|
-
this.streak = 0;
|
|
201
|
-
this.format = format;
|
|
202
|
-
this.t = { ...DOC_GATE_V1_DEFAULTS, ...overrides };
|
|
203
|
-
}
|
|
204
|
-
reset() {
|
|
205
|
-
this.p = 0;
|
|
206
|
-
this.lastTickAt = null;
|
|
207
|
-
this.lastQuad = null;
|
|
208
|
-
this.streak = 0;
|
|
209
|
-
}
|
|
210
|
-
tick(input) {
|
|
211
|
-
const { quad, quality, timestampMs } = input;
|
|
212
|
-
const dtSeconds = this.lastTickAt === null ? 0 : Math.max(0, (timestampMs - this.lastTickAt) / 1e3);
|
|
213
|
-
this.lastTickAt = timestampMs;
|
|
214
|
-
let geometryKey = null;
|
|
215
|
-
if (quad === null) {
|
|
216
|
-
geometryKey = "dcv1.no_doc";
|
|
217
|
-
} else if (this.format === "ID-3") {
|
|
218
|
-
const box = quadBounds(quad);
|
|
219
|
-
const aspect = box.height > 0 ? box.width / box.height : 0;
|
|
220
|
-
if (box.width < this.t.ID3_BAND_MIN_WIDTH_FRAC * input.frameWidth) {
|
|
221
|
-
geometryKey = "dcv1.come_closer";
|
|
222
|
-
} else if (this.edgeClipped(quad, input.frameWidth, input.frameHeight)) {
|
|
223
|
-
geometryKey = "dcv1.fit_document";
|
|
224
|
-
} else if (aspect < this.t.ID3_BAND_ASPECT_MIN || aspect > this.t.ID3_BAND_ASPECT_MAX) {
|
|
225
|
-
geometryKey = "dcv1.wrong_shape";
|
|
226
|
-
}
|
|
227
|
-
} else if (quadAreaFrac(quad, input.frameWidth, input.frameHeight) < this.t.MIN_AREA_FRAC) {
|
|
228
|
-
geometryKey = "dcv1.come_closer";
|
|
229
|
-
} else if (this.edgeClipped(quad, input.frameWidth, input.frameHeight)) {
|
|
230
|
-
geometryKey = "dcv1.fit_document";
|
|
231
|
-
} else {
|
|
232
|
-
const band = aspectBand(this.format);
|
|
233
|
-
const aspect = quadAspect(quad);
|
|
234
|
-
if (Math.abs(aspect - band.center) > band.tolerance) geometryKey = "dcv1.wrong_shape";
|
|
235
|
-
}
|
|
236
|
-
const plausible = geometryKey === null;
|
|
237
|
-
const exposureOk = clamp01(quality.exposure) >= this.t.GUIDE_EXPOSURE;
|
|
238
|
-
const sharpnessOk = clamp01(quality.sharpness) >= this.t.GUIDE_SHARPNESS;
|
|
239
|
-
const qualityOk = exposureOk && sharpnessOk;
|
|
240
|
-
if (plausible && qualityOk && quad !== null) {
|
|
241
|
-
const held = this.lastQuad === null || quadIou(quad, this.lastQuad) >= this.t.IOU_STABLE_MIN;
|
|
242
|
-
this.streak = held ? this.streak + 1 : 1;
|
|
243
|
-
} else {
|
|
244
|
-
this.streak = 0;
|
|
245
|
-
}
|
|
246
|
-
this.lastQuad = quad;
|
|
247
|
-
const good = plausible && qualityOk;
|
|
248
|
-
this.p = clamp01(this.p + (good ? this.t.RISE_PER_S : -this.t.FALL_PER_S) * dtSeconds);
|
|
249
|
-
const ready = good && this.streak >= this.t.STABLE_TICKS;
|
|
250
|
-
let guidanceKey;
|
|
251
|
-
if (geometryKey !== null) {
|
|
252
|
-
guidanceKey = geometryKey;
|
|
253
|
-
} else if (!exposureOk) {
|
|
254
|
-
const bandMid = (EXPOSURE_BAND[0] + EXPOSURE_BAND[1]) / 2;
|
|
255
|
-
guidanceKey = quality.meanLuma < bandMid ? "dcv1.more_light" : "dcv1.too_bright";
|
|
256
|
-
} else if (!sharpnessOk) {
|
|
257
|
-
guidanceKey = "dcv1.steady";
|
|
258
|
-
} else {
|
|
259
|
-
guidanceKey = "dcv1.hold_still";
|
|
260
|
-
}
|
|
261
|
-
return { at: timestampMs, quad, plausible, p: this.p, guidanceKey, ready, stableTicks: this.streak };
|
|
262
|
-
}
|
|
263
|
-
edgeClipped(quad, frameWidth, frameHeight) {
|
|
264
|
-
const margin = this.t.EDGE_CLIP_MARGIN_PX;
|
|
265
|
-
const pts = [quad.topLeft, quad.topRight, quad.bottomRight, quad.bottomLeft];
|
|
266
|
-
return pts.some(
|
|
267
|
-
(p2) => p2.x <= margin || p2.y <= margin || p2.x >= frameWidth - margin || p2.y >= frameHeight - margin
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
// scanic-ml-stub:scanic-ml-stub
|
|
275
|
-
var scanic_ml_stub_exports = {};
|
|
276
|
-
__export(scanic_ml_stub_exports, {
|
|
277
|
-
default: () => scanic_ml_stub_default,
|
|
278
|
-
initMlDetector: () => initMlDetector
|
|
279
|
-
});
|
|
280
|
-
function initMlDetector() {
|
|
281
|
-
throw new Error("scanic ML detector is not vendored (classical pipeline only)");
|
|
282
|
-
}
|
|
283
|
-
var scanic_ml_stub_default;
|
|
284
|
-
var init_scanic_ml_stub = __esm({
|
|
285
|
-
"scanic-ml-stub:scanic-ml-stub"() {
|
|
286
|
-
"use strict";
|
|
287
|
-
scanic_ml_stub_default = {};
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// vendor/scanic/scanic.js
|
|
292
|
-
function B(A2, B2 = {}) {
|
|
293
|
-
const t2 = B2.width || Math.sqrt(A2.length), i2 = B2.height || A2.length / t2, E2 = void 0 !== B2.mode ? B2.mode : 1, e2 = void 0 !== B2.method ? B2.method : 2, o2 = B2.minArea || I, n2 = t2 + 2, a2 = i2 + 2, s2 = new Int32Array(n2 * a2);
|
|
294
|
-
for (let I2 = 0; I2 < i2; I2++) for (let g2 = 0; g2 < t2; g2++) A2[I2 * t2 + g2] > 0 && (s2[(I2 + 1) * n2 + (g2 + 1)] = 1);
|
|
295
|
-
const c2 = [];
|
|
296
|
-
let r2 = 2;
|
|
297
|
-
for (let I2 = 1; I2 <= i2; I2++) for (let A3 = 1; A3 <= t2; A3++) {
|
|
298
|
-
const B3 = s2[I2 * n2 + A3], t3 = s2[I2 * n2 + (A3 - 1)];
|
|
299
|
-
let i3 = null, o3 = false, h3 = -1;
|
|
300
|
-
if (1 === B3 && 0 === t3 ? (o3 = true, i3 = { x: A3, y: I2 }, h3 = 2) : 0 === B3 && t3 >= 1 && -1 !== t3 && 1 === t3 && (o3 = false, i3 = { x: A3 - 1, y: I2 }, h3 = 6), i3) {
|
|
301
|
-
if (0 === E2 && !o3) {
|
|
302
|
-
s2[i3.y * n2 + i3.x] = -1;
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
const A4 = r2++, I3 = C(s2, n2, a2, i3, h3, A4);
|
|
306
|
-
if (I3 && I3.length > 0) {
|
|
307
|
-
let B4 = I3;
|
|
308
|
-
2 === e2 && (B4 = Q(I3));
|
|
309
|
-
const C2 = B4.map((A5) => ({ x: A5.x - 1, y: A5.y - 1 }));
|
|
310
|
-
if (C2.length >= (2 === e2 ? 4 : g)) {
|
|
311
|
-
const I4 = { id: A4, points: C2, isOuter: o3 };
|
|
312
|
-
c2.push(I4);
|
|
313
|
-
}
|
|
314
|
-
} else 1 === s2[i3.y * n2 + i3.x] && (s2[i3.y * n2 + i3.x] = A4);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
c2.forEach((A3) => {
|
|
318
|
-
A3.area = (function(A4) {
|
|
319
|
-
let I2 = 0;
|
|
320
|
-
const g2 = A4.length;
|
|
321
|
-
if (g2 < 3) return 0;
|
|
322
|
-
for (let B3 = 0; B3 < g2; B3++) {
|
|
323
|
-
const C2 = (B3 + 1) % g2;
|
|
324
|
-
I2 += A4[B3].x * A4[C2].y, I2 -= A4[C2].x * A4[B3].y;
|
|
325
|
-
}
|
|
326
|
-
return Math.abs(I2) / 2;
|
|
327
|
-
})(A3.points), A3.boundingBox = (function(A4) {
|
|
328
|
-
if (0 === A4.length) return { minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
|
329
|
-
let I2 = A4[0].x, g2 = A4[0].y, B3 = A4[0].x, C2 = A4[0].y;
|
|
330
|
-
for (let Q2 = 1; Q2 < A4.length; Q2++) {
|
|
331
|
-
const t3 = A4[Q2];
|
|
332
|
-
I2 = Math.min(I2, t3.x), g2 = Math.min(g2, t3.y), B3 = Math.max(B3, t3.x), C2 = Math.max(C2, t3.y);
|
|
333
|
-
}
|
|
334
|
-
return { minX: I2, minY: g2, maxX: B3, maxY: C2 };
|
|
335
|
-
})(A3.points);
|
|
336
|
-
});
|
|
337
|
-
const h2 = c2.filter((A3) => A3.area >= o2);
|
|
338
|
-
return h2.sort((A3, I2) => I2.area - A3.area), B2.debug && (B2.debug.labels = s2, B2.debug.rawContours = c2, B2.debug.finalContours = h2), h2;
|
|
339
|
-
}
|
|
340
|
-
function C(A2, I2, g2, B2, C2, Q2) {
|
|
341
|
-
const t2 = [], i2 = /* @__PURE__ */ new Set();
|
|
342
|
-
let E2 = B2.x, e2 = B2.y;
|
|
343
|
-
const o2 = E2, n2 = e2;
|
|
344
|
-
let a2 = -1;
|
|
345
|
-
A2[n2 * I2 + o2] = Q2;
|
|
346
|
-
let s2 = 0;
|
|
347
|
-
const c2 = I2 * g2, r2 = [0, 1, 1, 1, 0, -1, -1, -1], h2 = [-1, -1, 0, 1, 1, 1, 0, -1];
|
|
348
|
-
for (; s2++ < c2; ) {
|
|
349
|
-
let B3;
|
|
350
|
-
if (-1 === a2) {
|
|
351
|
-
let Q3 = false;
|
|
352
|
-
for (let t3 = 0; t3 < 8; t3++) {
|
|
353
|
-
B3 = C2 + t3 & 7;
|
|
354
|
-
const i3 = E2 + r2[B3], o3 = e2 + h2[B3];
|
|
355
|
-
if (i3 >= 0 && i3 < I2 && o3 >= 0 && o3 < g2 && A2[o3 * I2 + i3] > 0) {
|
|
356
|
-
Q3 = true;
|
|
357
|
-
break;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
if (!Q3) return null;
|
|
361
|
-
} else B3 = a2 + 2 & 7;
|
|
362
|
-
let s3 = -1, c3 = -1;
|
|
363
|
-
for (let C3 = 0; C3 < 8; C3++) {
|
|
364
|
-
const Q3 = B3 + C3 & 7, t3 = E2 + r2[Q3], i3 = e2 + h2[Q3];
|
|
365
|
-
if (t3 >= 0 && t3 < I2 && i3 >= 0 && i3 < g2 && A2[i3 * I2 + t3] > 0) {
|
|
366
|
-
s3 = t3, c3 = i3, a2 = Q3 + 4 & 7;
|
|
367
|
-
break;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
if (-1 === s3) {
|
|
371
|
-
0 === t2.length && t2.push({ x: E2, y: e2 });
|
|
372
|
-
break;
|
|
373
|
-
}
|
|
374
|
-
const l2 = e2 * I2 + E2;
|
|
375
|
-
if (i2.has(l2)) return t2;
|
|
376
|
-
t2.push({ x: E2, y: e2 }), i2.add(l2);
|
|
377
|
-
const y2 = c3 * I2 + s3;
|
|
378
|
-
if (1 === A2[y2] && (A2[y2] = Q2), E2 = s3, e2 = c3, E2 === o2 && e2 === n2) break;
|
|
379
|
-
}
|
|
380
|
-
return s2 >= c2 ? null : t2;
|
|
381
|
-
}
|
|
382
|
-
function Q(A2) {
|
|
383
|
-
const I2 = A2.length;
|
|
384
|
-
if (I2 <= 2) return A2;
|
|
385
|
-
const g2 = [], B2 = A2[I2 - 1], C2 = A2[0];
|
|
386
|
-
let Q2 = B2, t2 = C2, i2 = A2[1], E2 = t2.x - Q2.x, e2 = t2.y - Q2.y, o2 = i2.x - t2.x, n2 = i2.y - t2.y;
|
|
387
|
-
E2 * n2 !== e2 * o2 && g2.push(t2);
|
|
388
|
-
for (let a2 = 1; a2 < I2 - 1; a2++) Q2 = A2[a2 - 1], t2 = A2[a2], i2 = A2[a2 + 1], E2 = t2.x - Q2.x, e2 = t2.y - Q2.y, o2 = i2.x - t2.x, n2 = i2.y - t2.y, E2 * n2 !== e2 * o2 && g2.push(t2);
|
|
389
|
-
if (Q2 = A2[I2 - 2], t2 = B2, i2 = C2, E2 = t2.x - Q2.x, e2 = t2.y - Q2.y, o2 = i2.x - t2.x, n2 = i2.y - t2.y, E2 * n2 !== e2 * o2 && g2.push(t2), 0 === g2.length) {
|
|
390
|
-
if (1 === I2) return [A2[0]];
|
|
391
|
-
if (2 === I2) return A2;
|
|
392
|
-
let g3 = 0, B3 = 1;
|
|
393
|
-
const Q3 = C2.x, t3 = C2.y;
|
|
394
|
-
for (let C3 = 1; C3 < I2; C3++) {
|
|
395
|
-
const I3 = A2[C3], i3 = I3.x - Q3, E3 = I3.y - t3, e3 = i3 * i3 + E3 * E3;
|
|
396
|
-
e3 > g3 && (g3 = e3, B3 = C3);
|
|
397
|
-
}
|
|
398
|
-
return [C2, A2[B3]];
|
|
399
|
-
}
|
|
400
|
-
return g2;
|
|
401
|
-
}
|
|
402
|
-
function t(A2, I2 = 1) {
|
|
403
|
-
if (A2.length <= 2) return A2;
|
|
404
|
-
let g2 = 0, B2 = 0;
|
|
405
|
-
const C2 = A2[0], Q2 = A2[A2.length - 1];
|
|
406
|
-
for (let t2 = 1; t2 < A2.length - 1; t2++) {
|
|
407
|
-
const I3 = i(A2[t2], C2, Q2);
|
|
408
|
-
I3 > g2 && (g2 = I3, B2 = t2);
|
|
409
|
-
}
|
|
410
|
-
if (g2 > I2) {
|
|
411
|
-
const g3 = t(A2.slice(0, B2 + 1), I2), C3 = t(A2.slice(B2), I2);
|
|
412
|
-
return g3.slice(0, -1).concat(C3);
|
|
413
|
-
}
|
|
414
|
-
return [C2, Q2];
|
|
415
|
-
}
|
|
416
|
-
function i(A2, I2, g2) {
|
|
417
|
-
const B2 = g2.x - I2.x, C2 = g2.y - I2.y, Q2 = B2 * B2 + C2 * C2;
|
|
418
|
-
if (0 === Q2) return Math.sqrt(Math.pow(A2.x - I2.x, 2) + Math.pow(A2.y - I2.y, 2));
|
|
419
|
-
const t2 = ((A2.x - I2.x) * B2 + (A2.y - I2.y) * C2) / Q2;
|
|
420
|
-
let i2, E2;
|
|
421
|
-
t2 < 0 ? (i2 = I2.x, E2 = I2.y) : t2 > 1 ? (i2 = g2.x, E2 = g2.y) : (i2 = I2.x + t2 * B2, E2 = I2.y + t2 * C2);
|
|
422
|
-
const e2 = A2.x - i2, o2 = A2.y - E2;
|
|
423
|
-
return Math.sqrt(e2 * e2 + o2 * o2);
|
|
424
|
-
}
|
|
425
|
-
function E(A2, I2 = 0.02) {
|
|
426
|
-
return t(A2, I2 * (function(A3) {
|
|
427
|
-
let I3 = 0;
|
|
428
|
-
const g2 = A3.length;
|
|
429
|
-
if (g2 < 2) return 0;
|
|
430
|
-
for (let B2 = 0; B2 < g2; B2++) {
|
|
431
|
-
const C2 = (B2 + 1) % g2, Q2 = A3[B2].x - A3[C2].x, t2 = A3[B2].y - A3[C2].y;
|
|
432
|
-
I3 += Math.sqrt(Q2 * Q2 + t2 * t2);
|
|
433
|
-
}
|
|
434
|
-
return I3;
|
|
435
|
-
})(A2));
|
|
436
|
-
}
|
|
437
|
-
function e(A2, I2) {
|
|
438
|
-
return Math.hypot(I2.x - A2.x, I2.y - A2.y);
|
|
439
|
-
}
|
|
440
|
-
function o(A2) {
|
|
441
|
-
let I2 = 0, g2 = 0;
|
|
442
|
-
for (const B2 of A2) I2 += B2.x, g2 += B2.y;
|
|
443
|
-
return { x: I2 / A2.length, y: g2 / A2.length };
|
|
444
|
-
}
|
|
445
|
-
function n(A2, I2 = {}) {
|
|
446
|
-
if (!A2 || !A2.points || A2.points.length < 4) return null;
|
|
447
|
-
const g2 = I2.epsilon || 0.02, B2 = (function(A3, I3) {
|
|
448
|
-
if (!A3 || A3.length < 4) return null;
|
|
449
|
-
const g3 = [I3, 1.3 * I3, 1.6 * I3, 2 * I3], B3 = /* @__PURE__ */ new Set();
|
|
450
|
-
for (const C3 of g3) {
|
|
451
|
-
const I4 = Math.min(0.12, C3), g4 = I4.toFixed(4);
|
|
452
|
-
if (B3.has(g4)) continue;
|
|
453
|
-
B3.add(g4);
|
|
454
|
-
const Q2 = a(E(A3, I4));
|
|
455
|
-
if (Q2 && 4 === Q2.length) return Q2;
|
|
456
|
-
}
|
|
457
|
-
return null;
|
|
458
|
-
})(A2.points, g2);
|
|
459
|
-
let C2;
|
|
460
|
-
return B2 && 4 === B2.length ? C2 = (function(A3) {
|
|
461
|
-
if (4 !== A3.length) return null;
|
|
462
|
-
const I3 = o(A3), g3 = [...A3].sort((A4, g4) => Math.atan2(A4.y - I3.y, A4.x - I3.x) - Math.atan2(g4.y - I3.y, g4.x - I3.x));
|
|
463
|
-
let B3 = 1 / 0, C3 = 0;
|
|
464
|
-
for (let t2 = 0; t2 < 4; t2++) {
|
|
465
|
-
const A4 = g3[t2].x + g3[t2].y;
|
|
466
|
-
A4 < B3 && (B3 = A4, C3 = t2);
|
|
467
|
-
}
|
|
468
|
-
const Q2 = [g3[C3], g3[(C3 + 1) % 4], g3[(C3 + 2) % 4], g3[(C3 + 3) % 4]];
|
|
469
|
-
return { topLeft: Q2[0], topRight: Q2[1], bottomRight: Q2[2], bottomLeft: Q2[3] };
|
|
470
|
-
})(B2) : (C2 = (function(A3) {
|
|
471
|
-
if (!A3 || A3.length < 4) return null;
|
|
472
|
-
const I3 = o(A3);
|
|
473
|
-
let g3 = null, B3 = null, C3 = null, Q2 = null, t2 = 0, i2 = 0, E2 = 0, n2 = 0;
|
|
474
|
-
for (const o2 of A3) {
|
|
475
|
-
const A4 = e(o2, I3);
|
|
476
|
-
o2.x < I3.x && o2.y < I3.y ? A4 > t2 && (g3 = o2, t2 = A4) : o2.x > I3.x && o2.y < I3.y ? A4 > i2 && (B3 = o2, i2 = A4) : o2.x > I3.x && o2.y > I3.y ? A4 > E2 && (C3 = o2, E2 = A4) : o2.x < I3.x && o2.y > I3.y && A4 > n2 && (Q2 = o2, n2 = A4);
|
|
477
|
-
}
|
|
478
|
-
return g3 && B3 && C3 && Q2 ? { topLeft: g3, topRight: B3, bottomRight: C3, bottomLeft: Q2 } : null;
|
|
479
|
-
})(A2.points), C2 && C2.topLeft && C2.topRight && C2.bottomRight && C2.bottomLeft || (C2 = (function(A3) {
|
|
480
|
-
if (!A3 || 0 === A3.length) return null;
|
|
481
|
-
let I3 = A3[0], g3 = A3[0], B3 = A3[0], C3 = A3[0], Q2 = I3.x + I3.y, t2 = g3.x - g3.y, i2 = B3.x + B3.y, E2 = C3.x - C3.y;
|
|
482
|
-
for (let e2 = 1; e2 < A3.length; e2++) {
|
|
483
|
-
const o2 = A3[e2], n2 = o2.x + o2.y, a2 = o2.x - o2.y;
|
|
484
|
-
n2 < Q2 && (Q2 = n2, I3 = o2), n2 > i2 && (i2 = n2, B3 = o2), a2 > t2 && (t2 = a2, g3 = o2), a2 < E2 && (E2 = a2, C3 = o2);
|
|
485
|
-
}
|
|
486
|
-
return { topLeft: I3, topRight: g3, bottomRight: B3, bottomLeft: C3 };
|
|
487
|
-
})(A2.points))), C2 && C2.topLeft && C2.topRight && C2.bottomRight && C2.bottomLeft ? C2 : null;
|
|
488
|
-
}
|
|
489
|
-
function a(A2) {
|
|
490
|
-
if (!A2 || A2.length < 4) return null;
|
|
491
|
-
if (4 === A2.length) return A2;
|
|
492
|
-
if (A2.length > 8) return null;
|
|
493
|
-
let I2 = (function(A3, I3) {
|
|
494
|
-
if (!A3 || 0 === A3.length) return [];
|
|
495
|
-
const g2 = [];
|
|
496
|
-
for (const B2 of A3) (0 === g2.length || e(B2, g2[g2.length - 1]) > I3) && g2.push(B2);
|
|
497
|
-
g2.length > 2 && e(g2[0], g2[g2.length - 1]) <= I3 && g2.pop();
|
|
498
|
-
return g2;
|
|
499
|
-
})(A2, 6);
|
|
500
|
-
return 4 === I2.length ? I2 : (I2.length, null);
|
|
501
|
-
}
|
|
502
|
-
function c() {
|
|
503
|
-
return null !== s && 0 !== s.byteLength || (s = new Uint8Array(A.memory.buffer)), s;
|
|
504
|
-
}
|
|
505
|
-
function h(A2, I2) {
|
|
506
|
-
const g2 = I2(1 * A2.length, 1) >>> 0;
|
|
507
|
-
return c().set(A2, g2 / 1), r = A2.length, g2;
|
|
508
|
-
}
|
|
509
|
-
function l(A2, I2) {
|
|
510
|
-
return A2 >>>= 0, c().subarray(A2 / 1, A2 / 1 + I2);
|
|
511
|
-
}
|
|
512
|
-
function D() {
|
|
513
|
-
return null !== y && 0 !== y.byteLength || (y = new Float32Array(A.memory.buffer)), y;
|
|
514
|
-
}
|
|
515
|
-
function w(I2, g2, B2, C2, Q2) {
|
|
516
|
-
const t2 = (function(A2, I3) {
|
|
517
|
-
const g3 = I3(4 * A2.length, 4) >>> 0;
|
|
518
|
-
return D().set(A2, g3 / 4), r = A2.length, g3;
|
|
519
|
-
})(I2, A.__wbindgen_malloc), i2 = r, E2 = A.hysteresis_thresholding(t2, i2, g2, B2, C2, Q2);
|
|
520
|
-
var e2 = l(E2[0], E2[1]).slice();
|
|
521
|
-
return A.__wbindgen_free(E2[0], 1 * E2[1], 1), e2;
|
|
522
|
-
}
|
|
523
|
-
function f(I2, g2) {
|
|
524
|
-
const B2 = g2(2 * I2.length, 2) >>> 0;
|
|
525
|
-
return (null !== d && 0 !== d.byteLength || (d = new Uint16Array(A.memory.buffer)), d).set(I2, B2 / 2), r = I2.length, B2;
|
|
526
|
-
}
|
|
527
|
-
function u(I2, g2, B2, C2, Q2) {
|
|
528
|
-
const t2 = f(I2, A.__wbindgen_malloc), i2 = r, E2 = f(g2, A.__wbindgen_malloc), e2 = r, o2 = A.non_maximum_suppression(t2, i2, E2, e2, B2, C2, Q2);
|
|
529
|
-
var n2, a2, s2 = (n2 = o2[0], a2 = o2[1], n2 >>>= 0, D().subarray(n2 / 4, n2 / 4 + a2)).slice();
|
|
530
|
-
return A.__wbindgen_free(o2[0], 4 * o2[1], 4), s2;
|
|
531
|
-
}
|
|
532
|
-
async function m(I2) {
|
|
533
|
-
if (void 0 !== A) return A;
|
|
534
|
-
if (void 0 !== I2 && Object.getPrototypeOf(I2) === Object.prototype && ({ module_or_path: I2 } = I2), void 0 === I2) throw new Error("scanic: WASM module bytes must be passed to init().");
|
|
535
|
-
const g2 = (function() {
|
|
536
|
-
const I3 = { wbg: {} };
|
|
537
|
-
return I3.wbg.__wbindgen_init_externref_table = function() {
|
|
538
|
-
const I4 = A.__wbindgen_export_0, g3 = I4.grow(4);
|
|
539
|
-
I4.set(0, void 0), I4.set(g3 + 0, void 0), I4.set(g3 + 1, null), I4.set(g3 + 2, true), I4.set(g3 + 3, false);
|
|
540
|
-
}, I3;
|
|
541
|
-
})();
|
|
542
|
-
("string" == typeof I2 || "function" == typeof Request && I2 instanceof Request || "function" == typeof URL && I2 instanceof URL) && (I2 = fetch(I2));
|
|
543
|
-
const { instance: B2, module: C2 } = await (async function(A2, I3) {
|
|
544
|
-
if ("function" == typeof Response && A2 instanceof Response) {
|
|
545
|
-
if ("function" == typeof WebAssembly.instantiateStreaming) try {
|
|
546
|
-
return await WebAssembly.instantiateStreaming(A2, I3);
|
|
547
|
-
} catch (g3) {
|
|
548
|
-
if ("application/wasm" == A2.headers.get("Content-Type")) throw g3;
|
|
549
|
-
}
|
|
550
|
-
const B3 = await A2.arrayBuffer();
|
|
551
|
-
return await WebAssembly.instantiate(B3, I3);
|
|
552
|
-
}
|
|
553
|
-
{
|
|
554
|
-
const g3 = await WebAssembly.instantiate(A2, I3);
|
|
555
|
-
return g3 instanceof WebAssembly.Instance ? { instance: g3, module: A2 } : g3;
|
|
556
|
-
}
|
|
557
|
-
})(await I2, g2);
|
|
558
|
-
return (function(I3, g3) {
|
|
559
|
-
return A = I3.exports, m.__wbindgen_wasm_module = g3, y = null, d = null, s = null, A.__wbindgen_start(), A;
|
|
560
|
-
})(B2, C2);
|
|
561
|
-
}
|
|
562
|
-
function F(A2) {
|
|
563
|
-
if ("function" == typeof atob) {
|
|
564
|
-
const I2 = atob(A2), g2 = new Uint8Array(I2.length);
|
|
565
|
-
for (let A3 = 0; A3 < I2.length; A3++) g2[A3] = I2.charCodeAt(A3);
|
|
566
|
-
return g2;
|
|
567
|
-
}
|
|
568
|
-
return Uint8Array.from(Buffer.from(A2, "base64"));
|
|
569
|
-
}
|
|
570
|
-
async function J() {
|
|
571
|
-
if (!(function() {
|
|
572
|
-
if (null !== p) return p;
|
|
573
|
-
let A2 = false;
|
|
574
|
-
if ("object" == typeof WebAssembly && "function" == typeof WebAssembly.validate) {
|
|
575
|
-
const I2 = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 10, 1, 8, 0, 65, 0, 253, 15, 253, 98, 11]), g2 = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 111, 3, 2, 1, 0, 10, 6, 1, 4, 0, 208, 111, 11]);
|
|
576
|
-
try {
|
|
577
|
-
A2 = WebAssembly.validate(I2) && WebAssembly.validate(g2);
|
|
578
|
-
} catch {
|
|
579
|
-
A2 = false;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
return p = A2, A2;
|
|
583
|
-
})()) throw new Error("scanic: WebAssembly features required by the WASM module (reference-types, SIMD) are unavailable in this engine; using the JavaScript fallback.");
|
|
584
|
-
return await m({ module_or_path: F("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JAAgAiABIAJqIgFLBEBBAEEAEC0ACyADQQRqIAAoAgAiAiAAKAIEQQggASACQQF0IgIgASACSxsiASABQQhNGyIBQQFBARARIAMoAgRBAUYEQCADKAIIIAMoAgwQLQALIAMoAgghAiAAIAE2AgAgACACNgIEIANBEGokAAt5AQF/IwBBIGsiAiQAAn8gACgCAEGAgICAeEcEQCABIAAoAgQgACgCCBAwDAELIAJBEGogACgCDCgCACIAQQhqKQIANwMAIAJBGGogAEEQaikCADcDACACIAApAgA3AwggASgCACABKAIEIAJBCGoQBwsgAkEgaiQAC2kBA38jAEEQayIBJAAgAUEEaiAAKAIAIgIgACgCBEEEIAJBAXQiAiACQQRNGyICQQRBBBARIAEoAgRBAUYEQCABKAIIIAEoAgwQLQALIAEoAgghAyAAIAI2AgAgACADNgIEIAFBEGokAAtpAQN/IwBBEGsiASQAIAFBBGogACgCACICIAAoAgRBBCACQQF0IgIgAkEETRsiAkEEQQgQESABKAIEQQFGBEAgASgCCCABKAIMEC0ACyABKAIIIQMgACACNgIAIAAgAzYCBCABQRBqJAALaQEDfyMAQRBrIgEkACABQQRqIAAoAgAiAiAAKAIEQQQgAkEBdCICIAJBBE0bIgJBAkECEBEgASgCBEEBRgRAIAEoAgggASgCDBAtAAsgASgCCCEDIAAgAjYCACAAIAM2AgQgAUEQaiQACxIAIwBBMGsiACQAIABBMGokAAtoAgF/AX4jAEEwayIDJAAgAyABNgIEIAMgADYCACADQQI2AgwgA0Hkl8AANgIIIANCAjcCFCADQoCAgIAwIgQgA62ENwMoIAMgBCADQQRqrYQ3AyAgAyADQSBqNgIQIANBCGogAhAmAAtHAQF/IAAoAgAgACgCCCIDayACSQRAIAAgAyACEBIgACgCCCEDCyACBEAgACgCBCADaiABIAL8CgAACyAAIAIgA2o2AghBAAtEAQJ/IAEoAgQhAiABKAIAIQNBCEEEEDUiAUUEQEEEQQgQOQALIAEgAjYCBCABIAM2AgAgAEHcksAANgIEIAAgATYCAAvGAgACQCAAIAJNBEAgACABTSABIAJLcg0BIwBBMGsiAiQAIAIgATYCBCACIAA2AgAgAkECNgIMIAJB2JbAADYCCCACQgI3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IAAAABwAEAAMAAAAEgAAABcAAAAcABAADAAAABcAAAAZAAAAHAAQAAwAAAAUAAAAGQAAABwAEAAMAAAAUQAAAB4AAAAcABAADAAAAFIAAAAeAAAATGF6eSBpbnN0YW5jZSBoYXMgcHJldmlvdXNseSBiZWVuIHBvaXNvbmVkAAA8CBAAKgAAAEgBEABaAAAACAMAABkAAAByZWVudHJhbnQgaW5pdAAAgAgQAA4AAABIARAAWgAAAHoCAAANAAAAfP2LMlfmV/kC30S/40jnr21dy9YsUOtjeEGmV3Ebi7ltZW1vcnkgYWxsb2NhdGlvbiBvZiAgYnl0ZXMgZmFpbGVkAADICBAAFQAAAN0IEAANAAAALwEQABgAAABkAQAACQAAAAQAAAAMAAAABAAAAAUAAAAGAAAABwAAAAAAAAAIAAAABAAAAAgAAAAJAAAACgAAAAsAAAAMAAAAEAAAAAQAAAANAAAADgAAAA8AAAAQAAAAAAAAAAgAAAAEAAAAEQAAAGFzc2VydGlvbiBmYWlsZWQ6IHBzaXplID49IHNpemUgKyBtaW5fb3ZlcmhlYWQAAAQBEAAqAAAAsQQAAAkAAABhc3NlcnRpb24gZmFpbGVkOiBwc2l6ZSA8PSBzaXplICsgbWF4X292ZXJoZWFkAAAEARAAKgAAALcEAAANAAAABAAAAAwAAAAEAAAAEgAAAGNhcGFjaXR5IG92ZXJmbG93AAAA/AkQABEAAADjABAAIAAAABwAAAAFAAAAMDAwMTAyMDMwNDA1MDYwNzA4MDkxMDExMTIxMzE0MTUxNjE3MTgxOTIwMjEyMjIzMjQyNTI2MjcyODI5MzAzMTMyMzMzNDM1MzYzNzM4Mzk0MDQxNDI0MzQ0NDU0NjQ3NDg0OTUwNTE1MjUzNTQ1NTU2NTc1ODU5NjA2MTYyNjM2NDY1NjY2NzY4Njk3MDcxNzI3Mzc0NzU3Njc3Nzg3OTgwODE4MjgzODQ4NTg2ODc4ODg5OTA5MTkyOTM5NDk1OTY5Nzk4OTlyYW5nZSBlbmQgaW5kZXggIG91dCBvZiByYW5nZSBmb3Igc2xpY2Ugb2YgbGVuZ3RoIAAA8AoQABAAAAAACxAAIgAAAHNsaWNlIGluZGV4IHN0YXJ0cyBhdCAgYnV0IGVuZHMgYXQgADQLEAAWAAAASgsQAA0AAAByYW5nZSBzdGFydCBpbmRleCAAAGgLEAASAAAAAAsQACIAAABhdHRlbXB0IHRvIGRpdmlkZSBieSB6ZXJvAAAAjAsQABkAAABpbmRleCBvdXQgb2YgYm91bmRzOiB0aGUgbGVuIGlzICBidXQgdGhlIGluZGV4IGlzIAAAsAsQACAAAADQCxAAEgAAAAAAAD8AAAC/AEGUmMAACwEBAHwJcHJvZHVjZXJzAghsYW5ndWFnZQEEUnVzdAAMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjkyLjAgKGRlZDVjMDZjZiAyMDI1LTEyLTA4KQZ3YWxydXMGMC4yMy4zDHdhc20tYmluZGdlbhMwLjIuMTAwICgyNDA1ZWMyYjQpAHQPdGFyZ2V0X2ZlYXR1cmVzBysPbXV0YWJsZS1nbG9iYWxzKxNub250cmFwcGluZy1mcHRvaW50KwdzaW1kMTI4KwtidWxrLW1lbW9yeSsIc2lnbi1leHQrD3JlZmVyZW5jZS10eXBlcysKbXVsdGl2YWx1ZQ==") });
|
|
585
|
-
}
|
|
586
|
-
function G() {
|
|
587
|
-
return N || (N = J().catch((A2) => {
|
|
588
|
-
throw N = null, A2;
|
|
589
|
-
})), N;
|
|
590
|
-
}
|
|
591
|
-
function k(A2, I2, g2, B2 = 5, C2 = 0) {
|
|
592
|
-
0 === C2 && (C2 = 0.3 * (0.5 * (B2 - 1) - 1) + 0.8);
|
|
593
|
-
const Q2 = Math.floor(B2 / 2), t2 = (function(A3, I3) {
|
|
594
|
-
const g3 = new Float32Array(A3), B3 = Math.floor(A3 / 2);
|
|
595
|
-
let C3 = 0;
|
|
596
|
-
for (let Q3 = 0; Q3 < A3; Q3++) {
|
|
597
|
-
const A4 = Q3 - B3;
|
|
598
|
-
g3[Q3] = Math.exp(-A4 * A4 / (2 * I3 * I3)), C3 += g3[Q3];
|
|
599
|
-
}
|
|
600
|
-
for (let Q3 = 0; Q3 < A3; Q3++) g3[Q3] /= C3;
|
|
601
|
-
return g3;
|
|
602
|
-
})(B2, C2), i2 = new Uint8ClampedArray(I2 * g2), E2 = new Uint8ClampedArray(I2 * g2);
|
|
603
|
-
for (let e2 = 0; e2 < g2; e2++) {
|
|
604
|
-
const g3 = e2 * I2;
|
|
605
|
-
for (let B3 = 0; B3 < I2; B3++) {
|
|
606
|
-
let C3 = 0;
|
|
607
|
-
for (let i3 = -Q2; i3 <= Q2; i3++) {
|
|
608
|
-
C3 += A2[g3 + Math.min(I2 - 1, Math.max(0, B3 + i3))] * t2[Q2 + i3];
|
|
609
|
-
}
|
|
610
|
-
i2[g3 + B3] = C3;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
for (let e2 = 0; e2 < I2; e2++) for (let A3 = 0; A3 < g2; A3++) {
|
|
614
|
-
let B3 = 0;
|
|
615
|
-
for (let C3 = -Q2; C3 <= Q2; C3++) {
|
|
616
|
-
B3 += i2[Math.min(g2 - 1, Math.max(0, A3 + C3)) * I2 + e2] * t2[Q2 + C3];
|
|
617
|
-
}
|
|
618
|
-
E2[A3 * I2 + e2] = Math.round(B3);
|
|
619
|
-
}
|
|
620
|
-
return E2;
|
|
621
|
-
}
|
|
622
|
-
function S(A2, I2, g2, B2, C2) {
|
|
623
|
-
const Q2 = new Uint8Array(I2 * g2), t2 = [];
|
|
624
|
-
for (let e2 = 1; e2 < g2 - 1; e2++) for (let g3 = 1; g3 < I2 - 1; g3++) {
|
|
625
|
-
const i3 = e2 * I2 + g3, E3 = A2[i3];
|
|
626
|
-
E3 >= C2 ? (Q2[i3] = 2, t2.push({ x: g3, y: e2 })) : Q2[i3] = E3 >= B2 ? 0 : 1;
|
|
627
|
-
}
|
|
628
|
-
for (let e2 = 0; e2 < I2; e2++) Q2[e2] = 1, Q2[(g2 - 1) * I2 + e2] = 1;
|
|
629
|
-
for (let e2 = 1; e2 < g2 - 1; e2++) Q2[e2 * I2] = 1, Q2[e2 * I2 + I2 - 1] = 1;
|
|
630
|
-
const i2 = [-1, 0, 1, -1, 1, -1, 0, 1], E2 = [-1, -1, -1, 0, 0, 1, 1, 1];
|
|
631
|
-
for (; t2.length > 0; ) {
|
|
632
|
-
const { x: A3, y: g3 } = t2.pop();
|
|
633
|
-
for (let B3 = 0; B3 < 8; B3++) {
|
|
634
|
-
const C3 = A3 + i2[B3], e2 = g3 + E2[B3], o2 = e2 * I2 + C3;
|
|
635
|
-
0 === Q2[o2] && (Q2[o2] = 2, t2.push({ x: C3, y: e2 }));
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
return Q2;
|
|
639
|
-
}
|
|
640
|
-
async function M(I2, g2 = {}) {
|
|
641
|
-
const B2 = !(!g2.debug || g2.debug._timingsOnly), C2 = [], Q2 = performance.now();
|
|
642
|
-
let t2, i2, E2;
|
|
643
|
-
if (g2.skipGrayscale || false) t2 = g2.width, i2 = g2.height, E2 = I2, B2 && (g2.debug.grayscale = E2);
|
|
644
|
-
else {
|
|
645
|
-
t2 = I2.width, i2 = I2.height;
|
|
646
|
-
let A2 = performance.now();
|
|
647
|
-
E2 = (function(A3) {
|
|
648
|
-
const { width: I3, height: g3, data: B3 } = A3, C3 = new Uint8ClampedArray(I3 * g3);
|
|
649
|
-
for (let Q4 = 0, t3 = 0; Q4 < B3.length; Q4 += 4, t3++) C3[t3] = 54 * B3[Q4] + 183 * B3[Q4 + 1] + 19 * B3[Q4 + 2] >> 8;
|
|
650
|
-
return C3;
|
|
651
|
-
})(I2);
|
|
652
|
-
let Q3 = performance.now();
|
|
653
|
-
C2.push({ step: "Grayscale", ms: (Q3 - A2).toFixed(2) }), B2 && (g2.debug.grayscale = E2);
|
|
654
|
-
}
|
|
655
|
-
let e2 = void 0 !== g2.lowThreshold ? g2.lowThreshold : null, o2 = void 0 !== g2.highThreshold ? g2.highThreshold : null;
|
|
656
|
-
if (null === e2 || null === o2) {
|
|
657
|
-
const A2 = 1024, I3 = new Uint32Array(A2);
|
|
658
|
-
let g3 = 0;
|
|
659
|
-
for (let e3 = 1; e3 < i2 - 1; e3++) for (let B4 = 1; B4 < t2 - 1; B4++) {
|
|
660
|
-
const C4 = e3 * t2 + B4, Q4 = -E2[C4 - t2 - 1] + E2[C4 - t2 + 1] - 2 * E2[C4 - 1] + 2 * E2[C4 + 1] - E2[C4 + t2 - 1] + E2[C4 + t2 + 1], i3 = -E2[C4 - t2 - 1] - 2 * E2[C4 - t2] - E2[C4 - t2 + 1] + E2[C4 + t2 - 1] + 2 * E2[C4 + t2] + E2[C4 + t2 + 1];
|
|
661
|
-
let o3 = Math.abs(Q4) + Math.abs(i3);
|
|
662
|
-
o3 >= A2 && (o3 = A2 - 1), o3 > 0 && (I3[o3]++, g3++);
|
|
663
|
-
}
|
|
664
|
-
const B3 = Math.round(0.7 * g3);
|
|
665
|
-
let C3 = 0, Q3 = 75;
|
|
666
|
-
for (let t3 = 1; t3 < A2; t3++) if (C3 += I3[t3], C3 >= B3) {
|
|
667
|
-
Q3 = t3;
|
|
668
|
-
break;
|
|
669
|
-
}
|
|
670
|
-
null === e2 && (e2 = Math.max(5, Math.round(0.5 * Q3))), null === o2 && (o2 = Math.min(500, Math.round(1.5 * Q3)));
|
|
671
|
-
}
|
|
672
|
-
const n2 = g2.kernelSize || 5, a2 = g2.sigma || 0, s2 = void 0 !== g2.L2gradient && g2.L2gradient, c2 = void 0 === g2.applyDilation || g2.applyDilation, y2 = g2.dilationKernelSize || 5, D2 = void 0 !== g2.useWasmHysteresis && g2.useWasmHysteresis, d2 = void 0 !== g2.useWasmFullCanny && g2.useWasmFullCanny;
|
|
673
|
-
if (e2 >= o2 && ([e2, o2] = [o2, e2]), d2) try {
|
|
674
|
-
await G();
|
|
675
|
-
const I3 = performance.now(), D3 = new Uint8ClampedArray((function(I4, g3, B3, C3, Q3, t3, i3, E3, e3, o3) {
|
|
676
|
-
const n3 = h(I4, A.__wbindgen_malloc), a3 = r, s3 = A.canny_edge_detector_full(n3, a3, g3, B3, C3, Q3, t3, i3, E3, e3, o3);
|
|
677
|
-
var c3 = l(s3[0], s3[1]).slice();
|
|
678
|
-
return A.__wbindgen_free(s3[0], 1 * s3[1], 1), c3;
|
|
679
|
-
})(E2, t2, i2, e2, o2, n2, a2, s2, c2, y2)), w2 = (performance.now() - I3).toFixed(2);
|
|
680
|
-
C2.push({ step: "Edge Processing (WASM)", ms: w2 }), B2 && (g2.debug.finalEdges = D3), g2.debug ? g2.debug.timings = C2 : g2.debug = { timings: C2 };
|
|
681
|
-
const d3 = performance.now();
|
|
682
|
-
return C2.unshift({ step: "Edge Detection Total", ms: (d3 - Q2).toFixed(2) }), D3;
|
|
683
|
-
} catch (Y) {
|
|
684
|
-
if (!R) {
|
|
685
|
-
R = true;
|
|
686
|
-
Y instanceof Error ? Y.message : String(Y);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
let f2, m2, N2, p2, F2, J2;
|
|
690
|
-
f2 = performance.now();
|
|
691
|
-
try {
|
|
692
|
-
await G(), N2 = (function(I3, g3, B3, C3, Q3) {
|
|
693
|
-
const t3 = h(I3, A.__wbindgen_malloc), i3 = r, E3 = A.blur(t3, i3, g3, B3, C3, Q3);
|
|
694
|
-
var e3 = l(E3[0], E3[1]).slice();
|
|
695
|
-
return A.__wbindgen_free(E3[0], 1 * E3[1], 1), e3;
|
|
696
|
-
})(E2, t2, i2, n2, a2);
|
|
697
|
-
} catch {
|
|
698
|
-
N2 = k(E2, t2, i2, n2, a2);
|
|
699
|
-
}
|
|
700
|
-
m2 = performance.now(), C2.push({ step: "Gaussian Blur", ms: (m2 - f2).toFixed(2) }), B2 && (g2.debug.blurred = N2), f2 = performance.now();
|
|
701
|
-
{
|
|
702
|
-
const A2 = (function(A3, I3, g3) {
|
|
703
|
-
const B3 = new Int16Array(I3 * g3), C3 = new Int16Array(I3 * g3);
|
|
704
|
-
for (let Q3 = 1; Q3 < g3 - 1; Q3++) {
|
|
705
|
-
const g4 = Q3 * I3, t3 = (Q3 - 1) * I3, i3 = (Q3 + 1) * I3;
|
|
706
|
-
for (let Q4 = 1; Q4 < I3 - 1; Q4++) {
|
|
707
|
-
const I4 = g4 + Q4, E3 = A3[t3 + Q4 - 1], e3 = A3[t3 + Q4], o3 = A3[t3 + Q4 + 1], n3 = A3[g4 + Q4 - 1], a3 = A3[g4 + Q4 + 1], s3 = A3[i3 + Q4 - 1], c3 = A3[i3 + Q4], r2 = A3[i3 + Q4 + 1], h2 = o3 - E3 + 2 * (a3 - n3) + (r2 - s3), l2 = s3 + 2 * c3 + r2 - (E3 + 2 * e3 + o3);
|
|
708
|
-
B3[I4] = h2, C3[I4] = l2;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
return { dx: B3, dy: C3 };
|
|
712
|
-
})(N2, t2, i2);
|
|
713
|
-
p2 = A2.dx, F2 = A2.dy;
|
|
714
|
-
}
|
|
715
|
-
m2 = performance.now(), C2.push({ step: "Gradients", ms: (m2 - f2).toFixed(2) }), f2 = performance.now();
|
|
716
|
-
try {
|
|
717
|
-
await G(), J2 = await u(p2, F2, t2, i2, s2);
|
|
718
|
-
} catch {
|
|
719
|
-
J2 = (function(A2, I3, g3, B3, C3) {
|
|
720
|
-
const Q3 = new Float32Array(g3 * B3), t3 = new Float32Array(g3 * B3);
|
|
721
|
-
for (let i3 = 0; i3 < A2.length; i3++) {
|
|
722
|
-
const g4 = A2[i3], B4 = I3[i3];
|
|
723
|
-
Q3[i3] = C3 ? Math.sqrt(g4 * g4 + B4 * B4) : Math.abs(g4) + Math.abs(B4);
|
|
724
|
-
}
|
|
725
|
-
for (let i3 = 1; i3 < B3 - 1; i3++) for (let B4 = 1; B4 < g3 - 1; B4++) {
|
|
726
|
-
const C4 = i3 * g3 + B4, E3 = Q3[C4];
|
|
727
|
-
if (0 === E3) {
|
|
728
|
-
t3[C4] = 0;
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
731
|
-
const e3 = A2[C4], o3 = I3[C4];
|
|
732
|
-
let n3 = 0, a3 = 0;
|
|
733
|
-
const s3 = Math.abs(e3), c3 = Math.abs(o3);
|
|
734
|
-
if (c3 > 2.4142 * s3) n3 = Q3[C4 - g3], a3 = Q3[C4 + g3];
|
|
735
|
-
else if (s3 > 2.4142 * c3) n3 = Q3[C4 - 1], a3 = Q3[C4 + 1];
|
|
736
|
-
else {
|
|
737
|
-
const A3 = (e3 ^ o3) < 0 ? -1 : 1;
|
|
738
|
-
o3 > 0 ? (n3 = Q3[(i3 - 1) * g3 + (B4 - A3)], a3 = Q3[(i3 + 1) * g3 + (B4 + A3)]) : (n3 = Q3[(i3 + 1) * g3 + (B4 - A3)], a3 = Q3[(i3 - 1) * g3 + (B4 + A3)]), e3 > 0 && o3 > 0 || e3 < 0 && o3 < 0 ? (n3 = Q3[(i3 - 1) * g3 + (B4 + 1)], a3 = Q3[(i3 + 1) * g3 + (B4 - 1)]) : (n3 = Q3[(i3 - 1) * g3 + (B4 - 1)], a3 = Q3[(i3 + 1) * g3 + (B4 + 1)]);
|
|
739
|
-
}
|
|
740
|
-
t3[C4] = E3 >= n3 && E3 >= a3 ? E3 : 0;
|
|
741
|
-
}
|
|
742
|
-
return t3;
|
|
743
|
-
})(p2, F2, t2, i2, s2);
|
|
744
|
-
}
|
|
745
|
-
m2 = performance.now(), C2.push({ step: "Non-Max Suppression", ms: (m2 - f2).toFixed(2) }), f2 = performance.now();
|
|
746
|
-
const M2 = s2 ? e2 * e2 : e2, b = s2 ? o2 * o2 : o2;
|
|
747
|
-
let L;
|
|
748
|
-
if (D2) try {
|
|
749
|
-
await G(), L = w(J2, t2, i2, M2, b);
|
|
750
|
-
} catch (q) {
|
|
751
|
-
L = S(J2, t2, i2, M2, b);
|
|
752
|
-
}
|
|
753
|
-
else L = S(J2, t2, i2, M2, b);
|
|
754
|
-
m2 = performance.now(), C2.push({ step: "Hysteresis", ms: (m2 - f2).toFixed(2) }), f2 = performance.now();
|
|
755
|
-
const x = new Uint8ClampedArray(t2 * i2);
|
|
756
|
-
for (let A2 = 0; A2 < L.length; A2++) x[A2] = 2 === L[A2] ? 255 : 0;
|
|
757
|
-
m2 = performance.now(), C2.push({ step: "Binary Image", ms: (m2 - f2).toFixed(2) }), f2 = performance.now();
|
|
758
|
-
let U = x;
|
|
759
|
-
if (c2) try {
|
|
760
|
-
await G(), U = (function(I3, g3, B3, C3) {
|
|
761
|
-
const Q3 = h(I3, A.__wbindgen_malloc), t3 = r, i3 = A.dilate(Q3, t3, g3, B3, C3);
|
|
762
|
-
var E3 = l(i3[0], i3[1]).slice();
|
|
763
|
-
return A.__wbindgen_free(i3[0], 1 * i3[1], 1), E3;
|
|
764
|
-
})(x, t2, i2, y2);
|
|
765
|
-
} catch {
|
|
766
|
-
U = (function(A2, I3, g3, B3 = 5) {
|
|
767
|
-
const C3 = Math.floor(B3 / 2), Q3 = new Uint8ClampedArray(I3 * g3), t3 = new Uint8ClampedArray(I3 * g3);
|
|
768
|
-
for (let i3 = 0; i3 < g3; i3++) {
|
|
769
|
-
const g4 = i3 * I3;
|
|
770
|
-
for (let B4 = 0; B4 < I3; B4++) {
|
|
771
|
-
let t4 = 0;
|
|
772
|
-
for (let Q4 = -C3; Q4 <= C3; Q4++) {
|
|
773
|
-
const C4 = B4 + Q4;
|
|
774
|
-
if (C4 >= 0 && C4 < I3) {
|
|
775
|
-
const I4 = A2[g4 + C4];
|
|
776
|
-
I4 > t4 && (t4 = I4);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
Q3[g4 + B4] = t4;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
for (let i3 = 0; i3 < I3; i3++) for (let A3 = 0; A3 < g3; A3++) {
|
|
783
|
-
let B4 = 0;
|
|
784
|
-
for (let t4 = -C3; t4 <= C3; t4++) {
|
|
785
|
-
const C4 = A3 + t4;
|
|
786
|
-
if (C4 >= 0 && C4 < g3) {
|
|
787
|
-
const A4 = Q3[C4 * I3 + i3];
|
|
788
|
-
A4 > B4 && (B4 = A4);
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
t3[A3 * I3 + i3] = B4;
|
|
792
|
-
}
|
|
793
|
-
return t3;
|
|
794
|
-
})(x, t2, i2, y2);
|
|
795
|
-
}
|
|
796
|
-
if (m2 = performance.now(), C2.push({ step: "Dilation", ms: (m2 - f2).toFixed(2) }), B2) {
|
|
797
|
-
g2.debug.dx = p2, g2.debug.dy = F2;
|
|
798
|
-
const A2 = new Float32Array(t2 * i2);
|
|
799
|
-
for (let I3 = 0; I3 < p2.length; I3++) {
|
|
800
|
-
const g3 = p2[I3], B3 = F2[I3];
|
|
801
|
-
A2[I3] = s2 ? Math.sqrt(g3 * g3 + B3 * B3) : Math.abs(g3) + Math.abs(B3);
|
|
802
|
-
}
|
|
803
|
-
g2.debug.magnitude = A2, g2.debug.suppressed = J2, g2.debug.edgeMap = L, g2.debug.cannyEdges = x, g2.debug.finalEdges = U;
|
|
804
|
-
}
|
|
805
|
-
g2.debug ? g2.debug.timings = C2 : g2.debug || (g2.debug = { timings: C2 });
|
|
806
|
-
const H = performance.now();
|
|
807
|
-
return C2.unshift({ step: "Edge Detection Total", ms: (H - Q2).toFixed(2) }), U;
|
|
808
|
-
}
|
|
809
|
-
function Z(A2) {
|
|
810
|
-
return A2 <= 0 ? 0 : A2 >= 1 ? 1 : A2;
|
|
811
|
-
}
|
|
812
|
-
function X(A2, I2) {
|
|
813
|
-
return Math.hypot(A2.x - I2.x, A2.y - I2.y);
|
|
814
|
-
}
|
|
815
|
-
function V(A2) {
|
|
816
|
-
return [X(A2.topLeft, A2.topRight), X(A2.topRight, A2.bottomRight), X(A2.bottomRight, A2.bottomLeft), X(A2.bottomLeft, A2.topLeft)];
|
|
817
|
-
}
|
|
818
|
-
function j(A2) {
|
|
819
|
-
const I2 = (function(A3) {
|
|
820
|
-
const I3 = [A3.topLeft, A3.topRight, A3.bottomRight, A3.bottomLeft], g3 = [];
|
|
821
|
-
for (let B2 = 0; B2 < I3.length; B2++) {
|
|
822
|
-
const A4 = I3[(B2 + I3.length - 1) % I3.length], C2 = I3[B2], Q2 = I3[(B2 + 1) % I3.length], t2 = A4.x - C2.x, i2 = A4.y - C2.y, E2 = Q2.x - C2.x, e2 = Q2.y - C2.y, o2 = t2 * E2 + i2 * e2, n2 = Math.max(1e-6, Math.hypot(t2, i2) * Math.hypot(E2, e2)), a2 = Math.max(-1, Math.min(1, o2 / n2));
|
|
823
|
-
g3.push(180 * Math.acos(a2) / Math.PI);
|
|
824
|
-
}
|
|
825
|
-
return g3;
|
|
826
|
-
})(A2);
|
|
827
|
-
let g2 = 0;
|
|
828
|
-
for (const B2 of I2) {
|
|
829
|
-
g2 += Z(1 - Math.abs(B2 - 90) / 55);
|
|
830
|
-
}
|
|
831
|
-
return g2 / Math.max(1, I2.length);
|
|
832
|
-
}
|
|
833
|
-
function O(A2, I2) {
|
|
834
|
-
const g2 = Number(I2.isValid) - Number(A2.isValid);
|
|
835
|
-
if (0 !== g2) return g2;
|
|
836
|
-
const B2 = I2.confidence - A2.confidence;
|
|
837
|
-
if (!(Math.abs(B2) < 0.015) && 0 !== B2) return B2;
|
|
838
|
-
const C2 = Math.abs((A2.approxCount ?? 99) - 4) - Math.abs((I2.approxCount ?? 99) - 4);
|
|
839
|
-
if (0 !== C2) return C2;
|
|
840
|
-
const Q2 = (I2.rightAngleScore ?? 0) - (A2.rightAngleScore ?? 0);
|
|
841
|
-
if (Math.abs(Q2) > 1e-6) return Q2;
|
|
842
|
-
const t2 = Math.abs(1 - (A2.contourFitRatio ?? 1)) - Math.abs(1 - (I2.contourFitRatio ?? 1));
|
|
843
|
-
return Math.abs(t2) > 1e-6 ? t2 : B2 || I2.score - A2.score || I2.coverageRatio - A2.coverageRatio || I2.area - A2.area;
|
|
844
|
-
}
|
|
845
|
-
function z(A2, I2, g2, B2, C2 = {}) {
|
|
846
|
-
const Q2 = C2.epsilon || 0.02, t2 = E(A2.points, Q2).length, i2 = n(A2, { epsilon: Q2 });
|
|
847
|
-
if (!i2) return { contour: A2, corners: null, score: 0, confidence: 0, isValid: false, area: A2.area || 0, fillRatio: 0, coverageRatio: 0, cornersArea: 0, approxCount: t2, convex: false, edgeSupport: 0, aspectRatio: 1 / 0, minSide: 0, rightAngleScore: 0, oppositeSideConsistency: 0, contourFitRatio: 0, contourFitScore: 0 };
|
|
848
|
-
const e2 = g2 * B2, o2 = A2.area || 0, a2 = A2.boundingBox || { minX: 0, minY: 0, maxX: 0, maxY: 0 }, s2 = o2 / Math.max(1, (a2.maxX - a2.minX + 1) * (a2.maxY - a2.minY + 1)), c2 = (function(A3) {
|
|
849
|
-
if (!A3) return 0;
|
|
850
|
-
const I3 = [A3.topLeft, A3.topRight, A3.bottomRight, A3.bottomLeft];
|
|
851
|
-
let g3 = 0;
|
|
852
|
-
for (let B3 = 0; B3 < I3.length; B3++) {
|
|
853
|
-
const A4 = (B3 + 1) % I3.length;
|
|
854
|
-
g3 += I3[B3].x * I3[A4].y - I3[A4].x * I3[B3].y;
|
|
855
|
-
}
|
|
856
|
-
return Math.abs(g3) / 2;
|
|
857
|
-
})(i2), r2 = c2 / Math.max(1, e2), h2 = V(i2), l2 = Math.min(...h2), y2 = (h2[0] + h2[2]) / 2, D2 = (h2[1] + h2[3]) / 2, w2 = y2 > D2 ? y2 / Math.max(1e-6, D2) : D2 / Math.max(1e-6, y2), d2 = (function(A3) {
|
|
858
|
-
const I3 = [A3.topLeft, A3.topRight, A3.bottomRight, A3.bottomLeft], g3 = [];
|
|
859
|
-
for (let C3 = 0; C3 < I3.length; C3++) {
|
|
860
|
-
const A4 = I3[C3], B4 = I3[(C3 + 1) % I3.length], Q3 = I3[(C3 + 2) % I3.length], t3 = (B4.x - A4.x) * (Q3.y - B4.y) - (B4.y - A4.y) * (Q3.x - B4.x);
|
|
861
|
-
Math.abs(t3) < 1e-6 || g3.push(Math.sign(t3));
|
|
862
|
-
}
|
|
863
|
-
if (g3.length < 3) return false;
|
|
864
|
-
const B3 = g3[0];
|
|
865
|
-
return g3.every((A4) => A4 === B3);
|
|
866
|
-
})(i2), f2 = (function(A3, I3, g3, B3) {
|
|
867
|
-
if (!A3?.points || 0 === A3.points.length) return 0;
|
|
868
|
-
const C3 = Math.max(1, Math.floor(A3.points.length / 240));
|
|
869
|
-
let Q3 = 0, t3 = 0;
|
|
870
|
-
for (let i3 = 0; i3 < A3.points.length; i3 += C3) {
|
|
871
|
-
const C4 = A3.points[i3], E2 = Math.max(0, Math.min(g3 - 1, 0 | C4.x)), e3 = Math.max(0, Math.min(B3 - 1, 0 | C4.y));
|
|
872
|
-
Q3++;
|
|
873
|
-
let o3 = false;
|
|
874
|
-
for (let A4 = -1; A4 <= 1 && !o3; A4++) {
|
|
875
|
-
const C5 = e3 + A4;
|
|
876
|
-
if (!(C5 < 0 || C5 >= B3)) for (let A5 = -1; A5 <= 1; A5++) {
|
|
877
|
-
const B4 = E2 + A5;
|
|
878
|
-
if (!(B4 < 0 || B4 >= g3) && I3[C5 * g3 + B4] > 0) {
|
|
879
|
-
o3 = true;
|
|
880
|
-
break;
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
o3 && t3++;
|
|
885
|
-
}
|
|
886
|
-
return 0 === Q3 ? 0 : t3 / Q3;
|
|
887
|
-
})(A2, I2, g2, B2), u2 = j(i2), m2 = (function(A3) {
|
|
888
|
-
const I3 = V(A3);
|
|
889
|
-
return (Math.min(I3[0], I3[2]) / Math.max(1e-6, Math.max(I3[0], I3[2])) + Math.min(I3[1], I3[3]) / Math.max(1e-6, Math.max(I3[1], I3[3]))) / 2;
|
|
890
|
-
})(i2), N2 = o2 / Math.max(1, c2), R2 = Z(1 - Math.abs(N2 - 1) / 0.55), p2 = 4 === t2 ? 1 : 5 === t2 ? 0.9 : 6 === t2 ? 0.7 : t2 <= 8 ? 0.5 : 0.28, F2 = Z(o2 / Math.max(1, 0.4 * e2)), J2 = Z((s2 - 0.08) / 0.72), G2 = Z((r2 - 0.03) / 0.82), k2 = void 0 !== C2.minDocumentSideRatio ? C2.minDocumentSideRatio : 0.06, S2 = void 0 !== C2.minDocumentCoverageRatio ? C2.minDocumentCoverageRatio : 0.04, M2 = void 0 !== C2.maxDocumentAspectRatio ? C2.maxDocumentAspectRatio : 8, b = void 0 !== C2.minDocumentFillRatio ? C2.minDocumentFillRatio : 0.07, L = void 0 !== C2.minContourFitRatio ? C2.minContourFitRatio : 0.11, x = void 0 !== C2.maxContourFitRatio ? C2.maxContourFitRatio : 1.2, U = void 0 !== C2.minRightAngleScore ? C2.minRightAngleScore : 0.42, H = void 0 !== C2.minOppositeSideConsistency ? C2.minOppositeSideConsistency : 0.3, Y = Math.min(g2, B2) * k2, q = (function(A3, I3 = 6) {
|
|
891
|
-
const g3 = [A3?.topLeft, A3?.topRight, A3?.bottomRight, A3?.bottomLeft];
|
|
892
|
-
if (g3.some((A4) => !A4 || !Number.isFinite(A4.x) || !Number.isFinite(A4.y))) return false;
|
|
893
|
-
for (let B3 = 0; B3 < g3.length; B3++) for (let A4 = B3 + 1; A4 < g3.length; A4++) if (X(g3[B3], g3[A4]) < I3) return false;
|
|
894
|
-
return true;
|
|
895
|
-
})(i2) && d2 && l2 >= Y && r2 >= S2 && w2 <= M2 && s2 >= b && N2 >= L && N2 <= x && u2 >= U && m2 >= H, v = 0.22 * F2 + 0.14 * J2 + 0.15 * p2 + 0.08 * (d2 ? 1 : 0) + 0.08 * f2 + 0.13 * G2 + 0.1 * u2 + 0.05 * m2 + 0.05 * R2;
|
|
896
|
-
return { contour: A2, corners: i2, score: v, confidence: q ? v : 0.33 * v, isValid: q, area: o2, fillRatio: s2, coverageRatio: r2, cornersArea: c2, approxCount: t2, convex: d2, edgeSupport: f2, aspectRatio: w2, minSide: l2, rightAngleScore: u2, oppositeSideConsistency: m2, contourFitRatio: N2, contourFitScore: R2 };
|
|
897
|
-
}
|
|
898
|
-
async function _(A2, I2, g2, C2, Q2 = {}) {
|
|
899
|
-
const t2 = Q2.debug ? {} : { _timingsOnly: true }, i2 = [];
|
|
900
|
-
t2 && !t2._timingsOnly && (t2.preprocessing = { scaledDimensions: { width: I2, height: g2 }, scaleFactor: C2, maxProcessingDimension: Q2.maxProcessingDimension || 800 });
|
|
901
|
-
const E2 = (function(A3 = {}) {
|
|
902
|
-
const I3 = A3.dilationKernelSize || 3, g3 = A3.dilationIterations || 1, B2 = void 0 === A3.applyDilation || A3.applyDilation, C3 = [{ name: "default", lowThreshold: A3.lowThreshold, highThreshold: A3.highThreshold, dilationKernelSize: I3, dilationIterations: g3, applyDilation: B2 }];
|
|
903
|
-
if (false === A3.enableDetectionCascade) return C3;
|
|
904
|
-
C3.push({ name: "connect-edges", lowThreshold: A3.lowThreshold, highThreshold: A3.highThreshold, dilationKernelSize: Math.max(I3, 5), dilationIterations: Math.max(g3, 2), applyDilation: true }), C3.push({ name: "no-dilation", lowThreshold: A3.lowThreshold, highThreshold: A3.highThreshold, dilationKernelSize: I3, dilationIterations: g3, applyDilation: false }), void 0 === A3.lowThreshold && void 0 === A3.highThreshold && C3.push({ name: "fixed-mid-thresholds", lowThreshold: 60, highThreshold: 180, dilationKernelSize: I3, dilationIterations: g3, applyDilation: B2 });
|
|
905
|
-
const Q3 = [], t3 = /* @__PURE__ */ new Set();
|
|
906
|
-
for (const i3 of C3) {
|
|
907
|
-
const A4 = [i3.lowThreshold, i3.highThreshold, i3.dilationKernelSize, i3.dilationIterations, i3.applyDilation].join(":");
|
|
908
|
-
t3.has(A4) || (t3.add(A4), Q3.push(i3));
|
|
909
|
-
}
|
|
910
|
-
return Q3;
|
|
911
|
-
})(Q2), e2 = [], o2 = async (t3, E3) => {
|
|
912
|
-
const e3 = t3.name || `pass-${E3 + 1}`, o3 = 0 === E3 ? "" : ` (${e3})`, n3 = Q2.debug ? {} : { _timingsOnly: true }, a3 = await M(A2, { width: I2, height: g2, lowThreshold: t3.lowThreshold, highThreshold: t3.highThreshold, dilationKernelSize: t3.dilationKernelSize, dilationIterations: t3.dilationIterations, applyDilation: t3.applyDilation, debug: n3, skipGrayscale: true, useWasmBlur: true, useWasmHysteresis: Q2.useWasmHysteresis, useWasmFullCanny: Q2.useWasmFullCanny });
|
|
913
|
-
n3.timings && n3.timings.forEach((A3) => {
|
|
914
|
-
"Edge Detection Total" !== A3.step && i2.push({ step: `${A3.step}${o3}`, ms: A3.ms });
|
|
915
|
-
});
|
|
916
|
-
let s3 = performance.now();
|
|
917
|
-
const c3 = B(a3, { minArea: (Q2.minArea || 1e3) / (C2 * C2), width: I2, height: g2 });
|
|
918
|
-
i2.push({ step: `Find Contours${o3}`, ms: (performance.now() - s3).toFixed(2) }), s3 = performance.now();
|
|
919
|
-
const { best: r3, candidates: h3 } = (function(A3, I3, g3, B2, C3 = {}) {
|
|
920
|
-
const Q3 = C3.maxCandidateContours || 12, t4 = A3.slice(0, Q3).map((A4, Q4) => ({ rankByArea: Q4, ...z(A4, I3, g3, B2, C3) })).sort(O);
|
|
921
|
-
return { best: t4[0] || null, candidates: t4 };
|
|
922
|
-
})(c3, a3, I2, g2, Q2);
|
|
923
|
-
return i2.push({ step: `Corner Detection${o3}`, ms: (performance.now() - s3).toFixed(2) }), { name: e3, params: { lowThreshold: t3.lowThreshold, highThreshold: t3.highThreshold, dilationKernelSize: t3.dilationKernelSize, dilationIterations: t3.dilationIterations, applyDilation: t3.applyDilation }, contours: c3, best: r3, candidates: h3 };
|
|
924
|
-
}, n2 = await o2(E2[0], 0);
|
|
925
|
-
if (e2.push(n2), (function(A3, I3 = {}) {
|
|
926
|
-
if (false === I3.enableDetectionCascade) return false;
|
|
927
|
-
if (!A3 || !A3.corners) return true;
|
|
928
|
-
const g3 = void 0 !== I3.minCascadeTriggerConfidence ? I3.minCascadeTriggerConfidence : 0.68;
|
|
929
|
-
return !A3.isValid || A3.confidence < g3 || A3.approxCount > 5 || A3.rightAngleScore < 0.55 || A3.contourFitRatio < 0.14;
|
|
930
|
-
})(n2.best, Q2)) for (let B2 = 1; B2 < E2.length; B2++) e2.push(await o2(E2[B2], B2));
|
|
931
|
-
const a2 = [];
|
|
932
|
-
for (const B2 of e2) for (const A3 of B2.candidates) a2.push({ ...A3, passName: B2.name, passParams: B2.params });
|
|
933
|
-
a2.sort(O);
|
|
934
|
-
const s2 = a2[0] || null, c2 = a2;
|
|
935
|
-
if (!s2 || !s2.corners) return { success: false, message: "No document detected", debug: t2._timingsOnly ? null : t2, timings: i2 };
|
|
936
|
-
t2 && !t2._timingsOnly && (t2.passes = e2.map((A3) => ({ name: A3.name, params: A3.params, contourCount: A3.contours.length, bestCandidate: A3.best ? { score: A3.best.score, confidence: A3.best.confidence, isValid: A3.best.isValid, approxCount: A3.best.approxCount, coverageRatio: A3.best.coverageRatio, fillRatio: A3.best.fillRatio, rightAngleScore: A3.best.rightAngleScore, contourFitRatio: A3.best.contourFitRatio } : null })), t2.candidates = c2.map((A3) => ({ passName: A3.passName, rankByArea: A3.rankByArea, area: A3.area, fillRatio: A3.fillRatio, coverageRatio: A3.coverageRatio, cornersArea: A3.cornersArea, approxCount: A3.approxCount, convex: A3.convex, edgeSupport: A3.edgeSupport, aspectRatio: A3.aspectRatio, minSide: A3.minSide, rightAngleScore: A3.rightAngleScore, oppositeSideConsistency: A3.oppositeSideConsistency, contourFitRatio: A3.contourFitRatio, contourFitScore: A3.contourFitScore, score: A3.score, confidence: A3.confidence, isValid: A3.isValid })), t2.selectedCandidate = c2[0] ? { passName: c2[0].passName, rankByArea: c2[0].rankByArea, area: c2[0].area, fillRatio: c2[0].fillRatio, coverageRatio: c2[0].coverageRatio, approxCount: c2[0].approxCount, edgeSupport: c2[0].edgeSupport, aspectRatio: c2[0].aspectRatio, minSide: c2[0].minSide, rightAngleScore: c2[0].rightAngleScore, oppositeSideConsistency: c2[0].oppositeSideConsistency, contourFitRatio: c2[0].contourFitRatio, score: c2[0].score, confidence: c2[0].confidence, isValid: c2[0].isValid } : null);
|
|
937
|
-
const r2 = s2.corners, h2 = s2.contour;
|
|
938
|
-
let l2 = r2;
|
|
939
|
-
return 1 !== C2 && (l2 = { topLeft: { x: r2.topLeft.x * C2, y: r2.topLeft.y * C2 }, topRight: { x: r2.topRight.x * C2, y: r2.topRight.y * C2 }, bottomRight: { x: r2.bottomRight.x * C2, y: r2.bottomRight.y * C2 }, bottomLeft: { x: r2.bottomLeft.x * C2, y: r2.bottomLeft.y * C2 } }), { success: true, contour: h2, corners: l2, confidence: s2.confidence, debug: t2._timingsOnly ? null : t2, timings: i2 };
|
|
940
|
-
}
|
|
941
|
-
function $(A2, I2, g2) {
|
|
942
|
-
const { topLeft: B2, topRight: C2, bottomRight: Q2, bottomLeft: t2 } = g2, i2 = Math.hypot(Q2.x - t2.x, Q2.y - t2.y), E2 = Math.hypot(C2.x - B2.x, C2.y - B2.y), e2 = Math.round(Math.max(i2, E2)), o2 = Math.hypot(C2.x - Q2.x, C2.y - Q2.y), n2 = Math.hypot(B2.x - t2.x, B2.y - t2.y), a2 = Math.round(Math.max(o2, n2));
|
|
943
|
-
A2.canvas.width = e2, A2.canvas.height = a2, (function(A3, I3, g3, B3, C3) {
|
|
944
|
-
const Q3 = I3 && "number" == typeof I3.width && "number" == typeof I3.height && I3.data, t3 = I3.width || I3.naturalWidth, i3 = I3.height || I3.naturalHeight, E3 = document.createElement("canvas");
|
|
945
|
-
E3.width = t3, E3.height = i3;
|
|
946
|
-
const e3 = E3.getContext("2d", { willReadFrequently: true });
|
|
947
|
-
Q3 ? e3.putImageData(I3, 0, 0) : e3.drawImage(I3, 0, 0, t3, i3);
|
|
948
|
-
const o3 = e3.getImageData(0, 0, t3, i3).data, n3 = (function(A4) {
|
|
949
|
-
const I4 = A4[0][0], g4 = A4[0][1], B4 = A4[0][2], C4 = A4[1][0], Q4 = A4[1][1], t4 = A4[1][2], i4 = A4[2][0], E4 = A4[2][1], e4 = A4[2][2], o4 = Q4 * e4 - t4 * E4, n4 = -(C4 * e4 - t4 * i4), a4 = C4 * E4 - Q4 * i4, s3 = -(g4 * e4 - B4 * E4), c3 = I4 * e4 - B4 * i4, r3 = -(I4 * E4 - g4 * i4), h3 = g4 * t4 - B4 * Q4, l3 = -(I4 * t4 - B4 * C4), y3 = I4 * Q4 - g4 * C4, D3 = I4 * o4 + g4 * n4 + B4 * a4;
|
|
950
|
-
if (0 === D3) throw new Error("Singular matrix");
|
|
951
|
-
return [[o4 / D3, s3 / D3, h3 / D3], [n4 / D3, c3 / D3, l3 / D3], [a4 / D3, r3 / D3, y3 / D3]];
|
|
952
|
-
})(g3), a3 = n3[0][0], s2 = n3[0][1], c2 = n3[0][2], r2 = n3[1][0], h2 = n3[1][1], l2 = n3[1][2], y2 = n3[2][0], D2 = n3[2][1], w2 = n3[2][2], d2 = A3.createImageData(B3, C3), f2 = d2.data, u2 = t3 - 1, m2 = i3 - 1;
|
|
953
|
-
for (let N2 = 0; N2 < C3; N2++) {
|
|
954
|
-
const A4 = s2 * N2 + c2, I4 = h2 * N2 + l2, g4 = D2 * N2 + w2;
|
|
955
|
-
for (let C4 = 0; C4 < B3; C4++) {
|
|
956
|
-
const Q4 = 1 / (y2 * C4 + g4), i4 = (a3 * C4 + A4) * Q4, E4 = (r2 * C4 + I4) * Q4, e4 = i4 < 0 ? 0 : i4 > u2 ? u2 : i4, n4 = E4 < 0 ? 0 : E4 > m2 ? m2 : E4, s3 = 0 | e4, c3 = 0 | n4, h3 = s3 < u2 ? s3 + 1 : s3, l3 = c3 < m2 ? c3 + 1 : c3, D3 = e4 - s3, w3 = n4 - c3, d3 = 1 - D3, R2 = 1 - w3, p2 = d3 * R2, F2 = D3 * R2, J2 = d3 * w3, G2 = D3 * w3, k2 = c3 * t3 + s3 << 2, S2 = c3 * t3 + h3 << 2, M2 = l3 * t3 + s3 << 2, b = l3 * t3 + h3 << 2, L = N2 * B3 + C4 << 2;
|
|
957
|
-
f2[L] = o3[k2] * p2 + o3[S2] * F2 + o3[M2] * J2 + o3[b] * G2 + 0.5 | 0, f2[L + 1] = o3[k2 + 1] * p2 + o3[S2 + 1] * F2 + o3[M2 + 1] * J2 + o3[b + 1] * G2 + 0.5 | 0, f2[L + 2] = o3[k2 + 2] * p2 + o3[S2 + 2] * F2 + o3[M2 + 2] * J2 + o3[b + 2] * G2 + 0.5 | 0, f2[L + 3] = 255;
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
A3.putImageData(d2, 0, 0);
|
|
961
|
-
})(A2, I2, (function(A3, I3) {
|
|
962
|
-
const g3 = (function(A4, I4) {
|
|
963
|
-
const g4 = A4.length, B3 = A4[0].length, C3 = A4.map((A5) => A5.slice()), Q3 = I4.slice();
|
|
964
|
-
for (let i3 = 0; i3 < B3; i3++) {
|
|
965
|
-
let A5 = i3;
|
|
966
|
-
for (let I5 = i3 + 1; I5 < g4; I5++) Math.abs(C3[I5][i3]) > Math.abs(C3[A5][i3]) && (A5 = I5);
|
|
967
|
-
[C3[i3], C3[A5]] = [C3[A5], C3[i3]], [Q3[i3], Q3[A5]] = [Q3[A5], Q3[i3]];
|
|
968
|
-
for (let I5 = i3 + 1; I5 < g4; I5++) {
|
|
969
|
-
const A6 = C3[I5][i3] / C3[i3][i3];
|
|
970
|
-
for (let g5 = i3; g5 < B3; g5++) C3[I5][g5] -= A6 * C3[i3][g5];
|
|
971
|
-
Q3[I5] -= A6 * Q3[i3];
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
const t3 = new Array(B3);
|
|
975
|
-
for (let i3 = B3 - 1; i3 >= 0; i3--) {
|
|
976
|
-
let A5 = Q3[i3];
|
|
977
|
-
for (let I5 = i3 + 1; I5 < B3; I5++) A5 -= C3[i3][I5] * t3[I5];
|
|
978
|
-
t3[i3] = A5 / C3[i3][i3];
|
|
979
|
-
}
|
|
980
|
-
return t3;
|
|
981
|
-
})((function(A4) {
|
|
982
|
-
const g4 = [];
|
|
983
|
-
for (let B3 = 0; B3 < 4; B3++) {
|
|
984
|
-
const [C3, Q3] = A4[B3];
|
|
985
|
-
g4.push([C3, Q3, 1, 0, 0, 0, -C3 * I3[B3][0], -Q3 * I3[B3][0]]), g4.push([0, 0, 0, C3, Q3, 1, -C3 * I3[B3][1], -Q3 * I3[B3][1]]);
|
|
986
|
-
}
|
|
987
|
-
return g4;
|
|
988
|
-
})(A3), [I3[0][0], I3[0][1], I3[1][0], I3[1][1], I3[2][0], I3[2][1], I3[3][0], I3[3][1]]);
|
|
989
|
-
return [[g3[0], g3[1], g3[2]], [g3[3], g3[4], g3[5]], [g3[6], g3[7], 1]];
|
|
990
|
-
})([[B2.x, B2.y], [C2.x, C2.y], [Q2.x, Q2.y], [t2.x, t2.y]], [[0, 0], [e2 - 1, 0], [e2 - 1, a2 - 1], [0, a2 - 1]]), e2, a2);
|
|
991
|
-
}
|
|
992
|
-
async function IA(A2, I2 = {}) {
|
|
993
|
-
const g2 = [], B2 = performance.now(), C2 = I2.mode || "detect", Q2 = I2.output || "canvas", t2 = I2.maxProcessingDimension || 800;
|
|
994
|
-
let i2, E2, e2, o2;
|
|
995
|
-
if ("ml" === (I2.detector || "classical")) {
|
|
996
|
-
E2 = performance.now();
|
|
997
|
-
const { detectDocumentMl: B3 } = await Promise.resolve().then(() => (init_scanic_ml_stub(), scanic_ml_stub_exports));
|
|
998
|
-
i2 = await B3(A2, I2.ml || {}), g2.push({ step: "ML Detection Total", ms: (performance.now() - E2).toFixed(2) }), i2.timings && i2.timings.forEach((A3) => g2.push(A3));
|
|
999
|
-
} else {
|
|
1000
|
-
E2 = performance.now();
|
|
1001
|
-
const { grayscaleData: B3, scaleFactor: C3, scaledDimensions: Q3 } = await (async function(A3, I3 = 800) {
|
|
1002
|
-
let g3, B4;
|
|
1003
|
-
const C4 = A3 && "number" == typeof A3.width && "number" == typeof A3.height && A3.data;
|
|
1004
|
-
if (C4) g3 = A3.width, B4 = A3.height;
|
|
1005
|
-
else {
|
|
1006
|
-
if (!A3) throw new Error("No image provided");
|
|
1007
|
-
g3 = A3.width || A3.naturalWidth, B4 = A3.height || A3.naturalHeight;
|
|
1008
|
-
}
|
|
1009
|
-
const Q4 = Math.max(g3, B4);
|
|
1010
|
-
let t3, i3, E3;
|
|
1011
|
-
if (Q4 <= I3) t3 = g3, i3 = B4, E3 = 1;
|
|
1012
|
-
else {
|
|
1013
|
-
const A4 = I3 / Q4;
|
|
1014
|
-
t3 = Math.round(g3 * A4), i3 = Math.round(B4 * A4), E3 = 1 / A4;
|
|
1015
|
-
}
|
|
1016
|
-
const e3 = "undefined" != typeof OffscreenCanvas, o3 = e3 ? new OffscreenCanvas(t3, i3) : document.createElement("canvas");
|
|
1017
|
-
e3 || (o3.width = t3, o3.height = i3);
|
|
1018
|
-
const n3 = o3.getContext("2d", { willReadFrequently: true });
|
|
1019
|
-
if (n3.imageSmoothingEnabled = true, n3.imageSmoothingQuality = "medium", C4) {
|
|
1020
|
-
const I4 = e3 ? new OffscreenCanvas(g3, B4) : document.createElement("canvas");
|
|
1021
|
-
e3 || (I4.width = g3, I4.height = B4), I4.getContext("2d").putImageData(A3, 0, 0), n3.drawImage(I4, 0, 0, g3, B4, 0, 0, t3, i3);
|
|
1022
|
-
} else n3.drawImage(A3, 0, 0, g3, B4, 0, 0, t3, i3);
|
|
1023
|
-
const a2 = n3.getImageData(0, 0, t3, i3), s2 = a2.data, c2 = new Uint8ClampedArray(t3 * i3);
|
|
1024
|
-
for (let r2 = 0, h2 = 0; r2 < s2.length; r2 += 4, h2++) {
|
|
1025
|
-
const A4 = 54 * s2[r2] + 183 * s2[r2 + 1] + 19 * s2[r2 + 2] >> 8;
|
|
1026
|
-
c2[h2] = A4, s2[r2] = s2[r2 + 1] = s2[r2 + 2] = A4;
|
|
1027
|
-
}
|
|
1028
|
-
return { grayscaleData: c2, imageData: a2, scaleFactor: E3, originalDimensions: { width: g3, height: B4 }, scaledDimensions: { width: t3, height: i3 } };
|
|
1029
|
-
})(A2, t2);
|
|
1030
|
-
g2.push({ step: "Image Prep + Scale + Gray", ms: (performance.now() - E2).toFixed(2) }), i2 = await _(B3, Q3.width, Q3.height, C3, I2), i2.timings && i2.timings.forEach((A3) => g2.push(A3));
|
|
1031
|
-
}
|
|
1032
|
-
if (!i2.success) {
|
|
1033
|
-
const A3 = performance.now();
|
|
1034
|
-
return g2.unshift({ step: "Total", ms: (A3 - B2).toFixed(2) }), { output: null, corners: null, contour: null, confidence: i2.confidence || null, score: i2.score ?? null, debug: i2.debug, success: false, message: i2.message || "No document detected", timings: g2 };
|
|
1035
|
-
}
|
|
1036
|
-
"detect" === C2 ? o2 = null : "extract" === C2 && (E2 = performance.now(), e2 = document.createElement("canvas"), $(e2.getContext("2d"), A2, i2.corners), g2.push({ step: "Perspective Transform", ms: (performance.now() - E2).toFixed(2) })), "detect" !== C2 && e2 && (E2 = performance.now(), o2 = "canvas" === Q2 ? e2 : "imagedata" === Q2 ? e2.getContext("2d").getImageData(0, 0, e2.width, e2.height) : "dataurl" === Q2 ? e2.toDataURL() : e2, g2.push({ step: "Output Conversion", ms: (performance.now() - E2).toFixed(2) }));
|
|
1037
|
-
const n2 = performance.now();
|
|
1038
|
-
return g2.unshift({ step: "Total", ms: (n2 - B2).toFixed(2) }), { output: o2, corners: i2.corners, contour: i2.contour ?? null, confidence: i2.confidence || null, score: i2.score ?? null, debug: i2.debug, success: true, message: "Document detected", timings: g2 };
|
|
1039
|
-
}
|
|
1040
|
-
var A, I, g, s, r, y, d, N, R, p;
|
|
1041
|
-
var init_scanic = __esm({
|
|
1042
|
-
"vendor/scanic/scanic.js"() {
|
|
1043
|
-
"use strict";
|
|
1044
|
-
I = 1e3;
|
|
1045
|
-
g = 10;
|
|
1046
|
-
s = null;
|
|
1047
|
-
r = 0;
|
|
1048
|
-
y = null;
|
|
1049
|
-
d = null;
|
|
1050
|
-
N = null;
|
|
1051
|
-
R = false;
|
|
1052
|
-
p = null;
|
|
1053
|
-
}
|
|
1054
|
-
});
|
|
1055
|
-
|
|
1056
|
-
// src/core/doc/detector-scanic.ts
|
|
1057
|
-
var detector_scanic_exports = {};
|
|
1058
|
-
__export(detector_scanic_exports, {
|
|
1059
|
-
DOC_DETECT_MIN_CONFIDENCE: () => DOC_DETECT_MIN_CONFIDENCE,
|
|
1060
|
-
DOC_DETECT_WIDTH: () => DOC_DETECT_WIDTH,
|
|
1061
|
-
SCANIC_VERSION: () => SCANIC_VERSION,
|
|
1062
|
-
detectQuad: () => detectQuad
|
|
1063
|
-
});
|
|
1064
|
-
async function detectQuad(image) {
|
|
1065
|
-
const result = await IA(image, {
|
|
1066
|
-
mode: "detect",
|
|
1067
|
-
detector: "classical",
|
|
1068
|
-
maxProcessingDimension: DOC_DETECT_WIDTH
|
|
1069
|
-
});
|
|
1070
|
-
if (!result.success || result.corners === null) return null;
|
|
1071
|
-
if (typeof result.confidence === "number" && result.confidence < DOC_DETECT_MIN_CONFIDENCE) {
|
|
1072
|
-
return null;
|
|
1073
|
-
}
|
|
1074
|
-
return result.corners;
|
|
1075
|
-
}
|
|
1076
|
-
var SCANIC_VERSION, DOC_DETECT_MIN_CONFIDENCE;
|
|
1077
|
-
var init_detector_scanic = __esm({
|
|
1078
|
-
"src/core/doc/detector-scanic.ts"() {
|
|
1079
|
-
"use strict";
|
|
1080
|
-
init_scanic();
|
|
1081
|
-
init_doc_gate_v1();
|
|
1082
|
-
SCANIC_VERSION = "1.5.0";
|
|
1083
|
-
DOC_DETECT_MIN_CONFIDENCE = 0.35;
|
|
1084
|
-
}
|
|
1085
|
-
});
|
|
1086
|
-
|
|
1087
|
-
// src/internal/index.ts
|
|
1088
|
-
var internal_exports = {};
|
|
1089
|
-
__export(internal_exports, {
|
|
1090
|
-
ASPECT_BANDS: () => ASPECT_BANDS,
|
|
1091
|
-
AttestationError: () => AttestationError,
|
|
1092
|
-
CAPTURE_CANDIDATE_SPACING_MS: () => CAPTURE_CANDIDATE_SPACING_MS,
|
|
1093
|
-
CAPTURE_EXTRA_CANDIDATES: () => CAPTURE_EXTRA_CANDIDATES,
|
|
1094
|
-
CENTERED_FACTOR: () => CENTERED_FACTOR,
|
|
1095
|
-
CIRCLE_RADIUS_FACTOR: () => CIRCLE_RADIUS_FACTOR,
|
|
1096
|
-
CLIP_MIME_ALLOWLIST: () => CLIP_MIME_ALLOWLIST,
|
|
1097
|
-
CaptureSessionDoc: () => CaptureSessionDoc,
|
|
1098
|
-
DOC_ANALYSIS_DEFAULTS: () => DOC_ANALYSIS_DEFAULTS,
|
|
1099
|
-
DOC_CADENCE_HZ: () => DOC_CADENCE_HZ,
|
|
1100
|
-
DOC_DETECT_WIDTH: () => DOC_DETECT_WIDTH,
|
|
1101
|
-
DOC_FEEDBACK_STATES: () => DOC_FEEDBACK_STATES,
|
|
1102
|
-
DOC_GATE_V1_DEFAULTS: () => DOC_GATE_V1_DEFAULTS,
|
|
1103
|
-
DocFeedbackAggregator: () => DocFeedbackAggregator,
|
|
1104
|
-
DocGateV1: () => DocGateV1,
|
|
1105
|
-
IN_CIRCLE_FACTOR: () => IN_CIRCLE_FACTOR,
|
|
1106
|
-
MRZ_BAND_DEFAULTS: () => MRZ_BAND_DEFAULTS,
|
|
1107
|
-
MR_DOC_CATALOG: () => MR_DOC_CATALOG,
|
|
1108
|
-
PRELOAD_BLAZE_INIT_TIMEOUT_MS: () => PRELOAD_BLAZE_INIT_TIMEOUT_MS,
|
|
1109
|
-
PRELOAD_BUDGET_MS: () => PRELOAD_BUDGET_MS,
|
|
1110
|
-
PROCESSING_POLL_DELAY_MS: () => PROCESSING_POLL_DELAY_MS,
|
|
1111
|
-
PROCESSING_POLL_MAX: () => PROCESSING_POLL_MAX,
|
|
1112
|
-
REGISTRY_RETRY_DELAY_MS: () => REGISTRY_RETRY_DELAY_MS,
|
|
1113
|
-
RETRYABLE_REASONS: () => RETRYABLE_REASONS,
|
|
1114
|
-
TRANSPORT_REPLAY_ATTEMPTS: () => TRANSPORT_REPLAY_ATTEMPTS,
|
|
1115
|
-
TRANSPORT_REPLAY_DELAY_MS: () => TRANSPORT_REPLAY_DELAY_MS,
|
|
1116
|
-
TURNSTILE_EXECUTE_TIMEOUT_MS: () => TURNSTILE_EXECUTE_TIMEOUT_MS,
|
|
1117
|
-
TURNSTILE_SCRIPT_SRC: () => TURNSTILE_SCRIPT_SRC,
|
|
1118
|
-
analyzeDocFrame: () => analyzeDocFrame,
|
|
1119
|
-
aspectBand: () => aspectBand,
|
|
1120
|
-
createLivenessCaptureInternal: () => createLivenessCaptureInternal,
|
|
1121
|
-
createVerificationInternal: () => createVerificationInternal,
|
|
1122
|
-
detectMrzBand: () => detectMrzBand,
|
|
1123
|
-
executeTurnstile: () => executeTurnstile,
|
|
1124
|
-
feedbackVoteOf: () => feedbackVoteOf,
|
|
1125
|
-
frontierStep: () => frontierStep,
|
|
1126
|
-
isExecutable: () => isExecutable,
|
|
1127
|
-
isTerminalView: () => isTerminalView,
|
|
1128
|
-
loadTurnstile: () => loadTurnstile,
|
|
1129
|
-
mapEngineState: () => mapEngineState,
|
|
1130
|
-
mapErrorKind: () => mapErrorKind,
|
|
1131
|
-
mapGuidanceHint: () => mapGuidanceHint,
|
|
1132
|
-
mapSessionResult: () => mapSessionResult,
|
|
1133
|
-
mergeSessionView: () => mergeSessionView,
|
|
1134
|
-
preloadedDetectorFactory: () => preloadedDetectorFactory,
|
|
1135
|
-
quadAreaFrac: () => quadAreaFrac,
|
|
1136
|
-
quadBounds: () => quadBounds,
|
|
1137
|
-
quadIou: () => quadIou,
|
|
1138
|
-
quadTiltRatio: () => quadTiltRatio,
|
|
1139
|
-
randomUuid: () => randomUuid,
|
|
1140
|
-
resetLivenessAssetsForTests: () => resetLivenessAssetsForTests,
|
|
1141
|
-
resetTurnstileForTests: () => resetTurnstileForTests,
|
|
1142
|
-
runLivenessFlow: () => runLivenessFlow,
|
|
1143
|
-
runSubmission: () => runSubmission,
|
|
1144
|
-
setPreloadLoaderForTests: () => setPreloadLoaderForTests,
|
|
1145
|
-
specularFraction: () => specularFraction,
|
|
1146
|
-
stepOf: () => stepOf,
|
|
1147
|
-
warmDocDetector: () => warmDocDetector
|
|
1148
|
-
});
|
|
1149
|
-
module.exports = __toCommonJS(internal_exports);
|
|
1150
|
-
init_doc_catalog();
|
|
1151
|
-
init_doc_gate_v1();
|
|
1152
|
-
|
|
1153
|
-
// src/core/doc/mrz-band.ts
|
|
1154
|
-
var MRZ_BAND_DEFAULTS = Object.freeze({
|
|
1155
|
-
EDGE_LUMA_DELTA: 40,
|
|
1156
|
-
MIN_ROW_DENSITY: 0.08,
|
|
1157
|
-
MIN_COLUMN_COVERAGE: 0.6,
|
|
1158
|
-
MIN_WIDTH_FRAC: 0.55,
|
|
1159
|
-
MIN_HEIGHT_FRAC: 0.02,
|
|
1160
|
-
MAX_HEIGHT_FRAC: 0.16,
|
|
1161
|
-
ROW_MERGE_GAP: 18
|
|
1162
|
-
});
|
|
1163
|
-
var COLUMN_BUCKETS = 12;
|
|
1164
|
-
function detectMrzBand(frame, overrides = {}) {
|
|
1165
|
-
const t2 = { ...MRZ_BAND_DEFAULTS, ...overrides };
|
|
1166
|
-
const { data, width, height } = frame;
|
|
1167
|
-
if (width < 32 || height < 32) return null;
|
|
1168
|
-
const qualifies = new Array(height).fill(false);
|
|
1169
|
-
const density = new Float32Array(height);
|
|
1170
|
-
const minX = new Int32Array(height).fill(width);
|
|
1171
|
-
const maxX = new Int32Array(height).fill(-1);
|
|
1172
|
-
const bucketWidth = width / COLUMN_BUCKETS;
|
|
1173
|
-
const buckets = new Array(COLUMN_BUCKETS);
|
|
1174
|
-
for (let y2 = 0; y2 < height; y2++) {
|
|
1175
|
-
let edges = 0;
|
|
1176
|
-
buckets.fill(false);
|
|
1177
|
-
const rowOffset = y2 * width * 4;
|
|
1178
|
-
let previousLuma = 0.299 * (data[rowOffset] ?? 0) + 0.587 * (data[rowOffset + 1] ?? 0) + 0.114 * (data[rowOffset + 2] ?? 0);
|
|
1179
|
-
for (let x = 1; x < width; x++) {
|
|
1180
|
-
const i2 = rowOffset + x * 4;
|
|
1181
|
-
const luma = 0.299 * (data[i2] ?? 0) + 0.587 * (data[i2 + 1] ?? 0) + 0.114 * (data[i2 + 2] ?? 0);
|
|
1182
|
-
if (Math.abs(luma - previousLuma) >= t2.EDGE_LUMA_DELTA) {
|
|
1183
|
-
edges += 1;
|
|
1184
|
-
buckets[Math.min(COLUMN_BUCKETS - 1, Math.floor(x / bucketWidth))] = true;
|
|
1185
|
-
if (x < minX[y2]) minX[y2] = x;
|
|
1186
|
-
if (x > maxX[y2]) maxX[y2] = x;
|
|
1187
|
-
}
|
|
1188
|
-
previousLuma = luma;
|
|
1189
|
-
}
|
|
1190
|
-
density[y2] = edges / width;
|
|
1191
|
-
const coverage = buckets.filter(Boolean).length / COLUMN_BUCKETS;
|
|
1192
|
-
qualifies[y2] = density[y2] >= t2.MIN_ROW_DENSITY && coverage >= t2.MIN_COLUMN_COVERAGE && maxX[y2] - minX[y2] >= t2.MIN_WIDTH_FRAC * width;
|
|
1193
|
-
}
|
|
1194
|
-
let best = null;
|
|
1195
|
-
let runTop = -1;
|
|
1196
|
-
let lastQualifying = -1;
|
|
1197
|
-
let runRows = 0;
|
|
1198
|
-
const flush = (bottom) => {
|
|
1199
|
-
if (runTop < 0) return;
|
|
1200
|
-
const bandHeight = bottom - runTop + 1;
|
|
1201
|
-
if (bandHeight >= t2.MIN_HEIGHT_FRAC * height && bandHeight <= t2.MAX_HEIGHT_FRAC * height && runRows >= 4 && // at least a few real stroke rows, not a lone edge
|
|
1202
|
-
(best === null || runRows > best.rows)) {
|
|
1203
|
-
best = { top: runTop, bottom, rows: runRows };
|
|
1204
|
-
}
|
|
1205
|
-
runTop = -1;
|
|
1206
|
-
runRows = 0;
|
|
1207
|
-
};
|
|
1208
|
-
for (let y2 = 0; y2 < height; y2++) {
|
|
1209
|
-
if (qualifies[y2]) {
|
|
1210
|
-
if (runTop < 0) runTop = y2;
|
|
1211
|
-
else if (y2 - lastQualifying > t2.ROW_MERGE_GAP) {
|
|
1212
|
-
flush(lastQualifying);
|
|
1213
|
-
runTop = y2;
|
|
1214
|
-
}
|
|
1215
|
-
lastQualifying = y2;
|
|
1216
|
-
runRows += 1;
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
flush(lastQualifying);
|
|
1220
|
-
if (best === null) return null;
|
|
1221
|
-
const band = best;
|
|
1222
|
-
let x1 = width;
|
|
1223
|
-
let x2 = -1;
|
|
1224
|
-
let densitySum = 0;
|
|
1225
|
-
let densityRows = 0;
|
|
1226
|
-
for (let y2 = band.top; y2 <= band.bottom; y2++) {
|
|
1227
|
-
if (!qualifies[y2]) continue;
|
|
1228
|
-
if (minX[y2] < x1) x1 = minX[y2];
|
|
1229
|
-
if (maxX[y2] > x2) x2 = maxX[y2];
|
|
1230
|
-
densitySum += density[y2];
|
|
1231
|
-
densityRows += 1;
|
|
1232
|
-
}
|
|
1233
|
-
if (x2 - x1 < t2.MIN_WIDTH_FRAC * width) return null;
|
|
1234
|
-
return {
|
|
1235
|
-
x: x1,
|
|
1236
|
-
y: band.top,
|
|
1237
|
-
width: x2 - x1,
|
|
1238
|
-
height: band.bottom - band.top + 1,
|
|
1239
|
-
density: densityRows > 0 ? densitySum / densityRows : 0
|
|
1240
|
-
};
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
// src/core/doc/frame-analysis.ts
|
|
1244
|
-
init_frame_quality();
|
|
1245
|
-
init_doc_catalog();
|
|
1246
|
-
init_doc_gate_v1();
|
|
1247
|
-
var DOC_ANALYSIS_DEFAULTS = Object.freeze({
|
|
1248
|
-
/** Doc area fraction above which the camera is too close (capture would crop). */
|
|
1249
|
-
MAX_AREA_FRAC: 0.92,
|
|
1250
|
-
/** Opposite-side length ratio beyond which the view is too oblique. */
|
|
1251
|
-
TILT_MAX_SIDE_RATIO: 1.3,
|
|
1252
|
-
/** Specular-pixel fraction at/above which the frame reads as glare. */
|
|
1253
|
-
GLARE_MAX_SPECULAR_FRAC: 0.025
|
|
1254
|
-
});
|
|
1255
|
-
function specularFraction(crop) {
|
|
1256
|
-
const { data, width, height } = crop;
|
|
1257
|
-
const pixels = width * height;
|
|
1258
|
-
if (pixels === 0) return 0;
|
|
1259
|
-
let hot = 0;
|
|
1260
|
-
for (let i2 = 0; i2 < pixels; i2++) {
|
|
1261
|
-
const o2 = i2 * 4;
|
|
1262
|
-
const luma = 0.299 * (data[o2] ?? 0) + 0.587 * (data[o2 + 1] ?? 0) + 0.114 * (data[o2 + 2] ?? 0);
|
|
1263
|
-
if (luma >= 252) hot++;
|
|
1264
|
-
}
|
|
1265
|
-
return hot / pixels;
|
|
1266
|
-
}
|
|
1267
|
-
function quadTiltRatio(quad) {
|
|
1268
|
-
const len = (a2, b) => Math.hypot(b.x - a2.x, b.y - a2.y);
|
|
1269
|
-
const top = len(quad.topLeft, quad.topRight);
|
|
1270
|
-
const bottom = len(quad.bottomLeft, quad.bottomRight);
|
|
1271
|
-
const left = len(quad.topLeft, quad.bottomLeft);
|
|
1272
|
-
const right = len(quad.topRight, quad.bottomRight);
|
|
1273
|
-
const h2 = Math.max(top, bottom) / Math.max(1e-6, Math.min(top, bottom));
|
|
1274
|
-
const v = Math.max(left, right) / Math.max(1e-6, Math.min(left, right));
|
|
1275
|
-
return Math.max(h2, v);
|
|
1276
|
-
}
|
|
1277
|
-
function analyzeDocFrame(input) {
|
|
1278
|
-
const t2 = { ...DOC_GATE_V1_DEFAULTS, ...input.thresholds };
|
|
1279
|
-
const a2 = DOC_ANALYSIS_DEFAULTS;
|
|
1280
|
-
const { quad, frameWidth, frameHeight, measures } = input;
|
|
1281
|
-
const bandMode = input.format === "ID-3";
|
|
1282
|
-
let framing = "ok";
|
|
1283
|
-
if (quad === null) {
|
|
1284
|
-
framing = "no_document";
|
|
1285
|
-
} else if (bandMode) {
|
|
1286
|
-
const bounds = quadBounds(quad);
|
|
1287
|
-
const widthFrac = frameWidth > 0 ? bounds.width / frameWidth : 0;
|
|
1288
|
-
const aspect = bounds.height > 0 ? bounds.width / bounds.height : 0;
|
|
1289
|
-
if (widthFrac < t2.ID3_BAND_MIN_WIDTH_FRAC) framing = "too_far";
|
|
1290
|
-
else if (edgeClipped(quad, frameWidth, frameHeight, t2.EDGE_CLIP_MARGIN_PX)) {
|
|
1291
|
-
framing = "edge_clipped";
|
|
1292
|
-
} else if (aspect < t2.ID3_BAND_ASPECT_MIN || aspect > t2.ID3_BAND_ASPECT_MAX) {
|
|
1293
|
-
framing = "wrong_shape";
|
|
1294
|
-
}
|
|
1295
|
-
} else {
|
|
1296
|
-
const areaFrac = quadAreaFrac(quad, frameWidth, frameHeight);
|
|
1297
|
-
const band = aspectBand(input.format);
|
|
1298
|
-
const bounds = quadBounds(quad);
|
|
1299
|
-
const aspect = Math.max(bounds.width, bounds.height) / Math.max(1e-6, Math.min(bounds.width, bounds.height));
|
|
1300
|
-
if (areaFrac < t2.MIN_AREA_FRAC) framing = "too_far";
|
|
1301
|
-
else if (areaFrac > a2.MAX_AREA_FRAC) framing = "too_close";
|
|
1302
|
-
else if (edgeClipped(quad, frameWidth, frameHeight, t2.EDGE_CLIP_MARGIN_PX)) {
|
|
1303
|
-
framing = "edge_clipped";
|
|
1304
|
-
} else if (Math.abs(aspect - band.center) > band.tolerance) framing = "wrong_shape";
|
|
1305
|
-
else if (quadTiltRatio(quad) > a2.TILT_MAX_SIDE_RATIO) framing = "tilted";
|
|
1306
|
-
}
|
|
1307
|
-
const lighting = measures.exposure >= t2.GUIDE_EXPOSURE ? "ok" : measures.meanLuma < (EXPOSURE_BAND[0] + EXPOSURE_BAND[1]) / 2 ? "too_dark" : "too_bright";
|
|
1308
|
-
const blur = measures.sharpness >= t2.GUIDE_SHARPNESS ? "ok" : "blurry";
|
|
1309
|
-
const glare = measures.specularFraction >= a2.GLARE_MAX_SPECULAR_FRAC ? "glare" : "ok";
|
|
1310
|
-
return {
|
|
1311
|
-
framing,
|
|
1312
|
-
lighting,
|
|
1313
|
-
blur,
|
|
1314
|
-
glare,
|
|
1315
|
-
acceptable: framing === "ok" && lighting === "ok" && blur === "ok" && glare === "ok"
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
function edgeClipped(quad, frameWidth, frameHeight, margin) {
|
|
1319
|
-
const pts = [quad.topLeft, quad.topRight, quad.bottomRight, quad.bottomLeft];
|
|
1320
|
-
return pts.some(
|
|
1321
|
-
(p2) => p2.x <= margin || p2.y <= margin || p2.x >= frameWidth - margin || p2.y >= frameHeight - margin
|
|
1322
|
-
);
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
// src/core/doc/feedback-aggregator.ts
|
|
1326
|
-
var DOC_FEEDBACK_STATES = Object.freeze({
|
|
1327
|
-
sensing: { key: "sensing", reticle: "searching", minDurationMs: 900 },
|
|
1328
|
-
too_far: { key: "too_far", reticle: "coaching", minDurationMs: 1400 },
|
|
1329
|
-
too_close: { key: "too_close", reticle: "coaching", minDurationMs: 1400 },
|
|
1330
|
-
edge_clipped: { key: "edge_clipped", reticle: "coaching", minDurationMs: 1400 },
|
|
1331
|
-
wrong_shape: { key: "wrong_shape", reticle: "coaching", minDurationMs: 1400 },
|
|
1332
|
-
tilted: { key: "tilted", reticle: "coaching", minDurationMs: 1400 },
|
|
1333
|
-
too_dark: { key: "too_dark", reticle: "coaching", minDurationMs: 1400 },
|
|
1334
|
-
too_bright: { key: "too_bright", reticle: "coaching", minDurationMs: 1400 },
|
|
1335
|
-
blurry: { key: "blurry", reticle: "coaching", minDurationMs: 1400 },
|
|
1336
|
-
glare: { key: "glare", reticle: "coaching", minDurationMs: 1400 },
|
|
1337
|
-
hold_still: { key: "hold_still", reticle: "locking", minDurationMs: 900 },
|
|
1338
|
-
processing: { key: "processing", reticle: "processing", minDurationMs: 800 },
|
|
1339
|
-
side_captured: { key: "side_captured", reticle: "flip", minDurationMs: 2600 },
|
|
1340
|
-
captured: { key: "captured", reticle: "done", minDurationMs: 800 }
|
|
1341
|
-
});
|
|
1342
|
-
function feedbackVoteOf(analysis) {
|
|
1343
|
-
if (analysis.framing === "no_document") return "sensing";
|
|
1344
|
-
if (analysis.framing === "too_far") return "too_far";
|
|
1345
|
-
if (analysis.framing === "too_close") return "too_close";
|
|
1346
|
-
if (analysis.framing === "edge_clipped") return "edge_clipped";
|
|
1347
|
-
if (analysis.framing === "wrong_shape") return "wrong_shape";
|
|
1348
|
-
if (analysis.framing === "tilted") return "tilted";
|
|
1349
|
-
if (analysis.lighting === "too_dark") return "too_dark";
|
|
1350
|
-
if (analysis.lighting === "too_bright") return "too_bright";
|
|
1351
|
-
if (analysis.glare === "glare") return "glare";
|
|
1352
|
-
if (analysis.blur === "blurry") return "blurry";
|
|
1353
|
-
return "hold_still";
|
|
1354
|
-
}
|
|
1355
|
-
var DocFeedbackAggregator = class {
|
|
1356
|
-
constructor(windowMs = 2600, decayPerSecond = 0.35) {
|
|
1357
|
-
this.windowMs = windowMs;
|
|
1358
|
-
this.decayPerSecond = decayPerSecond;
|
|
1359
|
-
this.votes = [];
|
|
1360
|
-
this.current = DOC_FEEDBACK_STATES.sensing;
|
|
1361
|
-
this.currentSinceMs = null;
|
|
1362
|
-
}
|
|
1363
|
-
reset() {
|
|
1364
|
-
this.votes = [];
|
|
1365
|
-
this.current = DOC_FEEDBACK_STATES.sensing;
|
|
1366
|
-
this.currentSinceMs = null;
|
|
1367
|
-
}
|
|
1368
|
-
/** Force a terminal/choreography state (capture fired, side flip, upload). */
|
|
1369
|
-
force(key, nowMs) {
|
|
1370
|
-
this.votes = [];
|
|
1371
|
-
this.current = DOC_FEEDBACK_STATES[key];
|
|
1372
|
-
this.currentSinceMs = nowMs;
|
|
1373
|
-
return this.current;
|
|
1374
|
-
}
|
|
1375
|
-
/** Cast this frame's vote and return the (possibly held) winning state. */
|
|
1376
|
-
tick(vote, nowMs) {
|
|
1377
|
-
if (this.currentSinceMs === null) this.currentSinceMs = nowMs;
|
|
1378
|
-
this.votes.push({ key: vote, atMs: nowMs });
|
|
1379
|
-
this.votes = this.votes.filter((v) => nowMs - v.atMs <= this.windowMs);
|
|
1380
|
-
if (nowMs - this.currentSinceMs < this.current.minDurationMs) return this.current;
|
|
1381
|
-
const scores = /* @__PURE__ */ new Map();
|
|
1382
|
-
for (const v of this.votes) {
|
|
1383
|
-
const ageSeconds = (nowMs - v.atMs) / 1e3;
|
|
1384
|
-
const weight = this.decayPerSecond ** ageSeconds;
|
|
1385
|
-
scores.set(v.key, (scores.get(v.key) ?? 0) + weight);
|
|
1386
|
-
}
|
|
1387
|
-
let winner = this.current.key;
|
|
1388
|
-
let best = -1;
|
|
1389
|
-
for (const [key, score] of scores) {
|
|
1390
|
-
if (score > best) {
|
|
1391
|
-
best = score;
|
|
1392
|
-
winner = key;
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
if (winner !== this.current.key) {
|
|
1396
|
-
this.current = DOC_FEEDBACK_STATES[winner];
|
|
1397
|
-
this.currentSinceMs = nowMs;
|
|
1398
|
-
}
|
|
1399
|
-
return this.current;
|
|
1400
|
-
}
|
|
1401
|
-
};
|
|
1402
|
-
|
|
1403
|
-
// src/core/media.ts
|
|
1404
|
-
var MIME_CANDIDATES = [
|
|
1405
|
-
"video/mp4;codecs=avc1.42E01E,mp4a.40.2",
|
|
1406
|
-
"video/mp4;codecs=avc1.42E01E",
|
|
1407
|
-
"video/mp4",
|
|
1408
|
-
"video/webm;codecs=vp9,opus",
|
|
1409
|
-
"video/webm;codecs=vp9",
|
|
1410
|
-
"video/webm;codecs=vp8,opus",
|
|
1411
|
-
"video/webm;codecs=vp8",
|
|
1412
|
-
"video/webm"
|
|
1413
|
-
];
|
|
1414
|
-
function pickMimeType() {
|
|
1415
|
-
const MR = globalThis.MediaRecorder;
|
|
1416
|
-
if (!MR || typeof MR.isTypeSupported !== "function") return "";
|
|
1417
|
-
return MIME_CANDIDATES.find((t2) => MR.isTypeSupported(t2)) ?? "";
|
|
1418
|
-
}
|
|
1419
|
-
var DEFAULT_CLIP_MIME = "video/webm";
|
|
1420
|
-
function mapGetUserMediaError(err) {
|
|
1421
|
-
if (err instanceof TypeError) return "insecure";
|
|
1422
|
-
const name = err?.name ?? "";
|
|
1423
|
-
switch (name) {
|
|
1424
|
-
case "NotAllowedError":
|
|
1425
|
-
case "PermissionDeniedError":
|
|
1426
|
-
return "permission";
|
|
1427
|
-
case "NotFoundError":
|
|
1428
|
-
case "DevicesNotFoundError":
|
|
1429
|
-
return "notfound";
|
|
1430
|
-
case "NotReadableError":
|
|
1431
|
-
case "TrackStartError":
|
|
1432
|
-
return "inuse";
|
|
1433
|
-
case "OverconstrainedError":
|
|
1434
|
-
case "ConstraintNotSatisfiedError":
|
|
1435
|
-
return "overconstrained";
|
|
1436
|
-
default:
|
|
1437
|
-
return "generic";
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
function cameraPreflight() {
|
|
1441
|
-
const w2 = globalThis;
|
|
1442
|
-
if (typeof w2.window !== "undefined" && w2.window.isSecureContext === false)
|
|
1443
|
-
return "INSECURE_CONTEXT";
|
|
1444
|
-
const md = w2.navigator?.mediaDevices;
|
|
1445
|
-
if (!md || typeof md.getUserMedia !== "function") return "UNSUPPORTED";
|
|
1446
|
-
return "OK";
|
|
1447
|
-
}
|
|
1448
|
-
function isMediaRecorderSupported() {
|
|
1449
|
-
const MR = globalThis.MediaRecorder;
|
|
1450
|
-
return typeof MR !== "undefined" && typeof MR.isTypeSupported === "function";
|
|
1451
|
-
}
|
|
1452
|
-
var MAX_CLIP_BYTES = 8 * 1024 * 1024;
|
|
1453
|
-
function isClipTooLarge(blob) {
|
|
1454
|
-
return blob.size > MAX_CLIP_BYTES;
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
// src/core/v3/device-profile.ts
|
|
1458
|
-
var DEVICE_PROFILES = Object.freeze({
|
|
1459
|
-
LOW: Object.freeze({ cadenceHz: 5, inputWidth: 128, animations: "reduced" }),
|
|
1460
|
-
MID: Object.freeze({ cadenceHz: 8, inputWidth: 160, animations: "full" }),
|
|
1461
|
-
HIGH: Object.freeze({ cadenceHz: 10, inputWidth: 192, animations: "full" })
|
|
1462
|
-
});
|
|
1463
|
-
var SIMD_TEST_BYTES = new Uint8Array([
|
|
1464
|
-
0,
|
|
1465
|
-
97,
|
|
1466
|
-
115,
|
|
1467
|
-
109,
|
|
1468
|
-
1,
|
|
1469
|
-
0,
|
|
1470
|
-
0,
|
|
1471
|
-
0,
|
|
1472
|
-
1,
|
|
1473
|
-
5,
|
|
1474
|
-
1,
|
|
1475
|
-
96,
|
|
1476
|
-
0,
|
|
1477
|
-
1,
|
|
1478
|
-
123,
|
|
1479
|
-
3,
|
|
1480
|
-
2,
|
|
1481
|
-
1,
|
|
1482
|
-
0,
|
|
1483
|
-
10,
|
|
1484
|
-
10,
|
|
1485
|
-
1,
|
|
1486
|
-
8,
|
|
1487
|
-
0,
|
|
1488
|
-
65,
|
|
1489
|
-
0,
|
|
1490
|
-
253,
|
|
1491
|
-
15,
|
|
1492
|
-
253,
|
|
1493
|
-
98,
|
|
1494
|
-
11
|
|
1495
|
-
]);
|
|
1496
|
-
function probeDevice() {
|
|
1497
|
-
const nav = typeof navigator === "undefined" ? void 0 : navigator;
|
|
1498
|
-
let simd = false;
|
|
1499
|
-
try {
|
|
1500
|
-
simd = typeof WebAssembly !== "undefined" && WebAssembly.validate(SIMD_TEST_BYTES);
|
|
1501
|
-
} catch {
|
|
1502
|
-
simd = false;
|
|
1503
|
-
}
|
|
1504
|
-
return {
|
|
1505
|
-
cores: nav?.hardwareConcurrency ?? 2,
|
|
1506
|
-
memGB: nav?.deviceMemory ?? null,
|
|
1507
|
-
simd
|
|
1508
|
-
};
|
|
1509
|
-
}
|
|
1510
|
-
function classifyDevice(probe) {
|
|
1511
|
-
const memGB = probe.memGB ?? 4;
|
|
1512
|
-
if (probe.cores < 4 || memGB < 3) return "LOW";
|
|
1513
|
-
if (probe.cores >= 8 && memGB >= 6) return "HIGH";
|
|
1514
|
-
return "MID";
|
|
1515
|
-
}
|
|
1516
|
-
var ORDER = ["LOW", "MID", "HIGH"];
|
|
1517
|
-
var DeviceProfiler = class {
|
|
1518
|
-
constructor(initial) {
|
|
1519
|
-
this.samples = [];
|
|
1520
|
-
this.adjusted = false;
|
|
1521
|
-
this.current = initial;
|
|
1522
|
-
}
|
|
1523
|
-
get deviceClass() {
|
|
1524
|
-
return this.current;
|
|
1525
|
-
}
|
|
1526
|
-
get profile() {
|
|
1527
|
-
return DEVICE_PROFILES[this.current];
|
|
1528
|
-
}
|
|
1529
|
-
record(detectMs) {
|
|
1530
|
-
if (this.adjusted || this.samples.length >= 10) return this.current;
|
|
1531
|
-
this.samples.push(detectMs);
|
|
1532
|
-
if (this.samples.length < 10) return this.current;
|
|
1533
|
-
this.adjusted = true;
|
|
1534
|
-
const mean = this.samples.reduce((sum, value) => sum + value, 0) / this.samples.length;
|
|
1535
|
-
if (mean > 70) {
|
|
1536
|
-
this.current = "LOW";
|
|
1537
|
-
} else if (mean < 25) {
|
|
1538
|
-
const index = ORDER.indexOf(this.current);
|
|
1539
|
-
this.current = ORDER[Math.min(ORDER.length - 1, index + 1)] ?? this.current;
|
|
1540
|
-
}
|
|
1541
|
-
return this.current;
|
|
1542
|
-
}
|
|
1543
|
-
};
|
|
1544
|
-
|
|
1545
|
-
// src/core/doc/capture-session-doc.ts
|
|
1546
|
-
init_frame_quality();
|
|
1547
|
-
init_doc_gate_v1();
|
|
1548
|
-
var scanicDetect = null;
|
|
1549
|
-
async function lazyScanicDetect(image) {
|
|
1550
|
-
if (scanicDetect === null) {
|
|
1551
|
-
const module_ = await Promise.resolve().then(() => (init_detector_scanic(), detector_scanic_exports));
|
|
1552
|
-
scanicDetect = module_.detectQuad;
|
|
1553
|
-
}
|
|
1554
|
-
return scanicDetect(image);
|
|
1555
|
-
}
|
|
1556
|
-
async function mrzBandDetect(image) {
|
|
1557
|
-
const band = detectMrzBand(image);
|
|
1558
|
-
if (band === null) return null;
|
|
1559
|
-
return {
|
|
1560
|
-
topLeft: { x: band.x, y: band.y },
|
|
1561
|
-
topRight: { x: band.x + band.width, y: band.y },
|
|
1562
|
-
bottomRight: { x: band.x + band.width, y: band.y + band.height },
|
|
1563
|
-
bottomLeft: { x: band.x, y: band.y + band.height }
|
|
1564
|
-
};
|
|
1565
|
-
}
|
|
1566
|
-
function warmDocDetector() {
|
|
1567
|
-
if (scanicDetect !== null) return;
|
|
1568
|
-
void Promise.resolve().then(() => (init_detector_scanic(), detector_scanic_exports)).then((module_) => {
|
|
1569
|
-
scanicDetect = module_.detectQuad;
|
|
1570
|
-
}).catch(() => void 0);
|
|
1571
|
-
}
|
|
1572
|
-
var DOC_CADENCE_HZ = Object.freeze({
|
|
1573
|
-
LOW: 3,
|
|
1574
|
-
MID: 5,
|
|
1575
|
-
HIGH: 6
|
|
1576
|
-
});
|
|
1577
|
-
var CaptureSessionDoc = class {
|
|
1578
|
-
constructor(opts) {
|
|
1579
|
-
this.handlers = /* @__PURE__ */ new Map();
|
|
1580
|
-
this.stream = null;
|
|
1581
|
-
this.timer = null;
|
|
1582
|
-
this.state = "idle";
|
|
1583
|
-
this.lastGuidance = null;
|
|
1584
|
-
this.attempt = 0;
|
|
1585
|
-
this.capturing = false;
|
|
1586
|
-
this.feedback = new DocFeedbackAggregator();
|
|
1587
|
-
this.lastFeedbackKey = null;
|
|
1588
|
-
this.opts = opts;
|
|
1589
|
-
this.gate = new DocGateV1(opts.format ?? "ID-1", opts.thresholds);
|
|
1590
|
-
const deviceClass = opts.deviceClass ?? classifyDevice(probeDevice());
|
|
1591
|
-
this.cadenceMs = 1e3 / DOC_CADENCE_HZ[deviceClass];
|
|
1592
|
-
}
|
|
1593
|
-
on(event, handler) {
|
|
1594
|
-
let set = this.handlers.get(event);
|
|
1595
|
-
if (!set) {
|
|
1596
|
-
set = /* @__PURE__ */ new Set();
|
|
1597
|
-
this.handlers.set(event, set);
|
|
1598
|
-
}
|
|
1599
|
-
set.add(handler);
|
|
1600
|
-
return () => set?.delete(handler);
|
|
1601
|
-
}
|
|
1602
|
-
emit(event, payload) {
|
|
1603
|
-
for (const handler of [...this.handlers.get(event) ?? []]) {
|
|
1604
|
-
handler(payload);
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
async start() {
|
|
1608
|
-
const attempt = ++this.attempt;
|
|
1609
|
-
this.gate.reset();
|
|
1610
|
-
this.feedback.reset();
|
|
1611
|
-
this.lastFeedbackKey = null;
|
|
1612
|
-
this.lastGuidance = null;
|
|
1613
|
-
this.capturing = false;
|
|
1614
|
-
this.setState("preflight");
|
|
1615
|
-
try {
|
|
1616
|
-
const getUserMedia = this.opts.getUserMedia ?? ((constraints) => navigator.mediaDevices.getUserMedia(constraints));
|
|
1617
|
-
this.setState("acquiring");
|
|
1618
|
-
this.stream = await getUserMedia({
|
|
1619
|
-
audio: false,
|
|
1620
|
-
video: {
|
|
1621
|
-
facingMode: { ideal: "environment" },
|
|
1622
|
-
width: { ideal: 1920 },
|
|
1623
|
-
height: { ideal: 1080 }
|
|
1624
|
-
}
|
|
1625
|
-
});
|
|
1626
|
-
if (attempt !== this.attempt) {
|
|
1627
|
-
for (const track of this.stream.getTracks()) track.stop();
|
|
1628
|
-
return;
|
|
1629
|
-
}
|
|
1630
|
-
this.opts.video.srcObject = this.stream;
|
|
1631
|
-
await this.opts.video.play().catch(() => void 0);
|
|
1632
|
-
this.setState("positioning");
|
|
1633
|
-
this.scheduleTick(attempt);
|
|
1634
|
-
} catch (err) {
|
|
1635
|
-
const kind = mapGetUserMediaError(err);
|
|
1636
|
-
this.setState("error");
|
|
1637
|
-
this.emit("error", { kind, retryable: true });
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
/** Restart the gate for another side / a retake (same stream). */
|
|
1641
|
-
rearm(side) {
|
|
1642
|
-
if (side !== void 0) this.opts.side = side;
|
|
1643
|
-
this.gate.reset();
|
|
1644
|
-
this.feedback.reset();
|
|
1645
|
-
this.lastFeedbackKey = null;
|
|
1646
|
-
this.lastGuidance = null;
|
|
1647
|
-
this.capturing = false;
|
|
1648
|
-
if (this.state === "captured") {
|
|
1649
|
-
this.setState("positioning");
|
|
1650
|
-
this.scheduleTick(this.attempt);
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
destroy() {
|
|
1654
|
-
this.attempt += 1;
|
|
1655
|
-
if (this.timer !== null) (this.opts.clearTimer ?? clearTimeout)(this.timer);
|
|
1656
|
-
this.timer = null;
|
|
1657
|
-
if (this.stream) for (const track of this.stream.getTracks()) track.stop();
|
|
1658
|
-
this.stream = null;
|
|
1659
|
-
this.opts.video.srcObject = null;
|
|
1660
|
-
}
|
|
1661
|
-
setState(state) {
|
|
1662
|
-
this.state = state;
|
|
1663
|
-
this.emit("statechange", { state });
|
|
1664
|
-
}
|
|
1665
|
-
scheduleTick(attempt) {
|
|
1666
|
-
const setTimer = this.opts.setTimer ?? setTimeout;
|
|
1667
|
-
this.timer = setTimer(() => void this.tick(attempt), this.cadenceMs);
|
|
1668
|
-
}
|
|
1669
|
-
async tick(attempt) {
|
|
1670
|
-
if (attempt !== this.attempt || this.state !== "positioning" || this.capturing) return;
|
|
1671
|
-
const now = this.opts.now ?? (() => performance.now());
|
|
1672
|
-
const grab = this.opts.grabDetectFrame ?? defaultGrabDetectFrame;
|
|
1673
|
-
const detect = this.opts.detect ?? ((this.opts.format ?? "ID-1") === "ID-3" ? mrzBandDetect : lazyScanicDetect);
|
|
1674
|
-
const frame = grab(this.opts.video, DOC_DETECT_WIDTH);
|
|
1675
|
-
if (frame === null) {
|
|
1676
|
-
this.scheduleTick(attempt);
|
|
1677
|
-
return;
|
|
1678
|
-
}
|
|
1679
|
-
let quad = null;
|
|
1680
|
-
try {
|
|
1681
|
-
quad = await detect(frame);
|
|
1682
|
-
} catch {
|
|
1683
|
-
quad = null;
|
|
1684
|
-
}
|
|
1685
|
-
if (attempt !== this.attempt) return;
|
|
1686
|
-
const crop = quad !== null ? cropQuadRegion(frame, quad) : frame;
|
|
1687
|
-
const atMs = now();
|
|
1688
|
-
const measures = {
|
|
1689
|
-
sharpness: sharpness(crop),
|
|
1690
|
-
exposure: exposure(crop),
|
|
1691
|
-
meanLuma: meanLuma(crop),
|
|
1692
|
-
specularFraction: specularFraction(crop)
|
|
1693
|
-
};
|
|
1694
|
-
const analysis = analyzeDocFrame({
|
|
1695
|
-
format: this.opts.format ?? "ID-1",
|
|
1696
|
-
quad,
|
|
1697
|
-
frameWidth: frame.width,
|
|
1698
|
-
frameHeight: frame.height,
|
|
1699
|
-
measures,
|
|
1700
|
-
...this.opts.thresholds !== void 0 ? { thresholds: this.opts.thresholds } : {}
|
|
1701
|
-
});
|
|
1702
|
-
const tick = this.gate.tick({
|
|
1703
|
-
timestampMs: atMs,
|
|
1704
|
-
quad,
|
|
1705
|
-
quality: measures,
|
|
1706
|
-
frameWidth: frame.width,
|
|
1707
|
-
frameHeight: frame.height
|
|
1708
|
-
});
|
|
1709
|
-
this.emit("checks", {
|
|
1710
|
-
engine: "docv1",
|
|
1711
|
-
p: tick.p,
|
|
1712
|
-
plausible: tick.plausible,
|
|
1713
|
-
stableTicks: tick.stableTicks,
|
|
1714
|
-
quad: tick.quad,
|
|
1715
|
-
frameWidth: frame.width,
|
|
1716
|
-
frameHeight: frame.height,
|
|
1717
|
-
analysis
|
|
1718
|
-
});
|
|
1719
|
-
const feedbackState = this.feedback.tick(feedbackVoteOf(analysis), atMs);
|
|
1720
|
-
if (feedbackState.key !== this.lastFeedbackKey) {
|
|
1721
|
-
this.lastFeedbackKey = feedbackState.key;
|
|
1722
|
-
this.emit("feedback", feedbackState);
|
|
1723
|
-
}
|
|
1724
|
-
if (tick.guidanceKey !== this.lastGuidance) {
|
|
1725
|
-
this.lastGuidance = tick.guidanceKey;
|
|
1726
|
-
this.emit("guidance", { key: tick.guidanceKey });
|
|
1727
|
-
}
|
|
1728
|
-
if (tick.ready && quad !== null) {
|
|
1729
|
-
const processing = this.feedback.force("processing", atMs);
|
|
1730
|
-
this.lastFeedbackKey = processing.key;
|
|
1731
|
-
this.emit("feedback", processing);
|
|
1732
|
-
await this.fire(attempt, quad, {
|
|
1733
|
-
width: frame.width,
|
|
1734
|
-
height: frame.height,
|
|
1735
|
-
tickSharpness: measures.sharpness
|
|
1736
|
-
});
|
|
1737
|
-
return;
|
|
1738
|
-
}
|
|
1739
|
-
this.scheduleTick(attempt);
|
|
1740
|
-
}
|
|
1741
|
-
/**
|
|
1742
|
-
* BEST-FRAME capture window (capture-v2): the first ready tick captures a
|
|
1743
|
-
* candidate immediately, then up to CAPTURE_EXTRA_CANDIDATES more stills are
|
|
1744
|
-
* taken at CAPTURE_CANDIDATE_SPACING_MS while the user holds — each scored
|
|
1745
|
-
* by the live detect-tick sharpness at its capture moment — and the SHARPEST
|
|
1746
|
-
* wins. Beats first-acceptable-frame capture without decoding stills.
|
|
1747
|
-
*/
|
|
1748
|
-
async fire(attempt, quad, detectSize) {
|
|
1749
|
-
this.capturing = true;
|
|
1750
|
-
const capture = this.opts.captureStill ?? defaultCaptureStill;
|
|
1751
|
-
const grab = this.opts.grabDetectFrame ?? defaultGrabDetectFrame;
|
|
1752
|
-
const setTimer = this.opts.setTimer ?? setTimeout;
|
|
1753
|
-
const size = { width: detectSize.width, height: detectSize.height };
|
|
1754
|
-
try {
|
|
1755
|
-
let best = {
|
|
1756
|
-
still: await capture(this.opts.video, quad, size),
|
|
1757
|
-
sharpness: detectSize.tickSharpness
|
|
1758
|
-
};
|
|
1759
|
-
for (let i2 = 0; i2 < CAPTURE_EXTRA_CANDIDATES; i2++) {
|
|
1760
|
-
await new Promise((resolve) => {
|
|
1761
|
-
setTimer(() => resolve(), CAPTURE_CANDIDATE_SPACING_MS);
|
|
1762
|
-
});
|
|
1763
|
-
if (attempt !== this.attempt) return;
|
|
1764
|
-
const frame = grab(this.opts.video, DOC_DETECT_WIDTH);
|
|
1765
|
-
if (frame === null) continue;
|
|
1766
|
-
const candidateSharpness = sharpness(cropQuadRegion(frame, quad));
|
|
1767
|
-
if (candidateSharpness > best.sharpness * CAPTURE_IMPROVEMENT_MIN_RATIO) {
|
|
1768
|
-
const still = await capture(this.opts.video, quad, size);
|
|
1769
|
-
if (attempt !== this.attempt) return;
|
|
1770
|
-
best = { still, sharpness: candidateSharpness };
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
if (attempt !== this.attempt) return;
|
|
1774
|
-
this.setState("captured");
|
|
1775
|
-
this.emit("artifacts", {
|
|
1776
|
-
keyframe: best.still.blob,
|
|
1777
|
-
previewDataUrl: best.still.previewDataUrl,
|
|
1778
|
-
...this.opts.side !== void 0 ? { side: this.opts.side } : {}
|
|
1779
|
-
});
|
|
1780
|
-
} catch {
|
|
1781
|
-
if (attempt !== this.attempt) return;
|
|
1782
|
-
this.capturing = false;
|
|
1783
|
-
this.gate.reset();
|
|
1784
|
-
this.scheduleTick(attempt);
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
};
|
|
1788
|
-
var CAPTURE_EXTRA_CANDIDATES = 2;
|
|
1789
|
-
var CAPTURE_CANDIDATE_SPACING_MS = 260;
|
|
1790
|
-
var CAPTURE_IMPROVEMENT_MIN_RATIO = 1.08;
|
|
1791
|
-
function defaultGrabDetectFrame(video, targetWidth) {
|
|
1792
|
-
const vw = video.videoWidth;
|
|
1793
|
-
const vh = video.videoHeight;
|
|
1794
|
-
if (vw === 0 || vh === 0) return null;
|
|
1795
|
-
const scale = Math.min(1, targetWidth / vw);
|
|
1796
|
-
const width = Math.round(vw * scale);
|
|
1797
|
-
const height = Math.round(vh * scale);
|
|
1798
|
-
const canvas = document.createElement("canvas");
|
|
1799
|
-
canvas.width = width;
|
|
1800
|
-
canvas.height = height;
|
|
1801
|
-
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
1802
|
-
if (context === null) return null;
|
|
1803
|
-
context.drawImage(video, 0, 0, width, height);
|
|
1804
|
-
return context.getImageData(0, 0, width, height);
|
|
1805
|
-
}
|
|
1806
|
-
async function defaultCaptureStill(video, _quad, _detectSize) {
|
|
1807
|
-
const canvas = document.createElement("canvas");
|
|
1808
|
-
canvas.width = Math.max(1, video.videoWidth);
|
|
1809
|
-
canvas.height = Math.max(1, video.videoHeight);
|
|
1810
|
-
const context = canvas.getContext("2d");
|
|
1811
|
-
if (context === null) throw new Error("2d context unavailable");
|
|
1812
|
-
context.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
1813
|
-
const blob = await new Promise((resolve, reject) => {
|
|
1814
|
-
canvas.toBlob((b) => b ? resolve(b) : reject(new Error("toBlob failed")), "image/jpeg", 0.92);
|
|
1815
|
-
});
|
|
1816
|
-
return { blob, previewDataUrl: canvas.toDataURL("image/jpeg", 0.85) };
|
|
1817
|
-
}
|
|
1818
|
-
function cropQuadRegion(frame, quad) {
|
|
1819
|
-
const xs = [quad.topLeft.x, quad.topRight.x, quad.bottomRight.x, quad.bottomLeft.x];
|
|
1820
|
-
const ys = [quad.topLeft.y, quad.topRight.y, quad.bottomRight.y, quad.bottomLeft.y];
|
|
1821
|
-
const x1 = Math.max(0, Math.floor(Math.min(...xs)));
|
|
1822
|
-
const y1 = Math.max(0, Math.floor(Math.min(...ys)));
|
|
1823
|
-
const x2 = Math.min(frame.width, Math.ceil(Math.max(...xs)));
|
|
1824
|
-
const y2 = Math.min(frame.height, Math.ceil(Math.max(...ys)));
|
|
1825
|
-
const width = Math.max(1, x2 - x1);
|
|
1826
|
-
const height = Math.max(1, y2 - y1);
|
|
1827
|
-
const data = new Uint8ClampedArray(width * height * 4);
|
|
1828
|
-
for (let row = 0; row < height; row++) {
|
|
1829
|
-
const src = ((y1 + row) * frame.width + x1) * 4;
|
|
1830
|
-
data.set(frame.data.subarray(src, src + width * 4), row * width * 4);
|
|
1831
|
-
}
|
|
1832
|
-
return { data, width, height, colorSpace: frame.colorSpace };
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
|
-
// src/core/capture-constraints.ts
|
|
1836
|
-
function buildCaptureConstraints(isMobile, facingMode) {
|
|
1837
|
-
void isMobile;
|
|
1838
|
-
return {
|
|
1839
|
-
video: {
|
|
1840
|
-
width: { ideal: 1280, max: 1280 },
|
|
1841
|
-
height: { ideal: 720, max: 720 },
|
|
1842
|
-
facingMode,
|
|
1843
|
-
frameRate: { ideal: 24, max: 30 }
|
|
1844
|
-
},
|
|
1845
|
-
audio: false
|
|
1846
|
-
};
|
|
1847
|
-
}
|
|
1848
|
-
function detectIsMobile() {
|
|
1849
|
-
if (typeof window === "undefined" || typeof navigator === "undefined") return false;
|
|
1850
|
-
return window.innerWidth <= 768 || /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
1851
|
-
String(navigator.userAgent ?? "")
|
|
1852
|
-
);
|
|
1853
|
-
}
|
|
1854
|
-
var CIRCLE_RADIUS_FACTOR = 0.45;
|
|
1855
|
-
var IN_CIRCLE_FACTOR = 0.9;
|
|
1856
|
-
var CENTERED_FACTOR = 0.4;
|
|
1857
|
-
|
|
1858
|
-
// src/core/clip-recorder.ts
|
|
1859
|
-
var CLIP_MS = 2500;
|
|
1860
|
-
var TIMESLICE_MS = 1e3;
|
|
1861
|
-
var STOP_GRACE_MS = 1500;
|
|
1862
|
-
var VIDEO_BPS = 25e5;
|
|
1863
|
-
var ClipError = class extends Error {
|
|
1864
|
-
constructor(kind, msg) {
|
|
1865
|
-
super(msg ?? kind);
|
|
1866
|
-
this.kind = kind;
|
|
1867
|
-
}
|
|
1868
|
-
};
|
|
1869
|
-
var ClipRecorder = class {
|
|
1870
|
-
constructor() {
|
|
1871
|
-
this.recorder = null;
|
|
1872
|
-
this.stream = null;
|
|
1873
|
-
// own field so teardown can stop tracks (REVIEW FIX 8)
|
|
1874
|
-
this.chunks = [];
|
|
1875
|
-
this.bytes = 0;
|
|
1876
|
-
this.hardStopTimer = null;
|
|
1877
|
-
this.graceTimer = null;
|
|
1878
|
-
this.settled = false;
|
|
1879
|
-
}
|
|
1880
|
-
clearTimers() {
|
|
1881
|
-
if (this.hardStopTimer) clearTimeout(this.hardStopTimer);
|
|
1882
|
-
if (this.graceTimer) clearTimeout(this.graceTimer);
|
|
1883
|
-
this.hardStopTimer = null;
|
|
1884
|
-
this.graceTimer = null;
|
|
1885
|
-
}
|
|
1886
|
-
/** Records CLIP_MS of the stream. Resolves with the clip blob + its container mime;
|
|
1887
|
-
* rejects with ClipError. Behavior contract is the v1 hook's `start`, verbatim
|
|
1888
|
-
* (iOS workarounds included).
|
|
1889
|
-
*
|
|
1890
|
-
* ONE RECORDING AT A TIME: chunk/byte/timer state is shared per instance, so a
|
|
1891
|
-
* second record() while one is in flight rejects ClipError("generic") instead of
|
|
1892
|
-
* corrupting the in-flight clip. (v2 guard — v1's hook was gated by its owning
|
|
1893
|
-
* component; a class API invites reuse. Zero impact on clip bytes.)
|
|
1894
|
-
*
|
|
1895
|
-
* `onStopped` (optional) fires ONCE the moment the CLIP_MS recording window
|
|
1896
|
-
* elapses (the hard-stop), BEFORE finalize — which on iOS can lag up to
|
|
1897
|
-
* STOP_GRACE_MS while onstop flushes the last chunk. It lets the host freeze
|
|
1898
|
-
* the display on the already-captured keyframe the instant recording ends,
|
|
1899
|
-
* instead of waiting for the blob. PURE NOTIFICATION — it does not touch
|
|
1900
|
-
* timers, chunks, or the finalize path, so it is calibration-neutral (no
|
|
1901
|
-
* effect on clip bytes). Never fires on a rejected capture (error /
|
|
1902
|
-
* cliptoolarge settle before the hard-stop and clear the timer). */
|
|
1903
|
-
record(stream, onStopped) {
|
|
1904
|
-
if (this.recorder && !this.settled) {
|
|
1905
|
-
return Promise.reject(
|
|
1906
|
-
new ClipError(
|
|
1907
|
-
"generic",
|
|
1908
|
-
"record() called while a recording is in flight \u2014 ClipRecorder handles one recording at a time"
|
|
1909
|
-
)
|
|
1910
|
-
);
|
|
1911
|
-
}
|
|
1912
|
-
this.recorder = null;
|
|
1913
|
-
this.clearTimers();
|
|
1914
|
-
this.chunks = [];
|
|
1915
|
-
this.bytes = 0;
|
|
1916
|
-
this.settled = false;
|
|
1917
|
-
this.stream = stream;
|
|
1918
|
-
const picked = pickMimeType();
|
|
1919
|
-
return new Promise((resolve, reject) => {
|
|
1920
|
-
let recorder;
|
|
1921
|
-
try {
|
|
1922
|
-
recorder = new MediaRecorder(
|
|
1923
|
-
stream,
|
|
1924
|
-
picked ? { mimeType: picked, videoBitsPerSecond: VIDEO_BPS } : { videoBitsPerSecond: VIDEO_BPS }
|
|
1925
|
-
);
|
|
1926
|
-
} catch {
|
|
1927
|
-
try {
|
|
1928
|
-
recorder = new MediaRecorder(stream);
|
|
1929
|
-
} catch {
|
|
1930
|
-
reject(new ClipError("unsupported"));
|
|
1931
|
-
return;
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
this.recorder = recorder;
|
|
1935
|
-
const settle = (fn) => {
|
|
1936
|
-
if (this.settled) return;
|
|
1937
|
-
this.settled = true;
|
|
1938
|
-
this.clearTimers();
|
|
1939
|
-
fn();
|
|
1940
|
-
};
|
|
1941
|
-
const finalize = () => settle(() => {
|
|
1942
|
-
const type = recorder.mimeType || picked || DEFAULT_CLIP_MIME;
|
|
1943
|
-
const blob = new Blob(this.chunks, { type });
|
|
1944
|
-
if (isClipTooLarge(blob)) {
|
|
1945
|
-
reject(new ClipError("cliptoolarge"));
|
|
1946
|
-
return;
|
|
1947
|
-
}
|
|
1948
|
-
resolve({ blob, mimeType: type });
|
|
1949
|
-
});
|
|
1950
|
-
recorder.ondataavailable = (e2) => {
|
|
1951
|
-
if (e2.data && e2.data.size) {
|
|
1952
|
-
this.chunks.push(e2.data);
|
|
1953
|
-
this.bytes += e2.data.size;
|
|
1954
|
-
if (this.bytes > MAX_CLIP_BYTES) {
|
|
1955
|
-
settle(() => {
|
|
1956
|
-
try {
|
|
1957
|
-
if (recorder.state !== "inactive") recorder.stop();
|
|
1958
|
-
} catch {
|
|
1959
|
-
}
|
|
1960
|
-
reject(new ClipError("cliptoolarge"));
|
|
1961
|
-
});
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
};
|
|
1965
|
-
recorder.onstop = finalize;
|
|
1966
|
-
recorder.onerror = () => settle(() => reject(new ClipError("generic")));
|
|
1967
|
-
try {
|
|
1968
|
-
recorder.start(TIMESLICE_MS);
|
|
1969
|
-
} catch {
|
|
1970
|
-
settle(() => reject(new ClipError("generic")));
|
|
1971
|
-
return;
|
|
1972
|
-
}
|
|
1973
|
-
this.hardStopTimer = setTimeout(() => {
|
|
1974
|
-
if (!this.settled) onStopped?.();
|
|
1975
|
-
if (recorder.state !== "inactive") {
|
|
1976
|
-
try {
|
|
1977
|
-
recorder.stop();
|
|
1978
|
-
} catch {
|
|
1979
|
-
finalize();
|
|
1980
|
-
}
|
|
1981
|
-
this.graceTimer = setTimeout(finalize, STOP_GRACE_MS);
|
|
1982
|
-
} else {
|
|
1983
|
-
finalize();
|
|
1984
|
-
}
|
|
1985
|
-
}, CLIP_MS);
|
|
1986
|
-
});
|
|
1987
|
-
}
|
|
1988
|
-
/** Idempotent teardown: stop recorder + ALL tracks (v1 `stopAndCleanup` + the unmount
|
|
1989
|
-
* effect). Optional `stream` override mirrors v1's `stopAndCleanup(stream?)`.
|
|
1990
|
-
*
|
|
1991
|
-
* MID-RECORDING SEMANTICS (verbatim v1): calling stop() while a record() is in
|
|
1992
|
-
* flight may LATER resolve the pending promise with a TRUNCATED clip (recorder.stop()
|
|
1993
|
-
* fires onstop → finalize from whatever chunks arrived so far), or — because this
|
|
1994
|
-
* method clears the hard-stop/grace timers — never settle it at all if onstop never
|
|
1995
|
-
* fires (the iOS grace path has been torn down). Callers MUST NOT treat a resolution
|
|
1996
|
-
* arriving after stop() as a full CLIP_MS clip, and must not await a record()
|
|
1997
|
-
* promise past stop() without their own timeout. (Task 4's CaptureSession owns
|
|
1998
|
-
* sequencing on top of this contract.) */
|
|
1999
|
-
stop(stream) {
|
|
2000
|
-
this.clearTimers();
|
|
2001
|
-
const rec = this.recorder;
|
|
2002
|
-
if (rec && rec.state !== "inactive") {
|
|
2003
|
-
try {
|
|
2004
|
-
rec.stop();
|
|
2005
|
-
} catch {
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
|
-
this.recorder = null;
|
|
2009
|
-
const s2 = stream ?? this.stream;
|
|
2010
|
-
s2?.getTracks().forEach((t2) => t2.stop());
|
|
2011
|
-
this.stream = null;
|
|
2012
|
-
}
|
|
2013
|
-
};
|
|
2014
|
-
|
|
2015
|
-
// src/core/keyframe.ts
|
|
2016
|
-
function captureMirroredKeyframe(video, canvas, quality = 0.92) {
|
|
2017
|
-
return new Promise((resolve, reject) => {
|
|
2018
|
-
const w2 = video.videoWidth;
|
|
2019
|
-
const h2 = video.videoHeight;
|
|
2020
|
-
if (!w2 || !h2) {
|
|
2021
|
-
reject(new Error("video-not-ready"));
|
|
2022
|
-
return;
|
|
2023
|
-
}
|
|
2024
|
-
const ctx = canvas.getContext("2d");
|
|
2025
|
-
if (!ctx) {
|
|
2026
|
-
reject(new Error("no-2d-context"));
|
|
2027
|
-
return;
|
|
2028
|
-
}
|
|
2029
|
-
canvas.width = w2;
|
|
2030
|
-
canvas.height = h2;
|
|
2031
|
-
ctx.scale(-1, 1);
|
|
2032
|
-
ctx.drawImage(video, -w2, 0);
|
|
2033
|
-
canvas.toBlob(
|
|
2034
|
-
(blob) => blob ? resolve(blob) : reject(new Error("empty-keyframe")),
|
|
2035
|
-
"image/jpeg",
|
|
2036
|
-
quality
|
|
2037
|
-
);
|
|
2038
|
-
});
|
|
2039
|
-
}
|
|
2040
|
-
|
|
2041
|
-
// src/core/v3/detector-blaze.ts
|
|
2042
|
-
var BLAZE_INIT_TIMEOUT_MS = 1e4;
|
|
2043
|
-
var BlazeDetector = class _BlazeDetector {
|
|
2044
|
-
constructor(inner, fallbackReason) {
|
|
2045
|
-
this.tier = "blaze";
|
|
2046
|
-
this.inner = inner;
|
|
2047
|
-
if (fallbackReason !== void 0) this.fallbackReason = fallbackReason;
|
|
2048
|
-
}
|
|
2049
|
-
static async create(assetBaseUrl, fallbackReason) {
|
|
2050
|
-
const vision = await import("@mediapipe/tasks-vision");
|
|
2051
|
-
const fileset = await vision.FilesetResolver.forVisionTasks(assetBaseUrl);
|
|
2052
|
-
const inner = await vision.FaceDetector.createFromOptions(fileset, {
|
|
2053
|
-
baseOptions: {
|
|
2054
|
-
modelAssetPath: `${assetBaseUrl}/blaze_face_short_range.tflite`,
|
|
2055
|
-
// CPU delegate — the iOS-Safari-safe path (see header). NEVER 'GPU'.
|
|
2056
|
-
delegate: "CPU"
|
|
2057
|
-
},
|
|
2058
|
-
runningMode: "IMAGE"
|
|
2059
|
-
});
|
|
2060
|
-
return new _BlazeDetector(inner, fallbackReason);
|
|
2061
|
-
}
|
|
2062
|
-
async detect(source, sourceWidth, sourceHeight) {
|
|
2063
|
-
let detections;
|
|
2064
|
-
try {
|
|
2065
|
-
detections = this.inner.detect(source).detections;
|
|
2066
|
-
} catch {
|
|
2067
|
-
return null;
|
|
2068
|
-
}
|
|
2069
|
-
const best = pickMostConfident(detections);
|
|
2070
|
-
if (best?.boundingBox === void 0) return null;
|
|
2071
|
-
const size = intrinsicSize(source);
|
|
2072
|
-
const eyes = extractEyes(best.keypoints, size);
|
|
2073
|
-
const observation = {
|
|
2074
|
-
box: {
|
|
2075
|
-
x: best.boundingBox.originX,
|
|
2076
|
-
y: best.boundingBox.originY,
|
|
2077
|
-
width: best.boundingBox.width,
|
|
2078
|
-
height: best.boundingBox.height
|
|
2079
|
-
},
|
|
2080
|
-
...eyes !== void 0 ? { eyes } : {},
|
|
2081
|
-
score: best.categories[0]?.score ?? 0
|
|
2082
|
-
};
|
|
2083
|
-
return toStreamPixels(observation, source, sourceWidth, sourceHeight);
|
|
2084
|
-
}
|
|
2085
|
-
destroy() {
|
|
2086
|
-
try {
|
|
2087
|
-
this.inner.close();
|
|
2088
|
-
} catch {
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
};
|
|
2092
|
-
function pickMostConfident(detections) {
|
|
2093
|
-
let best;
|
|
2094
|
-
let bestScore = -1;
|
|
2095
|
-
for (const detection of detections) {
|
|
2096
|
-
const score = detection.categories[0]?.score ?? 0;
|
|
2097
|
-
if (score > bestScore) {
|
|
2098
|
-
best = detection;
|
|
2099
|
-
bestScore = score;
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
return best;
|
|
2103
|
-
}
|
|
2104
|
-
function extractEyes(keypoints, size) {
|
|
2105
|
-
const a2 = keypoints?.[0];
|
|
2106
|
-
const b = keypoints?.[1];
|
|
2107
|
-
if (a2 === void 0 || b === void 0) return void 0;
|
|
2108
|
-
const toSource = (p2) => ({ x: p2.x * size.width, y: p2.y * size.height });
|
|
2109
|
-
const [pa, pb] = [toSource(a2), toSource(b)];
|
|
2110
|
-
return pa.x <= pb.x ? { left: pa, right: pb } : { left: pb, right: pa };
|
|
2111
|
-
}
|
|
2112
|
-
|
|
2113
|
-
// src/core/v3/detector-native.ts
|
|
2114
|
-
function smokeSource() {
|
|
2115
|
-
if (typeof OffscreenCanvas === "function") return new OffscreenCanvas(2, 2);
|
|
2116
|
-
const canvas = document.createElement("canvas");
|
|
2117
|
-
canvas.width = 2;
|
|
2118
|
-
canvas.height = 2;
|
|
2119
|
-
return canvas;
|
|
2120
|
-
}
|
|
2121
|
-
async function probeNativeDetector() {
|
|
2122
|
-
const ctor = globalThis.FaceDetector;
|
|
2123
|
-
if (typeof ctor !== "function") return { ok: false, reason: "unsupported" };
|
|
2124
|
-
try {
|
|
2125
|
-
const instance = new ctor({ fastMode: true, maxDetectedFaces: 1 });
|
|
2126
|
-
await instance.detect(smokeSource());
|
|
2127
|
-
return { ok: true, instance };
|
|
2128
|
-
} catch (err) {
|
|
2129
|
-
return { ok: false, reason: `smoke_failed:${err instanceof Error ? err.message : String(err)}` };
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
var NativeDetector = class {
|
|
2133
|
-
constructor(inner, fallbackReason) {
|
|
2134
|
-
this.tier = "native";
|
|
2135
|
-
this.inner = inner;
|
|
2136
|
-
if (fallbackReason !== void 0) this.fallbackReason = fallbackReason;
|
|
2137
|
-
}
|
|
2138
|
-
async detect(source, sourceWidth, sourceHeight) {
|
|
2139
|
-
let faces;
|
|
2140
|
-
try {
|
|
2141
|
-
faces = await this.inner.detect(source);
|
|
2142
|
-
} catch {
|
|
2143
|
-
return null;
|
|
2144
|
-
}
|
|
2145
|
-
const face = faces[0];
|
|
2146
|
-
if (face === void 0) return null;
|
|
2147
|
-
const eyes = extractEyes2(face.landmarks);
|
|
2148
|
-
const observation = {
|
|
2149
|
-
box: {
|
|
2150
|
-
x: face.boundingBox.x,
|
|
2151
|
-
y: face.boundingBox.y,
|
|
2152
|
-
width: face.boundingBox.width,
|
|
2153
|
-
height: face.boundingBox.height
|
|
2154
|
-
},
|
|
2155
|
-
...eyes !== void 0 ? { eyes } : {},
|
|
2156
|
-
// The Shape Detection API exposes no confidence — the smoke-tested
|
|
2157
|
-
// native tier treats any detection as confident.
|
|
2158
|
-
score: 1
|
|
2159
|
-
};
|
|
2160
|
-
return toStreamPixels(observation, source, sourceWidth, sourceHeight);
|
|
2161
|
-
}
|
|
2162
|
-
destroy() {
|
|
2163
|
-
}
|
|
2164
|
-
};
|
|
2165
|
-
function extractEyes2(landmarks) {
|
|
2166
|
-
if (landmarks === void 0) return void 0;
|
|
2167
|
-
const eyePoints = landmarks.filter((landmark) => landmark.type === "eye").map((landmark) => landmark.locations[0]).filter((point) => point !== void 0);
|
|
2168
|
-
const [a2, b] = eyePoints;
|
|
2169
|
-
if (a2 === void 0 || b === void 0) return void 0;
|
|
2170
|
-
return a2.x <= b.x ? { left: a2, right: b } : { left: b, right: a2 };
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
// src/core/v3/detector.ts
|
|
2174
|
-
var DEFAULT_V3_ASSET_BASE_URL = "/models-v3";
|
|
2175
|
-
var DEFAULT_TIER_ORDER = ["native", "blaze"];
|
|
2176
|
-
var DetectorLadderError = class extends Error {
|
|
2177
|
-
constructor(fallbackReason) {
|
|
2178
|
-
super(`no detector tier available (${fallbackReason})`);
|
|
2179
|
-
this.name = "DetectorLadderError";
|
|
2180
|
-
this.fallbackReason = fallbackReason;
|
|
2181
|
-
}
|
|
2182
|
-
};
|
|
2183
|
-
function intrinsicSize(source) {
|
|
2184
|
-
const candidate = source;
|
|
2185
|
-
if (typeof candidate.videoWidth === "number" && candidate.videoWidth > 0) {
|
|
2186
|
-
return { width: candidate.videoWidth, height: candidate.videoHeight ?? 0 };
|
|
2187
|
-
}
|
|
2188
|
-
if (typeof candidate.naturalWidth === "number" && candidate.naturalWidth > 0) {
|
|
2189
|
-
return { width: candidate.naturalWidth, height: candidate.naturalHeight ?? 0 };
|
|
2190
|
-
}
|
|
2191
|
-
const width = typeof candidate.width === "number" ? candidate.width : 0;
|
|
2192
|
-
const height = typeof candidate.height === "number" ? candidate.height : 0;
|
|
2193
|
-
return { width, height };
|
|
2194
|
-
}
|
|
2195
|
-
function toStreamPixels(observation, source, streamWidth, streamHeight) {
|
|
2196
|
-
const size = intrinsicSize(source);
|
|
2197
|
-
const scaleX = size.width > 0 ? streamWidth / size.width : 1;
|
|
2198
|
-
const scaleY = size.height > 0 ? streamHeight / size.height : 1;
|
|
2199
|
-
const scalePoint = (p2) => ({ x: p2.x * scaleX, y: p2.y * scaleY });
|
|
2200
|
-
return {
|
|
2201
|
-
box: {
|
|
2202
|
-
x: observation.box.x * scaleX,
|
|
2203
|
-
y: observation.box.y * scaleY,
|
|
2204
|
-
width: observation.box.width * scaleX,
|
|
2205
|
-
height: observation.box.height * scaleY
|
|
2206
|
-
},
|
|
2207
|
-
...observation.eyes !== void 0 ? { eyes: { left: scalePoint(observation.eyes.left), right: scalePoint(observation.eyes.right) } } : {},
|
|
2208
|
-
score: observation.score
|
|
2209
|
-
};
|
|
2210
|
-
}
|
|
2211
|
-
function describeError(err) {
|
|
2212
|
-
return err instanceof Error ? err.message : String(err);
|
|
2213
|
-
}
|
|
2214
|
-
async function createDetector(options) {
|
|
2215
|
-
const order = options.preferred ?? DEFAULT_TIER_ORDER;
|
|
2216
|
-
const reasons = [];
|
|
2217
|
-
for (const tier of order) {
|
|
2218
|
-
if (tier === "native") {
|
|
2219
|
-
const probed = await probeNativeDetector();
|
|
2220
|
-
if (probed.ok) {
|
|
2221
|
-
return new NativeDetector(probed.instance, reasonOf(reasons));
|
|
2222
|
-
}
|
|
2223
|
-
reasons.push(`native:${probed.reason}`);
|
|
2224
|
-
continue;
|
|
2225
|
-
}
|
|
2226
|
-
try {
|
|
2227
|
-
return await withTimeout(
|
|
2228
|
-
BlazeDetector.create(
|
|
2229
|
-
options.v3AssetBaseUrl ?? DEFAULT_V3_ASSET_BASE_URL,
|
|
2230
|
-
reasonOf(reasons)
|
|
2231
|
-
),
|
|
2232
|
-
options.blazeInitTimeoutMs ?? BLAZE_INIT_TIMEOUT_MS
|
|
2233
|
-
);
|
|
2234
|
-
} catch (err) {
|
|
2235
|
-
reasons.push(
|
|
2236
|
-
err instanceof InitTimeoutError ? "blaze:blaze_init_timeout" : `blaze:${describeError(err)}`
|
|
2237
|
-
);
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
throw new DetectorLadderError(reasonOf(reasons) ?? "no_tier_available");
|
|
2241
|
-
}
|
|
2242
|
-
function reasonOf(reasons) {
|
|
2243
|
-
return reasons.length > 0 ? reasons.join("; ") : void 0;
|
|
2244
|
-
}
|
|
2245
|
-
var InitTimeoutError = class extends Error {
|
|
2246
|
-
};
|
|
2247
|
-
function withTimeout(promise, deadlineMs) {
|
|
2248
|
-
return new Promise((resolvePromise, rejectPromise) => {
|
|
2249
|
-
const timer = setTimeout(() => {
|
|
2250
|
-
rejectPromise(new InitTimeoutError(`init exceeded ${deadlineMs}ms`));
|
|
2251
|
-
promise.then(
|
|
2252
|
-
(value) => value.destroy(),
|
|
2253
|
-
() => void 0
|
|
2254
|
-
);
|
|
2255
|
-
}, deadlineMs);
|
|
2256
|
-
promise.then(
|
|
2257
|
-
(value) => {
|
|
2258
|
-
clearTimeout(timer);
|
|
2259
|
-
resolvePromise(value);
|
|
2260
|
-
},
|
|
2261
|
-
(err) => {
|
|
2262
|
-
clearTimeout(timer);
|
|
2263
|
-
rejectPromise(err);
|
|
2264
|
-
}
|
|
2265
|
-
);
|
|
2266
|
-
});
|
|
2267
|
-
}
|
|
2268
|
-
|
|
2269
|
-
// src/core/v3/detect-worker.ts
|
|
2270
|
-
var WORKER_PROTOCOL_VERSION = 1;
|
|
2271
|
-
function workerOffloadSupported() {
|
|
2272
|
-
return typeof OffscreenCanvas === "function" && typeof createImageBitmap === "function" && typeof Worker === "function";
|
|
2273
|
-
}
|
|
2274
|
-
function installWorkerDetectorHost(scope, deps = {}) {
|
|
2275
|
-
const create = deps.createDetector ?? createDetector;
|
|
2276
|
-
let detector = null;
|
|
2277
|
-
scope.onmessage = (event) => {
|
|
2278
|
-
const message = event.data;
|
|
2279
|
-
if (message.v !== WORKER_PROTOCOL_VERSION) {
|
|
2280
|
-
message.bitmap?.close?.();
|
|
2281
|
-
return;
|
|
2282
|
-
}
|
|
2283
|
-
if (message.type === "init") {
|
|
2284
|
-
void create(message.options).then((created) => {
|
|
2285
|
-
detector = created;
|
|
2286
|
-
const ready = {
|
|
2287
|
-
v: WORKER_PROTOCOL_VERSION,
|
|
2288
|
-
type: "ready",
|
|
2289
|
-
tier: created.tier,
|
|
2290
|
-
...created.fallbackReason !== void 0 ? { fallbackReason: created.fallbackReason } : {}
|
|
2291
|
-
};
|
|
2292
|
-
scope.postMessage(ready);
|
|
2293
|
-
});
|
|
2294
|
-
return;
|
|
2295
|
-
}
|
|
2296
|
-
if (message.type === "detect") {
|
|
2297
|
-
const respond = (observation) => {
|
|
2298
|
-
const result = {
|
|
2299
|
-
v: WORKER_PROTOCOL_VERSION,
|
|
2300
|
-
type: "result",
|
|
2301
|
-
id: message.id,
|
|
2302
|
-
observation
|
|
2303
|
-
};
|
|
2304
|
-
scope.postMessage(result);
|
|
2305
|
-
message.bitmap.close?.();
|
|
2306
|
-
};
|
|
2307
|
-
if (detector === null) {
|
|
2308
|
-
respond(null);
|
|
2309
|
-
return;
|
|
2310
|
-
}
|
|
2311
|
-
void detector.detect(message.bitmap, message.streamWidth, message.streamHeight).then(respond, () => respond(null));
|
|
2312
|
-
}
|
|
2313
|
-
};
|
|
2314
|
-
}
|
|
2315
|
-
if (typeof self !== "undefined" && typeof self.importScripts === "function") {
|
|
2316
|
-
installWorkerDetectorHost(self);
|
|
2317
|
-
}
|
|
2318
|
-
var WorkerDetectorProxy = class _WorkerDetectorProxy {
|
|
2319
|
-
constructor(worker, ready) {
|
|
2320
|
-
this.pending = /* @__PURE__ */ new Map();
|
|
2321
|
-
this.nextId = 1;
|
|
2322
|
-
this.worker = worker;
|
|
2323
|
-
this.tier = ready.tier;
|
|
2324
|
-
if (ready.fallbackReason !== void 0) this.fallbackReason = ready.fallbackReason;
|
|
2325
|
-
worker.addEventListener("message", (event) => {
|
|
2326
|
-
const message = event.data;
|
|
2327
|
-
if (message.v !== WORKER_PROTOCOL_VERSION || message.type !== "result") return;
|
|
2328
|
-
const resolve = this.pending.get(message.id);
|
|
2329
|
-
this.pending.delete(message.id);
|
|
2330
|
-
resolve?.(message.observation);
|
|
2331
|
-
});
|
|
2332
|
-
}
|
|
2333
|
-
static create(worker, options, readyTimeoutMs = 1e4) {
|
|
2334
|
-
return new Promise((resolve, reject) => {
|
|
2335
|
-
const timer = setTimeout(() => {
|
|
2336
|
-
worker.removeEventListener("message", onReady);
|
|
2337
|
-
worker.terminate();
|
|
2338
|
-
reject(new Error(`worker detector not ready within ${readyTimeoutMs}ms`));
|
|
2339
|
-
}, readyTimeoutMs);
|
|
2340
|
-
const onReady = (event) => {
|
|
2341
|
-
const message = event.data;
|
|
2342
|
-
if (message.v !== WORKER_PROTOCOL_VERSION || message.type !== "ready") return;
|
|
2343
|
-
clearTimeout(timer);
|
|
2344
|
-
worker.removeEventListener("message", onReady);
|
|
2345
|
-
resolve(new _WorkerDetectorProxy(worker, message));
|
|
2346
|
-
};
|
|
2347
|
-
worker.addEventListener("message", onReady);
|
|
2348
|
-
const init = { v: WORKER_PROTOCOL_VERSION, type: "init", options };
|
|
2349
|
-
worker.postMessage(init);
|
|
2350
|
-
});
|
|
2351
|
-
}
|
|
2352
|
-
async detect(source, sourceWidth, sourceHeight) {
|
|
2353
|
-
let bitmap;
|
|
2354
|
-
try {
|
|
2355
|
-
if (typeof ImageBitmap !== "undefined" && source instanceof ImageBitmap) {
|
|
2356
|
-
bitmap = source;
|
|
2357
|
-
} else if (typeof createImageBitmap === "function") {
|
|
2358
|
-
bitmap = await createImageBitmap(source);
|
|
2359
|
-
} else {
|
|
2360
|
-
bitmap = source;
|
|
2361
|
-
}
|
|
2362
|
-
} catch {
|
|
2363
|
-
return null;
|
|
2364
|
-
}
|
|
2365
|
-
const id = this.nextId;
|
|
2366
|
-
this.nextId += 1;
|
|
2367
|
-
return new Promise((resolve) => {
|
|
2368
|
-
this.pending.set(id, resolve);
|
|
2369
|
-
const message = {
|
|
2370
|
-
v: WORKER_PROTOCOL_VERSION,
|
|
2371
|
-
type: "detect",
|
|
2372
|
-
id,
|
|
2373
|
-
bitmap,
|
|
2374
|
-
streamWidth: sourceWidth,
|
|
2375
|
-
streamHeight: sourceHeight
|
|
2376
|
-
};
|
|
2377
|
-
this.worker.postMessage(message, [bitmap]);
|
|
2378
|
-
});
|
|
2379
|
-
}
|
|
2380
|
-
destroy() {
|
|
2381
|
-
for (const resolve of this.pending.values()) resolve(null);
|
|
2382
|
-
this.pending.clear();
|
|
2383
|
-
this.worker.terminate();
|
|
2384
|
-
}
|
|
2385
|
-
};
|
|
2386
|
-
|
|
2387
|
-
// src/core/v3/detect-scheduler.ts
|
|
2388
|
-
var STATS_WINDOW = 20;
|
|
2389
|
-
var DetectScheduler = class {
|
|
2390
|
-
constructor(options) {
|
|
2391
|
-
this.timer = null;
|
|
2392
|
-
this.frameCallbackId = null;
|
|
2393
|
-
this.running = false;
|
|
2394
|
-
this.busy = false;
|
|
2395
|
-
this.lastRunAt = Number.NEGATIVE_INFINITY;
|
|
2396
|
-
this.degradeCount = 0;
|
|
2397
|
-
this.durations = [];
|
|
2398
|
-
this.options = options;
|
|
2399
|
-
this.periodMs = 1e3 / options.cadenceHz;
|
|
2400
|
-
}
|
|
2401
|
-
start() {
|
|
2402
|
-
if (this.running) return;
|
|
2403
|
-
this.running = true;
|
|
2404
|
-
const video = this.options.video;
|
|
2405
|
-
if (typeof video?.requestVideoFrameCallback === "function") {
|
|
2406
|
-
const loop = () => {
|
|
2407
|
-
if (!this.running) return;
|
|
2408
|
-
this.frameCallbackId = video.requestVideoFrameCallback?.(() => {
|
|
2409
|
-
this.frameCallbackId = null;
|
|
2410
|
-
if (this.now() - this.lastRunAt >= this.periodMs) this.tick();
|
|
2411
|
-
loop();
|
|
2412
|
-
});
|
|
2413
|
-
};
|
|
2414
|
-
loop();
|
|
2415
|
-
return;
|
|
2416
|
-
}
|
|
2417
|
-
this.timer = setInterval(() => this.tick(), this.periodMs);
|
|
2418
|
-
}
|
|
2419
|
-
stop() {
|
|
2420
|
-
this.running = false;
|
|
2421
|
-
if (this.timer !== null) {
|
|
2422
|
-
clearInterval(this.timer);
|
|
2423
|
-
this.timer = null;
|
|
2424
|
-
}
|
|
2425
|
-
if (this.frameCallbackId !== null) {
|
|
2426
|
-
this.options.video?.cancelVideoFrameCallback?.(
|
|
2427
|
-
this.frameCallbackId
|
|
2428
|
-
);
|
|
2429
|
-
this.frameCallbackId = null;
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2432
|
-
stats() {
|
|
2433
|
-
const meanDetectMs = this.durations.length === 0 ? 0 : this.durations.reduce((sum, value) => sum + value, 0) / this.durations.length;
|
|
2434
|
-
return { degradeCount: this.degradeCount, meanDetectMs };
|
|
2435
|
-
}
|
|
2436
|
-
now() {
|
|
2437
|
-
return this.options.now?.() ?? performance.now();
|
|
2438
|
-
}
|
|
2439
|
-
tick() {
|
|
2440
|
-
if (!this.running) return;
|
|
2441
|
-
if (this.busy) {
|
|
2442
|
-
this.degradeCount += 1;
|
|
2443
|
-
return;
|
|
2444
|
-
}
|
|
2445
|
-
this.busy = true;
|
|
2446
|
-
this.lastRunAt = this.now();
|
|
2447
|
-
const startedAt = this.now();
|
|
2448
|
-
this.options.run().catch(() => void 0).finally(() => {
|
|
2449
|
-
this.busy = false;
|
|
2450
|
-
this.durations.push(this.now() - startedAt);
|
|
2451
|
-
if (this.durations.length > STATS_WINDOW) this.durations.shift();
|
|
2452
|
-
});
|
|
2453
|
-
}
|
|
2454
|
-
};
|
|
2455
|
-
|
|
2456
|
-
// src/core/v3/face-gate-v3.ts
|
|
2457
|
-
init_frame_quality();
|
|
2458
|
-
var V3_GATE_DEFAULTS = Object.freeze({
|
|
2459
|
-
Q_GOOD: 0.6,
|
|
2460
|
-
RISE_PER_S: 0.9,
|
|
2461
|
-
FALL_PER_S: 0.25,
|
|
2462
|
-
CENTER_FALLOFF_IOD_MULTIPLE: 1.35,
|
|
2463
|
-
BOX_EYE_OFFSET_FACTOR: 0.12,
|
|
2464
|
-
IOD_BOX_PROXY_FACTOR: 0.42,
|
|
2465
|
-
SIZE_IDEAL_MIN: 0.12,
|
|
2466
|
-
SIZE_IDEAL_MAX: 0.3,
|
|
2467
|
-
SIZE_HARD_MIN: 0.08,
|
|
2468
|
-
SIZE_HARD_MAX: 0.45,
|
|
2469
|
-
ROLL_MAX_DEG: 25,
|
|
2470
|
-
MOTION_VELOCITY_IOD_PER_S: 0.5,
|
|
2471
|
-
MOTION_TAU_S: 0.4,
|
|
2472
|
-
MOTION_DEADBAND_IOD_PER_S: 0.1,
|
|
2473
|
-
READY_WINDOW_MS: 500,
|
|
2474
|
-
// (READY_MEAN_Q, the former 0.75 window MEAN, is gone — 2026-10-17 field
|
|
2475
|
-
// bug: a steady q anywhere in [Q_GOOD, 0.75) saturated p at 1, passed every
|
|
2476
|
-
// guide ("hold still") and could never reach the mean, so the ring sat full
|
|
2477
|
-
// and green with nothing recording. Readiness now checks the window against
|
|
2478
|
-
// Q_GOOD, which keeps the one-flaky-tick forgiveness and the second-bad-tick
|
|
2479
|
-
// block without a dead band above the rise threshold.)
|
|
2480
|
-
GUIDE_SIZE: 0.6,
|
|
2481
|
-
GUIDE_CENTERING: 0.6,
|
|
2482
|
-
GUIDE_POSE: 0.6,
|
|
2483
|
-
GUIDE_EXPOSURE: 0.6,
|
|
2484
|
-
GUIDE_SHARPNESS: 0.35,
|
|
2485
|
-
GUIDE_MOTION: 0.5
|
|
2486
|
-
});
|
|
2487
|
-
var clamp012 = (value) => Math.min(1, Math.max(0, value));
|
|
2488
|
-
var FaceGateV3 = class {
|
|
2489
|
-
constructor(overrides = {}) {
|
|
2490
|
-
this.p = 0;
|
|
2491
|
-
this.lastTickAt = null;
|
|
2492
|
-
this.lastCenter = null;
|
|
2493
|
-
this.velocityEma = 0;
|
|
2494
|
-
this.qWindow = [];
|
|
2495
|
-
/** Which side of the ideal band the last size measurement fell on. */
|
|
2496
|
-
this.lastSizeDirection = "ideal";
|
|
2497
|
-
this.t = { ...V3_GATE_DEFAULTS, ...overrides };
|
|
2498
|
-
}
|
|
2499
|
-
reset() {
|
|
2500
|
-
this.p = 0;
|
|
2501
|
-
this.lastTickAt = null;
|
|
2502
|
-
this.lastCenter = null;
|
|
2503
|
-
this.velocityEma = 0;
|
|
2504
|
-
this.qWindow = [];
|
|
2505
|
-
this.lastSizeDirection = "ideal";
|
|
2506
|
-
}
|
|
2507
|
-
tick(input) {
|
|
2508
|
-
const { observation, quality, timestampMs } = input;
|
|
2509
|
-
const dtSeconds = this.lastTickAt === null ? 0 : Math.max(0, (timestampMs - this.lastTickAt) / 1e3);
|
|
2510
|
-
this.lastTickAt = timestampMs;
|
|
2511
|
-
const factors = {
|
|
2512
|
-
presence: 0,
|
|
2513
|
-
centering: 0,
|
|
2514
|
-
size: 0,
|
|
2515
|
-
pose: 0,
|
|
2516
|
-
sharpness: clamp012(quality.sharpness),
|
|
2517
|
-
exposure: clamp012(quality.exposure),
|
|
2518
|
-
motion: 1
|
|
2519
|
-
};
|
|
2520
|
-
if (observation !== null) {
|
|
2521
|
-
factors.presence = 1;
|
|
2522
|
-
const geometry = this.geometryOf(observation, input);
|
|
2523
|
-
factors.centering = geometry.centering;
|
|
2524
|
-
factors.size = geometry.size;
|
|
2525
|
-
factors.pose = geometry.pose;
|
|
2526
|
-
factors.motion = this.motionOf(
|
|
2527
|
-
geometry.center,
|
|
2528
|
-
geometry.iod,
|
|
2529
|
-
timestampMs
|
|
2530
|
-
);
|
|
2531
|
-
} else {
|
|
2532
|
-
this.lastCenter = null;
|
|
2533
|
-
this.velocityEma = 0;
|
|
2534
|
-
}
|
|
2535
|
-
const q = Math.min(
|
|
2536
|
-
factors.presence,
|
|
2537
|
-
factors.centering,
|
|
2538
|
-
factors.size,
|
|
2539
|
-
factors.pose,
|
|
2540
|
-
factors.sharpness,
|
|
2541
|
-
factors.exposure,
|
|
2542
|
-
factors.motion
|
|
2543
|
-
);
|
|
2544
|
-
this.p = clamp012(
|
|
2545
|
-
this.p + (q >= this.t.Q_GOOD ? this.t.RISE_PER_S * q : -this.t.FALL_PER_S * (1 - q)) * dtSeconds
|
|
2546
|
-
);
|
|
2547
|
-
this.qWindow.push({ at: timestampMs, q });
|
|
2548
|
-
this.qWindow = this.qWindow.filter(
|
|
2549
|
-
(entry) => entry.at >= timestampMs - this.t.READY_WINDOW_MS
|
|
2550
|
-
);
|
|
2551
|
-
let windowSamples = this.qWindow.map((entry) => entry.q);
|
|
2552
|
-
if (windowSamples.length >= 3) {
|
|
2553
|
-
const worst = Math.min(...windowSamples);
|
|
2554
|
-
const worstIndex = windowSamples.indexOf(worst);
|
|
2555
|
-
windowSamples = windowSamples.filter((_2, index) => index !== worstIndex);
|
|
2556
|
-
}
|
|
2557
|
-
const ready = this.p >= 1 && windowSamples.every((sample) => sample >= this.t.Q_GOOD);
|
|
2558
|
-
return {
|
|
2559
|
-
at: timestampMs,
|
|
2560
|
-
q,
|
|
2561
|
-
p: this.p,
|
|
2562
|
-
factors,
|
|
2563
|
-
guidanceKey: this.guidanceOf(factors, quality, q),
|
|
2564
|
-
ready,
|
|
2565
|
-
accepted: q >= this.t.Q_GOOD
|
|
2566
|
-
};
|
|
2567
|
-
}
|
|
2568
|
-
geometryOf(observation, input) {
|
|
2569
|
-
const { box, eyes } = observation;
|
|
2570
|
-
const frameCenter = { x: input.frameWidth / 2, y: input.frameHeight / 2 };
|
|
2571
|
-
const shortSide = Math.min(input.frameWidth, input.frameHeight);
|
|
2572
|
-
const center = eyes !== void 0 ? {
|
|
2573
|
-
x: (eyes.left.x + eyes.right.x) / 2,
|
|
2574
|
-
y: (eyes.left.y + eyes.right.y) / 2
|
|
2575
|
-
} : {
|
|
2576
|
-
x: box.x + box.width / 2,
|
|
2577
|
-
y: box.y + box.height / 2 - this.t.BOX_EYE_OFFSET_FACTOR * box.height
|
|
2578
|
-
};
|
|
2579
|
-
const iod = eyes !== void 0 ? Math.hypot(eyes.right.x - eyes.left.x, eyes.right.y - eyes.left.y) : this.t.IOD_BOX_PROXY_FACTOR * box.width;
|
|
2580
|
-
const distance = Math.hypot(
|
|
2581
|
-
center.x - frameCenter.x,
|
|
2582
|
-
center.y - frameCenter.y
|
|
2583
|
-
);
|
|
2584
|
-
const centering = iod <= 0 ? 0 : 1 - clamp012(distance / (this.t.CENTER_FALLOFF_IOD_MULTIPLE * iod));
|
|
2585
|
-
const ratio = shortSide > 0 ? iod / shortSide : 0;
|
|
2586
|
-
this.lastSizeDirection = ratio < this.t.SIZE_IDEAL_MIN ? "far" : ratio > this.t.SIZE_IDEAL_MAX ? "close" : "ideal";
|
|
2587
|
-
let size;
|
|
2588
|
-
if (ratio < this.t.SIZE_HARD_MIN || ratio > this.t.SIZE_HARD_MAX) {
|
|
2589
|
-
size = 0;
|
|
2590
|
-
} else if (ratio < this.t.SIZE_IDEAL_MIN) {
|
|
2591
|
-
size = (ratio - this.t.SIZE_HARD_MIN) / (this.t.SIZE_IDEAL_MIN - this.t.SIZE_HARD_MIN);
|
|
2592
|
-
} else if (ratio > this.t.SIZE_IDEAL_MAX) {
|
|
2593
|
-
size = (this.t.SIZE_HARD_MAX - ratio) / (this.t.SIZE_HARD_MAX - this.t.SIZE_IDEAL_MAX);
|
|
2594
|
-
} else {
|
|
2595
|
-
size = 1;
|
|
2596
|
-
}
|
|
2597
|
-
let pose = 1;
|
|
2598
|
-
if (eyes !== void 0) {
|
|
2599
|
-
const rollDeg = Math.abs(
|
|
2600
|
-
Math.atan2(eyes.right.y - eyes.left.y, eyes.right.x - eyes.left.x)
|
|
2601
|
-
) * 180 / Math.PI;
|
|
2602
|
-
pose = 1 - clamp012(rollDeg / this.t.ROLL_MAX_DEG);
|
|
2603
|
-
}
|
|
2604
|
-
return { center, iod, centering, size, pose };
|
|
2605
|
-
}
|
|
2606
|
-
motionOf(center, iod, at) {
|
|
2607
|
-
const previous = this.lastCenter;
|
|
2608
|
-
this.lastCenter = { ...center, at };
|
|
2609
|
-
if (previous === null || iod <= 0) return 1;
|
|
2610
|
-
const dtSeconds = (at - previous.at) / 1e3;
|
|
2611
|
-
if (dtSeconds <= 0) return 1;
|
|
2612
|
-
let velocityIodPerS = Math.hypot(center.x - previous.x, center.y - previous.y) / iod / dtSeconds;
|
|
2613
|
-
if (velocityIodPerS <= this.t.MOTION_DEADBAND_IOD_PER_S)
|
|
2614
|
-
velocityIodPerS = 0;
|
|
2615
|
-
const alpha = 1 - Math.exp(-dtSeconds / this.t.MOTION_TAU_S);
|
|
2616
|
-
this.velocityEma = alpha * velocityIodPerS + (1 - alpha) * this.velocityEma;
|
|
2617
|
-
return 1 - clamp012(this.velocityEma / this.t.MOTION_VELOCITY_IOD_PER_S);
|
|
2618
|
-
}
|
|
2619
|
-
/** Lowest failing factor speaks — fixed priority (diagnostic, incl. light):
|
|
2620
|
-
* presence → size → centering → pose → exposure → sharpness → hold.
|
|
2621
|
-
*
|
|
2622
|
-
* HONESTY FALLBACK (operator ruling 2026-07-12): when every factor clears
|
|
2623
|
-
* its guide threshold but q (= min factor) is still below Q_GOOD — i.e.
|
|
2624
|
-
* progress is DRAINING — "hold still" would be a lie ("Perfect" while
|
|
2625
|
-
* nothing advances; the field symptom was users discovering by accident
|
|
2626
|
-
* that only leaning in fires the gate). In that state the WORST factor
|
|
2627
|
-
* speaks even though it passed its guide level, so the user always hears
|
|
2628
|
-
* the change that actually unblocks recording. */
|
|
2629
|
-
guidanceOf(factors, quality, q) {
|
|
2630
|
-
if (factors.presence <= 0) return "fcv3.no_face";
|
|
2631
|
-
if (factors.size < this.t.GUIDE_SIZE) return this.sizeGuidance();
|
|
2632
|
-
if (factors.centering < this.t.GUIDE_CENTERING) return "fcv3.center_face";
|
|
2633
|
-
if (factors.pose < this.t.GUIDE_POSE) return "fcv3.straighten";
|
|
2634
|
-
if (factors.exposure < this.t.GUIDE_EXPOSURE)
|
|
2635
|
-
return this.exposureGuidance(quality);
|
|
2636
|
-
if (factors.sharpness < this.t.GUIDE_SHARPNESS) return "fcv3.steady_camera";
|
|
2637
|
-
if (factors.motion < this.t.GUIDE_MOTION) return "fcv3.hold_still";
|
|
2638
|
-
if (q < this.t.Q_GOOD) {
|
|
2639
|
-
const coachable = [
|
|
2640
|
-
["size", this.sizeGuidance()],
|
|
2641
|
-
["centering", "fcv3.center_face"],
|
|
2642
|
-
["pose", "fcv3.straighten"],
|
|
2643
|
-
["exposure", this.exposureGuidance(quality)],
|
|
2644
|
-
["sharpness", "fcv3.steady_camera"],
|
|
2645
|
-
["motion", "fcv3.hold_still"]
|
|
2646
|
-
];
|
|
2647
|
-
let worst = coachable[0];
|
|
2648
|
-
for (const entry of coachable) {
|
|
2649
|
-
if (factors[entry[0]] < factors[worst[0]]) worst = entry;
|
|
2650
|
-
}
|
|
2651
|
-
return worst[1];
|
|
2652
|
-
}
|
|
2653
|
-
return "fcv3.hold_still";
|
|
2654
|
-
}
|
|
2655
|
-
/** Direction from the size band set THIS tick (never inverted at the hard
|
|
2656
|
-
* limits: past SIZE_HARD_MAX the direction is 'close' → move_back). */
|
|
2657
|
-
sizeGuidance() {
|
|
2658
|
-
return this.lastSizeDirection === "far" ? "fcv3.come_closer" : "fcv3.move_back";
|
|
2659
|
-
}
|
|
2660
|
-
/** Band midpoint — direction only (tracks EXPOSURE_BAND automatically). */
|
|
2661
|
-
exposureGuidance(quality) {
|
|
2662
|
-
const bandMid = (EXPOSURE_BAND[0] + EXPOSURE_BAND[1]) / 2;
|
|
2663
|
-
return quality.meanLuma < bandMid ? "fcv3.more_light" : "fcv3.too_bright";
|
|
2664
|
-
}
|
|
2665
|
-
};
|
|
2666
|
-
|
|
2667
|
-
// src/core/v3/capture-session-v3.ts
|
|
2668
|
-
init_frame_quality();
|
|
2669
|
-
var KEYFRAME_CANDIDATES = 4;
|
|
2670
|
-
var KEYFRAME_WINDOW_MS = 600;
|
|
2671
|
-
var NEUTRAL_QUALITY = { sharpness: 1, exposure: 1, meanLuma: 128 };
|
|
2672
|
-
var CaptureSessionV3 = class {
|
|
2673
|
-
constructor(options) {
|
|
2674
|
-
this.handlers = /* @__PURE__ */ new Map();
|
|
2675
|
-
this.stateValue = "idle";
|
|
2676
|
-
this.attempt = 0;
|
|
2677
|
-
this.stream = null;
|
|
2678
|
-
this.detector = null;
|
|
2679
|
-
this.scheduler = null;
|
|
2680
|
-
this.gate = null;
|
|
2681
|
-
this.profiler = null;
|
|
2682
|
-
this.lastGuidanceKey = null;
|
|
2683
|
-
this.positioningStartedAt = 0;
|
|
2684
|
-
// Face-capture timing marks of the CURRENT attempt (engine clock; reset per run).
|
|
2685
|
-
this.cameraRequestedAt = null;
|
|
2686
|
-
this.cameraReadyAt = null;
|
|
2687
|
-
this.faceAcquiredAt = null;
|
|
2688
|
-
this.captureCompleteAt = null;
|
|
2689
|
-
this.fired = false;
|
|
2690
|
-
this.carriedDegrade = 0;
|
|
2691
|
-
this.carriedMeans = [];
|
|
2692
|
-
/** Reused downscale canvas — falls back to the raw video where canvas 2D
|
|
2693
|
-
* is unavailable (jsdom) so tests and exotic hosts keep detecting. */
|
|
2694
|
-
this.snapshotCanvas = null;
|
|
2695
|
-
this.options = options;
|
|
2696
|
-
this.facing = options.facingMode ?? "user";
|
|
2697
|
-
}
|
|
2698
|
-
get state() {
|
|
2699
|
-
return this.stateValue;
|
|
2700
|
-
}
|
|
2701
|
-
on(event, handler) {
|
|
2702
|
-
let set = this.handlers.get(event);
|
|
2703
|
-
if (!set) {
|
|
2704
|
-
set = /* @__PURE__ */ new Set();
|
|
2705
|
-
this.handlers.set(event, set);
|
|
2706
|
-
}
|
|
2707
|
-
set.add(handler);
|
|
2708
|
-
return () => set?.delete(handler);
|
|
2709
|
-
}
|
|
2710
|
-
emit(event, payload) {
|
|
2711
|
-
for (const handler of [...this.handlers.get(event) ?? []]) {
|
|
2712
|
-
handler(payload);
|
|
2713
|
-
}
|
|
2714
|
-
}
|
|
2715
|
-
async start(options = {}) {
|
|
2716
|
-
if (options.facingMode !== void 0) this.facing = options.facingMode;
|
|
2717
|
-
this.attempt += 1;
|
|
2718
|
-
await this.run(this.attempt);
|
|
2719
|
-
}
|
|
2720
|
-
/** The camera the current/next attempt requests (start option, switchCamera toggles). */
|
|
2721
|
-
get facingMode() {
|
|
2722
|
-
return this.facing;
|
|
2723
|
-
}
|
|
2724
|
-
/** Full reset + camera restart — the documented recovery path (v1 parity). */
|
|
2725
|
-
retry() {
|
|
2726
|
-
this.teardownAttempt();
|
|
2727
|
-
void this.start();
|
|
2728
|
-
}
|
|
2729
|
-
switchCamera() {
|
|
2730
|
-
this.facing = this.facing === "user" ? "environment" : "user";
|
|
2731
|
-
this.teardownAttempt();
|
|
2732
|
-
void this.start();
|
|
2733
|
-
}
|
|
2734
|
-
destroy() {
|
|
2735
|
-
this.attempt += 1;
|
|
2736
|
-
this.teardownAttempt();
|
|
2737
|
-
this.detector?.destroy();
|
|
2738
|
-
this.detector = null;
|
|
2739
|
-
this.options.video.srcObject = null;
|
|
2740
|
-
}
|
|
2741
|
-
// ── attempt lifecycle ──────────────────────────────────────────────────────
|
|
2742
|
-
async run(attempt) {
|
|
2743
|
-
const { options } = this;
|
|
2744
|
-
this.fired = false;
|
|
2745
|
-
this.lastGuidanceKey = null;
|
|
2746
|
-
this.carriedDegrade = 0;
|
|
2747
|
-
this.carriedMeans = [];
|
|
2748
|
-
this.cameraRequestedAt = null;
|
|
2749
|
-
this.cameraReadyAt = null;
|
|
2750
|
-
this.faceAcquiredAt = null;
|
|
2751
|
-
this.captureCompleteAt = null;
|
|
2752
|
-
this.setState("preflight");
|
|
2753
|
-
const preflight = cameraPreflight();
|
|
2754
|
-
if (preflight !== "OK") {
|
|
2755
|
-
this.fail(preflight === "INSECURE_CONTEXT" ? "insecure" : "unsupported");
|
|
2756
|
-
return;
|
|
2757
|
-
}
|
|
2758
|
-
this.setState("acquiring");
|
|
2759
|
-
const getUserMedia = options.getUserMedia ?? ((constraints) => navigator.mediaDevices.getUserMedia(constraints));
|
|
2760
|
-
let stream;
|
|
2761
|
-
this.cameraRequestedAt = this.now();
|
|
2762
|
-
try {
|
|
2763
|
-
stream = await getUserMedia(buildCaptureConstraints(detectIsMobile(), this.facing));
|
|
2764
|
-
} catch (err) {
|
|
2765
|
-
if (attempt !== this.attempt) return;
|
|
2766
|
-
this.fail(mapGetUserMediaError(err));
|
|
2767
|
-
return;
|
|
2768
|
-
}
|
|
2769
|
-
if (attempt !== this.attempt) {
|
|
2770
|
-
for (const track of stream.getTracks()) track.stop();
|
|
2771
|
-
return;
|
|
2772
|
-
}
|
|
2773
|
-
this.stream = stream;
|
|
2774
|
-
options.video.srcObject = stream;
|
|
2775
|
-
await options.video.play().catch(() => void 0);
|
|
2776
|
-
if (attempt !== this.attempt) return;
|
|
2777
|
-
this.cameraReadyAt = this.now();
|
|
2778
|
-
if (this.detector === null) {
|
|
2779
|
-
const ladderOptions = {
|
|
2780
|
-
...options.v3AssetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.v3AssetBaseUrl } : {}
|
|
2781
|
-
};
|
|
2782
|
-
const create = options.createDetector ?? (options.createWorker !== void 0 && workerOffloadSupported() ? async () => {
|
|
2783
|
-
try {
|
|
2784
|
-
return await WorkerDetectorProxy.create(
|
|
2785
|
-
options.createWorker(),
|
|
2786
|
-
ladderOptions
|
|
2787
|
-
);
|
|
2788
|
-
} catch {
|
|
2789
|
-
return createDetector(ladderOptions);
|
|
2790
|
-
}
|
|
2791
|
-
} : () => createDetector(ladderOptions));
|
|
2792
|
-
let created;
|
|
2793
|
-
try {
|
|
2794
|
-
created = await create();
|
|
2795
|
-
} catch (err) {
|
|
2796
|
-
if (attempt === this.attempt) {
|
|
2797
|
-
this.fail(
|
|
2798
|
-
"models",
|
|
2799
|
-
err instanceof DetectorLadderError ? err.fallbackReason : describeError2(err)
|
|
2800
|
-
);
|
|
2801
|
-
}
|
|
2802
|
-
return;
|
|
2803
|
-
}
|
|
2804
|
-
if (attempt !== this.attempt) {
|
|
2805
|
-
created.destroy();
|
|
2806
|
-
return;
|
|
2807
|
-
}
|
|
2808
|
-
this.detector = created;
|
|
2809
|
-
this.options.telemetry?.({
|
|
2810
|
-
type: "detector",
|
|
2811
|
-
tier: created.tier,
|
|
2812
|
-
...created.fallbackReason !== void 0 ? { fallbackReason: created.fallbackReason } : {}
|
|
2813
|
-
});
|
|
2814
|
-
}
|
|
2815
|
-
const probe = probeDevice();
|
|
2816
|
-
const staticClass = options.deviceClass ?? classifyDevice(probe);
|
|
2817
|
-
this.profiler = new DeviceProfiler(staticClass);
|
|
2818
|
-
this.options.telemetry?.({
|
|
2819
|
-
type: "profile",
|
|
2820
|
-
staticClass,
|
|
2821
|
-
activeClass: this.profiler.deviceClass,
|
|
2822
|
-
simd: probe.simd
|
|
2823
|
-
});
|
|
2824
|
-
this.gate = new FaceGateV3(options.thresholds ?? {});
|
|
2825
|
-
this.setState("positioning");
|
|
2826
|
-
this.positioningStartedAt = this.now();
|
|
2827
|
-
this.startScheduler(attempt);
|
|
2828
|
-
}
|
|
2829
|
-
/** Chrome hint (Task 11): 'reduced' on LOW even without the media query. */
|
|
2830
|
-
get animations() {
|
|
2831
|
-
return this.profiler?.profile.animations ?? "full";
|
|
2832
|
-
}
|
|
2833
|
-
startScheduler(attempt) {
|
|
2834
|
-
const profiler = this.profiler;
|
|
2835
|
-
if (profiler === null) return;
|
|
2836
|
-
if (this.scheduler !== null) {
|
|
2837
|
-
const stats = this.scheduler.stats();
|
|
2838
|
-
this.carriedDegrade += stats.degradeCount;
|
|
2839
|
-
if (stats.meanDetectMs > 0) this.carriedMeans.push(stats.meanDetectMs);
|
|
2840
|
-
this.scheduler.stop();
|
|
2841
|
-
}
|
|
2842
|
-
const scheduler = new DetectScheduler({
|
|
2843
|
-
cadenceHz: profiler.profile.cadenceHz,
|
|
2844
|
-
video: this.options.video,
|
|
2845
|
-
...this.options.now !== void 0 ? { now: this.options.now } : {},
|
|
2846
|
-
run: () => this.detectTick(attempt)
|
|
2847
|
-
});
|
|
2848
|
-
this.scheduler = scheduler;
|
|
2849
|
-
scheduler.start();
|
|
2850
|
-
}
|
|
2851
|
-
async detectTick(attempt) {
|
|
2852
|
-
const { detector, gate, profiler } = this;
|
|
2853
|
-
const video = this.options.video;
|
|
2854
|
-
if (attempt !== this.attempt || detector === null || gate === null || profiler === null)
|
|
2855
|
-
return;
|
|
2856
|
-
if (this.fired || video.videoWidth === 0) return;
|
|
2857
|
-
const before = profiler.deviceClass;
|
|
2858
|
-
const t0 = this.now();
|
|
2859
|
-
const source = this.options.makeSnapshot?.(video, profiler.profile.inputWidth) ?? this.defaultSnapshot(video, profiler.profile.inputWidth);
|
|
2860
|
-
let observation = null;
|
|
2861
|
-
try {
|
|
2862
|
-
observation = await detector.detect(source, video.videoWidth, video.videoHeight);
|
|
2863
|
-
} catch {
|
|
2864
|
-
observation = null;
|
|
2865
|
-
}
|
|
2866
|
-
if (attempt !== this.attempt || this.fired) return;
|
|
2867
|
-
profiler.record(this.now() - t0);
|
|
2868
|
-
if (profiler.deviceClass !== before) this.startScheduler(attempt);
|
|
2869
|
-
const quality = observation === null ? NEUTRAL_QUALITY : this.options.measureQuality?.(video, observation.box) ?? this.defaultQuality(video, observation.box);
|
|
2870
|
-
const tick = gate.tick({
|
|
2871
|
-
timestampMs: this.now(),
|
|
2872
|
-
observation,
|
|
2873
|
-
quality,
|
|
2874
|
-
frameWidth: video.videoWidth,
|
|
2875
|
-
frameHeight: video.videoHeight
|
|
2876
|
-
});
|
|
2877
|
-
if (tick.accepted && this.faceAcquiredAt === null) this.faceAcquiredAt = tick.at;
|
|
2878
|
-
this.emit("checks", {
|
|
2879
|
-
engine: "v3",
|
|
2880
|
-
progress: tick.p,
|
|
2881
|
-
q: tick.q,
|
|
2882
|
-
factors: tick.factors,
|
|
2883
|
-
attempt
|
|
2884
|
-
});
|
|
2885
|
-
if (tick.guidanceKey !== this.lastGuidanceKey) {
|
|
2886
|
-
this.lastGuidanceKey = tick.guidanceKey;
|
|
2887
|
-
this.emit("guidance", { key: tick.guidanceKey });
|
|
2888
|
-
}
|
|
2889
|
-
if (tick.ready && this.stateValue === "positioning") {
|
|
2890
|
-
this.fired = true;
|
|
2891
|
-
this.captureCompleteAt = tick.at;
|
|
2892
|
-
void this.fire(attempt);
|
|
2893
|
-
}
|
|
2894
|
-
}
|
|
2895
|
-
async fire(attempt) {
|
|
2896
|
-
const { options } = this;
|
|
2897
|
-
const scheduler = this.scheduler;
|
|
2898
|
-
scheduler?.stop();
|
|
2899
|
-
const stats = scheduler?.stats() ?? { degradeCount: 0, meanDetectMs: 0 };
|
|
2900
|
-
const means = [...this.carriedMeans, ...stats.meanDetectMs > 0 ? [stats.meanDetectMs] : []];
|
|
2901
|
-
options.telemetry?.({
|
|
2902
|
-
type: "fired",
|
|
2903
|
-
timeToGateMs: this.now() - this.positioningStartedAt,
|
|
2904
|
-
degradeCount: this.carriedDegrade + stats.degradeCount,
|
|
2905
|
-
meanDetectMs: means.length === 0 ? 0 : means.reduce((sum, value) => sum + value, 0) / means.length
|
|
2906
|
-
});
|
|
2907
|
-
const mode = options.mode ?? "clip";
|
|
2908
|
-
const bestOf = this.collectKeyframes(attempt);
|
|
2909
|
-
if (mode === "photo") {
|
|
2910
|
-
const best2 = await bestOf;
|
|
2911
|
-
if (attempt !== this.attempt) return;
|
|
2912
|
-
if (best2 === null) {
|
|
2913
|
-
this.fail("generic");
|
|
2914
|
-
return;
|
|
2915
|
-
}
|
|
2916
|
-
this.finishCapture({ keyframe: best2.blob, previewDataUrl: best2.previewDataUrl });
|
|
2917
|
-
return;
|
|
2918
|
-
}
|
|
2919
|
-
this.setState("recording");
|
|
2920
|
-
const recorder = options.createRecorder?.() ?? new ClipRecorder();
|
|
2921
|
-
const stream = this.stream;
|
|
2922
|
-
if (stream === null) {
|
|
2923
|
-
this.fail("generic");
|
|
2924
|
-
return;
|
|
2925
|
-
}
|
|
2926
|
-
let frozenBest = null;
|
|
2927
|
-
void bestOf.then(
|
|
2928
|
-
(candidate) => {
|
|
2929
|
-
frozenBest = candidate;
|
|
2930
|
-
},
|
|
2931
|
-
() => {
|
|
2932
|
-
}
|
|
2933
|
-
);
|
|
2934
|
-
const onRecordingStopped = () => {
|
|
2935
|
-
if (attempt !== this.attempt) return;
|
|
2936
|
-
if (frozenBest !== null) {
|
|
2937
|
-
this.emit("frozen", { previewDataUrl: frozenBest.previewDataUrl });
|
|
2938
|
-
}
|
|
2939
|
-
};
|
|
2940
|
-
let clip;
|
|
2941
|
-
let best;
|
|
2942
|
-
try {
|
|
2943
|
-
[clip, best] = await Promise.all([recorder.record(stream, onRecordingStopped), bestOf]);
|
|
2944
|
-
} catch (err) {
|
|
2945
|
-
if (attempt !== this.attempt) return;
|
|
2946
|
-
const kind = err.kind;
|
|
2947
|
-
this.fail(kind === "cliptoolarge" ? "cliptoolarge" : "recorder");
|
|
2948
|
-
return;
|
|
2949
|
-
}
|
|
2950
|
-
if (attempt !== this.attempt) return;
|
|
2951
|
-
if (isClipTooLarge(clip.blob)) {
|
|
2952
|
-
this.fail("cliptoolarge");
|
|
2953
|
-
return;
|
|
2954
|
-
}
|
|
2955
|
-
if (best === null) {
|
|
2956
|
-
this.fail("generic");
|
|
2957
|
-
return;
|
|
2958
|
-
}
|
|
2959
|
-
this.finishCapture({
|
|
2960
|
-
clip: clip.blob,
|
|
2961
|
-
clipMimeType: clip.mimeType,
|
|
2962
|
-
keyframe: best.blob,
|
|
2963
|
-
previewDataUrl: best.previewDataUrl
|
|
2964
|
-
});
|
|
2965
|
-
}
|
|
2966
|
-
/** Up to KEYFRAME_CANDIDATES mirrored frames over KEYFRAME_WINDOW_MS —
|
|
2967
|
-
* sharpest wins (reuses frame-quality.sharpness via the capture seam). */
|
|
2968
|
-
async collectKeyframes(attempt) {
|
|
2969
|
-
const capture = this.options.captureKeyframe ?? ((video) => this.defaultKeyframe(video));
|
|
2970
|
-
const stepMs = KEYFRAME_WINDOW_MS / (KEYFRAME_CANDIDATES - 1);
|
|
2971
|
-
let best = null;
|
|
2972
|
-
for (let i2 = 0; i2 < KEYFRAME_CANDIDATES; i2 += 1) {
|
|
2973
|
-
if (attempt !== this.attempt) return best;
|
|
2974
|
-
try {
|
|
2975
|
-
const candidate = await capture(this.options.video);
|
|
2976
|
-
if (best === null || candidate.sharpness > best.sharpness) best = candidate;
|
|
2977
|
-
} catch {
|
|
2978
|
-
}
|
|
2979
|
-
if (i2 < KEYFRAME_CANDIDATES - 1) await delay(stepMs);
|
|
2980
|
-
}
|
|
2981
|
-
return best;
|
|
2982
|
-
}
|
|
2983
|
-
finishCapture(artifacts) {
|
|
2984
|
-
this.stopTracks();
|
|
2985
|
-
this.setState("captured");
|
|
2986
|
-
const timing = this.captureTiming();
|
|
2987
|
-
this.emit("artifacts", timing === void 0 ? artifacts : { ...artifacts, timing });
|
|
2988
|
-
}
|
|
2989
|
-
/** The attempt's capture-phase timing (whole ms, ≥ 0); undefined when no mark landed. */
|
|
2990
|
-
captureTiming() {
|
|
2991
|
-
const span = (from, to) => from !== null && to !== null ? Math.max(0, Math.round(to - from)) : void 0;
|
|
2992
|
-
const cameraReadyMs = span(this.cameraRequestedAt, this.cameraReadyAt);
|
|
2993
|
-
const faceAcquireMs = span(this.cameraReadyAt, this.faceAcquiredAt);
|
|
2994
|
-
const captureMs = span(this.faceAcquiredAt, this.captureCompleteAt);
|
|
2995
|
-
const timing = {
|
|
2996
|
-
...cameraReadyMs !== void 0 ? { cameraReadyMs } : {},
|
|
2997
|
-
...faceAcquireMs !== void 0 ? { faceAcquireMs } : {},
|
|
2998
|
-
...captureMs !== void 0 ? { captureMs } : {}
|
|
2999
|
-
};
|
|
3000
|
-
return Object.keys(timing).length > 0 ? timing : void 0;
|
|
3001
|
-
}
|
|
3002
|
-
// ── defaults for the canvas-bound seams ────────────────────────────────────
|
|
3003
|
-
async defaultKeyframe(video) {
|
|
3004
|
-
const canvas = document.createElement("canvas");
|
|
3005
|
-
const blob = await captureMirroredKeyframe(video, canvas);
|
|
3006
|
-
const previewDataUrl = canvas.toDataURL("image/jpeg", 0.92);
|
|
3007
|
-
return { blob, previewDataUrl, sharpness: this.centerSharpness(video) };
|
|
3008
|
-
}
|
|
3009
|
-
defaultSnapshot(video, targetWidth) {
|
|
3010
|
-
try {
|
|
3011
|
-
const canvas = this.snapshotCanvas ?? document.createElement("canvas");
|
|
3012
|
-
const scale = targetWidth / Math.max(1, video.videoWidth);
|
|
3013
|
-
const width = Math.max(1, Math.round(video.videoWidth * scale));
|
|
3014
|
-
const height = Math.max(1, Math.round(video.videoHeight * scale));
|
|
3015
|
-
const ctx = canvas.getContext("2d");
|
|
3016
|
-
if (ctx === null) return video;
|
|
3017
|
-
if (canvas.width !== width) canvas.width = width;
|
|
3018
|
-
if (canvas.height !== height) canvas.height = height;
|
|
3019
|
-
ctx.drawImage(video, 0, 0, width, height);
|
|
3020
|
-
this.snapshotCanvas = canvas;
|
|
3021
|
-
return canvas;
|
|
3022
|
-
} catch {
|
|
3023
|
-
return video;
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
defaultQuality(video, box) {
|
|
3027
|
-
const crop = cropImageData(video, box, 96);
|
|
3028
|
-
if (crop === null) return NEUTRAL_QUALITY;
|
|
3029
|
-
return { sharpness: sharpness(crop), exposure: exposure(crop), meanLuma: meanLuma(crop) };
|
|
3030
|
-
}
|
|
3031
|
-
centerSharpness(video) {
|
|
3032
|
-
const size = intrinsicSize(video);
|
|
3033
|
-
const crop = cropImageData(
|
|
3034
|
-
video,
|
|
3035
|
-
{ x: size.width * 0.25, y: size.height * 0.25, width: size.width * 0.5, height: size.height * 0.5 },
|
|
3036
|
-
96
|
|
3037
|
-
);
|
|
3038
|
-
return crop === null ? 0 : sharpness(crop);
|
|
3039
|
-
}
|
|
3040
|
-
// ── teardown ──────────────────────────────────────────────────────────────
|
|
3041
|
-
teardownAttempt() {
|
|
3042
|
-
this.scheduler?.stop();
|
|
3043
|
-
this.scheduler = null;
|
|
3044
|
-
this.gate = null;
|
|
3045
|
-
this.stopTracks();
|
|
3046
|
-
}
|
|
3047
|
-
stopTracks() {
|
|
3048
|
-
if (this.stream === null) return;
|
|
3049
|
-
for (const track of this.stream.getTracks()) track.stop();
|
|
3050
|
-
this.stream = null;
|
|
3051
|
-
}
|
|
3052
|
-
fail(kind, fallbackReason) {
|
|
3053
|
-
this.scheduler?.stop();
|
|
3054
|
-
this.stopTracks();
|
|
3055
|
-
this.setState("error");
|
|
3056
|
-
this.options.telemetry?.({
|
|
3057
|
-
type: "error",
|
|
3058
|
-
kind,
|
|
3059
|
-
...fallbackReason !== void 0 ? { fallbackReason } : {}
|
|
3060
|
-
});
|
|
3061
|
-
this.emit("error", { kind, retryable: kind !== "insecure" && kind !== "unsupported" });
|
|
3062
|
-
}
|
|
3063
|
-
setState(state) {
|
|
3064
|
-
this.stateValue = state;
|
|
3065
|
-
this.emit("statechange", { state });
|
|
3066
|
-
}
|
|
3067
|
-
now() {
|
|
3068
|
-
return this.options.now?.() ?? performance.now();
|
|
3069
|
-
}
|
|
3070
|
-
};
|
|
3071
|
-
function delay(ms) {
|
|
3072
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3073
|
-
}
|
|
3074
|
-
function describeError2(err) {
|
|
3075
|
-
return err instanceof Error ? err.message : String(err);
|
|
3076
|
-
}
|
|
3077
|
-
function cropImageData(video, box, targetWidth) {
|
|
3078
|
-
try {
|
|
3079
|
-
const canvas = document.createElement("canvas");
|
|
3080
|
-
const scale = targetWidth / Math.max(1, box.width);
|
|
3081
|
-
canvas.width = Math.max(1, Math.round(box.width * scale));
|
|
3082
|
-
canvas.height = Math.max(1, Math.round(box.height * scale));
|
|
3083
|
-
const ctx = canvas.getContext("2d");
|
|
3084
|
-
if (ctx === null) return null;
|
|
3085
|
-
ctx.drawImage(
|
|
3086
|
-
video,
|
|
3087
|
-
box.x,
|
|
3088
|
-
box.y,
|
|
3089
|
-
box.width,
|
|
3090
|
-
box.height,
|
|
3091
|
-
0,
|
|
3092
|
-
0,
|
|
3093
|
-
canvas.width,
|
|
3094
|
-
canvas.height
|
|
3095
|
-
);
|
|
3096
|
-
return ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
3097
|
-
} catch {
|
|
3098
|
-
return null;
|
|
3099
|
-
}
|
|
3100
|
-
}
|
|
3101
|
-
|
|
3102
|
-
// src/verification/emitter.ts
|
|
3103
|
-
function createEmitter() {
|
|
3104
|
-
const handlers = /* @__PURE__ */ new Map();
|
|
3105
|
-
return {
|
|
3106
|
-
on(event, handler) {
|
|
3107
|
-
let set = handlers.get(event);
|
|
3108
|
-
if (set === void 0) {
|
|
3109
|
-
set = /* @__PURE__ */ new Set();
|
|
3110
|
-
handlers.set(event, set);
|
|
3111
|
-
}
|
|
3112
|
-
const h2 = handler;
|
|
3113
|
-
set.add(h2);
|
|
3114
|
-
return () => {
|
|
3115
|
-
set?.delete(h2);
|
|
3116
|
-
};
|
|
3117
|
-
},
|
|
3118
|
-
emit(event, payload) {
|
|
3119
|
-
const set = handlers.get(event);
|
|
3120
|
-
if (set === void 0) return;
|
|
3121
|
-
for (const handler of [...set]) {
|
|
3122
|
-
handler(payload);
|
|
3123
|
-
}
|
|
3124
|
-
},
|
|
3125
|
-
clear() {
|
|
3126
|
-
handlers.clear();
|
|
3127
|
-
}
|
|
3128
|
-
};
|
|
3129
|
-
}
|
|
3130
|
-
|
|
3131
|
-
// src/capture/preload.ts
|
|
3132
|
-
var PRELOAD_BLAZE_INIT_TIMEOUT_MS = 12e4;
|
|
3133
|
-
var PRELOAD_BUDGET_MS = 13e4;
|
|
3134
|
-
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("@sekyro/web.preload.v1");
|
|
3135
|
-
function registry() {
|
|
3136
|
-
const host = globalThis;
|
|
3137
|
-
host[REGISTRY_KEY] ?? (host[REGISTRY_KEY] = /* @__PURE__ */ new Map());
|
|
3138
|
-
return host[REGISTRY_KEY];
|
|
3139
|
-
}
|
|
3140
|
-
function normalizeBase(assetBaseUrl) {
|
|
3141
|
-
const base = (assetBaseUrl ?? DEFAULT_V3_ASSET_BASE_URL).replace(/\/+$/, "");
|
|
3142
|
-
return base === "" ? "/" : base;
|
|
3143
|
-
}
|
|
3144
|
-
function setStatus(entry, next) {
|
|
3145
|
-
if (entry.status === next) return;
|
|
3146
|
-
entry.status = next;
|
|
3147
|
-
for (const listener of [...entry.listeners]) listener(next);
|
|
3148
|
-
}
|
|
3149
|
-
var defaultLoader = (base) => createDetector({ preferred: ["blaze"], v3AssetBaseUrl: base, blazeInitTimeoutMs: PRELOAD_BLAZE_INIT_TIMEOUT_MS });
|
|
3150
|
-
var loader = defaultLoader;
|
|
3151
|
-
function setPreloadLoaderForTests(next) {
|
|
3152
|
-
loader = next ?? defaultLoader;
|
|
3153
|
-
}
|
|
3154
|
-
function resetLivenessAssetsForTests() {
|
|
3155
|
-
for (const entry of registry().values()) entry.detector?.destroy();
|
|
3156
|
-
registry().clear();
|
|
3157
|
-
loader = defaultLoader;
|
|
3158
|
-
}
|
|
3159
|
-
function begin(entry, base) {
|
|
3160
|
-
if ((entry.status === "loading" || entry.status === "ready") && entry.promise !== null) return entry.promise;
|
|
3161
|
-
setStatus(entry, "loading");
|
|
3162
|
-
const detectorPromise = new Promise((resolve) => resolve(loader(base)));
|
|
3163
|
-
entry.detectorPromise = detectorPromise;
|
|
3164
|
-
let timer;
|
|
3165
|
-
const budget = new Promise((_2, reject) => {
|
|
3166
|
-
timer = setTimeout(() => {
|
|
3167
|
-
detectorPromise.then((late) => late.destroy(), () => void 0);
|
|
3168
|
-
reject(new Error(`detector preload exceeded ${PRELOAD_BUDGET_MS} ms`));
|
|
3169
|
-
}, PRELOAD_BUDGET_MS);
|
|
3170
|
-
});
|
|
3171
|
-
const settled = Promise.race([detectorPromise, budget]).then(
|
|
3172
|
-
(detector) => {
|
|
3173
|
-
if (entry.detectorPromise !== detectorPromise) {
|
|
3174
|
-
detector.destroy();
|
|
3175
|
-
return entry.status;
|
|
3176
|
-
}
|
|
3177
|
-
entry.detector = detector;
|
|
3178
|
-
setStatus(entry, "ready");
|
|
3179
|
-
return "ready";
|
|
3180
|
-
},
|
|
3181
|
-
(err) => {
|
|
3182
|
-
if (entry.detectorPromise === detectorPromise) {
|
|
3183
|
-
entry.detectorPromise = null;
|
|
3184
|
-
entry.detector = null;
|
|
3185
|
-
setStatus(entry, "failed");
|
|
3186
|
-
console.warn("[@sekyro/web] face-detector preload failed:", err);
|
|
3187
|
-
}
|
|
3188
|
-
return entry.status;
|
|
3189
|
-
}
|
|
3190
|
-
);
|
|
3191
|
-
const promise = settled.finally(() => clearTimeout(timer));
|
|
3192
|
-
entry.promise = promise;
|
|
3193
|
-
entry.scheduled?.settle(promise);
|
|
3194
|
-
return promise;
|
|
3195
|
-
}
|
|
3196
|
-
function nonOwningView(shared) {
|
|
3197
|
-
return {
|
|
3198
|
-
get tier() {
|
|
3199
|
-
return shared.tier;
|
|
3200
|
-
},
|
|
3201
|
-
get fallbackReason() {
|
|
3202
|
-
return shared.fallbackReason;
|
|
3203
|
-
},
|
|
3204
|
-
detect: (...args) => shared.detect(...args),
|
|
3205
|
-
destroy: () => {
|
|
3206
|
-
}
|
|
3207
|
-
};
|
|
3208
|
-
}
|
|
3209
|
-
function preloadedDetectorFactory(assetBaseUrl) {
|
|
3210
|
-
const base = normalizeBase(assetBaseUrl);
|
|
3211
|
-
const entry = registry().get(base);
|
|
3212
|
-
if (entry === void 0 || entry.detector === null && entry.detectorPromise === null && entry.scheduled === null) {
|
|
3213
|
-
return void 0;
|
|
3214
|
-
}
|
|
3215
|
-
return async () => {
|
|
3216
|
-
if (entry.scheduled !== null) void begin(entry, base);
|
|
3217
|
-
let waitingOn = entry.promise;
|
|
3218
|
-
while (entry.detector === null && waitingOn !== null) {
|
|
3219
|
-
await waitingOn;
|
|
3220
|
-
if (entry.promise === waitingOn) break;
|
|
3221
|
-
waitingOn = entry.promise;
|
|
3222
|
-
}
|
|
3223
|
-
return entry.detector !== null ? nonOwningView(entry.detector) : createDetector({ v3AssetBaseUrl: base });
|
|
3224
|
-
};
|
|
3225
|
-
}
|
|
3226
|
-
|
|
3227
|
-
// src/capture/wake-lock.ts
|
|
3228
|
-
function wakeLockApi() {
|
|
3229
|
-
if (typeof navigator === "undefined" || !("wakeLock" in navigator)) return null;
|
|
3230
|
-
const api = navigator.wakeLock;
|
|
3231
|
-
return typeof api?.request === "function" ? api : null;
|
|
3232
|
-
}
|
|
3233
|
-
function isVisible() {
|
|
3234
|
-
return typeof document === "undefined" || document.visibilityState === "visible";
|
|
3235
|
-
}
|
|
3236
|
-
function releaseQuietly(sentinel) {
|
|
3237
|
-
try {
|
|
3238
|
-
void sentinel.release().catch(() => void 0);
|
|
3239
|
-
} catch {
|
|
3240
|
-
}
|
|
3241
|
-
}
|
|
3242
|
-
function createScreenWakeLock(options = {}) {
|
|
3243
|
-
if (options.enabled === false) return { setActive: () => void 0 };
|
|
3244
|
-
let wanted = false;
|
|
3245
|
-
let sentinel = null;
|
|
3246
|
-
let inFlight = false;
|
|
3247
|
-
let listening = false;
|
|
3248
|
-
const acquire = () => {
|
|
3249
|
-
if (!wanted || inFlight || sentinel !== null && !sentinel.released) return;
|
|
3250
|
-
const api = wakeLockApi();
|
|
3251
|
-
if (api === null || !isVisible()) return;
|
|
3252
|
-
let request;
|
|
3253
|
-
try {
|
|
3254
|
-
request = api.request("screen");
|
|
3255
|
-
} catch {
|
|
3256
|
-
return;
|
|
3257
|
-
}
|
|
3258
|
-
inFlight = true;
|
|
3259
|
-
request.then(
|
|
3260
|
-
(granted) => {
|
|
3261
|
-
inFlight = false;
|
|
3262
|
-
if (!wanted) {
|
|
3263
|
-
releaseQuietly(granted);
|
|
3264
|
-
return;
|
|
3265
|
-
}
|
|
3266
|
-
sentinel = granted;
|
|
3267
|
-
granted.addEventListener?.("release", () => {
|
|
3268
|
-
if (sentinel === granted) sentinel = null;
|
|
3269
|
-
});
|
|
3270
|
-
},
|
|
3271
|
-
() => {
|
|
3272
|
-
inFlight = false;
|
|
3273
|
-
}
|
|
3274
|
-
);
|
|
3275
|
-
};
|
|
3276
|
-
const onVisibilityChange = () => {
|
|
3277
|
-
if (isVisible()) acquire();
|
|
3278
|
-
};
|
|
3279
|
-
const listen = (on) => {
|
|
3280
|
-
if (typeof document === "undefined" || on === listening) return;
|
|
3281
|
-
listening = on;
|
|
3282
|
-
if (on) document.addEventListener("visibilitychange", onVisibilityChange);
|
|
3283
|
-
else document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
3284
|
-
};
|
|
3285
|
-
return {
|
|
3286
|
-
setActive: (active) => {
|
|
3287
|
-
wanted = active;
|
|
3288
|
-
if (active) {
|
|
3289
|
-
if (wakeLockApi() === null) return;
|
|
3290
|
-
listen(true);
|
|
3291
|
-
acquire();
|
|
3292
|
-
return;
|
|
3293
|
-
}
|
|
3294
|
-
listen(false);
|
|
3295
|
-
if (sentinel !== null) {
|
|
3296
|
-
const held = sentinel;
|
|
3297
|
-
sentinel = null;
|
|
3298
|
-
releaseQuietly(held);
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
3301
|
-
};
|
|
3302
|
-
}
|
|
3303
|
-
|
|
3304
|
-
// src/capture/create-liveness-capture.ts
|
|
3305
|
-
function mapGuidanceHint(key) {
|
|
3306
|
-
switch (key) {
|
|
3307
|
-
case "fcv3.no_face":
|
|
3308
|
-
case "fcv3.center_face":
|
|
3309
|
-
case "fcv3.straighten":
|
|
3310
|
-
return "center";
|
|
3311
|
-
case "fcv3.come_closer":
|
|
3312
|
-
return "closer";
|
|
3313
|
-
case "fcv3.move_back":
|
|
3314
|
-
return "farther";
|
|
3315
|
-
case "fcv3.more_light":
|
|
3316
|
-
case "fcv3.too_bright":
|
|
3317
|
-
return "light";
|
|
3318
|
-
case "fcv3.hold_still":
|
|
3319
|
-
case "fcv3.steady_camera":
|
|
3320
|
-
return "still";
|
|
3321
|
-
default:
|
|
3322
|
-
return null;
|
|
3323
|
-
}
|
|
3324
|
-
}
|
|
3325
|
-
function mapEngineState(state) {
|
|
3326
|
-
switch (state) {
|
|
3327
|
-
case "preflight":
|
|
3328
|
-
case "acquiring":
|
|
3329
|
-
return "starting";
|
|
3330
|
-
case "positioning":
|
|
3331
|
-
return "gating";
|
|
3332
|
-
case "idle":
|
|
3333
|
-
case "recording":
|
|
3334
|
-
case "captured":
|
|
3335
|
-
case "error":
|
|
3336
|
-
return state;
|
|
3337
|
-
default: {
|
|
3338
|
-
const unhandled = state;
|
|
3339
|
-
throw new Error(`mapEngineState: unhandled state ${unhandled}`);
|
|
3340
|
-
}
|
|
3341
|
-
}
|
|
3342
|
-
}
|
|
3343
|
-
function mapErrorKind(kind) {
|
|
3344
|
-
switch (kind) {
|
|
3345
|
-
case "permission":
|
|
3346
|
-
case "notfound":
|
|
3347
|
-
case "inuse":
|
|
3348
|
-
case "insecure":
|
|
3349
|
-
case "unsupported":
|
|
3350
|
-
return kind;
|
|
3351
|
-
case "overconstrained":
|
|
3352
|
-
case "generic":
|
|
3353
|
-
case "models":
|
|
3354
|
-
case "cliptoolarge":
|
|
3355
|
-
case "recorder":
|
|
3356
|
-
return "generic";
|
|
3357
|
-
default: {
|
|
3358
|
-
const unhandled = kind;
|
|
3359
|
-
throw new Error(`mapErrorKind: unhandled kind ${unhandled}`);
|
|
3360
|
-
}
|
|
3361
|
-
}
|
|
3362
|
-
}
|
|
3363
|
-
function createLivenessCaptureInternal(options, internals = {}) {
|
|
3364
|
-
const now = internals.now ?? (() => performance.now());
|
|
3365
|
-
const emitter = createEmitter();
|
|
3366
|
-
const degraded = !isMediaRecorderSupported();
|
|
3367
|
-
const hostSeams = internals.engineOptions;
|
|
3368
|
-
const preloadAware = hostSeams?.createDetector === void 0 && hostSeams?.createWorker === void 0 ? {
|
|
3369
|
-
createDetector: () => {
|
|
3370
|
-
const warmed = preloadedDetectorFactory(options.assetBaseUrl);
|
|
3371
|
-
return warmed !== void 0 ? warmed() : createDetector(
|
|
3372
|
-
options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {}
|
|
3373
|
-
);
|
|
3374
|
-
}
|
|
3375
|
-
} : {};
|
|
3376
|
-
const engineOptions = {
|
|
3377
|
-
video: options.video,
|
|
3378
|
-
mode: degraded ? "photo" : "clip",
|
|
3379
|
-
...options.facing !== void 0 ? { facingMode: options.facing } : {},
|
|
3380
|
-
...options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {},
|
|
3381
|
-
...internals.engineOptions ?? {},
|
|
3382
|
-
...preloadAware
|
|
3383
|
-
};
|
|
3384
|
-
const engine = internals.createEngine?.(engineOptions) ?? new CaptureSessionV3(engineOptions);
|
|
3385
|
-
let state = "idle";
|
|
3386
|
-
let hint = null;
|
|
3387
|
-
let progress = 0;
|
|
3388
|
-
let faceDetected = false;
|
|
3389
|
-
let animations = "full";
|
|
3390
|
-
let recordingStartedAt = null;
|
|
3391
|
-
let frozenAt = null;
|
|
3392
|
-
let frameWidth = 0;
|
|
3393
|
-
let frameHeight = 0;
|
|
3394
|
-
const latchFrameSize = () => {
|
|
3395
|
-
if (options.video.videoWidth > 0 && options.video.videoHeight > 0) {
|
|
3396
|
-
frameWidth = options.video.videoWidth;
|
|
3397
|
-
frameHeight = options.video.videoHeight;
|
|
3398
|
-
}
|
|
3399
|
-
};
|
|
3400
|
-
const wakeLock = createScreenWakeLock({ enabled: options.keepScreenAwake !== false });
|
|
3401
|
-
const setState = (next) => {
|
|
3402
|
-
state = next;
|
|
3403
|
-
wakeLock.setActive(next === "starting" || next === "gating" || next === "recording");
|
|
3404
|
-
emitter.emit("state", next);
|
|
3405
|
-
};
|
|
3406
|
-
const emitGate = () => emitter.emit("gate", { progress, hint, faceDetected });
|
|
3407
|
-
const syncProfile = () => {
|
|
3408
|
-
const next = engine.animations ?? "full";
|
|
3409
|
-
if (next === animations) return;
|
|
3410
|
-
animations = next;
|
|
3411
|
-
emitter.emit("profile", { animations });
|
|
3412
|
-
};
|
|
3413
|
-
const unsubscribes = [
|
|
3414
|
-
engine.on("statechange", ({ state: engineState }) => {
|
|
3415
|
-
const next = mapEngineState(engineState);
|
|
3416
|
-
if (next === "starting" && state !== "starting") {
|
|
3417
|
-
hint = null;
|
|
3418
|
-
progress = 0;
|
|
3419
|
-
faceDetected = false;
|
|
3420
|
-
recordingStartedAt = null;
|
|
3421
|
-
frozenAt = null;
|
|
3422
|
-
frameWidth = 0;
|
|
3423
|
-
frameHeight = 0;
|
|
3424
|
-
}
|
|
3425
|
-
setState(next);
|
|
3426
|
-
syncProfile();
|
|
3427
|
-
if (next === "recording") {
|
|
3428
|
-
latchFrameSize();
|
|
3429
|
-
recordingStartedAt = now();
|
|
3430
|
-
hint = "hold";
|
|
3431
|
-
progress = 1;
|
|
3432
|
-
emitGate();
|
|
3433
|
-
}
|
|
3434
|
-
}),
|
|
3435
|
-
engine.on("checks", (payload) => {
|
|
3436
|
-
latchFrameSize();
|
|
3437
|
-
progress = payload.progress;
|
|
3438
|
-
faceDetected = payload.factors.presence > 0;
|
|
3439
|
-
emitGate();
|
|
3440
|
-
syncProfile();
|
|
3441
|
-
}),
|
|
3442
|
-
engine.on("guidance", ({ key }) => {
|
|
3443
|
-
hint = mapGuidanceHint(key);
|
|
3444
|
-
emitGate();
|
|
3445
|
-
}),
|
|
3446
|
-
engine.on("frozen", ({ previewDataUrl }) => {
|
|
3447
|
-
frozenAt = now();
|
|
3448
|
-
emitter.emit("frozen", { previewDataUrl });
|
|
3449
|
-
}),
|
|
3450
|
-
engine.on("artifacts", (artifacts) => {
|
|
3451
|
-
const clipMs = recordingStartedAt !== null && frozenAt !== null ? Math.max(0, Math.round(frozenAt - recordingStartedAt)) : void 0;
|
|
3452
|
-
emitter.emit("captured", {
|
|
3453
|
-
keyframe: artifacts.keyframe,
|
|
3454
|
-
...artifacts.clip !== void 0 ? { clip: artifacts.clip } : {},
|
|
3455
|
-
...artifacts.clipMimeType !== void 0 ? { clipMime: artifacts.clipMimeType } : {},
|
|
3456
|
-
width: options.video.videoWidth || frameWidth,
|
|
3457
|
-
height: options.video.videoHeight || frameHeight,
|
|
3458
|
-
...clipMs !== void 0 ? { clipMs } : {},
|
|
3459
|
-
captureChannel: detectIsMobile() ? "mobile" : "desktop",
|
|
3460
|
-
...degraded ? { degraded: "NO_RECORDER" } : {},
|
|
3461
|
-
engine: "v3",
|
|
3462
|
-
// Face-capture timing: the engine's marks, forwarded flat (absent → absent).
|
|
3463
|
-
...artifacts.timing?.cameraReadyMs !== void 0 ? { cameraReadyMs: artifacts.timing.cameraReadyMs } : {},
|
|
3464
|
-
...artifacts.timing?.faceAcquireMs !== void 0 ? { faceAcquireMs: artifacts.timing.faceAcquireMs } : {},
|
|
3465
|
-
...artifacts.timing?.captureMs !== void 0 ? { captureMs: artifacts.timing.captureMs } : {}
|
|
3466
|
-
});
|
|
3467
|
-
}),
|
|
3468
|
-
engine.on("error", ({ kind, retryable }) => {
|
|
3469
|
-
emitter.emit("error", { kind: mapErrorKind(kind), retryable });
|
|
3470
|
-
})
|
|
3471
|
-
];
|
|
3472
|
-
const handle = {
|
|
3473
|
-
get state() {
|
|
3474
|
-
return state;
|
|
3475
|
-
},
|
|
3476
|
-
on: (event, handler) => emitter.on(event, handler),
|
|
3477
|
-
start: (startOptions) => engine.start(startOptions?.facing !== void 0 ? { facingMode: startOptions.facing } : {}),
|
|
3478
|
-
retry: () => engine.retry(),
|
|
3479
|
-
switchCamera: () => engine.switchCamera(),
|
|
3480
|
-
destroy: () => {
|
|
3481
|
-
for (const unsubscribe of unsubscribes) unsubscribe();
|
|
3482
|
-
wakeLock.setActive(false);
|
|
3483
|
-
engine.destroy();
|
|
3484
|
-
emitter.clear();
|
|
3485
|
-
}
|
|
3486
|
-
};
|
|
3487
|
-
return {
|
|
3488
|
-
handle,
|
|
3489
|
-
setFlowState: (next) => {
|
|
3490
|
-
if (state !== next) setState(next);
|
|
3491
|
-
}
|
|
3492
|
-
};
|
|
3493
|
-
}
|
|
3494
|
-
|
|
3495
|
-
// src/client/errors.ts
|
|
3496
|
-
var HostedApiError = class extends Error {
|
|
3497
|
-
constructor(args) {
|
|
3498
|
-
super(args.message);
|
|
3499
|
-
this.name = "HostedApiError";
|
|
3500
|
-
this.status = args.status;
|
|
3501
|
-
this.code = args.code;
|
|
3502
|
-
if (args.retryAfterSeconds !== void 0) this.retryAfterSeconds = args.retryAfterSeconds;
|
|
3503
|
-
}
|
|
3504
|
-
};
|
|
3505
|
-
|
|
3506
|
-
// src/client/hosted-client.ts
|
|
3507
|
-
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
3508
|
-
var SPACES_PUT_TIMEOUT_MS = 9e5;
|
|
3509
|
-
var UPLOAD_ERROR_SNIPPET_CHARS = 200;
|
|
3510
|
-
var IN_PROGRESS_RETRY_ATTEMPTS = 5;
|
|
3511
|
-
var IN_PROGRESS_RETRY_DELAY_MS = 400;
|
|
3512
|
-
function stripMimeParams(contentType) {
|
|
3513
|
-
return (contentType.split(";")[0] ?? "").trim();
|
|
3514
|
-
}
|
|
3515
|
-
var MEDIA_INPUT_FIELD = {
|
|
3516
|
-
liveness: "clipKey",
|
|
3517
|
-
face_match: "imageKey",
|
|
3518
|
-
doc_ocr: "imageKey"
|
|
3519
|
-
};
|
|
3520
|
-
var HostedClient = class {
|
|
3521
|
-
constructor(opts) {
|
|
3522
|
-
this.sessionId = null;
|
|
3523
|
-
this.bootstrap = null;
|
|
3524
|
-
this.apiOrigin = opts.apiOrigin.replace(/\/+$/, "");
|
|
3525
|
-
this.sessionToken = opts.sessionToken;
|
|
3526
|
-
const impl = opts.fetch;
|
|
3527
|
-
this.fetchImpl = (input, init) => (impl ?? globalThis.fetch).call(globalThis, input, init);
|
|
3528
|
-
this.requestTimeoutMs = opts.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
3529
|
-
this.retryAttempts = opts.inProgressRetry?.attempts ?? IN_PROGRESS_RETRY_ATTEMPTS;
|
|
3530
|
-
this.retryDelayMs = opts.inProgressRetry?.delayMs ?? IN_PROGRESS_RETRY_DELAY_MS;
|
|
3531
|
-
}
|
|
3532
|
-
/** Token-addressed bootstrap — the hosted page's first call. Caches the sessionId. */
|
|
3533
|
-
async getSession() {
|
|
3534
|
-
const view = await this.api("GET", "/hosted/session");
|
|
3535
|
-
this.sessionId = view.sessionId;
|
|
3536
|
-
return view;
|
|
3537
|
-
}
|
|
3538
|
-
/**
|
|
3539
|
-
* Record consent once (201; an idempotent replay answers 200 with
|
|
3540
|
-
* `alreadyRecorded: true`). Until recorded, media presign/complete AND check
|
|
3541
|
-
* submission answer 409 `consent_required`.
|
|
3542
|
-
*/
|
|
3543
|
-
async recordConsent(locale) {
|
|
3544
|
-
return this.api("POST", "/hosted/consent", { body: { locale } });
|
|
3545
|
-
}
|
|
3546
|
-
/**
|
|
3547
|
-
* Presign a Spaces PUT for a new pending object. `contentType` is stripped of
|
|
3548
|
-
* MIME parameters defensively (the allowlist holds bare types; the engine's
|
|
3549
|
-
* `clipMimeType` carries codec params) — PUT with the SAME bare type, or the
|
|
3550
|
-
* signed-header check rejects the upload.
|
|
3551
|
-
*/
|
|
3552
|
-
async presign(kind, contentType, contentLength) {
|
|
3553
|
-
const id = await this.ensureSessionId();
|
|
3554
|
-
const body = { kind, contentType: stripMimeParams(contentType), contentLength };
|
|
3555
|
-
return this.api("POST", `/sessions/${id}/media`, { body });
|
|
3556
|
-
}
|
|
3557
|
-
/**
|
|
3558
|
-
* Bare PUT to the presigned Spaces URL: EXACTLY the signed headers — the
|
|
3559
|
-
* declared Content-Type and the blob body. NO `x-session-token` (the
|
|
3560
|
-
* signature IS the credential; an extra header breaks nothing but leaks the
|
|
3561
|
-
* token to a third-party host) and no JSON envelope.
|
|
3562
|
-
*
|
|
3563
|
-
* `contentType` is param-stripped here too (idempotent for bare types): the
|
|
3564
|
-
* presign strips internally, so a caller composing manually with the engine's
|
|
3565
|
-
* parameterful `clipMimeType` would otherwise sign `video/webm` but PUT
|
|
3566
|
-
* `video/webm;codecs=…` — a signed-header mismatch S3 rejects at runtime.
|
|
3567
|
-
*
|
|
3568
|
-
* Timeout: 15 min (`AbortSignal.timeout(900_000)`, matching the presign TTL)
|
|
3569
|
-
* rather than the API calls' 30 s — a 15 MB clip on a slow link legitimately
|
|
3570
|
-
* exceeds 30 s, but a stalled in-flight socket must still be bounded (the TTL
|
|
3571
|
-
* alone only bounds when a PUT can START).
|
|
3572
|
-
*
|
|
3573
|
-
* Failures are ALWAYS typed: non-2xx throws {@link HostedApiError} with the
|
|
3574
|
-
* client-synthesized `upload_failed` (message carries a truncated S3 body
|
|
3575
|
-
* snippet — e.g. SignatureDoesNotMatch); a mid-transfer drop → `network_error`;
|
|
3576
|
-
* the timeout → `timeout`.
|
|
3577
|
-
*/
|
|
3578
|
-
async uploadToSpaces(uploadUrl, blob, contentType) {
|
|
3579
|
-
let res;
|
|
3580
|
-
try {
|
|
3581
|
-
res = await this.fetchImpl(uploadUrl, {
|
|
3582
|
-
method: "PUT",
|
|
3583
|
-
headers: { "content-type": stripMimeParams(contentType) },
|
|
3584
|
-
body: blob,
|
|
3585
|
-
signal: AbortSignal.timeout(SPACES_PUT_TIMEOUT_MS)
|
|
3586
|
-
});
|
|
3587
|
-
} catch (err) {
|
|
3588
|
-
throw asTransportError(err);
|
|
3589
|
-
}
|
|
3590
|
-
if (!res.ok) {
|
|
3591
|
-
const snippet = (await res.text().catch(() => "")).slice(0, UPLOAD_ERROR_SNIPPET_CHARS);
|
|
3592
|
-
throw new HostedApiError({
|
|
3593
|
-
status: res.status,
|
|
3594
|
-
code: "upload_failed",
|
|
3595
|
-
message: `media upload failed (HTTP ${res.status})${snippet ? `: ${snippet}` : ""}`
|
|
3596
|
-
});
|
|
3597
|
-
}
|
|
3598
|
-
}
|
|
3599
|
-
/** Finalize an upload: the backend sniffs + promotes; returns the PROMOTED key to submit. */
|
|
3600
|
-
async completeMedia(key) {
|
|
3601
|
-
const id = await this.ensureSessionId();
|
|
3602
|
-
return this.api("POST", `/sessions/${id}/media/complete`, {
|
|
3603
|
-
body: { key }
|
|
3604
|
-
});
|
|
3605
|
-
}
|
|
3606
|
-
/**
|
|
3607
|
-
* Submit one check. Sends `{input, forceOutcome?}` (SubmitCheckSchema) and the
|
|
3608
|
-
* optional `x-idempotency-key` header. Returns the whitelisted SessionView.
|
|
3609
|
-
*/
|
|
3610
|
-
async submitCheck(checkType, input, opts = {}) {
|
|
3611
|
-
const id = await this.ensureSessionId();
|
|
3612
|
-
const body = {
|
|
3613
|
-
input,
|
|
3614
|
-
...opts.forceOutcome !== void 0 ? { forceOutcome: opts.forceOutcome } : {}
|
|
3615
|
-
};
|
|
3616
|
-
return this.api("POST", `/sessions/${id}/checks/${checkType}`, {
|
|
3617
|
-
body,
|
|
3618
|
-
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {}
|
|
3619
|
-
});
|
|
3620
|
-
}
|
|
3621
|
-
/**
|
|
3622
|
-
* Convenience composition: presign → PUT → complete → submit, passing the
|
|
3623
|
-
* PROMOTED key (from complete's response — never the pending presign key)
|
|
3624
|
-
* into the check's media input field (`clipKey` for liveness, `imageKey` for
|
|
3625
|
-
* face_match/doc_ocr). `extraInput` carries the remaining input fields
|
|
3626
|
-
* (liveness `attestation`, doc_ocr `side`). The media content type is
|
|
3627
|
-
* param-stripped ONCE and used for both presign and PUT, so the signed
|
|
3628
|
-
* header always matches.
|
|
3629
|
-
*/
|
|
3630
|
-
async uploadAndSubmit(checkType, media, extraInput, opts = {}) {
|
|
3631
|
-
const bareType = stripMimeParams(media.contentType);
|
|
3632
|
-
const presigned = await this.presign(media.kind, bareType, media.blob.size);
|
|
3633
|
-
await this.uploadToSpaces(presigned.uploadUrl, media.blob, bareType);
|
|
3634
|
-
const completed = await this.completeMedia(presigned.key);
|
|
3635
|
-
const input = {
|
|
3636
|
-
...extraInput,
|
|
3637
|
-
[MEDIA_INPUT_FIELD[checkType]]: completed.key
|
|
3638
|
-
};
|
|
3639
|
-
return this.submitCheck(checkType, input, opts);
|
|
3640
|
-
}
|
|
3641
|
-
// ── transport ──────────────────────────────────────────────────────────────
|
|
3642
|
-
/** The token's own sessionId — cached from getSession(), lazily bootstrapped. */
|
|
3643
|
-
async ensureSessionId() {
|
|
3644
|
-
if (this.sessionId !== null) return this.sessionId;
|
|
3645
|
-
this.bootstrap ?? (this.bootstrap = this.getSession().finally(() => {
|
|
3646
|
-
this.bootstrap = null;
|
|
3647
|
-
}));
|
|
3648
|
-
return (await this.bootstrap).sessionId;
|
|
3649
|
-
}
|
|
3650
|
-
/**
|
|
3651
|
-
* One API request: `${apiOrigin}/api/v2${path}`, `x-session-token` always,
|
|
3652
|
-
* AbortSignal timeout (30 s default), `{success, data}` unwrap on 2xx, flat
|
|
3653
|
-
* `{code, message}` → {@link HostedApiError} on non-2xx. EVERY failure is
|
|
3654
|
-
* typed: a rejected fetch → `network_error`, the timeout → `timeout`, an
|
|
3655
|
-
* unparseable 2xx body → `unknown_error` — the page never sees a raw
|
|
3656
|
-
* TypeError/DOMException. The ONLY automatic retry is 409
|
|
3657
|
-
* `idempotency_in_progress` (bounded, short delay).
|
|
3658
|
-
*/
|
|
3659
|
-
async api(method, path, opts = {}) {
|
|
3660
|
-
const url = `${this.apiOrigin}/api/v2${path}`;
|
|
3661
|
-
const headers = { "x-session-token": this.sessionToken };
|
|
3662
|
-
if (opts.body !== void 0) headers["content-type"] = "application/json";
|
|
3663
|
-
if (opts.idempotencyKey !== void 0) headers["x-idempotency-key"] = opts.idempotencyKey;
|
|
3664
|
-
for (let attempt = 0; ; attempt++) {
|
|
3665
|
-
let res;
|
|
3666
|
-
try {
|
|
3667
|
-
res = await this.fetchImpl(url, {
|
|
3668
|
-
method,
|
|
3669
|
-
headers,
|
|
3670
|
-
...opts.body !== void 0 ? { body: JSON.stringify(opts.body) } : {},
|
|
3671
|
-
signal: AbortSignal.timeout(this.requestTimeoutMs)
|
|
3672
|
-
});
|
|
3673
|
-
} catch (err2) {
|
|
3674
|
-
throw asTransportError(err2);
|
|
3675
|
-
}
|
|
3676
|
-
if (res.ok) {
|
|
3677
|
-
let raw;
|
|
3678
|
-
try {
|
|
3679
|
-
raw = await res.json();
|
|
3680
|
-
} catch {
|
|
3681
|
-
throw new HostedApiError({
|
|
3682
|
-
status: res.status,
|
|
3683
|
-
code: "unknown_error",
|
|
3684
|
-
message: `response body was not valid JSON (HTTP ${res.status})`
|
|
3685
|
-
});
|
|
3686
|
-
}
|
|
3687
|
-
return unwrap(raw);
|
|
3688
|
-
}
|
|
3689
|
-
const err = await parseErrorResponse(res);
|
|
3690
|
-
if (err.code === "idempotency_in_progress" && attempt < this.retryAttempts) {
|
|
3691
|
-
if (this.retryDelayMs > 0) await sleep(this.retryDelayMs);
|
|
3692
|
-
continue;
|
|
3693
|
-
}
|
|
3694
|
-
throw err;
|
|
3695
|
-
}
|
|
3696
|
-
}
|
|
3697
|
-
};
|
|
3698
|
-
function asTransportError(err) {
|
|
3699
|
-
const name = typeof err === "object" && err !== null ? err.name : void 0;
|
|
3700
|
-
if (name === "TimeoutError") {
|
|
3701
|
-
return new HostedApiError({ status: 0, code: "timeout", message: "request timed out" });
|
|
3702
|
-
}
|
|
3703
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
3704
|
-
return new HostedApiError({
|
|
3705
|
-
status: 0,
|
|
3706
|
-
code: "network_error",
|
|
3707
|
-
message: `network request failed: ${detail}`
|
|
3708
|
-
});
|
|
3709
|
-
}
|
|
3710
|
-
function unwrap(raw) {
|
|
3711
|
-
if (raw !== null && typeof raw === "object" && "success" in raw && "data" in raw) {
|
|
3712
|
-
return raw.data;
|
|
3713
|
-
}
|
|
3714
|
-
return raw;
|
|
3715
|
-
}
|
|
3716
|
-
async function parseErrorResponse(res) {
|
|
3717
|
-
let code = "unknown_error";
|
|
3718
|
-
let message = `request failed (HTTP ${res.status})`;
|
|
3719
|
-
try {
|
|
3720
|
-
const body = await res.json();
|
|
3721
|
-
if (body !== null && typeof body === "object") {
|
|
3722
|
-
const env = body;
|
|
3723
|
-
if (typeof env.code === "string" && env.code.length > 0) code = env.code;
|
|
3724
|
-
if (typeof env.message === "string" && env.message.length > 0) message = env.message;
|
|
3725
|
-
}
|
|
3726
|
-
} catch {
|
|
3727
|
-
}
|
|
3728
|
-
const retryAfterRaw = res.headers.get("retry-after");
|
|
3729
|
-
const retryAfter = retryAfterRaw === null ? Number.NaN : Number.parseInt(retryAfterRaw, 10);
|
|
3730
|
-
return new HostedApiError({
|
|
3731
|
-
status: res.status,
|
|
3732
|
-
code,
|
|
3733
|
-
message,
|
|
3734
|
-
...Number.isFinite(retryAfter) && retryAfter >= 0 ? { retryAfterSeconds: retryAfter } : {}
|
|
3735
|
-
});
|
|
3736
|
-
}
|
|
3737
|
-
function sleep(ms) {
|
|
3738
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3739
|
-
}
|
|
3740
|
-
|
|
3741
|
-
// src/verification/result.ts
|
|
3742
|
-
var RETRYABLE_REASONS = /* @__PURE__ */ new Set([
|
|
3743
|
-
"no_face",
|
|
3744
|
-
"media_unreadable",
|
|
3745
|
-
"clip_too_short",
|
|
3746
|
-
"no_face_source",
|
|
3747
|
-
"side_missing",
|
|
3748
|
-
"unreadable",
|
|
3749
|
-
"registry_unavailable",
|
|
3750
|
-
"otp_mismatch",
|
|
3751
|
-
"otp_expired",
|
|
3752
|
-
"otp_send_failed"
|
|
3753
|
-
]);
|
|
3754
|
-
var BIOMETRIC_CHECKS = /* @__PURE__ */ new Set(["liveness", "face_match"]);
|
|
3755
|
-
function isExecutable(step) {
|
|
3756
|
-
return step !== void 0 && (step.state === "pending" || step.state === "input_required");
|
|
3757
|
-
}
|
|
3758
|
-
function isTerminalView(view) {
|
|
3759
|
-
return view.status !== "created" && view.status !== "in_progress";
|
|
3760
|
-
}
|
|
3761
|
-
function frontierStep(view) {
|
|
3762
|
-
return view.steps.find((step) => isExecutable(step));
|
|
3763
|
-
}
|
|
3764
|
-
function stepOf(view, checkType) {
|
|
3765
|
-
return view.steps.find((step) => step.checkType === checkType);
|
|
3766
|
-
}
|
|
3767
|
-
function mergeSessionView(prev, next, submitted) {
|
|
3768
|
-
const byType = new Map(prev.steps.map((step) => [step.checkType, step]));
|
|
3769
|
-
const steps = next.steps.map((step) => {
|
|
3770
|
-
const before = byType.get(step.checkType);
|
|
3771
|
-
const attemptsBefore = before?.attemptsRemaining ?? 0;
|
|
3772
|
-
const merged = {
|
|
3773
|
-
...before ?? {},
|
|
3774
|
-
checkType: step.checkType,
|
|
3775
|
-
state: step.state,
|
|
3776
|
-
attemptsRemaining: step.checkType === submitted ? Math.max(0, attemptsBefore - 1) : attemptsBefore
|
|
3777
|
-
};
|
|
3778
|
-
if (step.reason !== void 0) merged.reason = step.reason;
|
|
3779
|
-
else delete merged.reason;
|
|
3780
|
-
return merged;
|
|
3781
|
-
});
|
|
3782
|
-
return { ...prev, status: next.status, outcome: next.outcome, steps, expiresAt: next.expiresAt };
|
|
3783
|
-
}
|
|
3784
|
-
function unavailable(sessionId, reason) {
|
|
3785
|
-
return reason === void 0 ? { sessionId, status: "unavailable" } : { sessionId, status: "unavailable", reason };
|
|
3786
|
-
}
|
|
3787
|
-
function withReason(sessionId, status, reason) {
|
|
3788
|
-
return reason === void 0 ? { sessionId, status } : { sessionId, status, reason };
|
|
3789
|
-
}
|
|
3790
|
-
function mapSessionResult(view, opts) {
|
|
3791
|
-
const { sessionId } = view;
|
|
3792
|
-
switch (view.status) {
|
|
3793
|
-
case "completed":
|
|
3794
|
-
if (view.outcome === "approved") return { sessionId, status: "approved" };
|
|
3795
|
-
if (view.outcome === "review") return withReason(sessionId, "review", reasonIn(view, "review"));
|
|
3796
|
-
return withReason(sessionId, "rejected", reasonIn(view, "failed"));
|
|
3797
|
-
case "failed":
|
|
3798
|
-
return withReason(sessionId, "rejected", reasonIn(view, "failed"));
|
|
3799
|
-
case "review_pending":
|
|
3800
|
-
return withReason(sessionId, "review", reasonIn(view, "review"));
|
|
3801
|
-
case "expired":
|
|
3802
|
-
return unavailable(sessionId);
|
|
3803
|
-
case "created":
|
|
3804
|
-
case "in_progress":
|
|
3805
|
-
break;
|
|
3806
|
-
default: {
|
|
3807
|
-
const unhandled = view.status;
|
|
3808
|
-
throw new Error(`mapSessionResult: unhandled status ${unhandled}`);
|
|
3809
|
-
}
|
|
3810
|
-
}
|
|
3811
|
-
const parked = view.steps.find(
|
|
3812
|
-
(step) => BIOMETRIC_CHECKS.has(step.checkType) && step.state === "input_required" && step.reason !== void 0
|
|
3813
|
-
);
|
|
3814
|
-
if (parked?.reason !== void 0) {
|
|
3815
|
-
const canRetry = opts.autoRetry && RETRYABLE_REASONS.has(parked.reason) && parked.attemptsRemaining > 0;
|
|
3816
|
-
return {
|
|
3817
|
-
sessionId,
|
|
3818
|
-
status: canRetry ? "retry" : "rejected",
|
|
3819
|
-
reason: parked.reason,
|
|
3820
|
-
attemptsRemaining: parked.attemptsRemaining
|
|
3821
|
-
};
|
|
3822
|
-
}
|
|
3823
|
-
if (frontierStep(view) === void 0 && view.steps.some((step) => step.state === "review")) {
|
|
3824
|
-
return withReason(sessionId, "review", reasonIn(view, "review"));
|
|
3825
|
-
}
|
|
3826
|
-
return unavailable(sessionId);
|
|
3827
|
-
}
|
|
3828
|
-
function reasonIn(view, state) {
|
|
3829
|
-
return view.steps.find((step) => step.state === state && step.reason !== void 0)?.reason;
|
|
3830
|
-
}
|
|
3831
|
-
|
|
3832
|
-
// src/verification/liveness-flow.ts
|
|
3833
|
-
var CLIP_MIME_ALLOWLIST = ["video/webm", "video/mp4"];
|
|
3834
|
-
var KEYFRAME_MIME = "image/jpeg";
|
|
3835
|
-
var TRANSPORT_REPLAY_ATTEMPTS = 2;
|
|
3836
|
-
var TRANSPORT_REPLAY_DELAY_MS = 1e3;
|
|
3837
|
-
var MAX_RETRY_AFTER_MS = 1e4;
|
|
3838
|
-
var RESYNC_REENTRY_MAX = 3;
|
|
3839
|
-
var TRANSPORT_CODES = /* @__PURE__ */ new Set([
|
|
3840
|
-
"network_error",
|
|
3841
|
-
"timeout",
|
|
3842
|
-
"upload_failed",
|
|
3843
|
-
"unknown_error",
|
|
3844
|
-
"rate_limit_exceeded"
|
|
3845
|
-
]);
|
|
3846
|
-
var RESYNC_CODES = /* @__PURE__ */ new Set([
|
|
3847
|
-
"session_not_active",
|
|
3848
|
-
"check_not_found",
|
|
3849
|
-
"check_already_finalized",
|
|
3850
|
-
"check_out_of_order"
|
|
3851
|
-
]);
|
|
3852
|
-
var AttestationError = class extends Error {
|
|
3853
|
-
constructor(message) {
|
|
3854
|
-
super(message);
|
|
3855
|
-
this.name = "AttestationError";
|
|
3856
|
-
}
|
|
3857
|
-
};
|
|
3858
|
-
async function runSubmission(rt, executor, hooks = {}) {
|
|
3859
|
-
let key = rt.uuid();
|
|
3860
|
-
let consentRecovered = false;
|
|
3861
|
-
let attestationRetried = false;
|
|
3862
|
-
let transportReplays = 0;
|
|
3863
|
-
for (; ; ) {
|
|
3864
|
-
try {
|
|
3865
|
-
return await executor(rt.client, key);
|
|
3866
|
-
} catch (err) {
|
|
3867
|
-
if (err instanceof AttestationError) {
|
|
3868
|
-
if (attestationRetried) throw err;
|
|
3869
|
-
attestationRetried = true;
|
|
3870
|
-
hooks.onAttestationInvalid?.();
|
|
3871
|
-
key = rt.uuid();
|
|
3872
|
-
continue;
|
|
3873
|
-
}
|
|
3874
|
-
if (!(err instanceof HostedApiError)) throw err;
|
|
3875
|
-
if (err.code === "consent_required" && !consentRecovered) {
|
|
3876
|
-
consentRecovered = true;
|
|
3877
|
-
await rt.ensureConsent({ force: true });
|
|
3878
|
-
key = rt.uuid();
|
|
3879
|
-
continue;
|
|
3880
|
-
}
|
|
3881
|
-
if (err.code === "attestation_invalid" && !attestationRetried) {
|
|
3882
|
-
attestationRetried = true;
|
|
3883
|
-
hooks.onAttestationInvalid?.();
|
|
3884
|
-
key = rt.uuid();
|
|
3885
|
-
continue;
|
|
3886
|
-
}
|
|
3887
|
-
if (TRANSPORT_CODES.has(err.code) && transportReplays < TRANSPORT_REPLAY_ATTEMPTS) {
|
|
3888
|
-
transportReplays += 1;
|
|
3889
|
-
const retryAfterMs = err.retryAfterSeconds !== void 0 ? err.retryAfterSeconds * 1e3 : void 0;
|
|
3890
|
-
await rt.sleep(
|
|
3891
|
-
retryAfterMs !== void 0 ? Math.min(retryAfterMs, MAX_RETRY_AFTER_MS) : TRANSPORT_REPLAY_DELAY_MS
|
|
3892
|
-
);
|
|
3893
|
-
continue;
|
|
3894
|
-
}
|
|
3895
|
-
throw err;
|
|
3896
|
-
}
|
|
3897
|
-
}
|
|
3898
|
-
}
|
|
3899
|
-
var LIVENESS_CHAINABLE = /* @__PURE__ */ new Set(["passed", "review", "processing", "skipped"]);
|
|
3900
|
-
async function runLivenessFlow(deps) {
|
|
3901
|
-
const { artifacts } = deps;
|
|
3902
|
-
const clip = artifacts.clip;
|
|
3903
|
-
const bareMime = stripMimeParams(artifacts.clipMime ?? "");
|
|
3904
|
-
if (artifacts.degraded === "NO_RECORDER" || clip === void 0 || !CLIP_MIME_ALLOWLIST.includes(bareMime)) {
|
|
3905
|
-
return { kind: "unavailable", reason: "no_recorder" };
|
|
3906
|
-
}
|
|
3907
|
-
const submitLiveness = buildLivenessSubmit(deps, clip, bareMime);
|
|
3908
|
-
const submitFaceMatch = buildFaceMatchSubmit(deps, artifacts.keyframe);
|
|
3909
|
-
let livenessSubmitted = false;
|
|
3910
|
-
let faceMatchSubmitted = false;
|
|
3911
|
-
let livenessChainable = true;
|
|
3912
|
-
let resyncs = 0;
|
|
3913
|
-
for (; ; ) {
|
|
3914
|
-
if (deps.isCancelled()) return { kind: "cancelled" };
|
|
3915
|
-
const view = deps.getView();
|
|
3916
|
-
if (isTerminalView(view)) return { kind: "settled" };
|
|
3917
|
-
const frontier = frontierStep(view)?.checkType;
|
|
3918
|
-
const next = frontier === "liveness" && !livenessSubmitted ? "liveness" : frontier === "face_match" && !faceMatchSubmitted && livenessChainable ? "face_match" : null;
|
|
3919
|
-
if (next === null) return { kind: "settled" };
|
|
3920
|
-
try {
|
|
3921
|
-
if (next === "liveness") {
|
|
3922
|
-
livenessSubmitted = true;
|
|
3923
|
-
const response = await submitLiveness();
|
|
3924
|
-
if (deps.isCancelled()) return { kind: "cancelled" };
|
|
3925
|
-
deps.applySubmit(response, "liveness");
|
|
3926
|
-
const state = stepOf(deps.getView(), "liveness")?.state;
|
|
3927
|
-
livenessChainable = state === void 0 || LIVENESS_CHAINABLE.has(state);
|
|
3928
|
-
} else {
|
|
3929
|
-
faceMatchSubmitted = true;
|
|
3930
|
-
const response = await submitFaceMatch();
|
|
3931
|
-
if (deps.isCancelled()) return { kind: "cancelled" };
|
|
3932
|
-
deps.applySubmit(response, "face_match");
|
|
3933
|
-
}
|
|
3934
|
-
} catch (err) {
|
|
3935
|
-
if (deps.isCancelled()) return { kind: "cancelled" };
|
|
3936
|
-
const outcome = classifyFailure(err);
|
|
3937
|
-
if (outcome.kind !== "resync" || resyncs >= RESYNC_REENTRY_MAX) return outcome;
|
|
3938
|
-
resyncs += 1;
|
|
3939
|
-
await deps.refreshView();
|
|
3940
|
-
}
|
|
3941
|
-
}
|
|
3942
|
-
}
|
|
3943
|
-
function buildLivenessSubmit(deps, clip, bareMime) {
|
|
3944
|
-
let clipKey = null;
|
|
3945
|
-
let attestation = null;
|
|
3946
|
-
return () => runSubmission(
|
|
3947
|
-
deps,
|
|
3948
|
-
async (client, idempotencyKey) => {
|
|
3949
|
-
if (clipKey === null) {
|
|
3950
|
-
deps.onPhase("uploading");
|
|
3951
|
-
const presigned = await client.presign("clip", bareMime, clip.size);
|
|
3952
|
-
await client.uploadToSpaces(presigned.uploadUrl, clip, bareMime);
|
|
3953
|
-
clipKey = (await client.completeMedia(presigned.key)).key;
|
|
3954
|
-
}
|
|
3955
|
-
if (attestation === null) {
|
|
3956
|
-
try {
|
|
3957
|
-
attestation = await deps.getAttestation();
|
|
3958
|
-
} catch (err) {
|
|
3959
|
-
throw new AttestationError(err instanceof Error ? err.message : String(err));
|
|
3960
|
-
}
|
|
3961
|
-
}
|
|
3962
|
-
deps.onPhase("checking");
|
|
3963
|
-
return client.submitCheck(
|
|
3964
|
-
"liveness",
|
|
3965
|
-
{
|
|
3966
|
-
clipKey,
|
|
3967
|
-
attestation,
|
|
3968
|
-
captureChannel: deps.artifacts.captureChannel,
|
|
3969
|
-
capture: captureInputOf(deps.artifacts)
|
|
3970
|
-
},
|
|
3971
|
-
{ idempotencyKey }
|
|
3972
|
-
);
|
|
3973
|
-
},
|
|
3974
|
-
{
|
|
3975
|
-
onAttestationInvalid: () => {
|
|
3976
|
-
attestation = null;
|
|
3977
|
-
}
|
|
3978
|
-
}
|
|
3979
|
-
);
|
|
3980
|
-
}
|
|
3981
|
-
function captureInputOf(artifacts) {
|
|
3982
|
-
return {
|
|
3983
|
-
...artifacts.engine !== void 0 ? { engine: artifacts.engine } : {},
|
|
3984
|
-
width: artifacts.width,
|
|
3985
|
-
height: artifacts.height,
|
|
3986
|
-
...artifacts.clipMs !== void 0 ? { clipMs: artifacts.clipMs } : {},
|
|
3987
|
-
...artifacts.cameraReadyMs !== void 0 ? { cameraReadyMs: artifacts.cameraReadyMs } : {},
|
|
3988
|
-
...artifacts.faceAcquireMs !== void 0 ? { faceAcquireMs: artifacts.faceAcquireMs } : {},
|
|
3989
|
-
...artifacts.captureMs !== void 0 ? { captureMs: artifacts.captureMs } : {}
|
|
3990
|
-
};
|
|
3991
|
-
}
|
|
3992
|
-
function buildFaceMatchSubmit(deps, keyframe) {
|
|
3993
|
-
let imageKey = null;
|
|
3994
|
-
return () => runSubmission(deps, async (client, idempotencyKey) => {
|
|
3995
|
-
if (imageKey === null) {
|
|
3996
|
-
deps.onPhase("uploading");
|
|
3997
|
-
const presigned = await client.presign("image", KEYFRAME_MIME, keyframe.size);
|
|
3998
|
-
await client.uploadToSpaces(presigned.uploadUrl, keyframe, KEYFRAME_MIME);
|
|
3999
|
-
imageKey = (await client.completeMedia(presigned.key)).key;
|
|
4000
|
-
}
|
|
4001
|
-
deps.onPhase("checking");
|
|
4002
|
-
return client.submitCheck("face_match", { imageKey }, { idempotencyKey });
|
|
4003
|
-
});
|
|
4004
|
-
}
|
|
4005
|
-
function classifyFailure(err) {
|
|
4006
|
-
if (err instanceof AttestationError) return { kind: "unavailable", reason: "attestation" };
|
|
4007
|
-
if (err instanceof HostedApiError) {
|
|
4008
|
-
if (err.code === "attestation_invalid") return { kind: "unavailable", reason: "attestation" };
|
|
4009
|
-
if (RESYNC_CODES.has(err.code)) return { kind: "resync" };
|
|
4010
|
-
}
|
|
4011
|
-
return { kind: "unavailable", reason: "network" };
|
|
4012
|
-
}
|
|
4013
|
-
|
|
4014
|
-
// src/verification/turnstile.ts
|
|
4015
|
-
var TURNSTILE_SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js";
|
|
4016
|
-
var TURNSTILE_EXECUTE_TIMEOUT_MS = 3e4;
|
|
4017
|
-
function installedApi() {
|
|
4018
|
-
return window.turnstile;
|
|
4019
|
-
}
|
|
4020
|
-
var loaderPromise = null;
|
|
4021
|
-
function resetTurnstileForTests() {
|
|
4022
|
-
loaderPromise = null;
|
|
4023
|
-
}
|
|
4024
|
-
function loadTurnstile() {
|
|
4025
|
-
loaderPromise ?? (loaderPromise = new Promise((resolve, reject) => {
|
|
4026
|
-
const present = installedApi();
|
|
4027
|
-
if (present) {
|
|
4028
|
-
resolve(present);
|
|
4029
|
-
return;
|
|
4030
|
-
}
|
|
4031
|
-
const script = document.createElement("script");
|
|
4032
|
-
script.src = TURNSTILE_SCRIPT_SRC;
|
|
4033
|
-
script.async = true;
|
|
4034
|
-
script.onload = () => {
|
|
4035
|
-
const api = installedApi();
|
|
4036
|
-
if (api) {
|
|
4037
|
-
resolve(api);
|
|
4038
|
-
} else {
|
|
4039
|
-
loaderPromise = null;
|
|
4040
|
-
reject(new Error("Turnstile script loaded but exposed no API"));
|
|
4041
|
-
}
|
|
4042
|
-
};
|
|
4043
|
-
script.onerror = () => {
|
|
4044
|
-
loaderPromise = null;
|
|
4045
|
-
script.remove();
|
|
4046
|
-
reject(new Error("Turnstile script failed to load"));
|
|
4047
|
-
};
|
|
4048
|
-
document.head.appendChild(script);
|
|
4049
|
-
}));
|
|
4050
|
-
return loaderPromise;
|
|
4051
|
-
}
|
|
4052
|
-
function abortError() {
|
|
4053
|
-
return new Error("Turnstile challenge aborted");
|
|
4054
|
-
}
|
|
4055
|
-
async function executeTurnstile(siteKey, opts = {}) {
|
|
4056
|
-
const timeoutMs = opts.timeoutMs ?? TURNSTILE_EXECUTE_TIMEOUT_MS;
|
|
4057
|
-
const signal = opts.signal;
|
|
4058
|
-
if (signal?.aborted) throw abortError();
|
|
4059
|
-
const api = await loadTurnstile();
|
|
4060
|
-
if (signal?.aborted) throw abortError();
|
|
4061
|
-
const container = document.createElement("div");
|
|
4062
|
-
document.body.appendChild(container);
|
|
4063
|
-
let widgetId;
|
|
4064
|
-
let timer;
|
|
4065
|
-
let onAbort;
|
|
4066
|
-
try {
|
|
4067
|
-
return await new Promise((resolve, reject) => {
|
|
4068
|
-
timer = setTimeout(
|
|
4069
|
-
() => reject(new Error(`Turnstile challenge timed out (${timeoutMs} ms)`)),
|
|
4070
|
-
timeoutMs
|
|
4071
|
-
);
|
|
4072
|
-
if (signal !== void 0) {
|
|
4073
|
-
onAbort = () => reject(abortError());
|
|
4074
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
4075
|
-
}
|
|
4076
|
-
widgetId = api.render(container, {
|
|
4077
|
-
sitekey: siteKey,
|
|
4078
|
-
execution: "execute",
|
|
4079
|
-
appearance: "execute",
|
|
4080
|
-
callback: resolve,
|
|
4081
|
-
"error-callback": (code) => {
|
|
4082
|
-
reject(new Error(`Turnstile challenge failed${code ? ` (${code})` : ""}`));
|
|
4083
|
-
}
|
|
4084
|
-
});
|
|
4085
|
-
if (widgetId === void 0) {
|
|
4086
|
-
reject(new Error("Turnstile render returned no widget id"));
|
|
4087
|
-
return;
|
|
4088
|
-
}
|
|
4089
|
-
api.execute(widgetId);
|
|
4090
|
-
});
|
|
4091
|
-
} finally {
|
|
4092
|
-
clearTimeout(timer);
|
|
4093
|
-
if (signal !== void 0 && onAbort !== void 0) signal.removeEventListener("abort", onAbort);
|
|
4094
|
-
if (widgetId !== void 0) {
|
|
4095
|
-
try {
|
|
4096
|
-
api.remove(widgetId);
|
|
4097
|
-
} catch {
|
|
4098
|
-
}
|
|
4099
|
-
}
|
|
4100
|
-
container.remove();
|
|
4101
|
-
}
|
|
4102
|
-
}
|
|
4103
|
-
|
|
4104
|
-
// src/verification/create-verification.ts
|
|
4105
|
-
var DEFAULT_API_ORIGIN = "https://api.sekyro.ai";
|
|
4106
|
-
var REGISTRY_RETRY_DELAY_MS = 5e3;
|
|
4107
|
-
var PROCESSING_POLL_DELAY_MS = 1500;
|
|
4108
|
-
var PROCESSING_POLL_MAX = 10;
|
|
4109
|
-
function defaultSleep(ms) {
|
|
4110
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4111
|
-
}
|
|
4112
|
-
function randomUuid() {
|
|
4113
|
-
const c2 = globalThis.crypto;
|
|
4114
|
-
if (c2 !== void 0 && typeof c2.randomUUID === "function") return c2.randomUUID();
|
|
4115
|
-
const bytes = new Uint8Array(16);
|
|
4116
|
-
if (c2 !== void 0 && typeof c2.getRandomValues === "function") c2.getRandomValues(bytes);
|
|
4117
|
-
else for (let i2 = 0; i2 < 16; i2 += 1) bytes[i2] = Math.floor(Math.random() * 256);
|
|
4118
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
4119
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
4120
|
-
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4121
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
4122
|
-
}
|
|
4123
|
-
function createVerificationInternal(options, internals) {
|
|
4124
|
-
const apiOrigin = options.apiOrigin ?? DEFAULT_API_ORIGIN;
|
|
4125
|
-
const autoRetry = options.autoRetry ?? true;
|
|
4126
|
-
const rearm = options.rearm ?? "auto";
|
|
4127
|
-
const client = internals.client ?? new HostedClient({
|
|
4128
|
-
apiOrigin,
|
|
4129
|
-
sessionToken: options.clientToken,
|
|
4130
|
-
...options.fetch !== void 0 ? { fetch: options.fetch } : {}
|
|
4131
|
-
});
|
|
4132
|
-
const sleep2 = internals.sleep ?? defaultSleep;
|
|
4133
|
-
const uuid = internals.uuid ?? randomUuid;
|
|
4134
|
-
const getTurnstileToken = internals.getTurnstileToken ?? executeTurnstile;
|
|
4135
|
-
const emitter = createEmitter();
|
|
4136
|
-
let destroyed = false;
|
|
4137
|
-
const lifetime = new AbortController();
|
|
4138
|
-
let view = null;
|
|
4139
|
-
let bootstrap = null;
|
|
4140
|
-
const setView = (next) => {
|
|
4141
|
-
view = next;
|
|
4142
|
-
emitter.emit("view", next);
|
|
4143
|
-
};
|
|
4144
|
-
const ensureView = () => {
|
|
4145
|
-
if (view !== null) return Promise.resolve(view);
|
|
4146
|
-
bootstrap ?? (bootstrap = client.getSession().then((next) => {
|
|
4147
|
-
setView(next);
|
|
4148
|
-
return next;
|
|
4149
|
-
}).finally(() => {
|
|
4150
|
-
bootstrap = null;
|
|
4151
|
-
}));
|
|
4152
|
-
return bootstrap;
|
|
4153
|
-
};
|
|
4154
|
-
const refreshView = async () => {
|
|
4155
|
-
const next = await client.getSession();
|
|
4156
|
-
setView(next);
|
|
4157
|
-
return next;
|
|
4158
|
-
};
|
|
4159
|
-
const applySubmit = (response, submitted) => {
|
|
4160
|
-
if (view !== null) setView(mergeSessionView(view, response, submitted));
|
|
4161
|
-
};
|
|
4162
|
-
let consentInFlight = null;
|
|
4163
|
-
const recordConsent = async (force) => {
|
|
4164
|
-
const current = await ensureView();
|
|
4165
|
-
if (!force && current.consentRecorded) return;
|
|
4166
|
-
await client.recordConsent(options.locale);
|
|
4167
|
-
if (view !== null) setView({ ...view, consentRecorded: true });
|
|
4168
|
-
};
|
|
4169
|
-
const ensureConsent = (opts) => {
|
|
4170
|
-
if (opts?.force === true) return recordConsent(true);
|
|
4171
|
-
consentInFlight ?? (consentInFlight = recordConsent(false).finally(() => {
|
|
4172
|
-
consentInFlight = null;
|
|
4173
|
-
}));
|
|
4174
|
-
return consentInFlight;
|
|
4175
|
-
};
|
|
4176
|
-
const runtime = {
|
|
4177
|
-
client,
|
|
4178
|
-
ensureConsent: (opts) => ensureConsent(opts),
|
|
4179
|
-
sleep: sleep2,
|
|
4180
|
-
uuid
|
|
4181
|
-
};
|
|
4182
|
-
let terminalEmitted = false;
|
|
4183
|
-
const emitResult = (result) => {
|
|
4184
|
-
if (result.status === "approved" || result.status === "rejected" || result.status === "review") {
|
|
4185
|
-
if (terminalEmitted) return;
|
|
4186
|
-
terminalEmitted = true;
|
|
4187
|
-
}
|
|
4188
|
-
emitter.emit("result", result);
|
|
4189
|
-
};
|
|
4190
|
-
let registry2 = null;
|
|
4191
|
-
let registryFailed = false;
|
|
4192
|
-
let registryUnbound = false;
|
|
4193
|
-
const submitRegistry = () => runSubmission(
|
|
4194
|
-
runtime,
|
|
4195
|
-
(c2, idempotencyKey) => (
|
|
4196
|
-
// `{}` input: the session is bound to a SERVER-provided NNI (Plan A Task 2).
|
|
4197
|
-
c2.submitCheck("population_registry", {}, { idempotencyKey })
|
|
4198
|
-
)
|
|
4199
|
-
);
|
|
4200
|
-
const runRegistry = async () => {
|
|
4201
|
-
try {
|
|
4202
|
-
const current = await ensureView();
|
|
4203
|
-
if (isTerminalView(current) || frontierStep(current)?.checkType !== "population_registry") {
|
|
4204
|
-
return;
|
|
4205
|
-
}
|
|
4206
|
-
if (current.nniBound !== true) {
|
|
4207
|
-
registryUnbound = true;
|
|
4208
|
-
return;
|
|
4209
|
-
}
|
|
4210
|
-
await ensureConsent();
|
|
4211
|
-
applySubmit(await submitRegistry(), "population_registry");
|
|
4212
|
-
const step = view === null ? void 0 : stepOf(view, "population_registry");
|
|
4213
|
-
if (step?.state === "input_required" && step.reason === "registry_unavailable" && step.attemptsRemaining > 0) {
|
|
4214
|
-
await sleep2(REGISTRY_RETRY_DELAY_MS);
|
|
4215
|
-
if (destroyed) return;
|
|
4216
|
-
applySubmit(await submitRegistry(), "population_registry");
|
|
4217
|
-
}
|
|
4218
|
-
} catch {
|
|
4219
|
-
registryFailed = true;
|
|
4220
|
-
return;
|
|
4221
|
-
}
|
|
4222
|
-
if (view !== null && isTerminalView(view)) emitResult(mapSessionResult(view, { autoRetry }));
|
|
4223
|
-
};
|
|
4224
|
-
const startRegistry = () => {
|
|
4225
|
-
registry2 ?? (registry2 = runRegistry());
|
|
4226
|
-
return registry2;
|
|
4227
|
-
};
|
|
4228
|
-
const getAttestation = async () => {
|
|
4229
|
-
const current = await ensureView();
|
|
4230
|
-
const siteKey = current.turnstileSiteKey;
|
|
4231
|
-
if (siteKey === null) return { sandbox: true };
|
|
4232
|
-
return {
|
|
4233
|
-
channel: "hosted_web",
|
|
4234
|
-
siteKey,
|
|
4235
|
-
turnstileToken: await getTurnstileToken(siteKey, { signal: lifetime.signal })
|
|
4236
|
-
};
|
|
4237
|
-
};
|
|
4238
|
-
let active = null;
|
|
4239
|
-
let chain = Promise.resolve();
|
|
4240
|
-
let chainDepth = 0;
|
|
4241
|
-
const settleProcessing = async () => {
|
|
4242
|
-
let current = await ensureView();
|
|
4243
|
-
for (let i2 = 0; i2 < PROCESSING_POLL_MAX && !destroyed && !isTerminalView(current) && frontierStep(current) === void 0 && current.steps.some((step) => step.state === "processing"); i2 += 1) {
|
|
4244
|
-
await sleep2(PROCESSING_POLL_DELAY_MS);
|
|
4245
|
-
if (destroyed) break;
|
|
4246
|
-
current = await refreshView();
|
|
4247
|
-
}
|
|
4248
|
-
return current;
|
|
4249
|
-
};
|
|
4250
|
-
const runFlow = async (bound, artifacts, capturedAttempt) => {
|
|
4251
|
-
const { capture } = bound;
|
|
4252
|
-
const isCancelled = () => destroyed;
|
|
4253
|
-
const ownsHandle = () => bound.alive && bound.attempt === capturedAttempt;
|
|
4254
|
-
const setPhase = (phase) => {
|
|
4255
|
-
if (ownsHandle()) capture.setFlowState(phase);
|
|
4256
|
-
};
|
|
4257
|
-
const finish = (result) => {
|
|
4258
|
-
if (isCancelled()) return;
|
|
4259
|
-
emitResult(result);
|
|
4260
|
-
if (!ownsHandle()) return;
|
|
4261
|
-
if (result.status === "retry" && rearm === "auto") {
|
|
4262
|
-
capture.handle.retry();
|
|
4263
|
-
return;
|
|
4264
|
-
}
|
|
4265
|
-
capture.setFlowState("done");
|
|
4266
|
-
};
|
|
4267
|
-
if (artifacts.degraded === "NO_RECORDER") {
|
|
4268
|
-
const sessionId = view?.sessionId ?? (await ensureView().catch(() => null))?.sessionId ?? "";
|
|
4269
|
-
finish(unavailable(sessionId, "no_recorder"));
|
|
4270
|
-
return;
|
|
4271
|
-
}
|
|
4272
|
-
setPhase("uploading");
|
|
4273
|
-
try {
|
|
4274
|
-
await startRegistry();
|
|
4275
|
-
if (isCancelled()) return;
|
|
4276
|
-
let current = await ensureView();
|
|
4277
|
-
if (!isTerminalView(current)) {
|
|
4278
|
-
if (frontierStep(current)?.checkType === "population_registry") {
|
|
4279
|
-
finish(
|
|
4280
|
-
unavailable(
|
|
4281
|
-
current.sessionId,
|
|
4282
|
-
registryUnbound ? "nni_unbound" : registryFailed ? "network" : "registry_unavailable"
|
|
4283
|
-
)
|
|
4284
|
-
);
|
|
4285
|
-
return;
|
|
4286
|
-
}
|
|
4287
|
-
await ensureConsent();
|
|
4288
|
-
if (isCancelled()) return;
|
|
4289
|
-
const outcome = await runLivenessFlow({
|
|
4290
|
-
...runtime,
|
|
4291
|
-
artifacts,
|
|
4292
|
-
getAttestation,
|
|
4293
|
-
getView: () => view ?? current,
|
|
4294
|
-
refreshView,
|
|
4295
|
-
applySubmit,
|
|
4296
|
-
onPhase: setPhase,
|
|
4297
|
-
isCancelled
|
|
4298
|
-
});
|
|
4299
|
-
if (isCancelled()) return;
|
|
4300
|
-
if (outcome.kind === "unavailable") {
|
|
4301
|
-
finish(unavailable(current.sessionId, outcome.reason));
|
|
4302
|
-
return;
|
|
4303
|
-
}
|
|
4304
|
-
if (outcome.kind === "resync") await refreshView();
|
|
4305
|
-
current = await settleProcessing();
|
|
4306
|
-
}
|
|
4307
|
-
finish(mapSessionResult(current, { autoRetry }));
|
|
4308
|
-
} catch {
|
|
4309
|
-
if (isCancelled()) return;
|
|
4310
|
-
finish(unavailable(view?.sessionId ?? "", "network"));
|
|
4311
|
-
}
|
|
4312
|
-
};
|
|
4313
|
-
const enqueueFlow = (bound, artifacts) => {
|
|
4314
|
-
const capturedAttempt = bound.attempt;
|
|
4315
|
-
if (chainDepth > 0 && bound.alive) {
|
|
4316
|
-
bound.capture.setFlowState("uploading");
|
|
4317
|
-
}
|
|
4318
|
-
chainDepth += 1;
|
|
4319
|
-
chain = chain.then(() => runFlow(bound, artifacts, capturedAttempt)).finally(() => {
|
|
4320
|
-
chainDepth -= 1;
|
|
4321
|
-
}).catch(() => void 0);
|
|
4322
|
-
};
|
|
4323
|
-
const liveness = (captureOptions) => {
|
|
4324
|
-
active?.capture.handle.destroy();
|
|
4325
|
-
if (active !== null) active.alive = false;
|
|
4326
|
-
const capture = createLivenessCaptureInternal(captureOptions, internals.capture);
|
|
4327
|
-
const bound = { capture, alive: true, attempt: 0 };
|
|
4328
|
-
active = bound;
|
|
4329
|
-
capture.handle.on("state", (state) => {
|
|
4330
|
-
if (state === "starting") bound.attempt += 1;
|
|
4331
|
-
});
|
|
4332
|
-
capture.handle.on("captured", (artifacts) => {
|
|
4333
|
-
void Promise.resolve().then(() => enqueueFlow(bound, artifacts));
|
|
4334
|
-
});
|
|
4335
|
-
const inner = capture.handle;
|
|
4336
|
-
return {
|
|
4337
|
-
get state() {
|
|
4338
|
-
return inner.state;
|
|
4339
|
-
},
|
|
4340
|
-
on: (event, handler) => inner.on(event, handler),
|
|
4341
|
-
start: (startOptions) => inner.start(startOptions),
|
|
4342
|
-
retry: () => inner.retry(),
|
|
4343
|
-
switchCamera: () => inner.switchCamera(),
|
|
4344
|
-
destroy: () => {
|
|
4345
|
-
bound.alive = false;
|
|
4346
|
-
if (active === bound) active = null;
|
|
4347
|
-
inner.destroy();
|
|
4348
|
-
}
|
|
4349
|
-
};
|
|
4350
|
-
};
|
|
4351
|
-
return {
|
|
4352
|
-
consent: () => ensureConsent(),
|
|
4353
|
-
startRegistry,
|
|
4354
|
-
liveness,
|
|
4355
|
-
on: (event, handler) => emitter.on(event, handler),
|
|
4356
|
-
destroy: () => {
|
|
4357
|
-
destroyed = true;
|
|
4358
|
-
lifetime.abort();
|
|
4359
|
-
if (active !== null) {
|
|
4360
|
-
active.alive = false;
|
|
4361
|
-
active.capture.handle.destroy();
|
|
4362
|
-
active = null;
|
|
4363
|
-
}
|
|
4364
|
-
emitter.clear();
|
|
4365
|
-
}
|
|
4366
|
-
};
|
|
4367
|
-
}
|
|
4368
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
4369
|
-
0 && (module.exports = {
|
|
4370
|
-
ASPECT_BANDS,
|
|
4371
|
-
AttestationError,
|
|
4372
|
-
CAPTURE_CANDIDATE_SPACING_MS,
|
|
4373
|
-
CAPTURE_EXTRA_CANDIDATES,
|
|
4374
|
-
CENTERED_FACTOR,
|
|
4375
|
-
CIRCLE_RADIUS_FACTOR,
|
|
4376
|
-
CLIP_MIME_ALLOWLIST,
|
|
4377
|
-
CaptureSessionDoc,
|
|
4378
|
-
DOC_ANALYSIS_DEFAULTS,
|
|
4379
|
-
DOC_CADENCE_HZ,
|
|
4380
|
-
DOC_DETECT_WIDTH,
|
|
4381
|
-
DOC_FEEDBACK_STATES,
|
|
4382
|
-
DOC_GATE_V1_DEFAULTS,
|
|
4383
|
-
DocFeedbackAggregator,
|
|
4384
|
-
DocGateV1,
|
|
4385
|
-
IN_CIRCLE_FACTOR,
|
|
4386
|
-
MRZ_BAND_DEFAULTS,
|
|
4387
|
-
MR_DOC_CATALOG,
|
|
4388
|
-
PRELOAD_BLAZE_INIT_TIMEOUT_MS,
|
|
4389
|
-
PRELOAD_BUDGET_MS,
|
|
4390
|
-
PROCESSING_POLL_DELAY_MS,
|
|
4391
|
-
PROCESSING_POLL_MAX,
|
|
4392
|
-
REGISTRY_RETRY_DELAY_MS,
|
|
4393
|
-
RETRYABLE_REASONS,
|
|
4394
|
-
TRANSPORT_REPLAY_ATTEMPTS,
|
|
4395
|
-
TRANSPORT_REPLAY_DELAY_MS,
|
|
4396
|
-
TURNSTILE_EXECUTE_TIMEOUT_MS,
|
|
4397
|
-
TURNSTILE_SCRIPT_SRC,
|
|
4398
|
-
analyzeDocFrame,
|
|
4399
|
-
aspectBand,
|
|
4400
|
-
createLivenessCaptureInternal,
|
|
4401
|
-
createVerificationInternal,
|
|
4402
|
-
detectMrzBand,
|
|
4403
|
-
executeTurnstile,
|
|
4404
|
-
feedbackVoteOf,
|
|
4405
|
-
frontierStep,
|
|
4406
|
-
isExecutable,
|
|
4407
|
-
isTerminalView,
|
|
4408
|
-
loadTurnstile,
|
|
4409
|
-
mapEngineState,
|
|
4410
|
-
mapErrorKind,
|
|
4411
|
-
mapGuidanceHint,
|
|
4412
|
-
mapSessionResult,
|
|
4413
|
-
mergeSessionView,
|
|
4414
|
-
preloadedDetectorFactory,
|
|
4415
|
-
quadAreaFrac,
|
|
4416
|
-
quadBounds,
|
|
4417
|
-
quadIou,
|
|
4418
|
-
quadTiltRatio,
|
|
4419
|
-
randomUuid,
|
|
4420
|
-
resetLivenessAssetsForTests,
|
|
4421
|
-
resetTurnstileForTests,
|
|
4422
|
-
runLivenessFlow,
|
|
4423
|
-
runSubmission,
|
|
4424
|
-
setPreloadLoaderForTests,
|
|
4425
|
-
specularFraction,
|
|
4426
|
-
stepOf,
|
|
4427
|
-
warmDocDetector
|
|
4428
|
-
});
|
|
4429
|
-
//# sourceMappingURL=index.cjs.map
|
|
1
|
+
"use strict";var Si=Object.create;var WA=Object.defineProperty;var Ri=Object.getOwnPropertyDescriptor;var vi=Object.getOwnPropertyNames;var ki=Object.getPrototypeOf,Mi=Object.prototype.hasOwnProperty;var hA=(e,A)=>()=>(e&&(A=e(e=0)),A);var Ne=(e,A)=>{for(var t in A)WA(e,t,{get:A[t],enumerable:!0})},pt=(e,A,t,i)=>{if(A&&typeof A=="object"||typeof A=="function")for(let n of vi(A))!Mi.call(e,n)&&n!==t&&WA(e,n,{get:()=>A[n],enumerable:!(i=Ri(A,n))||i.enumerable});return e};var xi=(e,A,t)=>(t=e!=null?Si(ki(e)):{},pt(A||!e||!e.__esModule?WA(t,"default",{value:e,enumerable:!0}):t,e)),Ni=e=>pt(WA({},"__esModule",{value:!0}),e);function wA(e){return yt[e]}var yt,Fi,XA=hA(()=>{"use strict";yt={"ID-1":{center:1.586,tolerance:.15},"ID-3":{center:1.42,tolerance:.2}};Fi=[{docType:"carte_identite",format:"ID-1",sides:["recto","verso"],mrz:"td1",portraitSide:"recto",aspectRatio:1.586,identifierSource:"mrz"},{docType:"passport",format:"ID-3",sides:["single"],mrz:"td3",portraitSide:"single",aspectRatio:1.42,identifierSource:"mrz"}]});function wt(e,A){return .299*(e[A]??0)+.587*(e[A+1]??0)+.114*(e[A+2]??0)}function DA(e){let{data:A,width:t,height:i}=e,n=t*i;if(n===0)return 0;let o=0;for(let r=0;r<n;r+=1)o+=wt(A,r*4);return o/n}function QA(e){let{data:A,width:t,height:i}=e;if(t<3||i<3)return 0;let n=new Float32Array(t*i);for(let g=0;g<n.length;g+=1)n[g]=wt(A,g*4);let o=0,r=0,s=0;for(let g=1;g<i-1;g+=1)for(let l=1;l<t-1;l+=1){let d=g*t+l,C=(n[d-t]??0)+(n[d-1]??0)+(n[d+1]??0)+(n[d+t]??0)-4*(n[d]??0);o+=C,r+=C*C,s+=1}if(s===0)return 0;let a=o/s,c=r/s-a*a;return Math.min(1,Math.max(0,c)/180)}function zA(e){let A=DA(e),[t,i]=nA,n=A<t?t-A:A>i?A-i:0;return Math.max(0,1-n/Li)}var nA,Li,mA=hA(()=>{"use strict";nA=[100,190],Li=64});function jA(e,A,t){let i=[e.topLeft,e.topRight,e.bottomRight,e.bottomLeft],n=0;for(let r=0;r<4;r++){let s=i[r],a=i[(r+1)%4];n+=s.x*a.y-a.x*s.y}let o=A*t;return o>0?Math.abs(n/2)/o:0}function Dt(e,A){let t=Le(e),i=Le(A),n=Math.max(t.x1,i.x1),o=Math.max(t.y1,i.y1),r=Math.min(t.x2,i.x2),s=Math.min(t.y2,i.y2),a=Math.max(0,r-n)*Math.max(0,s-o),c=(t.x2-t.x1)*(t.y2-t.y1),g=(i.x2-i.x1)*(i.y2-i.y1),l=c+g-a;return l>0?a/l:0}function SA(e){let A=Le(e);return{width:A.x2-A.x1,height:A.y2-A.y1}}function Le(e){let A=[e.topLeft,e.topRight,e.bottomRight,e.bottomLeft];return{x1:Math.min(...A.map(t=>t.x)),y1:Math.min(...A.map(t=>t.y)),x2:Math.max(...A.map(t=>t.x)),y2:Math.max(...A.map(t=>t.y))}}function Ti(e){let A=Math.hypot(e.topRight.x-e.topLeft.x,e.topRight.y-e.topLeft.y),t=Math.hypot(e.bottomRight.x-e.bottomLeft.x,e.bottomRight.y-e.bottomLeft.y),i=Math.hypot(e.bottomLeft.x-e.topLeft.x,e.bottomLeft.y-e.topLeft.y),n=Math.hypot(e.bottomRight.x-e.topRight.x,e.bottomRight.y-e.topRight.y),o=(A+t)/2,r=(i+n)/2;return o<=0||r<=0?0:Math.max(o,r)/Math.min(o,r)}var dA,ZA,Fe,bA,RA=hA(()=>{"use strict";mA();XA();dA=480,ZA=Object.freeze({MIN_AREA_FRAC:.15,EDGE_CLIP_MARGIN_PX:4,STABLE_TICKS:3,IOU_STABLE_MIN:.85,RISE_PER_S:.9,FALL_PER_S:.25,GUIDE_EXPOSURE:.6,GUIDE_SHARPNESS:.35,ID3_BAND_MIN_WIDTH_FRAC:.55,ID3_BAND_ASPECT_MIN:4,ID3_BAND_ASPECT_MAX:40}),Fe=e=>Math.min(1,Math.max(0,e));bA=class{constructor(A,t={}){this.p=0;this.lastTickAt=null;this.lastQuad=null;this.streak=0;this.format=A,this.t={...ZA,...t}}reset(){this.p=0,this.lastTickAt=null,this.lastQuad=null,this.streak=0}tick(A){let{quad:t,quality:i,timestampMs:n}=A,o=this.lastTickAt===null?0:Math.max(0,(n-this.lastTickAt)/1e3);this.lastTickAt=n;let r=null;if(t===null)r="dcv1.no_doc";else if(this.format==="ID-3"){let u=SA(t),E=u.height>0?u.width/u.height:0;u.width<this.t.ID3_BAND_MIN_WIDTH_FRAC*A.frameWidth?r="dcv1.come_closer":this.edgeClipped(t,A.frameWidth,A.frameHeight)?r="dcv1.fit_document":(E<this.t.ID3_BAND_ASPECT_MIN||E>this.t.ID3_BAND_ASPECT_MAX)&&(r="dcv1.wrong_shape")}else if(jA(t,A.frameWidth,A.frameHeight)<this.t.MIN_AREA_FRAC)r="dcv1.come_closer";else if(this.edgeClipped(t,A.frameWidth,A.frameHeight))r="dcv1.fit_document";else{let u=wA(this.format),E=Ti(t);Math.abs(E-u.center)>u.tolerance&&(r="dcv1.wrong_shape")}let s=r===null,a=Fe(i.exposure)>=this.t.GUIDE_EXPOSURE,c=Fe(i.sharpness)>=this.t.GUIDE_SHARPNESS,g=a&&c;if(s&&g&&t!==null){let u=this.lastQuad===null||Dt(t,this.lastQuad)>=this.t.IOU_STABLE_MIN;this.streak=u?this.streak+1:1}else this.streak=0;this.lastQuad=t;let l=s&&g;this.p=Fe(this.p+(l?this.t.RISE_PER_S:-this.t.FALL_PER_S)*o);let d=l&&this.streak>=this.t.STABLE_TICKS,C;if(r!==null)C=r;else if(a)c?C="dcv1.hold_still":C="dcv1.steady";else{let u=(nA[0]+nA[1])/2;C=i.meanLuma<u?"dcv1.more_light":"dcv1.too_bright"}return{at:n,quad:t,plausible:s,p:this.p,guidanceKey:C,ready:d,stableTicks:this.streak}}edgeClipped(A,t,i){let n=this.t.EDGE_CLIP_MARGIN_PX;return[A.topLeft,A.topRight,A.bottomRight,A.bottomLeft].some(r=>r.x<=n||r.y<=n||r.x>=t-n||r.y>=i-n)}}});var _t={};Ne(_t,{default:()=>qi,initMlDetector:()=>Oi});function Oi(){throw new Error("scanic ML detector is not vendored (classical pipeline only)")}var qi,Ht=hA(()=>{"use strict";qi={}});function Wi(e,A={}){let t=A.width||Math.sqrt(e.length),i=A.height||e.length/t,n=A.mode!==void 0?A.mode:1,o=A.method!==void 0?A.method:2,r=A.minArea||Ki,s=t+2,a=i+2,c=new Int32Array(s*a);for(let C=0;C<i;C++)for(let u=0;u<t;u++)e[C*t+u]>0&&(c[(C+1)*s+(u+1)]=1);let g=[],l=2;for(let C=1;C<=i;C++)for(let u=1;u<=t;u++){let E=c[C*s+u],p=c[C*s+(u-1)],I=null,Q=!1,D=-1;if(E===1&&p===0?(Q=!0,I={x:u,y:C},D=2):E===0&&p>=1&&p!==-1&&p===1&&(Q=!1,I={x:u-1,y:C},D=6),I){if(n===0&&!Q){c[I.y*s+I.x]=-1;continue}let w=l++,R=Xi(c,s,a,I,D,w);if(R&&R.length>0){let m=R;o===2&&(m=zi(R));let f=m.map(b=>({x:b.x-1,y:b.y-1}));if(f.length>=(o===2?4:Vi)){let b={id:w,points:f,isOuter:Q};g.push(b)}}else c[I.y*s+I.x]===1&&(c[I.y*s+I.x]=w)}}g.forEach(C=>{C.area=(function(u){let E=0,p=u.length;if(p<3)return 0;for(let I=0;I<p;I++){let Q=(I+1)%p;E+=u[I].x*u[Q].y,E-=u[Q].x*u[I].y}return Math.abs(E)/2})(C.points),C.boundingBox=(function(u){if(u.length===0)return{minX:0,minY:0,maxX:0,maxY:0};let E=u[0].x,p=u[0].y,I=u[0].x,Q=u[0].y;for(let D=1;D<u.length;D++){let w=u[D];E=Math.min(E,w.x),p=Math.min(p,w.y),I=Math.max(I,w.x),Q=Math.max(Q,w.y)}return{minX:E,minY:p,maxX:I,maxY:Q}})(C.points)});let d=g.filter(C=>C.area>=r);return d.sort((C,u)=>u.area-C.area),A.debug&&(A.debug.labels=c,A.debug.rawContours=g,A.debug.finalContours=d),d}function Xi(e,A,t,i,n,o){let r=[],s=new Set,a=i.x,c=i.y,g=a,l=c,d=-1;e[l*A+g]=o;let C=0,u=A*t,E=[0,1,1,1,0,-1,-1,-1],p=[-1,-1,0,1,1,1,0,-1];for(;C++<u;){let I;if(d===-1){let m=!1;for(let f=0;f<8;f++){I=n+f&7;let b=a+E[I],h=c+p[I];if(b>=0&&b<A&&h>=0&&h<t&&e[h*A+b]>0){m=!0;break}}if(!m)return null}else I=d+2&7;let Q=-1,D=-1;for(let m=0;m<8;m++){let f=I+m&7,b=a+E[f],h=c+p[f];if(b>=0&&b<A&&h>=0&&h<t&&e[h*A+b]>0){Q=b,D=h,d=f+4&7;break}}if(Q===-1){r.length===0&&r.push({x:a,y:c});break}let w=c*A+a;if(s.has(w))return r;r.push({x:a,y:c}),s.add(w);let R=D*A+Q;if(e[R]===1&&(e[R]=o),a=Q,c=D,a===g&&c===l)break}return C>=u?null:r}function zi(e){let A=e.length;if(A<=2)return e;let t=[],i=e[A-1],n=e[0],o=i,r=n,s=e[1],a=r.x-o.x,c=r.y-o.y,g=s.x-r.x,l=s.y-r.y;a*l!==c*g&&t.push(r);for(let d=1;d<A-1;d++)o=e[d-1],r=e[d],s=e[d+1],a=r.x-o.x,c=r.y-o.y,g=s.x-r.x,l=s.y-r.y,a*l!==c*g&&t.push(r);if(o=e[A-2],r=i,s=n,a=r.x-o.x,c=r.y-o.y,g=s.x-r.x,l=s.y-r.y,a*l!==c*g&&t.push(r),t.length===0){if(A===1)return[e[0]];if(A===2)return e;let d=0,C=1,u=n.x,E=n.y;for(let p=1;p<A;p++){let I=e[p],Q=I.x-u,D=I.y-E,w=Q*Q+D*D;w>d&&(d=w,C=p)}return[n,e[C]]}return t}function Ke(e,A=1){if(e.length<=2)return e;let t=0,i=0,n=e[0],o=e[e.length-1];for(let r=1;r<e.length-1;r++){let s=Zi(e[r],n,o);s>t&&(t=s,i=r)}if(t>A){let r=Ke(e.slice(0,i+1),A),s=Ke(e.slice(i),A);return r.slice(0,-1).concat(s)}return[n,o]}function Zi(e,A,t){let i=t.x-A.x,n=t.y-A.y,o=i*i+n*n;if(o===0)return Math.sqrt(Math.pow(e.x-A.x,2)+Math.pow(e.y-A.y,2));let r=((e.x-A.x)*i+(e.y-A.y)*n)/o,s,a;r<0?(s=A.x,a=A.y):r>1?(s=t.x,a=t.y):(s=A.x+r*i,a=A.y+r*n);let c=e.x-s,g=e.y-a;return Math.sqrt(c*c+g*g)}function Kt(e,A=.02){return Ke(e,A*(function(t){let i=0,n=t.length;if(n<2)return 0;for(let o=0;o<n;o++){let r=(o+1)%n,s=t[o].x-t[r].x,a=t[o].y-t[r].y;i+=Math.sqrt(s*s+a*a)}return i})(e))}function Ve(e,A){return Math.hypot(A.x-e.x,A.y-e.y)}function Ut(e){let A=0,t=0;for(let i of e)A+=i.x,t+=i.y;return{x:A/e.length,y:t/e.length}}function ji(e,A={}){if(!e||!e.points||e.points.length<4)return null;let t=A.epsilon||.02,i=(function(o,r){if(!o||o.length<4)return null;let s=[r,1.3*r,1.6*r,2*r],a=new Set;for(let c of s){let g=Math.min(.12,c),l=g.toFixed(4);if(a.has(l))continue;a.add(l);let d=$i(Kt(o,g));if(d&&d.length===4)return d}return null})(e.points,t),n;return i&&i.length===4?n=(function(o){if(o.length!==4)return null;let r=Ut(o),s=[...o].sort((l,d)=>Math.atan2(l.y-r.y,l.x-r.x)-Math.atan2(d.y-r.y,d.x-r.x)),a=1/0,c=0;for(let l=0;l<4;l++){let d=s[l].x+s[l].y;d<a&&(a=d,c=l)}let g=[s[c],s[(c+1)%4],s[(c+2)%4],s[(c+3)%4]];return{topLeft:g[0],topRight:g[1],bottomRight:g[2],bottomLeft:g[3]}})(i):(n=(function(o){if(!o||o.length<4)return null;let r=Ut(o),s=null,a=null,c=null,g=null,l=0,d=0,C=0,u=0;for(let E of o){let p=Ve(E,r);E.x<r.x&&E.y<r.y?p>l&&(s=E,l=p):E.x>r.x&&E.y<r.y?p>d&&(a=E,d=p):E.x>r.x&&E.y>r.y?p>C&&(c=E,C=p):E.x<r.x&&E.y>r.y&&p>u&&(g=E,u=p)}return s&&a&&c&&g?{topLeft:s,topRight:a,bottomRight:c,bottomLeft:g}:null})(e.points),n&&n.topLeft&&n.topRight&&n.bottomRight&&n.bottomLeft||(n=(function(o){if(!o||o.length===0)return null;let r=o[0],s=o[0],a=o[0],c=o[0],g=r.x+r.y,l=s.x-s.y,d=a.x+a.y,C=c.x-c.y;for(let u=1;u<o.length;u++){let E=o[u],p=E.x+E.y,I=E.x-E.y;p<g&&(g=p,r=E),p>d&&(d=p,a=E),I>l&&(l=I,s=E),I<C&&(C=I,c=E)}return{topLeft:r,topRight:s,bottomRight:a,bottomLeft:c}})(e.points))),n&&n.topLeft&&n.topRight&&n.bottomRight&&n.bottomLeft?n:null}function $i(e){if(!e||e.length<4)return null;if(e.length===4)return e;if(e.length>8)return null;let A=(function(t,i){if(!t||t.length===0)return[];let n=[];for(let o of t)(n.length===0||Ve(o,n[n.length-1])>i)&&n.push(o);return n.length>2&&Ve(n[0],n[n.length-1])<=i&&n.pop(),n})(e,6);return A.length===4?A:(A.length,null)}function Vt(){return xA!==null&&xA.byteLength!==0||(xA=new Uint8Array(W.memory.buffer)),xA}function Oe(e,A){let t=A(1*e.length,1)>>>0;return Vt().set(e,t/1),aA=e.length,t}function ae(e,A){return e>>>=0,Vt().subarray(e/1,e/1+A)}function Wt(){return NA!==null&&NA.byteLength!==0||(NA=new Float32Array(W.memory.buffer)),NA}function An(e,A,t,i,n){let o=(function(c,g){let l=g(4*c.length,4)>>>0;return Wt().set(c,l/4),aA=c.length,l})(e,W.__wbindgen_malloc),r=aA,s=W.hysteresis_thresholding(o,r,A,t,i,n);var a=ae(s[0],s[1]).slice();return W.__wbindgen_free(s[0],1*s[1],1),a}function Jt(e,A){let t=A(2*e.length,2)>>>0;return(FA!==null&&FA.byteLength!==0||(FA=new Uint16Array(W.memory.buffer)),FA).set(e,t/2),aA=e.length,t}function en(e,A,t,i,n){let o=Jt(e,W.__wbindgen_malloc),r=aA,s=Jt(A,W.__wbindgen_malloc),a=aA,c=W.non_maximum_suppression(o,r,s,a,t,i,n);var g,l,d=(g=c[0],l=c[1],g>>>=0,Wt().subarray(g/4,g/4+l)).slice();return W.__wbindgen_free(c[0],4*c[1],4),d}async function Xt(e){if(W!==void 0)return W;if(e!==void 0&&Object.getPrototypeOf(e)===Object.prototype&&({module_or_path:e}=e),e===void 0)throw new Error("scanic: WASM module bytes must be passed to init().");let A=(function(){let n={wbg:{}};return n.wbg.__wbindgen_init_externref_table=function(){let o=W.__wbindgen_export_0,r=o.grow(4);o.set(0,void 0),o.set(r+0,void 0),o.set(r+1,null),o.set(r+2,!0),o.set(r+3,!1)},n})();(typeof e=="string"||typeof Request=="function"&&e instanceof Request||typeof URL=="function"&&e instanceof URL)&&(e=fetch(e));let{instance:t,module:i}=await(async function(n,o){if(typeof Response=="function"&&n instanceof Response){if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(n,o)}catch(s){if(n.headers.get("Content-Type")=="application/wasm")throw s}let r=await n.arrayBuffer();return await WebAssembly.instantiate(r,o)}{let r=await WebAssembly.instantiate(n,o);return r instanceof WebAssembly.Instance?{instance:r,module:n}:r}})(await e,A);return(function(n,o){return W=n.exports,Xt.__wbindgen_wasm_module=o,NA=null,FA=null,xA=null,W.__wbindgen_start(),W})(t,i)}function tn(e){if(typeof atob=="function"){let A=atob(e),t=new Uint8Array(A.length);for(let i=0;i<A.length;i++)t[i]=A.charCodeAt(i);return t}return Uint8Array.from(Buffer.from(e,"base64"))}async function nn(){if(!(function(){if(qe!==null)return qe;let e=!1;if(typeof WebAssembly=="object"&&typeof WebAssembly.validate=="function"){let A=new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11]),t=new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,96,0,1,111,3,2,1,0,10,6,1,4,0,208,111,11]);try{e=WebAssembly.validate(A)&&WebAssembly.validate(t)}catch{e=!1}}return qe=e,e})())throw new Error("scanic: WebAssembly features required by the WASM module (reference-types, SIMD) are unavailable in this engine; using the JavaScript fallback.");return await Xt({module_or_path:tn("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JAAgAiABIAJqIgFLBEBBAEEAEC0ACyADQQRqIAAoAgAiAiAAKAIEQQggASACQQF0IgIgASACSxsiASABQQhNGyIBQQFBARARIAMoAgRBAUYEQCADKAIIIAMoAgwQLQALIAMoAgghAiAAIAE2AgAgACACNgIEIANBEGokAAt5AQF/IwBBIGsiAiQAAn8gACgCAEGAgICAeEcEQCABIAAoAgQgACgCCBAwDAELIAJBEGogACgCDCgCACIAQQhqKQIANwMAIAJBGGogAEEQaikCADcDACACIAApAgA3AwggASgCACABKAIEIAJBCGoQBwsgAkEgaiQAC2kBA38jAEEQayIBJAAgAUEEaiAAKAIAIgIgACgCBEEEIAJBAXQiAiACQQRNGyICQQRBBBARIAEoAgRBAUYEQCABKAIIIAEoAgwQLQALIAEoAgghAyAAIAI2AgAgACADNgIEIAFBEGokAAtpAQN/IwBBEGsiASQAIAFBBGogACgCACICIAAoAgRBBCACQQF0IgIgAkEETRsiAkEEQQgQESABKAIEQQFGBEAgASgCCCABKAIMEC0ACyABKAIIIQMgACACNgIAIAAgAzYCBCABQRBqJAALaQEDfyMAQRBrIgEkACABQQRqIAAoAgAiAiAAKAIEQQQgAkEBdCICIAJBBE0bIgJBAkECEBEgASgCBEEBRgRAIAEoAgggASgCDBAtAAsgASgCCCEDIAAgAjYCACAAIAM2AgQgAUEQaiQACxIAIwBBMGsiACQAIABBMGokAAtoAgF/AX4jAEEwayIDJAAgAyABNgIEIAMgADYCACADQQI2AgwgA0Hkl8AANgIIIANCAjcCFCADQoCAgIAwIgQgA62ENwMoIAMgBCADQQRqrYQ3AyAgAyADQSBqNgIQIANBCGogAhAmAAtHAQF/IAAoAgAgACgCCCIDayACSQRAIAAgAyACEBIgACgCCCEDCyACBEAgACgCBCADaiABIAL8CgAACyAAIAIgA2o2AghBAAtEAQJ/IAEoAgQhAiABKAIAIQNBCEEEEDUiAUUEQEEEQQgQOQALIAEgAjYCBCABIAM2AgAgAEHcksAANgIEIAAgATYCAAvGAgACQCAAIAJNBEAgACABTSABIAJLcg0BIwBBMGsiAiQAIAIgATYCBCACIAA2AgAgAkECNgIMIAJB2JbAADYCCCACQgI3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IAAAABwAEAAMAAAAEgAAABcAAAAcABAADAAAABcAAAAZAAAAHAAQAAwAAAAUAAAAGQAAABwAEAAMAAAAUQAAAB4AAAAcABAADAAAAFIAAAAeAAAATGF6eSBpbnN0YW5jZSBoYXMgcHJldmlvdXNseSBiZWVuIHBvaXNvbmVkAAA8CBAAKgAAAEgBEABaAAAACAMAABkAAAByZWVudHJhbnQgaW5pdAAAgAgQAA4AAABIARAAWgAAAHoCAAANAAAAfP2LMlfmV/kC30S/40jnr21dy9YsUOtjeEGmV3Ebi7ltZW1vcnkgYWxsb2NhdGlvbiBvZiAgYnl0ZXMgZmFpbGVkAADICBAAFQAAAN0IEAANAAAALwEQABgAAABkAQAACQAAAAQAAAAMAAAABAAAAAUAAAAGAAAABwAAAAAAAAAIAAAABAAAAAgAAAAJAAAACgAAAAsAAAAMAAAAEAAAAAQAAAANAAAADgAAAA8AAAAQAAAAAAAAAAgAAAAEAAAAEQAAAGFzc2VydGlvbiBmYWlsZWQ6IHBzaXplID49IHNpemUgKyBtaW5fb3ZlcmhlYWQAAAQBEAAqAAAAsQQAAAkAAABhc3NlcnRpb24gZmFpbGVkOiBwc2l6ZSA8PSBzaXplICsgbWF4X292ZXJoZWFkAAAEARAAKgAAALcEAAANAAAABAAAAAwAAAAEAAAAEgAAAGNhcGFjaXR5IG92ZXJmbG93AAAA/AkQABEAAADjABAAIAAAABwAAAAFAAAAMDAwMTAyMDMwNDA1MDYwNzA4MDkxMDExMTIxMzE0MTUxNjE3MTgxOTIwMjEyMjIzMjQyNTI2MjcyODI5MzAzMTMyMzMzNDM1MzYzNzM4Mzk0MDQxNDI0MzQ0NDU0NjQ3NDg0OTUwNTE1MjUzNTQ1NTU2NTc1ODU5NjA2MTYyNjM2NDY1NjY2NzY4Njk3MDcxNzI3Mzc0NzU3Njc3Nzg3OTgwODE4MjgzODQ4NTg2ODc4ODg5OTA5MTkyOTM5NDk1OTY5Nzk4OTlyYW5nZSBlbmQgaW5kZXggIG91dCBvZiByYW5nZSBmb3Igc2xpY2Ugb2YgbGVuZ3RoIAAA8AoQABAAAAAACxAAIgAAAHNsaWNlIGluZGV4IHN0YXJ0cyBhdCAgYnV0IGVuZHMgYXQgADQLEAAWAAAASgsQAA0AAAByYW5nZSBzdGFydCBpbmRleCAAAGgLEAASAAAAAAsQACIAAABhdHRlbXB0IHRvIGRpdmlkZSBieSB6ZXJvAAAAjAsQABkAAABpbmRleCBvdXQgb2YgYm91bmRzOiB0aGUgbGVuIGlzICBidXQgdGhlIGluZGV4IGlzIAAAsAsQACAAAADQCxAAEgAAAAAAAD8AAAC/AEGUmMAACwEBAHwJcHJvZHVjZXJzAghsYW5ndWFnZQEEUnVzdAAMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjkyLjAgKGRlZDVjMDZjZiAyMDI1LTEyLTA4KQZ3YWxydXMGMC4yMy4zDHdhc20tYmluZGdlbhMwLjIuMTAwICgyNDA1ZWMyYjQpAHQPdGFyZ2V0X2ZlYXR1cmVzBysPbXV0YWJsZS1nbG9iYWxzKxNub250cmFwcGluZy1mcHRvaW50KwdzaW1kMTI4KwtidWxrLW1lbW9yeSsIc2lnbi1leHQrD3JlZmVyZW5jZS10eXBlcysKbXVsdGl2YWx1ZQ==")})}function MA(){return se||(se=nn().catch(e=>{throw se=null,e})),se}function on(e,A,t,i=5,n=0){n===0&&(n=.3*(.5*(i-1)-1)+.8);let o=Math.floor(i/2),r=(function(c,g){let l=new Float32Array(c),d=Math.floor(c/2),C=0;for(let u=0;u<c;u++){let E=u-d;l[u]=Math.exp(-E*E/(2*g*g)),C+=l[u]}for(let u=0;u<c;u++)l[u]/=C;return l})(i,n),s=new Uint8ClampedArray(A*t),a=new Uint8ClampedArray(A*t);for(let c=0;c<t;c++){let g=c*A;for(let l=0;l<A;l++){let d=0;for(let C=-o;C<=o;C++)d+=e[g+Math.min(A-1,Math.max(0,l+C))]*r[o+C];s[g+l]=d}}for(let c=0;c<A;c++)for(let g=0;g<t;g++){let l=0;for(let d=-o;d<=o;d++)l+=s[Math.min(t-1,Math.max(0,g+d))*A+c]*r[o+d];a[g*A+c]=Math.round(l)}return a}function Yt(e,A,t,i,n){let o=new Uint8Array(A*t),r=[];for(let c=1;c<t-1;c++)for(let g=1;g<A-1;g++){let l=c*A+g,d=e[l];d>=n?(o[l]=2,r.push({x:g,y:c})):o[l]=d>=i?0:1}for(let c=0;c<A;c++)o[c]=1,o[(t-1)*A+c]=1;for(let c=1;c<t-1;c++)o[c*A]=1,o[c*A+A-1]=1;let s=[-1,0,1,-1,1,-1,0,1],a=[-1,-1,-1,0,0,1,1,1];for(;r.length>0;){let{x:c,y:g}=r.pop();for(let l=0;l<8;l++){let d=c+s[l],C=g+a[l],u=C*A+d;o[u]===0&&(o[u]=2,r.push({x:d,y:C}))}}return o}async function rn(e,A={}){let t=!(!A.debug||A.debug._timingsOnly),i=[],n=performance.now(),o,r,s;if(A.skipGrayscale)o=A.width,r=A.height,s=e,t&&(A.debug.grayscale=s);else{o=e.width,r=e.height;let T=performance.now();s=(function(k){let{width:q,height:J,data:G}=k,L=new Uint8ClampedArray(q*J);for(let v=0,M=0;v<G.length;v+=4,M++)L[M]=54*G[v]+183*G[v+1]+19*G[v+2]>>8;return L})(e);let S=performance.now();i.push({step:"Grayscale",ms:(S-T).toFixed(2)}),t&&(A.debug.grayscale=s)}let a=A.lowThreshold!==void 0?A.lowThreshold:null,c=A.highThreshold!==void 0?A.highThreshold:null;if(a===null||c===null){let S=new Uint32Array(1024),k=0;for(let L=1;L<r-1;L++)for(let v=1;v<o-1;v++){let M=L*o+v,F=-s[M-o-1]+s[M-o+1]-2*s[M-1]+2*s[M+1]-s[M+o-1]+s[M+o+1],H=-s[M-o-1]-2*s[M-o]-s[M-o+1]+s[M+o-1]+2*s[M+o]+s[M+o+1],x=Math.abs(F)+Math.abs(H);x>=1024&&(x=1023),x>0&&(S[x]++,k++)}let q=Math.round(.7*k),J=0,G=75;for(let L=1;L<1024;L++)if(J+=S[L],J>=q){G=L;break}a===null&&(a=Math.max(5,Math.round(.5*G))),c===null&&(c=Math.min(500,Math.round(1.5*G)))}let g=A.kernelSize||5,l=A.sigma||0,d=A.L2gradient!==void 0&&A.L2gradient,C=A.applyDilation===void 0||A.applyDilation,u=A.dilationKernelSize||5,E=A.useWasmHysteresis!==void 0&&A.useWasmHysteresis,p=A.useWasmFullCanny!==void 0&&A.useWasmFullCanny;if(a>=c&&([a,c]=[c,a]),p)try{await MA();let T=performance.now(),S=new Uint8ClampedArray((function(J,G,L,v,M,F,H,x,N,_){let B=Oe(J,W.__wbindgen_malloc),y=aA,U=W.canny_edge_detector_full(B,y,G,L,v,M,F,H,x,N,_);var Y=ae(U[0],U[1]).slice();return W.__wbindgen_free(U[0],1*U[1],1),Y})(s,o,r,a,c,g,l,d,C,u)),k=(performance.now()-T).toFixed(2);i.push({step:"Edge Processing (WASM)",ms:k}),t&&(A.debug.finalEdges=S),A.debug?A.debug.timings=i:A.debug={timings:i};let q=performance.now();return i.unshift({step:"Edge Detection Total",ms:(q-n).toFixed(2)}),S}catch(T){Pt||(Pt=!0,T instanceof Error?T.message:String(T))}let I,Q,D,w,R,m;I=performance.now();try{await MA(),D=(function(T,S,k,q,J){let G=Oe(T,W.__wbindgen_malloc),L=aA,v=W.blur(G,L,S,k,q,J);var M=ae(v[0],v[1]).slice();return W.__wbindgen_free(v[0],1*v[1],1),M})(s,o,r,g,l)}catch{D=on(s,o,r,g,l)}Q=performance.now(),i.push({step:"Gaussian Blur",ms:(Q-I).toFixed(2)}),t&&(A.debug.blurred=D),I=performance.now();{let T=(function(S,k,q){let J=new Int16Array(k*q),G=new Int16Array(k*q);for(let L=1;L<q-1;L++){let v=L*k,M=(L-1)*k,F=(L+1)*k;for(let H=1;H<k-1;H++){let x=v+H,N=S[M+H-1],_=S[M+H],B=S[M+H+1],y=S[v+H-1],U=S[v+H+1],Y=S[F+H-1],K=S[F+H],X=S[F+H+1],Z=B-N+2*(U-y)+(X-Y),j=Y+2*K+X-(N+2*_+B);J[x]=Z,G[x]=j}}return{dx:J,dy:G}})(D,o,r);w=T.dx,R=T.dy}Q=performance.now(),i.push({step:"Gradients",ms:(Q-I).toFixed(2)}),I=performance.now();try{await MA(),m=await en(w,R,o,r,d)}catch{m=(function(T,S,k,q,J){let G=new Float32Array(k*q),L=new Float32Array(k*q);for(let v=0;v<T.length;v++){let M=T[v],F=S[v];G[v]=J?Math.sqrt(M*M+F*F):Math.abs(M)+Math.abs(F)}for(let v=1;v<q-1;v++)for(let M=1;M<k-1;M++){let F=v*k+M,H=G[F];if(H===0){L[F]=0;continue}let x=T[F],N=S[F],_=0,B=0,y=Math.abs(x),U=Math.abs(N);if(U>2.4142*y)_=G[F-k],B=G[F+k];else if(y>2.4142*U)_=G[F-1],B=G[F+1];else{let Y=(x^N)<0?-1:1;N>0?(_=G[(v-1)*k+(M-Y)],B=G[(v+1)*k+(M+Y)]):(_=G[(v+1)*k+(M-Y)],B=G[(v-1)*k+(M+Y)]),x>0&&N>0||x<0&&N<0?(_=G[(v-1)*k+(M+1)],B=G[(v+1)*k+(M-1)]):(_=G[(v-1)*k+(M-1)],B=G[(v+1)*k+(M+1)])}L[F]=H>=_&&H>=B?H:0}return L})(w,R,o,r,d)}Q=performance.now(),i.push({step:"Non-Max Suppression",ms:(Q-I).toFixed(2)}),I=performance.now();let f=d?a*a:a,b=d?c*c:c,h;if(E)try{await MA(),h=An(m,o,r,f,b)}catch{h=Yt(m,o,r,f,b)}else h=Yt(m,o,r,f,b);Q=performance.now(),i.push({step:"Hysteresis",ms:(Q-I).toFixed(2)}),I=performance.now();let P=new Uint8ClampedArray(o*r);for(let T=0;T<h.length;T++)P[T]=h[T]===2?255:0;Q=performance.now(),i.push({step:"Binary Image",ms:(Q-I).toFixed(2)}),I=performance.now();let O=P;if(C)try{await MA(),O=(function(T,S,k,q){let J=Oe(T,W.__wbindgen_malloc),G=aA,L=W.dilate(J,G,S,k,q);var v=ae(L[0],L[1]).slice();return W.__wbindgen_free(L[0],1*L[1],1),v})(P,o,r,u)}catch{O=(function(T,S,k,q=5){let J=Math.floor(q/2),G=new Uint8ClampedArray(S*k),L=new Uint8ClampedArray(S*k);for(let v=0;v<k;v++){let M=v*S;for(let F=0;F<S;F++){let H=0;for(let x=-J;x<=J;x++){let N=F+x;if(N>=0&&N<S){let _=T[M+N];_>H&&(H=_)}}G[M+F]=H}}for(let v=0;v<S;v++)for(let M=0;M<k;M++){let F=0;for(let H=-J;H<=J;H++){let x=M+H;if(x>=0&&x<k){let N=G[x*S+v];N>F&&(F=N)}}L[M*S+v]=F}return L})(P,o,r,u)}if(Q=performance.now(),i.push({step:"Dilation",ms:(Q-I).toFixed(2)}),t){A.debug.dx=w,A.debug.dy=R;let T=new Float32Array(o*r);for(let S=0;S<w.length;S++){let k=w[S],q=R[S];T[S]=d?Math.sqrt(k*k+q*q):Math.abs(k)+Math.abs(q)}A.debug.magnitude=T,A.debug.suppressed=m,A.debug.edgeMap=h,A.debug.cannyEdges=P,A.debug.finalEdges=O}A.debug?A.debug.timings=i:A.debug||(A.debug={timings:i});let z=performance.now();return i.unshift({step:"Edge Detection Total",ms:(z-n).toFixed(2)}),O}function LA(e){return e<=0?0:e>=1?1:e}function TA(e,A){return Math.hypot(e.x-A.x,e.y-A.y)}function Ot(e){return[TA(e.topLeft,e.topRight),TA(e.topRight,e.bottomRight),TA(e.bottomRight,e.bottomLeft),TA(e.bottomLeft,e.topLeft)]}function sn(e){let A=(function(i){let n=[i.topLeft,i.topRight,i.bottomRight,i.bottomLeft],o=[];for(let r=0;r<n.length;r++){let s=n[(r+n.length-1)%n.length],a=n[r],c=n[(r+1)%n.length],g=s.x-a.x,l=s.y-a.y,d=c.x-a.x,C=c.y-a.y,u=g*d+l*C,E=Math.max(1e-6,Math.hypot(g,l)*Math.hypot(d,C)),p=Math.max(-1,Math.min(1,u/E));o.push(180*Math.acos(p)/Math.PI)}return o})(e),t=0;for(let i of A)t+=LA(1-Math.abs(i-90)/55);return t/Math.max(1,A.length)}function qt(e,A){let t=Number(A.isValid)-Number(e.isValid);if(t!==0)return t;let i=A.confidence-e.confidence;if(!(Math.abs(i)<.015)&&i!==0)return i;let n=Math.abs((e.approxCount??99)-4)-Math.abs((A.approxCount??99)-4);if(n!==0)return n;let o=(A.rightAngleScore??0)-(e.rightAngleScore??0);if(Math.abs(o)>1e-6)return o;let r=Math.abs(1-(e.contourFitRatio??1))-Math.abs(1-(A.contourFitRatio??1));return Math.abs(r)>1e-6?r:i||A.score-e.score||A.coverageRatio-e.coverageRatio||A.area-e.area}function an(e,A,t,i,n={}){let o=n.epsilon||.02,r=Kt(e.points,o).length,s=ji(e,{epsilon:o});if(!s)return{contour:e,corners:null,score:0,confidence:0,isValid:!1,area:e.area||0,fillRatio:0,coverageRatio:0,cornersArea:0,approxCount:r,convex:!1,edgeSupport:0,aspectRatio:1/0,minSide:0,rightAngleScore:0,oppositeSideConsistency:0,contourFitRatio:0,contourFitScore:0};let a=t*i,c=e.area||0,g=e.boundingBox||{minX:0,minY:0,maxX:0,maxY:0},l=c/Math.max(1,(g.maxX-g.minX+1)*(g.maxY-g.minY+1)),d=(function(x){if(!x)return 0;let N=[x.topLeft,x.topRight,x.bottomRight,x.bottomLeft],_=0;for(let B=0;B<N.length;B++){let y=(B+1)%N.length;_+=N[B].x*N[y].y-N[y].x*N[B].y}return Math.abs(_)/2})(s),C=d/Math.max(1,a),u=Ot(s),E=Math.min(...u),p=(u[0]+u[2])/2,I=(u[1]+u[3])/2,Q=p>I?p/Math.max(1e-6,I):I/Math.max(1e-6,p),D=(function(x){let N=[x.topLeft,x.topRight,x.bottomRight,x.bottomLeft],_=[];for(let y=0;y<N.length;y++){let U=N[y],Y=N[(y+1)%N.length],K=N[(y+2)%N.length],X=(Y.x-U.x)*(K.y-Y.y)-(Y.y-U.y)*(K.x-Y.x);Math.abs(X)<1e-6||_.push(Math.sign(X))}if(_.length<3)return!1;let B=_[0];return _.every(y=>y===B)})(s),w=(function(x,N,_,B){if(!x?.points||x.points.length===0)return 0;let y=Math.max(1,Math.floor(x.points.length/240)),U=0,Y=0;for(let K=0;K<x.points.length;K+=y){let X=x.points[K],Z=Math.max(0,Math.min(_-1,0|X.x)),j=Math.max(0,Math.min(B-1,0|X.y));U++;let V=!1;for(let $=-1;$<=1&&!V;$++){let sA=j+$;if(!(sA<0||sA>=B))for(let gA=-1;gA<=1;gA++){let IA=Z+gA;if(!(IA<0||IA>=_)&&N[sA*_+IA]>0){V=!0;break}}}V&&Y++}return U===0?0:Y/U})(e,A,t,i),R=sn(s),m=(function(x){let N=Ot(x);return(Math.min(N[0],N[2])/Math.max(1e-6,Math.max(N[0],N[2]))+Math.min(N[1],N[3])/Math.max(1e-6,Math.max(N[1],N[3])))/2})(s),f=c/Math.max(1,d),b=LA(1-Math.abs(f-1)/.55),h=r===4?1:r===5?.9:r===6?.7:r<=8?.5:.28,P=LA(c/Math.max(1,.4*a)),O=LA((l-.08)/.72),z=LA((C-.03)/.82),T=n.minDocumentSideRatio!==void 0?n.minDocumentSideRatio:.06,S=n.minDocumentCoverageRatio!==void 0?n.minDocumentCoverageRatio:.04,k=n.maxDocumentAspectRatio!==void 0?n.maxDocumentAspectRatio:8,q=n.minDocumentFillRatio!==void 0?n.minDocumentFillRatio:.07,J=n.minContourFitRatio!==void 0?n.minContourFitRatio:.11,G=n.maxContourFitRatio!==void 0?n.maxContourFitRatio:1.2,L=n.minRightAngleScore!==void 0?n.minRightAngleScore:.42,v=n.minOppositeSideConsistency!==void 0?n.minOppositeSideConsistency:.3,M=Math.min(t,i)*T,F=(function(x,N=6){let _=[x?.topLeft,x?.topRight,x?.bottomRight,x?.bottomLeft];if(_.some(B=>!B||!Number.isFinite(B.x)||!Number.isFinite(B.y)))return!1;for(let B=0;B<_.length;B++)for(let y=B+1;y<_.length;y++)if(TA(_[B],_[y])<N)return!1;return!0})(s)&&D&&E>=M&&C>=S&&Q<=k&&l>=q&&f>=J&&f<=G&&R>=L&&m>=v,H=.22*P+.14*O+.15*h+.08*(D?1:0)+.08*w+.13*z+.1*R+.05*m+.05*b;return{contour:e,corners:s,score:H,confidence:F?H:.33*H,isValid:F,area:c,fillRatio:l,coverageRatio:C,cornersArea:d,approxCount:r,convex:D,edgeSupport:w,aspectRatio:Q,minSide:E,rightAngleScore:R,oppositeSideConsistency:m,contourFitRatio:f,contourFitScore:b}}async function cn(e,A,t,i,n={}){let o=n.debug?{}:{_timingsOnly:!0},r=[];o&&!o._timingsOnly&&(o.preprocessing={scaledDimensions:{width:A,height:t},scaleFactor:i,maxProcessingDimension:n.maxProcessingDimension||800});let s=(function(I={}){let Q=I.dilationKernelSize||3,D=I.dilationIterations||1,w=I.applyDilation===void 0||I.applyDilation,R=[{name:"default",lowThreshold:I.lowThreshold,highThreshold:I.highThreshold,dilationKernelSize:Q,dilationIterations:D,applyDilation:w}];if(I.enableDetectionCascade===!1)return R;R.push({name:"connect-edges",lowThreshold:I.lowThreshold,highThreshold:I.highThreshold,dilationKernelSize:Math.max(Q,5),dilationIterations:Math.max(D,2),applyDilation:!0}),R.push({name:"no-dilation",lowThreshold:I.lowThreshold,highThreshold:I.highThreshold,dilationKernelSize:Q,dilationIterations:D,applyDilation:!1}),I.lowThreshold===void 0&&I.highThreshold===void 0&&R.push({name:"fixed-mid-thresholds",lowThreshold:60,highThreshold:180,dilationKernelSize:Q,dilationIterations:D,applyDilation:w});let m=[],f=new Set;for(let b of R){let h=[b.lowThreshold,b.highThreshold,b.dilationKernelSize,b.dilationIterations,b.applyDilation].join(":");f.has(h)||(f.add(h),m.push(b))}return m})(n),a=[],c=async(I,Q)=>{let D=I.name||`pass-${Q+1}`,w=Q===0?"":` (${D})`,R=n.debug?{}:{_timingsOnly:!0},m=await rn(e,{width:A,height:t,lowThreshold:I.lowThreshold,highThreshold:I.highThreshold,dilationKernelSize:I.dilationKernelSize,dilationIterations:I.dilationIterations,applyDilation:I.applyDilation,debug:R,skipGrayscale:!0,useWasmBlur:!0,useWasmHysteresis:n.useWasmHysteresis,useWasmFullCanny:n.useWasmFullCanny});R.timings&&R.timings.forEach(O=>{O.step!=="Edge Detection Total"&&r.push({step:`${O.step}${w}`,ms:O.ms})});let f=performance.now(),b=Wi(m,{minArea:(n.minArea||1e3)/(i*i),width:A,height:t});r.push({step:`Find Contours${w}`,ms:(performance.now()-f).toFixed(2)}),f=performance.now();let{best:h,candidates:P}=(function(O,z,T,S,k={}){let q=k.maxCandidateContours||12,J=O.slice(0,q).map((G,L)=>({rankByArea:L,...an(G,z,T,S,k)})).sort(qt);return{best:J[0]||null,candidates:J}})(b,m,A,t,n);return r.push({step:`Corner Detection${w}`,ms:(performance.now()-f).toFixed(2)}),{name:D,params:{lowThreshold:I.lowThreshold,highThreshold:I.highThreshold,dilationKernelSize:I.dilationKernelSize,dilationIterations:I.dilationIterations,applyDilation:I.applyDilation},contours:b,best:h,candidates:P}},g=await c(s[0],0);if(a.push(g),(function(I,Q={}){if(Q.enableDetectionCascade===!1)return!1;if(!I||!I.corners)return!0;let D=Q.minCascadeTriggerConfidence!==void 0?Q.minCascadeTriggerConfidence:.68;return!I.isValid||I.confidence<D||I.approxCount>5||I.rightAngleScore<.55||I.contourFitRatio<.14})(g.best,n))for(let I=1;I<s.length;I++)a.push(await c(s[I],I));let l=[];for(let I of a)for(let Q of I.candidates)l.push({...Q,passName:I.name,passParams:I.params});l.sort(qt);let d=l[0]||null,C=l;if(!d||!d.corners)return{success:!1,message:"No document detected",debug:o._timingsOnly?null:o,timings:r};o&&!o._timingsOnly&&(o.passes=a.map(I=>({name:I.name,params:I.params,contourCount:I.contours.length,bestCandidate:I.best?{score:I.best.score,confidence:I.best.confidence,isValid:I.best.isValid,approxCount:I.best.approxCount,coverageRatio:I.best.coverageRatio,fillRatio:I.best.fillRatio,rightAngleScore:I.best.rightAngleScore,contourFitRatio:I.best.contourFitRatio}:null})),o.candidates=C.map(I=>({passName:I.passName,rankByArea:I.rankByArea,area:I.area,fillRatio:I.fillRatio,coverageRatio:I.coverageRatio,cornersArea:I.cornersArea,approxCount:I.approxCount,convex:I.convex,edgeSupport:I.edgeSupport,aspectRatio:I.aspectRatio,minSide:I.minSide,rightAngleScore:I.rightAngleScore,oppositeSideConsistency:I.oppositeSideConsistency,contourFitRatio:I.contourFitRatio,contourFitScore:I.contourFitScore,score:I.score,confidence:I.confidence,isValid:I.isValid})),o.selectedCandidate=C[0]?{passName:C[0].passName,rankByArea:C[0].rankByArea,area:C[0].area,fillRatio:C[0].fillRatio,coverageRatio:C[0].coverageRatio,approxCount:C[0].approxCount,edgeSupport:C[0].edgeSupport,aspectRatio:C[0].aspectRatio,minSide:C[0].minSide,rightAngleScore:C[0].rightAngleScore,oppositeSideConsistency:C[0].oppositeSideConsistency,contourFitRatio:C[0].contourFitRatio,score:C[0].score,confidence:C[0].confidence,isValid:C[0].isValid}:null);let u=d.corners,E=d.contour,p=u;return i!==1&&(p={topLeft:{x:u.topLeft.x*i,y:u.topLeft.y*i},topRight:{x:u.topRight.x*i,y:u.topRight.y*i},bottomRight:{x:u.bottomRight.x*i,y:u.bottomRight.y*i},bottomLeft:{x:u.bottomLeft.x*i,y:u.bottomLeft.y*i}}),{success:!0,contour:E,corners:p,confidence:d.confidence,debug:o._timingsOnly?null:o,timings:r}}function gn(e,A,t){let{topLeft:i,topRight:n,bottomRight:o,bottomLeft:r}=t,s=Math.hypot(o.x-r.x,o.y-r.y),a=Math.hypot(n.x-i.x,n.y-i.y),c=Math.round(Math.max(s,a)),g=Math.hypot(n.x-o.x,n.y-o.y),l=Math.hypot(i.x-r.x,i.y-r.y),d=Math.round(Math.max(g,l));e.canvas.width=c,e.canvas.height=d,(function(C,u,E,p,I){let Q=u&&typeof u.width=="number"&&typeof u.height=="number"&&u.data,D=u.width||u.naturalWidth,w=u.height||u.naturalHeight,R=document.createElement("canvas");R.width=D,R.height=w;let m=R.getContext("2d",{willReadFrequently:!0});Q?m.putImageData(u,0,0):m.drawImage(u,0,0,D,w);let f=m.getImageData(0,0,D,w).data,b=(function(F){let H=F[0][0],x=F[0][1],N=F[0][2],_=F[1][0],B=F[1][1],y=F[1][2],U=F[2][0],Y=F[2][1],K=F[2][2],X=B*K-y*Y,Z=-(_*K-y*U),j=_*Y-B*U,V=-(x*K-N*Y),$=H*K-N*U,sA=-(H*Y-x*U),gA=x*y-N*B,IA=-(H*y-N*_),yA=H*B-x*_,AA=H*X+x*Z+N*j;if(AA===0)throw new Error("Singular matrix");return[[X/AA,V/AA,gA/AA],[Z/AA,$/AA,IA/AA],[j/AA,sA/AA,yA/AA]]})(E),h=b[0][0],P=b[0][1],O=b[0][2],z=b[1][0],T=b[1][1],S=b[1][2],k=b[2][0],q=b[2][1],J=b[2][2],G=C.createImageData(p,I),L=G.data,v=D-1,M=w-1;for(let F=0;F<I;F++){let H=P*F+O,x=T*F+S,N=q*F+J;for(let _=0;_<p;_++){let B=1/(k*_+N),y=(h*_+H)*B,U=(z*_+x)*B,Y=y<0?0:y>v?v:y,K=U<0?0:U>M?M:U,X=0|Y,Z=0|K,j=X<v?X+1:X,V=Z<M?Z+1:Z,$=Y-X,sA=K-Z,gA=1-$,IA=1-sA,yA=gA*IA,AA=$*IA,Se=gA*sA,Re=$*sA,ve=Z*D+X<<2,ke=Z*D+j<<2,Me=V*D+X<<2,xe=V*D+j<<2,VA=F*p+_<<2;L[VA]=f[ve]*yA+f[ke]*AA+f[Me]*Se+f[xe]*Re+.5|0,L[VA+1]=f[ve+1]*yA+f[ke+1]*AA+f[Me+1]*Se+f[xe+1]*Re+.5|0,L[VA+2]=f[ve+2]*yA+f[ke+2]*AA+f[Me+2]*Se+f[xe+2]*Re+.5|0,L[VA+3]=255}}C.putImageData(G,0,0)})(e,A,(function(C,u){let E=(function(p,I){let Q=p.length,D=p[0].length,w=p.map(f=>f.slice()),R=I.slice();for(let f=0;f<D;f++){let b=f;for(let h=f+1;h<Q;h++)Math.abs(w[h][f])>Math.abs(w[b][f])&&(b=h);[w[f],w[b]]=[w[b],w[f]],[R[f],R[b]]=[R[b],R[f]];for(let h=f+1;h<Q;h++){let P=w[h][f]/w[f][f];for(let O=f;O<D;O++)w[h][O]-=P*w[f][O];R[h]-=P*R[f]}}let m=new Array(D);for(let f=D-1;f>=0;f--){let b=R[f];for(let h=f+1;h<D;h++)b-=w[f][h]*m[h];m[f]=b/w[f][f]}return m})((function(p){let I=[];for(let Q=0;Q<4;Q++){let[D,w]=p[Q];I.push([D,w,1,0,0,0,-D*u[Q][0],-w*u[Q][0]]),I.push([0,0,0,D,w,1,-D*u[Q][1],-w*u[Q][1]])}return I})(C),[u[0][0],u[0][1],u[1][0],u[1][1],u[2][0],u[2][1],u[3][0],u[3][1]]);return[[E[0],E[1],E[2]],[E[3],E[4],E[5]],[E[6],E[7],1]]})([[i.x,i.y],[n.x,n.y],[o.x,o.y],[r.x,r.y]],[[0,0],[c-1,0],[c-1,d-1],[0,d-1]]),c,d)}async function zt(e,A={}){let t=[],i=performance.now(),n=A.mode||"detect",o=A.output||"canvas",r=A.maxProcessingDimension||800,s,a,c,g;if((A.detector||"classical")==="ml"){a=performance.now();let{detectDocumentMl:d}=await Promise.resolve().then(()=>(Ht(),_t));s=await d(e,A.ml||{}),t.push({step:"ML Detection Total",ms:(performance.now()-a).toFixed(2)}),s.timings&&s.timings.forEach(C=>t.push(C))}else{a=performance.now();let{grayscaleData:d,scaleFactor:C,scaledDimensions:u}=await(async function(E,p=800){let I,Q,D=E&&typeof E.width=="number"&&typeof E.height=="number"&&E.data;if(D)I=E.width,Q=E.height;else{if(!E)throw new Error("No image provided");I=E.width||E.naturalWidth,Q=E.height||E.naturalHeight}let w=Math.max(I,Q),R,m,f;if(w<=p)R=I,m=Q,f=1;else{let S=p/w;R=Math.round(I*S),m=Math.round(Q*S),f=1/S}let b=typeof OffscreenCanvas<"u",h=b?new OffscreenCanvas(R,m):document.createElement("canvas");b||(h.width=R,h.height=m);let P=h.getContext("2d",{willReadFrequently:!0});if(P.imageSmoothingEnabled=!0,P.imageSmoothingQuality="medium",D){let S=b?new OffscreenCanvas(I,Q):document.createElement("canvas");b||(S.width=I,S.height=Q),S.getContext("2d").putImageData(E,0,0),P.drawImage(S,0,0,I,Q,0,0,R,m)}else P.drawImage(E,0,0,I,Q,0,0,R,m);let O=P.getImageData(0,0,R,m),z=O.data,T=new Uint8ClampedArray(R*m);for(let S=0,k=0;S<z.length;S+=4,k++){let q=54*z[S]+183*z[S+1]+19*z[S+2]>>8;T[k]=q,z[S]=z[S+1]=z[S+2]=q}return{grayscaleData:T,imageData:O,scaleFactor:f,originalDimensions:{width:I,height:Q},scaledDimensions:{width:R,height:m}}})(e,r);t.push({step:"Image Prep + Scale + Gray",ms:(performance.now()-a).toFixed(2)}),s=await cn(d,u.width,u.height,C,A),s.timings&&s.timings.forEach(E=>t.push(E))}if(!s.success){let d=performance.now();return t.unshift({step:"Total",ms:(d-i).toFixed(2)}),{output:null,corners:null,contour:null,confidence:s.confidence||null,score:s.score??null,debug:s.debug,success:!1,message:s.message||"No document detected",timings:t}}n==="detect"?g=null:n==="extract"&&(a=performance.now(),c=document.createElement("canvas"),gn(c.getContext("2d"),e,s.corners),t.push({step:"Perspective Transform",ms:(performance.now()-a).toFixed(2)})),n!=="detect"&&c&&(a=performance.now(),g=o==="canvas"?c:o==="imagedata"?c.getContext("2d").getImageData(0,0,c.width,c.height):o==="dataurl"?c.toDataURL():c,t.push({step:"Output Conversion",ms:(performance.now()-a).toFixed(2)}));let l=performance.now();return t.unshift({step:"Total",ms:(l-i).toFixed(2)}),{output:g,corners:s.corners,contour:s.contour??null,confidence:s.confidence||null,score:s.score??null,debug:s.debug,success:!0,message:"Document detected",timings:t}}var W,Ki,Vi,xA,aA,NA,FA,se,Pt,qe,Zt=hA(()=>{"use strict";Ki=1e3,Vi=10;xA=null;aA=0;NA=null;FA=null;se=null,Pt=!1,qe=null});var We={};Ne(We,{DOC_DETECT_MIN_CONFIDENCE:()=>jt,DOC_DETECT_WIDTH:()=>dA,SCANIC_VERSION:()=>In,detectQuad:()=>ln});async function ln(e){let A=await zt(e,{mode:"detect",detector:"classical",maxProcessingDimension:dA});return!A.success||A.corners===null||typeof A.confidence=="number"&&A.confidence<jt?null:A.corners}var In,jt,Xe=hA(()=>{"use strict";Zt();RA();In="1.5.0",jt=.35});var ro={};Ne(ro,{ASPECT_BANDS:()=>yt,AttestationError:()=>BA,CAPTURE_CANDIDATE_SPACING_MS:()=>ii,CAPTURE_EXTRA_CANDIDATES:()=>ti,CENTERED_FACTOR:()=>si,CIRCLE_RADIUS_FACTOR:()=>oi,CLIP_MIME_ALLOWLIST:()=>gt,CaptureSessionDoc:()=>ze,DOC_ANALYSIS_DEFAULTS:()=>Rt,DOC_CADENCE_HZ:()=>ei,DOC_DETECT_WIDTH:()=>dA,DOC_FEEDBACK_STATES:()=>vA,DOC_GATE_V1_DEFAULTS:()=>ZA,DocFeedbackAggregator:()=>kA,DocGateV1:()=>bA,INFERENCE_RETRY_ATTEMPTS:()=>ut,INFERENCE_RETRY_DEFAULT_S:()=>dt,IN_CIRCLE_FACTOR:()=>ri,MRZ_BAND_DEFAULTS:()=>bt,MR_DOC_CATALOG:()=>Fi,PRELOAD_BLAZE_INIT_TIMEOUT_MS:()=>tt,PRELOAD_BUDGET_MS:()=>me,PROCESSING_POLL_DELAY_MS:()=>Qt,PROCESSING_POLL_MAX:()=>mt,REGISTRY_RETRY_DELAY_MS:()=>ht,RETRYABLE_REASONS:()=>at,TRANSPORT_REPLAY_ATTEMPTS:()=>It,TRANSPORT_REPLAY_DELAY_MS:()=>lt,TURNSTILE_EXECUTE_TIMEOUT_MS:()=>wi,TURNSTILE_SCRIPT_SRC:()=>Et,analyzeDocFrame:()=>_e,aspectBand:()=>wA,createLivenessCaptureInternal:()=>pe,createVerificationInternal:()=>bi,detectIsMobile:()=>lA,detectMrzBand:()=>Te,executeTurnstile:()=>be,feedbackVoteOf:()=>He,frontierStep:()=>rA,isExecutable:()=>EA,isTerminalView:()=>tA,loadTurnstile:()=>Bt,mapEngineState:()=>rt,mapErrorKind:()=>st,mapGuidanceHint:()=>ot,mapSessionResult:()=>OA,mergeSessionView:()=>we,preloadedDetectorFactory:()=>fe,quadAreaFrac:()=>jA,quadBounds:()=>SA,quadIou:()=>Dt,quadTiltRatio:()=>vt,randomUuid:()=>ft,resetLivenessAssetsForTests:()=>Ei,resetTurnstileForTests:()=>Di,runLivenessFlow:()=>De,runSubmission:()=>pA,setPreloadLoaderForTests:()=>Ci,specularFraction:()=>Ge,stepOf:()=>iA,warmDocDetector:()=>Cn});module.exports=Ni(ro);XA();RA();var bt=Object.freeze({EDGE_LUMA_DELTA:40,MIN_ROW_DENSITY:.08,MIN_COLUMN_COVERAGE:.6,MIN_WIDTH_FRAC:.55,MIN_HEIGHT_FRAC:.02,MAX_HEIGHT_FRAC:.16,ROW_MERGE_GAP:18}),$A=12;function Te(e,A={}){let t={...bt,...A},{data:i,width:n,height:o}=e;if(n<32||o<32)return null;let r=new Array(o).fill(!1),s=new Float32Array(o),a=new Int32Array(o).fill(n),c=new Int32Array(o).fill(-1),g=n/$A,l=new Array($A);for(let m=0;m<o;m++){let f=0;l.fill(!1);let b=m*n*4,h=.299*(i[b]??0)+.587*(i[b+1]??0)+.114*(i[b+2]??0);for(let O=1;O<n;O++){let z=b+O*4,T=.299*(i[z]??0)+.587*(i[z+1]??0)+.114*(i[z+2]??0);Math.abs(T-h)>=t.EDGE_LUMA_DELTA&&(f+=1,l[Math.min($A-1,Math.floor(O/g))]=!0,O<a[m]&&(a[m]=O),O>c[m]&&(c[m]=O)),h=T}s[m]=f/n;let P=l.filter(Boolean).length/$A;r[m]=s[m]>=t.MIN_ROW_DENSITY&&P>=t.MIN_COLUMN_COVERAGE&&c[m]-a[m]>=t.MIN_WIDTH_FRAC*n}let d=null,C=-1,u=-1,E=0,p=m=>{if(C<0)return;let f=m-C+1;f>=t.MIN_HEIGHT_FRAC*o&&f<=t.MAX_HEIGHT_FRAC*o&&E>=4&&(d===null||E>d.rows)&&(d={top:C,bottom:m,rows:E}),C=-1,E=0};for(let m=0;m<o;m++)r[m]&&(C<0?C=m:m-u>t.ROW_MERGE_GAP&&(p(u),C=m),u=m,E+=1);if(p(u),d===null)return null;let I=d,Q=n,D=-1,w=0,R=0;for(let m=I.top;m<=I.bottom;m++)r[m]&&(a[m]<Q&&(Q=a[m]),c[m]>D&&(D=c[m]),w+=s[m],R+=1);return D-Q<t.MIN_WIDTH_FRAC*n?null:{x:Q,y:I.top,width:D-Q,height:I.bottom-I.top+1,density:R>0?w/R:0}}mA();XA();RA();var Rt=Object.freeze({MAX_AREA_FRAC:.92,TILT_MAX_SIDE_RATIO:1.3,GLARE_MAX_SPECULAR_FRAC:.025});function Ge(e){let{data:A,width:t,height:i}=e,n=t*i;if(n===0)return 0;let o=0;for(let r=0;r<n;r++){let s=r*4;.299*(A[s]??0)+.587*(A[s+1]??0)+.114*(A[s+2]??0)>=252&&o++}return o/n}function vt(e){let A=(a,c)=>Math.hypot(c.x-a.x,c.y-a.y),t=A(e.topLeft,e.topRight),i=A(e.bottomLeft,e.bottomRight),n=A(e.topLeft,e.bottomLeft),o=A(e.topRight,e.bottomRight),r=Math.max(t,i)/Math.max(1e-6,Math.min(t,i)),s=Math.max(n,o)/Math.max(1e-6,Math.min(n,o));return Math.max(r,s)}function _e(e){let A={...ZA,...e.thresholds},t=Rt,{quad:i,frameWidth:n,frameHeight:o,measures:r}=e,s=e.format==="ID-3",a="ok";if(i===null)a="no_document";else if(s){let d=SA(i),C=n>0?d.width/n:0,u=d.height>0?d.width/d.height:0;C<A.ID3_BAND_MIN_WIDTH_FRAC?a="too_far":St(i,n,o,A.EDGE_CLIP_MARGIN_PX)?a="edge_clipped":(u<A.ID3_BAND_ASPECT_MIN||u>A.ID3_BAND_ASPECT_MAX)&&(a="wrong_shape")}else{let d=jA(i,n,o),C=wA(e.format),u=SA(i),E=Math.max(u.width,u.height)/Math.max(1e-6,Math.min(u.width,u.height));d<A.MIN_AREA_FRAC?a="too_far":d>t.MAX_AREA_FRAC?a="too_close":St(i,n,o,A.EDGE_CLIP_MARGIN_PX)?a="edge_clipped":Math.abs(E-C.center)>C.tolerance?a="wrong_shape":vt(i)>t.TILT_MAX_SIDE_RATIO&&(a="tilted")}let c=r.exposure>=A.GUIDE_EXPOSURE?"ok":r.meanLuma<(nA[0]+nA[1])/2?"too_dark":"too_bright",g=r.sharpness>=A.GUIDE_SHARPNESS?"ok":"blurry",l=r.specularFraction>=t.GLARE_MAX_SPECULAR_FRAC?"glare":"ok";return{framing:a,lighting:c,blur:g,glare:l,acceptable:a==="ok"&&c==="ok"&&g==="ok"&&l==="ok"}}function St(e,A,t,i){return[e.topLeft,e.topRight,e.bottomRight,e.bottomLeft].some(o=>o.x<=i||o.y<=i||o.x>=A-i||o.y>=t-i)}var vA=Object.freeze({sensing:{key:"sensing",reticle:"searching",minDurationMs:900},too_far:{key:"too_far",reticle:"coaching",minDurationMs:1400},too_close:{key:"too_close",reticle:"coaching",minDurationMs:1400},edge_clipped:{key:"edge_clipped",reticle:"coaching",minDurationMs:1400},wrong_shape:{key:"wrong_shape",reticle:"coaching",minDurationMs:1400},tilted:{key:"tilted",reticle:"coaching",minDurationMs:1400},too_dark:{key:"too_dark",reticle:"coaching",minDurationMs:1400},too_bright:{key:"too_bright",reticle:"coaching",minDurationMs:1400},blurry:{key:"blurry",reticle:"coaching",minDurationMs:1400},glare:{key:"glare",reticle:"coaching",minDurationMs:1400},hold_still:{key:"hold_still",reticle:"locking",minDurationMs:900},processing:{key:"processing",reticle:"processing",minDurationMs:800},side_captured:{key:"side_captured",reticle:"flip",minDurationMs:2600},captured:{key:"captured",reticle:"done",minDurationMs:800}});function He(e){return e.framing==="no_document"?"sensing":e.framing==="too_far"?"too_far":e.framing==="too_close"?"too_close":e.framing==="edge_clipped"?"edge_clipped":e.framing==="wrong_shape"?"wrong_shape":e.framing==="tilted"?"tilted":e.lighting==="too_dark"?"too_dark":e.lighting==="too_bright"?"too_bright":e.glare==="glare"?"glare":e.blur==="blurry"?"blurry":"hold_still"}var kA=class{constructor(A=2600,t=.35){this.windowMs=A;this.decayPerSecond=t;this.votes=[];this.current=vA.sensing;this.currentSinceMs=null}reset(){this.votes=[],this.current=vA.sensing,this.currentSinceMs=null}force(A,t){return this.votes=[],this.current=vA[A],this.currentSinceMs=t,this.current}tick(A,t){if(this.currentSinceMs===null&&(this.currentSinceMs=t),this.votes.push({key:A,atMs:t}),this.votes=this.votes.filter(r=>t-r.atMs<=this.windowMs),t-this.currentSinceMs<this.current.minDurationMs)return this.current;let i=new Map;for(let r of this.votes){let s=(t-r.atMs)/1e3,a=this.decayPerSecond**s;i.set(r.key,(i.get(r.key)??0)+a)}let n=this.current.key,o=-1;for(let[r,s]of i)s>o&&(o=s,n=r);return n!==this.current.key&&(this.current=vA[n],this.currentSinceMs=t),this.current}};var kt="0.1.0-rc.7";var Mt="sekyro.did",xt;function Nt(){let e=globalThis.crypto;try{if(typeof e?.randomUUID=="function")return e.randomUUID()}catch{}try{if(typeof e?.getRandomValues=="function"){let A=e.getRandomValues(new Uint8Array(16));return Array.from(A,t=>t.toString(16).padStart(2,"0")).join("")}}catch{}}function Gi(){try{let e=localStorage.getItem(Mt);if(e)return e;let A=Nt();return A!==void 0&&localStorage.setItem(Mt,A),A}catch{return xt??(xt=Nt()),xt}}async function _i(){let e=globalThis.crypto?.subtle;if(!e)return"";let A=navigator,t=JSON.stringify([A.userAgent,A.platform,A.hardwareConcurrency??null,A.deviceMemory??null,A.maxTouchPoints??0,screen.width,screen.height,screen.colorDepth,globalThis.devicePixelRatio??1,Intl.DateTimeFormat().resolvedOptions().timeZone,A.languages?.join(",")??A.language]),i=await e.digest("SHA-256",new TextEncoder().encode(t));return Array.from(new Uint8Array(i),n=>n.toString(16).padStart(2,"0")).join("").slice(0,32)}function Hi(e){let A=new TextEncoder().encode(e),t="";for(let i of A)t+=String.fromCharCode(i);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Ui(e){let A,t;return{timezone(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone?.slice(0,64)||void 0}catch{return}},noteCameraLabel(i){i&&(A=i.slice(0,128))},async deviceHeader(){try{t??(t=_i().catch(()=>""));let i=[];navigator.webdriver&&i.push("webdriver"),/HeadlessChrome/.test(navigator.userAgent)&&i.push("headless");let n=await t,o=Gi(),r={v:1,platform:"web",sdk:e.sdk.slice(0,64),ids:[...o?[{kind:"web_local",value:o.slice(0,128)}]:[],...n?[{kind:"web_fp",value:n}]:[]],screen:{w:screen.width,h:screen.height,dpr:globalThis.devicePixelRatio??1},...A?{cameraLabel:A}:{},...i.length?{signals:i}:{}};return Hi(JSON.stringify(r))}catch{return}}}}var Ue=Symbol.for("sekyro.web.clientContext"),Je=globalThis,Ae=Je[Ue]??(Je[Ue]=Ui({sdk:`@sekyro/web@${kt}`}));function ee(e){try{let A=e.getVideoTracks?.()[0];A&&Ae.noteCameraLabel(A.label)}catch{}}var Ji=["video/mp4;codecs=avc1.42E01E,mp4a.40.2","video/mp4;codecs=avc1.42E01E","video/mp4","video/webm;codecs=vp9,opus","video/webm;codecs=vp9","video/webm;codecs=vp8,opus","video/webm;codecs=vp8","video/webm"];function Ft(){let e=globalThis.MediaRecorder;return!e||typeof e.isTypeSupported!="function"?"":Ji.find(A=>e.isTypeSupported(A))??""}var Lt="video/webm";function te(e){if(e instanceof TypeError)return"insecure";switch(e?.name??""){case"NotAllowedError":case"PermissionDeniedError":return"permission";case"NotFoundError":case"DevicesNotFoundError":return"notfound";case"NotReadableError":case"TrackStartError":return"inuse";case"OverconstrainedError":case"ConstraintNotSatisfiedError":return"overconstrained";default:return"generic"}}function Tt(){let e=globalThis;if(typeof e.window<"u"&&e.window.isSecureContext===!1)return"INSECURE_CONTEXT";let A=e.navigator?.mediaDevices;return!A||typeof A.getUserMedia!="function"?"UNSUPPORTED":"OK"}function Gt(){let e=globalThis.MediaRecorder;return typeof e<"u"&&typeof e.isTypeSupported=="function"}var Pe=8*1024*1024;function ie(e){return e.size>Pe}var Pi=Object.freeze({LOW:Object.freeze({cadenceHz:5,inputWidth:128,animations:"reduced"}),MID:Object.freeze({cadenceHz:8,inputWidth:160,animations:"full"}),HIGH:Object.freeze({cadenceHz:10,inputWidth:192,animations:"full"})}),Yi=new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11]);function oe(){let e=typeof navigator>"u"?void 0:navigator,A=!1;try{A=typeof WebAssembly<"u"&&WebAssembly.validate(Yi)}catch{A=!1}return{cores:e?.hardwareConcurrency??2,memGB:e?.deviceMemory??null,simd:A}}function re(e){let A=e.memGB??4;return e.cores<4||A<3?"LOW":e.cores>=8&&A>=6?"HIGH":"MID"}var Ye=["LOW","MID","HIGH"],ne=class{constructor(A){this.samples=[];this.adjusted=!1;this.current=A}get deviceClass(){return this.current}get profile(){return Pi[this.current]}record(A){if(this.adjusted||this.samples.length>=10)return this.current;if(this.samples.push(A),this.samples.length<10)return this.current;this.adjusted=!0;let t=this.samples.reduce((i,n)=>i+n,0)/this.samples.length;if(t>70)this.current="LOW";else if(t<25){let i=Ye.indexOf(this.current);this.current=Ye[Math.min(Ye.length-1,i+1)]??this.current}return this.current}};mA();RA();var GA=null;async function un(e){return GA===null&&(GA=(await Promise.resolve().then(()=>(Xe(),We))).detectQuad),GA(e)}async function dn(e){let A=Te(e);return A===null?null:{topLeft:{x:A.x,y:A.y},topRight:{x:A.x+A.width,y:A.y},bottomRight:{x:A.x+A.width,y:A.y+A.height},bottomLeft:{x:A.x,y:A.y+A.height}}}function Cn(){GA===null&&Promise.resolve().then(()=>(Xe(),We)).then(e=>{GA=e.detectQuad}).catch(()=>{})}var ei=Object.freeze({LOW:3,MID:5,HIGH:6}),ze=class{constructor(A){this.handlers=new Map;this.stream=null;this.timer=null;this.state="idle";this.lastGuidance=null;this.attempt=0;this.capturing=!1;this.feedback=new kA;this.lastFeedbackKey=null;this.opts=A,this.gate=new bA(A.format??"ID-1",A.thresholds);let t=A.deviceClass??re(oe());this.cadenceMs=1e3/ei[t]}on(A,t){let i=this.handlers.get(A);return i||(i=new Set,this.handlers.set(A,i)),i.add(t),()=>i?.delete(t)}emit(A,t){for(let i of[...this.handlers.get(A)??[]])i(t)}async start(){let A=++this.attempt;this.gate.reset(),this.feedback.reset(),this.lastFeedbackKey=null,this.lastGuidance=null,this.capturing=!1,this.setState("preflight");try{let t=this.opts.getUserMedia??(i=>navigator.mediaDevices.getUserMedia(i));if(this.setState("acquiring"),this.stream=await t({audio:!1,video:{facingMode:{ideal:"environment"},width:{ideal:1920},height:{ideal:1080}}}),ee(this.stream),A!==this.attempt){for(let i of this.stream.getTracks())i.stop();return}this.opts.video.srcObject=this.stream,await this.opts.video.play().catch(()=>{}),this.setState("positioning"),this.scheduleTick(A)}catch(t){let i=te(t);this.setState("error"),this.emit("error",{kind:i,retryable:!0})}}rearm(A){A!==void 0&&(this.opts.side=A),this.gate.reset(),this.feedback.reset(),this.lastFeedbackKey=null,this.lastGuidance=null,this.capturing=!1,this.state==="captured"&&(this.setState("positioning"),this.scheduleTick(this.attempt))}destroy(){if(this.attempt+=1,this.timer!==null&&(this.opts.clearTimer??clearTimeout)(this.timer),this.timer=null,this.stream)for(let A of this.stream.getTracks())A.stop();this.stream=null,this.opts.video.srcObject=null}setState(A){this.state=A,this.emit("statechange",{state:A})}scheduleTick(A){let t=this.opts.setTimer??setTimeout;this.timer=t(()=>{this.tick(A)},this.cadenceMs)}async tick(A){if(A!==this.attempt||this.state!=="positioning"||this.capturing)return;let t=this.opts.now??(()=>performance.now()),i=this.opts.grabDetectFrame??$t,n=this.opts.detect??((this.opts.format??"ID-1")==="ID-3"?dn:un),o=i(this.opts.video,dA);if(o===null){this.scheduleTick(A);return}let r=null;try{r=await n(o)}catch{r=null}if(A!==this.attempt)return;let s=r!==null?Ai(o,r):o,a=t(),c={sharpness:QA(s),exposure:zA(s),meanLuma:DA(s),specularFraction:Ge(s)},g=_e({format:this.opts.format??"ID-1",quad:r,frameWidth:o.width,frameHeight:o.height,measures:c,...this.opts.thresholds!==void 0?{thresholds:this.opts.thresholds}:{}}),l=this.gate.tick({timestampMs:a,quad:r,quality:c,frameWidth:o.width,frameHeight:o.height});this.emit("checks",{engine:"docv1",p:l.p,plausible:l.plausible,stableTicks:l.stableTicks,quad:l.quad,frameWidth:o.width,frameHeight:o.height,analysis:g});let d=this.feedback.tick(He(g),a);if(d.key!==this.lastFeedbackKey&&(this.lastFeedbackKey=d.key,this.emit("feedback",d)),l.guidanceKey!==this.lastGuidance&&(this.lastGuidance=l.guidanceKey,this.emit("guidance",{key:l.guidanceKey})),l.ready&&r!==null){let C=this.feedback.force("processing",a);this.lastFeedbackKey=C.key,this.emit("feedback",C),await this.fire(A,r,{width:o.width,height:o.height,tickSharpness:c.sharpness});return}this.scheduleTick(A)}async fire(A,t,i){this.capturing=!0;let n=this.opts.captureStill??Bn,o=this.opts.grabDetectFrame??$t,r=this.opts.setTimer??setTimeout,s={width:i.width,height:i.height};try{let a={still:await n(this.opts.video,t,s),sharpness:i.tickSharpness};for(let c=0;c<ti;c++){if(await new Promise(d=>{r(()=>d(),ii)}),A!==this.attempt)return;let g=o(this.opts.video,dA);if(g===null)continue;let l=QA(Ai(g,t));if(l>a.sharpness*En){let d=await n(this.opts.video,t,s);if(A!==this.attempt)return;a={still:d,sharpness:l}}}if(A!==this.attempt)return;this.setState("captured"),this.emit("artifacts",{keyframe:a.still.blob,previewDataUrl:a.still.previewDataUrl,...this.opts.side!==void 0?{side:this.opts.side}:{}})}catch{if(A!==this.attempt)return;this.capturing=!1,this.gate.reset(),this.scheduleTick(A)}}},ti=2,ii=260,En=1.08;function $t(e,A){let t=e.videoWidth,i=e.videoHeight;if(t===0||i===0)return null;let n=Math.min(1,A/t),o=Math.round(t*n),r=Math.round(i*n),s=document.createElement("canvas");s.width=o,s.height=r;let a=s.getContext("2d",{willReadFrequently:!0});return a===null?null:(a.drawImage(e,0,0,o,r),a.getImageData(0,0,o,r))}async function Bn(e,A,t){let i=document.createElement("canvas");i.width=Math.max(1,e.videoWidth),i.height=Math.max(1,e.videoHeight);let n=i.getContext("2d");if(n===null)throw new Error("2d context unavailable");return n.drawImage(e,0,0,i.width,i.height),{blob:await new Promise((r,s)=>{i.toBlob(a=>a?r(a):s(new Error("toBlob failed")),"image/jpeg",.92)}),previewDataUrl:i.toDataURL("image/jpeg",.85)}}function Ai(e,A){let t=[A.topLeft.x,A.topRight.x,A.bottomRight.x,A.bottomLeft.x],i=[A.topLeft.y,A.topRight.y,A.bottomRight.y,A.bottomLeft.y],n=Math.max(0,Math.floor(Math.min(...t))),o=Math.max(0,Math.floor(Math.min(...i))),r=Math.min(e.width,Math.ceil(Math.max(...t))),s=Math.min(e.height,Math.ceil(Math.max(...i))),a=Math.max(1,r-n),c=Math.max(1,s-o),g=new Uint8ClampedArray(a*c*4);for(let l=0;l<c;l++){let d=((o+l)*e.width+n)*4;g.set(e.data.subarray(d,d+a*4),l*a*4)}return{data:g,width:a,height:c,colorSpace:e.colorSpace}}function ni(e,A){return{video:{width:{ideal:1280,max:1280},height:{ideal:720,max:720},facingMode:A,frameRate:{ideal:24,max:30}},audio:!1}}function lA(){return typeof window>"u"||typeof navigator>"u"?!1:window.innerWidth<=768||/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(String(navigator.userAgent??""))}var oi=.45,ri=.9,si=.4;var hn=2500,Qn=1e3,mn=1500,ai=25e5,uA=class extends Error{constructor(A,t){super(t??A),this.kind=A}},ce=class{constructor(){this.recorder=null;this.stream=null;this.chunks=[];this.bytes=0;this.hardStopTimer=null;this.graceTimer=null;this.settled=!1}clearTimers(){this.hardStopTimer&&clearTimeout(this.hardStopTimer),this.graceTimer&&clearTimeout(this.graceTimer),this.hardStopTimer=null,this.graceTimer=null}record(A,t){if(this.recorder&&!this.settled)return Promise.reject(new uA("generic","record() called while a recording is in flight \u2014 ClipRecorder handles one recording at a time"));this.recorder=null,this.clearTimers(),this.chunks=[],this.bytes=0,this.settled=!1,this.stream=A;let i=Ft();return new Promise((n,o)=>{let r;try{r=new MediaRecorder(A,i?{mimeType:i,videoBitsPerSecond:ai}:{videoBitsPerSecond:ai})}catch{try{r=new MediaRecorder(A)}catch{o(new uA("unsupported"));return}}this.recorder=r;let s=c=>{this.settled||(this.settled=!0,this.clearTimers(),c())},a=()=>s(()=>{let c=r.mimeType||i||Lt,g=new Blob(this.chunks,{type:c});if(ie(g)){o(new uA("cliptoolarge"));return}n({blob:g,mimeType:c})});r.ondataavailable=c=>{c.data&&c.data.size&&(this.chunks.push(c.data),this.bytes+=c.data.size,this.bytes>Pe&&s(()=>{try{r.state!=="inactive"&&r.stop()}catch{}o(new uA("cliptoolarge"))}))},r.onstop=a,r.onerror=()=>s(()=>o(new uA("generic")));try{r.start(Qn)}catch{s(()=>o(new uA("generic")));return}this.hardStopTimer=setTimeout(()=>{if(this.settled||t?.(),r.state!=="inactive"){try{r.stop()}catch{a()}this.graceTimer=setTimeout(a,mn)}else a()},hn)})}stop(A){this.clearTimers();let t=this.recorder;if(t&&t.state!=="inactive")try{t.stop()}catch{}this.recorder=null,(A??this.stream)?.getTracks().forEach(n=>n.stop()),this.stream=null}};function ci(e,A,t=.92){return new Promise((i,n)=>{let o=e.videoWidth,r=e.videoHeight;if(!o||!r){n(new Error("video-not-ready"));return}let s=A.getContext("2d");if(!s){n(new Error("no-2d-context"));return}A.width=o,A.height=r,s.scale(-1,1),s.drawImage(e,-o,0),A.toBlob(a=>a?i(a):n(new Error("empty-keyframe")),"image/jpeg",t)})}var gi=1e4,ge=class e{constructor(A,t){this.tier="blaze";this.inner=A,t!==void 0&&(this.fallbackReason=t)}static async create(A,t){let i=await import("@mediapipe/tasks-vision"),n=await i.FilesetResolver.forVisionTasks(A),o=await i.FaceDetector.createFromOptions(n,{baseOptions:{modelAssetPath:`${A}/blaze_face_short_range.tflite`,delegate:"CPU"},runningMode:"IMAGE"});return new e(o,t)}async detect(A,t,i){let n;try{n=this.inner.detect(A).detections}catch{return null}let o=fn(n);if(o?.boundingBox===void 0)return null;let r=_A(A),s=pn(o.keypoints,r),a={box:{x:o.boundingBox.originX,y:o.boundingBox.originY,width:o.boundingBox.width,height:o.boundingBox.height},...s!==void 0?{eyes:s}:{},score:o.categories[0]?.score??0};return Ie(a,A,t,i)}destroy(){try{this.inner.close()}catch{}}};function fn(e){let A,t=-1;for(let i of e){let n=i.categories[0]?.score??0;n>t&&(A=i,t=n)}return A}function pn(e,A){let t=e?.[0],i=e?.[1];if(t===void 0||i===void 0)return;let n=s=>({x:s.x*A.width,y:s.y*A.height}),[o,r]=[n(t),n(i)];return o.x<=r.x?{left:o,right:r}:{left:r,right:o}}function yn(){if(typeof OffscreenCanvas=="function")return new OffscreenCanvas(2,2);let e=document.createElement("canvas");return e.width=2,e.height=2,e}async function Ii(){let e=globalThis.FaceDetector;if(typeof e!="function")return{ok:!1,reason:"unsupported"};try{let A=new e({fastMode:!0,maxDetectedFaces:1});return await A.detect(yn()),{ok:!0,instance:A}}catch(A){return{ok:!1,reason:`smoke_failed:${A instanceof Error?A.message:String(A)}`}}}var le=class{constructor(A,t){this.tier="native";this.inner=A,t!==void 0&&(this.fallbackReason=t)}async detect(A,t,i){let n;try{n=await this.inner.detect(A)}catch{return null}let o=n[0];if(o===void 0)return null;let r=wn(o.landmarks),s={box:{x:o.boundingBox.x,y:o.boundingBox.y,width:o.boundingBox.width,height:o.boundingBox.height},...r!==void 0?{eyes:r}:{},score:1};return Ie(s,A,t,i)}destroy(){}};function wn(e){if(e===void 0)return;let A=e.filter(n=>n.type==="eye").map(n=>n.locations[0]).filter(n=>n!==void 0),[t,i]=A;if(!(t===void 0||i===void 0))return t.x<=i.x?{left:t,right:i}:{left:i,right:t}}var je="/models-v3",Dn=["native","blaze"],HA=class extends Error{constructor(A){super(`no detector tier available (${A})`),this.name="DetectorLadderError",this.fallbackReason=A}};function _A(e){let A=e;if(typeof A.videoWidth=="number"&&A.videoWidth>0)return{width:A.videoWidth,height:A.videoHeight??0};if(typeof A.naturalWidth=="number"&&A.naturalWidth>0)return{width:A.naturalWidth,height:A.naturalHeight??0};let t=typeof A.width=="number"?A.width:0,i=typeof A.height=="number"?A.height:0;return{width:t,height:i}}function Ie(e,A,t,i){let n=_A(A),o=n.width>0?t/n.width:1,r=n.height>0?i/n.height:1,s=a=>({x:a.x*o,y:a.y*r});return{box:{x:e.box.x*o,y:e.box.y*r,width:e.box.width*o,height:e.box.height*r},...e.eyes!==void 0?{eyes:{left:s(e.eyes.left),right:s(e.eyes.right)}}:{},score:e.score}}function bn(e){return e instanceof Error?e.message:String(e)}async function oA(e){let A=e.preferred??Dn,t=[];for(let i of A){if(i==="native"){let n=await Ii();if(n.ok)return new le(n.instance,Ze(t));t.push(`native:${n.reason}`);continue}try{return await Sn(ge.create(e.v3AssetBaseUrl??je,Ze(t)),e.blazeInitTimeoutMs??gi)}catch(n){t.push(n instanceof ue?"blaze:blaze_init_timeout":`blaze:${bn(n)}`)}}throw new HA(Ze(t)??"no_tier_available")}function Ze(e){return e.length>0?e.join("; "):void 0}var ue=class extends Error{};function Sn(e,A){return new Promise((t,i)=>{let n=setTimeout(()=>{i(new ue(`init exceeded ${A}ms`)),e.then(o=>o.destroy(),()=>{})},A);e.then(o=>{clearTimeout(n),t(o)},o=>{clearTimeout(n),i(o)})})}var CA=1;function li(){return typeof OffscreenCanvas=="function"&&typeof createImageBitmap=="function"&&typeof Worker=="function"}function Rn(e,A={}){let t=A.createDetector??oA,i=null;e.onmessage=n=>{let o=n.data;if(o.v!==CA){o.bitmap?.close?.();return}if(o.type==="init"){t(o.options).then(r=>{i=r;let s={v:CA,type:"ready",tier:r.tier,...r.fallbackReason!==void 0?{fallbackReason:r.fallbackReason}:{}};e.postMessage(s)});return}if(o.type==="detect"){let r=s=>{let a={v:CA,type:"result",id:o.id,observation:s};e.postMessage(a),o.bitmap.close?.()};if(i===null){r(null);return}i.detect(o.bitmap,o.streamWidth,o.streamHeight).then(r,()=>r(null))}}}typeof self<"u"&&typeof self.importScripts=="function"&&Rn(self);var de=class e{constructor(A,t){this.pending=new Map;this.nextId=1;this.worker=A,this.tier=t.tier,t.fallbackReason!==void 0&&(this.fallbackReason=t.fallbackReason),A.addEventListener("message",i=>{let n=i.data;if(n.v!==CA||n.type!=="result")return;let o=this.pending.get(n.id);this.pending.delete(n.id),o?.(n.observation)})}static create(A,t,i=1e4){return new Promise((n,o)=>{let r=setTimeout(()=>{A.removeEventListener("message",s),A.terminate(),o(new Error(`worker detector not ready within ${i}ms`))},i),s=c=>{let g=c.data;g.v!==CA||g.type!=="ready"||(clearTimeout(r),A.removeEventListener("message",s),n(new e(A,g)))};A.addEventListener("message",s);let a={v:CA,type:"init",options:t};A.postMessage(a)})}async detect(A,t,i){let n;try{typeof ImageBitmap<"u"&&A instanceof ImageBitmap?n=A:typeof createImageBitmap=="function"?n=await createImageBitmap(A):n=A}catch{return null}let o=this.nextId;return this.nextId+=1,new Promise(r=>{this.pending.set(o,r);let s={v:CA,type:"detect",id:o,bitmap:n,streamWidth:t,streamHeight:i};this.worker.postMessage(s,[n])})}destroy(){for(let A of this.pending.values())A(null);this.pending.clear(),this.worker.terminate()}};var Ce=class{constructor(A){this.timer=null;this.frameCallbackId=null;this.running=!1;this.busy=!1;this.lastRunAt=Number.NEGATIVE_INFINITY;this.degradeCount=0;this.durations=[];this.options=A,this.periodMs=1e3/A.cadenceHz}start(){if(this.running)return;this.running=!0;let A=this.options.video;if(typeof A?.requestVideoFrameCallback=="function"){let t=()=>{this.running&&(this.frameCallbackId=A.requestVideoFrameCallback?.(()=>{this.frameCallbackId=null,this.now()-this.lastRunAt>=this.periodMs&&this.tick(),t()}))};t();return}this.timer=setInterval(()=>this.tick(),this.periodMs)}stop(){this.running=!1,this.timer!==null&&(clearInterval(this.timer),this.timer=null),this.frameCallbackId!==null&&(this.options.video?.cancelVideoFrameCallback?.(this.frameCallbackId),this.frameCallbackId=null)}stats(){let A=this.durations.length===0?0:this.durations.reduce((t,i)=>t+i,0)/this.durations.length;return{degradeCount:this.degradeCount,meanDetectMs:A}}now(){return this.options.now?.()??performance.now()}tick(){if(!this.running)return;if(this.busy){this.degradeCount+=1;return}this.busy=!0,this.lastRunAt=this.now();let A=this.now();this.options.run().catch(()=>{}).finally(()=>{this.busy=!1,this.durations.push(this.now()-A),this.durations.length>20&&this.durations.shift()})}};mA();var vn=Object.freeze({Q_GOOD:.6,RISE_PER_S:.9,FALL_PER_S:.25,CENTER_FALLOFF_IOD_MULTIPLE:1.35,BOX_EYE_OFFSET_FACTOR:.12,IOD_BOX_PROXY_FACTOR:.42,SIZE_IDEAL_MIN:.12,SIZE_IDEAL_MAX:.3,SIZE_HARD_MIN:.08,SIZE_HARD_MAX:.45,ROLL_MAX_DEG:25,MOTION_VELOCITY_IOD_PER_S:.5,MOTION_TAU_S:.4,MOTION_DEADBAND_IOD_PER_S:.1,READY_WINDOW_MS:500,GUIDE_SIZE:.6,GUIDE_CENTERING:.6,GUIDE_POSE:.6,GUIDE_EXPOSURE:.6,GUIDE_SHARPNESS:.35,GUIDE_MOTION:.5}),fA=e=>Math.min(1,Math.max(0,e)),Ee=class{constructor(A={}){this.p=0;this.lastTickAt=null;this.lastCenter=null;this.velocityEma=0;this.qWindow=[];this.lastSizeDirection="ideal";this.t={...vn,...A}}reset(){this.p=0,this.lastTickAt=null,this.lastCenter=null,this.velocityEma=0,this.qWindow=[],this.lastSizeDirection="ideal"}tick(A){let{observation:t,quality:i,timestampMs:n}=A,o=this.lastTickAt===null?0:Math.max(0,(n-this.lastTickAt)/1e3);this.lastTickAt=n;let r={presence:0,centering:0,size:0,pose:0,sharpness:fA(i.sharpness),exposure:fA(i.exposure),motion:1};if(t!==null){r.presence=1;let g=this.geometryOf(t,A);r.centering=g.centering,r.size=g.size,r.pose=g.pose,r.motion=this.motionOf(g.center,g.iod,n)}else this.lastCenter=null,this.velocityEma=0;let s=Math.min(r.presence,r.centering,r.size,r.pose,r.sharpness,r.exposure,r.motion);this.p=fA(this.p+(s>=this.t.Q_GOOD?this.t.RISE_PER_S*s:-this.t.FALL_PER_S*(1-s))*o),this.qWindow.push({at:n,q:s}),this.qWindow=this.qWindow.filter(g=>g.at>=n-this.t.READY_WINDOW_MS);let a=this.qWindow.map(g=>g.q);if(a.length>=3){let g=Math.min(...a),l=a.indexOf(g);a=a.filter((d,C)=>C!==l)}let c=this.p>=1&&a.every(g=>g>=this.t.Q_GOOD);return{at:n,q:s,p:this.p,factors:r,guidanceKey:this.guidanceOf(r,i,s),ready:c,accepted:s>=this.t.Q_GOOD}}geometryOf(A,t){let{box:i,eyes:n}=A,o={x:t.frameWidth/2,y:t.frameHeight/2},r=Math.min(t.frameWidth,t.frameHeight),s=n!==void 0?{x:(n.left.x+n.right.x)/2,y:(n.left.y+n.right.y)/2}:{x:i.x+i.width/2,y:i.y+i.height/2-this.t.BOX_EYE_OFFSET_FACTOR*i.height},a=n!==void 0?Math.hypot(n.right.x-n.left.x,n.right.y-n.left.y):this.t.IOD_BOX_PROXY_FACTOR*i.width,c=Math.hypot(s.x-o.x,s.y-o.y),g=a<=0?0:1-fA(c/(this.t.CENTER_FALLOFF_IOD_MULTIPLE*a)),l=r>0?a/r:0;this.lastSizeDirection=l<this.t.SIZE_IDEAL_MIN?"far":l>this.t.SIZE_IDEAL_MAX?"close":"ideal";let d;l<this.t.SIZE_HARD_MIN||l>this.t.SIZE_HARD_MAX?d=0:l<this.t.SIZE_IDEAL_MIN?d=(l-this.t.SIZE_HARD_MIN)/(this.t.SIZE_IDEAL_MIN-this.t.SIZE_HARD_MIN):l>this.t.SIZE_IDEAL_MAX?d=(this.t.SIZE_HARD_MAX-l)/(this.t.SIZE_HARD_MAX-this.t.SIZE_IDEAL_MAX):d=1;let C=1;if(n!==void 0){let u=Math.abs(Math.atan2(n.right.y-n.left.y,n.right.x-n.left.x))*180/Math.PI;C=1-fA(u/this.t.ROLL_MAX_DEG)}return{center:s,iod:a,centering:g,size:d,pose:C}}motionOf(A,t,i){let n=this.lastCenter;if(this.lastCenter={...A,at:i},n===null||t<=0)return 1;let o=(i-n.at)/1e3;if(o<=0)return 1;let r=Math.hypot(A.x-n.x,A.y-n.y)/t/o;r<=this.t.MOTION_DEADBAND_IOD_PER_S&&(r=0);let s=1-Math.exp(-o/this.t.MOTION_TAU_S);return this.velocityEma=s*r+(1-s)*this.velocityEma,1-fA(this.velocityEma/this.t.MOTION_VELOCITY_IOD_PER_S)}guidanceOf(A,t,i){if(A.presence<=0)return"fcv3.no_face";if(A.size<this.t.GUIDE_SIZE)return this.sizeGuidance();if(A.centering<this.t.GUIDE_CENTERING)return"fcv3.center_face";if(A.pose<this.t.GUIDE_POSE)return"fcv3.straighten";if(A.exposure<this.t.GUIDE_EXPOSURE)return this.exposureGuidance(t);if(A.sharpness<this.t.GUIDE_SHARPNESS)return"fcv3.steady_camera";if(A.motion<this.t.GUIDE_MOTION)return"fcv3.hold_still";if(i<this.t.Q_GOOD){let n=[["size",this.sizeGuidance()],["centering","fcv3.center_face"],["pose","fcv3.straighten"],["exposure",this.exposureGuidance(t)],["sharpness","fcv3.steady_camera"],["motion","fcv3.hold_still"]],o=n[0];for(let r of n)A[r[0]]<A[o[0]]&&(o=r);return o[1]}return"fcv3.hold_still"}sizeGuidance(){return this.lastSizeDirection==="far"?"fcv3.come_closer":"fcv3.move_back"}exposureGuidance(A){let t=(nA[0]+nA[1])/2;return A.meanLuma<t?"fcv3.more_light":"fcv3.too_bright"}};mA();var $e=4,kn=600,ui={sharpness:1,exposure:1,meanLuma:128},Be=class{constructor(A){this.handlers=new Map;this.stateValue="idle";this.attempt=0;this.stream=null;this.detector=null;this.scheduler=null;this.gate=null;this.profiler=null;this.lastGuidanceKey=null;this.positioningStartedAt=0;this.cameraRequestedAt=null;this.cameraReadyAt=null;this.faceAcquiredAt=null;this.captureCompleteAt=null;this.fired=!1;this.carriedDegrade=0;this.carriedMeans=[];this.snapshotCanvas=null;this.options=A,this.facing=A.facingMode??"user"}get state(){return this.stateValue}on(A,t){let i=this.handlers.get(A);return i||(i=new Set,this.handlers.set(A,i)),i.add(t),()=>i?.delete(t)}emit(A,t){for(let i of[...this.handlers.get(A)??[]])i(t)}async start(A={}){A.facingMode!==void 0&&(this.facing=A.facingMode),this.attempt+=1,await this.run(this.attempt)}get facingMode(){return this.facing}retry(){this.teardownAttempt(),this.start()}switchCamera(){this.facing=this.facing==="user"?"environment":"user",this.teardownAttempt(),this.start()}destroy(){this.attempt+=1,this.teardownAttempt(),this.detector?.destroy(),this.detector=null,this.options.video.srcObject=null}async run(A){let{options:t}=this;this.fired=!1,this.lastGuidanceKey=null,this.carriedDegrade=0,this.carriedMeans=[],this.cameraRequestedAt=null,this.cameraReadyAt=null,this.faceAcquiredAt=null,this.captureCompleteAt=null,this.setState("preflight");let i=Tt();if(i!=="OK"){this.fail(i==="INSECURE_CONTEXT"?"insecure":"unsupported");return}this.setState("acquiring");let n=t.getUserMedia??(a=>navigator.mediaDevices.getUserMedia(a)),o;this.cameraRequestedAt=this.now();try{o=await n(ni(lA(),this.facing))}catch(a){if(A!==this.attempt)return;this.fail(te(a));return}if(ee(o),A!==this.attempt){for(let a of o.getTracks())a.stop();return}if(this.stream=o,t.video.srcObject=o,await t.video.play().catch(()=>{}),A!==this.attempt)return;if(this.cameraReadyAt=this.now(),this.detector===null){let a={...t.v3AssetBaseUrl!==void 0?{v3AssetBaseUrl:t.v3AssetBaseUrl}:{}},c=t.createDetector??(t.createWorker!==void 0&&li()?async()=>{try{return await de.create(t.createWorker(),a)}catch{return oA(a)}}:()=>oA(a)),g;try{g=await c()}catch(l){A===this.attempt&&this.fail("models",l instanceof HA?l.fallbackReason:xn(l));return}if(A!==this.attempt){g.destroy();return}this.detector=g,this.options.telemetry?.({type:"detector",tier:g.tier,...g.fallbackReason!==void 0?{fallbackReason:g.fallbackReason}:{}})}let r=oe(),s=t.deviceClass??re(r);this.profiler=new ne(s),this.options.telemetry?.({type:"profile",staticClass:s,activeClass:this.profiler.deviceClass,simd:r.simd}),this.gate=new Ee(t.thresholds??{}),this.setState("positioning"),this.positioningStartedAt=this.now(),this.startScheduler(A)}get animations(){return this.profiler?.profile.animations??"full"}startScheduler(A){let t=this.profiler;if(t===null)return;if(this.scheduler!==null){let n=this.scheduler.stats();this.carriedDegrade+=n.degradeCount,n.meanDetectMs>0&&this.carriedMeans.push(n.meanDetectMs),this.scheduler.stop()}let i=new Ce({cadenceHz:t.profile.cadenceHz,video:this.options.video,...this.options.now!==void 0?{now:this.options.now}:{},run:()=>this.detectTick(A)});this.scheduler=i,i.start()}async detectTick(A){let{detector:t,gate:i,profiler:n}=this,o=this.options.video;if(A!==this.attempt||t===null||i===null||n===null||this.fired||o.videoWidth===0)return;let r=n.deviceClass,s=this.now(),a=this.options.makeSnapshot?.(o,n.profile.inputWidth)??this.defaultSnapshot(o,n.profile.inputWidth),c=null;try{c=await t.detect(a,o.videoWidth,o.videoHeight)}catch{c=null}if(A!==this.attempt||this.fired)return;n.record(this.now()-s),n.deviceClass!==r&&this.startScheduler(A);let g=c===null?ui:this.options.measureQuality?.(o,c.box)??this.defaultQuality(o,c.box),l=i.tick({timestampMs:this.now(),observation:c,quality:g,frameWidth:o.videoWidth,frameHeight:o.videoHeight});l.accepted&&this.faceAcquiredAt===null&&(this.faceAcquiredAt=l.at),this.emit("checks",{engine:"v3",progress:l.p,q:l.q,factors:l.factors,attempt:A}),l.guidanceKey!==this.lastGuidanceKey&&(this.lastGuidanceKey=l.guidanceKey,this.emit("guidance",{key:l.guidanceKey})),l.ready&&this.stateValue==="positioning"&&(this.fired=!0,this.captureCompleteAt=l.at,this.fire(A))}async fire(A){let{options:t}=this,i=this.scheduler;i?.stop();let n=i?.stats()??{degradeCount:0,meanDetectMs:0},o=[...this.carriedMeans,...n.meanDetectMs>0?[n.meanDetectMs]:[]];t.telemetry?.({type:"fired",timeToGateMs:this.now()-this.positioningStartedAt,degradeCount:this.carriedDegrade+n.degradeCount,meanDetectMs:o.length===0?0:o.reduce((u,E)=>u+E,0)/o.length});let r=t.mode??"clip",s=this.collectKeyframes(A);if(r==="photo"){let u=await s;if(A!==this.attempt)return;if(u===null){this.fail("generic");return}this.finishCapture({keyframe:u.blob,previewDataUrl:u.previewDataUrl});return}this.setState("recording");let a=t.createRecorder?.()??new ce,c=this.stream;if(c===null){this.fail("generic");return}let g=null;s.then(u=>{g=u},()=>{});let l=()=>{A===this.attempt&&g!==null&&this.emit("frozen",{previewDataUrl:g.previewDataUrl})},d,C;try{[d,C]=await Promise.all([a.record(c,l),s])}catch(u){if(A!==this.attempt)return;let E=u.kind;this.fail(E==="cliptoolarge"?"cliptoolarge":"recorder");return}if(A===this.attempt){if(ie(d.blob)){this.fail("cliptoolarge");return}if(C===null){this.fail("generic");return}this.finishCapture({clip:d.blob,clipMimeType:d.mimeType,keyframe:C.blob,previewDataUrl:C.previewDataUrl})}}async collectKeyframes(A){let t=this.options.captureKeyframe??(o=>this.defaultKeyframe(o)),i=kn/($e-1),n=null;for(let o=0;o<$e;o+=1){if(A!==this.attempt)return n;try{let r=await t(this.options.video);(n===null||r.sharpness>n.sharpness)&&(n=r)}catch{}o<$e-1&&await Mn(i)}return n}finishCapture(A){this.stopTracks(),this.setState("captured");let t=this.captureTiming();this.emit("artifacts",t===void 0?A:{...A,timing:t})}captureTiming(){let A=(r,s)=>r!==null&&s!==null?Math.max(0,Math.round(s-r)):void 0,t=A(this.cameraRequestedAt,this.cameraReadyAt),i=A(this.cameraReadyAt,this.faceAcquiredAt),n=A(this.faceAcquiredAt,this.captureCompleteAt),o={...t!==void 0?{cameraReadyMs:t}:{},...i!==void 0?{faceAcquireMs:i}:{},...n!==void 0?{captureMs:n}:{}};return Object.keys(o).length>0?o:void 0}async defaultKeyframe(A){let t=document.createElement("canvas"),i=await ci(A,t),n=t.toDataURL("image/jpeg",.92);return{blob:i,previewDataUrl:n,sharpness:this.centerSharpness(A)}}defaultSnapshot(A,t){try{let i=this.snapshotCanvas??document.createElement("canvas"),n=t/Math.max(1,A.videoWidth),o=Math.max(1,Math.round(A.videoWidth*n)),r=Math.max(1,Math.round(A.videoHeight*n)),s=i.getContext("2d");return s===null?A:(i.width!==o&&(i.width=o),i.height!==r&&(i.height=r),s.drawImage(A,0,0,o,r),this.snapshotCanvas=i,i)}catch{return A}}defaultQuality(A,t){let i=di(A,t,96);return i===null?ui:{sharpness:QA(i),exposure:zA(i),meanLuma:DA(i)}}centerSharpness(A){let t=_A(A),i=di(A,{x:t.width*.25,y:t.height*.25,width:t.width*.5,height:t.height*.5},96);return i===null?0:QA(i)}teardownAttempt(){this.scheduler?.stop(),this.scheduler=null,this.gate=null,this.stopTracks()}stopTracks(){if(this.stream!==null){for(let A of this.stream.getTracks())A.stop();this.stream=null}}fail(A,t){this.scheduler?.stop(),this.stopTracks(),this.setState("error"),this.options.telemetry?.({type:"error",kind:A,...t!==void 0?{fallbackReason:t}:{}}),this.emit("error",{kind:A,retryable:A!=="insecure"&&A!=="unsupported"})}setState(A){this.stateValue=A,this.emit("statechange",{state:A})}now(){return this.options.now?.()??performance.now()}};function Mn(e){return new Promise(A=>setTimeout(A,e))}function xn(e){return e instanceof Error?e.message:String(e)}function di(e,A,t){try{let i=document.createElement("canvas"),n=t/Math.max(1,A.width);i.width=Math.max(1,Math.round(A.width*n)),i.height=Math.max(1,Math.round(A.height*n));let o=i.getContext("2d");return o===null?null:(o.drawImage(e,A.x,A.y,A.width,A.height,0,0,i.width,i.height),o.getImageData(0,0,i.width,i.height))}catch{return null}}function he(){let e=new Map;return{on(A,t){let i=e.get(A);i===void 0&&(i=new Set,e.set(A,i));let n=t;return i.add(n),()=>{i?.delete(n)}},emit(A,t){let i=e.get(A);if(i!==void 0)for(let n of[...i])n(t)},clear(){e.clear()}}}var tt=12e4,me=13e4;var Qe=Symbol.for("@sekyro/web.preload.v1");function et(){let e=globalThis;return e[Qe]??(e[Qe]=new Map),e[Qe]}function Nn(e){let A=(e??je).replace(/\/+$/,"");return A===""?"/":A}function At(e,A){if(e.status!==A){e.status=A;for(let t of[...e.listeners])t(A)}}var it=e=>oA({preferred:["blaze"],v3AssetBaseUrl:e,blazeInitTimeoutMs:tt}),nt=it;function Ci(e){nt=e??it}function Ei(){for(let e of et().values())e.detector?.destroy();et().clear(),nt=it}function Fn(e,A){if((e.status==="loading"||e.status==="ready")&&e.promise!==null)return e.promise;At(e,"loading");let t=new Promise(s=>s(nt(A)));e.detectorPromise=t;let i,n=new Promise((s,a)=>{i=setTimeout(()=>{t.then(c=>c.destroy(),()=>{}),a(new Error(`detector preload exceeded ${me} ms`))},me)}),r=Promise.race([t,n]).then(s=>e.detectorPromise!==t?(s.destroy(),e.status):(e.detector=s,At(e,"ready"),"ready"),s=>(e.detectorPromise===t&&(e.detectorPromise=null,e.detector=null,At(e,"failed"),console.warn("[@sekyro/web] face-detector preload failed:",s)),e.status)).finally(()=>clearTimeout(i));return e.promise=r,e.scheduled?.settle(r),r}function Ln(e){return{get tier(){return e.tier},get fallbackReason(){return e.fallbackReason},detect:(...A)=>e.detect(...A),destroy:()=>{}}}function fe(e){let A=Nn(e),t=et().get(A);if(!(t===void 0||t.detector===null&&t.detectorPromise===null&&t.scheduled===null))return async()=>{t.scheduled!==null&&Fn(t,A);let i=t.promise;for(;t.detector===null&&i!==null&&(await i,t.promise!==i);)i=t.promise;return t.detector!==null?Ln(t.detector):oA({v3AssetBaseUrl:A})}}function Bi(){if(typeof navigator>"u"||!("wakeLock"in navigator))return null;let e=navigator.wakeLock;return typeof e?.request=="function"?e:null}function hi(){return typeof document>"u"||document.visibilityState==="visible"}function Qi(e){try{e.release().catch(()=>{})}catch{}}function mi(e={}){if(e.enabled===!1)return{setActive:()=>{}};let A=!1,t=null,i=!1,n=!1,o=()=>{if(!A||i||t!==null&&!t.released)return;let a=Bi();if(a===null||!hi())return;let c;try{c=a.request("screen")}catch{return}i=!0,c.then(g=>{if(i=!1,!A){Qi(g);return}t=g,g.addEventListener?.("release",()=>{t===g&&(t=null)})},()=>{i=!1})},r=()=>{hi()&&o()},s=a=>{typeof document>"u"||a===n||(n=a,a?document.addEventListener("visibilitychange",r):document.removeEventListener("visibilitychange",r))};return{setActive:a=>{if(A=a,a){if(Bi()===null)return;s(!0),o();return}if(s(!1),t!==null){let c=t;t=null,Qi(c)}}}}function ot(e){switch(e){case"fcv3.no_face":case"fcv3.center_face":case"fcv3.straighten":return"center";case"fcv3.come_closer":return"closer";case"fcv3.move_back":return"farther";case"fcv3.more_light":case"fcv3.too_bright":return"light";case"fcv3.hold_still":case"fcv3.steady_camera":return"still";default:return null}}function rt(e){switch(e){case"preflight":case"acquiring":return"starting";case"positioning":return"gating";case"idle":case"recording":case"captured":case"error":return e;default:{let A=e;throw new Error(`mapEngineState: unhandled state ${A}`)}}}function st(e){switch(e){case"permission":case"notfound":case"inuse":case"insecure":case"unsupported":return e;case"overconstrained":case"generic":case"models":case"cliptoolarge":case"recorder":return"generic";default:{let A=e;throw new Error(`mapErrorKind: unhandled kind ${A}`)}}}function pe(e,A={}){let t=A.now??(()=>performance.now()),i=he(),n=!Gt(),o=A.engineOptions,r=o?.createDetector===void 0&&o?.createWorker===void 0?{createDetector:()=>{let h=fe(e.assetBaseUrl);return h!==void 0?h():oA(e.assetBaseUrl!==void 0?{v3AssetBaseUrl:e.assetBaseUrl}:{})}}:{},s={video:e.video,mode:n?"photo":"clip",...e.facing!==void 0?{facingMode:e.facing}:{},...e.assetBaseUrl!==void 0?{v3AssetBaseUrl:e.assetBaseUrl}:{},...A.engineOptions??{},...r},a=A.createEngine?.(s)??new Be(s),c="idle",g=null,l=0,d=!1,C="full",u=null,E=null,p=0,I=0,Q=()=>{e.video.videoWidth>0&&e.video.videoHeight>0&&(p=e.video.videoWidth,I=e.video.videoHeight)},D=mi({enabled:e.keepScreenAwake!==!1}),w=h=>{c=h,D.setActive(h==="starting"||h==="gating"||h==="recording"),i.emit("state",h)},R=()=>i.emit("gate",{progress:l,hint:g,faceDetected:d}),m=()=>{let h=a.animations??"full";h!==C&&(C=h,i.emit("profile",{animations:C}))},f=[a.on("statechange",({state:h})=>{let P=rt(h);P==="starting"&&c!=="starting"&&(g=null,l=0,d=!1,u=null,E=null,p=0,I=0),w(P),m(),P==="recording"&&(Q(),u=t(),g="hold",l=1,R())}),a.on("checks",h=>{Q(),l=h.progress,d=h.factors.presence>0,R(),m()}),a.on("guidance",({key:h})=>{g=ot(h),R()}),a.on("frozen",({previewDataUrl:h})=>{E=t(),i.emit("frozen",{previewDataUrl:h})}),a.on("artifacts",h=>{let P=u!==null&&E!==null?Math.max(0,Math.round(E-u)):void 0;i.emit("captured",{keyframe:h.keyframe,...h.clip!==void 0?{clip:h.clip}:{},...h.clipMimeType!==void 0?{clipMime:h.clipMimeType}:{},width:e.video.videoWidth||p,height:e.video.videoHeight||I,...P!==void 0?{clipMs:P}:{},captureChannel:lA()?"mobile":"desktop",...n?{degraded:"NO_RECORDER"}:{},engine:"v3",...h.timing?.cameraReadyMs!==void 0?{cameraReadyMs:h.timing.cameraReadyMs}:{},...h.timing?.faceAcquireMs!==void 0?{faceAcquireMs:h.timing.faceAcquireMs}:{},...h.timing?.captureMs!==void 0?{captureMs:h.timing.captureMs}:{}})}),a.on("error",({kind:h,retryable:P})=>{i.emit("error",{kind:st(h),retryable:P})})];return{handle:{get state(){return c},on:(h,P)=>i.on(h,P),start:h=>a.start(h?.facing!==void 0?{facingMode:h.facing}:{}),retry:()=>a.retry(),switchCamera:()=>a.switchCamera(),destroy:()=>{for(let h of f)h();D.setActive(!1),a.destroy(),i.clear()}},setFlowState:h=>{c!==h&&w(h)}}}var eA=class extends Error{constructor(A){super(A.message),this.name="HostedApiError",this.status=A.status,this.code=A.code,A.retryAfterSeconds!==void 0&&(this.retryAfterSeconds=A.retryAfterSeconds)}};var Tn=3e4,Gn=9e5,_n=200,Hn=5,Un=400;function UA(e){return(e.split(";")[0]??"").trim()}var Jn={liveness:"clipKey",face_match:"imageKey",doc_ocr:"imageKey"},ye=class{constructor(A){this.sessionId=null;this.bootstrap=null;this.apiOrigin=A.apiOrigin.replace(/\/+$/,""),this.sessionToken=A.sessionToken;let t=A.fetch;this.fetchImpl=(i,n)=>(t??globalThis.fetch).call(globalThis,i,n),this.requestTimeoutMs=A.requestTimeoutMs??Tn,this.retryAttempts=A.inProgressRetry?.attempts??Hn,this.retryDelayMs=A.inProgressRetry?.delayMs??Un}async getSession(){let A=await this.api("GET","/hosted/session");return this.sessionId=A.sessionId,A}async recordConsent(A){return this.api("POST","/hosted/consent",{body:{locale:A}})}async selectMethod(A){let t=await this.api("POST","/hosted/method",{body:{method:A}});return this.sessionId=t.sessionId,t}async presign(A,t,i){let n=await this.ensureSessionId(),o={kind:A,contentType:UA(t),contentLength:i};return this.api("POST",`/sessions/${n}/media`,{body:o})}async uploadToSpaces(A,t,i){let n;try{n=await this.fetchImpl(A,{method:"PUT",headers:{"content-type":UA(i)},body:t,signal:AbortSignal.timeout(Gn)})}catch(o){throw fi(o)}if(!n.ok){let o=(await n.text().catch(()=>"")).slice(0,_n);throw new eA({status:n.status,code:"upload_failed",message:`media upload failed (HTTP ${n.status})${o?`: ${o}`:""}`})}}async completeMedia(A){let t=await this.ensureSessionId();return this.api("POST",`/sessions/${t}/media/complete`,{body:{key:A}})}async submitCheck(A,t,i={}){let n=await this.ensureSessionId(),o={input:t,...i.forceOutcome!==void 0?{forceOutcome:i.forceOutcome}:{}};return this.api("POST",`/sessions/${n}/checks/${A}`,{body:o,...i.idempotencyKey!==void 0?{idempotencyKey:i.idempotencyKey}:{}})}async uploadAndSubmit(A,t,i,n={}){let o=UA(t.contentType),r=await this.presign(t.kind,o,t.blob.size);await this.uploadToSpaces(r.uploadUrl,t.blob,o);let s=await this.completeMedia(r.key),a={...i,[Jn[A]]:s.key};return this.submitCheck(A,a,n)}async ensureSessionId(){return this.sessionId!==null?this.sessionId:(this.bootstrap??(this.bootstrap=this.getSession().finally(()=>{this.bootstrap=null})),(await this.bootstrap).sessionId)}async api(A,t,i={}){let n=`${this.apiOrigin}/api/v2${t}`,o={"x-session-token":this.sessionToken};i.body!==void 0&&(o["content-type"]="application/json"),i.idempotencyKey!==void 0&&(o["x-idempotency-key"]=i.idempotencyKey);try{let r=Ae.timezone();r!==void 0&&(o["sekyro-client-timezone"]=r)}catch{}try{let r=await Ae.deviceHeader();r!==void 0&&(o["sekyro-client-device"]=r)}catch{}for(let r=0;;r++){let s;try{s=await this.fetchImpl(n,{method:A,headers:o,...i.body!==void 0?{body:JSON.stringify(i.body)}:{},signal:AbortSignal.timeout(this.requestTimeoutMs)})}catch(c){throw fi(c)}if(s.ok){let c;try{c=await s.json()}catch{throw new eA({status:s.status,code:"unknown_error",message:`response body was not valid JSON (HTTP ${s.status})`})}return Pn(c)}let a=await Yn(s);if(a.code==="idempotency_in_progress"&&r<this.retryAttempts){this.retryDelayMs>0&&await On(this.retryDelayMs);continue}throw a}}};function fi(e){if((typeof e=="object"&&e!==null?e.name:void 0)==="TimeoutError")return new eA({status:0,code:"timeout",message:"request timed out"});let t=e instanceof Error?e.message:String(e);return new eA({status:0,code:"network_error",message:`network request failed: ${t}`})}function Pn(e){return e!==null&&typeof e=="object"&&"success"in e&&"data"in e?e.data:e}async function Yn(e){let A="unknown_error",t=`request failed (HTTP ${e.status})`,i=Number.NaN;try{let r=await e.json();if(r!==null&&typeof r=="object"){let s=r;typeof s.code=="string"&&s.code.length>0&&(A=s.code),typeof s.message=="string"&&s.message.length>0&&(t=s.message),typeof s.retryAfterSeconds=="number"&&(i=s.retryAfterSeconds)}}catch{}let n=e.headers.get("retry-after"),o=n===null?Math.floor(i):Number.parseInt(n,10);return new eA({status:e.status,code:A,message:t,...Number.isFinite(o)&&o>=0?{retryAfterSeconds:o}:{}})}function On(e){return new Promise(A=>setTimeout(A,e))}var at=new Set(["no_face","media_unreadable","clip_too_short","no_face_source","side_missing","unreadable","registry_unavailable","otp_mismatch","otp_expired","otp_send_failed","document_type_not_accepted","age_unverifiable","liveness_inconclusive","match_inconclusive","document_authenticity"]);function YA(e){let A=e.methodChoice;return A===void 0||!A.available||A.selected!==null||rA(e)?.checkType==="phone_otp"?null:A}var qn=new Set(["liveness","face_match"]);function EA(e){return e!==void 0&&(e.state==="pending"||e.state==="input_required")}function tA(e){return e.status!=="created"&&e.status!=="in_progress"}function rA(e){return e.steps.find(A=>EA(A))}function iA(e,A){return e.steps.find(t=>t.checkType===A)}function ct(e,A){let t=A.steps.find(n=>n.checkType==="liveness"),i=A.steps.find(n=>n.checkType==="face_match");return iA(e,"face_match")?.state==="pending"&&(t?.state==="passed"||t?.state==="review")&&i!==void 0&&i.state!=="pending"&&i.state!=="skipped"}function we(e,A,t){let i=new Map(e.steps.map(r=>[r.checkType,r])),n=t==="liveness"&&ct(e,A),o=A.steps.map(r=>{let s=i.get(r.checkType),a=s?.attemptsRemaining??0,c={...s??{},checkType:r.checkType,state:r.state,attemptsRemaining:r.checkType===t||r.checkType==="face_match"&&n?Math.max(0,a-1):a};return r.reason!==void 0?c.reason=r.reason:delete c.reason,r.attemptsExhausted!==void 0?c.attemptsExhausted=r.attemptsExhausted:delete c.attemptsExhausted,c});return{...e,status:A.status,outcome:A.outcome,failureReason:A.failureReason,steps:o,expiresAt:A.expiresAt}}function cA(e,A){return A===void 0?{sessionId:e,status:"unavailable"}:{sessionId:e,status:"unavailable",reason:A}}function JA(e,A,t){return t===void 0?{sessionId:e,status:A}:{sessionId:e,status:A,reason:t}}function OA(e,A){let{sessionId:t}=e;switch(e.status){case"completed":return e.outcome==="approved"?{sessionId:t,status:"approved"}:e.outcome==="review"?JA(t,"review",PA(e,"review")):JA(t,"rejected",PA(e,"failed"));case"failed":{let n=JA(t,"rejected",e.failureReason??PA(e,"failed"));return e.failureReason==="device_cooldown"&&e.retryAfter!==void 0&&(n.retryAfter=e.retryAfter),n}case"review_pending":return JA(t,"review",PA(e,"review"));case"expired":return cA(t);case"created":case"in_progress":break;default:{let n=e.status;throw new Error(`mapSessionResult: unhandled status ${n}`)}}let i=e.steps.find(n=>qn.has(n.checkType)&&n.state==="input_required"&&n.reason!==void 0);if(i?.reason==="mobile_required")return cA(t,"mobile_required");if(i?.reason!==void 0){let n=A.autoRetry&&at.has(i.reason)&&i.attemptsRemaining>0;return{sessionId:t,status:n?"retry":"rejected",reason:i.reason,attemptsRemaining:i.attemptsRemaining}}return rA(e)===void 0&&e.steps.some(n=>n.state==="review")?JA(t,"review",PA(e,"review")):cA(t)}function PA(e,A){return e.steps.find(t=>t.state===A&&t.reason!==void 0)?.reason}var gt=["video/webm","video/mp4"],pi="image/jpeg",It=2,lt=1e3,Kn=1e4,ut=3,dt=10,Vn=30,Wn=3,Xn=new Set(["network_error","timeout","upload_failed","unknown_error","rate_limit_exceeded"]),zn=new Set(["session_not_active","check_not_found","check_already_finalized","check_out_of_order"]),qA=class extends Error{constructor(){super("verification destroyed"),this.name="FlowCancelledError"}};function Zn(e){let A=e.steps.findIndex(i=>i.checkType==="liveness");return A<0?!1:e.steps.slice(A+1).find(i=>EA(i))?.checkType==="face_match"}var BA=class extends Error{constructor(A){super(A),this.name="AttestationError"}};async function pA(e,A,t={}){let i=e.uuid(),n=!1,o=!1,r=0,s=0;for(;;)try{return await A(e.client,i)}catch(a){if(a instanceof BA){if(o)throw a;o=!0,t.onAttestationInvalid?.(),i=e.uuid();continue}if(!(a instanceof eA))throw a;if(a.code==="consent_required"&&!n){n=!0,await e.ensureConsent({force:!0}),i=e.uuid();continue}if(a.code==="attestation_invalid"&&!o){o=!0,t.onAttestationInvalid?.(),i=e.uuid();continue}if(a.code==="inference_unavailable"&&s<ut){s+=1;let c=Math.min(a.retryAfterSeconds??dt,Vn);e.onBusy?.({attempt:s,retryAfterSeconds:c}),await e.sleep(c*1e3),t.onAttestationInvalid?.(),i=e.uuid();continue}if(Xn.has(a.code)&&r<It){r+=1;let c=a.retryAfterSeconds!==void 0?a.retryAfterSeconds*1e3:void 0;await e.sleep(c!==void 0?Math.min(c,Kn):lt);continue}throw a}}var jn=new Set(["passed","review","processing","skipped"]);async function De(e){let{artifacts:A}=e,t=A.clip,i=UA(A.clipMime??"");if(A.degraded==="NO_RECORDER"||t===void 0||!gt.includes(i))return{kind:"unavailable",reason:"no_recorder"};let n=$n(e,A.keyframe),o=Ao(e,t,i,n),r=eo(e,n),s=!1,a=!1,c=!0,g=0;for(;;){if(e.isCancelled())return{kind:"cancelled"};let l=e.getView();if(tA(l))return{kind:"settled"};let d=rA(l)?.checkType,C=d==="liveness"&&!s?"liveness":d==="face_match"&&!a&&c?"face_match":null;if(C===null)return{kind:"settled"};if(C==="liveness"&&A.captureChannel==="desktop"&&iA(l,"liveness")?.requiresMobile===!0)return{kind:"unavailable",reason:"mobile_required"};try{if(C==="liveness"){s=!0;let u=await o();if(e.isCancelled())return{kind:"cancelled"};a=ct(l,u),e.applySubmit(u,"liveness");let E=iA(e.getView(),"liveness")?.state;c=E===void 0||jn.has(E)}else{a=!0;let u=await r();if(e.isCancelled())return{kind:"cancelled"};e.applySubmit(u,"face_match")}}catch(u){if(e.isCancelled()||u instanceof qA)return{kind:"cancelled"};let E=io(u);if(E.kind!=="resync"||g>=Wn)return E;g+=1,await e.refreshView()}}}function $n(e,A){let t=null,i=null,n=()=>t!==null?Promise.resolve(t):(i??(i=(async()=>{let o=await e.client.presign("image",pi,A.size);return await e.client.uploadToSpaces(o.uploadUrl,A,pi),t=(await e.client.completeMedia(o.key)).key,t})().finally(()=>{i=null})),i);return{ensure:n,attempt:()=>n().catch(()=>null),current:()=>t}}function Ao(e,A,t,i){let n=null,o,r=null;return()=>pA(e,async(s,a)=>{if(n===null||o===void 0){if(e.isCancelled())throw new qA;o===void 0&&!Zn(e.getView())&&(o=null),e.onPhase("uploading");let[c,g]=await Promise.all([n!==null?Promise.resolve(n):(async()=>{let l=await s.presign("clip",t,A.size);return await s.uploadToSpaces(l.uploadUrl,A,t),(await s.completeMedia(l.key)).key})(),o!==void 0?Promise.resolve(o===null?null:o.imageKey):i.attempt()]);n=c,o===void 0&&(o=g===null?null:{imageKey:g})}if(r===null)try{r=await e.getAttestation()}catch(c){throw new BA(c instanceof Error?c.message:String(c))}return e.onPhase("checking"),s.submitCheck("liveness",{clipKey:n,attestation:r,captureChannel:e.artifacts.captureChannel,capture:to(e.artifacts),...o!==null?{faceMatch:o}:{}},{idempotencyKey:a})},{onAttestationInvalid:()=>{r=null}})}function eo(e,A){return()=>pA(e,async(t,i)=>{if(A.current()===null){if(e.isCancelled())throw new qA;e.onPhase("uploading")}let n=await A.ensure();return e.onPhase("checking"),t.submitCheck("face_match",{imageKey:n},{idempotencyKey:i})})}function to(e){return{...e.engine!==void 0?{engine:e.engine}:{},width:e.width,height:e.height,...e.clipMs!==void 0?{clipMs:e.clipMs}:{},...e.cameraReadyMs!==void 0?{cameraReadyMs:e.cameraReadyMs}:{},...e.faceAcquireMs!==void 0?{faceAcquireMs:e.faceAcquireMs}:{},...e.captureMs!==void 0?{captureMs:e.captureMs}:{}}}function io(e){if(e instanceof BA)return{kind:"unavailable",reason:"attestation"};if(e instanceof eA){if(e.code==="attestation_invalid")return{kind:"unavailable",reason:"attestation"};if(e.code==="method_required")return{kind:"unavailable",reason:"method_required"};if(e.code==="inference_unavailable")return{kind:"unavailable",reason:"inference_unavailable"};if(e.code==="mobile_required")return{kind:"unavailable",reason:"mobile_required"};if(zn.has(e.code))return{kind:"resync"}}return{kind:"unavailable",reason:"network"}}var Et="https://challenges.cloudflare.com/turnstile/v0/api.js",wi=3e4;function yi(){return window.turnstile}var KA=null;function Di(){KA=null}function Bt(){return KA??(KA=new Promise((e,A)=>{let t=yi();if(t){e(t);return}let i=document.createElement("script");i.src=Et,i.async=!0,i.onload=()=>{let n=yi();n?e(n):(KA=null,A(new Error("Turnstile script loaded but exposed no API")))},i.onerror=()=>{KA=null,i.remove(),A(new Error("Turnstile script failed to load"))},document.head.appendChild(i)})),KA}function Ct(){return new Error("Turnstile challenge aborted")}async function be(e,A={}){let t=A.timeoutMs??3e4,i=A.signal;if(i?.aborted)throw Ct();let n=await Bt();if(i?.aborted)throw Ct();let o=document.createElement("div");document.body.appendChild(o);let r,s,a;try{return await new Promise((c,g)=>{if(s=setTimeout(()=>g(new Error(`Turnstile challenge timed out (${t} ms)`)),t),i!==void 0&&(a=()=>g(Ct()),i.addEventListener("abort",a,{once:!0})),r=n.render(o,{sitekey:e,execution:"execute",appearance:"execute",callback:c,"error-callback":l=>{g(new Error(`Turnstile challenge failed${l?` (${l})`:""}`))}}),r===void 0){g(new Error("Turnstile render returned no widget id"));return}n.execute(r)})}finally{if(clearTimeout(s),i!==void 0&&a!==void 0&&i.removeEventListener("abort",a),r!==void 0)try{n.remove(r)}catch{}o.remove()}}var no="https://api.sekyro.ai",ht=5e3,Qt=1500,mt=10;function oo(e){return new Promise(A=>setTimeout(A,e))}function ft(){let e=globalThis.crypto;if(e!==void 0&&typeof e.randomUUID=="function")return e.randomUUID();let A=new Uint8Array(16);if(e!==void 0&&typeof e.getRandomValues=="function")e.getRandomValues(A);else for(let i=0;i<16;i+=1)A[i]=Math.floor(Math.random()*256);A[6]=(A[6]??0)&15|64,A[8]=(A[8]??0)&63|128;let t=[...A].map(i=>i.toString(16).padStart(2,"0")).join("");return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}function bi(e,A){let t=e.apiOrigin??no,i=e.autoRetry??!0,n=e.rearm??"auto",o=A.client??new ye({apiOrigin:t,sessionToken:e.clientToken,...e.fetch!==void 0?{fetch:e.fetch}:{}}),r=A.sleep??oo,s=A.uuid??ft,a=A.getTurnstileToken??be,c=he(),g=!1,l=new AbortController,d=null,C=null,u=B=>{d=B,c.emit("view",B)},E=()=>d!==null?Promise.resolve(d):(C??(C=o.getSession().then(B=>(u(B),B)).finally(()=>{C=null})),C),p=async()=>{let B=await o.getSession();return u(B),B},I=(B,y)=>{d!==null&&u(we(d,B,y))},Q=null,D=async B=>{let y=await E();!B&&y.consentRecorded||(await o.recordConsent(e.locale),d!==null&&u({...d,consentRecorded:!0}))},w=B=>B?.force===!0?D(!0):(Q??(Q=D(!1).finally(()=>{Q=null})),Q),R={client:o,ensureConsent:B=>w(B),sleep:r,uuid:s,onBusy:B=>{g||c.emit("busy",B)}},m=!1,f=B=>{if(B.status==="approved"||B.status==="rejected"||B.status==="review"){if(m)return;m=!0}c.emit("result",B)},b=async B=>{let y=d;B&&(y=await p().catch(()=>d));let U=y===null?null:YA(y);return U!==null&&!g&&c.emit("method",U),U!==null},h=null,P=!1,O=!1,z=()=>pA(R,(B,y)=>B.submitCheck("population_registry",{},{idempotencyKey:y})),T=B=>B.nniBound===!0||iA(B,"population_registry")?.nniFromDocument===!0,S=async()=>{try{let B=await E();if(tA(B)||YA(B)!==null||rA(B)?.checkType!=="population_registry")return;if(!T(B)&&iA(B,"population_registry")?.nniFromDocument===void 0&&(B=await p()),!T(B)){O=!0;return}await w(),I(await z(),"population_registry");let y=d===null?void 0:iA(d,"population_registry");if(y?.state==="input_required"&&y.reason==="registry_unavailable"&&y.attemptsRemaining>0){if(await r(ht),g)return;I(await z(),"population_registry")}}catch(B){if(B instanceof eA&&B.code==="method_required"){await p().catch(()=>{});return}P=!0;return}d!==null&&tA(d)&&f(OA(d,{autoRetry:i}))},k=()=>(h??(h=S()),h),q=async()=>{let y=(await E()).turnstileSiteKey;return y===null?{sandbox:!0}:{channel:"hosted_web",siteKey:y,turnstileToken:await a(y,{signal:l.signal})}},J=null,G=Promise.resolve(),L=0,v=async()=>{let B=await E();for(let y=0;y<mt&&!g&&!tA(B)&&rA(B)===void 0&&B.steps.some(U=>U.state==="processing")&&(await r(Qt),!g);y+=1)B=await p();return B},M=async(B,y,U)=>{let{capture:Y}=B,K=()=>g,X=()=>B.alive&&B.attempt===U,Z=V=>{X()&&Y.setFlowState(V)},j=V=>{if(!K()&&(f(V),!!X())){if(V.status==="retry"&&n==="auto"){Y.handle.retry();return}Y.setFlowState("done")}};if(y.degraded==="NO_RECORDER"){let V=d?.sessionId??(await E().catch(()=>null))?.sessionId??"";j(cA(V,"no_recorder"));return}Z("uploading");try{if(await k(),K())return;let V=await E();if(!tA(V)&&YA(V)!==null){await b(!1),j(cA(V.sessionId,"method_required"));return}if(!tA(V)){if(rA(V)?.checkType==="population_registry"){j(cA(V.sessionId,O?"nni_unbound":P?"network":"registry_unavailable"));return}if(await w(),K())return;let $=await De({...R,artifacts:y,getAttestation:q,getView:()=>d??V,refreshView:p,applySubmit:I,onPhase:Z,isCancelled:K});if(K())return;if($.kind==="unavailable"){if($.reason==="method_required"&&await b(!0),K())return;j(cA(V.sessionId,$.reason));return}$.kind==="resync"&&await p(),V=await v()}j(OA(V,{autoRetry:i}))}catch{if(K())return;j(cA(d?.sessionId??"","network"))}},F=(B,y)=>{let U=B.attempt;L>0&&B.alive&&B.capture.setFlowState("uploading"),L+=1,G=G.then(()=>M(B,y,U)).finally(()=>{L-=1}).catch(()=>{})};return{consent:()=>w(),startRegistry:k,methodChoice:async()=>{if(g)return null;try{let B=await E();return g?null:YA(B)}catch{return null}},selectMethod:async B=>{let y=await o.selectMethod(B);g||u(y)},mobileRequired:async()=>{if(g)return!1;try{let B=await E();if(g||tA(B))return!1;let y=iA(B,"liveness");return EA(y)&&y?.requiresMobile===!0&&!lA()}catch{return!1}},liveness:B=>{J?.capture.handle.destroy(),J!==null&&(J.alive=!1);let y=pe(B,A.capture),U={capture:y,alive:!0,attempt:0};J=U,y.handle.on("state",K=>{K==="starting"&&(U.attempt+=1)}),y.handle.on("captured",K=>{Promise.resolve().then(()=>F(U,K))});let Y=y.handle;return{get state(){return Y.state},on:(K,X)=>Y.on(K,X),start:K=>Y.start(K),retry:()=>Y.retry(),switchCamera:()=>Y.switchCamera(),destroy:()=>{U.alive=!1,J===U&&(J=null),Y.destroy()}}},voiceClips:async()=>{if(g)return null;try{let B=await E();return g?null:B.voice??null}catch{return null}},on:(B,y)=>c.on(B,y),destroy:()=>{g=!0,l.abort(),J!==null&&(J.alive=!1,J.capture.handle.destroy(),J=null),c.clear()}}}0&&(module.exports={ASPECT_BANDS,AttestationError,CAPTURE_CANDIDATE_SPACING_MS,CAPTURE_EXTRA_CANDIDATES,CENTERED_FACTOR,CIRCLE_RADIUS_FACTOR,CLIP_MIME_ALLOWLIST,CaptureSessionDoc,DOC_ANALYSIS_DEFAULTS,DOC_CADENCE_HZ,DOC_DETECT_WIDTH,DOC_FEEDBACK_STATES,DOC_GATE_V1_DEFAULTS,DocFeedbackAggregator,DocGateV1,INFERENCE_RETRY_ATTEMPTS,INFERENCE_RETRY_DEFAULT_S,IN_CIRCLE_FACTOR,MRZ_BAND_DEFAULTS,MR_DOC_CATALOG,PRELOAD_BLAZE_INIT_TIMEOUT_MS,PRELOAD_BUDGET_MS,PROCESSING_POLL_DELAY_MS,PROCESSING_POLL_MAX,REGISTRY_RETRY_DELAY_MS,RETRYABLE_REASONS,TRANSPORT_REPLAY_ATTEMPTS,TRANSPORT_REPLAY_DELAY_MS,TURNSTILE_EXECUTE_TIMEOUT_MS,TURNSTILE_SCRIPT_SRC,analyzeDocFrame,aspectBand,createLivenessCaptureInternal,createVerificationInternal,detectIsMobile,detectMrzBand,executeTurnstile,feedbackVoteOf,frontierStep,isExecutable,isTerminalView,loadTurnstile,mapEngineState,mapErrorKind,mapGuidanceHint,mapSessionResult,mergeSessionView,preloadedDetectorFactory,quadAreaFrac,quadBounds,quadIou,quadTiltRatio,randomUuid,resetLivenessAssetsForTests,resetTurnstileForTests,runLivenessFlow,runSubmission,setPreloadLoaderForTests,specularFraction,stepOf,warmDocDetector});
|