@sekyro/web 0.1.0-rc.6 → 0.1.0-rc.8

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 (118) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/LICENSE +25 -16
  3. package/README.md +6 -8
  4. package/THIRD-PARTY-NOTICES.txt +154 -0
  5. package/dist/background-monitor-A7FKJOAG.js +1 -0
  6. package/dist/capture-session-doc-YBE3UMJJ.js +1 -0
  7. package/dist/{capture-session-v3-Okq0EQHG.d.ts → capture-session-v3-CQK-G-qX.d.ts} +72 -3
  8. package/dist/{capture-session-v3-UQU_WjO2.d.cts → capture-session-v3-Dvob-uH2.d.cts} +72 -3
  9. package/dist/chunk-4RS2YIL4.js +1 -0
  10. package/dist/chunk-7V2M6Y2W.js +1 -0
  11. package/dist/chunk-7ZCZY3ZP.js +1 -0
  12. package/dist/chunk-A3FUGHAH.js +2 -0
  13. package/dist/chunk-BPE5NSQ2.js +1 -0
  14. package/dist/chunk-CJHMFPL6.js +1 -0
  15. package/dist/chunk-DFTCAWMP.js +1 -0
  16. package/dist/chunk-DK5YPBDQ.js +1 -0
  17. package/dist/chunk-DQWW4OCK.js +1 -0
  18. package/dist/chunk-FHJ7HO3M.js +1 -0
  19. package/dist/chunk-FPXL63KZ.js +1 -0
  20. package/dist/chunk-G6LKLN4U.js +1 -0
  21. package/dist/chunk-GI3UFGRQ.js +1 -0
  22. package/dist/chunk-LQ7C44JG.js +1 -0
  23. package/dist/chunk-M43ET4NJ.js +1 -0
  24. package/dist/chunk-MXWLCDFX.js +1 -0
  25. package/dist/chunk-MY6EXEL2.js +1 -0
  26. package/dist/chunk-O5WKNZ2C.js +1 -0
  27. package/dist/chunk-OKWG3PVH.js +1 -0
  28. package/dist/chunk-POIQGEZM.js +1 -0
  29. package/dist/chunk-RI3E7GBN.js +1 -0
  30. package/dist/chunk-RNRFHUJA.js +1 -0
  31. package/dist/chunk-RVHAKRAI.js +1 -0
  32. package/dist/chunk-SMKCBUJL.js +1 -0
  33. package/dist/chunk-SXZCL5NO.js +1 -0
  34. package/dist/chunk-TDLIXFDT.js +1 -0
  35. package/dist/chunk-XLN5OT25.js +1 -0
  36. package/dist/chunk-YDQBVWXG.js +1 -0
  37. package/dist/chunk-YPG7CB4O.js +1 -0
  38. package/dist/client/index.cjs +1 -357
  39. package/dist/client/index.d.cts +13 -3
  40. package/dist/client/index.d.ts +13 -3
  41. package/dist/client/index.js +1 -15
  42. package/dist/core/index.cjs +1 -1639
  43. package/dist/core/index.d.cts +3 -3
  44. package/dist/core/index.d.ts +3 -3
  45. package/dist/core/index.js +1 -139
  46. package/dist/core/ocr/worker.bundle.js +42 -0
  47. package/dist/core/ocr/worker.cjs +42 -0
  48. package/dist/core/ocr/worker.d.cts +84 -0
  49. package/dist/core/ocr/worker.d.ts +84 -0
  50. package/dist/core/ocr/worker.js +41 -0
  51. package/dist/core/v3/detect-worker.cjs +1 -392
  52. package/dist/core/v3/detect-worker.d.cts +1 -1
  53. package/dist/core/v3/detect-worker.d.ts +1 -1
  54. package/dist/core/v3/detect-worker.js +1 -13
  55. package/dist/create-document-capture-T4APHJMY.js +1 -0
  56. package/dist/create-portrait-capture-B57GE5FU.js +1 -0
  57. package/dist/create-verification-C0AVIcVW.d.ts +2787 -0
  58. package/dist/create-verification-DoFpgzok.d.cts +2787 -0
  59. package/dist/{detect-worker-CKXHlXx9.d.cts → detect-worker-DclNxkZT.d.cts} +6 -0
  60. package/dist/{detect-worker-CKXHlXx9.d.ts → detect-worker-DclNxkZT.d.ts} +6 -0
  61. package/dist/detector-scanic-YCDNLCGV.js +1 -0
  62. package/dist/icao-gate-D8qm1O2t.d.cts +1655 -0
  63. package/dist/icao-gate-D8qm1O2t.d.ts +1655 -0
  64. package/dist/icao-gate-PWBMKRGC.js +1 -0
  65. package/dist/icao-tuning-4GGVX6B4.js +1 -0
  66. package/dist/index.cjs +1 -3030
  67. package/dist/index.d.cts +52 -6
  68. package/dist/index.d.ts +52 -6
  69. package/dist/index.js +1 -85
  70. package/dist/internal/index.cjs +1 -4439
  71. package/dist/internal/index.d.cts +352 -312
  72. package/dist/internal/index.d.ts +352 -312
  73. package/dist/internal/index.js +1 -657
  74. package/dist/keyframe-rank-YAJLYJYF.js +1 -0
  75. package/dist/landmarker-KB7LVXAU.js +1 -0
  76. package/dist/mrz-band-VYHOTDHY.js +1 -0
  77. package/dist/plan-runner-UBK4B4CG.js +1 -0
  78. package/dist/popup/index.cjs +1 -76
  79. package/dist/popup/index.js +1 -11
  80. package/dist/scanic-ml-stub-KW7IZHUT.js +1 -0
  81. package/dist/segmenter-USLNBX65.js +1 -0
  82. package/dist/turnstile-FATNZ4AG.js +1 -0
  83. package/package.json +25 -10
  84. package/sbom.cdx.json +195 -0
  85. package/dist/chunk-MWNFSNMP.js +0 -52
  86. package/dist/chunk-MWNFSNMP.js.map +0 -1
  87. package/dist/chunk-NILNKLK6.js +0 -50
  88. package/dist/chunk-NILNKLK6.js.map +0 -1
  89. package/dist/chunk-NWF46H7K.js +0 -359
  90. package/dist/chunk-NWF46H7K.js.map +0 -1
  91. package/dist/chunk-R3SJQHYU.js +0 -1173
  92. package/dist/chunk-R3SJQHYU.js.map +0 -1
  93. package/dist/chunk-TG5IE552.js +0 -1128
  94. package/dist/chunk-TG5IE552.js.map +0 -1
  95. package/dist/chunk-WY6U7AIK.js +0 -193
  96. package/dist/chunk-WY6U7AIK.js.map +0 -1
  97. package/dist/chunk-YVFF4Q4G.js +0 -327
  98. package/dist/chunk-YVFF4Q4G.js.map +0 -1
  99. package/dist/client/index.cjs.map +0 -1
  100. package/dist/client/index.js.map +0 -1
  101. package/dist/core/index.cjs.map +0 -1
  102. package/dist/core/index.js.map +0 -1
  103. package/dist/core/v3/detect-worker.cjs.map +0 -1
  104. package/dist/core/v3/detect-worker.js.map +0 -1
  105. package/dist/create-verification-BKc9NFQj.d.cts +0 -455
  106. package/dist/create-verification-DjcMZHAQ.d.ts +0 -455
  107. package/dist/detector-scanic-4C6IEW2K.js +0 -787
  108. package/dist/detector-scanic-4C6IEW2K.js.map +0 -1
  109. package/dist/hosted-client-DJXq_CB9.d.cts +0 -449
  110. package/dist/hosted-client-DJXq_CB9.d.ts +0 -449
  111. package/dist/index.cjs.map +0 -1
  112. package/dist/index.js.map +0 -1
  113. package/dist/internal/index.cjs.map +0 -1
  114. package/dist/internal/index.js.map +0 -1
  115. package/dist/popup/index.cjs.map +0 -1
  116. package/dist/popup/index.js.map +0 -1
  117. package/dist/scanic-ml-stub-S2OZ6HEF.js +0 -10
  118. package/dist/scanic-ml-stub-S2OZ6HEF.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,119 @@
1
1
  # @sekyro/web
2
2
 
3
+ ## 0.1.0-rc.8
4
+
5
+ ### Minor Changes
6
+
7
+ - 6571361: Flow model 2026-10-23: `KNOWN_CODES` gains `contact_locked` (the phone-or-email contact choice can no longer change), `identity_confirm_required` (confirm the identity summary before the live photo) and `identity_confirm_locked` (no summary to confirm, or no retake left). All three are 409s from the new hosted routes (`POST /hosted/contact`, `POST /hosted/identity/confirm`) and the submit flow. Nothing changes at runtime until a profile turns on `contact.mode: 'one_of'` or `identity.confirmSummary` (the latter also behind the platform's `IDENTITY_SUMMARY_ENABLED`).
8
+
9
+ **Migration (TypeScript):** `KnownCode` gained three members; code that switches over it exhaustively must handle them.
10
+ - ff0dcc7: Flow model 2026-10-23 (web lane):
11
+
12
+ - Every step snapshot / handle carries `section` (`contact` / `identity` / `live_photo`, an unknown value is kept) and `context.sections` lists the session's sections in order.
13
+ - New steps: `contact_choice` (a `contact.mode: one_of` session: `select("phone" | "email")`; the OTP handles gain `canSwitch` / `switchChannel` / `switchTo()`) and `identity_summary` (the masked identity data to confirm before the live photo — `summaryKind` `full` / `partial`, `birthYear` on a partial one; `portrait()`, `confirm()`, `retake()`; a partial summary without a masked NNI shows the last 4 of the NNI typed on this page); it outranks the live-photo frontier.
14
+ - `eid.afterPass` / `nationalEid.afterPass` (`continue`: a live photo follows the national eID).
15
+ - Implicit consent: the runner records the consent right before the person's first action (method, contact choice and summary answers included); a `consent_required` reply records it and retries once. `contact_locked` / `identity_confirm_*` re-read the session.
16
+ - Step 3 one-capture live photo: `liveness.instructions` (`icao` on an ID-document session), `liveness.livePhoto` and `onLivePhoto()`. On an ICAO session the liveness gate also waits for the ICAO conditions (Face Landmarker, same gate as the portrait step) and, when it fires, a full-resolution unmirrored still (long side ≥ `capture.livePhoto.still.minLongSidePx`, JPEG `jpegQuality`) is sent with the liveness submission as `input.portraitIcao`; a still below the minimum is never sent. `portrait.retakeOnly` marks a portrait-only retake; when the server could not run the ICAO check the runner submits the same still directly. Old backends keep the sequential path.
17
+ - ICAO background-uniformity check (ID mode only, portrait step and one-capture live photo): the MediaPipe selfie segmenter (`selfie_segmenter.tflite`, lazy, self-hosted under `assetBaseUrl`) removes the person a few times per second; colour spread, edge density and brightness gradient around the head and shoulders hold the shot with the "plain, light wall" coaching for at most `timeoutMs` (then the shot proceeds; the server decides). Strip fallback beside the face when the segmenter is missing or slow. Thresholds: `capture.icao.backgroundCheck` (defaults in `BACKGROUND_CHECK_DEFAULTS`); the numbers ride `portrait_icao` `gate.backgroundCheck`.
18
+ - `HostedClient.selectContact(channel)`, `confirmIdentity(decision)`, `identityPortrait()` (session-token header, returns a `Blob`); view types `otpChoice`, `identitySummary`, `capture.livePhoto`, `docCapture`, `LivenessInput.portraitIcao`.
19
+ - The Turnstile loader now loads on first use (its own chunk, −459 B gzip on the liveness-only bundle), which pays for the new client calls: that bundle stays at ~18.6 KB (budget unchanged).
20
+
21
+ **Migration (TypeScript):** `VerificationStep` / `StepSnapshot` gained two members (`contact_choice`, `identity_summary`); exhaustive switches must handle them. Hand-built handles (test fakes) need the new required fields (`section`, `context.sections`, `canSwitch`, `switchTo`, `afterPass`, `instructions`, `livePhoto`, `onLivePhoto`, `retakeOnly`).
22
+ - 46905f2: National eID sign-in (SDK spec §3.2; Houwiyeti in Mauritania) in the headless web runner: the `eid_auth` check type, the `national_eid` identity method with `methodChoice.nationalEid`, the `HostedStepView.eid` view and the `eid_*` reasons, and an `eid` plan-runner step whose handle offers `start(identifier?)`, `verify(code)`, `resend()` and `useCamera()`. `@sekyro/react` has no eID screen yet: its method screen leaves `national_eid` out and `<SekyroVerification>` shows the "unsupported" notice when the eID is the only path.
23
+
24
+ **Migration (TypeScript):** these exported unions gained members — `CheckType` (`eid_auth`), `IdentityMethod` (`national_eid`), `CheckReason` (the `eid_*` reasons), `VerificationStep` (a new `kind: "eid"` step). Code that switches over them exhaustively must add an `eid` branch. An invalid eID number rejects with the existing `InvalidInputError("nni", …)`. Nothing changes at runtime until a profile turns the national eID on.
25
+ - 7a3f935: Phone OTP channel choice and the email OTP step.
26
+
27
+ - `@sekyro/web`: the phone OTP step lists the channels the person can choose from (`PhoneOtpSnapshot.channels`, WhatsApp first; `[]` on a session created before channel choice). With more than one, `send({ channel })` needs the person's choice — it is never picked for them — and a channel the step doesn't offer is refused locally with the new `OtpInputError` (`channel`, `required` / `unavailable`). `resendAvailableAt` now follows the server's resend time when the session reports one. New `email_otp` step (`EmailOtpStepHandle`: `send({ email })`, `verify(code)`; a malformed address rejects with `OtpInputError("email", "format")`), shown before the identity-method choice like the phone step. New types: `HostedEmailOtpView`, `EmailOtpInput`, `EmailOtpReason`, the `email_otp` check type and the reasons `otp_channel_unavailable`, `otp_destination_not_allowed`, `otp_destination_unknown`, `otp_rate_limited`, `email_invalid`, `email_disposable`.
28
+ - `@sekyro/react`: `PhoneOtpStep` shows one "Send code by …" button per offered channel (WhatsApp first) and a "Resend by …" button per channel after the server's resend time; sessions without channels keep the single "Send code" button. New `EmailOtpStep` (rendered by `SekyroVerification` for the `email_otp` step); its view and ar/fr/en copy load on demand, so apps without an email check don't download them. New copy keys `otp.chooseChannel`, `otp.sendBy`, `otp.resendBy`, the new `otp.reason_*` keys and `emailOtp.*` (overridable through `strings`).
29
+ - `@sekyro/node`: profile `phone.channels` / `phone.destinations`, `email` (`ProfileEmail`: `emailSource`, `disposable`), `attempts.email`, the `input.email` session anchor and the `email_otp` check type.
30
+ - c9edead: `<SekyroVerification>` takes the Sekyro identity the Android and iOS SDKs share: the partner's logo (or initials) and name
31
+ over "identity verification", the section progress (flow model v3) drawn as thin bars, the Sekyro brand as the default
32
+ theme (partner branding and the runtime theme still win), six-box one-time code fields, document cards with an icon
33
+ and a subtitle, the camera stages in the Sekyro navy, and a "Verified by Sekyro AI" footer (`slots.footer` replaces it,
34
+ `footer: null` hides it). New `colorScheme` prop: `light` (default), `dark` or `system`; in dark a partner's brand is
35
+ kept only while it stands out from the dark canvas.
36
+ The 0.1 surface (`DEFAULT_THEME`, `styles.css`, `<LivenessCapture>` and the other 0.1 components) is unchanged.
37
+
38
+ **Visible to existing `<SekyroVerification>` integrations.** Without a `theme` prop or session branding the flow now
39
+ draws in the Sekyro brand (it used `DEFAULT_THEME` before) and ends with a "Verified by Sekyro AI" line. To keep your
40
+ current look, pass your colours (`theme={{ colors: … }}`, or set them as the app's branding) and `slots={{ footer: null }}`.
41
+ `verification.css` now loads the SDK's fonts (`./fonts/*.woff2`): your bundler must emit them as assets (esbuild needs
42
+ `loader: { ".woff2": "file" }`; Vite, webpack 5 and Next.js do it by default) and your CSP `font-src` must allow them.
43
+ - 4e6bfb8: `createDocumentCapture({ video, documentType })` is now public: the guided rear-camera scanner for identity cards (ID-1 or ID-2), residence permits and passport data pages of any issuing country. Cards can be shown either face first; after the first photo the `scan` event asks to turn the card over and the other face is taken once the flip is seen (same flow as the Sekyro native SDKs). A blurred photo is refused before upload and retaken at once (`scan.notice: "blurry"`). Coaching (`feedback`), the gate ring (`gate`), a manual shutter fallback and the torch toggle are included; `captured` delivers an upload-ready JPEG per face. Live camera only. Its engine loads on first use, so the main bundle does not grow.
44
+ - 69638fa: The plan runner now drives the document step for any issuing country (`selectDocument(issuer, type)` then `capture({ video })`: the SDK uploads each side and follows recto → verso) and the liveness step (`capture({ video })`), and ends on a typed `result` step for every outcome: approved, review, rejected, expired, policy stops, device cooldown (with `retryAfter`), "needs a phone app" (`platform_unsupported`) and no attempts left. After a wrong national number (`nni_recheck`) the registry step comes back with `recheck: true`, and the face match is resubmitted with the photo already taken.
45
+ - fce1604: Web live MRZ (on-device, server-controlled, additive):
46
+
47
+ - `@sekyro/web`: `DocumentCaptureOptions.liveMrz` (`mode`, `assetBaseUrl`, `workerUrl`) and the text-free `mrz` event (`searching` / `reading` / `valid` / `mismatch` / `fallback`, with a format, a coaching hint or a reason — never MRZ text). On a face with an MRZ, the session's `capture.document.liveMrz` tuning lets the on-device reader (the OCR engine in a Web Worker, now with a 494 KB MRZ-only recognizer) take the photo once every hard check digit validates twice in a row; card fronts get a text-layout lock (`capture.document.frontLock`). Every failure is today's capture. `doc_ocr` submissions carry the optional, text-free `deviceRead` hint (`DocOcrInput.deviceRead`, `DocDeviceRead`) when the still confirmation is ready. New static file `core/ocr/worker.bundle.js` (a self-contained OCR worker for static hosting).
48
+ - `@sekyro/react`: `SekyroVerification`'s `documentLiveMrz` and `DocumentCapture`'s `liveMrz` pass the options through; the document view shows the reader's states (ar/fr/en), a band strip and the 2 s "this looks like a passport/card" line.
49
+ - 426e180: On-device OCR engine for the browser (web live MRZ, groundwork; nothing in the capture flow uses it yet): a new `@sekyro/web/core/ocr/worker` module-worker entry runs the shared Sekyro OCR engine compiled to WebAssembly (SIMD, single thread; built and locked in the private `packages/sekyro-ocr-wasm`). The worker downloads its wasm and models lazily, checks every byte against a sha256 manifest compiled into the SDK before compiling or loading anything, and answers MRZ band reads with geometry, the MRZ format, a check-digit validity flag and a session-salted digest — never the read text. No change to the root entry's bundle or public API.
50
+ - b4c8465: Profile v2 step model: `deriveStep(view)` tells you which step a hosted session waits on (phone OTP, method choice, registry, document, liveness, results, policy stops, cooldown), `isValidNni` / `normalizeNni` check an NNI before it is submitted, and `acceptedIssuers` / `acceptedTypesFor` read the per-country accepted documents (`HostedSessionView.acceptedDocuments`). Nothing existing changes.
51
+ - 225aeea: Plan runner: `await verification.run()` returns the step the session waits on and `verification.on('step', …)` delivers every next one with its actions — `send(phone?)` / `verify(code)` for the phone OTP, `select(method)` for the method choice, `submitNni(nni)` / `confirm()` / `useDocument()` for the registry, `capture({ video })` for liveness, `refresh()` while processing. Inputs are checked before anything is submitted (`InvalidInputError`). Without `run()` the SDK behaves exactly as before.
52
+
53
+ ### Patch Changes
54
+
55
+ - b81288e: Adds types for the native-only `capture.document.ocr` block (`HostedOcrTuning`) and `capture.document.scan.blurRefuseSharpnessMax` (`HostedDocumentScanTuning`, `HostedDocumentCaptureTuning`), reachable through `HostedNativeCaptureTuning` (narrow `HostedSessionView.capture` to it). `HostedCaptureTuning` is unchanged; the web SDK ignores both blocks.
56
+ - d0c4f27: Capture telemetry: the v3 engine's `telemetry` callback gains a `gate` event — for the attempt, every face-gate tick that did not let progress rise is charged to ONE reason (`no_face`, or the weakest check: `size`, `centering`, `pose`, `exposure`, `sharpness`, `motion`) with its tick count and the milliseconds it covered, plus the accepted time and the device class / detection cadence ACTIVE at the end (after any dynamic profile adjustment). Throttled snapshots (≤ 1 per second) while positioning, one `final: true` report at fire / error / retry / destroy. Read-only over the gate: no threshold, calibration or gate behaviour changes.
57
+ - 3438e10: Live-camera capture proof (D8): the SDK's own capture flows (liveness clip + keyframe, document sides, ICAO portrait) now presign through `HostedClient.presignCapture`. It attaches a single-use server capture nonce (prefetched by `armCapture` when a liveness flow starts, else fetched just in time) with the capture engine and duration. `presign` (no proof) is unchanged. When the backend has no nonce route, the presign goes out without a proof, as before.
58
+ - e82324a: Document capture: ID-1 cards (fronts and backs, any issuer) lock from the card measured inside the on-screen guide — the native Lumière measure (row/column gradient edges → fill, centring, edge strength; card-only glare; sharpness) with a lock on a good card held `lockMs` or a steady card held `steadyMs` — instead of depending on the detector's largest contour, which a hand or a patterned floor around the card turned into an edge-clipped quad forever. Web calibration: the guide is the card slot (no margin), card-only glare gated at 0.025, steady deltas scaled to the tick interval, the edge pick prefers the guide's side, and a card must be card-shaped and printed (no lock on a floor tile). Thresholds come from server `capture.document.guide` (`resolveGuideLockTuning`); hosts pass the drawn guide (`guide` option / `setGuide()`). The quad gate still fires where it frames; passports keep the MRZ-band lock. The manual-shutter safety net now waits for `steadyMs + lockMs` of an unlocked detection; `captureReport()` adds `guideBlockers`.
59
+ - c9edead: Document capture: `DocumentCaptureHandle.setGuide(guide)` declares where the on-screen guide is drawn (normalized rect in a `object-fit: cover` video of aspect `viewAspect`), so a full-screen camera with its guide above the middle locks on the card in that guide; the `gate` event carries `guide` — the card measured inside it (`detected`, `fill`, `offsetX`, `offsetY`) — so a UI can frame the card the camera sees.
60
+ - f0ba853: Document capture safety net: `CaptureSessionDoc` emits a `shutter` event offering the manual shutter (`captureNow()`) after `manualShutterMs` (6 s, server `capture.document.scan`) without a capture, or once a document has been detected for `steadyMs` (1.5 s) without the gate locking — e.g. a card held in a hand, where the detector's largest contour is edge-clipped on every frame. A manual still skips the engine's focus gate (the server decides). `captureReport()` returns a PII-free per-side report (capture path, timings, per-tick lock-failure reasons) for telemetry. Lock, zoom and focus behaviour are unchanged.
61
+ - 626104f: Document capture resolution: the scanner asks the opened back camera for up to 4K, takes the full-sensor `ImageCapture` photo where the browser has one (else the native video frame), crops the still to the document (card + 15 % margins, or the passport page estimated from the MRZ band) and encodes the crop at a 2560 / 2240 / 1920 px long edge (1.6 MB / 1.2 MB / 700 KB budgets). A passport MRZ goes from ~630 px to ≥ 1300 px wide where the camera delivers the pixels. `@sekyro/react`'s document stage switches to a 4:3 layout when the phone is held sideways.
62
+
63
+ Focus: the scanner ranks the back cameras after permission and opens the main wide lens (lowest-index Android back camera with continuous AF; iOS "Back Camera"), remembered per browser, falling back to `facingMode`. It applies continuous autofocus where offered, refocuses (single-shot at the document) before the photo, zooms in (≤ 2×) when the camera reports a minimum focus distance beyond the document's fill distance (1.5× on a ≥ 4K track with no focus info), refuses an out-of-focus auto-captured still (`focus_far` coaching, `dcv2.focus_far`), and adds `DocumentCaptureHandle.focusAt(x, y)` for tap-to-focus (wired into `@sekyro/react`'s stage). All feature-detected; no-ops where unsupported.
64
+ - 0f19e99: Document capture: the manual shutter is offered only while a document is in view (seen by the quad or the guide lock within the last 1.2 s) and is withdrawn otherwise — a tap can no longer upload a frame with no document in it. Zoomed in with nothing in view, the scanner coaches "move a little farther" after `camera.noDocFarHintMs` and drops the zoom to 1× after `camera.zoomFallbackMs` (server tuning; report field `zoomFallbackAtMs`).
65
+ - 76174c8: The document capture internals gain an `onFaceReport` seam: one engine report per document face (`captured`, or `abandoned` when destroyed mid-face after the engine ticked), so the hosted page can keep its scanner telemetry.
66
+ - 67a6be3: The document camera takes a steady card after 0.9 s instead of 1.5 s (`GUIDE_LOCK_DEFAULTS.steadyMs`, used when the session sends no capture tuning). The hosted backend serves the same value from capture tuning version `2026-10-23.2`.
67
+ - c9edead: Document capture no longer uses `ImageCapture.takePhoto()` on WebKit (Safari and every iOS browser). iOS 18 Safari ships ImageCapture, and its `takePhoto` aborts Safari's GPU process (`AVCapturePhotoOutput setMaxPhotoDimensions:` throws): the camera track goes muted, the page goes black and the scan never finishes. WebKit keeps the native video frame for the still, as before ImageCapture existed there; Chromium keeps the full-sensor photo.
68
+ - 650d70d: Treat an empty liveness recording as a recorder failure. iOS Safari can stop a `MediaRecorder` without delivering any data; the SDK used to upload the 0-byte clip, the API refused it (400) and the capture stalled. The capture now fails with the retryable recorder error so the person records again.
69
+ - 68b5df5: `HostedSessionView.acceptedDocuments` (optional): the document types accepted per issuing country, for an issuer-first document picker (profile v2 §5.3a).
70
+ - 4c1aa5b: International documents (profile v2 §5.3a): the document type `residence_permit` joins `AcceptedDocumentType` / `ProfileDocumentType`, and `carte_identite` now means a national identity card of any issuing country. `acceptedDocumentTypes` keeps listing only `carte_identite` and `passport`.
71
+ - 273d2ce: Live MRZ: the passport band read around the detected MRZ is at least a fifth of its width tall (a thin detect band cut the MRZ lines, so passports never read on device).
72
+ - 9db49b8: Adds the `nationality_unreadable` document reason: the MRZ nationality (no check digit covers it) could not be read as a country code, nor recovered from a re-read or the printed nationality — retryable with a clearer photo, not billed, and never replaced by the issuing state. `@sekyro/web`: in `DocReason` and the retryable set (the same document side is re-read). `@sekyro/react`: `reasons.nationality_unreadable` copy in ar/fr/en.
73
+ - 6420611: `HostedSessionView` now types the optional `capture` (server capture-coaching tuning) and `integrity` (native device attestation) blocks the hosted API returns. The web SDK does not change its behaviour. Adds the `device_integrity_failed` liveness reason and the `platform_unregistered` / `app_attest_invalid` / `too_many_challenges` error codes used by the native SDKs.
74
+ - 9acafe1: NFC chip check (M-nfc). New check type `nfc_chip` (read by the native SDKs only), its reasons (`NfcChipReason`: retryable `chip_read_failed`, `chip_access_denied`, `chip_parse_failed`; final `chip_not_present`, `nfc_unavailable`, `chip_document_mismatch`, `chip_integrity_failed`, `registry_mismatch`, `document_nni_mismatch`, `platform_unsupported`), the error codes `method_not_offered` and `nfc_outcome_not_accepted` (`KNOWN_CODES`), the identity method `document_chip`, the session failure reason `platform_unsupported` and the optional `HostedStepView.nfc` hints (`NfcStepView`). Web behaviour is unchanged: the server skips the chip for web clients or stops the session with `platform_unsupported`; `@sekyro/react`'s method screen never renders the `document_chip` option.
75
+ - 2055ae5: Adds the `nni_recheck` registry reason (profile v2 §4.4 wrong-number re-entry): after a face match fails against the registry record of a typed national number, the registry step is re-opened once (`input_required`, reason `nni_recheck`) so the person can re-enter the number. `@sekyro/web`: in `PopulationReason` and the retryable set.
76
+ - 9e27401: The OCR worker manifest pins the canonical CI rebuild of the wasm engine (`c15968a6…`, new asset directory `08390000c48a`). Behaviour is unchanged.
77
+ - 723039c: ICAO gate (ID mode): the pitch limit now applies around a 4° offset (`capture.icao.pitchOffsetDeg`, bundled fallback 4 — the same value kyc-portrait applies server-side), so faces people judge frontal are no longer coached to "look straight". `baselineEyeOpenness` is documented as 1 − the Landmarker blink blendshape (the server's unit) and is never sent when it rounds to 0.
78
+ - 4dd2b68: ICAO `id_document` portrait capture on the web (SDK spec §5.4, M1b): `createPortraitCapture` (strict ICAO gate on the MediaPipe Face Landmarker, lazy-loaded engine, server `capture.icao` tuning), a `portrait` plan-runner step that submits `portrait_icao` and then `face_match` with the portrait, the `portrait_required` hand-over reason, and `<PortraitCapture>` in `@sekyro/react/verification`.
79
+ - 471d833: ICAO `id_document` portrait mode (SDK spec §4.9), types and partner helper. New check type `portrait_icao` with its reasons (`PortraitIcaoReason`: `icao_pose`, `icao_eyes`, `icao_mouth`, `icao_occlusion`, `icao_lighting`, `icao_background`, `icao_resolution`, `portrait_not_from_liveness`, all retryable), the per-criterion report (`IcaoReportEntry` on `HostedStepView.icao` and `SessionStepView.report`), `HostedSessionView.portrait` and the `PortraitIcaoInput` submission shape. `@sekyro/node`: `sessions.portrait(id)` returns a 5-minute link to the 480×640 portrait (scope `portrait:read`), `Session.portraitAvailable` and the same flag on the terminal and `review_decided` webhook data. Web behaviour is unchanged: the browser ICAO gate ships later, so the web step model renders `portrait_icao` as "update required".
80
+ - 8425e95: Face-match probe binding (SDK spec §4.10): the server matches the face_match probe against face crops of the scored liveness clip. New retryable, billed `face_match` reason `probe_not_from_liveness` (the face image isn't the person in the liveness clip). `@sekyro/web`: in `FaceMatchReason` and the retryable set (the runner re-captures). `@sekyro/react`: ar/fr/en retry copy "Keep your face in the frame the whole time, then try again"; bundle budget 24 → 25 KiB. `@sekyro/node`: `ProbeBindingReason`. No capture or upload change on any platform.
81
+ - c9edead: `<SekyroVerification>` matches the native SDKs on the remaining screens. Consent stays flow model v3's one line (no consent screen). The face capture (liveness and the ICAO live photo, whose tips the Sekyro face intro lists) is the Lumière face camera, edge to edge: the oval cut from the ink, a ring of ticks that turns green with the capture while the aqua light runs round it, the instruction above the oval. New optional `onClose(event)` prop: when given, the header ends with a close button (`{ reason: "cancelled" }`) and the result page with "Back to {org}" (`{ reason: "finished", result }`); `VerificationResultScreen` takes an optional `onClose` for the same action. The SDK's fonts (Inter, Inter Display for titles, Cairo for Arabic) ship as woff2 assets beside `verification.css` (`font-display: swap`, `unicode-range`). Also: resending a code is a quiet text action with its countdown; the document intro offers "Change document" when the country has other documents and "Change country" only with several countries; the ID card's landscape glyph; white turn cue; one 2 px read line; the lock and the capture checks in the camera's own green; the reference is one tabular caption line with a copy glyph. In `@sekyro/web`, a card's face is classified on the card itself (an MRZ band in its lower half), so the photo side is no longer filed as the verso. The 0.1 surface (`DEFAULT_THEME`, `styles.css`) is unchanged.
82
+ - 8425e95: Returning-user sessions (profile v2 step 5): `HostedSessionView.returning` and the optional `Verification.returning()` tell the host the session only confirms a known person (liveness + face match against their stored photo); `<LivenessCapture>` opens with "Confirm it's you" (ar/fr/en) unless the host renders its own header.
83
+ - a3689e4: The step runner applies the session's `capture.document` camera, scan and guide tuning (and the `scan.blurRefuseSharpnessMax` blur gate) to the document camera, so the hosted page and partner integrations get the same server tuning. Explicit `documentInternals` seams still win.
84
+ - 45f639f: Licence: the SDKs are now under the Sekyro AI SDK License of Ibtikar Technologies, Inc. (https://sekyro.ai/en/legal/sdk-license, also in French and Arabic). `package.json#license` is `SEE LICENSE IN LICENSE`, and each package's `LICENSE` summarises the licence and links to it. `@sekyro/web` now also ships `THIRD-PARTY-NOTICES.txt` with the MIT notice of the bundled scanic document detector and the MIT/NCSA notice of the Emscripten glue in its OCR worker.
85
+ - bc29977: Selfie ICAO policy (spec 2026-10-24): `HostedSessionView.portrait` becomes `HostedPortraitView` (`purpose: "verification" | "id_document"`, optional `icao: { policy: "reject" | "alert"; profile: "id_document" | "selfie" }`). The new `icaoPolicyOf(view)` is the one activation rule: ICAO runs when `portrait.icao` is present or the purpose is `id_document` (an older server's `{ purpose: "id_document" }` alone reads as `reject` / `id_document`). The liveness and portrait snapshots carry it as `icao`; `PortraitCaptureOptions.policy` and the live-photo kit take the policy. With `alert` the gate coaches but never blocks: the capture goes `capture.icao.alertGraceMs` (default 3000) after the face was accepted at the latest. `capture.icao` gains `profile`, `alertGraceMs` and `background.required`; with `background.required: false` (the `selfie` profile) the background is not measured, coached or reported, and the web segmenter check never loads. The bundled fallbacks are now the generated device limits of the profile (a little stricter than the server, from packages/sekyro-contract/icao/profiles.json) instead of looser ones. The gate's per-criterion decision is the pure `judgeIcao`, checked against the shared conformance cases. `portraitIcao.gate.criteria` is now the device verdict as booleans under the server's criterion names (`IcaoGateSummary.verdict`; `framing` folds into `resolution`, unmeasured criteria are omitted). While the eye baseline is still being learned, the gate now says "hold" instead of coaching the eyes.
86
+ - 5eec558: Keyframe ranking (SDK spec §4.8a, M1a), off by default. When the server capture tuning sets `capture.liveness.keyframe.rank: true`, the liveness engine loads a small lazy chunk and picks the face_match keyframe among the same 4 best-of-N candidates (unchanged 600 ms window) by frontal pose (BlazeFace nose-to-eye-midpoint yaw proxy plus eye-line roll), then relative eye-openness (neutral on web: no landmarker in liveness), then detector confidence as an occlusion proxy, with today's sharpness as the tiebreaker. Ranking only, never a rejection. One extra detector pass per candidate. The v3 `telemetry` callback gains a `keyframe` event with the rank numbers (never an image). `FaceObservation` gains an optional `nose` (blaze keypoint 2). The default path and the createVerification-only bundle are unchanged in behaviour.
87
+ - bdcd56e: Internal groundwork for live MRZ scanning in the browser (not exported, not in any entry yet): TypeScript ports of the native on-device OCR read logic (CTC, the any-issuer ICAO 9303 MRZ parser with the OCR look-alike repair, MrzRead, the live MRZ stability gate, and the still-read layer: labels, printed NNI, Arabic/Latin cross-check, recto fields, recovery, profiles, side reads and device verdicts), conformance-tested against the shared Android/iOS vectors and replays. No public API or bundle change.
88
+ - 2f9e980: Step actions are now guarded per action, not per check: repeating the same action while it is in flight (a double tap on `send()`) still sends once, but a different action on the same check — `verify()` right after `send()`, `confirm()` after `submitNni()` — is no longer swallowed by the pending one. It runs as soon as the previous submission settles, including when you call it from inside the `on("step")` handler.
89
+
90
+ ## 0.1.0-rc.7
91
+
92
+ ### Minor Changes
93
+
94
+ - fa95c41: Bundled liveness + face_match submission (one round trip off the face step).
95
+
96
+ - `createVerification` now uploads the liveness clip and the selfie keyframe **in parallel** and submits `liveness` with `input.faceMatch: { imageKey }` (`LivenessInput.faceMatch`). A Sekyro backend with the bundle enabled runs face_match in the same submission; otherwise the SDK submits `face_match` itself with the **already-uploaded** keyframe (no second upload). A keyframe upload failure falls back to today's sequential path. The parallel keyframe upload (and `faceMatch`) only happens when `face_match` is the next executable step after `liveness`: liveness-only templates and skipped / absent face_match never upload a face image, and nothing is uploaded after `destroy()`.
97
+ - Idempotency: a same-key transport replay sends a byte-identical body (same clip key, same keyframe key).
98
+ - Capture states: the chained face_match no longer shows a second `uploading` phase when the key is reused.
99
+ - 06ea302: Org and app branding from the Sekyro AI dashboard.
100
+
101
+ - `@sekyro/web`: `HostedSessionView.branding` (`HostedBranding`: `logoUrl`, `colors`, `radius`, `fontFamily`, all optional) carries only the branding set in the dashboard, merged per field (app over org). Fields nobody set are absent (keep your defaults for them), and `branding` itself is absent when nothing is set or on older APIs.
102
+ - `@sekyro/react`: `brandingToTheme(view.branding)` maps it onto a `SekyroTheme`, and `mergeThemes(...)` combines themes left to right. Pass your own theme last so it still wins: `mergeThemes(brandingToTheme(view.branding), myTheme)`. Integrations that ignore it keep the default theme.
103
+
104
+ ### Patch Changes
105
+
106
+ - 4e7a4a3: Brand: the product is now **Sekyro AI** — package descriptions, READMEs, LICENSE text and the `@sekyro/node` configuration error messages say "Sekyro AI". No API change: package names, the `Sekyro` class, exports and header names are unchanged.
107
+ - d321707: Wait for a fresh capture when bundled face matching requires new input, and reflect its consumed attempt in view and retry events. Keep sequential fallback when the backend leaves matching pending, without double-counting same-key transport replays.
108
+ - c3699fd: Send the time zone and a device context header on hosted API calls (profile v2 §9–§10). No permission prompt; raw ids are hashed per org server-side.
109
+ - c3699fd: Add the `document_nni_mismatch` doc_ocr reason (document NNI differs from the session's bound NNI).
110
+ - 1b6bef2: Profile v2 policy reasons, round 2. New eligibility reason `age_unverifiable` (the app sets a minimum age but the birth date could not be read — `doc_ocr`: retryable with a clearer photo; `population_registry`: final). `failureReason` is widened to every policy reason a session can end on: the session-level reasons plus `nationality_not_allowed`, `underage`, `document_type_not_accepted`, `document_nni_mismatch` and `age_unverifiable`. `@sekyro/web`: `PolicyFailureReason` widened, `age_unverifiable` in `DocReason` / `PopulationReason` / `RETRYABLE_REASONS`. `@sekyro/react`: ar/fr/en copy for `age_unverifiable`. `@sekyro/node`: `EligibilityReason` gains `age_unverifiable`; new `PolicyFailureReason` type used by `Session.failureReason` and the `session.failed` webhook data.
111
+ - 1b6bef2: Profile v2 policy reasons. `@sekyro/web`: the eligibility check reasons `document_type_not_accepted` (retryable), `nationality_not_allowed` and `underage`; `SessionFailureReason` (`location_not_allowed`, `anonymizer_blocked`); `HostedSessionView.failureReason` and `acceptedDocumentTypes`. `@sekyro/react`: ar/fr/en copy for the five reasons. `@sekyro/node`: `SessionFailureReason`, `EligibilityReason`, and `failureReason` on `Session` and the `session.failed` webhook data.
112
+ - 13d512f: Profile v2 step 3: identity-method choice, document-NNI registry submit, matching reasons, `subject` input. `@sekyro/web`: `IdentityMethod`, `MethodChoiceView` and `HostedSessionView.methodChoice`; `HostedStepView.attemptsExhausted` / `nniFromDocument` (and `SessionStepView.attemptsExhausted`); `HostedClient.selectMethod()` (`POST /hosted/method`); error codes `method_required` / `method_locked` / `document_nni_mismatch` (409 when a typed NNI differs from the document's); matching reasons `name_mismatch`, `birth_date_mismatch`, `nationality_mismatch` (doc_ocr + population_registry) and `document_number_mismatch` (doc_ocr) as `MatchReason`. The headless runner stops on a pending choice (`unavailable` / `method_required`, `on('method')`), adds `verification.methodChoice()` / `selectMethod()`, and submits the registry with an empty input when the document gave the NNI. `@sekyro/react`: ar/fr/en copy for the four reasons and the method screen; `<LivenessCapture>` handles the method step while the session waits on the choice: it offers only the methods it can run (the national number — it has no document capture) and selects it without a screen when it is the only one left. `@sekyro/node`: `CreateSessionInput.subject` (`SessionSubjectInput`), `CreateSessionInput.identityMethod` and `sessions.selectMethod(id, method)` (`POST /sessions/:id/method`, `IdentityMethod`) so partners can choose the identity method of an optional-document session, `subjectId` / `externalRef` / `subjectConflict` on `Session` and session webhooks, `SessionStep.attemptsExhausted`, `MatchReason`, and the `subject.updated` webhook data (`{ subjectId, sessionId, status, outcome }`).
113
+ - acbf409: Profile v2 step 4: decision mode, desktop handoff, device stops, per-session profile overrides. `@sekyro/web`: decision-mode reasons `liveness_inconclusive`, `mobile_required`, `match_inconclusive`, `document_authenticity` (`DecisionReason`; the inconclusive three are retryable) and the registered `uncalibrated_capture_channel`; session failure reasons `capture_tampered`, `device_velocity_exceeded`, `device_cooldown`; `HostedStepView.requiresMobile` and `HostedSessionView.retryAfter`; the error code `inference_unavailable` (503, request-level, with `retryAfterSeconds` from `Retry-After` or the body). The headless runner retries `inference_unavailable` up to 3 times after `retryAfterSeconds` (default 10 s) under a fresh idempotency key and a fresh attestation, emitting `on('busy')` for each retry, then surfaces `unavailable / inference_unavailable`; it never captures a `requiresMobile` liveness step on desktop (`unavailable / mobile_required`, nothing uploaded) and adds `verification.mobileRequired()`; a `device_cooldown` result carries `retryAfter`. `@sekyro/react`: ar/fr/en coaching for the inconclusive reasons, "Continue on your phone" instead of the camera when liveness needs a phone, a "busy, trying again" notice, the device stop copy (the cooldown says when to try again — `retryAfterTime()`), no retry button for stops or `mobile_required`; bundle budget 23 → 24 KiB. `@sekyro/node`: `CreateSessionInput.profile` (`EkycProfileOverride`, a deep-partial of `EkycProfileSettings`; unknown keys are rejected server-side, widening answers `403 override_widens_profile` unless the app allows it), `Session.overriddenFields` / `riskSignals` / `retryAfter` (`overriddenFields` and `riskSignals` also on the session lifecycle webhooks), `DecisionReason`, the three device stops in `SessionFailureReason`, and the `subject.suspicious_activity` webhook data (`{ subjectId, sessionId, count }`).
114
+ - b230694: Preserve session policy failure reasons in verification results, including location and anonymizer blocks before capture or during submission. The public result reason type now includes session policy codes, so partner integrations and the React component can display the existing policy explanations.
115
+ - 58e220c: Published bundles are now minified and no longer include source maps. The public API and types are unchanged.
116
+
3
117
  ## 0.1.0-rc.6
4
118
 
5
119
  ### Minor Changes
package/LICENSE CHANGED
@@ -1,20 +1,29 @@
1
- Sekyro SDK — Proprietary License
1
+ Sekyro AI SDK License (proprietary)
2
2
 
3
- Copyright (c) 2026 Ibtikar Technologies. All rights reserved.
3
+ Copyright (c) 2026 Ibtikar Technologies, Inc. All rights reserved.
4
4
 
5
- This software (the "SDK") is proprietary to Ibtikar Technologies. Use is
6
- permitted only under a Sekyro customer agreement with Ibtikar Technologies, and
7
- only to integrate the customer's own applications with the Sekyro
8
- identity-verification service. For that purpose you may install the SDK
9
- from the public npm registry and run it unmodified.
5
+ This SDK is proprietary software of Ibtikar Technologies, Inc., licensed
6
+ only under the Sekyro AI SDK License:
7
+ https://sekyro.ai/en/legal/sdk-license
8
+ (in French: https://sekyro.ai/fr/legal/sdk-license,
9
+ in Arabic: https://sekyro.ai/ar/legal/sdk-license).
10
10
 
11
- No other right or license, express or implied, is granted. You may not
12
- modify, reverse-engineer, redistribute, sublicense, resell or create
13
- derivative works of the SDK, nor remove or alter this notice. The source
14
- code is published for transparency (build provenance, security review); its
15
- publication grants no additional rights.
11
+ In short, and subject to the full license:
16
12
 
17
- THE SDK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- IMPLIED. TO THE EXTENT PERMITTED BY LAW, IBTIKAR TECHNOLOGIES SHALL NOT BE
19
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM THE USE OF THE
20
- SDK, EXCEPT AS PROVIDED IN THE APPLICABLE CUSTOMER AGREEMENT.
13
+ - You may use the SDK only with a Sekyro AI account, a customer agreement
14
+ with Ibtikar Technologies, Inc. and valid API keys, to integrate the
15
+ Sekyro AI services into your own applications. You may distribute it to
16
+ your end users only as embedded in those applications.
17
+ - You may not use it with another identity verification provider, modify it
18
+ beyond the configuration and theming it exposes, reverse engineer,
19
+ decompile or disassemble it (except where the law expressly allows it),
20
+ circumvent its security, attestation, license checks or usage metering,
21
+ or distribute or sublicense it on its own.
22
+ - Open-source components keep their own licenses. See the third-party
23
+ notices shipped with this SDK.
24
+ - The SDK is provided "as is", without warranty, except as your customer
25
+ agreement provides. The license is governed by the laws of the State of
26
+ Delaware, USA. If this license and your customer agreement conflict,
27
+ the customer agreement prevails.
28
+
29
+ Questions: contact@sekyro.ai
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @sekyro/web
2
2
 
3
- Browser SDK for [Sekyro](https://sekyro.ai) identity verification: liveness capture
3
+ Browser SDK for [Sekyro AI](https://sekyro.ai) identity verification: liveness capture
4
4
  (v3 engine), the typed hosted-verification client, and the partner popup embed.
5
5
  Framework-free; pair it with `@sekyro/react` for a ready-made `<LivenessCapture>`.
6
6
 
@@ -19,17 +19,15 @@ void v.startRegistry();
19
19
  await cap.start();
20
20
  ```
21
21
 
22
- **Read the [Web SDK guide](../../docs/partner/web-sdk.md)** for the full integration:
22
+ **Read the [Web SDK guide](https://docs.sekyro.ai/en/web/embedded-sdk)** for the full integration:
23
23
  server-bound NNI, results, webhooks, registered origins, CSP, model assets
24
24
  (`assetBaseUrl`), reason codes, billing. Hosted-flow embed only? The
25
- [quickstart](../../docs/partner/quickstart.md) covers `openHosted`.
25
+ [quickstart](https://docs.sekyro.ai/en/getting-started/quickstart) covers `openHosted`.
26
26
 
27
27
  Requirements: a secure context (HTTPS or `localhost`), self-hosted detector assets
28
- under `/models-v3` (or `assetBaseUrl`), and your page origin registered with Sekyro.
28
+ under `/models-v3` (or `assetBaseUrl`), and your page origin registered with Sekyro AI.
29
29
 
30
- > Status: `0.1.0-rc.0`, still `private` — the public npm release is wired in a later
31
- > task (Changesets + Trusted Publishing). Sekyro's own hosted page consumes it via a
32
- > `file:` link today. Formerly `@ibtikar/identity-web-sdk` (internal).
30
+ > Release candidate (`npm i @sekyro/web@next`). Formerly `@ibtikar/identity-web-sdk` (internal).
33
31
 
34
32
  ## Preload & camera probes
35
33
 
@@ -84,7 +82,7 @@ await cap.start({ facing: "user" }); // reuses the warm detector automatically
84
82
  | `@sekyro/web/core/v3/detect-worker` | Standalone detection-worker entry — construct with `new Worker(new URL('@sekyro/web/core/v3/detect-worker', import.meta.url))` so its worker-scope auto-install never rides a main-thread bundle | Yes |
85
83
  | `@sekyro/web/client` | Typed client for the hosted session API: `HostedClient`, error codes, check input/view types | Yes |
86
84
  | `@sekyro/web/popup` | Partner-facing popup/redirect embed (`open`, `SekyroEvent`) | Yes |
87
- | `@sekyro/web/internal` | **Not public.** Document scanner (doc catalog, `DocGateV1`, MRZ band, frame analysis, feedback aggregator, `CaptureSessionDoc`) + gate-debug geometry constants + the verification internals (`createVerificationInternal` / `createLivenessCaptureInternal` seams, `runLivenessFlow`, `runSubmission`, result mapping, Turnstile loader), consumed by Sekyro's hosted page / `@sekyro/react` only. No semver guarantees; may change in any release | No |
85
+ | `@sekyro/web/internal` | **Not public.** Document scanner (doc catalog, `DocGateV1`, MRZ band, frame analysis, feedback aggregator, `CaptureSessionDoc`) + gate-debug geometry constants + the verification internals (`createVerificationInternal` / `createLivenessCaptureInternal` seams, `runLivenessFlow`, `runSubmission`, result mapping, Turnstile loader), consumed by Sekyro AI's hosted page / `@sekyro/react` only. No semver guarantees; may change in any release | No |
88
86
 
89
87
  Every entry ships ESM + CJS + `.d.ts` (`tsup`). `./core` deliberately does **not**
90
88
  re-export the document scanner (that lives only under `./internal`) nor the worker
@@ -0,0 +1,154 @@
1
+ @sekyro/web: third-party notices
2
+ ================================
3
+
4
+ @sekyro/web is licensed under the Sekyro AI SDK License (see LICENSE). The
5
+ dist/ files of this package also contain the third-party components below,
6
+ which remain under their own licenses.
7
+
8
+ 1. scanic (document edge detection, classical pipeline)
9
+ Bundled in dist/ (the document detector chunks).
10
+ Copyright (c) 2025 marquaye
11
+ License: MIT License (full text in section A).
12
+
13
+ 2. Emscripten runtime and JavaScript glue, version 4.0.23
14
+ Bundled in the OCR worker (dist/core/ocr/worker.js, worker.cjs and
15
+ worker.bundle.js) as the loader of the on-device OCR engine.
16
+ Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
17
+ License: MIT License or University of Illinois/NCSA Open Source License,
18
+ at your choice (full text in section B).
19
+
20
+ The OCR engine binary (sekyro-ocr.wasm) and its models are not part of this
21
+ package: they are downloaded at runtime, and their notices are published with
22
+ them.
23
+
24
+ -------------------------------------------------------------------------------
25
+ A. scanic: MIT License
26
+ -------------------------------------------------------------------------------
27
+
28
+ MIT License
29
+
30
+ Copyright (c) 2025 marquaye
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining a copy
33
+ of this software and associated documentation files (the "Software"), to deal
34
+ in the Software without restriction, including without limitation the rights
35
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36
+ copies of the Software, and to permit persons to whom the Software is
37
+ furnished to do so, subject to the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be included in all
40
+ copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
48
+ SOFTWARE.
49
+ -------------------------------------------------------------------------------
50
+ B. Emscripten: MIT License or University of Illinois/NCSA Open Source License
51
+ -------------------------------------------------------------------------------
52
+
53
+ Emscripten is available under 2 licenses, the MIT license and the
54
+ University of Illinois/NCSA Open Source License.
55
+
56
+ Both are permissive open source licenses, with little if any
57
+ practical difference between them.
58
+
59
+ The reason for offering both is that (1) the MIT license is
60
+ well-known, while (2) the University of Illinois/NCSA Open Source
61
+ License allows Emscripten's code to be integrated upstream into
62
+ LLVM, which uses that license, should the opportunity arise.
63
+
64
+ The full text of both licenses follows.
65
+
66
+ ==============================================================================
67
+
68
+ Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
69
+
70
+ Permission is hereby granted, free of charge, to any person obtaining a copy
71
+ of this software and associated documentation files (the "Software"), to deal
72
+ in the Software without restriction, including without limitation the rights
73
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
74
+ copies of the Software, and to permit persons to whom the Software is
75
+ furnished to do so, subject to the following conditions:
76
+
77
+ The above copyright notice and this permission notice shall be included in
78
+ all copies or substantial portions of the Software.
79
+
80
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
81
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
82
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
83
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
84
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
85
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
86
+ THE SOFTWARE.
87
+
88
+ ==============================================================================
89
+
90
+ Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
91
+ All rights reserved.
92
+
93
+ Permission is hereby granted, free of charge, to any person obtaining a
94
+ copy of this software and associated documentation files (the
95
+ "Software"), to deal with the Software without restriction, including
96
+ without limitation the rights to use, copy, modify, merge, publish,
97
+ distribute, sublicense, and/or sell copies of the Software, and to
98
+ permit persons to whom the Software is furnished to do so, subject to
99
+ the following conditions:
100
+
101
+ Redistributions of source code must retain the above copyright
102
+ notice, this list of conditions and the following disclaimers.
103
+
104
+ Redistributions in binary form must reproduce the above
105
+ copyright notice, this list of conditions and the following disclaimers
106
+ in the documentation and/or other materials provided with the
107
+ distribution.
108
+
109
+ Neither the names of Mozilla,
110
+ nor the names of its contributors may be used to endorse
111
+ or promote products derived from this Software without specific prior
112
+ written permission.
113
+
114
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
115
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
116
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
117
+ IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
118
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
119
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
120
+ SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
121
+
122
+ ==============================================================================
123
+
124
+ This program uses portions of Node.js source code located in src/library_path.js,
125
+ in accordance with the terms of the MIT license. Node's license follows:
126
+
127
+ """
128
+ Copyright Joyent, Inc. and other Node contributors. All rights reserved.
129
+ Permission is hereby granted, free of charge, to any person obtaining a copy
130
+ of this software and associated documentation files (the "Software"), to
131
+ deal in the Software without restriction, including without limitation the
132
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
133
+ sell copies of the Software, and to permit persons to whom the Software is
134
+ furnished to do so, subject to the following conditions:
135
+
136
+ The above copyright notice and this permission notice shall be included in
137
+ all copies or substantial portions of the Software.
138
+
139
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
140
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
141
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
142
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
143
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
144
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
145
+ IN THE SOFTWARE.
146
+ """
147
+
148
+ The musl libc project is bundled in this repo, and it has the MIT license, see
149
+ system/lib/libc/musl/COPYRIGHT
150
+
151
+ The third_party/ subdirectory contains code with other licenses. None of it is
152
+ used by default, but certain options use it (e.g., the optional closure compiler
153
+ flag will run closure compiler from third_party/).
154
+
@@ -0,0 +1 @@
1
+ import{a}from"./chunk-RI3E7GBN.js";import"./chunk-GI3UFGRQ.js";export{a as createBackgroundMonitor};
@@ -0,0 +1 @@
1
+ import{C as a,D as b,E as c,F as d,G as e,H as f,I as g,J as h}from"./chunk-YPG7CB4O.js";import"./chunk-SMKCBUJL.js";import"./chunk-MXWLCDFX.js";import"./chunk-G6LKLN4U.js";import"./chunk-TDLIXFDT.js";import"./chunk-POIQGEZM.js";import"./chunk-FHJ7HO3M.js";import"./chunk-FPXL63KZ.js";import"./chunk-LQ7C44JG.js";import"./chunk-DK5YPBDQ.js";export{h as CAPTURE_CANDIDATE_SPACING_MS,g as CAPTURE_EXTRA_CANDIDATES,e as CaptureSessionDoc,b as DOC_CADENCE_HZ,c as DOC_SHUTTER_DEFAULTS,f as passportBandOf,d as resolveDocShutterTuning,a as warmDocDetector};
@@ -1,4 +1,4 @@
1
- import { F as FaceObservation, b as Detector, W as WorkerLike } from './detect-worker-CKXHlXx9.js';
1
+ import { F as FaceObservation, b as Detector, W as WorkerLike } from './detect-worker-DclNxkZT.js';
2
2
 
3
3
  /** MP4-first ladder: one container (H.264 MP4) covers iOS/macOS Safari + Chrome 130+.
4
4
  * WebM/VP9→VP8 only as the Firefox/older-Chromium fallback. */
@@ -312,6 +312,37 @@ declare class DeviceProfiler {
312
312
  record(detectMs: number): DeviceClass;
313
313
  }
314
314
 
315
+ /** Why a gate tick did not advance: no face, or the weakest gate check. */
316
+ type GateBlockReason = "no_face" | Exclude<V3GateFactor, "presence">;
317
+ interface GateBlockEntry {
318
+ /** Ticks blocked by this reason. */
319
+ count: number;
320
+ /** Wall time (whole ms) those ticks covered. */
321
+ ms: number;
322
+ }
323
+ interface GateBlockSummary {
324
+ /** Only the reasons that blocked at least once. */
325
+ blocked: Partial<Record<GateBlockReason, GateBlockEntry>>;
326
+ /** Wall time (whole ms) of ticks the gate accepted (progress rising). */
327
+ acceptedMs: number;
328
+ /** Total ticks seen. */
329
+ ticks: number;
330
+ }
331
+ /** The reason one tick was held back, or null when the gate accepted it. */
332
+ declare function blockReasonOf(tick: Pick<V3GateTick, "accepted" | "factors">): GateBlockReason | null;
333
+ declare class GateBlockStats {
334
+ private blocked;
335
+ private acceptedMs;
336
+ private ticks;
337
+ private lastAt;
338
+ /** Record one gate tick; the time since the previous tick is charged to THIS tick's state. */
339
+ record(tick: Pick<V3GateTick, "at" | "accepted" | "factors">): void;
340
+ summary(): GateBlockSummary;
341
+ }
342
+
343
+ /** A non-final `gate` telemetry snapshot at most this often while positioning
344
+ * (so an abandoned attempt still reports where its time went). */
345
+ declare const GATE_SNAPSHOT_MS = 1000;
315
346
  /** Best-of-N keyframe window: up to 4 candidates over the first 600ms. */
316
347
  declare const KEYFRAME_CANDIDATES = 4;
317
348
  declare const KEYFRAME_WINDOW_MS = 600;
@@ -363,10 +394,39 @@ type V3TelemetryEvent = {
363
394
  timeToGateMs: number;
364
395
  degradeCount: number;
365
396
  meanDetectMs: number;
366
- } | {
397
+ }
398
+ /** Where the attempt's gate time went (see gate-blocking.ts): per-reason
399
+ * blocked tick counts + ms, accepted ms, and the ACTIVE device class /
400
+ * cadence after any dynamic profile adjustment. `final` on the attempt's
401
+ * end (fired / error / retry / destroy); throttled snapshots before it. */
402
+ | ({
403
+ type: "gate";
404
+ final: boolean;
405
+ activeClass: DeviceClass;
406
+ cadenceHz: number;
407
+ } & GateBlockSummary) | {
367
408
  type: "error";
368
409
  kind: CaptureErrorKind;
369
410
  fallbackReason?: string;
411
+ }
412
+ /** Keyframe ranking (SDK spec §4.8a; only when `rankKeyframes` is on): the
413
+ * rank numbers of every candidate (never an image), the chosen index, the
414
+ * index sharpness alone would have chosen, and the extra detector time. */
415
+ | {
416
+ type: "keyframe";
417
+ tier: Detector["tier"] | null;
418
+ chosen: number;
419
+ sharpest: number;
420
+ detectMs: number;
421
+ candidates: Array<{
422
+ face: boolean;
423
+ frontal: number;
424
+ yaw?: number;
425
+ roll?: number;
426
+ eyes?: number;
427
+ score: number;
428
+ sharpness: number;
429
+ }>;
370
430
  };
371
431
  interface KeyframeCandidate {
372
432
  blob: Blob;
@@ -422,6 +482,11 @@ interface CaptureSessionV3Options {
422
482
  * worker-side through WorkerDetectorProxy. Hosts own Worker construction
423
483
  * (bundler-specific `new Worker(new URL(...))`). */
424
484
  createWorker?: () => WorkerLike;
485
+ /** Keyframe ranking switch (SDK spec §4.8a; server tuning
486
+ * `capture.liveness.keyframe.rank`, default off): read when positioning
487
+ * starts (prefetches the lazy ranker) and again when the capture fires.
488
+ * Off → today's sharpest-of-N, byte for byte. */
489
+ rankKeyframes?: () => boolean;
425
490
  /** INERT (Phase 3 escalation seam — see EscalationHook). Accepted and
426
491
  * retained, never called in Phases 1–2. */
427
492
  escalation?: EscalationHook;
@@ -454,6 +519,8 @@ declare class CaptureSessionV3 {
454
519
  private fired;
455
520
  private carriedDegrade;
456
521
  private carriedMeans;
522
+ private blockStats;
523
+ private lastGateSnapshotAt;
457
524
  constructor(options: CaptureSessionV3Options);
458
525
  get state(): CaptureState;
459
526
  on<K extends keyof CaptureSessionV3Events>(event: K, handler: Handler<K>): () => void;
@@ -485,10 +552,12 @@ declare class CaptureSessionV3 {
485
552
  private defaultQuality;
486
553
  private centerSharpness;
487
554
  private teardownAttempt;
555
+ /** Emit the attempt's gate breakdown; a final report closes it (exactly once). */
556
+ private reportGate;
488
557
  private stopTracks;
489
558
  private fail;
490
559
  private setState;
491
560
  private now;
492
561
  }
493
562
 
494
- export { type V3GateTickInput as A, type V3TelemetryEvent as B, type CameraErrorKind as C, DEFAULT_CLIP_MIME as D, type EscalationClipArtifact as E, FaceGateV3 as F, type GateChecks as G, V3_GATE_DEFAULTS as H, VIDEO_BPS as I, buildLivenessV2FormData as J, KEYFRAME_CANDIDATES as K, cameraPreflight as L, MAX_CLIP_BYTES as M, classifyDevice as N, extForMime as O, isClipTooLarge as P, isMediaRecorderSupported as Q, mapGetUserMediaError as R, STOP_GRACE_MS as S, TIMESLICE_MS as T, pickMimeType as U, type V3ChecksPayload as V, probeDevice as W, CLIP_MS as a, type CaptureArtifacts as b, type CaptureErrorKind as c, type CaptureSessionEvents as d, CaptureSessionV3 as e, type CaptureSessionV3Events as f, type CaptureSessionV3Options as g, type CaptureSessionV3StartOptions as h, type CaptureState as i, type CaptureTiming as j, ClipError as k, type ClipErrorKind as l, ClipRecorder as m, type ClipResult as n, DEVICE_PROFILES as o, type DeviceClass as p, type DeviceProbe as q, type DeviceProfile as r, DeviceProfiler as s, type EscalationHook as t, KEYFRAME_WINDOW_MS as u, MIME_CANDIDATES as v, type V3GateFactor as w, type V3GateQuality as x, type V3GateThresholds as y, type V3GateTick as z };
563
+ export { pickMimeType as $, MIME_CANDIDATES as A, type V3GateFactor as B, type CameraErrorKind as C, DEFAULT_CLIP_MIME as D, type EscalationClipArtifact as E, FaceGateV3 as F, GATE_SNAPSHOT_MS as G, type V3GateQuality as H, type V3GateThresholds as I, type V3GateTick as J, KEYFRAME_CANDIDATES as K, type V3GateTickInput as L, MAX_CLIP_BYTES as M, type V3TelemetryEvent as N, V3_GATE_DEFAULTS as O, VIDEO_BPS as P, blockReasonOf as Q, buildLivenessV2FormData as R, STOP_GRACE_MS as S, TIMESLICE_MS as T, cameraPreflight as U, type V3ChecksPayload as V, classifyDevice as W, extForMime as X, isClipTooLarge as Y, isMediaRecorderSupported as Z, mapGetUserMediaError as _, CLIP_MS as a, probeDevice as a0, type CaptureArtifacts as b, type CaptureErrorKind as c, type CaptureSessionEvents as d, CaptureSessionV3 as e, type CaptureSessionV3Events as f, type CaptureSessionV3Options as g, type CaptureSessionV3StartOptions as h, type CaptureState as i, type CaptureTiming as j, ClipError as k, type ClipErrorKind as l, ClipRecorder as m, type ClipResult as n, DEVICE_PROFILES as o, type DeviceClass as p, type DeviceProbe as q, type DeviceProfile as r, DeviceProfiler as s, type EscalationHook as t, type GateBlockEntry as u, type GateBlockReason as v, GateBlockStats as w, type GateBlockSummary as x, type GateChecks as y, KEYFRAME_WINDOW_MS as z };