@sekyro/web 0.1.0-rc.6 → 0.1.0-rc.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/LICENSE +3 -3
- package/README.md +4 -4
- package/dist/chunk-B45J4KC3.js +1 -0
- package/dist/chunk-BMA36W2W.js +1 -0
- package/dist/chunk-FPXL63KZ.js +1 -0
- package/dist/chunk-IYUOYINS.js +1 -0
- package/dist/chunk-P7ECRN47.js +1 -0
- package/dist/chunk-SXZCL5NO.js +1 -0
- package/dist/chunk-YOB4ZTYN.js +1 -0
- package/dist/chunk-YQBK6OIM.js +1 -0
- package/dist/client/index.cjs +1 -357
- package/dist/client/index.d.cts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +1 -15
- package/dist/core/index.cjs +1 -1639
- package/dist/core/index.js +1 -139
- package/dist/core/v3/detect-worker.cjs +1 -392
- package/dist/core/v3/detect-worker.js +1 -13
- package/dist/{create-verification-BKc9NFQj.d.cts → create-verification-C2JhOO6Y.d.cts} +94 -16
- package/dist/{create-verification-DjcMZHAQ.d.ts → create-verification-CGazwtAh.d.ts} +94 -16
- package/dist/detector-scanic-VQFFXKTL.js +1 -0
- package/dist/{hosted-client-DJXq_CB9.d.ts → hosted-client-kBeq-mnJ.d.cts} +164 -11
- package/dist/{hosted-client-DJXq_CB9.d.cts → hosted-client-kBeq-mnJ.d.ts} +164 -11
- package/dist/index.cjs +1 -3030
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -85
- package/dist/internal/index.cjs +1 -4439
- package/dist/internal/index.d.cts +10 -7
- package/dist/internal/index.d.ts +10 -7
- package/dist/internal/index.js +1 -657
- package/dist/popup/index.cjs +1 -76
- package/dist/popup/index.js +1 -11
- package/dist/scanic-ml-stub-KW7IZHUT.js +1 -0
- package/package.json +5 -8
- package/sbom.cdx.json +195 -0
- package/dist/chunk-MWNFSNMP.js +0 -52
- package/dist/chunk-MWNFSNMP.js.map +0 -1
- package/dist/chunk-NILNKLK6.js +0 -50
- package/dist/chunk-NILNKLK6.js.map +0 -1
- package/dist/chunk-NWF46H7K.js +0 -359
- package/dist/chunk-NWF46H7K.js.map +0 -1
- package/dist/chunk-R3SJQHYU.js +0 -1173
- package/dist/chunk-R3SJQHYU.js.map +0 -1
- package/dist/chunk-TG5IE552.js +0 -1128
- package/dist/chunk-TG5IE552.js.map +0 -1
- package/dist/chunk-WY6U7AIK.js +0 -193
- package/dist/chunk-WY6U7AIK.js.map +0 -1
- package/dist/chunk-YVFF4Q4G.js +0 -327
- package/dist/chunk-YVFF4Q4G.js.map +0 -1
- package/dist/client/index.cjs.map +0 -1
- package/dist/client/index.js.map +0 -1
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/v3/detect-worker.cjs.map +0 -1
- package/dist/core/v3/detect-worker.js.map +0 -1
- package/dist/detector-scanic-4C6IEW2K.js +0 -787
- package/dist/detector-scanic-4C6IEW2K.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal/index.cjs.map +0 -1
- package/dist/internal/index.js.map +0 -1
- package/dist/popup/index.cjs.map +0 -1
- package/dist/popup/index.js.map +0 -1
- package/dist/scanic-ml-stub-S2OZ6HEF.js +0 -10
- package/dist/scanic-ml-stub-S2OZ6HEF.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @sekyro/web
|
|
2
2
|
|
|
3
|
+
## 0.1.0-rc.7
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fa95c41: Bundled liveness + face_match submission (one round trip off the face step).
|
|
8
|
+
|
|
9
|
+
- `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()`.
|
|
10
|
+
- Idempotency: a same-key transport replay sends a byte-identical body (same clip key, same keyframe key).
|
|
11
|
+
- Capture states: the chained face_match no longer shows a second `uploading` phase when the key is reused.
|
|
12
|
+
- 06ea302: Org and app branding from the Sekyro AI dashboard.
|
|
13
|
+
|
|
14
|
+
- `@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.
|
|
15
|
+
- `@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.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 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.
|
|
20
|
+
- 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.
|
|
21
|
+
- 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.
|
|
22
|
+
- c3699fd: Add the `document_nni_mismatch` doc_ocr reason (document NNI differs from the session's bound NNI).
|
|
23
|
+
- 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.
|
|
24
|
+
- 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.
|
|
25
|
+
- 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 }`).
|
|
26
|
+
- 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 }`).
|
|
27
|
+
- 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.
|
|
28
|
+
- 58e220c: Published bundles are now minified and no longer include source maps. The public API and types are unchanged.
|
|
29
|
+
|
|
3
30
|
## 0.1.0-rc.6
|
|
4
31
|
|
|
5
32
|
### Minor Changes
|
package/LICENSE
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
Sekyro SDK — Proprietary License
|
|
1
|
+
Sekyro AI SDK — Proprietary License
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2026 Ibtikar Technologies. All rights reserved.
|
|
4
4
|
|
|
5
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
|
|
6
|
+
permitted only under a Sekyro AI customer agreement with Ibtikar Technologies, and
|
|
7
|
+
only to integrate the customer's own applications with the Sekyro AI
|
|
8
8
|
identity-verification service. For that purpose you may install the SDK
|
|
9
9
|
from the public npm registry and run it unmodified.
|
|
10
10
|
|
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
|
|
|
@@ -25,10 +25,10 @@ server-bound NNI, results, webhooks, registered origins, CSP, model assets
|
|
|
25
25
|
[quickstart](../../docs/partner/quickstart.md) 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
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
|
|
31
|
+
> task (Changesets + Trusted Publishing). Sekyro AI's own hosted page consumes it via a
|
|
32
32
|
> `file:` link today. Formerly `@ibtikar/identity-web-sdk` (internal).
|
|
33
33
|
|
|
34
34
|
## Preload & camera probes
|
|
@@ -84,7 +84,7 @@ await cap.start({ facing: "user" }); // reuses the warm detector automatically
|
|
|
84
84
|
| `@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
85
|
| `@sekyro/web/client` | Typed client for the hosted session API: `HostedClient`, error codes, check input/view types | Yes |
|
|
86
86
|
| `@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 |
|
|
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 AI's hosted page / `@sekyro/react` only. No semver guarantees; may change in any release | No |
|
|
88
88
|
|
|
89
89
|
Every entry ships ESM + CJS + `.d.ts` (`tsup`). `./core` deliberately does **not**
|
|
90
90
|
re-export the document scanner (that lives only under `./internal`) nor the worker
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as d}from"./chunk-FPXL63KZ.js";var y={"ID-1":{center:1.586,tolerance:.15},"ID-3":{center:1.42,tolerance:.2}};function f(t){return y[t]}var M=[{docType:"carte_identite",format:"ID-1",sides:["recto","verso"],mrz:"td1",portraitSide:"recto",aspectRatio:1.586,identifierSource:"mrz"},{docType:"passport",format:"ID-3",sides:["single"],mrz:"td3",portraitSide:"single",aspectRatio:1.42,identifierSource:"mrz"}];var C=480,I=Object.freeze({MIN_AREA_FRAC:.15,EDGE_CLIP_MARGIN_PX:4,STABLE_TICKS:3,IOU_STABLE_MIN:.85,RISE_PER_S:.9,FALL_PER_S:.25,GUIDE_EXPOSURE:.6,GUIDE_SHARPNESS:.35,ID3_BAND_MIN_WIDTH_FRAC:.55,ID3_BAND_ASPECT_MIN:4,ID3_BAND_ASPECT_MAX:40}),D=t=>Math.min(1,Math.max(0,t));function E(t,o,e){let n=[t.topLeft,t.topRight,t.bottomRight,t.bottomLeft],a=0;for(let r=0;r<4;r++){let c=n[r],m=n[(r+1)%4];a+=c.x*m.y-m.x*c.y}let i=o*e;return i>0?Math.abs(a/2)/i:0}function R(t,o){let e=b(t),n=b(o),a=Math.max(e.x1,n.x1),i=Math.max(e.y1,n.y1),r=Math.min(e.x2,n.x2),c=Math.min(e.y2,n.y2),m=Math.max(0,r-a)*Math.max(0,c-i),u=(e.x2-e.x1)*(e.y2-e.y1),p=(n.x2-n.x1)*(n.y2-n.y1),h=u+p-m;return h>0?m/h:0}function S(t){let o=b(t);return{width:o.x2-o.x1,height:o.y2-o.y1}}function b(t){let o=[t.topLeft,t.topRight,t.bottomRight,t.bottomLeft];return{x1:Math.min(...o.map(e=>e.x)),y1:Math.min(...o.map(e=>e.y)),x2:Math.max(...o.map(e=>e.x)),y2:Math.max(...o.map(e=>e.y))}}function g(t){let o=Math.hypot(t.topRight.x-t.topLeft.x,t.topRight.y-t.topLeft.y),e=Math.hypot(t.bottomRight.x-t.bottomLeft.x,t.bottomRight.y-t.bottomLeft.y),n=Math.hypot(t.bottomLeft.x-t.topLeft.x,t.bottomLeft.y-t.topLeft.y),a=Math.hypot(t.bottomRight.x-t.topRight.x,t.bottomRight.y-t.topRight.y),i=(o+e)/2,r=(n+a)/2;return i<=0||r<=0?0:Math.max(i,r)/Math.min(i,r)}var x=class{constructor(o,e={}){this.p=0;this.lastTickAt=null;this.lastQuad=null;this.streak=0;this.format=o,this.t={...I,...e}}reset(){this.p=0,this.lastTickAt=null,this.lastQuad=null,this.streak=0}tick(o){let{quad:e,quality:n,timestampMs:a}=o,i=this.lastTickAt===null?0:Math.max(0,(a-this.lastTickAt)/1e3);this.lastTickAt=a;let r=null;if(e===null)r="dcv1.no_doc";else if(this.format==="ID-3"){let s=S(e),_=s.height>0?s.width/s.height:0;s.width<this.t.ID3_BAND_MIN_WIDTH_FRAC*o.frameWidth?r="dcv1.come_closer":this.edgeClipped(e,o.frameWidth,o.frameHeight)?r="dcv1.fit_document":(_<this.t.ID3_BAND_ASPECT_MIN||_>this.t.ID3_BAND_ASPECT_MAX)&&(r="dcv1.wrong_shape")}else if(E(e,o.frameWidth,o.frameHeight)<this.t.MIN_AREA_FRAC)r="dcv1.come_closer";else if(this.edgeClipped(e,o.frameWidth,o.frameHeight))r="dcv1.fit_document";else{let s=f(this.format),_=g(e);Math.abs(_-s.center)>s.tolerance&&(r="dcv1.wrong_shape")}let c=r===null,m=D(n.exposure)>=this.t.GUIDE_EXPOSURE,u=D(n.sharpness)>=this.t.GUIDE_SHARPNESS,p=m&&u;if(c&&p&&e!==null){let s=this.lastQuad===null||R(e,this.lastQuad)>=this.t.IOU_STABLE_MIN;this.streak=s?this.streak+1:1}else this.streak=0;this.lastQuad=e;let h=c&&p;this.p=D(this.p+(h?this.t.RISE_PER_S:-this.t.FALL_PER_S)*i);let A=h&&this.streak>=this.t.STABLE_TICKS,l;if(r!==null)l=r;else if(m)u?l="dcv1.hold_still":l="dcv1.steady";else{let s=(d[0]+d[1])/2;l=n.meanLuma<s?"dcv1.more_light":"dcv1.too_bright"}return{at:a,quad:e,plausible:c,p:this.p,guidanceKey:l,ready:A,stableTicks:this.streak}}edgeClipped(o,e,n){let a=this.t.EDGE_CLIP_MARGIN_PX;return[o.topLeft,o.topRight,o.bottomRight,o.bottomLeft].some(r=>r.x<=a||r.y<=a||r.x>=e-a||r.y>=n-a)}};export{y as a,f as b,M as c,C as d,I as e,E as f,R as g,S as h,x as i};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var y=1e4,d=class t{constructor(e,i){this.tier="blaze";this.inner=e,i!==void 0&&(this.fallbackReason=i)}static async create(e,i){let o=await import("@mediapipe/tasks-vision"),n=await o.FilesetResolver.forVisionTasks(e),r=await o.FaceDetector.createFromOptions(n,{baseOptions:{modelAssetPath:`${e}/blaze_face_short_range.tflite`,delegate:"CPU"},runningMode:"IMAGE"});return new t(r,i)}async detect(e,i,o){let n;try{n=this.inner.detect(e).detections}catch{return null}let r=O(n);if(r?.boundingBox===void 0)return null;let a=p(e),s=w(r.keypoints,a),c={box:{x:r.boundingBox.originX,y:r.boundingBox.originY,width:r.boundingBox.width,height:r.boundingBox.height},...s!==void 0?{eyes:s}:{},score:r.categories[0]?.score??0};return l(c,e,i,o)}destroy(){try{this.inner.close()}catch{}}};function O(t){let e,i=-1;for(let o of t){let n=o.categories[0]?.score??0;n>i&&(e=o,i=n)}return e}function w(t,e){let i=t?.[0],o=t?.[1];if(i===void 0||o===void 0)return;let n=s=>({x:s.x*e.width,y:s.y*e.height}),[r,a]=[n(i),n(o)];return r.x<=a.x?{left:r,right:a}:{left:a,right:r}}function R(){if(typeof OffscreenCanvas=="function")return new OffscreenCanvas(2,2);let t=document.createElement("canvas");return t.width=2,t.height=2,t}async function v(){let t=globalThis.FaceDetector;if(typeof t!="function")return{ok:!1,reason:"unsupported"};try{let e=new t({fastMode:!0,maxDetectedFaces:1});return await e.detect(R()),{ok:!0,instance:e}}catch(e){return{ok:!1,reason:`smoke_failed:${e instanceof Error?e.message:String(e)}`}}}var m=class{constructor(e,i){this.tier="native";this.inner=e,i!==void 0&&(this.fallbackReason=i)}async detect(e,i,o){let n;try{n=await this.inner.detect(e)}catch{return null}let r=n[0];if(r===void 0)return null;let a=T(r.landmarks),s={box:{x:r.boundingBox.x,y:r.boundingBox.y,width:r.boundingBox.width,height:r.boundingBox.height},...a!==void 0?{eyes:a}:{},score:1};return l(s,e,i,o)}destroy(){}};function T(t){if(t===void 0)return;let e=t.filter(n=>n.type==="eye").map(n=>n.locations[0]).filter(n=>n!==void 0),[i,o]=e;if(!(i===void 0||o===void 0))return i.x<=o.x?{left:i,right:o}:{left:o,right:i}}var F="/models-v3",I=["native","blaze"],h=class extends Error{constructor(e){super(`no detector tier available (${e})`),this.name="DetectorLadderError",this.fallbackReason=e}};function p(t){let e=t;if(typeof e.videoWidth=="number"&&e.videoWidth>0)return{width:e.videoWidth,height:e.videoHeight??0};if(typeof e.naturalWidth=="number"&&e.naturalWidth>0)return{width:e.naturalWidth,height:e.naturalHeight??0};let i=typeof e.width=="number"?e.width:0,o=typeof e.height=="number"?e.height:0;return{width:i,height:o}}function l(t,e,i,o){let n=p(e),r=n.width>0?i/n.width:1,a=n.height>0?o/n.height:1,s=c=>({x:c.x*r,y:c.y*a});return{box:{x:t.box.x*r,y:t.box.y*a,width:t.box.width*r,height:t.box.height*a},...t.eyes!==void 0?{eyes:{left:s(t.eyes.left),right:s(t.eyes.right)}}:{},score:t.score}}function E(t){return t instanceof Error?t.message:String(t)}async function x(t){let e=t.preferred??I,i=[];for(let o of e){if(o==="native"){let n=await v();if(n.ok)return new m(n.instance,g(i));i.push(`native:${n.reason}`);continue}try{return await S(d.create(t.v3AssetBaseUrl??F,g(i)),t.blazeInitTimeoutMs??y)}catch(n){i.push(n instanceof f?"blaze:blaze_init_timeout":`blaze:${E(n)}`)}}throw new h(g(i)??"no_tier_available")}function g(t){return t.length>0?t.join("; "):void 0}var f=class extends Error{};function S(t,e){return new Promise((i,o)=>{let n=setTimeout(()=>{o(new f(`init exceeded ${e}ms`)),t.then(r=>r.destroy(),()=>{})},e);t.then(r=>{clearTimeout(n),i(r)},r=>{clearTimeout(n),o(r)})})}var u=1;function A(){return typeof OffscreenCanvas=="function"&&typeof createImageBitmap=="function"&&typeof Worker=="function"}function M(t,e={}){let i=e.createDetector??x,o=null;t.onmessage=n=>{let r=n.data;if(r.v!==u){r.bitmap?.close?.();return}if(r.type==="init"){i(r.options).then(a=>{o=a;let s={v:u,type:"ready",tier:a.tier,...a.fallbackReason!==void 0?{fallbackReason:a.fallbackReason}:{}};t.postMessage(s)});return}if(r.type==="detect"){let a=s=>{let c={v:u,type:"result",id:r.id,observation:s};t.postMessage(c),r.bitmap.close?.()};if(o===null){a(null);return}o.detect(r.bitmap,r.streamWidth,r.streamHeight).then(a,()=>a(null))}}}typeof self<"u"&&typeof self.importScripts=="function"&&M(self);var k=class t{constructor(e,i){this.pending=new Map;this.nextId=1;this.worker=e,this.tier=i.tier,i.fallbackReason!==void 0&&(this.fallbackReason=i.fallbackReason),e.addEventListener("message",o=>{let n=o.data;if(n.v!==u||n.type!=="result")return;let r=this.pending.get(n.id);this.pending.delete(n.id),r?.(n.observation)})}static create(e,i,o=1e4){return new Promise((n,r)=>{let a=setTimeout(()=>{e.removeEventListener("message",s),e.terminate(),r(new Error(`worker detector not ready within ${o}ms`))},o),s=D=>{let b=D.data;b.v!==u||b.type!=="ready"||(clearTimeout(a),e.removeEventListener("message",s),n(new t(e,b)))};e.addEventListener("message",s);let c={v:u,type:"init",options:i};e.postMessage(c)})}async detect(e,i,o){let n;try{typeof ImageBitmap<"u"&&e instanceof ImageBitmap?n=e:typeof createImageBitmap=="function"?n=await createImageBitmap(e):n=e}catch{return null}let r=this.nextId;return this.nextId+=1,new Promise(a=>{this.pending.set(r,a);let s={v:u,type:"detect",id:r,bitmap:n,streamWidth:i,streamHeight:o};this.worker.postMessage(s,[n])})}destroy(){for(let e of this.pending.values())e(null);this.pending.clear(),this.worker.terminate()}};export{F as a,I as b,h as c,p as d,l as e,x as f,u as g,A as h,M as i,k as j};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var A=180,g=[100,190],x=64;function f(r,n){return .299*(r[n]??0)+.587*(r[n+1]??0)+.114*(r[n+2]??0)}function b(r){let{data:n,width:e,height:o}=r,t=e*o;if(t===0)return 0;let u=0;for(let s=0;s<t;s+=1)u+=f(n,s*4);return u/t}function E(r){let{data:n,width:e,height:o}=r;if(e<3||o<3)return 0;let t=new Float32Array(e*o);for(let a=0;a<t.length;a+=1)t[a]=f(n,a*4);let u=0,s=0,c=0;for(let a=1;a<o-1;a+=1)for(let i=1;i<e-1;i+=1){let m=a*e+i,l=(t[m-e]??0)+(t[m-1]??0)+(t[m+1]??0)+(t[m+e]??0)-4*(t[m]??0);u+=l,s+=l*l,c+=1}if(c===0)return 0;let h=u/c,p=s/c-h*h;return Math.min(1,Math.max(0,p)/180)}function F(r){let n=b(r),[e,o]=g,t=n<e?e-n:n>o?n-o:0;return Math.max(0,1-t/x)}export{A as a,g as b,b as c,E as d,F as e};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c="0.1.0-rc.7";var d="sekyro.did",l;function g(){let t=globalThis.crypto;try{if(typeof t?.randomUUID=="function")return t.randomUUID()}catch{}try{if(typeof t?.getRandomValues=="function"){let e=t.getRandomValues(new Uint8Array(16));return Array.from(e,r=>r.toString(16).padStart(2,"0")).join("")}}catch{}}function f(){try{let t=localStorage.getItem(d);if(t)return t;let e=g();return e!==void 0&&localStorage.setItem(d,e),e}catch{return l??(l=g()),l}}async function m(){let t=globalThis.crypto?.subtle;if(!t)return"";let e=navigator,r=JSON.stringify([e.userAgent,e.platform,e.hardwareConcurrency??null,e.deviceMemory??null,e.maxTouchPoints??0,screen.width,screen.height,screen.colorDepth,globalThis.devicePixelRatio??1,Intl.DateTimeFormat().resolvedOptions().timeZone,e.languages?.join(",")??e.language]),n=await t.digest("SHA-256",new TextEncoder().encode(r));return Array.from(new Uint8Array(n),i=>i.toString(16).padStart(2,"0")).join("").slice(0,32)}function p(t){let e=new TextEncoder().encode(t),r="";for(let n of e)r+=String.fromCharCode(n);return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function y(t){let e,r;return{timezone(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone?.slice(0,64)||void 0}catch{return}},noteCameraLabel(n){n&&(e=n.slice(0,128))},async deviceHeader(){try{r??(r=m().catch(()=>""));let n=[];navigator.webdriver&&n.push("webdriver"),/HeadlessChrome/.test(navigator.userAgent)&&n.push("headless");let i=await r,s=f(),u={v:1,platform:"web",sdk:t.sdk.slice(0,64),ids:[...s?[{kind:"web_local",value:s.slice(0,128)}]:[],...i?[{kind:"web_fp",value:i}]:[]],screen:{w:screen.width,h:screen.height,dpr:globalThis.devicePixelRatio??1},...e?{cameraLabel:e}:{},...n.length?{signals:n}:{}};return p(JSON.stringify(u))}catch{return}}}}var o=Symbol.for("sekyro.web.clientContext"),a=globalThis,b=a[o]??(a[o]=y({sdk:`@sekyro/web@${c}`}));function C(t){try{let e=t.getVideoTracks?.()[0];e&&b.noteCameraLabel(e.label)}catch{}}export{b as a,C as b};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{F as we,b as Q,k as ye}from"./chunk-YQBK6OIM.js";import{c as U,d as Se,e as be}from"./chunk-YOB4ZTYN.js";import{a as ge,f as F}from"./chunk-BMA36W2W.js";var Ue=12e4,_e=13e4,qe=3e3,Ke=1200,Z=Symbol.for("@sekyro/web.preload.v1");function H(){let e=globalThis;return e[Z]??(e[Z]=new Map),e[Z]}function q(e){let t=(e??ge).replace(/\/+$/,"");return t===""?"/":t}function Ce(e){let t=H(),n=t.get(e);return n===void 0&&(n={status:"idle",promise:null,scheduled:null,detectorPromise:null,detector:null,listeners:new Set},t.set(e,n)),n}function J(e,t){if(e.status!==t){e.status=t;for(let n of[...e.listeners])n(t)}}var re=e=>F({preferred:["blaze"],v3AssetBaseUrl:e,blazeInitTimeoutMs:Ue}),se=re;function At(e){se=e??re}function Pt(){for(let e of H().values())e.detector?.destroy();H().clear(),se=re}function Be(){return typeof window<"u"&&typeof navigator<"u"&&typeof navigator.mediaDevices?.getUserMedia=="function"}function Ne(){return navigator.connection?.saveData===!0}function We(e){let t=window;if(typeof t.requestIdleCallback=="function"){let i=t.requestIdleCallback(e,{timeout:qe});return()=>t.cancelIdleCallback?.(i)}let n=setTimeout(e,Ke);return()=>clearTimeout(n)}function Ye(e,t){let n,i=new Promise(d=>{n=d}),s=!1,l=()=>{},u={promise:i,settle:d=>{s||(s=!0,l(),e.scheduled===u&&(e.scheduled=null),n(d))}};return e.scheduled=u,l=We(()=>{s||u.settle(oe(e,t))}),i}function oe(e,t){if((e.status==="loading"||e.status==="ready")&&e.promise!==null)return e.promise;J(e,"loading");let n=new Promise(d=>d(se(t)));e.detectorPromise=n;let i,s=new Promise((d,o)=>{i=setTimeout(()=>{n.then(c=>c.destroy(),()=>{}),o(new Error(`detector preload exceeded ${_e} ms`))},_e)}),u=Promise.race([n,s]).then(d=>e.detectorPromise!==n?(d.destroy(),e.status):(e.detector=d,J(e,"ready"),"ready"),d=>(e.detectorPromise===n&&(e.detectorPromise=null,e.detector=null,J(e,"failed"),console.warn("[@sekyro/web] face-detector preload failed:",d)),e.status)).finally(()=>clearTimeout(i));return e.promise=u,e.scheduled?.settle(u),u}function xt(e={}){if(!Be())return Promise.resolve("idle");let t=q(e.assetBaseUrl),n=Ce(t);return e.respectSaveData===!0&&Ne()?Promise.resolve(n.status):(n.status==="loading"||n.status==="ready")&&n.promise!==null?n.promise:e.idle===!0?n.scheduled?.promise??Ye(n,t):oe(n,t)}function Mt(e){return H().get(q(e))?.status??"idle"}function Vt(e,t){let n=Ce(q(t));return n.listeners.add(e),()=>{n.listeners.delete(e)}}function It(e){let t=H().get(q(e));t!==void 0&&(t.scheduled?.settle("idle"),t.detector?.destroy(),t.detector=null,t.detectorPromise=null,t.promise=null,J(t,"idle"))}function $e(e){return{get tier(){return e.tier},get fallbackReason(){return e.fallbackReason},detect:(...t)=>e.detect(...t),destroy:()=>{}}}function Ee(e){let t=q(e),n=H().get(t);if(!(n===void 0||n.detector===null&&n.detectorPromise===null&&n.scheduled===null))return async()=>{n.scheduled!==null&&oe(n,t);let i=n.promise;for(;n.detector===null&&i!==null&&(await i,n.promise!==i);)i=n.promise;return n.detector!==null?$e(n.detector):F({v3AssetBaseUrl:t})}}function ee(){let e=new Map;return{on(t,n){let i=e.get(t);i===void 0&&(i=new Set,e.set(t,i));let s=n;return i.add(s),()=>{i?.delete(s)}},emit(t,n){let i=e.get(t);if(i!==void 0)for(let s of[...i])s(n)},clear(){e.clear()}}}function ke(){if(typeof navigator>"u"||!("wakeLock"in navigator))return null;let e=navigator.wakeLock;return typeof e?.request=="function"?e:null}function Le(){return typeof document>"u"||document.visibilityState==="visible"}function Re(e){try{e.release().catch(()=>{})}catch{}}function Te(e={}){if(e.enabled===!1)return{setActive:()=>{}};let t=!1,n=null,i=!1,s=!1,l=()=>{if(!t||i||n!==null&&!n.released)return;let o=ke();if(o===null||!Le())return;let c;try{c=o.request("screen")}catch{return}i=!0,c.then(p=>{if(i=!1,!t){Re(p);return}n=p,p.addEventListener?.("release",()=>{n===p&&(n=null)})},()=>{i=!1})},u=()=>{Le()&&l()},d=o=>{typeof document>"u"||o===s||(s=o,o?document.addEventListener("visibilitychange",u):document.removeEventListener("visibilitychange",u))};return{setActive:o=>{if(t=o,o){if(ke()===null)return;d(!0),l();return}if(d(!1),n!==null){let c=n;n=null,Re(c)}}}}function ze(e){switch(e){case"fcv3.no_face":case"fcv3.center_face":case"fcv3.straighten":return"center";case"fcv3.come_closer":return"closer";case"fcv3.move_back":return"farther";case"fcv3.more_light":case"fcv3.too_bright":return"light";case"fcv3.hold_still":case"fcv3.steady_camera":return"still";default:return null}}function Ge(e){switch(e){case"preflight":case"acquiring":return"starting";case"positioning":return"gating";case"idle":case"recording":case"captured":case"error":return e;default:{let t=e;throw new Error(`mapEngineState: unhandled state ${t}`)}}}function Xe(e){switch(e){case"permission":case"notfound":case"inuse":case"insecure":case"unsupported":return e;case"overconstrained":case"generic":case"models":case"cliptoolarge":case"recorder":return"generic";default:{let t=e;throw new Error(`mapErrorKind: unhandled kind ${t}`)}}}function ae(e,t={}){let n=t.now??(()=>performance.now()),i=ee(),s=!ye(),l=t.engineOptions,u=l?.createDetector===void 0&&l?.createWorker===void 0?{createDetector:()=>{let a=Ee(e.assetBaseUrl);return a!==void 0?a():F(e.assetBaseUrl!==void 0?{v3AssetBaseUrl:e.assetBaseUrl}:{})}}:{},d={video:e.video,mode:s?"photo":"clip",...e.facing!==void 0?{facingMode:e.facing}:{},...e.assetBaseUrl!==void 0?{v3AssetBaseUrl:e.assetBaseUrl}:{},...t.engineOptions??{},...u},o=t.createEngine?.(d)??new we(d),c="idle",p=null,h=0,m=!1,_="full",y=null,v=null,C=0,P=0,M=()=>{e.video.videoWidth>0&&e.video.videoHeight>0&&(C=e.video.videoWidth,P=e.video.videoHeight)},O=Te({enabled:e.keepScreenAwake!==!1}),x=a=>{c=a,O.setActive(a==="starting"||a==="gating"||a==="recording"),i.emit("state",a)},V=()=>i.emit("gate",{progress:h,hint:p,faceDetected:m}),D=()=>{let a=o.animations??"full";a!==_&&(_=a,i.emit("profile",{animations:_}))},G=[o.on("statechange",({state:a})=>{let b=Ge(a);b==="starting"&&c!=="starting"&&(p=null,h=0,m=!1,y=null,v=null,C=0,P=0),x(b),D(),b==="recording"&&(M(),y=n(),p="hold",h=1,V())}),o.on("checks",a=>{M(),h=a.progress,m=a.factors.presence>0,V(),D()}),o.on("guidance",({key:a})=>{p=ze(a),V()}),o.on("frozen",({previewDataUrl:a})=>{v=n(),i.emit("frozen",{previewDataUrl:a})}),o.on("artifacts",a=>{let b=y!==null&&v!==null?Math.max(0,Math.round(v-y)):void 0;i.emit("captured",{keyframe:a.keyframe,...a.clip!==void 0?{clip:a.clip}:{},...a.clipMimeType!==void 0?{clipMime:a.clipMimeType}:{},width:e.video.videoWidth||C,height:e.video.videoHeight||P,...b!==void 0?{clipMs:b}:{},captureChannel:Q()?"mobile":"desktop",...s?{degraded:"NO_RECORDER"}:{},engine:"v3",...a.timing?.cameraReadyMs!==void 0?{cameraReadyMs:a.timing.cameraReadyMs}:{},...a.timing?.faceAcquireMs!==void 0?{faceAcquireMs:a.timing.faceAcquireMs}:{},...a.timing?.captureMs!==void 0?{captureMs:a.timing.captureMs}:{}})}),o.on("error",({kind:a,retryable:b})=>{i.emit("error",{kind:Xe(a),retryable:b})})];return{handle:{get state(){return c},on:(a,b)=>i.on(a,b),start:a=>o.start(a?.facing!==void 0?{facingMode:a.facing}:{}),retry:()=>o.retry(),switchCamera:()=>o.switchCamera(),destroy:()=>{for(let a of G)a();O.setActive(!1),o.destroy(),i.clear()}},setFlowState:a=>{c!==a&&x(a)}}}function Yt(e){return ae(e).handle}var je=new Set(["no_face","media_unreadable","clip_too_short","no_face_source","side_missing","unreadable","registry_unavailable","otp_mismatch","otp_expired","otp_send_failed","document_type_not_accepted","age_unverifiable","liveness_inconclusive","match_inconclusive","document_authenticity"]);function N(e){let t=e.methodChoice;return t===void 0||!t.available||t.selected!==null||A(e)?.checkType==="phone_otp"?null:t}var Qe=new Set(["liveness","face_match"]);function W(e){return e!==void 0&&(e.state==="pending"||e.state==="input_required")}function R(e){return e.status!=="created"&&e.status!=="in_progress"}function A(e){return e.steps.find(t=>W(t))}function T(e,t){return e.steps.find(n=>n.checkType===t)}function ue(e,t){let n=t.steps.find(s=>s.checkType==="liveness"),i=t.steps.find(s=>s.checkType==="face_match");return T(e,"face_match")?.state==="pending"&&(n?.state==="passed"||n?.state==="review")&&i!==void 0&&i.state!=="pending"&&i.state!=="skipped"}function Ae(e,t,n){let i=new Map(e.steps.map(u=>[u.checkType,u])),s=n==="liveness"&&ue(e,t),l=t.steps.map(u=>{let d=i.get(u.checkType),o=d?.attemptsRemaining??0,c={...d??{},checkType:u.checkType,state:u.state,attemptsRemaining:u.checkType===n||u.checkType==="face_match"&&s?Math.max(0,o-1):o};return u.reason!==void 0?c.reason=u.reason:delete c.reason,u.attemptsExhausted!==void 0?c.attemptsExhausted=u.attemptsExhausted:delete c.attemptsExhausted,c});return{...e,status:t.status,outcome:t.outcome,failureReason:t.failureReason,steps:l,expiresAt:t.expiresAt}}function L(e,t){return t===void 0?{sessionId:e,status:"unavailable"}:{sessionId:e,status:"unavailable",reason:t}}function K(e,t,n){return n===void 0?{sessionId:e,status:t}:{sessionId:e,status:t,reason:n}}function le(e,t){let{sessionId:n}=e;switch(e.status){case"completed":return e.outcome==="approved"?{sessionId:n,status:"approved"}:e.outcome==="review"?K(n,"review",B(e,"review")):K(n,"rejected",B(e,"failed"));case"failed":{let s=K(n,"rejected",e.failureReason??B(e,"failed"));return e.failureReason==="device_cooldown"&&e.retryAfter!==void 0&&(s.retryAfter=e.retryAfter),s}case"review_pending":return K(n,"review",B(e,"review"));case"expired":return L(n);case"created":case"in_progress":break;default:{let s=e.status;throw new Error(`mapSessionResult: unhandled status ${s}`)}}let i=e.steps.find(s=>Qe.has(s.checkType)&&s.state==="input_required"&&s.reason!==void 0);if(i?.reason==="mobile_required")return L(n,"mobile_required");if(i?.reason!==void 0){let s=t.autoRetry&&je.has(i.reason)&&i.attemptsRemaining>0;return{sessionId:n,status:s?"retry":"rejected",reason:i.reason,attemptsRemaining:i.attemptsRemaining}}return A(e)===void 0&&e.steps.some(s=>s.state==="review")?K(n,"review",B(e,"review")):L(n)}function B(e,t){return e.steps.find(n=>n.state===t&&n.reason!==void 0)?.reason}var Ze=["video/webm","video/mp4"],Pe="image/jpeg",Je=2,et=1e3,tt=1e4,nt=3,it=10,rt=30,st=3,ot=new Set(["network_error","timeout","upload_failed","unknown_error","rate_limit_exceeded"]),at=new Set(["session_not_active","check_not_found","check_already_finalized","check_out_of_order"]),Y=class extends Error{constructor(){super("verification destroyed"),this.name="FlowCancelledError"}};function ut(e){let t=e.steps.findIndex(i=>i.checkType==="liveness");return t<0?!1:e.steps.slice(t+1).find(i=>W(i))?.checkType==="face_match"}var $=class extends Error{constructor(t){super(t),this.name="AttestationError"}};async function te(e,t,n={}){let i=e.uuid(),s=!1,l=!1,u=0,d=0;for(;;)try{return await t(e.client,i)}catch(o){if(o instanceof $){if(l)throw o;l=!0,n.onAttestationInvalid?.(),i=e.uuid();continue}if(!(o instanceof U))throw o;if(o.code==="consent_required"&&!s){s=!0,await e.ensureConsent({force:!0}),i=e.uuid();continue}if(o.code==="attestation_invalid"&&!l){l=!0,n.onAttestationInvalid?.(),i=e.uuid();continue}if(o.code==="inference_unavailable"&&d<nt){d+=1;let c=Math.min(o.retryAfterSeconds??it,rt);e.onBusy?.({attempt:d,retryAfterSeconds:c}),await e.sleep(c*1e3),n.onAttestationInvalid?.(),i=e.uuid();continue}if(ot.has(o.code)&&u<Je){u+=1;let c=o.retryAfterSeconds!==void 0?o.retryAfterSeconds*1e3:void 0;await e.sleep(c!==void 0?Math.min(c,tt):et);continue}throw o}}var lt=new Set(["passed","review","processing","skipped"]);async function xe(e){let{artifacts:t}=e,n=t.clip,i=Se(t.clipMime??"");if(t.degraded==="NO_RECORDER"||n===void 0||!Ze.includes(i))return{kind:"unavailable",reason:"no_recorder"};let s=ct(e,t.keyframe),l=dt(e,n,i,s),u=ft(e,s),d=!1,o=!1,c=!0,p=0;for(;;){if(e.isCancelled())return{kind:"cancelled"};let h=e.getView();if(R(h))return{kind:"settled"};let m=A(h)?.checkType,_=m==="liveness"&&!d?"liveness":m==="face_match"&&!o&&c?"face_match":null;if(_===null)return{kind:"settled"};if(_==="liveness"&&t.captureChannel==="desktop"&&T(h,"liveness")?.requiresMobile===!0)return{kind:"unavailable",reason:"mobile_required"};try{if(_==="liveness"){d=!0;let y=await l();if(e.isCancelled())return{kind:"cancelled"};o=ue(h,y),e.applySubmit(y,"liveness");let v=T(e.getView(),"liveness")?.state;c=v===void 0||lt.has(v)}else{o=!0;let y=await u();if(e.isCancelled())return{kind:"cancelled"};e.applySubmit(y,"face_match")}}catch(y){if(e.isCancelled()||y instanceof Y)return{kind:"cancelled"};let v=mt(y);if(v.kind!=="resync"||p>=st)return v;p+=1,await e.refreshView()}}}function ct(e,t){let n=null,i=null,s=()=>n!==null?Promise.resolve(n):(i??(i=(async()=>{let l=await e.client.presign("image",Pe,t.size);return await e.client.uploadToSpaces(l.uploadUrl,t,Pe),n=(await e.client.completeMedia(l.key)).key,n})().finally(()=>{i=null})),i);return{ensure:s,attempt:()=>s().catch(()=>null),current:()=>n}}function dt(e,t,n,i){let s=null,l,u=null;return()=>te(e,async(d,o)=>{if(s===null||l===void 0){if(e.isCancelled())throw new Y;l===void 0&&!ut(e.getView())&&(l=null),e.onPhase("uploading");let[c,p]=await Promise.all([s!==null?Promise.resolve(s):(async()=>{let h=await d.presign("clip",n,t.size);return await d.uploadToSpaces(h.uploadUrl,t,n),(await d.completeMedia(h.key)).key})(),l!==void 0?Promise.resolve(l===null?null:l.imageKey):i.attempt()]);s=c,l===void 0&&(l=p===null?null:{imageKey:p})}if(u===null)try{u=await e.getAttestation()}catch(c){throw new $(c instanceof Error?c.message:String(c))}return e.onPhase("checking"),d.submitCheck("liveness",{clipKey:s,attestation:u,captureChannel:e.artifacts.captureChannel,capture:pt(e.artifacts),...l!==null?{faceMatch:l}:{}},{idempotencyKey:o})},{onAttestationInvalid:()=>{u=null}})}function ft(e,t){return()=>te(e,async(n,i)=>{if(t.current()===null){if(e.isCancelled())throw new Y;e.onPhase("uploading")}let s=await t.ensure();return e.onPhase("checking"),n.submitCheck("face_match",{imageKey:s},{idempotencyKey:i})})}function pt(e){return{...e.engine!==void 0?{engine:e.engine}:{},width:e.width,height:e.height,...e.clipMs!==void 0?{clipMs:e.clipMs}:{},...e.cameraReadyMs!==void 0?{cameraReadyMs:e.cameraReadyMs}:{},...e.faceAcquireMs!==void 0?{faceAcquireMs:e.faceAcquireMs}:{},...e.captureMs!==void 0?{captureMs:e.captureMs}:{}}}function mt(e){if(e instanceof $)return{kind:"unavailable",reason:"attestation"};if(e instanceof U){if(e.code==="attestation_invalid")return{kind:"unavailable",reason:"attestation"};if(e.code==="method_required")return{kind:"unavailable",reason:"method_required"};if(e.code==="inference_unavailable")return{kind:"unavailable",reason:"inference_unavailable"};if(e.code==="mobile_required")return{kind:"unavailable",reason:"mobile_required"};if(at.has(e.code))return{kind:"resync"}}return{kind:"unavailable",reason:"network"}}var vt="https://challenges.cloudflare.com/turnstile/v0/api.js",Zt=3e4;function Me(){return window.turnstile}var z=null;function Jt(){z=null}function ht(){return z??(z=new Promise((e,t)=>{let n=Me();if(n){e(n);return}let i=document.createElement("script");i.src=vt,i.async=!0,i.onload=()=>{let s=Me();s?e(s):(z=null,t(new Error("Turnstile script loaded but exposed no API")))},i.onerror=()=>{z=null,i.remove(),t(new Error("Turnstile script failed to load"))},document.head.appendChild(i)})),z}function ce(){return new Error("Turnstile challenge aborted")}async function Ve(e,t={}){let n=t.timeoutMs??3e4,i=t.signal;if(i?.aborted)throw ce();let s=await ht();if(i?.aborted)throw ce();let l=document.createElement("div");document.body.appendChild(l);let u,d,o;try{return await new Promise((c,p)=>{if(d=setTimeout(()=>p(new Error(`Turnstile challenge timed out (${n} ms)`)),n),i!==void 0&&(o=()=>p(ce()),i.addEventListener("abort",o,{once:!0})),u=s.render(l,{sitekey:e,execution:"execute",appearance:"execute",callback:c,"error-callback":h=>{p(new Error(`Turnstile challenge failed${h?` (${h})`:""}`))}}),u===void 0){p(new Error("Turnstile render returned no widget id"));return}s.execute(u)})}finally{if(clearTimeout(d),i!==void 0&&o!==void 0&&i.removeEventListener("abort",o),u!==void 0)try{s.remove(u)}catch{}l.remove()}}var yt="https://api.sekyro.ai",gt=5e3,wt=1500,St=10;function bt(e){return new Promise(t=>setTimeout(t,e))}function _t(){let e=globalThis.crypto;if(e!==void 0&&typeof e.randomUUID=="function")return e.randomUUID();let t=new Uint8Array(16);if(e!==void 0&&typeof e.getRandomValues=="function")e.getRandomValues(t);else for(let i=0;i<16;i+=1)t[i]=Math.floor(Math.random()*256);t[6]=(t[6]??0)&15|64,t[8]=(t[8]??0)&63|128;let n=[...t].map(i=>i.toString(16).padStart(2,"0")).join("");return`${n.slice(0,8)}-${n.slice(8,12)}-${n.slice(12,16)}-${n.slice(16,20)}-${n.slice(20)}`}function cn(e){return Ct(e,{})}function Ct(e,t){let n=e.apiOrigin??yt,i=e.autoRetry??!0,s=e.rearm??"auto",l=t.client??new be({apiOrigin:n,sessionToken:e.clientToken,...e.fetch!==void 0?{fetch:e.fetch}:{}}),u=t.sleep??bt,d=t.uuid??_t,o=t.getTurnstileToken??Ve,c=ee(),p=!1,h=new AbortController,m=null,_=null,y=r=>{m=r,c.emit("view",r)},v=()=>m!==null?Promise.resolve(m):(_??(_=l.getSession().then(r=>(y(r),r)).finally(()=>{_=null})),_),C=async()=>{let r=await l.getSession();return y(r),r},P=(r,f)=>{m!==null&&y(Ae(m,r,f))},M=null,O=async r=>{let f=await v();!r&&f.consentRecorded||(await l.recordConsent(e.locale),m!==null&&y({...m,consentRecorded:!0}))},x=r=>r?.force===!0?O(!0):(M??(M=O(!1).finally(()=>{M=null})),M),V={client:l,ensureConsent:r=>x(r),sleep:u,uuid:d,onBusy:r=>{p||c.emit("busy",r)}},D=!1,G=r=>{if(r.status==="approved"||r.status==="rejected"||r.status==="review"){if(D)return;D=!0}c.emit("result",r)},ne=async r=>{let f=m;r&&(f=await C().catch(()=>m));let w=f===null?null:N(f);return w!==null&&!p&&c.emit("method",w),w!==null},a=null,b=!1,de=!1,fe=()=>te(V,(r,f)=>r.submitCheck("population_registry",{},{idempotencyKey:f})),pe=r=>r.nniBound===!0||T(r,"population_registry")?.nniFromDocument===!0,Ie=async()=>{try{let r=await v();if(R(r)||N(r)!==null||A(r)?.checkType!=="population_registry")return;if(!pe(r)&&T(r,"population_registry")?.nniFromDocument===void 0&&(r=await C()),!pe(r)){de=!0;return}await x(),P(await fe(),"population_registry");let f=m===null?void 0:T(m,"population_registry");if(f?.state==="input_required"&&f.reason==="registry_unavailable"&&f.attemptsRemaining>0){if(await u(gt),p)return;P(await fe(),"population_registry")}}catch(r){if(r instanceof U&&r.code==="method_required"){await C().catch(()=>{});return}b=!0;return}m!==null&&R(m)&&G(le(m,{autoRetry:i}))},me=()=>(a??(a=Ie()),a),He=async()=>{let f=(await v()).turnstileSiteKey;return f===null?{sandbox:!0}:{channel:"hosted_web",siteKey:f,turnstileToken:await o(f,{signal:h.signal})}},E=null,ve=Promise.resolve(),ie=0,Oe=async()=>{let r=await v();for(let f=0;f<St&&!p&&!R(r)&&A(r)===void 0&&r.steps.some(w=>w.state==="processing")&&(await u(wt),!p);f+=1)r=await C();return r},De=async(r,f,w)=>{let{capture:k}=r,S=()=>p,X=()=>r.alive&&r.attempt===w,he=g=>{X()&&k.setFlowState(g)},I=g=>{if(!S()&&(G(g),!!X())){if(g.status==="retry"&&s==="auto"){k.handle.retry();return}k.setFlowState("done")}};if(f.degraded==="NO_RECORDER"){let g=m?.sessionId??(await v().catch(()=>null))?.sessionId??"";I(L(g,"no_recorder"));return}he("uploading");try{if(await me(),S())return;let g=await v();if(!R(g)&&N(g)!==null){await ne(!1),I(L(g.sessionId,"method_required"));return}if(!R(g)){if(A(g)?.checkType==="population_registry"){I(L(g.sessionId,de?"nni_unbound":b?"network":"registry_unavailable"));return}if(await x(),S())return;let j=await xe({...V,artifacts:f,getAttestation:He,getView:()=>m??g,refreshView:C,applySubmit:P,onPhase:he,isCancelled:S});if(S())return;if(j.kind==="unavailable"){if(j.reason==="method_required"&&await ne(!0),S())return;I(L(g.sessionId,j.reason));return}j.kind==="resync"&&await C(),g=await Oe()}I(le(g,{autoRetry:i}))}catch{if(S())return;I(L(m?.sessionId??"","network"))}},Fe=(r,f)=>{let w=r.attempt;ie>0&&r.alive&&r.capture.setFlowState("uploading"),ie+=1,ve=ve.then(()=>De(r,f,w)).finally(()=>{ie-=1}).catch(()=>{})};return{consent:()=>x(),startRegistry:me,methodChoice:async()=>{if(p)return null;try{let r=await v();return p?null:N(r)}catch{return null}},selectMethod:async r=>{let f=await l.selectMethod(r);p||y(f)},mobileRequired:async()=>{if(p)return!1;try{let r=await v();if(p||R(r))return!1;let f=T(r,"liveness");return W(f)&&f?.requiresMobile===!0&&!Q()}catch{return!1}},liveness:r=>{E?.capture.handle.destroy(),E!==null&&(E.alive=!1);let f=ae(r,t.capture),w={capture:f,alive:!0,attempt:0};E=w,f.handle.on("state",S=>{S==="starting"&&(w.attempt+=1)}),f.handle.on("captured",S=>{Promise.resolve().then(()=>Fe(w,S))});let k=f.handle;return{get state(){return k.state},on:(S,X)=>k.on(S,X),start:S=>k.start(S),retry:()=>k.retry(),switchCamera:()=>k.switchCamera(),destroy:()=>{w.alive=!1,E===w&&(E=null),k.destroy()}}},voiceClips:async()=>{if(p)return null;try{let r=await v();return p?null:r.voice??null}catch{return null}},on:(r,f)=>c.on(r,f),destroy:()=>{p=!0,h.abort(),E!==null&&(E.alive=!1,E.capture.handle.destroy(),E=null),c.clear()}}}export{Ue as a,_e as b,At as c,Pt as d,xt as e,Mt as f,Vt as g,It as h,Ee as i,ze as j,Ge as k,Xe as l,ae as m,Yt as n,je as o,W as p,R as q,A as r,T as s,Ae as t,le as u,Ze as v,Je as w,et as x,nt as y,it as z,$ as A,te as B,xe as C,vt as D,Zt as E,Jt as F,ht as G,Ve as H,yt as I,gt as J,wt as K,St as L,_t as M,cn as N,Ct as O};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=["session.completed","session.failed","session.review_pending","session.expired","session.cancelled"],v="sekyro-verify",u="width=480,height=720,noopener=false",a=new Set(c);function E(e){if(e===null||typeof e!="object")return!1;let n=e;return n.source==="sekyro"&&n.version===1&&typeof n.sessionId=="string"&&typeof n.event=="string"&&a.has(n.event)}function l(e){let n=new URL(e.hostedUrl).origin,t=window.open(e.hostedUrl,v,u);if(!t)return{error:"popup_blocked"};let r=o=>{if(o.source!==t||o.origin!==n||!E(o.data))return;let{source:s,version:i,event:p,sessionId:d}=o.data;e.onEvent({source:s,version:i,event:p,sessionId:d})};return window.addEventListener("message",r),{close(){window.removeEventListener("message",r),t.close()}}}function f(e,n){return n.mode==="redirect"?(window.location.assign(e),{close(){}}):l({hostedUrl:e,onEvent:n.onEvent??(()=>{})})}export{c as a,l as b,f as c};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as l}from"./chunk-IYUOYINS.js";var h=["session_token_invalid","session_token_required","session_token_session_mismatch","session_not_found","rate_limit_exceeded","consent_required","session_not_active","check_not_found","check_already_finalized","check_out_of_order","check_type_unsupported","force_outcome_forbidden","attestation_invalid","inference_unavailable","mobile_required","nni_locked","method_required","method_locked","document_nni_mismatch","media_content_type_not_allowed","media_too_large","media_key_invalid","media_key_forbidden","media_not_found","media_unreadable","media_session_invalid","idempotency_key_collision","idempotency_in_progress","idempotency_key_required","validation_error","internal_error","unknown_error","upload_failed","network_error","timeout"];function R(t){return h.includes(t)}var d=class extends Error{constructor(e){super(e.message),this.name="HostedApiError",this.status=e.status,this.code=e.code,e.retryAfterSeconds!==void 0&&(this.retryAfterSeconds=e.retryAfterSeconds)}};var f=3e4,_=9e5,T=200,g=5,b=400;function p(t){return(t.split(";")[0]??"").trim()}var w={liveness:"clipKey",face_match:"imageKey",doc_ocr:"imageKey"},y=class{constructor(e){this.sessionId=null;this.bootstrap=null;this.apiOrigin=e.apiOrigin.replace(/\/+$/,""),this.sessionToken=e.sessionToken;let s=e.fetch;this.fetchImpl=(n,i)=>(s??globalThis.fetch).call(globalThis,n,i),this.requestTimeoutMs=e.requestTimeoutMs??f,this.retryAttempts=e.inProgressRetry?.attempts??g,this.retryDelayMs=e.inProgressRetry?.delayMs??b}async getSession(){let e=await this.api("GET","/hosted/session");return this.sessionId=e.sessionId,e}async recordConsent(e){return this.api("POST","/hosted/consent",{body:{locale:e}})}async selectMethod(e){let s=await this.api("POST","/hosted/method",{body:{method:e}});return this.sessionId=s.sessionId,s}async presign(e,s,n){let i=await this.ensureSessionId(),o={kind:e,contentType:p(s),contentLength:n};return this.api("POST",`/sessions/${i}/media`,{body:o})}async uploadToSpaces(e,s,n){let i;try{i=await this.fetchImpl(e,{method:"PUT",headers:{"content-type":p(n)},body:s,signal:AbortSignal.timeout(_)})}catch(o){throw m(o)}if(!i.ok){let o=(await i.text().catch(()=>"")).slice(0,T);throw new d({status:i.status,code:"upload_failed",message:`media upload failed (HTTP ${i.status})${o?`: ${o}`:""}`})}}async completeMedia(e){let s=await this.ensureSessionId();return this.api("POST",`/sessions/${s}/media/complete`,{body:{key:e}})}async submitCheck(e,s,n={}){let i=await this.ensureSessionId(),o={input:s,...n.forceOutcome!==void 0?{forceOutcome:n.forceOutcome}:{}};return this.api("POST",`/sessions/${i}/checks/${e}`,{body:o,...n.idempotencyKey!==void 0?{idempotencyKey:n.idempotencyKey}:{}})}async uploadAndSubmit(e,s,n,i={}){let o=p(s.contentType),a=await this.presign(s.kind,o,s.blob.size);await this.uploadToSpaces(a.uploadUrl,s.blob,o);let r=await this.completeMedia(a.key),c={...n,[w[e]]:r.key};return this.submitCheck(e,c,i)}async ensureSessionId(){return this.sessionId!==null?this.sessionId:(this.bootstrap??(this.bootstrap=this.getSession().finally(()=>{this.bootstrap=null})),(await this.bootstrap).sessionId)}async api(e,s,n={}){let i=`${this.apiOrigin}/api/v2${s}`,o={"x-session-token":this.sessionToken};n.body!==void 0&&(o["content-type"]="application/json"),n.idempotencyKey!==void 0&&(o["x-idempotency-key"]=n.idempotencyKey);try{let a=l.timezone();a!==void 0&&(o["sekyro-client-timezone"]=a)}catch{}try{let a=await l.deviceHeader();a!==void 0&&(o["sekyro-client-device"]=a)}catch{}for(let a=0;;a++){let r;try{r=await this.fetchImpl(i,{method:e,headers:o,...n.body!==void 0?{body:JSON.stringify(n.body)}:{},signal:AbortSignal.timeout(this.requestTimeoutMs)})}catch(u){throw m(u)}if(r.ok){let u;try{u=await r.json()}catch{throw new d({status:r.status,code:"unknown_error",message:`response body was not valid JSON (HTTP ${r.status})`})}return k(u)}let c=await S(r);if(c.code==="idempotency_in_progress"&&a<this.retryAttempts){this.retryDelayMs>0&&await P(this.retryDelayMs);continue}throw c}}};function m(t){if((typeof t=="object"&&t!==null?t.name:void 0)==="TimeoutError")return new d({status:0,code:"timeout",message:"request timed out"});let s=t instanceof Error?t.message:String(t);return new d({status:0,code:"network_error",message:`network request failed: ${s}`})}function k(t){return t!==null&&typeof t=="object"&&"success"in t&&"data"in t?t.data:t}async function S(t){let e="unknown_error",s=`request failed (HTTP ${t.status})`,n=Number.NaN;try{let a=await t.json();if(a!==null&&typeof a=="object"){let r=a;typeof r.code=="string"&&r.code.length>0&&(e=r.code),typeof r.message=="string"&&r.message.length>0&&(s=r.message),typeof r.retryAfterSeconds=="number"&&(n=r.retryAfterSeconds)}}catch{}let i=t.headers.get("retry-after"),o=i===null?Math.floor(n):Number.parseInt(i,10);return new d({status:t.status,code:e,message:s,...Number.isFinite(o)&&o>=0?{retryAfterSeconds:o}:{}})}function P(t){return new Promise(e=>setTimeout(e,t))}export{h as a,R as b,d as c,p as d,y as e};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as O}from"./chunk-IYUOYINS.js";import{c as R,d as x,f as C,h as k,j as L}from"./chunk-BMA36W2W.js";import{b as D,c as G,d as S,e as P}from"./chunk-FPXL63KZ.js";function V(a,e){return{video:{width:{ideal:1280,max:1280},height:{ideal:720,max:720},facingMode:e,frameRate:{ideal:24,max:30}},audio:!1}}function N(){return typeof window>"u"||typeof navigator>"u"?!1:window.innerWidth<=768||/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(String(navigator.userAgent??""))}var ae=.45,oe=.9,ce=.4;var Y=["video/mp4;codecs=avc1.42E01E,mp4a.40.2","video/mp4;codecs=avc1.42E01E","video/mp4","video/webm;codecs=vp9,opus","video/webm;codecs=vp9","video/webm;codecs=vp8,opus","video/webm;codecs=vp8","video/webm"];function F(){let a=globalThis.MediaRecorder;return!a||typeof a.isTypeSupported!="function"?"":Y.find(e=>a.isTypeSupported(e))??""}var T="video/webm";function H(a){if(a instanceof TypeError)return"insecure";switch(a?.name??""){case"NotAllowedError":case"PermissionDeniedError":return"permission";case"NotFoundError":case"DevicesNotFoundError":return"notfound";case"NotReadableError":case"TrackStartError":return"inuse";case"OverconstrainedError":case"ConstraintNotSatisfiedError":return"overconstrained";default:return"generic"}}function W(){let a=globalThis;if(typeof a.window<"u"&&a.window.isSecureContext===!1)return"INSECURE_CONTEXT";let e=a.navigator?.mediaDevices;return!e||typeof e.getUserMedia!="function"?"UNSUPPORTED":"OK"}function ue(){let a=globalThis.MediaRecorder;return typeof a<"u"&&typeof a.isTypeSupported=="function"}var I=8*1024*1024;function y(a){return a.size>I}function U(a){let e=(a||"").toLowerCase();return e.startsWith("image/jpeg")?"jpg":e.startsWith("video/mp4")?"mp4":(e.startsWith("video/webm"),"webm")}function de(a){let{token:e,clip:t,keyframe:r}=a,i=a.ts??Date.now(),n=t.type||T,s=r.type||"image/jpeg",o=new FormData;return o.append("token",e),o.append("clip",new File([t],`liveness-${i}.${U(n)}`,{type:n})),o.append("keyframe",new File([r],`keyframe-${i}.${U(s)}`,{type:s})),o}var j=2500,$=1e3,J=1500,K=25e5,f=class extends Error{constructor(e,t){super(t??e),this.kind=e}},g=class{constructor(){this.recorder=null;this.stream=null;this.chunks=[];this.bytes=0;this.hardStopTimer=null;this.graceTimer=null;this.settled=!1}clearTimers(){this.hardStopTimer&&clearTimeout(this.hardStopTimer),this.graceTimer&&clearTimeout(this.graceTimer),this.hardStopTimer=null,this.graceTimer=null}record(e,t){if(this.recorder&&!this.settled)return Promise.reject(new f("generic","record() called while a recording is in flight \u2014 ClipRecorder handles one recording at a time"));this.recorder=null,this.clearTimers(),this.chunks=[],this.bytes=0,this.settled=!1,this.stream=e;let r=F();return new Promise((i,n)=>{let s;try{s=new MediaRecorder(e,r?{mimeType:r,videoBitsPerSecond:K}:{videoBitsPerSecond:K})}catch{try{s=new MediaRecorder(e)}catch{n(new f("unsupported"));return}}this.recorder=s;let o=d=>{this.settled||(this.settled=!0,this.clearTimers(),d())},c=()=>o(()=>{let d=s.mimeType||r||T,l=new Blob(this.chunks,{type:d});if(y(l)){n(new f("cliptoolarge"));return}i({blob:l,mimeType:d})});s.ondataavailable=d=>{d.data&&d.data.size&&(this.chunks.push(d.data),this.bytes+=d.data.size,this.bytes>I&&o(()=>{try{s.state!=="inactive"&&s.stop()}catch{}n(new f("cliptoolarge"))}))},s.onstop=c,s.onerror=()=>o(()=>n(new f("generic")));try{s.start($)}catch{o(()=>n(new f("generic")));return}this.hardStopTimer=setTimeout(()=>{if(this.settled||t?.(),s.state!=="inactive"){try{s.stop()}catch{c()}this.graceTimer=setTimeout(c,J)}else c()},j)})}stop(e){this.clearTimers();let t=this.recorder;if(t&&t.state!=="inactive")try{t.stop()}catch{}this.recorder=null,(e??this.stream)?.getTracks().forEach(i=>i.stop()),this.stream=null}};function B(a,e,t=.92){return new Promise((r,i)=>{let n=a.videoWidth,s=a.videoHeight;if(!n||!s){i(new Error("video-not-ready"));return}let o=e.getContext("2d");if(!o){i(new Error("no-2d-context"));return}e.width=n,e.height=s,o.scale(-1,1),o.drawImage(a,-n,0),e.toBlob(c=>c?r(c):i(new Error("empty-keyframe")),"image/jpeg",t)})}var ee=Object.freeze({LOW:Object.freeze({cadenceHz:5,inputWidth:128,animations:"reduced"}),MID:Object.freeze({cadenceHz:8,inputWidth:160,animations:"full"}),HIGH:Object.freeze({cadenceHz:10,inputWidth:192,animations:"full"})}),te=new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11]);function z(){let a=typeof navigator>"u"?void 0:navigator,e=!1;try{e=typeof WebAssembly<"u"&&WebAssembly.validate(te)}catch{e=!1}return{cores:a?.hardwareConcurrency??2,memGB:a?.deviceMemory??null,simd:e}}function X(a){let e=a.memGB??4;return a.cores<4||e<3?"LOW":a.cores>=8&&e>=6?"HIGH":"MID"}var A=["LOW","MID","HIGH"],b=class{constructor(e){this.samples=[];this.adjusted=!1;this.current=e}get deviceClass(){return this.current}get profile(){return ee[this.current]}record(e){if(this.adjusted||this.samples.length>=10)return this.current;if(this.samples.push(e),this.samples.length<10)return this.current;this.adjusted=!0;let t=this.samples.reduce((r,i)=>r+i,0)/this.samples.length;if(t>70)this.current="LOW";else if(t<25){let r=A.indexOf(this.current);this.current=A[Math.min(A.length-1,r+1)]??this.current}return this.current}};var E=class{constructor(e){this.timer=null;this.frameCallbackId=null;this.running=!1;this.busy=!1;this.lastRunAt=Number.NEGATIVE_INFINITY;this.degradeCount=0;this.durations=[];this.options=e,this.periodMs=1e3/e.cadenceHz}start(){if(this.running)return;this.running=!0;let e=this.options.video;if(typeof e?.requestVideoFrameCallback=="function"){let t=()=>{this.running&&(this.frameCallbackId=e.requestVideoFrameCallback?.(()=>{this.frameCallbackId=null,this.now()-this.lastRunAt>=this.periodMs&&this.tick(),t()}))};t();return}this.timer=setInterval(()=>this.tick(),this.periodMs)}stop(){this.running=!1,this.timer!==null&&(clearInterval(this.timer),this.timer=null),this.frameCallbackId!==null&&(this.options.video?.cancelVideoFrameCallback?.(this.frameCallbackId),this.frameCallbackId=null)}stats(){let e=this.durations.length===0?0:this.durations.reduce((t,r)=>t+r,0)/this.durations.length;return{degradeCount:this.degradeCount,meanDetectMs:e}}now(){return this.options.now?.()??performance.now()}tick(){if(!this.running)return;if(this.busy){this.degradeCount+=1;return}this.busy=!0,this.lastRunAt=this.now();let e=this.now();this.options.run().catch(()=>{}).finally(()=>{this.busy=!1,this.durations.push(this.now()-e),this.durations.length>20&&this.durations.shift()})}};var re=Object.freeze({Q_GOOD:.6,RISE_PER_S:.9,FALL_PER_S:.25,CENTER_FALLOFF_IOD_MULTIPLE:1.35,BOX_EYE_OFFSET_FACTOR:.12,IOD_BOX_PROXY_FACTOR:.42,SIZE_IDEAL_MIN:.12,SIZE_IDEAL_MAX:.3,SIZE_HARD_MIN:.08,SIZE_HARD_MAX:.45,ROLL_MAX_DEG:25,MOTION_VELOCITY_IOD_PER_S:.5,MOTION_TAU_S:.4,MOTION_DEADBAND_IOD_PER_S:.1,READY_WINDOW_MS:500,GUIDE_SIZE:.6,GUIDE_CENTERING:.6,GUIDE_POSE:.6,GUIDE_EXPOSURE:.6,GUIDE_SHARPNESS:.35,GUIDE_MOTION:.5}),v=a=>Math.min(1,Math.max(0,a)),_=class{constructor(e={}){this.p=0;this.lastTickAt=null;this.lastCenter=null;this.velocityEma=0;this.qWindow=[];this.lastSizeDirection="ideal";this.t={...re,...e}}reset(){this.p=0,this.lastTickAt=null,this.lastCenter=null,this.velocityEma=0,this.qWindow=[],this.lastSizeDirection="ideal"}tick(e){let{observation:t,quality:r,timestampMs:i}=e,n=this.lastTickAt===null?0:Math.max(0,(i-this.lastTickAt)/1e3);this.lastTickAt=i;let s={presence:0,centering:0,size:0,pose:0,sharpness:v(r.sharpness),exposure:v(r.exposure),motion:1};if(t!==null){s.presence=1;let l=this.geometryOf(t,e);s.centering=l.centering,s.size=l.size,s.pose=l.pose,s.motion=this.motionOf(l.center,l.iod,i)}else this.lastCenter=null,this.velocityEma=0;let o=Math.min(s.presence,s.centering,s.size,s.pose,s.sharpness,s.exposure,s.motion);this.p=v(this.p+(o>=this.t.Q_GOOD?this.t.RISE_PER_S*o:-this.t.FALL_PER_S*(1-o))*n),this.qWindow.push({at:i,q:o}),this.qWindow=this.qWindow.filter(l=>l.at>=i-this.t.READY_WINDOW_MS);let c=this.qWindow.map(l=>l.q);if(c.length>=3){let l=Math.min(...c),u=c.indexOf(l);c=c.filter((h,m)=>m!==u)}let d=this.p>=1&&c.every(l=>l>=this.t.Q_GOOD);return{at:i,q:o,p:this.p,factors:s,guidanceKey:this.guidanceOf(s,r,o),ready:d,accepted:o>=this.t.Q_GOOD}}geometryOf(e,t){let{box:r,eyes:i}=e,n={x:t.frameWidth/2,y:t.frameHeight/2},s=Math.min(t.frameWidth,t.frameHeight),o=i!==void 0?{x:(i.left.x+i.right.x)/2,y:(i.left.y+i.right.y)/2}:{x:r.x+r.width/2,y:r.y+r.height/2-this.t.BOX_EYE_OFFSET_FACTOR*r.height},c=i!==void 0?Math.hypot(i.right.x-i.left.x,i.right.y-i.left.y):this.t.IOD_BOX_PROXY_FACTOR*r.width,d=Math.hypot(o.x-n.x,o.y-n.y),l=c<=0?0:1-v(d/(this.t.CENTER_FALLOFF_IOD_MULTIPLE*c)),u=s>0?c/s:0;this.lastSizeDirection=u<this.t.SIZE_IDEAL_MIN?"far":u>this.t.SIZE_IDEAL_MAX?"close":"ideal";let h;u<this.t.SIZE_HARD_MIN||u>this.t.SIZE_HARD_MAX?h=0:u<this.t.SIZE_IDEAL_MIN?h=(u-this.t.SIZE_HARD_MIN)/(this.t.SIZE_IDEAL_MIN-this.t.SIZE_HARD_MIN):u>this.t.SIZE_IDEAL_MAX?h=(this.t.SIZE_HARD_MAX-u)/(this.t.SIZE_HARD_MAX-this.t.SIZE_IDEAL_MAX):h=1;let m=1;if(i!==void 0){let p=Math.abs(Math.atan2(i.right.y-i.left.y,i.right.x-i.left.x))*180/Math.PI;m=1-v(p/this.t.ROLL_MAX_DEG)}return{center:o,iod:c,centering:l,size:h,pose:m}}motionOf(e,t,r){let i=this.lastCenter;if(this.lastCenter={...e,at:r},i===null||t<=0)return 1;let n=(r-i.at)/1e3;if(n<=0)return 1;let s=Math.hypot(e.x-i.x,e.y-i.y)/t/n;s<=this.t.MOTION_DEADBAND_IOD_PER_S&&(s=0);let o=1-Math.exp(-n/this.t.MOTION_TAU_S);return this.velocityEma=o*s+(1-o)*this.velocityEma,1-v(this.velocityEma/this.t.MOTION_VELOCITY_IOD_PER_S)}guidanceOf(e,t,r){if(e.presence<=0)return"fcv3.no_face";if(e.size<this.t.GUIDE_SIZE)return this.sizeGuidance();if(e.centering<this.t.GUIDE_CENTERING)return"fcv3.center_face";if(e.pose<this.t.GUIDE_POSE)return"fcv3.straighten";if(e.exposure<this.t.GUIDE_EXPOSURE)return this.exposureGuidance(t);if(e.sharpness<this.t.GUIDE_SHARPNESS)return"fcv3.steady_camera";if(e.motion<this.t.GUIDE_MOTION)return"fcv3.hold_still";if(r<this.t.Q_GOOD){let i=[["size",this.sizeGuidance()],["centering","fcv3.center_face"],["pose","fcv3.straighten"],["exposure",this.exposureGuidance(t)],["sharpness","fcv3.steady_camera"],["motion","fcv3.hold_still"]],n=i[0];for(let s of i)e[s[0]]<e[n[0]]&&(n=s);return n[1]}return"fcv3.hold_still"}sizeGuidance(){return this.lastSizeDirection==="far"?"fcv3.come_closer":"fcv3.move_back"}exposureGuidance(e){let t=(D[0]+D[1])/2;return e.meanLuma<t?"fcv3.more_light":"fcv3.too_bright"}};var w=4,ie=600,q={sharpness:1,exposure:1,meanLuma:128},Z=class{constructor(e){this.handlers=new Map;this.stateValue="idle";this.attempt=0;this.stream=null;this.detector=null;this.scheduler=null;this.gate=null;this.profiler=null;this.lastGuidanceKey=null;this.positioningStartedAt=0;this.cameraRequestedAt=null;this.cameraReadyAt=null;this.faceAcquiredAt=null;this.captureCompleteAt=null;this.fired=!1;this.carriedDegrade=0;this.carriedMeans=[];this.snapshotCanvas=null;this.options=e,this.facing=e.facingMode??"user"}get state(){return this.stateValue}on(e,t){let r=this.handlers.get(e);return r||(r=new Set,this.handlers.set(e,r)),r.add(t),()=>r?.delete(t)}emit(e,t){for(let r of[...this.handlers.get(e)??[]])r(t)}async start(e={}){e.facingMode!==void 0&&(this.facing=e.facingMode),this.attempt+=1,await this.run(this.attempt)}get facingMode(){return this.facing}retry(){this.teardownAttempt(),this.start()}switchCamera(){this.facing=this.facing==="user"?"environment":"user",this.teardownAttempt(),this.start()}destroy(){this.attempt+=1,this.teardownAttempt(),this.detector?.destroy(),this.detector=null,this.options.video.srcObject=null}async run(e){let{options:t}=this;this.fired=!1,this.lastGuidanceKey=null,this.carriedDegrade=0,this.carriedMeans=[],this.cameraRequestedAt=null,this.cameraReadyAt=null,this.faceAcquiredAt=null,this.captureCompleteAt=null,this.setState("preflight");let r=W();if(r!=="OK"){this.fail(r==="INSECURE_CONTEXT"?"insecure":"unsupported");return}this.setState("acquiring");let i=t.getUserMedia??(c=>navigator.mediaDevices.getUserMedia(c)),n;this.cameraRequestedAt=this.now();try{n=await i(V(N(),this.facing))}catch(c){if(e!==this.attempt)return;this.fail(H(c));return}if(O(n),e!==this.attempt){for(let c of n.getTracks())c.stop();return}if(this.stream=n,t.video.srcObject=n,await t.video.play().catch(()=>{}),e!==this.attempt)return;if(this.cameraReadyAt=this.now(),this.detector===null){let c={...t.v3AssetBaseUrl!==void 0?{v3AssetBaseUrl:t.v3AssetBaseUrl}:{}},d=t.createDetector??(t.createWorker!==void 0&&k()?async()=>{try{return await L.create(t.createWorker(),c)}catch{return C(c)}}:()=>C(c)),l;try{l=await d()}catch(u){e===this.attempt&&this.fail("models",u instanceof R?u.fallbackReason:se(u));return}if(e!==this.attempt){l.destroy();return}this.detector=l,this.options.telemetry?.({type:"detector",tier:l.tier,...l.fallbackReason!==void 0?{fallbackReason:l.fallbackReason}:{}})}let s=z(),o=t.deviceClass??X(s);this.profiler=new b(o),this.options.telemetry?.({type:"profile",staticClass:o,activeClass:this.profiler.deviceClass,simd:s.simd}),this.gate=new _(t.thresholds??{}),this.setState("positioning"),this.positioningStartedAt=this.now(),this.startScheduler(e)}get animations(){return this.profiler?.profile.animations??"full"}startScheduler(e){let t=this.profiler;if(t===null)return;if(this.scheduler!==null){let i=this.scheduler.stats();this.carriedDegrade+=i.degradeCount,i.meanDetectMs>0&&this.carriedMeans.push(i.meanDetectMs),this.scheduler.stop()}let r=new E({cadenceHz:t.profile.cadenceHz,video:this.options.video,...this.options.now!==void 0?{now:this.options.now}:{},run:()=>this.detectTick(e)});this.scheduler=r,r.start()}async detectTick(e){let{detector:t,gate:r,profiler:i}=this,n=this.options.video;if(e!==this.attempt||t===null||r===null||i===null||this.fired||n.videoWidth===0)return;let s=i.deviceClass,o=this.now(),c=this.options.makeSnapshot?.(n,i.profile.inputWidth)??this.defaultSnapshot(n,i.profile.inputWidth),d=null;try{d=await t.detect(c,n.videoWidth,n.videoHeight)}catch{d=null}if(e!==this.attempt||this.fired)return;i.record(this.now()-o),i.deviceClass!==s&&this.startScheduler(e);let l=d===null?q:this.options.measureQuality?.(n,d.box)??this.defaultQuality(n,d.box),u=r.tick({timestampMs:this.now(),observation:d,quality:l,frameWidth:n.videoWidth,frameHeight:n.videoHeight});u.accepted&&this.faceAcquiredAt===null&&(this.faceAcquiredAt=u.at),this.emit("checks",{engine:"v3",progress:u.p,q:u.q,factors:u.factors,attempt:e}),u.guidanceKey!==this.lastGuidanceKey&&(this.lastGuidanceKey=u.guidanceKey,this.emit("guidance",{key:u.guidanceKey})),u.ready&&this.stateValue==="positioning"&&(this.fired=!0,this.captureCompleteAt=u.at,this.fire(e))}async fire(e){let{options:t}=this,r=this.scheduler;r?.stop();let i=r?.stats()??{degradeCount:0,meanDetectMs:0},n=[...this.carriedMeans,...i.meanDetectMs>0?[i.meanDetectMs]:[]];t.telemetry?.({type:"fired",timeToGateMs:this.now()-this.positioningStartedAt,degradeCount:this.carriedDegrade+i.degradeCount,meanDetectMs:n.length===0?0:n.reduce((p,M)=>p+M,0)/n.length});let s=t.mode??"clip",o=this.collectKeyframes(e);if(s==="photo"){let p=await o;if(e!==this.attempt)return;if(p===null){this.fail("generic");return}this.finishCapture({keyframe:p.blob,previewDataUrl:p.previewDataUrl});return}this.setState("recording");let c=t.createRecorder?.()??new g,d=this.stream;if(d===null){this.fail("generic");return}let l=null;o.then(p=>{l=p},()=>{});let u=()=>{e===this.attempt&&l!==null&&this.emit("frozen",{previewDataUrl:l.previewDataUrl})},h,m;try{[h,m]=await Promise.all([c.record(d,u),o])}catch(p){if(e!==this.attempt)return;let M=p.kind;this.fail(M==="cliptoolarge"?"cliptoolarge":"recorder");return}if(e===this.attempt){if(y(h.blob)){this.fail("cliptoolarge");return}if(m===null){this.fail("generic");return}this.finishCapture({clip:h.blob,clipMimeType:h.mimeType,keyframe:m.blob,previewDataUrl:m.previewDataUrl})}}async collectKeyframes(e){let t=this.options.captureKeyframe??(n=>this.defaultKeyframe(n)),r=ie/(w-1),i=null;for(let n=0;n<w;n+=1){if(e!==this.attempt)return i;try{let s=await t(this.options.video);(i===null||s.sharpness>i.sharpness)&&(i=s)}catch{}n<w-1&&await ne(r)}return i}finishCapture(e){this.stopTracks(),this.setState("captured");let t=this.captureTiming();this.emit("artifacts",t===void 0?e:{...e,timing:t})}captureTiming(){let e=(s,o)=>s!==null&&o!==null?Math.max(0,Math.round(o-s)):void 0,t=e(this.cameraRequestedAt,this.cameraReadyAt),r=e(this.cameraReadyAt,this.faceAcquiredAt),i=e(this.faceAcquiredAt,this.captureCompleteAt),n={...t!==void 0?{cameraReadyMs:t}:{},...r!==void 0?{faceAcquireMs:r}:{},...i!==void 0?{captureMs:i}:{}};return Object.keys(n).length>0?n:void 0}async defaultKeyframe(e){let t=document.createElement("canvas"),r=await B(e,t),i=t.toDataURL("image/jpeg",.92);return{blob:r,previewDataUrl:i,sharpness:this.centerSharpness(e)}}defaultSnapshot(e,t){try{let r=this.snapshotCanvas??document.createElement("canvas"),i=t/Math.max(1,e.videoWidth),n=Math.max(1,Math.round(e.videoWidth*i)),s=Math.max(1,Math.round(e.videoHeight*i)),o=r.getContext("2d");return o===null?e:(r.width!==n&&(r.width=n),r.height!==s&&(r.height=s),o.drawImage(e,0,0,n,s),this.snapshotCanvas=r,r)}catch{return e}}defaultQuality(e,t){let r=Q(e,t,96);return r===null?q:{sharpness:S(r),exposure:P(r),meanLuma:G(r)}}centerSharpness(e){let t=x(e),r=Q(e,{x:t.width*.25,y:t.height*.25,width:t.width*.5,height:t.height*.5},96);return r===null?0:S(r)}teardownAttempt(){this.scheduler?.stop(),this.scheduler=null,this.gate=null,this.stopTracks()}stopTracks(){if(this.stream!==null){for(let e of this.stream.getTracks())e.stop();this.stream=null}}fail(e,t){this.scheduler?.stop(),this.stopTracks(),this.setState("error"),this.options.telemetry?.({type:"error",kind:e,...t!==void 0?{fallbackReason:t}:{}}),this.emit("error",{kind:e,retryable:e!=="insecure"&&e!=="unsupported"})}setState(e){this.stateValue=e,this.emit("statechange",{state:e})}now(){return this.options.now?.()??performance.now()}};function ne(a){return new Promise(e=>setTimeout(e,a))}function se(a){return a instanceof Error?a.message:String(a)}function Q(a,e,t){try{let r=document.createElement("canvas"),i=t/Math.max(1,e.width);r.width=Math.max(1,Math.round(e.width*i)),r.height=Math.max(1,Math.round(e.height*i));let n=r.getContext("2d");return n===null?null:(n.drawImage(a,e.x,e.y,e.width,e.height,0,0,r.width,r.height),n.getImageData(0,0,r.width,r.height))}catch{return null}}export{V as a,N as b,ae as c,oe as d,ce as e,Y as f,F as g,T as h,H as i,W as j,ue as k,I as l,y as m,U as n,de as o,j as p,$ as q,J as r,K as s,f as t,g as u,B as v,ee as w,z as x,X as y,b as z,E as A,re as B,_ as C,w as D,ie as E,Z as F};
|
package/dist/client/index.cjs
CHANGED
|
@@ -1,357 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/client/index.ts
|
|
21
|
-
var client_exports = {};
|
|
22
|
-
__export(client_exports, {
|
|
23
|
-
HostedApiError: () => HostedApiError,
|
|
24
|
-
HostedClient: () => HostedClient,
|
|
25
|
-
KNOWN_CODES: () => KNOWN_CODES,
|
|
26
|
-
isKnownCode: () => isKnownCode,
|
|
27
|
-
stripMimeParams: () => stripMimeParams
|
|
28
|
-
});
|
|
29
|
-
module.exports = __toCommonJS(client_exports);
|
|
30
|
-
|
|
31
|
-
// src/client/errors.ts
|
|
32
|
-
var KNOWN_CODES = [
|
|
33
|
-
// auth / addressing (session-token.guard, controllers' own-session guards)
|
|
34
|
-
"session_token_invalid",
|
|
35
|
-
// 401 — missing/invalid/expired/revoked token
|
|
36
|
-
"session_token_required",
|
|
37
|
-
// 403 — non-session-token principal on a hosted route (defensive)
|
|
38
|
-
"session_token_session_mismatch",
|
|
39
|
-
// 403 — route :id differs from the token's bound session
|
|
40
|
-
"session_not_found",
|
|
41
|
-
// 404 — token's session missing/foreign (findOwned)
|
|
42
|
-
// rate limiting (session-token.guard — per-token + per-IP buckets)
|
|
43
|
-
"rate_limit_exceeded",
|
|
44
|
-
// 429 + Retry-After header (seconds)
|
|
45
|
-
// consent gate (Task 0 — submit AND media presign/complete)
|
|
46
|
-
"consent_required",
|
|
47
|
-
// 409 — record consent before uploading/submitting
|
|
48
|
-
// submit flow (sessions.service.submitCheck)
|
|
49
|
-
"session_not_active",
|
|
50
|
-
// 409 — session is terminal/review; no further submissions
|
|
51
|
-
"check_not_found",
|
|
52
|
-
// 404 — session has no such checkType
|
|
53
|
-
"check_already_finalized",
|
|
54
|
-
// 409 — re-submit of a terminal/review check
|
|
55
|
-
"check_out_of_order",
|
|
56
|
-
// 409 — only the frontier check accepts a submission
|
|
57
|
-
"check_type_unsupported",
|
|
58
|
-
// 400 — no registered executor for the checkType
|
|
59
|
-
"force_outcome_forbidden",
|
|
60
|
-
// 403 — forceOutcome on a live session (plan D-C)
|
|
61
|
-
"attestation_invalid",
|
|
62
|
-
// 400 (Task 1) — Turnstile/attestation failed; reload and retry
|
|
63
|
-
"nni_locked",
|
|
64
|
-
// 409 (Plan A Task 2) — session is bound to a server-provided NNI; a different client NNI was sent
|
|
65
|
-
// media (media.service)
|
|
66
|
-
"media_content_type_not_allowed",
|
|
67
|
-
// 400 — outside the per-kind MIME allowlist
|
|
68
|
-
"media_too_large",
|
|
69
|
-
// 413 — over the per-kind byte cap
|
|
70
|
-
"media_key_invalid",
|
|
71
|
-
// 400 — not a well-formed pending key
|
|
72
|
-
"media_key_forbidden",
|
|
73
|
-
// 403 — key names another session's pending space
|
|
74
|
-
"media_not_found",
|
|
75
|
-
// 404 — no uploaded object behind the key
|
|
76
|
-
"media_unreadable",
|
|
77
|
-
// 422 — magic-byte sniff rejected the object
|
|
78
|
-
"media_session_invalid",
|
|
79
|
-
// 400 — sessionId unusable as a key segment (never in practice)
|
|
80
|
-
// idempotency (idempotency.interceptor — Task 2)
|
|
81
|
-
"idempotency_key_collision",
|
|
82
|
-
// 409 — same key, different body hash
|
|
83
|
-
"idempotency_in_progress",
|
|
84
|
-
// 409 — concurrent identical request mid-flight (client auto-retries)
|
|
85
|
-
"idempotency_key_required",
|
|
86
|
-
// 400 — only on @Idempotent({required:true}) routes (none hosted yet)
|
|
87
|
-
// validation + fallback
|
|
88
|
-
"validation_error",
|
|
89
|
-
// 400 — ValibotPipe rejection (camelToSnake of 'ValidationError')
|
|
90
|
-
"internal_error",
|
|
91
|
-
// 500 — unhandled server error
|
|
92
|
-
// client-synthesized (never sent by the backend)
|
|
93
|
-
"unknown_error",
|
|
94
|
-
// unparseable (non-JSON) response body — error OR 2xx (defensive)
|
|
95
|
-
"upload_failed",
|
|
96
|
-
// the bare Spaces PUT answered non-2xx
|
|
97
|
-
"network_error",
|
|
98
|
-
// fetch rejected: connection drop / DNS / CORS / mid-transfer (status 0)
|
|
99
|
-
"timeout"
|
|
100
|
-
// AbortSignal.timeout fired — API calls 30 s default, Spaces PUT 15 min (status 0)
|
|
101
|
-
];
|
|
102
|
-
function isKnownCode(code) {
|
|
103
|
-
return KNOWN_CODES.includes(code);
|
|
104
|
-
}
|
|
105
|
-
var HostedApiError = class extends Error {
|
|
106
|
-
constructor(args) {
|
|
107
|
-
super(args.message);
|
|
108
|
-
this.name = "HostedApiError";
|
|
109
|
-
this.status = args.status;
|
|
110
|
-
this.code = args.code;
|
|
111
|
-
if (args.retryAfterSeconds !== void 0) this.retryAfterSeconds = args.retryAfterSeconds;
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// src/client/hosted-client.ts
|
|
116
|
-
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
117
|
-
var SPACES_PUT_TIMEOUT_MS = 9e5;
|
|
118
|
-
var UPLOAD_ERROR_SNIPPET_CHARS = 200;
|
|
119
|
-
var IN_PROGRESS_RETRY_ATTEMPTS = 5;
|
|
120
|
-
var IN_PROGRESS_RETRY_DELAY_MS = 400;
|
|
121
|
-
function stripMimeParams(contentType) {
|
|
122
|
-
return (contentType.split(";")[0] ?? "").trim();
|
|
123
|
-
}
|
|
124
|
-
var MEDIA_INPUT_FIELD = {
|
|
125
|
-
liveness: "clipKey",
|
|
126
|
-
face_match: "imageKey",
|
|
127
|
-
doc_ocr: "imageKey"
|
|
128
|
-
};
|
|
129
|
-
var HostedClient = class {
|
|
130
|
-
constructor(opts) {
|
|
131
|
-
this.sessionId = null;
|
|
132
|
-
this.bootstrap = null;
|
|
133
|
-
this.apiOrigin = opts.apiOrigin.replace(/\/+$/, "");
|
|
134
|
-
this.sessionToken = opts.sessionToken;
|
|
135
|
-
const impl = opts.fetch;
|
|
136
|
-
this.fetchImpl = (input, init) => (impl ?? globalThis.fetch).call(globalThis, input, init);
|
|
137
|
-
this.requestTimeoutMs = opts.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
138
|
-
this.retryAttempts = opts.inProgressRetry?.attempts ?? IN_PROGRESS_RETRY_ATTEMPTS;
|
|
139
|
-
this.retryDelayMs = opts.inProgressRetry?.delayMs ?? IN_PROGRESS_RETRY_DELAY_MS;
|
|
140
|
-
}
|
|
141
|
-
/** Token-addressed bootstrap — the hosted page's first call. Caches the sessionId. */
|
|
142
|
-
async getSession() {
|
|
143
|
-
const view = await this.api("GET", "/hosted/session");
|
|
144
|
-
this.sessionId = view.sessionId;
|
|
145
|
-
return view;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Record consent once (201; an idempotent replay answers 200 with
|
|
149
|
-
* `alreadyRecorded: true`). Until recorded, media presign/complete AND check
|
|
150
|
-
* submission answer 409 `consent_required`.
|
|
151
|
-
*/
|
|
152
|
-
async recordConsent(locale) {
|
|
153
|
-
return this.api("POST", "/hosted/consent", { body: { locale } });
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Presign a Spaces PUT for a new pending object. `contentType` is stripped of
|
|
157
|
-
* MIME parameters defensively (the allowlist holds bare types; the engine's
|
|
158
|
-
* `clipMimeType` carries codec params) — PUT with the SAME bare type, or the
|
|
159
|
-
* signed-header check rejects the upload.
|
|
160
|
-
*/
|
|
161
|
-
async presign(kind, contentType, contentLength) {
|
|
162
|
-
const id = await this.ensureSessionId();
|
|
163
|
-
const body = { kind, contentType: stripMimeParams(contentType), contentLength };
|
|
164
|
-
return this.api("POST", `/sessions/${id}/media`, { body });
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Bare PUT to the presigned Spaces URL: EXACTLY the signed headers — the
|
|
168
|
-
* declared Content-Type and the blob body. NO `x-session-token` (the
|
|
169
|
-
* signature IS the credential; an extra header breaks nothing but leaks the
|
|
170
|
-
* token to a third-party host) and no JSON envelope.
|
|
171
|
-
*
|
|
172
|
-
* `contentType` is param-stripped here too (idempotent for bare types): the
|
|
173
|
-
* presign strips internally, so a caller composing manually with the engine's
|
|
174
|
-
* parameterful `clipMimeType` would otherwise sign `video/webm` but PUT
|
|
175
|
-
* `video/webm;codecs=…` — a signed-header mismatch S3 rejects at runtime.
|
|
176
|
-
*
|
|
177
|
-
* Timeout: 15 min (`AbortSignal.timeout(900_000)`, matching the presign TTL)
|
|
178
|
-
* rather than the API calls' 30 s — a 15 MB clip on a slow link legitimately
|
|
179
|
-
* exceeds 30 s, but a stalled in-flight socket must still be bounded (the TTL
|
|
180
|
-
* alone only bounds when a PUT can START).
|
|
181
|
-
*
|
|
182
|
-
* Failures are ALWAYS typed: non-2xx throws {@link HostedApiError} with the
|
|
183
|
-
* client-synthesized `upload_failed` (message carries a truncated S3 body
|
|
184
|
-
* snippet — e.g. SignatureDoesNotMatch); a mid-transfer drop → `network_error`;
|
|
185
|
-
* the timeout → `timeout`.
|
|
186
|
-
*/
|
|
187
|
-
async uploadToSpaces(uploadUrl, blob, contentType) {
|
|
188
|
-
let res;
|
|
189
|
-
try {
|
|
190
|
-
res = await this.fetchImpl(uploadUrl, {
|
|
191
|
-
method: "PUT",
|
|
192
|
-
headers: { "content-type": stripMimeParams(contentType) },
|
|
193
|
-
body: blob,
|
|
194
|
-
signal: AbortSignal.timeout(SPACES_PUT_TIMEOUT_MS)
|
|
195
|
-
});
|
|
196
|
-
} catch (err) {
|
|
197
|
-
throw asTransportError(err);
|
|
198
|
-
}
|
|
199
|
-
if (!res.ok) {
|
|
200
|
-
const snippet = (await res.text().catch(() => "")).slice(0, UPLOAD_ERROR_SNIPPET_CHARS);
|
|
201
|
-
throw new HostedApiError({
|
|
202
|
-
status: res.status,
|
|
203
|
-
code: "upload_failed",
|
|
204
|
-
message: `media upload failed (HTTP ${res.status})${snippet ? `: ${snippet}` : ""}`
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/** Finalize an upload: the backend sniffs + promotes; returns the PROMOTED key to submit. */
|
|
209
|
-
async completeMedia(key) {
|
|
210
|
-
const id = await this.ensureSessionId();
|
|
211
|
-
return this.api("POST", `/sessions/${id}/media/complete`, {
|
|
212
|
-
body: { key }
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Submit one check. Sends `{input, forceOutcome?}` (SubmitCheckSchema) and the
|
|
217
|
-
* optional `x-idempotency-key` header. Returns the whitelisted SessionView.
|
|
218
|
-
*/
|
|
219
|
-
async submitCheck(checkType, input, opts = {}) {
|
|
220
|
-
const id = await this.ensureSessionId();
|
|
221
|
-
const body = {
|
|
222
|
-
input,
|
|
223
|
-
...opts.forceOutcome !== void 0 ? { forceOutcome: opts.forceOutcome } : {}
|
|
224
|
-
};
|
|
225
|
-
return this.api("POST", `/sessions/${id}/checks/${checkType}`, {
|
|
226
|
-
body,
|
|
227
|
-
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {}
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Convenience composition: presign → PUT → complete → submit, passing the
|
|
232
|
-
* PROMOTED key (from complete's response — never the pending presign key)
|
|
233
|
-
* into the check's media input field (`clipKey` for liveness, `imageKey` for
|
|
234
|
-
* face_match/doc_ocr). `extraInput` carries the remaining input fields
|
|
235
|
-
* (liveness `attestation`, doc_ocr `side`). The media content type is
|
|
236
|
-
* param-stripped ONCE and used for both presign and PUT, so the signed
|
|
237
|
-
* header always matches.
|
|
238
|
-
*/
|
|
239
|
-
async uploadAndSubmit(checkType, media, extraInput, opts = {}) {
|
|
240
|
-
const bareType = stripMimeParams(media.contentType);
|
|
241
|
-
const presigned = await this.presign(media.kind, bareType, media.blob.size);
|
|
242
|
-
await this.uploadToSpaces(presigned.uploadUrl, media.blob, bareType);
|
|
243
|
-
const completed = await this.completeMedia(presigned.key);
|
|
244
|
-
const input = {
|
|
245
|
-
...extraInput,
|
|
246
|
-
[MEDIA_INPUT_FIELD[checkType]]: completed.key
|
|
247
|
-
};
|
|
248
|
-
return this.submitCheck(checkType, input, opts);
|
|
249
|
-
}
|
|
250
|
-
// ── transport ──────────────────────────────────────────────────────────────
|
|
251
|
-
/** The token's own sessionId — cached from getSession(), lazily bootstrapped. */
|
|
252
|
-
async ensureSessionId() {
|
|
253
|
-
if (this.sessionId !== null) return this.sessionId;
|
|
254
|
-
this.bootstrap ?? (this.bootstrap = this.getSession().finally(() => {
|
|
255
|
-
this.bootstrap = null;
|
|
256
|
-
}));
|
|
257
|
-
return (await this.bootstrap).sessionId;
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* One API request: `${apiOrigin}/api/v2${path}`, `x-session-token` always,
|
|
261
|
-
* AbortSignal timeout (30 s default), `{success, data}` unwrap on 2xx, flat
|
|
262
|
-
* `{code, message}` → {@link HostedApiError} on non-2xx. EVERY failure is
|
|
263
|
-
* typed: a rejected fetch → `network_error`, the timeout → `timeout`, an
|
|
264
|
-
* unparseable 2xx body → `unknown_error` — the page never sees a raw
|
|
265
|
-
* TypeError/DOMException. The ONLY automatic retry is 409
|
|
266
|
-
* `idempotency_in_progress` (bounded, short delay).
|
|
267
|
-
*/
|
|
268
|
-
async api(method, path, opts = {}) {
|
|
269
|
-
const url = `${this.apiOrigin}/api/v2${path}`;
|
|
270
|
-
const headers = { "x-session-token": this.sessionToken };
|
|
271
|
-
if (opts.body !== void 0) headers["content-type"] = "application/json";
|
|
272
|
-
if (opts.idempotencyKey !== void 0) headers["x-idempotency-key"] = opts.idempotencyKey;
|
|
273
|
-
for (let attempt = 0; ; attempt++) {
|
|
274
|
-
let res;
|
|
275
|
-
try {
|
|
276
|
-
res = await this.fetchImpl(url, {
|
|
277
|
-
method,
|
|
278
|
-
headers,
|
|
279
|
-
...opts.body !== void 0 ? { body: JSON.stringify(opts.body) } : {},
|
|
280
|
-
signal: AbortSignal.timeout(this.requestTimeoutMs)
|
|
281
|
-
});
|
|
282
|
-
} catch (err2) {
|
|
283
|
-
throw asTransportError(err2);
|
|
284
|
-
}
|
|
285
|
-
if (res.ok) {
|
|
286
|
-
let raw;
|
|
287
|
-
try {
|
|
288
|
-
raw = await res.json();
|
|
289
|
-
} catch {
|
|
290
|
-
throw new HostedApiError({
|
|
291
|
-
status: res.status,
|
|
292
|
-
code: "unknown_error",
|
|
293
|
-
message: `response body was not valid JSON (HTTP ${res.status})`
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
return unwrap(raw);
|
|
297
|
-
}
|
|
298
|
-
const err = await parseErrorResponse(res);
|
|
299
|
-
if (err.code === "idempotency_in_progress" && attempt < this.retryAttempts) {
|
|
300
|
-
if (this.retryDelayMs > 0) await sleep(this.retryDelayMs);
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
throw err;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
function asTransportError(err) {
|
|
308
|
-
const name = typeof err === "object" && err !== null ? err.name : void 0;
|
|
309
|
-
if (name === "TimeoutError") {
|
|
310
|
-
return new HostedApiError({ status: 0, code: "timeout", message: "request timed out" });
|
|
311
|
-
}
|
|
312
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
313
|
-
return new HostedApiError({
|
|
314
|
-
status: 0,
|
|
315
|
-
code: "network_error",
|
|
316
|
-
message: `network request failed: ${detail}`
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
function unwrap(raw) {
|
|
320
|
-
if (raw !== null && typeof raw === "object" && "success" in raw && "data" in raw) {
|
|
321
|
-
return raw.data;
|
|
322
|
-
}
|
|
323
|
-
return raw;
|
|
324
|
-
}
|
|
325
|
-
async function parseErrorResponse(res) {
|
|
326
|
-
let code = "unknown_error";
|
|
327
|
-
let message = `request failed (HTTP ${res.status})`;
|
|
328
|
-
try {
|
|
329
|
-
const body = await res.json();
|
|
330
|
-
if (body !== null && typeof body === "object") {
|
|
331
|
-
const env = body;
|
|
332
|
-
if (typeof env.code === "string" && env.code.length > 0) code = env.code;
|
|
333
|
-
if (typeof env.message === "string" && env.message.length > 0) message = env.message;
|
|
334
|
-
}
|
|
335
|
-
} catch {
|
|
336
|
-
}
|
|
337
|
-
const retryAfterRaw = res.headers.get("retry-after");
|
|
338
|
-
const retryAfter = retryAfterRaw === null ? Number.NaN : Number.parseInt(retryAfterRaw, 10);
|
|
339
|
-
return new HostedApiError({
|
|
340
|
-
status: res.status,
|
|
341
|
-
code,
|
|
342
|
-
message,
|
|
343
|
-
...Number.isFinite(retryAfter) && retryAfter >= 0 ? { retryAfterSeconds: retryAfter } : {}
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
function sleep(ms) {
|
|
347
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
348
|
-
}
|
|
349
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
350
|
-
0 && (module.exports = {
|
|
351
|
-
HostedApiError,
|
|
352
|
-
HostedClient,
|
|
353
|
-
KNOWN_CODES,
|
|
354
|
-
isKnownCode,
|
|
355
|
-
stripMimeParams
|
|
356
|
-
});
|
|
357
|
-
//# sourceMappingURL=index.cjs.map
|
|
1
|
+
"use strict";var y=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var P=(t,e)=>{for(var n in e)y(t,n,{get:e[n],enumerable:!0})},O=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of R(e))!I.call(t,o)&&o!==n&&y(t,o,{get:()=>e[o],enumerable:!(s=C(e,o))||s.enumerable});return t};var v=t=>O(y({},"__esModule",{value:!0}),t);var B={};P(B,{HostedApiError:()=>d,HostedClient:()=>p,KNOWN_CODES:()=>g,isKnownCode:()=>w,stripMimeParams:()=>c});module.exports=v(B);var _="0.1.0-rc.7";var b="sekyro.did",S;function T(){let t=globalThis.crypto;try{if(typeof t?.randomUUID=="function")return t.randomUUID()}catch{}try{if(typeof t?.getRandomValues=="function"){let e=t.getRandomValues(new Uint8Array(16));return Array.from(e,n=>n.toString(16).padStart(2,"0")).join("")}}catch{}}function M(){try{let t=localStorage.getItem(b);if(t)return t;let e=T();return e!==void 0&&localStorage.setItem(b,e),e}catch{return S??(S=T()),S}}async function E(){let t=globalThis.crypto?.subtle;if(!t)return"";let e=navigator,n=JSON.stringify([e.userAgent,e.platform,e.hardwareConcurrency??null,e.deviceMemory??null,e.maxTouchPoints??0,screen.width,screen.height,screen.colorDepth,globalThis.devicePixelRatio??1,Intl.DateTimeFormat().resolvedOptions().timeZone,e.languages?.join(",")??e.language]),s=await t.digest("SHA-256",new TextEncoder().encode(n));return Array.from(new Uint8Array(s),o=>o.toString(16).padStart(2,"0")).join("").slice(0,32)}function A(t){let e=new TextEncoder().encode(t),n="";for(let s of e)n+=String.fromCharCode(s);return btoa(n).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function x(t){let e,n;return{timezone(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone?.slice(0,64)||void 0}catch{return}},noteCameraLabel(s){s&&(e=s.slice(0,128))},async deviceHeader(){try{n??(n=E().catch(()=>""));let s=[];navigator.webdriver&&s.push("webdriver"),/HeadlessChrome/.test(navigator.userAgent)&&s.push("headless");let o=await n,i=M(),r={v:1,platform:"web",sdk:t.sdk.slice(0,64),ids:[...i?[{kind:"web_local",value:i.slice(0,128)}]:[],...o?[{kind:"web_fp",value:o}]:[]],screen:{w:screen.width,h:screen.height,dpr:globalThis.devicePixelRatio??1},...e?{cameraLabel:e}:{},...s.length?{signals:s}:{}};return A(JSON.stringify(r))}catch{return}}}}var m=Symbol.for("sekyro.web.clientContext"),f=globalThis,h=f[m]??(f[m]=x({sdk:`@sekyro/web@${_}`}));var g=["session_token_invalid","session_token_required","session_token_session_mismatch","session_not_found","rate_limit_exceeded","consent_required","session_not_active","check_not_found","check_already_finalized","check_out_of_order","check_type_unsupported","force_outcome_forbidden","attestation_invalid","inference_unavailable","mobile_required","nni_locked","method_required","method_locked","document_nni_mismatch","media_content_type_not_allowed","media_too_large","media_key_invalid","media_key_forbidden","media_not_found","media_unreadable","media_session_invalid","idempotency_key_collision","idempotency_in_progress","idempotency_key_required","validation_error","internal_error","unknown_error","upload_failed","network_error","timeout"];function w(t){return g.includes(t)}var d=class extends Error{constructor(e){super(e.message),this.name="HostedApiError",this.status=e.status,this.code=e.code,e.retryAfterSeconds!==void 0&&(this.retryAfterSeconds=e.retryAfterSeconds)}};var D=3e4,K=9e5,N=200,V=5,H=400;function c(t){return(t.split(";")[0]??"").trim()}var U={liveness:"clipKey",face_match:"imageKey",doc_ocr:"imageKey"},p=class{constructor(e){this.sessionId=null;this.bootstrap=null;this.apiOrigin=e.apiOrigin.replace(/\/+$/,""),this.sessionToken=e.sessionToken;let n=e.fetch;this.fetchImpl=(s,o)=>(n??globalThis.fetch).call(globalThis,s,o),this.requestTimeoutMs=e.requestTimeoutMs??D,this.retryAttempts=e.inProgressRetry?.attempts??V,this.retryDelayMs=e.inProgressRetry?.delayMs??H}async getSession(){let e=await this.api("GET","/hosted/session");return this.sessionId=e.sessionId,e}async recordConsent(e){return this.api("POST","/hosted/consent",{body:{locale:e}})}async selectMethod(e){let n=await this.api("POST","/hosted/method",{body:{method:e}});return this.sessionId=n.sessionId,n}async presign(e,n,s){let o=await this.ensureSessionId(),i={kind:e,contentType:c(n),contentLength:s};return this.api("POST",`/sessions/${o}/media`,{body:i})}async uploadToSpaces(e,n,s){let o;try{o=await this.fetchImpl(e,{method:"PUT",headers:{"content-type":c(s)},body:n,signal:AbortSignal.timeout(K)})}catch(i){throw k(i)}if(!o.ok){let i=(await o.text().catch(()=>"")).slice(0,N);throw new d({status:o.status,code:"upload_failed",message:`media upload failed (HTTP ${o.status})${i?`: ${i}`:""}`})}}async completeMedia(e){let n=await this.ensureSessionId();return this.api("POST",`/sessions/${n}/media/complete`,{body:{key:e}})}async submitCheck(e,n,s={}){let o=await this.ensureSessionId(),i={input:n,...s.forceOutcome!==void 0?{forceOutcome:s.forceOutcome}:{}};return this.api("POST",`/sessions/${o}/checks/${e}`,{body:i,...s.idempotencyKey!==void 0?{idempotencyKey:s.idempotencyKey}:{}})}async uploadAndSubmit(e,n,s,o={}){let i=c(n.contentType),r=await this.presign(n.kind,i,n.blob.size);await this.uploadToSpaces(r.uploadUrl,n.blob,i);let a=await this.completeMedia(r.key),u={...s,[U[e]]:a.key};return this.submitCheck(e,u,o)}async ensureSessionId(){return this.sessionId!==null?this.sessionId:(this.bootstrap??(this.bootstrap=this.getSession().finally(()=>{this.bootstrap=null})),(await this.bootstrap).sessionId)}async api(e,n,s={}){let o=`${this.apiOrigin}/api/v2${n}`,i={"x-session-token":this.sessionToken};s.body!==void 0&&(i["content-type"]="application/json"),s.idempotencyKey!==void 0&&(i["x-idempotency-key"]=s.idempotencyKey);try{let r=h.timezone();r!==void 0&&(i["sekyro-client-timezone"]=r)}catch{}try{let r=await h.deviceHeader();r!==void 0&&(i["sekyro-client-device"]=r)}catch{}for(let r=0;;r++){let a;try{a=await this.fetchImpl(o,{method:e,headers:i,...s.body!==void 0?{body:JSON.stringify(s.body)}:{},signal:AbortSignal.timeout(this.requestTimeoutMs)})}catch(l){throw k(l)}if(a.ok){let l;try{l=await a.json()}catch{throw new d({status:a.status,code:"unknown_error",message:`response body was not valid JSON (HTTP ${a.status})`})}return L(l)}let u=await q(a);if(u.code==="idempotency_in_progress"&&r<this.retryAttempts){this.retryDelayMs>0&&await $(this.retryDelayMs);continue}throw u}}};function k(t){if((typeof t=="object"&&t!==null?t.name:void 0)==="TimeoutError")return new d({status:0,code:"timeout",message:"request timed out"});let n=t instanceof Error?t.message:String(t);return new d({status:0,code:"network_error",message:`network request failed: ${n}`})}function L(t){return t!==null&&typeof t=="object"&&"success"in t&&"data"in t?t.data:t}async function q(t){let e="unknown_error",n=`request failed (HTTP ${t.status})`,s=Number.NaN;try{let r=await t.json();if(r!==null&&typeof r=="object"){let a=r;typeof a.code=="string"&&a.code.length>0&&(e=a.code),typeof a.message=="string"&&a.message.length>0&&(n=a.message),typeof a.retryAfterSeconds=="number"&&(s=a.retryAfterSeconds)}}catch{}let o=t.headers.get("retry-after"),i=o===null?Math.floor(s):Number.parseInt(o,10);return new d({status:t.status,code:e,message:n,...Number.isFinite(i)&&i>=0?{retryAfterSeconds:i}:{}})}function $(t){return new Promise(e=>setTimeout(e,t))}0&&(module.exports={HostedApiError,HostedClient,KNOWN_CODES,isKnownCode,stripMimeParams});
|