@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.
Files changed (68) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +20 -0
  3. package/README.md +92 -2
  4. package/dist/capture-constraints-BujkZcfb.d.cts +21 -0
  5. package/dist/capture-constraints-BujkZcfb.d.ts +21 -0
  6. package/dist/capture-session-v3-BJ8ciaWl.d.ts +457 -0
  7. package/dist/capture-session-v3-CAoWM8iT.d.cts +457 -0
  8. package/dist/chunk-BPYVZUJA.js +852 -0
  9. package/dist/chunk-BPYVZUJA.js.map +1 -0
  10. package/dist/chunk-LOTAJDRE.js +1094 -0
  11. package/dist/chunk-LOTAJDRE.js.map +1 -0
  12. package/dist/chunk-MWNFSNMP.js +52 -0
  13. package/dist/chunk-MWNFSNMP.js.map +1 -0
  14. package/dist/chunk-NILNKLK6.js +50 -0
  15. package/dist/chunk-NILNKLK6.js.map +1 -0
  16. package/dist/chunk-NWF46H7K.js +359 -0
  17. package/dist/chunk-NWF46H7K.js.map +1 -0
  18. package/dist/chunk-WY6U7AIK.js +193 -0
  19. package/dist/chunk-WY6U7AIK.js.map +1 -0
  20. package/dist/chunk-YVFF4Q4G.js +327 -0
  21. package/dist/chunk-YVFF4Q4G.js.map +1 -0
  22. package/dist/client/index.cjs +357 -0
  23. package/dist/client/index.cjs.map +1 -0
  24. package/dist/client/index.d.cts +39 -0
  25. package/dist/client/index.d.ts +39 -0
  26. package/dist/client/index.js +15 -0
  27. package/dist/client/index.js.map +1 -0
  28. package/dist/core/index.cjs +1605 -0
  29. package/dist/core/index.cjs.map +1 -0
  30. package/dist/core/index.d.cts +87 -0
  31. package/dist/core/index.d.ts +87 -0
  32. package/dist/core/index.js +139 -0
  33. package/dist/core/index.js.map +1 -0
  34. package/dist/core/v3/detect-worker.cjs +392 -0
  35. package/dist/core/v3/detect-worker.cjs.map +1 -0
  36. package/dist/core/v3/detect-worker.d.cts +1 -0
  37. package/dist/core/v3/detect-worker.d.ts +1 -0
  38. package/dist/core/v3/detect-worker.js +13 -0
  39. package/dist/core/v3/detect-worker.js.map +1 -0
  40. package/dist/create-verification-CjLnJ0w6.d.ts +348 -0
  41. package/dist/create-verification-EnZ13uY4.d.cts +348 -0
  42. package/dist/detect-worker-CKXHlXx9.d.cts +105 -0
  43. package/dist/detect-worker-CKXHlXx9.d.ts +105 -0
  44. package/dist/detector-scanic-4C6IEW2K.js +787 -0
  45. package/dist/detector-scanic-4C6IEW2K.js.map +1 -0
  46. package/dist/hosted-client-BpvBqu9f.d.cts +408 -0
  47. package/dist/hosted-client-BpvBqu9f.d.ts +408 -0
  48. package/dist/index.cjs +2636 -0
  49. package/dist/index.cjs.map +1 -0
  50. package/dist/index.d.cts +6 -0
  51. package/dist/index.d.ts +6 -0
  52. package/dist/index.js +28 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/internal/index.cjs +4166 -0
  55. package/dist/internal/index.cjs.map +1 -0
  56. package/dist/internal/index.d.cts +431 -0
  57. package/dist/internal/index.d.ts +431 -0
  58. package/dist/internal/index.js +647 -0
  59. package/dist/internal/index.js.map +1 -0
  60. package/dist/popup/index.cjs +76 -0
  61. package/dist/popup/index.cjs.map +1 -0
  62. package/dist/popup/index.d.cts +66 -0
  63. package/dist/popup/index.d.ts +66 -0
  64. package/dist/popup/index.js +11 -0
  65. package/dist/popup/index.js.map +1 -0
  66. package/dist/scanic-ml-stub-S2OZ6HEF.js +10 -0
  67. package/dist/scanic-ml-stub-S2OZ6HEF.js.map +1 -0
  68. package/package.json +118 -4
@@ -0,0 +1,348 @@
1
+ import { h as CaptureState$1, f as CaptureSessionV3Events, g as CaptureSessionV3Options, c as CaptureErrorKind$1 } from './capture-session-v3-CAoWM8iT.cjs';
2
+ import { C as CheckReason, c as HostedSessionView, g as HostedClient, A as AttestationInput, h as SessionView, b as CheckType } from './hosted-client-BpvBqu9f.cjs';
3
+
4
+ /** Gate guidance code. `'hold'` is emitted while the clip records. */
5
+ type GateHint = "center" | "closer" | "farther" | "light" | "still" | "hold";
6
+ /**
7
+ * Capture + verification state, as seen by the partner:
8
+ * - `idle → starting → gating → recording → captured` is the engine's run;
9
+ * - `uploading → checking → done` follows ONLY when the handle was obtained via
10
+ * {@link Verification.liveness} (the SDK uploads and submits after `captured`);
11
+ * - `error` on a camera / engine failure (see {@link CaptureError}).
12
+ */
13
+ type CaptureState = "idle" | "starting" | "gating" | "recording" | "captured" | "uploading" | "checking" | "done" | "error";
14
+ /** Declared capture channel — the liveness band is mobile-calibrated. */
15
+ type CaptureChannel = "mobile" | "desktop";
16
+ /** Gate ring update: integrator progress 0..1 and the current guidance code. */
17
+ interface GateUpdate {
18
+ /** Gate integrator 0..1 — drive the ring off this directly. */
19
+ progress: number;
20
+ /** Current guidance code, `null` when no hint applies. */
21
+ hint: GateHint | null;
22
+ /**
23
+ * The detector currently sees a face (a non-zero engine presence factor). `false`
24
+ * with `hint: 'center'` means "no face in frame" rather than "off-center" —
25
+ * hosts use it to tell seeking from aligning, and for the no-face copy.
26
+ */
27
+ faceDetected: boolean;
28
+ }
29
+ /**
30
+ * What the engine captured. Fires BEFORE any upload. `clip` is absent only in
31
+ * the `degraded: 'NO_RECORDER'` case (no MediaRecorder on this browser).
32
+ */
33
+ interface CapturedArtifacts {
34
+ /** Mirrored JPEG keyframe (the face_match probe). */
35
+ keyframe: Blob;
36
+ /** The 2.5 s liveness clip (MP4-first mime ladder). */
37
+ clip?: Blob;
38
+ /** Container mime the clip finalized with — may carry codec params. */
39
+ clipMime?: string;
40
+ /** Intrinsic video width at capture time. */
41
+ width: number;
42
+ /** Intrinsic video height at capture time. */
43
+ height: number;
44
+ /** Recording-start → recording-stop, when known. */
45
+ clipMs?: number;
46
+ /** `'mobile'` on phones/tablets (the calibrated channel), else `'desktop'`. */
47
+ captureChannel: CaptureChannel;
48
+ /** Set when the browser has no MediaRecorder: a keyframe-only capture. */
49
+ degraded?: "NO_RECORDER";
50
+ }
51
+ /** Camera / engine failure kinds — collapsed to the public six. */
52
+ type CaptureErrorKind = "permission" | "notfound" | "inuse" | "insecure" | "unsupported" | "generic";
53
+ /** Camera / engine failure payload (`on('error')`). */
54
+ interface CaptureError {
55
+ kind: CaptureErrorKind;
56
+ /**
57
+ * A fresh attempt (`retry()`) can succeed — false for `insecure` (needs
58
+ * HTTPS) and `unsupported` (the browser cannot capture at all).
59
+ */
60
+ retryable: boolean;
61
+ }
62
+ /** Event map of a {@link LivenessCaptureHandle}. */
63
+ interface LivenessCaptureEvents {
64
+ /** Every state transition. */
65
+ state: CaptureState;
66
+ /** Gate ring updates while gating/recording. */
67
+ gate: GateUpdate;
68
+ /** The capture result — fires BEFORE any upload. */
69
+ captured: CapturedArtifacts;
70
+ /** Camera / engine failure. */
71
+ error: CaptureError;
72
+ /**
73
+ * Clip mode only: the recording window just ended and the keyframe is already
74
+ * available, BEFORE the clip finalizes (iOS can lag ~1.5 s). Hosts freeze the
75
+ * display on `previewDataUrl` (a mirrored JPEG data URL) instead of leaving
76
+ * the live feed running; `captured` follows once the clip lands.
77
+ */
78
+ frozen: {
79
+ previewDataUrl: string;
80
+ };
81
+ /**
82
+ * Device-profile hint, emitted when it changes: `'reduced'` asks the host to
83
+ * drop decorative animations (a LOW device class needs the frames for
84
+ * detection). Chrome only — never gate input.
85
+ */
86
+ profile: {
87
+ animations: "full" | "reduced";
88
+ };
89
+ }
90
+ /** Live capture handle returned by {@link createLivenessCapture} / {@link Verification.liveness}. */
91
+ interface LivenessCaptureHandle {
92
+ /** Current {@link CaptureState}. */
93
+ readonly state: CaptureState;
94
+ /** Subscribe; returns the unsubscribe function. */
95
+ on<K extends keyof LivenessCaptureEvents>(event: K, handler: (payload: LivenessCaptureEvents[K]) => void): () => void;
96
+ /** Ask for the camera and start gating. Resolves once the attempt is running or failed. */
97
+ start(): Promise<void>;
98
+ /** Fresh attempt: resets the engine and restarts the camera. */
99
+ retry(): void;
100
+ /** Toggle user/environment camera (gate restarts from zero). */
101
+ switchCamera(): void;
102
+ /** Stop the camera, release the detector, drop all listeners. */
103
+ destroy(): void;
104
+ }
105
+ /** Options of {@link createLivenessCapture} / {@link Verification.liveness}. */
106
+ interface LivenessCaptureOptions {
107
+ /** Host-owned `<video playsInline muted>` element. */
108
+ video: HTMLVideoElement;
109
+ /** Initial camera; default `'user'`. */
110
+ facing?: "user" | "environment";
111
+ /** Base URL of the detector assets (tasks-vision wasm + tflite); default `/models-v3`. */
112
+ assetBaseUrl?: string;
113
+ }
114
+ /**
115
+ * SDK-side failure reasons (status `'unavailable'`):
116
+ * - `no_recorder` — no MediaRecorder / unsupported clip container;
117
+ * - `attestation` — Turnstile failed client-side or `attestation_invalid` after one retry;
118
+ * - `network` — transport / API failure after the bounded replays;
119
+ * - `nni_unbound` — `population_registry` is the frontier but the session was
120
+ * minted WITHOUT a server-bound NNI (`POST /sessions` `input.nni`), so the
121
+ * SDK cannot submit it. Mint the session with the NNI (or complete the
122
+ * population step yourself) before starting the capture.
123
+ */
124
+ type SdkFailureReason = "no_recorder" | "attestation" | "network" | "nni_unbound";
125
+ /** Backend reason taxonomy ∪ the SDK-side failure reasons. */
126
+ type ReasonCode = CheckReason | SdkFailureReason;
127
+ /**
128
+ * - `approved` / `rejected` / `review` — the session's verdict;
129
+ * - `retry` — a retryable reason with attempts remaining (`autoRetry` on); the
130
+ * capture re-arms by itself;
131
+ * - `unavailable` — v2 could not run this capture (no MediaRecorder,
132
+ * attestation failed, network/API failure). Treat as "not completed".
133
+ */
134
+ type VerificationStatus = "approved" | "rejected" | "review" | "retry" | "unavailable";
135
+ /** The verdict for this session / capture (`on('result')`). */
136
+ interface VerificationResult {
137
+ sessionId: string;
138
+ status: VerificationStatus;
139
+ /** Backend reason, or the SDK-side failure reason for `unavailable`. */
140
+ reason?: ReasonCode;
141
+ /**
142
+ * `retry` (and a `rejected` on a still-parked step): submissions left for
143
+ * the parked check after this one, for the host's coaching copy.
144
+ */
145
+ attemptsRemaining?: number;
146
+ }
147
+ /** Consent locale picklist (mirrors the backend HostedConsentSchema). */
148
+ type VerificationLocale = "ar" | "fr" | "en";
149
+ /** Options of {@link createVerification}. */
150
+ interface VerificationOptions {
151
+ /** The session's client token (from `POST /api/v2/sessions` → `client_token`). */
152
+ clientToken: string;
153
+ /** API origin; default `https://api.sekyro.ai`. */
154
+ apiOrigin?: string;
155
+ /** Consent locale (echoed into the auditable consent record). */
156
+ locale: VerificationLocale;
157
+ /** Re-arm the capture on retryable reasons while attempts remain. Default `true`. */
158
+ autoRetry?: boolean;
159
+ /**
160
+ * How a `retry` verdict re-arms the capture (`autoRetry` on):
161
+ * - `'auto'` (default) — the camera restarts immediately;
162
+ * - `'manual'` — the handle settles at `done`; the host shows its coaching
163
+ * and calls `retry()` on the handle when the user is ready.
164
+ */
165
+ rearm?: "auto" | "manual";
166
+ /** Injection seam for `fetch` (tests / non-window contexts). */
167
+ fetch?: typeof globalThis.fetch;
168
+ }
169
+ /** Event map of a {@link Verification}. */
170
+ interface VerificationEvents {
171
+ /** Verdict per capture (and once when the registry settles the session early). */
172
+ result: VerificationResult;
173
+ /** Every hosted-view update the SDK observes (bootstrap, consent, submits). */
174
+ view: HostedSessionView;
175
+ }
176
+ /** One session's verification, from {@link createVerification}. */
177
+ interface Verification {
178
+ /** Record consent once (no-op when the session already has it). */
179
+ consent(): Promise<void>;
180
+ /**
181
+ * Submit `population_registry` early with the SERVER-bound NNI (empty input)
182
+ * when it is the pending frontier. Retries once on `registry_unavailable`.
183
+ * Never rejects — failures surface through `result`. Idempotent.
184
+ */
185
+ startRegistry(): Promise<void>;
186
+ /** Start a liveness capture bound to this verification; the SDK uploads + submits on `captured`. */
187
+ liveness(options: LivenessCaptureOptions): LivenessCaptureHandle;
188
+ /** Subscribe; returns the unsubscribe function. */
189
+ on<K extends keyof VerificationEvents>(event: K, handler: (payload: VerificationEvents[K]) => void): () => void;
190
+ /** Destroy the active capture and drop all listeners. */
191
+ destroy(): void;
192
+ }
193
+
194
+ /** The engine surface the handle binds — CaptureSessionV3 or a test double. */
195
+ interface CaptureEngine {
196
+ readonly state: CaptureState$1;
197
+ on<K extends keyof CaptureSessionV3Events>(event: K, handler: (payload: CaptureSessionV3Events[K]) => void): () => void;
198
+ start(): Promise<void>;
199
+ retry(): void;
200
+ switchCamera(): void;
201
+ destroy(): void;
202
+ /** Device-profile chrome hint (LOW class → `'reduced'`); absent on doubles. */
203
+ readonly animations?: "full" | "reduced";
204
+ }
205
+ /** Internal construction seams (tests; Sekyro's own hosted page). */
206
+ interface LivenessCaptureInternals {
207
+ /** Replace the engine constructor (default: `new CaptureSessionV3(options)`). */
208
+ createEngine?: (options: CaptureSessionV3Options) => CaptureEngine;
209
+ /** Extra engine options merged in (worker offload, warmed detector, telemetry). */
210
+ engineOptions?: Partial<Pick<CaptureSessionV3Options, "createDetector" | "createWorker" | "telemetry" | "deviceClass">>;
211
+ /** Clock (default `performance.now`). */
212
+ now?: () => number;
213
+ }
214
+ /** fcv3.* guidance key → public gate hint code. */
215
+ declare function mapGuidanceHint(key: string): GateHint | null;
216
+ /** Engine state → public state (`preflight|acquiring → starting`, `positioning → gating`). */
217
+ declare function mapEngineState(state: CaptureState$1): CaptureState;
218
+ /** Engine error kinds → the public six (`models|cliptoolarge|recorder|overconstrained → generic`). */
219
+ declare function mapErrorKind(kind: CaptureErrorKind$1): CaptureErrorKind;
220
+ /** Handle + the hooks the verification layer uses to drive the flow states. */
221
+ interface LivenessCaptureInternalHandle {
222
+ handle: LivenessCaptureHandle;
223
+ /** Push a post-capture state (`uploading | checking | done`). */
224
+ setFlowState(state: Extract<CaptureState, "uploading" | "checking" | "done">): void;
225
+ }
226
+ declare function createLivenessCaptureInternal(options: LivenessCaptureOptions, internals?: LivenessCaptureInternals): LivenessCaptureInternalHandle;
227
+ /**
228
+ * Create a standalone liveness capture (no upload — pair it with your own
229
+ * submission, or use {@link Verification.liveness} for the full flow).
230
+ */
231
+ declare function createLivenessCapture(options: LivenessCaptureOptions): LivenessCaptureHandle;
232
+
233
+ /** The client surface the flow needs — `HostedClient` or a test fake. */
234
+ type HostedClientLike = Pick<HostedClient, "getSession" | "recordConsent" | "presign" | "uploadToSpaces" | "completeMedia" | "submitCheck">;
235
+ /** The presign policy allowlist for `kind: 'clip'` (media.policy.ts mirror). */
236
+ declare const CLIP_MIME_ALLOWLIST: readonly string[];
237
+ /** Same-key transport replays AFTER the first try, and the pause between them. */
238
+ declare const TRANSPORT_REPLAY_ATTEMPTS = 2;
239
+ declare const TRANSPORT_REPLAY_DELAY_MS = 1000;
240
+ /** A Turnstile challenge failed client-side (load / render / error-callback / timeout). */
241
+ declare class AttestationError extends Error {
242
+ constructor(message: string);
243
+ }
244
+ /** One logical submission: receives the client and ITS idempotency key. */
245
+ type SubmitExecutor = (client: HostedClientLike, idempotencyKey: string) => Promise<SessionView>;
246
+ interface SubmitRuntime {
247
+ client: HostedClientLike;
248
+ /** Force-record consent (the server just said it is missing). */
249
+ ensureConsent(opts: {
250
+ force: true;
251
+ }): Promise<void>;
252
+ sleep(ms: number): Promise<void>;
253
+ uuid(): string;
254
+ }
255
+ /**
256
+ * Run one logical submission (ported use-submit semantics):
257
+ * - a fresh `crypto.randomUUID()` key per logical submission;
258
+ * - a transport-class failure replays the SAME executor under the SAME key
259
+ * (bounded) — the backend's IdempotencyInterceptor returns the first
260
+ * response instead of burning another attempt;
261
+ * - ONE `consent_required` recovery: force-record consent and re-run under a
262
+ * FRESH key (the 409 envelope is idempotency-cached under the old one);
263
+ * - ONE attestation retry (client-side Turnstile failure or a server
264
+ * `attestation_invalid`): `onAttestationInvalid` drops the cached token and
265
+ * the executor re-runs under a fresh key.
266
+ * Throws the final error otherwise.
267
+ */
268
+ declare function runSubmission(rt: SubmitRuntime, executor: SubmitExecutor, hooks?: {
269
+ onAttestationInvalid?: () => void;
270
+ }): Promise<SessionView>;
271
+ interface LivenessFlowDeps extends SubmitRuntime {
272
+ artifacts: CapturedArtifacts;
273
+ /** Fresh attestation per call (Turnstile execute, or `{sandbox: true}`). */
274
+ getAttestation(): Promise<AttestationInput>;
275
+ /** Latest hosted view (frontier decisions). */
276
+ getView(): HostedSessionView;
277
+ /** Re-GET the view from the server (after a RESYNC code) and fold it in. */
278
+ refreshView(): Promise<HostedSessionView>;
279
+ /** Fold a submit response into the view. */
280
+ applySubmit(view: SessionView, submitted: CheckType): void;
281
+ /** Flow-state hook for the capture handle. */
282
+ onPhase(phase: "uploading" | "checking"): void;
283
+ /** The verification was destroyed (the only thing that cancels a chain). */
284
+ isCancelled(): boolean;
285
+ }
286
+ type LivenessFlowOutcome = {
287
+ kind: "settled";
288
+ } | {
289
+ kind: "resync";
290
+ } | {
291
+ kind: "unavailable";
292
+ reason: SdkFailureReason;
293
+ } | {
294
+ kind: "cancelled";
295
+ };
296
+ /**
297
+ * Run the biometric submissions the CURRENT frontier allows, re-evaluating
298
+ * after every submit and after every RESYNC re-GET:
299
+ * - frontier `liveness` → clip upload + attestation + `liveness` (once per capture);
300
+ * - frontier `face_match` → keyframe upload + `face_match` (once per capture),
301
+ * allowed when liveness was NOT part of this capture (it had already
302
+ * settled — any state, incl. `failed`: the selfie-fallback case, where this
303
+ * capture only feeds face_match) or settled chainable from this capture
304
+ * (passed / review / processing / skipped — or absent);
305
+ * - anything else (terminal view, a non-biometric frontier, a check this
306
+ * capture already submitted) → `settled` for the caller to map the view.
307
+ * A RESYNC code re-GETs the view and re-enters (at most {@link RESYNC_REENTRY_MAX}
308
+ * times); the final RESYNC surfaces as `resync` for the caller to map.
309
+ */
310
+ declare function runLivenessFlow(deps: LivenessFlowDeps): Promise<LivenessFlowOutcome>;
311
+
312
+ /** Production API origin — the `apiOrigin` default. */
313
+ declare const DEFAULT_API_ORIGIN = "https://api.sekyro.ai";
314
+ /** Pause before the single `registry_unavailable` re-submit. */
315
+ declare const REGISTRY_RETRY_DELAY_MS = 5000;
316
+ /** Bounded poll while a biometric verdict is still `processing` after the submits. */
317
+ declare const PROCESSING_POLL_DELAY_MS = 1500;
318
+ declare const PROCESSING_POLL_MAX = 10;
319
+ /**
320
+ * Construction seams — tests and Sekyro's own hosted page (`@sekyro/web/internal`).
321
+ * Not part of the partner contract.
322
+ */
323
+ interface VerificationInternals {
324
+ /** Replace the HTTP client (default: `new HostedClient({...})`). */
325
+ client?: HostedClientLike;
326
+ /**
327
+ * Replace the Turnstile challenge (default: {@link executeTurnstile}). The
328
+ * `signal` aborts when the verification is destroyed (M1).
329
+ */
330
+ getTurnstileToken?: (siteKey: string, opts: {
331
+ signal: AbortSignal;
332
+ }) => Promise<string>;
333
+ /** Capture-engine seams forwarded to {@link createLivenessCaptureInternal}. */
334
+ capture?: LivenessCaptureInternals;
335
+ sleep?: (ms: number) => Promise<void>;
336
+ uuid?: () => string;
337
+ }
338
+ /** `crypto.randomUUID()` with a v4 fallback for hosts that lack it. */
339
+ declare function randomUuid(): string;
340
+ /**
341
+ * Create a verification bound to one session (spec §3.1):
342
+ * `consent()` → `startRegistry()` → `liveness({video})` → `on('result')`.
343
+ */
344
+ declare function createVerification(options: VerificationOptions): Verification;
345
+ /** {@link createVerification} with construction seams (tests / Sekyro-internal hosts). */
346
+ declare function createVerificationInternal(options: VerificationOptions, internals: VerificationInternals): Verification;
347
+
348
+ export { AttestationError as A, createLivenessCaptureInternal as B, type CaptureChannel as C, DEFAULT_API_ORIGIN as D, createVerificationInternal as E, mapEngineState as F, type GateHint as G, type HostedClientLike as H, mapErrorKind as I, mapGuidanceHint as J, randomUuid as K, type LivenessCaptureEvents as L, runLivenessFlow as M, runSubmission as N, PROCESSING_POLL_DELAY_MS as P, type ReasonCode as R, type SdkFailureReason as S, TRANSPORT_REPLAY_ATTEMPTS as T, type Verification as V, type CaptureError as a, type CaptureErrorKind as b, type CaptureState as c, type CapturedArtifacts as d, type GateUpdate as e, type LivenessCaptureHandle as f, type LivenessCaptureOptions as g, type VerificationEvents as h, type VerificationLocale as i, type VerificationOptions as j, type VerificationResult as k, type VerificationStatus as l, createLivenessCapture as m, createVerification as n, CLIP_MIME_ALLOWLIST as o, type CaptureEngine as p, type LivenessCaptureInternalHandle as q, type LivenessCaptureInternals as r, type LivenessFlowDeps as s, type LivenessFlowOutcome as t, PROCESSING_POLL_MAX as u, REGISTRY_RETRY_DELAY_MS as v, type SubmitExecutor as w, type SubmitRuntime as x, TRANSPORT_REPLAY_DELAY_MS as y, type VerificationInternals as z };
@@ -0,0 +1,105 @@
1
+ interface FaceObservation {
2
+ /** Stream-pixel box. */
3
+ box: {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ };
9
+ /** Stream-pixel eye centers when the tier provides them (native:
10
+ * landmarks?, blaze: keypoints). Convention: `left` = smaller x in IMAGE
11
+ * coordinates — gate math only uses the midpoint and |roll|, so the
12
+ * person-left/image-left distinction never matters. */
13
+ eyes?: {
14
+ left: {
15
+ x: number;
16
+ y: number;
17
+ };
18
+ right: {
19
+ x: number;
20
+ y: number;
21
+ };
22
+ };
23
+ score: number;
24
+ }
25
+ interface Detector {
26
+ readonly tier: "native" | "blaze";
27
+ /** Why higher tiers were skipped (telemetry) — absent when the first
28
+ * preference won. */
29
+ readonly fallbackReason?: string;
30
+ /** Detect on a (possibly downscaled) snapshot; `sourceWidth/Height` are the
31
+ * STREAM dimensions the observation must be normalized to. Returns the
32
+ * single most-confident face or null; NEVER rejects. */
33
+ detect(source: CanvasImageSource, sourceWidth: number, sourceHeight: number): Promise<FaceObservation | null>;
34
+ destroy(): void;
35
+ }
36
+ interface CreateDetectorOptions {
37
+ /** v3 self-hosted assets base (tasks-vision wasm + blaze tflite) — hosted
38
+ * serves `/models-v3`, a sha256-manifested directory. */
39
+ v3AssetBaseUrl?: string;
40
+ preferred?: Detector["tier"][];
41
+ /** Test/tuning override for the blaze init deadline. */
42
+ blazeInitTimeoutMs?: number;
43
+ }
44
+ declare const DEFAULT_V3_ASSET_BASE_URL = "/models-v3";
45
+ declare const DEFAULT_TIER_ORDER: Detector["tier"][];
46
+ /** Thrown by createDetector when the ENTIRE tier ladder is exhausted. Since the
47
+ * face-api floor was dropped (2026-07-09) there is no last resort, so blaze
48
+ * failing on an already-native-less platform leaves NO detector. Carries the
49
+ * joined per-tier reasons ('native:unsupported; blaze:blaze_init_timeout') as
50
+ * `fallbackReason` for telemetry. Callers MUST map this to a retryable models
51
+ * error — never a hang (CaptureSessionV3.run → error{kind:'models'}). */
52
+ declare class DetectorLadderError extends Error {
53
+ readonly fallbackReason: string;
54
+ constructor(fallbackReason: string);
55
+ }
56
+ /** Intrinsic pixel size of a snapshot source (video → videoWidth, image →
57
+ * naturalWidth, canvas/bitmap → width). Property-sniffed so worker-side
58
+ * sources (OffscreenCanvas, ImageBitmap) work without instanceof. */
59
+ declare function intrinsicSize(source: CanvasImageSource): {
60
+ width: number;
61
+ height: number;
62
+ };
63
+ /** Scale a source-space observation to stream px. Exported for adapters. */
64
+ declare function toStreamPixels(observation: FaceObservation, source: CanvasImageSource, streamWidth: number, streamHeight: number): FaceObservation;
65
+ declare function createDetector(options: CreateDetectorOptions): Promise<Detector>;
66
+
67
+ declare const WORKER_PROTOCOL_VERSION = 1;
68
+ /** The slice of Worker/scope surface the protocol needs — test-stubbable. */
69
+ interface WorkerScopeLike {
70
+ onmessage: ((event: {
71
+ data: unknown;
72
+ }) => void) | null;
73
+ postMessage(data: unknown): void;
74
+ }
75
+ interface WorkerLike {
76
+ postMessage(data: unknown, transfer?: Transferable[]): void;
77
+ addEventListener(type: "message", listener: (event: {
78
+ data: unknown;
79
+ }) => void): void;
80
+ removeEventListener(type: "message", listener: (event: {
81
+ data: unknown;
82
+ }) => void): void;
83
+ terminate(): void;
84
+ }
85
+ /** Feature gate: bitmap capture + transfer + workers must all exist. */
86
+ declare function workerOffloadSupported(): boolean;
87
+ /** Worker-side message host. `deps.createDetector` is a test seam. */
88
+ declare function installWorkerDetectorHost(scope: WorkerScopeLike, deps?: {
89
+ createDetector?: (options: CreateDetectorOptions) => Promise<Detector>;
90
+ }): void;
91
+ /** Main-side Detector over the worker protocol. Same interface as the
92
+ * in-thread detectors — Task 10's engine cannot tell the difference. */
93
+ declare class WorkerDetectorProxy implements Detector {
94
+ readonly tier: Detector["tier"];
95
+ readonly fallbackReason?: string;
96
+ private readonly worker;
97
+ private readonly pending;
98
+ private nextId;
99
+ private constructor();
100
+ static create(worker: WorkerLike, options: CreateDetectorOptions, readyTimeoutMs?: number): Promise<WorkerDetectorProxy>;
101
+ detect(source: CanvasImageSource, sourceWidth: number, sourceHeight: number): Promise<FaceObservation | null>;
102
+ destroy(): void;
103
+ }
104
+
105
+ export { type CreateDetectorOptions as C, DEFAULT_TIER_ORDER as D, type FaceObservation as F, type WorkerLike as W, DEFAULT_V3_ASSET_BASE_URL as a, type Detector as b, DetectorLadderError as c, createDetector as d, WORKER_PROTOCOL_VERSION as e, WorkerDetectorProxy as f, type WorkerScopeLike as g, installWorkerDetectorHost as h, intrinsicSize as i, toStreamPixels as t, workerOffloadSupported as w };
@@ -0,0 +1,105 @@
1
+ interface FaceObservation {
2
+ /** Stream-pixel box. */
3
+ box: {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ };
9
+ /** Stream-pixel eye centers when the tier provides them (native:
10
+ * landmarks?, blaze: keypoints). Convention: `left` = smaller x in IMAGE
11
+ * coordinates — gate math only uses the midpoint and |roll|, so the
12
+ * person-left/image-left distinction never matters. */
13
+ eyes?: {
14
+ left: {
15
+ x: number;
16
+ y: number;
17
+ };
18
+ right: {
19
+ x: number;
20
+ y: number;
21
+ };
22
+ };
23
+ score: number;
24
+ }
25
+ interface Detector {
26
+ readonly tier: "native" | "blaze";
27
+ /** Why higher tiers were skipped (telemetry) — absent when the first
28
+ * preference won. */
29
+ readonly fallbackReason?: string;
30
+ /** Detect on a (possibly downscaled) snapshot; `sourceWidth/Height` are the
31
+ * STREAM dimensions the observation must be normalized to. Returns the
32
+ * single most-confident face or null; NEVER rejects. */
33
+ detect(source: CanvasImageSource, sourceWidth: number, sourceHeight: number): Promise<FaceObservation | null>;
34
+ destroy(): void;
35
+ }
36
+ interface CreateDetectorOptions {
37
+ /** v3 self-hosted assets base (tasks-vision wasm + blaze tflite) — hosted
38
+ * serves `/models-v3`, a sha256-manifested directory. */
39
+ v3AssetBaseUrl?: string;
40
+ preferred?: Detector["tier"][];
41
+ /** Test/tuning override for the blaze init deadline. */
42
+ blazeInitTimeoutMs?: number;
43
+ }
44
+ declare const DEFAULT_V3_ASSET_BASE_URL = "/models-v3";
45
+ declare const DEFAULT_TIER_ORDER: Detector["tier"][];
46
+ /** Thrown by createDetector when the ENTIRE tier ladder is exhausted. Since the
47
+ * face-api floor was dropped (2026-07-09) there is no last resort, so blaze
48
+ * failing on an already-native-less platform leaves NO detector. Carries the
49
+ * joined per-tier reasons ('native:unsupported; blaze:blaze_init_timeout') as
50
+ * `fallbackReason` for telemetry. Callers MUST map this to a retryable models
51
+ * error — never a hang (CaptureSessionV3.run → error{kind:'models'}). */
52
+ declare class DetectorLadderError extends Error {
53
+ readonly fallbackReason: string;
54
+ constructor(fallbackReason: string);
55
+ }
56
+ /** Intrinsic pixel size of a snapshot source (video → videoWidth, image →
57
+ * naturalWidth, canvas/bitmap → width). Property-sniffed so worker-side
58
+ * sources (OffscreenCanvas, ImageBitmap) work without instanceof. */
59
+ declare function intrinsicSize(source: CanvasImageSource): {
60
+ width: number;
61
+ height: number;
62
+ };
63
+ /** Scale a source-space observation to stream px. Exported for adapters. */
64
+ declare function toStreamPixels(observation: FaceObservation, source: CanvasImageSource, streamWidth: number, streamHeight: number): FaceObservation;
65
+ declare function createDetector(options: CreateDetectorOptions): Promise<Detector>;
66
+
67
+ declare const WORKER_PROTOCOL_VERSION = 1;
68
+ /** The slice of Worker/scope surface the protocol needs — test-stubbable. */
69
+ interface WorkerScopeLike {
70
+ onmessage: ((event: {
71
+ data: unknown;
72
+ }) => void) | null;
73
+ postMessage(data: unknown): void;
74
+ }
75
+ interface WorkerLike {
76
+ postMessage(data: unknown, transfer?: Transferable[]): void;
77
+ addEventListener(type: "message", listener: (event: {
78
+ data: unknown;
79
+ }) => void): void;
80
+ removeEventListener(type: "message", listener: (event: {
81
+ data: unknown;
82
+ }) => void): void;
83
+ terminate(): void;
84
+ }
85
+ /** Feature gate: bitmap capture + transfer + workers must all exist. */
86
+ declare function workerOffloadSupported(): boolean;
87
+ /** Worker-side message host. `deps.createDetector` is a test seam. */
88
+ declare function installWorkerDetectorHost(scope: WorkerScopeLike, deps?: {
89
+ createDetector?: (options: CreateDetectorOptions) => Promise<Detector>;
90
+ }): void;
91
+ /** Main-side Detector over the worker protocol. Same interface as the
92
+ * in-thread detectors — Task 10's engine cannot tell the difference. */
93
+ declare class WorkerDetectorProxy implements Detector {
94
+ readonly tier: Detector["tier"];
95
+ readonly fallbackReason?: string;
96
+ private readonly worker;
97
+ private readonly pending;
98
+ private nextId;
99
+ private constructor();
100
+ static create(worker: WorkerLike, options: CreateDetectorOptions, readyTimeoutMs?: number): Promise<WorkerDetectorProxy>;
101
+ detect(source: CanvasImageSource, sourceWidth: number, sourceHeight: number): Promise<FaceObservation | null>;
102
+ destroy(): void;
103
+ }
104
+
105
+ export { type CreateDetectorOptions as C, DEFAULT_TIER_ORDER as D, type FaceObservation as F, type WorkerLike as W, DEFAULT_V3_ASSET_BASE_URL as a, type Detector as b, DetectorLadderError as c, createDetector as d, WORKER_PROTOCOL_VERSION as e, WorkerDetectorProxy as f, type WorkerScopeLike as g, installWorkerDetectorHost as h, intrinsicSize as i, toStreamPixels as t, workerOffloadSupported as w };