@sekyro/web 0.0.0-stage → 0.1.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/LICENSE +20 -0
- package/README.md +92 -2
- package/dist/capture-constraints-BujkZcfb.d.cts +21 -0
- package/dist/capture-constraints-BujkZcfb.d.ts +21 -0
- package/dist/capture-session-v3-BJ8ciaWl.d.ts +457 -0
- package/dist/capture-session-v3-CAoWM8iT.d.cts +457 -0
- package/dist/chunk-BPYVZUJA.js +852 -0
- package/dist/chunk-BPYVZUJA.js.map +1 -0
- package/dist/chunk-LOTAJDRE.js +1094 -0
- package/dist/chunk-LOTAJDRE.js.map +1 -0
- package/dist/chunk-MWNFSNMP.js +52 -0
- package/dist/chunk-MWNFSNMP.js.map +1 -0
- package/dist/chunk-NILNKLK6.js +50 -0
- package/dist/chunk-NILNKLK6.js.map +1 -0
- package/dist/chunk-NWF46H7K.js +359 -0
- package/dist/chunk-NWF46H7K.js.map +1 -0
- package/dist/chunk-WY6U7AIK.js +193 -0
- package/dist/chunk-WY6U7AIK.js.map +1 -0
- package/dist/chunk-YVFF4Q4G.js +327 -0
- package/dist/chunk-YVFF4Q4G.js.map +1 -0
- package/dist/client/index.cjs +357 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +39 -0
- package/dist/client/index.d.ts +39 -0
- package/dist/client/index.js +15 -0
- package/dist/client/index.js.map +1 -0
- package/dist/core/index.cjs +1605 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +87 -0
- package/dist/core/index.d.ts +87 -0
- package/dist/core/index.js +139 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/v3/detect-worker.cjs +392 -0
- package/dist/core/v3/detect-worker.cjs.map +1 -0
- package/dist/core/v3/detect-worker.d.cts +1 -0
- package/dist/core/v3/detect-worker.d.ts +1 -0
- package/dist/core/v3/detect-worker.js +13 -0
- package/dist/core/v3/detect-worker.js.map +1 -0
- package/dist/create-verification-CjLnJ0w6.d.ts +348 -0
- package/dist/create-verification-EnZ13uY4.d.cts +348 -0
- package/dist/detect-worker-CKXHlXx9.d.cts +105 -0
- package/dist/detect-worker-CKXHlXx9.d.ts +105 -0
- package/dist/detector-scanic-4C6IEW2K.js +787 -0
- package/dist/detector-scanic-4C6IEW2K.js.map +1 -0
- package/dist/hosted-client-BpvBqu9f.d.cts +408 -0
- package/dist/hosted-client-BpvBqu9f.d.ts +408 -0
- package/dist/index.cjs +2636 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/index.cjs +4166 -0
- package/dist/internal/index.cjs.map +1 -0
- package/dist/internal/index.d.cts +431 -0
- package/dist/internal/index.d.ts +431 -0
- package/dist/internal/index.js +647 -0
- package/dist/internal/index.js.map +1 -0
- package/dist/popup/index.cjs +76 -0
- package/dist/popup/index.cjs.map +1 -0
- package/dist/popup/index.d.cts +66 -0
- package/dist/popup/index.d.ts +66 -0
- package/dist/popup/index.js +11 -0
- package/dist/popup/index.js.map +1 -0
- package/dist/scanic-ml-stub-S2OZ6HEF.js +10 -0
- package/dist/scanic-ml-stub-S2OZ6HEF.js.map +1 -0
- package/package.json +118 -4
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CENTERED_FACTOR,
|
|
3
|
+
CIRCLE_RADIUS_FACTOR,
|
|
4
|
+
CLIP_MS,
|
|
5
|
+
CaptureSessionV3,
|
|
6
|
+
ClipError,
|
|
7
|
+
ClipRecorder,
|
|
8
|
+
DEFAULT_CLIP_MIME,
|
|
9
|
+
DEVICE_PROFILES,
|
|
10
|
+
DetectScheduler,
|
|
11
|
+
DeviceProfiler,
|
|
12
|
+
FaceGateV3,
|
|
13
|
+
IN_CIRCLE_FACTOR,
|
|
14
|
+
KEYFRAME_CANDIDATES,
|
|
15
|
+
KEYFRAME_WINDOW_MS,
|
|
16
|
+
MAX_CLIP_BYTES,
|
|
17
|
+
MIME_CANDIDATES,
|
|
18
|
+
STOP_GRACE_MS,
|
|
19
|
+
TIMESLICE_MS,
|
|
20
|
+
V3_GATE_DEFAULTS,
|
|
21
|
+
VIDEO_BPS,
|
|
22
|
+
buildCaptureConstraints,
|
|
23
|
+
buildLivenessV2FormData,
|
|
24
|
+
cameraPreflight,
|
|
25
|
+
captureMirroredKeyframe,
|
|
26
|
+
classifyDevice,
|
|
27
|
+
detectIsMobile,
|
|
28
|
+
extForMime,
|
|
29
|
+
isClipTooLarge,
|
|
30
|
+
isMediaRecorderSupported,
|
|
31
|
+
mapGetUserMediaError,
|
|
32
|
+
pickMimeType,
|
|
33
|
+
probeDevice
|
|
34
|
+
} from "../chunk-LOTAJDRE.js";
|
|
35
|
+
import {
|
|
36
|
+
DEFAULT_TIER_ORDER,
|
|
37
|
+
DEFAULT_V3_ASSET_BASE_URL,
|
|
38
|
+
DetectorLadderError,
|
|
39
|
+
createDetector,
|
|
40
|
+
intrinsicSize,
|
|
41
|
+
toStreamPixels
|
|
42
|
+
} from "../chunk-NWF46H7K.js";
|
|
43
|
+
import {
|
|
44
|
+
EXPOSURE_BAND,
|
|
45
|
+
SHARP_REF,
|
|
46
|
+
exposure,
|
|
47
|
+
meanLuma,
|
|
48
|
+
sharpness
|
|
49
|
+
} from "../chunk-MWNFSNMP.js";
|
|
50
|
+
|
|
51
|
+
// src/core/messages.ts
|
|
52
|
+
var errorCopy = {
|
|
53
|
+
permission: {
|
|
54
|
+
key: "fcv2.err.permission",
|
|
55
|
+
fr: "Autorisez la cam\xE9ra dans les r\xE9glages du navigateur (ic\xF4ne cam\xE9ra dans la barre d\u2019adresse), puis r\xE9essayez."
|
|
56
|
+
},
|
|
57
|
+
notfound: {
|
|
58
|
+
key: "fcv2.err.notfound",
|
|
59
|
+
fr: "Aucune cam\xE9ra d\xE9tect\xE9e. Branchez ou activez une cam\xE9ra, puis r\xE9essayez."
|
|
60
|
+
},
|
|
61
|
+
inuse: {
|
|
62
|
+
key: "fcv2.err.inuse",
|
|
63
|
+
fr: "La cam\xE9ra est utilis\xE9e par une autre application. Fermez-la puis r\xE9essayez."
|
|
64
|
+
},
|
|
65
|
+
overconstrained: {
|
|
66
|
+
key: "fcv2.err.overconstrained",
|
|
67
|
+
fr: "La cam\xE9ra ne prend pas en charge les r\xE9glages demand\xE9s. Nouvel essai en cours\u2026"
|
|
68
|
+
},
|
|
69
|
+
insecure: {
|
|
70
|
+
key: "fcv2.err.insecure",
|
|
71
|
+
fr: "La cam\xE9ra n\xE9cessite une connexion s\xE9curis\xE9e (HTTPS). Ouvrez la page en HTTPS."
|
|
72
|
+
},
|
|
73
|
+
unsupported: {
|
|
74
|
+
key: "fcv2.err.unsupported",
|
|
75
|
+
fr: "Votre navigateur ne prend pas en charge l\u2019enregistrement vid\xE9o. Utilisez un navigateur r\xE9cent."
|
|
76
|
+
},
|
|
77
|
+
generic: {
|
|
78
|
+
key: "fcv2.err.generic",
|
|
79
|
+
fr: "Cam\xE9ra indisponible. R\xE9essayez ou contactez le support."
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var uiCopy = {
|
|
83
|
+
recording: { key: "fcv2.recording", fr: "Enregistrement en cours\u2026" },
|
|
84
|
+
uploading: { key: "fcv2.uploading", fr: "Envoi en cours\u2026" },
|
|
85
|
+
clipTooLarge: {
|
|
86
|
+
key: "fcv2.err.cliptoolarge",
|
|
87
|
+
fr: "La vid\xE9o est trop volumineuse. R\xE9essayez dans un endroit mieux \xE9clair\xE9."
|
|
88
|
+
},
|
|
89
|
+
retry: { key: "fcv2.retry", fr: "R\xE9essayer" },
|
|
90
|
+
listen: { key: "fcv2.listen", fr: "\xC9couter" }
|
|
91
|
+
};
|
|
92
|
+
export {
|
|
93
|
+
CENTERED_FACTOR,
|
|
94
|
+
CIRCLE_RADIUS_FACTOR,
|
|
95
|
+
CLIP_MS,
|
|
96
|
+
CaptureSessionV3,
|
|
97
|
+
ClipError,
|
|
98
|
+
ClipRecorder,
|
|
99
|
+
DEFAULT_CLIP_MIME,
|
|
100
|
+
DEFAULT_TIER_ORDER,
|
|
101
|
+
DEFAULT_V3_ASSET_BASE_URL,
|
|
102
|
+
DEVICE_PROFILES,
|
|
103
|
+
DetectScheduler,
|
|
104
|
+
DetectorLadderError,
|
|
105
|
+
DeviceProfiler,
|
|
106
|
+
EXPOSURE_BAND,
|
|
107
|
+
FaceGateV3,
|
|
108
|
+
IN_CIRCLE_FACTOR,
|
|
109
|
+
KEYFRAME_CANDIDATES,
|
|
110
|
+
KEYFRAME_WINDOW_MS,
|
|
111
|
+
MAX_CLIP_BYTES,
|
|
112
|
+
MIME_CANDIDATES,
|
|
113
|
+
SHARP_REF,
|
|
114
|
+
STOP_GRACE_MS,
|
|
115
|
+
TIMESLICE_MS,
|
|
116
|
+
V3_GATE_DEFAULTS,
|
|
117
|
+
VIDEO_BPS,
|
|
118
|
+
buildCaptureConstraints,
|
|
119
|
+
buildLivenessV2FormData,
|
|
120
|
+
cameraPreflight,
|
|
121
|
+
captureMirroredKeyframe,
|
|
122
|
+
classifyDevice,
|
|
123
|
+
createDetector,
|
|
124
|
+
detectIsMobile,
|
|
125
|
+
errorCopy,
|
|
126
|
+
exposure,
|
|
127
|
+
extForMime,
|
|
128
|
+
intrinsicSize,
|
|
129
|
+
isClipTooLarge,
|
|
130
|
+
isMediaRecorderSupported,
|
|
131
|
+
mapGetUserMediaError,
|
|
132
|
+
meanLuma,
|
|
133
|
+
pickMimeType,
|
|
134
|
+
probeDevice,
|
|
135
|
+
sharpness,
|
|
136
|
+
toStreamPixels,
|
|
137
|
+
uiCopy
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/messages.ts"],"sourcesContent":["// PORTED byte-identical from kyc-ibtikar/lib/faceCaptureV2/messages.ts (calibration-pinned\n// capture contract — spec §6 / risk 1). Do NOT edit without a calibration re-validation.\n\nimport type { CameraErrorKind } from \"./media\";\n\n/** i18n key + inline French fallback (repo convention: t(key, frenchFallback)). */\nexport const errorCopy: Record<CameraErrorKind, { key: string; fr: string }> = {\n permission: {\n key: \"fcv2.err.permission\",\n fr: \"Autorisez la caméra dans les réglages du navigateur (icône caméra dans la barre d’adresse), puis réessayez.\",\n },\n notfound: {\n key: \"fcv2.err.notfound\",\n fr: \"Aucune caméra détectée. Branchez ou activez une caméra, puis réessayez.\",\n },\n inuse: {\n key: \"fcv2.err.inuse\",\n fr: \"La caméra est utilisée par une autre application. Fermez-la puis réessayez.\",\n },\n overconstrained: {\n key: \"fcv2.err.overconstrained\",\n fr: \"La caméra ne prend pas en charge les réglages demandés. Nouvel essai en cours…\",\n },\n insecure: {\n key: \"fcv2.err.insecure\",\n fr: \"La caméra nécessite une connexion sécurisée (HTTPS). Ouvrez la page en HTTPS.\",\n },\n unsupported: {\n key: \"fcv2.err.unsupported\",\n fr: \"Votre navigateur ne prend pas en charge l’enregistrement vidéo. Utilisez un navigateur récent.\",\n },\n generic: {\n key: \"fcv2.err.generic\",\n fr: \"Caméra indisponible. Réessayez ou contactez le support.\",\n },\n};\n\nexport const uiCopy = {\n recording: { key: \"fcv2.recording\", fr: \"Enregistrement en cours…\" },\n uploading: { key: \"fcv2.uploading\", fr: \"Envoi en cours…\" },\n clipTooLarge: {\n key: \"fcv2.err.cliptoolarge\",\n fr: \"La vidéo est trop volumineuse. Réessayez dans un endroit mieux éclairé.\",\n },\n retry: { key: \"fcv2.retry\", fr: \"Réessayer\" },\n listen: { key: \"fcv2.listen\", fr: \"Écouter\" },\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMO,IAAM,YAAkE;AAAA,EAC7E,YAAY;AAAA,IACV,KAAK;AAAA,IACL,IAAI;AAAA,EACN;AAAA,EACA,UAAU;AAAA,IACR,KAAK;AAAA,IACL,IAAI;AAAA,EACN;AAAA,EACA,OAAO;AAAA,IACL,KAAK;AAAA,IACL,IAAI;AAAA,EACN;AAAA,EACA,iBAAiB;AAAA,IACf,KAAK;AAAA,IACL,IAAI;AAAA,EACN;AAAA,EACA,UAAU;AAAA,IACR,KAAK;AAAA,IACL,IAAI;AAAA,EACN;AAAA,EACA,aAAa;AAAA,IACX,KAAK;AAAA,IACL,IAAI;AAAA,EACN;AAAA,EACA,SAAS;AAAA,IACP,KAAK;AAAA,IACL,IAAI;AAAA,EACN;AACF;AAEO,IAAM,SAAS;AAAA,EACpB,WAAW,EAAE,KAAK,kBAAkB,IAAI,gCAA2B;AAAA,EACnE,WAAW,EAAE,KAAK,kBAAkB,IAAI,uBAAkB;AAAA,EAC1D,cAAc;AAAA,IACZ,KAAK;AAAA,IACL,IAAI;AAAA,EACN;AAAA,EACA,OAAO,EAAE,KAAK,cAAc,IAAI,eAAY;AAAA,EAC5C,QAAQ,EAAE,KAAK,eAAe,IAAI,aAAU;AAC9C;","names":[]}
|
|
@@ -0,0 +1,392 @@
|
|
|
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 __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/core/v3/detect-worker.ts
|
|
31
|
+
var detect_worker_exports = {};
|
|
32
|
+
__export(detect_worker_exports, {
|
|
33
|
+
WORKER_PROTOCOL_VERSION: () => WORKER_PROTOCOL_VERSION,
|
|
34
|
+
WorkerDetectorProxy: () => WorkerDetectorProxy,
|
|
35
|
+
installWorkerDetectorHost: () => installWorkerDetectorHost,
|
|
36
|
+
workerOffloadSupported: () => workerOffloadSupported
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(detect_worker_exports);
|
|
39
|
+
|
|
40
|
+
// src/core/v3/detector-blaze.ts
|
|
41
|
+
var BLAZE_INIT_TIMEOUT_MS = 1e4;
|
|
42
|
+
var BlazeDetector = class _BlazeDetector {
|
|
43
|
+
constructor(inner, fallbackReason) {
|
|
44
|
+
this.tier = "blaze";
|
|
45
|
+
this.inner = inner;
|
|
46
|
+
if (fallbackReason !== void 0) this.fallbackReason = fallbackReason;
|
|
47
|
+
}
|
|
48
|
+
static async create(assetBaseUrl, fallbackReason) {
|
|
49
|
+
const vision = await import("@mediapipe/tasks-vision");
|
|
50
|
+
const fileset = await vision.FilesetResolver.forVisionTasks(assetBaseUrl);
|
|
51
|
+
const inner = await vision.FaceDetector.createFromOptions(fileset, {
|
|
52
|
+
baseOptions: {
|
|
53
|
+
modelAssetPath: `${assetBaseUrl}/blaze_face_short_range.tflite`,
|
|
54
|
+
// CPU delegate — the iOS-Safari-safe path (see header). NEVER 'GPU'.
|
|
55
|
+
delegate: "CPU"
|
|
56
|
+
},
|
|
57
|
+
runningMode: "IMAGE"
|
|
58
|
+
});
|
|
59
|
+
return new _BlazeDetector(inner, fallbackReason);
|
|
60
|
+
}
|
|
61
|
+
async detect(source, sourceWidth, sourceHeight) {
|
|
62
|
+
let detections;
|
|
63
|
+
try {
|
|
64
|
+
detections = this.inner.detect(source).detections;
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const best = pickMostConfident(detections);
|
|
69
|
+
if (best?.boundingBox === void 0) return null;
|
|
70
|
+
const size = intrinsicSize(source);
|
|
71
|
+
const eyes = extractEyes(best.keypoints, size);
|
|
72
|
+
const observation = {
|
|
73
|
+
box: {
|
|
74
|
+
x: best.boundingBox.originX,
|
|
75
|
+
y: best.boundingBox.originY,
|
|
76
|
+
width: best.boundingBox.width,
|
|
77
|
+
height: best.boundingBox.height
|
|
78
|
+
},
|
|
79
|
+
...eyes !== void 0 ? { eyes } : {},
|
|
80
|
+
score: best.categories[0]?.score ?? 0
|
|
81
|
+
};
|
|
82
|
+
return toStreamPixels(observation, source, sourceWidth, sourceHeight);
|
|
83
|
+
}
|
|
84
|
+
destroy() {
|
|
85
|
+
try {
|
|
86
|
+
this.inner.close();
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
function pickMostConfident(detections) {
|
|
92
|
+
let best;
|
|
93
|
+
let bestScore = -1;
|
|
94
|
+
for (const detection of detections) {
|
|
95
|
+
const score = detection.categories[0]?.score ?? 0;
|
|
96
|
+
if (score > bestScore) {
|
|
97
|
+
best = detection;
|
|
98
|
+
bestScore = score;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return best;
|
|
102
|
+
}
|
|
103
|
+
function extractEyes(keypoints, size) {
|
|
104
|
+
const a = keypoints?.[0];
|
|
105
|
+
const b = keypoints?.[1];
|
|
106
|
+
if (a === void 0 || b === void 0) return void 0;
|
|
107
|
+
const toSource = (p) => ({ x: p.x * size.width, y: p.y * size.height });
|
|
108
|
+
const [pa, pb] = [toSource(a), toSource(b)];
|
|
109
|
+
return pa.x <= pb.x ? { left: pa, right: pb } : { left: pb, right: pa };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/core/v3/detector-native.ts
|
|
113
|
+
function smokeSource() {
|
|
114
|
+
if (typeof OffscreenCanvas === "function") return new OffscreenCanvas(2, 2);
|
|
115
|
+
const canvas = document.createElement("canvas");
|
|
116
|
+
canvas.width = 2;
|
|
117
|
+
canvas.height = 2;
|
|
118
|
+
return canvas;
|
|
119
|
+
}
|
|
120
|
+
async function probeNativeDetector() {
|
|
121
|
+
const ctor = globalThis.FaceDetector;
|
|
122
|
+
if (typeof ctor !== "function") return { ok: false, reason: "unsupported" };
|
|
123
|
+
try {
|
|
124
|
+
const instance = new ctor({ fastMode: true, maxDetectedFaces: 1 });
|
|
125
|
+
await instance.detect(smokeSource());
|
|
126
|
+
return { ok: true, instance };
|
|
127
|
+
} catch (err) {
|
|
128
|
+
return { ok: false, reason: `smoke_failed:${err instanceof Error ? err.message : String(err)}` };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
var NativeDetector = class {
|
|
132
|
+
constructor(inner, fallbackReason) {
|
|
133
|
+
this.tier = "native";
|
|
134
|
+
this.inner = inner;
|
|
135
|
+
if (fallbackReason !== void 0) this.fallbackReason = fallbackReason;
|
|
136
|
+
}
|
|
137
|
+
async detect(source, sourceWidth, sourceHeight) {
|
|
138
|
+
let faces;
|
|
139
|
+
try {
|
|
140
|
+
faces = await this.inner.detect(source);
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const face = faces[0];
|
|
145
|
+
if (face === void 0) return null;
|
|
146
|
+
const eyes = extractEyes2(face.landmarks);
|
|
147
|
+
const observation = {
|
|
148
|
+
box: {
|
|
149
|
+
x: face.boundingBox.x,
|
|
150
|
+
y: face.boundingBox.y,
|
|
151
|
+
width: face.boundingBox.width,
|
|
152
|
+
height: face.boundingBox.height
|
|
153
|
+
},
|
|
154
|
+
...eyes !== void 0 ? { eyes } : {},
|
|
155
|
+
// The Shape Detection API exposes no confidence — the smoke-tested
|
|
156
|
+
// native tier treats any detection as confident.
|
|
157
|
+
score: 1
|
|
158
|
+
};
|
|
159
|
+
return toStreamPixels(observation, source, sourceWidth, sourceHeight);
|
|
160
|
+
}
|
|
161
|
+
destroy() {
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
function extractEyes2(landmarks) {
|
|
165
|
+
if (landmarks === void 0) return void 0;
|
|
166
|
+
const eyePoints = landmarks.filter((landmark) => landmark.type === "eye").map((landmark) => landmark.locations[0]).filter((point) => point !== void 0);
|
|
167
|
+
const [a, b] = eyePoints;
|
|
168
|
+
if (a === void 0 || b === void 0) return void 0;
|
|
169
|
+
return a.x <= b.x ? { left: a, right: b } : { left: b, right: a };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/core/v3/detector.ts
|
|
173
|
+
var DEFAULT_V3_ASSET_BASE_URL = "/models-v3";
|
|
174
|
+
var DEFAULT_TIER_ORDER = ["native", "blaze"];
|
|
175
|
+
var DetectorLadderError = class extends Error {
|
|
176
|
+
constructor(fallbackReason) {
|
|
177
|
+
super(`no detector tier available (${fallbackReason})`);
|
|
178
|
+
this.name = "DetectorLadderError";
|
|
179
|
+
this.fallbackReason = fallbackReason;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
function intrinsicSize(source) {
|
|
183
|
+
const candidate = source;
|
|
184
|
+
if (typeof candidate.videoWidth === "number" && candidate.videoWidth > 0) {
|
|
185
|
+
return { width: candidate.videoWidth, height: candidate.videoHeight ?? 0 };
|
|
186
|
+
}
|
|
187
|
+
if (typeof candidate.naturalWidth === "number" && candidate.naturalWidth > 0) {
|
|
188
|
+
return { width: candidate.naturalWidth, height: candidate.naturalHeight ?? 0 };
|
|
189
|
+
}
|
|
190
|
+
const width = typeof candidate.width === "number" ? candidate.width : 0;
|
|
191
|
+
const height = typeof candidate.height === "number" ? candidate.height : 0;
|
|
192
|
+
return { width, height };
|
|
193
|
+
}
|
|
194
|
+
function toStreamPixels(observation, source, streamWidth, streamHeight) {
|
|
195
|
+
const size = intrinsicSize(source);
|
|
196
|
+
const scaleX = size.width > 0 ? streamWidth / size.width : 1;
|
|
197
|
+
const scaleY = size.height > 0 ? streamHeight / size.height : 1;
|
|
198
|
+
const scalePoint = (p) => ({ x: p.x * scaleX, y: p.y * scaleY });
|
|
199
|
+
return {
|
|
200
|
+
box: {
|
|
201
|
+
x: observation.box.x * scaleX,
|
|
202
|
+
y: observation.box.y * scaleY,
|
|
203
|
+
width: observation.box.width * scaleX,
|
|
204
|
+
height: observation.box.height * scaleY
|
|
205
|
+
},
|
|
206
|
+
...observation.eyes !== void 0 ? { eyes: { left: scalePoint(observation.eyes.left), right: scalePoint(observation.eyes.right) } } : {},
|
|
207
|
+
score: observation.score
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function describeError(err) {
|
|
211
|
+
return err instanceof Error ? err.message : String(err);
|
|
212
|
+
}
|
|
213
|
+
async function createDetector(options) {
|
|
214
|
+
const order = options.preferred ?? DEFAULT_TIER_ORDER;
|
|
215
|
+
const reasons = [];
|
|
216
|
+
for (const tier of order) {
|
|
217
|
+
if (tier === "native") {
|
|
218
|
+
const probed = await probeNativeDetector();
|
|
219
|
+
if (probed.ok) {
|
|
220
|
+
return new NativeDetector(probed.instance, reasonOf(reasons));
|
|
221
|
+
}
|
|
222
|
+
reasons.push(`native:${probed.reason}`);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
return await withTimeout(
|
|
227
|
+
BlazeDetector.create(
|
|
228
|
+
options.v3AssetBaseUrl ?? DEFAULT_V3_ASSET_BASE_URL,
|
|
229
|
+
reasonOf(reasons)
|
|
230
|
+
),
|
|
231
|
+
options.blazeInitTimeoutMs ?? BLAZE_INIT_TIMEOUT_MS
|
|
232
|
+
);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
reasons.push(
|
|
235
|
+
err instanceof InitTimeoutError ? "blaze:blaze_init_timeout" : `blaze:${describeError(err)}`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
throw new DetectorLadderError(reasonOf(reasons) ?? "no_tier_available");
|
|
240
|
+
}
|
|
241
|
+
function reasonOf(reasons) {
|
|
242
|
+
return reasons.length > 0 ? reasons.join("; ") : void 0;
|
|
243
|
+
}
|
|
244
|
+
var InitTimeoutError = class extends Error {
|
|
245
|
+
};
|
|
246
|
+
function withTimeout(promise, deadlineMs) {
|
|
247
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
248
|
+
const timer = setTimeout(() => {
|
|
249
|
+
rejectPromise(new InitTimeoutError(`init exceeded ${deadlineMs}ms`));
|
|
250
|
+
promise.then(
|
|
251
|
+
(value) => value.destroy(),
|
|
252
|
+
() => void 0
|
|
253
|
+
);
|
|
254
|
+
}, deadlineMs);
|
|
255
|
+
promise.then(
|
|
256
|
+
(value) => {
|
|
257
|
+
clearTimeout(timer);
|
|
258
|
+
resolvePromise(value);
|
|
259
|
+
},
|
|
260
|
+
(err) => {
|
|
261
|
+
clearTimeout(timer);
|
|
262
|
+
rejectPromise(err);
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// src/core/v3/detect-worker.ts
|
|
269
|
+
var WORKER_PROTOCOL_VERSION = 1;
|
|
270
|
+
function workerOffloadSupported() {
|
|
271
|
+
return typeof OffscreenCanvas === "function" && typeof createImageBitmap === "function" && typeof Worker === "function";
|
|
272
|
+
}
|
|
273
|
+
function installWorkerDetectorHost(scope, deps = {}) {
|
|
274
|
+
const create = deps.createDetector ?? createDetector;
|
|
275
|
+
let detector = null;
|
|
276
|
+
scope.onmessage = (event) => {
|
|
277
|
+
const message = event.data;
|
|
278
|
+
if (message.v !== WORKER_PROTOCOL_VERSION) {
|
|
279
|
+
message.bitmap?.close?.();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (message.type === "init") {
|
|
283
|
+
void create(message.options).then((created) => {
|
|
284
|
+
detector = created;
|
|
285
|
+
const ready = {
|
|
286
|
+
v: WORKER_PROTOCOL_VERSION,
|
|
287
|
+
type: "ready",
|
|
288
|
+
tier: created.tier,
|
|
289
|
+
...created.fallbackReason !== void 0 ? { fallbackReason: created.fallbackReason } : {}
|
|
290
|
+
};
|
|
291
|
+
scope.postMessage(ready);
|
|
292
|
+
});
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (message.type === "detect") {
|
|
296
|
+
const respond = (observation) => {
|
|
297
|
+
const result = {
|
|
298
|
+
v: WORKER_PROTOCOL_VERSION,
|
|
299
|
+
type: "result",
|
|
300
|
+
id: message.id,
|
|
301
|
+
observation
|
|
302
|
+
};
|
|
303
|
+
scope.postMessage(result);
|
|
304
|
+
message.bitmap.close?.();
|
|
305
|
+
};
|
|
306
|
+
if (detector === null) {
|
|
307
|
+
respond(null);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
void detector.detect(message.bitmap, message.streamWidth, message.streamHeight).then(respond, () => respond(null));
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (typeof self !== "undefined" && typeof self.importScripts === "function") {
|
|
315
|
+
installWorkerDetectorHost(self);
|
|
316
|
+
}
|
|
317
|
+
var WorkerDetectorProxy = class _WorkerDetectorProxy {
|
|
318
|
+
constructor(worker, ready) {
|
|
319
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
320
|
+
this.nextId = 1;
|
|
321
|
+
this.worker = worker;
|
|
322
|
+
this.tier = ready.tier;
|
|
323
|
+
if (ready.fallbackReason !== void 0) this.fallbackReason = ready.fallbackReason;
|
|
324
|
+
worker.addEventListener("message", (event) => {
|
|
325
|
+
const message = event.data;
|
|
326
|
+
if (message.v !== WORKER_PROTOCOL_VERSION || message.type !== "result") return;
|
|
327
|
+
const resolve = this.pending.get(message.id);
|
|
328
|
+
this.pending.delete(message.id);
|
|
329
|
+
resolve?.(message.observation);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
static create(worker, options, readyTimeoutMs = 1e4) {
|
|
333
|
+
return new Promise((resolve, reject) => {
|
|
334
|
+
const timer = setTimeout(() => {
|
|
335
|
+
worker.removeEventListener("message", onReady);
|
|
336
|
+
worker.terminate();
|
|
337
|
+
reject(new Error(`worker detector not ready within ${readyTimeoutMs}ms`));
|
|
338
|
+
}, readyTimeoutMs);
|
|
339
|
+
const onReady = (event) => {
|
|
340
|
+
const message = event.data;
|
|
341
|
+
if (message.v !== WORKER_PROTOCOL_VERSION || message.type !== "ready") return;
|
|
342
|
+
clearTimeout(timer);
|
|
343
|
+
worker.removeEventListener("message", onReady);
|
|
344
|
+
resolve(new _WorkerDetectorProxy(worker, message));
|
|
345
|
+
};
|
|
346
|
+
worker.addEventListener("message", onReady);
|
|
347
|
+
const init = { v: WORKER_PROTOCOL_VERSION, type: "init", options };
|
|
348
|
+
worker.postMessage(init);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
async detect(source, sourceWidth, sourceHeight) {
|
|
352
|
+
let bitmap;
|
|
353
|
+
try {
|
|
354
|
+
if (typeof ImageBitmap !== "undefined" && source instanceof ImageBitmap) {
|
|
355
|
+
bitmap = source;
|
|
356
|
+
} else if (typeof createImageBitmap === "function") {
|
|
357
|
+
bitmap = await createImageBitmap(source);
|
|
358
|
+
} else {
|
|
359
|
+
bitmap = source;
|
|
360
|
+
}
|
|
361
|
+
} catch {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
const id = this.nextId;
|
|
365
|
+
this.nextId += 1;
|
|
366
|
+
return new Promise((resolve) => {
|
|
367
|
+
this.pending.set(id, resolve);
|
|
368
|
+
const message = {
|
|
369
|
+
v: WORKER_PROTOCOL_VERSION,
|
|
370
|
+
type: "detect",
|
|
371
|
+
id,
|
|
372
|
+
bitmap,
|
|
373
|
+
streamWidth: sourceWidth,
|
|
374
|
+
streamHeight: sourceHeight
|
|
375
|
+
};
|
|
376
|
+
this.worker.postMessage(message, [bitmap]);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
destroy() {
|
|
380
|
+
for (const resolve of this.pending.values()) resolve(null);
|
|
381
|
+
this.pending.clear();
|
|
382
|
+
this.worker.terminate();
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
386
|
+
0 && (module.exports = {
|
|
387
|
+
WORKER_PROTOCOL_VERSION,
|
|
388
|
+
WorkerDetectorProxy,
|
|
389
|
+
installWorkerDetectorHost,
|
|
390
|
+
workerOffloadSupported
|
|
391
|
+
});
|
|
392
|
+
//# sourceMappingURL=detect-worker.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/core/v3/detect-worker.ts","../../../src/core/v3/detector-blaze.ts","../../../src/core/v3/detector-native.ts","../../../src/core/v3/detector.ts"],"sourcesContent":["// Worker offload (Task 9) — detection off the main thread when the platform\n// allows (OffscreenCanvas + createImageBitmap + Worker). BOTH protocol sides\n// live in this file so the tiny versioned protocol (v:1) can never drift:\n// - installWorkerDetectorHost: runs INSIDE the worker (auto-installed when\n// this module loads in a DedicatedWorkerGlobalScope; tsup builds it as\n// its own entry so hosts can `new Worker(new URL(...))` the dist file);\n// - WorkerDetectorProxy: main-thread Detector implementation that posts\n// ImageBitmap frames as transferables and resolves results by id.\nimport {\n type CreateDetectorOptions,\n createDetector as defaultCreateDetector,\n type Detector,\n type FaceObservation,\n} from \"./detector\";\n\nexport const WORKER_PROTOCOL_VERSION = 1;\n\ninterface InitMessage {\n v: typeof WORKER_PROTOCOL_VERSION;\n type: \"init\";\n options: CreateDetectorOptions;\n}\ninterface DetectMessage {\n v: typeof WORKER_PROTOCOL_VERSION;\n type: \"detect\";\n id: number;\n bitmap: ImageBitmap;\n streamWidth: number;\n streamHeight: number;\n}\ninterface ReadyMessage {\n v: typeof WORKER_PROTOCOL_VERSION;\n type: \"ready\";\n tier: Detector[\"tier\"];\n fallbackReason?: string;\n}\ninterface ResultMessage {\n v: typeof WORKER_PROTOCOL_VERSION;\n type: \"result\";\n id: number;\n observation: FaceObservation | null;\n}\n\ntype ToWorker = InitMessage | DetectMessage;\ntype ToMain = ReadyMessage | ResultMessage;\n\n/** The slice of Worker/scope surface the protocol needs — test-stubbable. */\nexport interface WorkerScopeLike {\n onmessage: ((event: { data: unknown }) => void) | null;\n postMessage(data: unknown): void;\n}\nexport interface WorkerLike {\n postMessage(data: unknown, transfer?: Transferable[]): void;\n addEventListener(type: \"message\", listener: (event: { data: unknown }) => void): void;\n removeEventListener(type: \"message\", listener: (event: { data: unknown }) => void): void;\n terminate(): void;\n}\n\n/** Feature gate: bitmap capture + transfer + workers must all exist. */\nexport function workerOffloadSupported(): boolean {\n return (\n typeof OffscreenCanvas === \"function\" &&\n typeof createImageBitmap === \"function\" &&\n typeof Worker === \"function\"\n );\n}\n\n/** Worker-side message host. `deps.createDetector` is a test seam. */\nexport function installWorkerDetectorHost(\n scope: WorkerScopeLike,\n deps: { createDetector?: (options: CreateDetectorOptions) => Promise<Detector> } = {},\n): void {\n const create = deps.createDetector ?? defaultCreateDetector;\n let detector: Detector | null = null;\n scope.onmessage = (event) => {\n const message = event.data as ToWorker;\n if (message.v !== WORKER_PROTOCOL_VERSION) {\n // Ignore unknown versions — but never strand a transferred bitmap.\n (message as { bitmap?: { close?: () => void } }).bitmap?.close?.();\n return;\n }\n if (message.type === \"init\") {\n void create(message.options).then((created) => {\n detector = created;\n const ready: ReadyMessage = {\n v: WORKER_PROTOCOL_VERSION,\n type: \"ready\",\n tier: created.tier,\n ...(created.fallbackReason !== undefined\n ? { fallbackReason: created.fallbackReason }\n : {}),\n };\n scope.postMessage(ready);\n });\n return;\n }\n if (message.type === \"detect\") {\n const respond = (observation: FaceObservation | null) => {\n const result: ResultMessage = {\n v: WORKER_PROTOCOL_VERSION,\n type: \"result\",\n id: message.id,\n observation,\n };\n scope.postMessage(result);\n (message.bitmap as { close?: () => void }).close?.();\n };\n if (detector === null) {\n respond(null);\n return;\n }\n void detector\n .detect(message.bitmap, message.streamWidth, message.streamHeight)\n .then(respond, () => respond(null));\n }\n };\n}\n\n// Auto-install when this module IS the worker entry (DedicatedWorkerGlobalScope\n// has importScripts; window does not). Inert in main-thread bundles/tests.\ndeclare const self: (WorkerScopeLike & { importScripts?: unknown }) | undefined;\nif (typeof self !== \"undefined\" && typeof self.importScripts === \"function\") {\n installWorkerDetectorHost(self);\n}\n\n/** Main-side Detector over the worker protocol. Same interface as the\n * in-thread detectors — Task 10's engine cannot tell the difference. */\nexport class WorkerDetectorProxy implements Detector {\n readonly tier: Detector[\"tier\"];\n readonly fallbackReason?: string;\n private readonly worker: WorkerLike;\n private readonly pending = new Map<number, (observation: FaceObservation | null) => void>();\n private nextId = 1;\n\n private constructor(worker: WorkerLike, ready: ReadyMessage) {\n this.worker = worker;\n this.tier = ready.tier;\n if (ready.fallbackReason !== undefined) this.fallbackReason = ready.fallbackReason;\n worker.addEventListener(\"message\", (event) => {\n const message = event.data as ToMain;\n if (message.v !== WORKER_PROTOCOL_VERSION || message.type !== \"result\") return;\n const resolve = this.pending.get(message.id);\n this.pending.delete(message.id);\n resolve?.(message.observation);\n });\n }\n\n static create(\n worker: WorkerLike,\n options: CreateDetectorOptions,\n readyTimeoutMs = 10_000,\n ): Promise<WorkerDetectorProxy> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n worker.removeEventListener(\"message\", onReady);\n worker.terminate();\n reject(new Error(`worker detector not ready within ${readyTimeoutMs}ms`));\n }, readyTimeoutMs);\n const onReady = (event: { data: unknown }) => {\n const message = event.data as ToMain;\n if (message.v !== WORKER_PROTOCOL_VERSION || message.type !== \"ready\") return;\n clearTimeout(timer);\n worker.removeEventListener(\"message\", onReady);\n resolve(new WorkerDetectorProxy(worker, message));\n };\n worker.addEventListener(\"message\", onReady);\n const init: InitMessage = { v: WORKER_PROTOCOL_VERSION, type: \"init\", options };\n worker.postMessage(init);\n });\n }\n\n async detect(\n source: CanvasImageSource,\n sourceWidth: number,\n sourceHeight: number,\n ): Promise<FaceObservation | null> {\n // Only ImageBitmap is Transferable — convert other sources (video,\n // canvas) via createImageBitmap (the feature gate guarantees it). A\n // failed conversion yields null, never a DataCloneError.\n let bitmap: ImageBitmap;\n try {\n if (typeof ImageBitmap !== \"undefined\" && source instanceof ImageBitmap) {\n bitmap = source;\n } else if (typeof createImageBitmap === \"function\") {\n bitmap = await createImageBitmap(source as ImageBitmapSource);\n } else {\n // No conversion surface (stubbed environments) — trust the caller.\n bitmap = source as ImageBitmap;\n }\n } catch {\n return null;\n }\n const id = this.nextId;\n this.nextId += 1;\n return new Promise((resolve) => {\n this.pending.set(id, resolve);\n const message: DetectMessage = {\n v: WORKER_PROTOCOL_VERSION,\n type: \"detect\",\n id,\n bitmap,\n streamWidth: sourceWidth,\n streamHeight: sourceHeight,\n };\n this.worker.postMessage(message, [bitmap]);\n });\n }\n\n destroy(): void {\n // Settle in-flight detects (an awaiting scheduler run must not hang\n // busy=true forever) before tearing the worker down.\n for (const resolve of this.pending.values()) resolve(null);\n this.pending.clear();\n this.worker.terminate();\n }\n}\n","// BlazeFace short-range tier (Task 7) — MediaPipe tasks-vision FaceDetector,\n// the modern default: strong on large/close faces, ~15–40ms on low-end\n// WASM+SIMD. tasks-vision is imported LAZILY (only when this tier is\n// selected) and every asset is SELF-HOSTED under the v3 asset base (hosted\n// CSP is connect-src 'self' — no CDN): the wasm fileset via\n// FilesetResolver.forVisionTasks(base) and the model at\n// `${base}/blaze_face_short_range.tflite` (synced by\n// identity-hosted/scripts/sync-models-v3.mjs, sha256-manifested).\n//\n// DELEGATE = 'CPU' (2026-07-09, capture-v3 hardening — the load-bearing fix):\n// tasks-vision defaults `baseOptions.delegate` to the WebGL/GPU delegate,\n// which HANGS on iOS Safari — every field capture blew the 10s init deadline\n// (`blaze:blaze_init_timeout`) and silently fell to the deprecated face-api\n// floor at ~2.4fps. BlazeFace short-range is tiny (~229 KB) and fast on the\n// WASM+SIMD CPU delegate, so we pin CPU UNCONDITIONALLY: reliability across\n// iOS Safari + Chrome beats the marginal desktop-GPU speedup (and the GPU\n// path is exactly the bug). A GPU-first-with-CPU-fallback dance would only\n// re-introduce the hang risk inside the init budget for no real win.\nimport { type Detector, type FaceObservation, intrinsicSize, toStreamPixels } from \"./detector\";\n\n/** createFromOptions must resolve within this window or the ladder falls\n * through with `blaze_init_timeout` (overridable via CreateDetectorOptions).\n * Raised 5s→10s: on iOS Safari the COLD wasm SIMD compile of tasks-vision can\n * exceed 5s on the FIRST (unwarmed) capture. Since the face-api floor was\n * dropped (2026-07-09), blowing this deadline is now a HARD capture failure\n * (error{kind:'models'} — retryable), not a silent degrade, so the CPU\n * delegate above (which makes init actually complete on iOS) is what keeps\n * this deadline reachable. The hosted warm path\n * (identity-hosted/lib/warm-models.ts) pre-warms this tier in idle time so\n * capture usually reuses a hot instance; this larger deadline only covers the\n * un-warmed fallback (Data-Saver, deep-link straight to liveness). */\nexport const BLAZE_INIT_TIMEOUT_MS = 10_000;\n\ninterface TasksVisionDetection {\n boundingBox?: { originX: number; originY: number; width: number; height: number };\n categories: Array<{ score: number }>;\n /** NORMALIZED to the input image (0..1) — tasks-vision docs; index 0 =\n * right eye, 1 = left eye (SUBJECT perspective). We re-order by image-x\n * (the FaceObservation convention), so the doc order is informative only. */\n keypoints?: Array<{ x: number; y: number }>;\n}\n\ninterface TasksVisionFaceDetector {\n detect(source: unknown): { detections: TasksVisionDetection[] };\n close(): void;\n}\n\nexport class BlazeDetector implements Detector {\n readonly tier = \"blaze\" as const;\n readonly fallbackReason?: string;\n private readonly inner: TasksVisionFaceDetector;\n\n private constructor(inner: TasksVisionFaceDetector, fallbackReason?: string) {\n this.inner = inner;\n if (fallbackReason !== undefined) this.fallbackReason = fallbackReason;\n }\n\n static async create(assetBaseUrl: string, fallbackReason?: string): Promise<BlazeDetector> {\n const vision = await import(\"@mediapipe/tasks-vision\");\n const fileset = await vision.FilesetResolver.forVisionTasks(assetBaseUrl);\n const inner = (await vision.FaceDetector.createFromOptions(fileset, {\n baseOptions: {\n modelAssetPath: `${assetBaseUrl}/blaze_face_short_range.tflite`,\n // CPU delegate — the iOS-Safari-safe path (see header). NEVER 'GPU'.\n delegate: \"CPU\",\n },\n runningMode: \"IMAGE\",\n })) as unknown as TasksVisionFaceDetector;\n return new BlazeDetector(inner, fallbackReason);\n }\n\n async detect(\n source: CanvasImageSource,\n sourceWidth: number,\n sourceHeight: number,\n ): Promise<FaceObservation | null> {\n let detections: TasksVisionDetection[];\n try {\n detections = this.inner.detect(source).detections;\n } catch {\n return null; // never rejects mid-loop\n }\n const best = pickMostConfident(detections);\n if (best?.boundingBox === undefined) return null;\n const size = intrinsicSize(source);\n const eyes = extractEyes(best.keypoints, size);\n const observation: FaceObservation = {\n box: {\n x: best.boundingBox.originX,\n y: best.boundingBox.originY,\n width: best.boundingBox.width,\n height: best.boundingBox.height,\n },\n ...(eyes !== undefined ? { eyes } : {}),\n score: best.categories[0]?.score ?? 0,\n };\n return toStreamPixels(observation, source, sourceWidth, sourceHeight);\n }\n\n destroy(): void {\n try {\n this.inner.close();\n } catch {\n // wasm teardown failures are inert\n }\n }\n}\n\nfunction pickMostConfident(detections: TasksVisionDetection[]): TasksVisionDetection | undefined {\n let best: TasksVisionDetection | undefined;\n let bestScore = -1;\n for (const detection of detections) {\n const score = detection.categories[0]?.score ?? 0;\n if (score > bestScore) {\n best = detection;\n bestScore = score;\n }\n }\n return best;\n}\n\n/** Keypoints 0/1 (the eye pair) — normalized → SOURCE px, ordered by image-x. */\nfunction extractEyes(\n keypoints: Array<{ x: number; y: number }> | undefined,\n size: { width: number; height: number },\n): FaceObservation[\"eyes\"] | undefined {\n const a = keypoints?.[0];\n const b = keypoints?.[1];\n if (a === undefined || b === undefined) return undefined;\n const toSource = (p: { x: number; y: number }) => ({ x: p.x * size.width, y: p.y * size.height });\n const [pa, pb] = [toSource(a), toSource(b)];\n return pa.x <= pb.x ? { left: pa, right: pb } : { left: pb, right: pa };\n}\n","// Native FaceDetector tier (Task 6) — the Shape Detection API: free,\n// hardware-backed where implemented (Chrome/Android, some WebViews). Probed\n// with a 1-frame smoke detect because several browsers EXPOSE the constructor\n// but throw `Face detection service unavailable` at runtime.\nimport { type Detector, type FaceObservation, toStreamPixels } from \"./detector\";\n\ninterface NativeLandmark {\n type: string;\n locations: Array<{ x: number; y: number }>;\n}\n\ninterface NativeDetectedFace {\n boundingBox: { x: number; y: number; width: number; height: number };\n landmarks?: NativeLandmark[];\n}\n\nexport interface NativeFaceDetectorLike {\n detect(source: CanvasImageSource): Promise<NativeDetectedFace[]>;\n}\n\ntype NativeGlobal = typeof globalThis & {\n FaceDetector?: new (options: { fastMode: boolean; maxDetectedFaces: number }) => NativeFaceDetectorLike;\n};\n\nfunction smokeSource(): CanvasImageSource {\n if (typeof OffscreenCanvas === \"function\") return new OffscreenCanvas(2, 2);\n const canvas = document.createElement(\"canvas\");\n canvas.width = 2;\n canvas.height = 2;\n return canvas;\n}\n\n/** Constructor + 1-frame smoke detect (must not throw; result irrelevant). */\nexport async function probeNativeDetector(): Promise<\n { ok: true; instance: NativeFaceDetectorLike } | { ok: false; reason: string }\n> {\n const ctor = (globalThis as NativeGlobal).FaceDetector;\n if (typeof ctor !== \"function\") return { ok: false, reason: \"unsupported\" };\n try {\n const instance = new ctor({ fastMode: true, maxDetectedFaces: 1 });\n await instance.detect(smokeSource());\n return { ok: true, instance };\n } catch (err) {\n return { ok: false, reason: `smoke_failed:${err instanceof Error ? err.message : String(err)}` };\n }\n}\n\nexport class NativeDetector implements Detector {\n readonly tier = \"native\" as const;\n readonly fallbackReason?: string;\n private readonly inner: NativeFaceDetectorLike;\n\n constructor(inner: NativeFaceDetectorLike, fallbackReason?: string) {\n this.inner = inner;\n if (fallbackReason !== undefined) this.fallbackReason = fallbackReason;\n }\n\n async detect(\n source: CanvasImageSource,\n sourceWidth: number,\n sourceHeight: number,\n ): Promise<FaceObservation | null> {\n let faces: NativeDetectedFace[];\n try {\n faces = await this.inner.detect(source);\n } catch {\n return null; // never rejects mid-loop\n }\n const face = faces[0];\n if (face === undefined) return null;\n const eyes = extractEyes(face.landmarks);\n const observation: FaceObservation = {\n box: {\n x: face.boundingBox.x,\n y: face.boundingBox.y,\n width: face.boundingBox.width,\n height: face.boundingBox.height,\n },\n ...(eyes !== undefined ? { eyes } : {}),\n // The Shape Detection API exposes no confidence — the smoke-tested\n // native tier treats any detection as confident.\n score: 1,\n };\n return toStreamPixels(observation, source, sourceWidth, sourceHeight);\n }\n\n destroy(): void {\n // The native detector holds no resources the page must release.\n }\n}\n\n/** Two `eye` landmarks → {left, right} by image-x (see FaceObservation). */\nfunction extractEyes(\n landmarks: NativeLandmark[] | undefined,\n): FaceObservation[\"eyes\"] | undefined {\n if (landmarks === undefined) return undefined;\n const eyePoints = landmarks\n .filter((landmark) => landmark.type === \"eye\")\n .map((landmark) => landmark.locations[0])\n .filter((point): point is { x: number; y: number } => point !== undefined);\n const [a, b] = eyePoints;\n if (a === undefined || b === undefined) return undefined;\n return a.x <= b.x ? { left: a, right: b } : { left: b, right: a };\n}\n","// v3 Detector abstraction (capture program Task 6) — one detection seam with\n// capability-probed tiers. NEW parallel module: nothing in the v1 core\n// imports from v3/*, and the calibration-pinned v1 gate math is untouched.\n//\n// Ladder (DEFAULT_TIER_ORDER): `native` (the browser's FaceDetector — free,\n// hardware-backed where it exists) → `blaze` (MediaPipe BlazeFace short-range,\n// Task 7). The legacy `faceapi` floor was DROPPED (2026-07-09, capture-v3\n// hardening): field telemetry showed BlazeFace never initialized (the GPU\n// delegate hung on iOS → `blaze_init_timeout`) so EVERY capture silently ran\n// the deprecated ~2.4fps face-api floor. With the CPU-delegate blaze fix,\n// face-api is dead weight — and a floor that masks a blaze regression is worse\n// than none, so the ladder is now native→blaze with NO fallback.\n//\n// Contract change: createDetector still never throws while a tier can be\n// built, but with no floor it REJECTS (DetectorLadderError) once the ladder is\n// exhausted — a CLEAN, non-hanging failure carrying the per-tier reasons.\n// CaptureSessionV3.run() maps that rejection to error{kind:'models'}\n// (retryable; the hosted UI renders a retry). A skipped tier still records a\n// `fallbackReason` (exposed on the returned Detector) so a native→blaze\n// fall-through stays observable in telemetry.\nimport { BLAZE_INIT_TIMEOUT_MS, BlazeDetector } from \"./detector-blaze\";\nimport { NativeDetector, probeNativeDetector } from \"./detector-native\";\n\nexport interface FaceObservation {\n /** Stream-pixel box. */\n box: { x: number; y: number; width: number; height: number };\n /** Stream-pixel eye centers when the tier provides them (native:\n * landmarks?, blaze: keypoints). Convention: `left` = smaller x in IMAGE\n * coordinates — gate math only uses the midpoint and |roll|, so the\n * person-left/image-left distinction never matters. */\n eyes?: { left: { x: number; y: number }; right: { x: number; y: number } };\n score: number;\n}\n\nexport interface Detector {\n readonly tier: \"native\" | \"blaze\";\n /** Why higher tiers were skipped (telemetry) — absent when the first\n * preference won. */\n readonly fallbackReason?: string;\n /** Detect on a (possibly downscaled) snapshot; `sourceWidth/Height` are the\n * STREAM dimensions the observation must be normalized to. Returns the\n * single most-confident face or null; NEVER rejects. */\n detect(\n source: CanvasImageSource,\n sourceWidth: number,\n sourceHeight: number,\n ): Promise<FaceObservation | null>;\n destroy(): void;\n}\n\nexport interface CreateDetectorOptions {\n /** v3 self-hosted assets base (tasks-vision wasm + blaze tflite) — hosted\n * serves `/models-v3`, a sha256-manifested directory. */\n v3AssetBaseUrl?: string;\n preferred?: Detector[\"tier\"][];\n /** Test/tuning override for the blaze init deadline. */\n blazeInitTimeoutMs?: number;\n}\n\nexport const DEFAULT_V3_ASSET_BASE_URL = \"/models-v3\";\n\nexport const DEFAULT_TIER_ORDER: Detector[\"tier\"][] = [\"native\", \"blaze\"];\n\n/** Thrown by createDetector when the ENTIRE tier ladder is exhausted. Since the\n * face-api floor was dropped (2026-07-09) there is no last resort, so blaze\n * failing on an already-native-less platform leaves NO detector. Carries the\n * joined per-tier reasons ('native:unsupported; blaze:blaze_init_timeout') as\n * `fallbackReason` for telemetry. Callers MUST map this to a retryable models\n * error — never a hang (CaptureSessionV3.run → error{kind:'models'}). */\nexport class DetectorLadderError extends Error {\n readonly fallbackReason: string;\n constructor(fallbackReason: string) {\n super(`no detector tier available (${fallbackReason})`);\n this.name = \"DetectorLadderError\";\n this.fallbackReason = fallbackReason;\n }\n}\n\n/** Intrinsic pixel size of a snapshot source (video → videoWidth, image →\n * naturalWidth, canvas/bitmap → width). Property-sniffed so worker-side\n * sources (OffscreenCanvas, ImageBitmap) work without instanceof. */\nexport function intrinsicSize(source: CanvasImageSource): { width: number; height: number } {\n const candidate = source as {\n videoWidth?: number;\n videoHeight?: number;\n naturalWidth?: number;\n naturalHeight?: number;\n width?: number | { baseVal?: { value?: number } };\n height?: number | { baseVal?: { value?: number } };\n };\n if (typeof candidate.videoWidth === \"number\" && candidate.videoWidth > 0) {\n return { width: candidate.videoWidth, height: candidate.videoHeight ?? 0 };\n }\n if (typeof candidate.naturalWidth === \"number\" && candidate.naturalWidth > 0) {\n return { width: candidate.naturalWidth, height: candidate.naturalHeight ?? 0 };\n }\n const width = typeof candidate.width === \"number\" ? candidate.width : 0;\n const height = typeof candidate.height === \"number\" ? candidate.height : 0;\n return { width, height };\n}\n\n/** Scale a source-space observation to stream px. Exported for adapters. */\nexport function toStreamPixels(\n observation: FaceObservation,\n source: CanvasImageSource,\n streamWidth: number,\n streamHeight: number,\n): FaceObservation {\n const size = intrinsicSize(source);\n const scaleX = size.width > 0 ? streamWidth / size.width : 1;\n const scaleY = size.height > 0 ? streamHeight / size.height : 1;\n const scalePoint = (p: { x: number; y: number }) => ({ x: p.x * scaleX, y: p.y * scaleY });\n return {\n box: {\n x: observation.box.x * scaleX,\n y: observation.box.y * scaleY,\n width: observation.box.width * scaleX,\n height: observation.box.height * scaleY,\n },\n ...(observation.eyes !== undefined\n ? { eyes: { left: scalePoint(observation.eyes.left), right: scalePoint(observation.eyes.right) } }\n : {}),\n score: observation.score,\n };\n}\n\nfunction describeError(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nexport async function createDetector(options: CreateDetectorOptions): Promise<Detector> {\n const order = options.preferred ?? DEFAULT_TIER_ORDER;\n const reasons: string[] = [];\n for (const tier of order) {\n if (tier === \"native\") {\n const probed = await probeNativeDetector();\n if (probed.ok) {\n return new NativeDetector(probed.instance, reasonOf(reasons));\n }\n reasons.push(`native:${probed.reason}`);\n continue;\n }\n // tier === \"blaze\": smoke test = createFromOptions resolving within the\n // deadline (wasm fetch/compile can hang on hostile networks — never stall\n // the ladder).\n try {\n return await withTimeout(\n BlazeDetector.create(\n options.v3AssetBaseUrl ?? DEFAULT_V3_ASSET_BASE_URL,\n reasonOf(reasons),\n ),\n options.blazeInitTimeoutMs ?? BLAZE_INIT_TIMEOUT_MS,\n );\n } catch (err) {\n reasons.push(\n err instanceof InitTimeoutError\n ? \"blaze:blaze_init_timeout\"\n : `blaze:${describeError(err)}`,\n );\n }\n }\n // Ladder exhausted — the face-api floor is gone, so there is NO fallback.\n // REJECT cleanly with the recorded reasons; CaptureSessionV3.run() maps this\n // to a retryable error{kind:'models'} (a bounded failure the hosted UI\n // retries) rather than hanging. NEVER return a null-yielding detector here.\n throw new DetectorLadderError(reasonOf(reasons) ?? \"no_tier_available\");\n}\n\nfunction reasonOf(reasons: string[]): string | undefined {\n return reasons.length > 0 ? reasons.join(\"; \") : undefined;\n}\n\nclass InitTimeoutError extends Error {}\n\nfunction withTimeout<T extends { destroy(): void }>(\n promise: Promise<T>,\n deadlineMs: number,\n): Promise<T> {\n return new Promise<T>((resolvePromise, rejectPromise) => {\n const timer = setTimeout(() => {\n rejectPromise(new InitTimeoutError(`init exceeded ${deadlineMs}ms`));\n // A late resolution after the deadline is an orphan (wasm/GPU\n // delegate) — release it instead of leaking until navigation.\n promise.then(\n (value) => value.destroy(),\n () => undefined,\n );\n }, deadlineMs);\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolvePromise(value);\n },\n (err) => {\n clearTimeout(timer);\n rejectPromise(err);\n },\n );\n });\n}\n"],"mappings":"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","names":["extractEyes"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { e as WORKER_PROTOCOL_VERSION, f as WorkerDetectorProxy, W as WorkerLike, g as WorkerScopeLike, h as installWorkerDetectorHost, w as workerOffloadSupported } from '../../detect-worker-CKXHlXx9.cjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { e as WORKER_PROTOCOL_VERSION, f as WorkerDetectorProxy, W as WorkerLike, g as WorkerScopeLike, h as installWorkerDetectorHost, w as workerOffloadSupported } from '../../detect-worker-CKXHlXx9.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WORKER_PROTOCOL_VERSION,
|
|
3
|
+
WorkerDetectorProxy,
|
|
4
|
+
installWorkerDetectorHost,
|
|
5
|
+
workerOffloadSupported
|
|
6
|
+
} from "../../chunk-NWF46H7K.js";
|
|
7
|
+
export {
|
|
8
|
+
WORKER_PROTOCOL_VERSION,
|
|
9
|
+
WorkerDetectorProxy,
|
|
10
|
+
installWorkerDetectorHost,
|
|
11
|
+
workerOffloadSupported
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=detect-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|