@monitordog/detector 1.0.9 → 1.0.11
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/dist/assets/{inference-worker-ekAaVxft.js → inference-worker-KC9Jl3J4.js} +2 -2
- package/dist/assets/sdk/detector-320.onnx.enc +0 -0
- package/dist/assets/sdk/detector-416.onnx.enc +0 -0
- package/dist/assets/sdk/detector-640.onnx.enc +0 -0
- package/dist/detect/rotation-fallback.d.ts +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/monitordog-detector.js +195 -121
- package/dist/monitordog-detector.umd.cjs +1 -1
- package/dist/service/container.d.ts +3 -0
- package/dist/service/detect-service.d.ts +6 -0
- package/dist/types.d.ts +7 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("html2canvas")):typeof define==`function`&&define.amd?define([`exports`,`html2canvas`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.MonitorDogDetector={},e.html2canvas))})(this,function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=Object.create,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,o=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty,c=(e,t,n,o)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=a(t),l=0,u=c.length,d;l<u;l++)d=c[l],!s.call(e,d)&&d!==n&&r(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(o=i(t,d))||o.enumerable});return e};t=((e,t,i)=>(i=e==null?{}:n(o(e)),c(t||!e||!e.__esModule?r(i,`default`,{value:e,enumerable:!0}):i,e)))(t,1);var l=.8,u=.7,d=3e4;function f(e){return typeof e==`string`?e:e.access_token}function p(e){if(typeof e!=`string`)return e.expires_in}function ee(e){if(typeof e!=`string`)return e.expires_at}function te(e){if(e.expiresAt){let t=Date.parse(e.expiresAt);if(!Number.isNaN(t))return t}if(e.expiresIn)return e.expiresIn>1e9?e.expiresIn*1e3:Date.now()+e.expiresIn*1e3}function m(e){return e.replace(/\/+$/,``)}function h(){if(typeof navigator>`u`)return`none`;let e=navigator.userAgent.toLowerCase();return/ipad|tablet/.test(e)?`tablet`:/mobile|iphone|ipod|android/.test(e)?`mobile`:/macintosh|windows|linux/.test(e)?`desktop`:`other`}function g(){if(typeof navigator>`u`)return`none`;let e=navigator.userAgent.toLowerCase(),t=navigator.platform.toLowerCase();return/win/.test(t)||/windows/.test(e)?`windows`:/mac/.test(t)||/mac os/.test(e)?`macos`:/android/.test(e)?`android`:/iphone|ipad|ipod/.test(e)?`ios`:/linux/.test(t)||/linux/.test(e)?`linux`:`other`}function ne(e){return e===`auto`||e===320||e===416||e===640}function re(e,t){let n=e??`auto`;return n===`auto`?t?416:640:n}function _(e){if(e===void 0)return;let t=e.trim();if(t.length===0)throw Error(`MonitorDog runtimeAssetBaseUrl must be a non-empty string when provided.`);let n=t.replace(/\/+$/,``);return n.length===0?`/`:n}var v=class extends Error{code;cause;constructor(e,t,n){super(t),this.name=`MonitorDogSdkError`,this.code=e,this.cause=n}};function y(e){return e instanceof v}function b(e){if(e.faceCount===0)return C(`noFaceDetected`,e);if(e.faceCount>=2)return C(`twoFacesDetected`,e);if(e.phoneCount>=1)return C(`mobileDevice`,e)}function x(e){return{...w(e,new Date().toISOString()),properties:{},webcam_imgs:[],monitor_imgs:[]}}function S(e){return e.phoneCount===0&&e.faceCount===1}function C(e,t){let n=w(e,new Date().toISOString());switch(e){case`noFaceDetected`:return{type:e,payload:{...n,properties:{confidence:0,spots:[]}}};case`twoFacesDetected`:return{type:e,payload:{...n,properties:{confidence:T(t.faceDetections),spots:t.faceDetections}}};case`mobileDevice`:return{type:e,payload:{...n,properties:{confidence:T(t.phoneDetections),spots:t.phoneDetections}}}}}function w(e,t){return{basic_event_uuid:E(e),occured_at:t,sended_at:new Date().toISOString(),device_type:h(),device_os:g(),device_id:`browser`}}function T(e){return e.reduce((e,t)=>Math.max(e,t.score),0)}function E(e){switch(e){case`logout`:return`12ca646a-d832-4581-b03c-48f95627507f`;case`login`:return`4c6f98ea-88d8-4215-9b6a-9b3ccb753465`;case`mobileDevice`:return`027af5c9-297d-4a64-983f-4199ca4b9dad`;case`twoFacesDetected`:return`d555efac-7465-4067-8056-611e95385807`;case`noFaceDetected`:return`e2c48c23-36e2-4253-82b1-7f6565fdcfc3`;default:return``}}var D=`MonitorDog token refresh was superseded.`,ie=300*1e3,O=class{services;refreshBeforeMs=ie;periodicRefreshIntervalMs=d;token;tokenExpiresAt;refreshPromise;refreshIntervalId;authSessionId=0;sessionEmail;constructor(e){this.services=e}async login(e){let t=await this.fetchSessionToken(e.email);return this.sessionEmail=e.email,this.setSessionTokenPayload(t),await this.sendUserActivityEvent(`login`),t}async logout(){if(this.token)try{await this.sendUserActivityEvent(`logout`)}finally{this.clearTokens()}}getToken(){return this.token}getTokenExpiresAt(){return this.tokenExpiresAt}getSessionEmail(){return this.sessionEmail}isAuthenticated(){return!!this.token}dispose(){this.clearTokens()}assertAuthenticated(){if(!this.token)throw Error(`MonitorDog login is required before detection.`)}async getValidAccessToken(){if(this.shouldRefreshCurrentToken())try{await this.handleAuthRefresh()}catch(e){if(this.token&&this.tokenExpiresAt&&Date.now()<this.tokenExpiresAt)return this.token;throw e}return this.token}async authorizedFetch(e,t={},n=!0){let r=await this.getValidAccessToken(),i=new Headers(t.headers);r&&(i.set(`authorization`,`Bearer ${r}`),i.set(`Device-Access-Token`,r));let a=await fetch(`${this.services.options.apiBaseUrl}${e}`,{...t,headers:i}),o=await this.getApiErrorCode(a);return a.status===400&&o===4221&&this.handleLoginRequired(),n&&this.shouldRefreshAfterAuthError(a,o)?(await this.handleAuthRefresh(),this.authorizedFetch(e,t,!1)):a}setSessionTokenPayload(e){let t=f(e);if(!t||typeof t!=`string`)throw Error(`MonitorDog SDK token response did not include a token.`);this.setTokens({accessToken:t,expiresIn:p(e),expiresAt:ee(e)})}setTokens(e){this.token=e.accessToken,this.tokenExpiresAt=te(e),this.tokenExpiresAt?this.startPeriodicRefresh():this.stopPeriodicRefresh()}clearTokens(){this.authSessionId+=1,this.token=void 0,this.tokenExpiresAt=void 0,this.refreshPromise=void 0,this.sessionEmail=void 0,this.stopPeriodicRefresh()}async refreshToken(){return this.refreshPromise||=this.refreshAccessToken().finally(()=>{this.refreshPromise=void 0}),this.refreshPromise}async handleAuthRefresh(){try{return await this.refreshToken()}catch(e){if(!this.isRefreshSupersededError(e)){let t=this.createSdkError(`SESSION_TOKEN_FAILED`,`MonitorDog SDK session token refresh failed.`,e);this.services.options.onAuthError?.(t),this.isCurrentTokenExpired()&&(this.clearTokens(),this.services.onLoginRequired?.())}throw e}}async refreshAccessToken(){let e=this.authSessionId,t=this.sessionEmail;if(!t)throw Error(`MonitorDog login email is required to refresh SDK token.`);let n=await this.fetchSessionToken(t),r=f(n);if(!r||typeof r!=`string`)throw Error(`MonitorDog SDK token response did not include a token.`);if(e!==this.authSessionId)throw Error(D);return this.setSessionTokenPayload(n),r}startPeriodicRefresh(){this.refreshIntervalId||=setInterval(()=>{this.refreshPeriodically()},this.periodicRefreshIntervalMs)}stopPeriodicRefresh(){this.refreshIntervalId&&=(clearInterval(this.refreshIntervalId),void 0)}async refreshPeriodically(){if(!(!this.token||!this.shouldRefreshCurrentToken()))try{await this.handleAuthRefresh()}catch{}}shouldRefreshCurrentToken(){return!!(this.token&&this.tokenExpiresAt&&Date.now()>=this.tokenExpiresAt-this.refreshBeforeMs)}isCurrentTokenExpired(){return!!(this.tokenExpiresAt&&Date.now()>=this.tokenExpiresAt)}isRefreshSupersededError(e){return e instanceof Error&&e.message===D}async fetchSessionToken(e){let t=this.services.options.sessionTokenProvider;if(!t)throw Error(`MonitorDog sessionTokenProvider is required for SDK login.`);return t({email:e})}async getApiErrorCode(e){if(e.status===400)try{let t=await e.clone().json();if(typeof t==`object`&&t&&`code`in t&&typeof t.code==`number`)return t.code}catch{return}}shouldRefreshAfterAuthError(e,t){return e.status===401?!0:e.status===400&&t===422}handleLoginRequired(){this.clearTokens(),this.services.onLoginRequired?.();let e=this.createSdkError(`NOT_LOGGED_IN`,`MonitorDog login is required.`);throw this.services.options.onAuthError?.(e),e}async sendUserActivityEvent(e){try{let t=await this.authorizedFetch(`/event`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(x(e))});if(!t.ok)throw Error(`MonitorDog ${e} event failed: ${t.status}`)}catch(t){let n=this.createSdkError(k(e),`MonitorDog ${e} event failed.`,t);if(this.services.options.onAuthError?.(n),e===`logout`)throw n}}createSdkError(e,t,n){return n instanceof v?n:new v(e,t,n)}};function k(e){return e===`logout`?`LOGOUT_FAILED`:`SESSION_TOKEN_FAILED`}var A=/(obs|virtual|xsplit|manycam|splitcam|webcammax|fake|loopback|camtwist|e2esoft|vcam|droidcam|iriun|epoccam|camo|mmhmm|nvidia broadcast|streamlabs|bandicam|wirecast|chromacam|youcam|cyberlink|altserver|logi capture|elgato|streamfx|screen capture|desktop capture)/i,j=class{async createWebcamStream(e){if(!navigator.mediaDevices?.getUserMedia)throw Error(`getUserMedia is not available in this browser.`);let t=this.getBaseWebcamConstraints(e),n=await this.createFirstCameraConstraints(t);try{return await this.getUserMedia(n)}catch(e){if(this.isPermissionError(e)||this.isCameraRequestTimeoutError(e))throw e;return this.createFallbackWebcamStream(n,e)}}stopStream(e){e.getTracks().forEach(e=>e.stop())}watchStreamDeviceDisconnect(e,t,n=1e3){let r=e.getVideoTracks()[0],i=r?.getSettings().deviceId;if(!r&&!i)return()=>{};let a=!1,o,s=()=>{a||(a=!0,t())},c=()=>{i&&(o&&clearTimeout(o),o=setTimeout(()=>{o=void 0,this.handleDeviceChange(i,s)},n))};return r?.addEventListener(`ended`,s),navigator.mediaDevices?.addEventListener?.(`devicechange`,c),()=>{o&&clearTimeout(o),r?.removeEventListener(`ended`,s),navigator.mediaDevices?.removeEventListener?.(`devicechange`,c)}}isCameraInUse(e){let t=e?.srcObject;return t instanceof MediaStream?t.getTracks().some(e=>e.readyState===`live`):!1}async getVideoInputDevices(){return navigator.mediaDevices?.enumerateDevices?(await navigator.mediaDevices.enumerateDevices()).filter(e=>e.kind===`videoinput`):[]}async getRealVideoInputDevices(){let e=await this.getVideoInputDevices(),t=e.filter(e=>!this.isVirtualCamera(e));return t.length>0?t:e}async createFallbackWebcamStream(e,t){let n=await this.getRealVideoInputDevices();if(n.length===0)throw t;let r=n[0],i=t;if(!r?.deviceId)throw i;let a=await this.getWebcamNaturalAspectRatio(r.deviceId),o=this.generateResolutionList(a,720);for(let t of o)try{return await this.getUserMedia(this.createDeviceConstraints(e,r.deviceId,t))}catch(e){i=e}throw i}async handleDeviceChange(e,t){try{(await this.getVideoInputDevices()).some(t=>t.deviceId===e)||t()}catch{}}getBaseWebcamConstraints(e){return e??{audio:!1,video:{facingMode:`user`}}}async createFirstCameraConstraints(e){let t=typeof e.video==`object`?e.video:{};if(`deviceId`in t||`facingMode`in t)return e;let n=(await this.getRealVideoInputDevices())[0];return n?.deviceId?this.createDeviceConstraints(e,n.deviceId):e}async getWebcamNaturalAspectRatio(e){let t;try{t=await this.getUserMedia({audio:!1,video:{deviceId:{exact:e}}});let n=t.getVideoTracks()[0]?.getSettings();if(n?.width&&n.height)return n.width/n.height}catch{}finally{t?.getTracks().forEach(e=>e.stop())}return 16/9}generateResolutionList(e,t){return[t,640,600,480,320,240].map(t=>({width:t,height:Math.max(1,Math.round(t/e))})).filter(e=>e.width<=t&&e.height>=120&&e.height<=1080)}createDeviceConstraints(e,t,n){let r=typeof e.video==`object`?e.video:{};return{...e,audio:e.audio??!1,video:{...r,deviceId:{exact:t},...n?{width:{min:n.width,ideal:n.width},height:{min:n.height,ideal:n.height}}:{}}}}isVirtualCamera(e){let t=e.label.toLowerCase();return t?A.test(t):!1}isPermissionError(e){return e instanceof DOMException&&(e.name===`NotAllowedError`||e.name===`SecurityError`)}getUserMedia(e,t=15e3){let n=!1,r,i=navigator.mediaDevices.getUserMedia(e).then(e=>{if(n)throw this.stopStream(e),Error(`Camera permission request timed out.`);return e}),a=new Promise((e,i)=>{r=setTimeout(()=>{n=!0,i(Error(`Camera permission request timed out.`))},t)});return Promise.race([i,a]).finally(()=>{r&&clearTimeout(r)})}isCameraRequestTimeoutError(e){return e instanceof Error&&e.message===`Camera permission request timed out.`}};function M(e){if(e.videoWidth===0||e.videoHeight===0)throw Error(`Video frame is not ready for capture.`);let t=document.createElement(`canvas`),n=t.getContext(`2d`);if(!n)throw Error(`2D canvas context is not available.`);return t.width=e.videoWidth,t.height=e.videoHeight,n.drawImage(e,0,0,t.width,t.height),new Promise((e,n)=>{t.toBlob(t=>{if(!t){n(Error(`Failed to capture video frame.`));return}e(t)},`image/jpeg`,.9)})}async function N(){if(typeof document>`u`||!document.body)return null;let e=await(0,t.default)(document.body,{backgroundColor:`#ffffff`,useCORS:!0});return new Promise(t=>{e.toBlob(t,`image/jpeg`,.9)})}var P=class e{static NO_FACE_STARTUP_GRACE_MS=1500;services;detectionEventLocked=!1;detectionEventInFlight=!1;detectionEventLockTimeoutId;noFaceStartedAt;suppressNoFaceUntil=0;constructor(e){this.services=e}async handleDetectionResult(e,t){if(!this.services.options.eventReportingEnabled){this.reset();return}if(S(e)){this.noFaceStartedAt=void 0,this.clearLock();return}if(this.detectionEventLocked||this.detectionEventInFlight)return;let n=b(e);if(!n){this.noFaceStartedAt=void 0;return}if(!(n.type===`noFaceDetected`&&!this.hasNoFaceDelayElapsed())&&(n.type!==`noFaceDetected`&&(this.noFaceStartedAt=void 0),this.shouldSendEvent(n.type,e))){this.lock(n.type),this.detectionEventInFlight=!0;try{let e=await this.uploadWebcamImage(t);e&&(n.payload.webcam_imgs=e);let r=await this.uploadMonitorImage();if(r&&(n.payload.monitor_imgs=r),this.services.options.debug){console.log(n.payload);return}let i=await this.services.auth.authorizedFetch(`/event`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(n.payload)});if(!i.ok)throw Error(`MonitorDog event request failed: ${i.status}`)}finally{this.detectionEventInFlight=!1}}}reset(){this.clearLock(),this.noFaceStartedAt=void 0,this.suppressNoFaceUntil=0}resetForWebcamStartup(){this.clearLock(),this.noFaceStartedAt=void 0,this.suppressNoFaceUntil=Date.now()+e.NO_FACE_STARTUP_GRACE_MS}clearTransientState(){this.noFaceStartedAt=void 0}async uploadWebcamImage(e){if(!(e instanceof HTMLVideoElement)||e.videoWidth===0||e.videoHeight===0)return;let t=await M(e);return this.uploadImage(t,`webcam.jpg`)}async uploadMonitorImage(){let e=this.services.options.captureMonitorImage?await this.services.options.captureMonitorImage():await N();if(!e)return;let t=typeof File<`u`&&e instanceof File?e.name:`monitor.jpg`;return this.uploadImage(e,t)}async uploadImage(e,t){let n=new FormData;n.append(`files`,e,t);let r=await this.services.auth.authorizedFetch(`/file/image`,{method:`POST`,body:n});if(!r.ok)throw Error(`MonitorDog image upload failed: ${r.status}`);return r.json()}shouldSendEvent(e,t){switch(e){case`mobileDevice`:return this.services.userPreferences.mobileDetectionEnabled&&F(t.phoneDetections)>=this.services.userPreferences.mobileDetectionThreshold;case`twoFacesDetected`:return this.services.userPreferences.multiPersonDetectionEnabled&&F(t.faceDetections)>=this.services.userPreferences.faceDetectionThreshold;case`noFaceDetected`:return this.services.userPreferences.noPersonDetectionEnabled}}lock(e){this.clearLock(),this.detectionEventLocked=!0;let t=this.getLockDuration(e);if(t<=0){this.detectionEventLocked=!1;return}this.detectionEventLockTimeoutId=setTimeout(()=>{this.detectionEventLocked=!1,this.detectionEventLockTimeoutId=void 0},t)}clearLock(){this.detectionEventLockTimeoutId&&=(clearTimeout(this.detectionEventLockTimeoutId),void 0),this.detectionEventLocked=!1}hasNoFaceDelayElapsed(){if(Date.now()<this.suppressNoFaceUntil)return this.noFaceStartedAt=void 0,!1;if(this.services.noFaceEventDelayMs<=0)return!0;let e=Date.now();return this.noFaceStartedAt===void 0?(this.noFaceStartedAt=e,!1):e-this.noFaceStartedAt>=this.services.noFaceEventDelayMs}getLockDuration(e){switch(e){case`mobileDevice`:return this.services.userPreferences.mobileDetectionLockDuration;case`twoFacesDetected`:return this.services.userPreferences.multiPersonLockDuration;case`noFaceDetected`:return this.services.userPreferences.noPersonLockDuration}}};function F(e){return e.reduce((e,t)=>Math.max(e,t.confidence),0)}var I=``+(typeof document>`u`&&typeof location>`u`?require("url").pathToFileURL(__dirname+`/assets/inference-worker-ekAaVxft.js`).href:new URL(`assets/inference-worker-ekAaVxft.js`,typeof document>`u`?location.href:document.currentScript&&document.currentScript.tagName.toUpperCase()===`SCRIPT`&&document.currentScript.src||document.baseURI).href),L=class{worker;nextRequestId=1;pendingRequests=new Map;async load(e){let t={id:this.nextRequestId,type:`load`,model:e};await this.request(t)}run(e){let t=e.source.kind===`bitmap`?[e.source.bitmap]:[e.source.data.buffer];return this.request({id:this.nextRequestId,type:`run`,source:e.source,inputSize:e.inputSize,inputType:e.inputType,confidenceThreshold:e.confidenceThreshold,faceConfidenceThreshold:e.faceConfidenceThreshold,iouThreshold:e.iouThreshold,maxDetections:e.maxDetections,debug:e.debug,inputRotation:e.inputRotation??`none`,phoneRotationFallback:e.phoneRotationFallback,phoneRotationFallbackMinIntervalMs:e.phoneRotationFallbackMinIntervalMs},t)}terminate(){this.rejectPendingRequests(Error(`Inference worker was terminated.`)),this.worker?.terminate(),this.worker=void 0}request(e,t=[]){let n=this.getWorker();return this.nextRequestId+=1,new Promise((r,i)=>{this.pendingRequests.set(e.id,{resolve:r,reject:i}),n.postMessage(e,t)})}getWorker(){return this.worker||(this.worker=new Worker(I,{name:`MonitorDogInference`,type:`module`}),this.worker.addEventListener(`message`,this.handleMessage),this.worker.addEventListener(`error`,this.handleError),this.worker.addEventListener(`messageerror`,this.handleError)),this.worker}handleMessage=e=>{let t=e.data,n=this.pendingRequests.get(t.id);if(n){if(this.pendingRequests.delete(t.id),!t.ok){n.reject(Error(t.error));return}n.resolve(t.result)}};handleError=e=>{let t=e instanceof ErrorEvent?e.message:`Inference worker message failed.`;this.rejectPendingRequests(Error(t)),this.worker?.terminate(),this.worker=void 0};rejectPendingRequests(e){for(let t of this.pendingRequests.values())t.reject(e);this.pendingRequests.clear()}};async function R(e){let{sourceWidth:t,sourceHeight:n}=z(e);if(e instanceof ImageData)return{kind:`rgba`,data:new Uint8ClampedArray(e.data),sourceWidth:t,sourceHeight:n};if(B()&&V())try{return{kind:`bitmap`,bitmap:await createImageBitmap(e),sourceWidth:t,sourceHeight:n}}catch{}return H(e,t,n)}function z(e){return e instanceof ImageData?{sourceWidth:e.width,sourceHeight:e.height}:e instanceof HTMLVideoElement?{sourceWidth:e.videoWidth,sourceHeight:e.videoHeight}:e instanceof HTMLImageElement?{sourceWidth:e.naturalWidth,sourceHeight:e.naturalHeight}:{sourceWidth:e.width,sourceHeight:e.height}}function B(){return typeof createImageBitmap==`function`}function V(){return typeof OffscreenCanvas==`function`}function H(e,t,n){let r=document.createElement(`canvas`),i=r.getContext(`2d`);if(!i)throw Error(`2D canvas context is not available.`);return r.width=t,r.height=n,i.drawImage(e,0,0,t,n),{kind:`rgba`,data:i.getImageData(0,0,t,n).data,sourceWidth:t,sourceHeight:n}}function U(e,t,n){return e.map(e=>W(e,t,n))}function ae(e,t,n,r){if(e.phoneDetected||t.phoneDetections.length===0)return e;let i=U(t.phoneDetections,n,r);return{...e,phoneDetected:i.length>0,phoneCount:i.length,phoneDetections:i}}function W(e,t,n){return{...e,x:G(t-(e.x+e.width),0,t),y:G(n-(e.y+e.height),0,n)}}function G(e,t,n){return Math.min(Math.max(e,t),n)}function K(e){return e.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&e.videoWidth>0&&e.videoHeight>0&&!e.paused&&!e.ended}function q(e,t=1e4){return J(e)?Promise.resolve():new Promise((n,r)=>{let i=window.setTimeout(()=>{o(),r(Error(`Timed out waiting for webcam video metadata.`))},t),a=window.setInterval(s,100),o=()=>{window.clearTimeout(i),window.clearInterval(a),e.removeEventListener(`loadedmetadata`,c),e.removeEventListener(`loadeddata`,c),e.removeEventListener(`canplay`,c),e.removeEventListener(`playing`,c),e.removeEventListener(`resize`,c),e.removeEventListener(`error`,l)};function s(){J(e)&&(o(),n())}let c=()=>{s()},l=()=>{o(),r(Error(`Failed to load webcam video stream.`))};e.addEventListener(`loadedmetadata`,c,{once:!0}),e.addEventListener(`loadeddata`,c,{once:!0}),e.addEventListener(`canplay`,c,{once:!0}),e.addEventListener(`playing`,c,{once:!0}),e.addEventListener(`resize`,c,{once:!0}),e.addEventListener(`error`,l,{once:!0})})}function J(e){return e.readyState>=HTMLMediaElement.HAVE_METADATA&&e.videoWidth>0&&e.videoHeight>0}var oe=class{services;primaryInferenceWorker;rotationInferenceWorker;loadPromise;lastInferenceMemoryErrorAt=0;lastParallelPhoneRotationFallbackAt=-1/0;constructor(e){this.services=e}async load(){this.loadPromise||=this.loadInferenceWorker(),await this.loadPromise}async detect(e){await this.load();let t=this.services.options,n=t.debug?performance.now():0,r=t.debug?performance.now():0,i=r,a;if(this.shouldUseParallelPhoneRotationFallback(t)&&this.shouldRunParallelPhoneRotationFallback(t)){let[n,r]=await Promise.all([R(e),R(e)]);i=t.debug?performance.now():0,a=await this.runParallelPhoneRotationFallback(n,r,t)}else{let n=await R(e);i=t.debug?performance.now():0,a=await this.runWorkerInference(this.getInferenceWorker(),n,t,{inputRotation:`none`,phoneRotationFallback:t.phoneRotationFallback&&t.phoneRotationFallbackExecution===`sequential`})}if(t.debug){let e=performance.now();console.info(`[MonitorDog] detect frame`,{sourceMs:Y(i-r),workerRoundtripMs:Y(e-i),totalMs:Y(e-n)})}return a}toPublicResult(e){return{faceDetected:e.faceDetected,phoneDetected:e.phoneDetected,faceCount:e.faceCount,phoneCount:e.phoneCount,faceDetections:e.faceDetections,phoneDetections:e.phoneDetections}}detectVideo(e){let t=!1,n,r=this.services.options.detectionIntervalMs,i=async()=>{if(!t){try{if(!this.services.shouldPauseDetection&&K(e)){let n=await this.detect(e);if(t)return;try{await this.services.options.onDetect({...this.toPublicResult(n),video:e,timestamp:e.currentTime})}catch(e){this.services.options.onError?.(e)}if(t)return;await this.services.detectionEvents.handleDetectionResult(n,e).catch(e=>{this.services.options.onError?.(e)}),S(n)&&this.services.detectionEvents.clearTransientState()}}catch(e){this.handleDetectionLoopError(e)}t||(n=setTimeout(i,r))}};return i(),{stop(){t=!0,n&&clearTimeout(n)}}}async startWebcamDetection(){let e=this.services.options,t=await this.services.camera.createWebcamStream(e.constraints),n=this.services.camera,r=!e.video,i=e.video??document.createElement(`video`),a=i.muted,o=i.playsInline,s=i.srcObject;i.muted=!0,i.playsInline=!0,i.srcObject=t;try{await Promise.all([i.play(),q(i)])}catch(e){throw n.stopStream(t),i.pause(),r?i.srcObject=null:(i.muted=a,i.playsInline=o,i.srcObject=s),e}this.services.detectionEvents.resetForWebcamStartup();let c=this.detectVideo(i),l=!1,u=n.watchStreamDeviceDisconnect(t,()=>{d(),this.services.onCameraDisconnect?.()}),d=()=>{l||(l=!0,u(),c.stop(),this.services.detectionEvents.reset(),n.stopStream(t),i.pause(),r?i.srcObject=null:(i.muted=a,i.playsInline=o,i.srcObject=s))};return{stream:t,video:i,stop:d}}dispose(){this.primaryInferenceWorker?.terminate(),this.rotationInferenceWorker?.terminate(),this.primaryInferenceWorker=void 0,this.rotationInferenceWorker=void 0,this.loadPromise=void 0,this.lastParallelPhoneRotationFallbackAt=-1/0,this.services.detectionEvents.reset()}async runParallelPhoneRotationFallback(e,t,n){let r=e.sourceWidth,i=e.sourceHeight,[a,o]=await Promise.all([this.runWorkerInference(this.getInferenceWorker(),e,n,{inputRotation:`none`,phoneRotationFallback:!1}),this.runWorkerInference(this.getRotationInferenceWorker(),t,n,{inputRotation:`rotate180`,phoneRotationFallback:!1})]);return ae(a,o,r,i)}runWorkerInference(e,t,n,r){return e.run({source:t,inputSize:n.inputSize,inputType:n.inputType,confidenceThreshold:n.confidenceThreshold,faceConfidenceThreshold:n.faceConfidenceThreshold,iouThreshold:n.iouThreshold,maxDetections:n.maxDetections,debug:!!n.debug,inputRotation:r.inputRotation,phoneRotationFallback:r.phoneRotationFallback,phoneRotationFallbackMinIntervalMs:n.phoneRotationFallbackMinIntervalMs})}handleDetectionLoopError(e){this.isInferenceMemoryError(e)&&this.recoverFromInferenceMemoryError(),this.services.options.onError?.(e)}recoverFromInferenceMemoryError(){this.degradeModelForMemoryPressure(),this.resetInferenceWorker();let e=Date.now();e-this.lastInferenceMemoryErrorAt<1e4||(this.lastInferenceMemoryErrorAt=e)}resetInferenceWorker(){this.primaryInferenceWorker?.terminate(),this.rotationInferenceWorker?.terminate(),this.primaryInferenceWorker=void 0,this.rotationInferenceWorker=void 0,this.loadPromise=void 0,this.lastParallelPhoneRotationFallbackAt=-1/0}degradeModelForMemoryPressure(){this.services.degradeInferenceModelForMemoryPressure()}isInferenceMemoryError(e){let t=e instanceof Error?e.message:String(e);return/out of memory|oom|memory access out of bounds|cannot enlarge memory|array buffer allocation|bad allocation|allocation failed/i.test(t)}getInferenceWorker(){return this.primaryInferenceWorker||=new L,this.primaryInferenceWorker}getRotationInferenceWorker(){return this.rotationInferenceWorker||=new L,this.rotationInferenceWorker}shouldUseParallelPhoneRotationFallback(e){return e.phoneRotationFallback&&e.phoneRotationFallbackExecution===`parallel`}shouldRunParallelPhoneRotationFallback(e){let t=performance.now();return t-this.lastParallelPhoneRotationFallbackAt<e.phoneRotationFallbackMinIntervalMs?!1:(this.lastParallelPhoneRotationFallbackAt=t,!0)}async loadInferenceWorker(){try{await this.loadInferenceWorkerWithOptions()}catch(e){if(!this.isInferenceMemoryError(e))throw e;this.recoverFromInferenceMemoryError();try{await this.loadInferenceWorkerWithOptions()}catch(e){throw this.recoverFromInferenceMemoryError(),e}}}async loadInferenceWorkerWithOptions(){let e=await this.services.auth.getValidAccessToken();if(!e)throw Error(`MonitorDog login is required before loading detection.`);let t=this.services.options,n={apiBaseUrl:t.apiBaseUrl,accessToken:e,inputSize:t.inputSize,runtimeAssetBaseUrl:t.runtimeAssetBaseUrl};if(this.shouldUseParallelPhoneRotationFallback(t)){await Promise.all([this.getInferenceWorker().load(n),this.getRotationInferenceWorker().load(n)]);return}await this.getInferenceWorker().load(n)}};function Y(e){return Math.round(e*10)/10}var se=class{services;isWebcamRunning=!1;stopCurrentWebcam;startPromise;shouldRun=!1;resumeWhenUnpaused=!1;handleVisibilityChange=()=>{this.syncPauseState()};constructor(e){this.services=e,this.services.onCameraDisconnect=()=>this.handleCameraDisconnect()}get isRunning(){return this.isWebcamRunning}get isStarting(){return!!this.startPromise}async start(){if(this.services.auth.assertAuthenticated(),this.shouldRun=!0,this.addVisibilityListener(),!this.isWebcamRunning){if(this.services.shouldPauseDetection){this.resumeWhenUnpaused=!0;return}await this.ensureStarted()}}stop(){this.shouldRun=!1,this.resumeWhenUnpaused=!1,this.removeVisibilityListener(),this.services.detectionEvents.reset(),this.stopWebcam()}async retryConnection(){await this.start()}async syncPauseState(){if(this.shouldRun){if(this.services.shouldPauseDetection){this.isWebcamRunning&&(this.resumeWhenUnpaused=!0,this.stopWebcam());return}if(this.resumeWhenUnpaused&&!this.isWebcamRunning){this.resumeWhenUnpaused=!1;try{await this.ensureStarted()}catch{this.shouldRun=!1}}}}syncBackgroundPreference(){this.services.options.runInBackground?this.removeVisibilityListener():this.shouldRun&&this.addVisibilityListener(),this.syncPauseState()}async ensureStarted(){this.isWebcamRunning||(this.startPromise||=this.startWebcam().finally(()=>{this.startPromise=void 0}),await this.startPromise)}async startWebcam(){try{let e=await this.services.detect.startWebcamDetection();if(!this.shouldRun){e.stop();return}this.isWebcamRunning=!0,this.stopCurrentWebcam=e.stop}catch(e){throw this.services.options.onError?.(e),e}}stopWebcam(){this.stopCurrentWebcam?.(),this.stopCurrentWebcam=void 0,this.isWebcamRunning=!1}async handleCameraDisconnect(){if(this.isWebcamRunning&&(this.stopWebcam(),this.shouldRun)){if(this.services.shouldPauseDetection){this.resumeWhenUnpaused=!0;return}try{await this.ensureStarted()}catch(e){this.services.options.onError?.(e)}}}addVisibilityListener(){this.services.options.runInBackground||typeof document>`u`||document.addEventListener(`visibilitychange`,this.handleVisibilityChange)}removeVisibilityListener(){typeof document>`u`||document.removeEventListener(`visibilitychange`,this.handleVisibilityChange)}},ce=class{auth;camera;detectionEvents;detect;webcam;onCameraDisconnect;onLoginRequired;#e=0;#t=X();#n;constructor(e){this.#n=e,this.camera=new j,this.auth=new O(this),this.detectionEvents=new P(this),this.detect=new oe(this),this.webcam=new se(this)}get options(){return Object.freeze({...this.#n()})}get noFaceEventDelayMs(){return this.#e}get userPreferences(){return Object.freeze({...this.#t})}updateNoFaceEventDelayFromServer(e){this.#e=e}updateRequiredPasswordFromServer(e){this.#n().requiredPassword=e}degradeInferenceModelForMemoryPressure(){let e=this.#n();if(e.inputSize>416){e.inputSize=416;return}e.inputSize>320&&(e.inputSize=320)}get shouldPauseForHiddenPage(){return!this.options.runInBackground&&typeof document<`u`&&document.hidden}get shouldPauseDetection(){return this.shouldPauseForHiddenPage}applyPolicyItems(e,t){let n={...this.#t};for(let t of e)ue(n,t);this.#t=n,this.#n().confidenceThreshold=n.mobileDetectionThreshold,this.#n().faceConfidenceThreshold=n.faceDetectionThreshold,this.updateRequiredPasswordFromServer(!1)}resetAuthenticatedState(){this.#e=0,this.#t=X(),this.#n().requiredPassword=!1,this.#n().confidenceThreshold=this.#t.mobileDetectionThreshold,this.#n().faceConfidenceThreshold=this.#t.faceDetectionThreshold}};function X(){return{mobileDetectionThreshold:.8,faceDetectionThreshold:.8,language:`en`,mobileDetectionEnabled:!0,noPersonDetectionEnabled:!0,multiPersonDetectionEnabled:!0,noPersonLockDuration:0,multiPersonLockDuration:0,mobileDetectionLockDuration:0,mobileDetectionMode:`auto_lock`,noPersonDetectionMode:`auto_lock`,multiPersonDetectionMode:`auto_lock`}}function le(e){return e===`none`}function ue(e,t){let n=t.policy,r=!le(t.policy),i=t.seconds*1e3,a=`sensitivity`in t?1-t.sensitivity/100:void 0;switch(t.basic_event_uuid){case E(`mobileDevice`):e.mobileDetectionEnabled=r,e.mobileDetectionLockDuration=i,e.mobileDetectionMode=n,a!==void 0&&(e.mobileDetectionThreshold=a);break;case E(`twoFacesDetected`):e.multiPersonDetectionEnabled=r,e.multiPersonLockDuration=i,e.multiPersonDetectionMode=n,a!==void 0&&(e.faceDetectionThreshold=a);break;case E(`noFaceDetected`):e.noPersonDetectionEnabled=r,e.noPersonLockDuration=i,e.noPersonDetectionMode=n;break;default:break}}var Z=class e{#e;#t;#n=`anonymous`;#r=`idle`;#i=`idle`;#a;#o;static async init(t){return $=new e(t),$}constructor(e){he(e);let t=me(),n=_(e.runtimeAssetBaseUrl);this.#t={...e,apiBaseUrl:m(e.apiBaseUrl),runtimeAssetBaseUrl:n,confidenceThreshold:l,faceConfidenceThreshold:l,iouThreshold:u,maxDetections:100,inputSize:re(e.modelInputSize,t),inputType:`float32`,detectionIntervalMs:e.detectionIntervalMs??300,requiredPassword:!1,eventReportingEnabled:e.eventReportingEnabled??!0,phoneRotationFallback:e.phoneRotationFallback??!1,phoneRotationFallbackMinIntervalMs:e.phoneRotationFallbackMinIntervalMs??500,phoneRotationFallbackExecution:e.phoneRotationFallbackExecution??`sequential`},this.#e=new ce(()=>this.#t),this.#e.onLoginRequired=()=>this.resetLoggedOutState(),Object.freeze(this)}async load(){this.assertUsable(),this.assertAuthenticated(`loading detection`);try{this.updateState({runtime:`loading`,lastError:void 0}),await this.#e.detect.load(),this.updateState({runtime:`idle`,lastError:void 0})}catch(e){let t=this.createSdkError(`MODEL_LOAD_FAILED`,`MonitorDog detection model failed to load.`,e);throw this.transitionToError(t),this.#t.onError?.(t),t}}async login(e){this.assertUsable();let t;try{t=await this.#e.auth.login(e)}catch(e){let t=this.createSdkError(`SESSION_TOKEN_FAILED`,`MonitorDog SDK session token could not be created.`,e);throw this.transitionToError(t),this.#t.onAuthError?.(t),t}try{await this.completeAuthenticatedLogin()}catch(e){let t=this.createSdkError(`ACCOUNT_POLICY_FAILED`,`MonitorDog account or policy could not be loaded.`,e);throw this.rollbackAuthenticatedLogin(),this.transitionToError(t),this.#t.onAuthError?.(t),t}return this.updateState({session:`authenticated`,runtime:this.#r===`stopped`?`stopped`:`idle`,camera:this.#e.webcam.isRunning?`active`:`idle`,lastError:void 0}),t}async logout(){if(this.assertUsable(),this.isDetectorRunning())throw this.lifecycleError(`DETECTOR_RUNNING`,`MonitorDog detector is running. Call stop() before logout().`);if(!this.#e.auth.getToken()){this.clearSessionState();return}try{await this.#e.auth.logout(),this.clearSessionState()}catch(e){let t=this.createSdkError(`LOGOUT_FAILED`,`MonitorDog logout failed.`,e);throw this.transitionToError(t),this.#t.onAuthError?.(t),t}finally{this.#e.auth.getToken()||(this.#n=`anonymous`)}}async start(){this.assertUsable(),this.assertAuthenticated(`starting detection`),!this.isDetectorRunning()&&(this.#o||=this.startRuntime().finally(()=>{this.#o=void 0}),await this.#o)}stop(){this.assertUsable(),this.hasRuntimeToStop()&&(this.stopLocalRuntime(),this.updateState({runtime:this.#n===`authenticated`?`stopped`:`idle`,camera:`idle`,lastError:void 0}))}setRunInBackground(e){this.assertUsable(),this.#t.runInBackground=e,this.#e.webcam.syncBackgroundPreference()}setEventReportingEnabled(e){this.assertUsable(),this.#t.eventReportingEnabled=e,e||this.#e.detectionEvents.reset()}getUserPreferences(){return this.assertUsable(),this.#e.userPreferences}getState(){return Object.freeze({session:this.#n,runtime:this.#r,camera:this.#i,tokenExpiresAt:this.#e.auth.getTokenExpiresAt(),email:this.#e.auth.getSessionEmail(),lastError:this.#a})}dispose(){this.#r!==`disposed`&&(this.stopLocalRuntime(),this.#e.detect.dispose(),this.#e.auth.dispose(),this.#e.resetAuthenticatedState(),this.updateState({session:`anonymous`,runtime:`disposed`,camera:`idle`,lastError:void 0}))}async completeAuthenticatedLogin(){let e=await this.#e.auth.authorizedFetch(`/account/me`,{method:`GET`,headers:{"content-type":`application/json`}});if(!e.ok)throw Error(`MonitorDog account request failed: ${e.status}`);let t=await e.json(),n=await this.#e.auth.authorizedFetch(`/account/${t.uuid}/lock_policy`,{method:`GET`,headers:{"content-type":`application/json`}});if(!n.ok)throw Error(`MonitorDog lock policy request failed: ${n.status}`);let r=await n.json();this.#e.applyPolicyItems(r,t)}resetLoggedOutState(){this.stopLocalRuntime(),this.#e.detect.dispose(),this.#e.resetAuthenticatedState(),this.updateState({session:`anonymous`,runtime:`idle`,camera:`idle`,lastError:void 0})}rollbackAuthenticatedLogin(){this.#e.auth.dispose(),this.#e.resetAuthenticatedState()}async startRuntime(){try{this.updateState({runtime:`loading`,camera:`idle`,lastError:void 0}),await this.#e.detect.load(),this.updateState({runtime:`starting`,camera:`requesting`}),await this.#e.webcam.start(),this.updateState({runtime:`running`,camera:this.#e.shouldPauseDetection?`idle`:`active`,lastError:void 0})}catch(e){let t=this.createSdkError(this.getStartErrorCode(e),this.getStartErrorMessage(e),e);throw this.transitionToError(t,Q(t.code)?`blocked`:`idle`),this.#t.onError?.(t),t}}clearSessionState(){this.#e.resetAuthenticatedState(),this.updateState({session:`anonymous`,runtime:`idle`,camera:`idle`,lastError:void 0})}stopLocalRuntime(){this.#e.webcam.stop()}assertUsable(){if(this.#r===`disposed`)throw this.lifecycleError(`ALREADY_DISPOSED`,`MonitorDog detector instance has already been disposed.`,void 0,`disposed`)}assertAuthenticated(e){if(!this.#e.auth.isAuthenticated())throw this.lifecycleError(`NOT_LOGGED_IN`,`MonitorDog login is required before ${e}.`)}lifecycleError(e,t,n,r=`error`){let i=this.createSdkError(e,t,n);return r===`error`?this.transitionToError(i):this.updateState({runtime:r,lastError:i},!0),i}transitionToError(e,t=this.#i){this.updateState({runtime:`error`,camera:t,lastError:e},!0)}updateState(e,t=!1){let n=!1;e.session!==void 0&&e.session!==this.#n&&(this.#n=e.session,n=!0),e.runtime!==void 0&&e.runtime!==this.#r&&(this.#r=e.runtime,n=!0),e.camera!==void 0&&e.camera!==this.#i&&(this.#i=e.camera,n=!0),`lastError`in e&&e.lastError!==this.#a&&(this.#a=e.lastError,n=!0),(n||t)&&this.notifyStatusChange()}notifyStatusChange(){try{this.#t.onStatusChange?.(this.getState())}catch(e){this.#t.onError?.(e)}}createSdkError(e,t,n){return y(n)?n:new v(e,t,n)}isDetectorRunning(){return this.#r===`running`||this.#r===`starting`||!!this.#o||this.#e.webcam.isRunning||this.#e.webcam.isStarting}hasRuntimeToStop(){return this.#r===`running`||this.#r===`starting`||this.#i!==`idle`||!!this.#o||this.#e.webcam.isRunning||this.#e.webcam.isStarting}getStartErrorCode(e){return this.isCameraPermissionError(e)?`CAMERA_PERMISSION_DENIED`:this.isCameraReadError(e)?`CAMERA_READ_FAILED`:this.#r===`loading`?`MODEL_LOAD_FAILED`:`RUNTIME_START_FAILED`}getStartErrorMessage(e){switch(this.getStartErrorCode(e)){case`CAMERA_PERMISSION_DENIED`:return`MonitorDog camera permission was denied.`;case`CAMERA_READ_FAILED`:return`MonitorDog camera could not be read.`;case`MODEL_LOAD_FAILED`:return`MonitorDog detection model failed to load.`;default:return`MonitorDog detector runtime failed to start.`}}isCameraPermissionError(e){return typeof DOMException<`u`&&e instanceof DOMException&&(e.name===`NotAllowedError`||e.name===`SecurityError`)}isCameraReadError(e){return typeof DOMException<`u`&&e instanceof DOMException&&e.name===`NotReadableError`}};function Q(e){return e===`CAMERA_PERMISSION_DENIED`||e===`CAMERA_READ_FAILED`}var $;function de(e){return $=new Z(e),$}async function fe(e){return Z.init(e)}function pe(){if(!$)throw Error(`MonitorDog is not configured. Call MonitorDogDetector.init(), createMonitorDogDetector(), or configureMonitorDogDetector() before using getDetector().`);return $}function me(){if(typeof navigator>`u`)return!1;let e=h();return e===`mobile`||e===`tablet`}function he(e){if(!e||typeof e.apiBaseUrl!=`string`||e.apiBaseUrl.length===0)throw Error(`MonitorDog apiBaseUrl is required.`);if(typeof e.sessionTokenProvider!=`function`)throw Error(`MonitorDog sessionTokenProvider is required.`);if(typeof e.onDetect!=`function`)throw Error(`MonitorDog onDetect callback is required.`);if(e.runtimeAssetBaseUrl!==void 0&&_(e.runtimeAssetBaseUrl),e.modelInputSize!==void 0&&!ne(e.modelInputSize))throw Error(`MonitorDog modelInputSize must be "auto", 320, 416, or 640.`);if(e.phoneRotationFallback!==void 0&&typeof e.phoneRotationFallback!=`boolean`)throw Error(`MonitorDog phoneRotationFallback must be a boolean when provided.`);if(e.phoneRotationFallbackMinIntervalMs!==void 0&&!ge(e.phoneRotationFallbackMinIntervalMs))throw Error(`MonitorDog phoneRotationFallbackMinIntervalMs must be a non-negative finite number when provided.`);if(e.phoneRotationFallbackExecution!==void 0&&e.phoneRotationFallbackExecution!==`sequential`&&e.phoneRotationFallbackExecution!==`parallel`)throw Error(`MonitorDog phoneRotationFallbackExecution must be "sequential" or "parallel" when provided.`)}function ge(e){return typeof e==`number`&&Number.isFinite(e)&&e>=0}e.MonitorDogDetector=Z,e.MonitorDogSdkError=v,e.configureMonitorDogDetector=de,e.createMonitorDogDetector=fe,e.getDetector=pe,e.isMonitorDogSdkError=y});
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("html2canvas")):typeof define==`function`&&define.amd?define([`exports`,`html2canvas`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.MonitorDogDetector={},e.html2canvas))})(this,function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=Object.create,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,o=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty,c=(e,t,n,o)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=a(t),l=0,u=c.length,d;l<u;l++)d=c[l],!s.call(e,d)&&d!==n&&r(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(o=i(t,d))||o.enumerable});return e};t=((e,t,i)=>(i=e==null?{}:n(o(e)),c(t||!e||!e.__esModule?r(i,`default`,{value:e,enumerable:!0}):i,e)))(t,1);var l=.8,u=.7,d=3e4;function f(e){return typeof e==`string`?e:e.access_token}function p(e){if(typeof e!=`string`)return e.expires_in}function ee(e){if(typeof e!=`string`)return e.expires_at}function te(e){if(e.expiresAt){let t=Date.parse(e.expiresAt);if(!Number.isNaN(t))return t}if(e.expiresIn)return e.expiresIn>1e9?e.expiresIn*1e3:Date.now()+e.expiresIn*1e3}function m(e){return e.replace(/\/+$/,``)}function h(){if(typeof navigator>`u`)return`none`;let e=navigator.userAgent.toLowerCase();return/ipad|tablet/.test(e)?`tablet`:/mobile|iphone|ipod|android/.test(e)?`mobile`:/macintosh|windows|linux/.test(e)?`desktop`:`other`}function g(){if(typeof navigator>`u`)return`none`;let e=navigator.userAgent.toLowerCase(),t=navigator.platform.toLowerCase();return/win/.test(t)||/windows/.test(e)?`windows`:/mac/.test(t)||/mac os/.test(e)?`macos`:/android/.test(e)?`android`:/iphone|ipad|ipod/.test(e)?`ios`:/linux/.test(t)||/linux/.test(e)?`linux`:`other`}function ne(e){return e===`auto`||e===320||e===416||e===640}function re(e,t){let n=e??`auto`;return n===`auto`?t?416:640:n}function _(e){if(e===void 0)return;let t=e.trim();if(t.length===0)throw Error(`MonitorDog runtimeAssetBaseUrl must be a non-empty string when provided.`);let n=t.replace(/\/+$/,``);return n.length===0?`/`:n}var v=class extends Error{code;cause;constructor(e,t,n){super(t),this.name=`MonitorDogSdkError`,this.code=e,this.cause=n}};function y(e){return e instanceof v}function b(e){if(e.faceCount===0)return C(`noFaceDetected`,e);if(e.faceCount>=2)return C(`twoFacesDetected`,e);if(e.phoneCount>=1)return C(`mobileDevice`,e)}function x(e){return{...w(e,new Date().toISOString()),properties:{},webcam_imgs:[],monitor_imgs:[]}}function S(e){return e.phoneCount===0&&e.faceCount===1}function C(e,t){let n=w(e,new Date().toISOString());switch(e){case`noFaceDetected`:return{type:e,payload:{...n,properties:{confidence:0,spots:[]}}};case`twoFacesDetected`:return{type:e,payload:{...n,properties:{confidence:T(t.faceDetections),spots:t.faceDetections}}};case`mobileDevice`:return{type:e,payload:{...n,properties:{confidence:T(t.phoneDetections),spots:t.phoneDetections}}}}}function w(e,t){return{basic_event_uuid:E(e),occured_at:t,sended_at:new Date().toISOString(),device_type:h(),device_os:g(),device_id:`browser`}}function T(e){return e.reduce((e,t)=>Math.max(e,t.score),0)}function E(e){switch(e){case`logout`:return`12ca646a-d832-4581-b03c-48f95627507f`;case`login`:return`4c6f98ea-88d8-4215-9b6a-9b3ccb753465`;case`mobileDevice`:return`027af5c9-297d-4a64-983f-4199ca4b9dad`;case`twoFacesDetected`:return`d555efac-7465-4067-8056-611e95385807`;case`noFaceDetected`:return`e2c48c23-36e2-4253-82b1-7f6565fdcfc3`;default:return``}}var D=`MonitorDog token refresh was superseded.`,ie=300*1e3,ae=class{services;refreshBeforeMs=ie;periodicRefreshIntervalMs=d;token;tokenExpiresAt;refreshPromise;refreshIntervalId;authSessionId=0;sessionEmail;constructor(e){this.services=e}async login(e){let t=await this.fetchSessionToken(e.email);return this.sessionEmail=e.email,this.setSessionTokenPayload(t),await this.sendUserActivityEvent(`login`),t}async logout(){if(this.token)try{await this.sendUserActivityEvent(`logout`)}finally{this.clearTokens()}}getToken(){return this.token}getTokenExpiresAt(){return this.tokenExpiresAt}getSessionEmail(){return this.sessionEmail}isAuthenticated(){return!!this.token}dispose(){this.clearTokens()}assertAuthenticated(){if(!this.token)throw Error(`MonitorDog login is required before detection.`)}async getValidAccessToken(){if(this.shouldRefreshCurrentToken())try{await this.handleAuthRefresh()}catch(e){if(this.token&&this.tokenExpiresAt&&Date.now()<this.tokenExpiresAt)return this.token;throw e}return this.token}async authorizedFetch(e,t={},n=!0){let r=await this.getValidAccessToken(),i=new Headers(t.headers);r&&(i.set(`authorization`,`Bearer ${r}`),i.set(`Device-Access-Token`,r));let a=await fetch(`${this.services.options.apiBaseUrl}${e}`,{...t,headers:i}),o=await this.getApiErrorCode(a);return a.status===400&&o===4221&&this.handleLoginRequired(),n&&this.shouldRefreshAfterAuthError(a,o)?(await this.handleAuthRefresh(),this.authorizedFetch(e,t,!1)):a}setSessionTokenPayload(e){let t=f(e);if(!t||typeof t!=`string`)throw Error(`MonitorDog SDK token response did not include a token.`);this.setTokens({accessToken:t,expiresIn:p(e),expiresAt:ee(e)})}setTokens(e){this.token=e.accessToken,this.tokenExpiresAt=te(e),this.tokenExpiresAt?this.startPeriodicRefresh():this.stopPeriodicRefresh()}clearTokens(){this.authSessionId+=1,this.token=void 0,this.tokenExpiresAt=void 0,this.refreshPromise=void 0,this.sessionEmail=void 0,this.stopPeriodicRefresh()}async refreshToken(){return this.refreshPromise||=this.refreshAccessToken().finally(()=>{this.refreshPromise=void 0}),this.refreshPromise}async handleAuthRefresh(){try{return await this.refreshToken()}catch(e){if(!this.isRefreshSupersededError(e)){let t=this.createSdkError(`SESSION_TOKEN_FAILED`,`MonitorDog SDK session token refresh failed.`,e);this.services.options.onAuthError?.(t),this.isCurrentTokenExpired()&&(this.clearTokens(),this.services.onLoginRequired?.())}throw e}}async refreshAccessToken(){let e=this.authSessionId,t=this.sessionEmail;if(!t)throw Error(`MonitorDog login email is required to refresh SDK token.`);let n=await this.fetchSessionToken(t),r=f(n);if(!r||typeof r!=`string`)throw Error(`MonitorDog SDK token response did not include a token.`);if(e!==this.authSessionId)throw Error(D);return this.setSessionTokenPayload(n),r}startPeriodicRefresh(){this.refreshIntervalId||=setInterval(()=>{this.refreshPeriodically()},this.periodicRefreshIntervalMs)}stopPeriodicRefresh(){this.refreshIntervalId&&=(clearInterval(this.refreshIntervalId),void 0)}async refreshPeriodically(){if(!(!this.token||!this.shouldRefreshCurrentToken()))try{await this.handleAuthRefresh()}catch{}}shouldRefreshCurrentToken(){return!!(this.token&&this.tokenExpiresAt&&Date.now()>=this.tokenExpiresAt-this.refreshBeforeMs)}isCurrentTokenExpired(){return!!(this.tokenExpiresAt&&Date.now()>=this.tokenExpiresAt)}isRefreshSupersededError(e){return e instanceof Error&&e.message===D}async fetchSessionToken(e){let t=this.services.options.sessionTokenProvider;if(!t)throw Error(`MonitorDog sessionTokenProvider is required for SDK login.`);return t({email:e})}async getApiErrorCode(e){if(e.status===400)try{let t=await e.clone().json();if(typeof t==`object`&&t&&`code`in t&&typeof t.code==`number`)return t.code}catch{return}}shouldRefreshAfterAuthError(e,t){return e.status===401?!0:e.status===400&&t===422}handleLoginRequired(){this.clearTokens(),this.services.onLoginRequired?.();let e=this.createSdkError(`NOT_LOGGED_IN`,`MonitorDog login is required.`);throw this.services.options.onAuthError?.(e),e}async sendUserActivityEvent(e){try{let t=await this.authorizedFetch(`/event`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(x(e))});if(!t.ok)throw Error(`MonitorDog ${e} event failed: ${t.status}`)}catch(t){let n=this.createSdkError(oe(e),`MonitorDog ${e} event failed.`,t);if(this.services.options.onAuthError?.(n),e===`logout`)throw n}}createSdkError(e,t,n){return n instanceof v?n:new v(e,t,n)}};function oe(e){return e===`logout`?`LOGOUT_FAILED`:`SESSION_TOKEN_FAILED`}var se=/(obs|virtual|xsplit|manycam|splitcam|webcammax|fake|loopback|camtwist|e2esoft|vcam|droidcam|iriun|epoccam|camo|mmhmm|nvidia broadcast|streamlabs|bandicam|wirecast|chromacam|youcam|cyberlink|altserver|logi capture|elgato|streamfx|screen capture|desktop capture)/i,ce=class{async createWebcamStream(e){if(!navigator.mediaDevices?.getUserMedia)throw Error(`getUserMedia is not available in this browser.`);let t=this.getBaseWebcamConstraints(e),n=await this.createFirstCameraConstraints(t);try{return await this.getUserMedia(n)}catch(e){if(this.isPermissionError(e)||this.isCameraRequestTimeoutError(e))throw e;return this.createFallbackWebcamStream(n,e)}}stopStream(e){e.getTracks().forEach(e=>e.stop())}watchStreamDeviceDisconnect(e,t,n=1e3){let r=e.getVideoTracks()[0],i=r?.getSettings().deviceId;if(!r&&!i)return()=>{};let a=!1,o,s=()=>{a||(a=!0,t())},c=()=>{i&&(o&&clearTimeout(o),o=setTimeout(()=>{o=void 0,this.handleDeviceChange(i,s)},n))};return r?.addEventListener(`ended`,s),navigator.mediaDevices?.addEventListener?.(`devicechange`,c),()=>{o&&clearTimeout(o),r?.removeEventListener(`ended`,s),navigator.mediaDevices?.removeEventListener?.(`devicechange`,c)}}isCameraInUse(e){let t=e?.srcObject;return t instanceof MediaStream?t.getTracks().some(e=>e.readyState===`live`):!1}async getVideoInputDevices(){return navigator.mediaDevices?.enumerateDevices?(await navigator.mediaDevices.enumerateDevices()).filter(e=>e.kind===`videoinput`):[]}async getRealVideoInputDevices(){let e=await this.getVideoInputDevices(),t=e.filter(e=>!this.isVirtualCamera(e));return t.length>0?t:e}async createFallbackWebcamStream(e,t){let n=await this.getRealVideoInputDevices();if(n.length===0)throw t;let r=n[0],i=t;if(!r?.deviceId)throw i;let a=await this.getWebcamNaturalAspectRatio(r.deviceId),o=this.generateResolutionList(a,720);for(let t of o)try{return await this.getUserMedia(this.createDeviceConstraints(e,r.deviceId,t))}catch(e){i=e}throw i}async handleDeviceChange(e,t){try{(await this.getVideoInputDevices()).some(t=>t.deviceId===e)||t()}catch{}}getBaseWebcamConstraints(e){return e??{audio:!1,video:{facingMode:`user`}}}async createFirstCameraConstraints(e){let t=typeof e.video==`object`?e.video:{};if(`deviceId`in t||`facingMode`in t)return e;let n=(await this.getRealVideoInputDevices())[0];return n?.deviceId?this.createDeviceConstraints(e,n.deviceId):e}async getWebcamNaturalAspectRatio(e){let t;try{t=await this.getUserMedia({audio:!1,video:{deviceId:{exact:e}}});let n=t.getVideoTracks()[0]?.getSettings();if(n?.width&&n.height)return n.width/n.height}catch{}finally{t?.getTracks().forEach(e=>e.stop())}return 16/9}generateResolutionList(e,t){return[t,640,600,480,320,240].map(t=>({width:t,height:Math.max(1,Math.round(t/e))})).filter(e=>e.width<=t&&e.height>=120&&e.height<=1080)}createDeviceConstraints(e,t,n){let r=typeof e.video==`object`?e.video:{};return{...e,audio:e.audio??!1,video:{...r,deviceId:{exact:t},...n?{width:{min:n.width,ideal:n.width},height:{min:n.height,ideal:n.height}}:{}}}}isVirtualCamera(e){let t=e.label.toLowerCase();return t?se.test(t):!1}isPermissionError(e){return e instanceof DOMException&&(e.name===`NotAllowedError`||e.name===`SecurityError`)}getUserMedia(e,t=15e3){let n=!1,r,i=navigator.mediaDevices.getUserMedia(e).then(e=>{if(n)throw this.stopStream(e),Error(`Camera permission request timed out.`);return e}),a=new Promise((e,i)=>{r=setTimeout(()=>{n=!0,i(Error(`Camera permission request timed out.`))},t)});return Promise.race([i,a]).finally(()=>{r&&clearTimeout(r)})}isCameraRequestTimeoutError(e){return e instanceof Error&&e.message===`Camera permission request timed out.`}};function O(e){if(e.videoWidth===0||e.videoHeight===0)throw Error(`Video frame is not ready for capture.`);let t=document.createElement(`canvas`),n=t.getContext(`2d`);if(!n)throw Error(`2D canvas context is not available.`);return t.width=e.videoWidth,t.height=e.videoHeight,n.drawImage(e,0,0,t.width,t.height),new Promise((e,n)=>{t.toBlob(t=>{if(!t){n(Error(`Failed to capture video frame.`));return}e(t)},`image/jpeg`,.9)})}async function k(){if(typeof document>`u`||!document.body)return null;let e=await(0,t.default)(document.body,{backgroundColor:`#ffffff`,useCORS:!0});return new Promise(t=>{e.toBlob(t,`image/jpeg`,.9)})}var A=class e{static NO_FACE_STARTUP_GRACE_MS=1500;services;detectionEventLocked=!1;detectionEventInFlight=!1;detectionEventLockTimeoutId;noFaceStartedAt;suppressNoFaceUntil=0;constructor(e){this.services=e}async handleDetectionResult(e,t){if(!this.services.options.eventReportingEnabled){this.reset();return}if(S(e)){this.noFaceStartedAt=void 0,this.clearLock();return}if(this.detectionEventLocked||this.detectionEventInFlight)return;let n=b(e);if(!n){this.noFaceStartedAt=void 0;return}if(!(n.type===`noFaceDetected`&&!this.hasNoFaceDelayElapsed())&&(n.type!==`noFaceDetected`&&(this.noFaceStartedAt=void 0),this.shouldSendEvent(n.type,e))){this.lock(n.type),this.detectionEventInFlight=!0;try{let e=await this.uploadWebcamImage(t);e&&(n.payload.webcam_imgs=e);let r=await this.uploadMonitorImage();if(r&&(n.payload.monitor_imgs=r),this.services.options.debug){console.log(n.payload);return}let i=await this.services.auth.authorizedFetch(`/event`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(n.payload)});if(!i.ok)throw Error(`MonitorDog event request failed: ${i.status}`)}finally{this.detectionEventInFlight=!1}}}reset(){this.clearLock(),this.noFaceStartedAt=void 0,this.suppressNoFaceUntil=0}resetForWebcamStartup(){this.clearLock(),this.noFaceStartedAt=void 0,this.suppressNoFaceUntil=Date.now()+e.NO_FACE_STARTUP_GRACE_MS}clearTransientState(){this.noFaceStartedAt=void 0}async uploadWebcamImage(e){if(!(e instanceof HTMLVideoElement)||e.videoWidth===0||e.videoHeight===0)return;let t=await O(e);return this.uploadImage(t,`webcam.jpg`)}async uploadMonitorImage(){let e=this.services.options.captureMonitorImage?await this.services.options.captureMonitorImage():await k();if(!e)return;let t=typeof File<`u`&&e instanceof File?e.name:`monitor.jpg`;return this.uploadImage(e,t)}async uploadImage(e,t){let n=new FormData;n.append(`files`,e,t);let r=await this.services.auth.authorizedFetch(`/file/image`,{method:`POST`,body:n});if(!r.ok)throw Error(`MonitorDog image upload failed: ${r.status}`);return r.json()}shouldSendEvent(e,t){switch(e){case`mobileDevice`:return this.services.userPreferences.mobileDetectionEnabled&&j(t.phoneDetections)>=this.services.options.confidenceThreshold;case`twoFacesDetected`:return this.services.userPreferences.multiPersonDetectionEnabled&&j(t.faceDetections)>=this.services.userPreferences.faceDetectionThreshold;case`noFaceDetected`:return this.services.userPreferences.noPersonDetectionEnabled}}lock(e){this.clearLock(),this.detectionEventLocked=!0;let t=this.getLockDuration(e);if(t<=0){this.detectionEventLocked=!1;return}this.detectionEventLockTimeoutId=setTimeout(()=>{this.detectionEventLocked=!1,this.detectionEventLockTimeoutId=void 0},t)}clearLock(){this.detectionEventLockTimeoutId&&=(clearTimeout(this.detectionEventLockTimeoutId),void 0),this.detectionEventLocked=!1}hasNoFaceDelayElapsed(){if(Date.now()<this.suppressNoFaceUntil)return this.noFaceStartedAt=void 0,!1;if(this.services.noFaceEventDelayMs<=0)return!0;let e=Date.now();return this.noFaceStartedAt===void 0?(this.noFaceStartedAt=e,!1):e-this.noFaceStartedAt>=this.services.noFaceEventDelayMs}getLockDuration(e){switch(e){case`mobileDevice`:return this.services.userPreferences.mobileDetectionLockDuration;case`twoFacesDetected`:return this.services.userPreferences.multiPersonLockDuration;case`noFaceDetected`:return this.services.userPreferences.noPersonLockDuration}}};function j(e){return e.reduce((e,t)=>Math.max(e,t.score),0)}var M=``+(typeof document>`u`&&typeof location>`u`?require("url").pathToFileURL(__dirname+`/assets/inference-worker-KC9Jl3J4.js`).href:new URL(`assets/inference-worker-KC9Jl3J4.js`,typeof document>`u`?location.href:document.currentScript&&document.currentScript.tagName.toUpperCase()===`SCRIPT`&&document.currentScript.src||document.baseURI).href),N=class{worker;nextRequestId=1;pendingRequests=new Map;async load(e){let t={id:this.nextRequestId,type:`load`,model:e};await this.request(t)}run(e){let t=e.source.kind===`bitmap`?[e.source.bitmap]:[e.source.data.buffer];return this.request({id:this.nextRequestId,type:`run`,source:e.source,inputSize:e.inputSize,inputType:e.inputType,confidenceThreshold:e.confidenceThreshold,faceConfidenceThreshold:e.faceConfidenceThreshold,iouThreshold:e.iouThreshold,maxDetections:e.maxDetections,debug:e.debug,inputRotation:e.inputRotation??`none`,phoneRotationFallback:e.phoneRotationFallback,phoneRotationFallbackMinIntervalMs:e.phoneRotationFallbackMinIntervalMs},t)}terminate(){this.rejectPendingRequests(Error(`Inference worker was terminated.`)),this.worker?.terminate(),this.worker=void 0}request(e,t=[]){let n=this.getWorker();return this.nextRequestId+=1,new Promise((r,i)=>{this.pendingRequests.set(e.id,{resolve:r,reject:i}),n.postMessage(e,t)})}getWorker(){return this.worker||(this.worker=new Worker(M,{name:`MonitorDogInference`,type:`module`}),this.worker.addEventListener(`message`,this.handleMessage),this.worker.addEventListener(`error`,this.handleError),this.worker.addEventListener(`messageerror`,this.handleError)),this.worker}handleMessage=e=>{let t=e.data,n=this.pendingRequests.get(t.id);if(n){if(this.pendingRequests.delete(t.id),!t.ok){n.reject(Error(t.error));return}n.resolve(t.result)}};handleError=e=>{let t=e instanceof ErrorEvent?e.message:`Inference worker message failed.`;this.rejectPendingRequests(Error(t)),this.worker?.terminate(),this.worker=void 0};rejectPendingRequests(e){for(let t of this.pendingRequests.values())t.reject(e);this.pendingRequests.clear()}};async function P(e){let{sourceWidth:t,sourceHeight:n}=F(e);if(e instanceof ImageData)return{kind:`rgba`,data:new Uint8ClampedArray(e.data),sourceWidth:t,sourceHeight:n};if(I()&&L())try{return{kind:`bitmap`,bitmap:await createImageBitmap(e),sourceWidth:t,sourceHeight:n}}catch{}return R(e,t,n)}function F(e){return e instanceof ImageData?{sourceWidth:e.width,sourceHeight:e.height}:e instanceof HTMLVideoElement?{sourceWidth:e.videoWidth,sourceHeight:e.videoHeight}:e instanceof HTMLImageElement?{sourceWidth:e.naturalWidth,sourceHeight:e.naturalHeight}:{sourceWidth:e.width,sourceHeight:e.height}}function I(){return typeof createImageBitmap==`function`}function L(){return typeof OffscreenCanvas==`function`}function R(e,t,n){let r=document.createElement(`canvas`),i=r.getContext(`2d`);if(!i)throw Error(`2D canvas context is not available.`);return r.width=t,r.height=n,i.drawImage(e,0,0,t,n),{kind:`rgba`,data:i.getImageData(0,0,t,n).data,sourceWidth:t,sourceHeight:n}}function z(e,t,n){return e.map(e=>H(e,t,n))}function B(e,t,n){return e.map(e=>H(e,t,n))}function V(e,t,n,r){if(e.phoneDetected)return e;let i=[...e.phoneCandidates,...B(t.phoneCandidates,n,r)];if(t.phoneDetections.length===0)return i.length===e.phoneCandidates.length?e:{...e,phoneCandidates:i};let a=z(t.phoneDetections,n,r);return{...e,phoneDetected:a.length>0,phoneCount:a.length,phoneDetections:a,phoneCandidates:i}}function H(e,t,n){return{...e,x:U(t-(e.x+e.width),0,t),y:U(n-(e.y+e.height),0,n)}}function U(e,t,n){return Math.min(Math.max(e,t),n)}function W(e){return e.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&e.videoWidth>0&&e.videoHeight>0&&!e.paused&&!e.ended}function G(e,t=1e4){return K(e)?Promise.resolve():new Promise((n,r)=>{let i=window.setTimeout(()=>{o(),r(Error(`Timed out waiting for webcam video metadata.`))},t),a=window.setInterval(s,100),o=()=>{window.clearTimeout(i),window.clearInterval(a),e.removeEventListener(`loadedmetadata`,c),e.removeEventListener(`loadeddata`,c),e.removeEventListener(`canplay`,c),e.removeEventListener(`playing`,c),e.removeEventListener(`resize`,c),e.removeEventListener(`error`,l)};function s(){K(e)&&(o(),n())}let c=()=>{s()},l=()=>{o(),r(Error(`Failed to load webcam video stream.`))};e.addEventListener(`loadedmetadata`,c,{once:!0}),e.addEventListener(`loadeddata`,c,{once:!0}),e.addEventListener(`canplay`,c,{once:!0}),e.addEventListener(`playing`,c,{once:!0}),e.addEventListener(`resize`,c,{once:!0}),e.addEventListener(`error`,l,{once:!0})})}function K(e){return e.readyState>=HTMLMediaElement.HAVE_METADATA&&e.videoWidth>0&&e.videoHeight>0}var le=700,ue=.25,de=.8,fe=class{services;primaryInferenceWorker;rotationInferenceWorker;loadPromise;lastInferenceMemoryErrorAt=0;lastParallelPhoneRotationFallbackAt=-1/0;phoneCandidateHistory=[];constructor(e){this.services=e}async load(){this.loadPromise||=this.loadInferenceWorker(),await this.loadPromise}async detect(e){await this.load();let t=this.services.options,n=t.debug?performance.now():0,r=t.debug?performance.now():0,i=r,a;if(this.shouldUseParallelPhoneRotationFallback(t)&&this.shouldRunParallelPhoneRotationFallback(t)){let[n,r]=await Promise.all([P(e),P(e)]);i=t.debug?performance.now():0,a=await this.runParallelPhoneRotationFallback(n,r,t)}else{let n=await P(e);i=t.debug?performance.now():0,a=await this.runWorkerInference(this.getInferenceWorker(),n,t,{inputRotation:`none`,phoneRotationFallback:t.phoneRotationFallback&&t.phoneRotationFallbackExecution===`sequential`})}let o=this.confirmTemporalPhoneCandidates(a);if(t.debug){let e=performance.now();console.info(`[MonitorDog] detect frame`,{sourceMs:J(i-r),workerRoundtripMs:J(e-i),totalMs:J(e-n)})}return o}toPublicResult(e){return{faceDetected:e.faceDetected,phoneDetected:e.phoneDetected,faceCount:e.faceCount,phoneCount:e.phoneCount,faceDetections:e.faceDetections,phoneDetections:e.phoneDetections}}detectVideo(e){let t=!1,n,r=this.services.options.detectionIntervalMs,i=async()=>{if(!t){try{if(!this.services.shouldPauseDetection&&W(e)){let n=await this.detect(e);if(t)return;try{await this.services.options.onDetect({...this.toPublicResult(n),video:e,timestamp:e.currentTime})}catch(e){this.services.options.onError?.(e)}if(t)return;await this.services.detectionEvents.handleDetectionResult(n,e).catch(e=>{this.services.options.onError?.(e)}),S(n)&&this.services.detectionEvents.clearTransientState()}}catch(e){this.handleDetectionLoopError(e)}t||(n=setTimeout(i,r))}};return i(),{stop:()=>{t=!0,this.resetPhoneCandidateHistory(),n&&clearTimeout(n)}}}async startWebcamDetection(){let e=this.services.options,t=await this.services.camera.createWebcamStream(e.constraints),n=this.services.camera,r=!e.video,i=e.video??document.createElement(`video`),a=i.muted,o=i.playsInline,s=i.srcObject;i.muted=!0,i.playsInline=!0,i.srcObject=t;try{await Promise.all([i.play(),G(i)])}catch(e){throw n.stopStream(t),i.pause(),r?i.srcObject=null:(i.muted=a,i.playsInline=o,i.srcObject=s),e}this.services.detectionEvents.resetForWebcamStartup();let c=this.detectVideo(i),l=!1,u=n.watchStreamDeviceDisconnect(t,()=>{d(),this.services.onCameraDisconnect?.()}),d=()=>{l||(l=!0,u(),c.stop(),this.services.detectionEvents.reset(),n.stopStream(t),i.pause(),r?i.srcObject=null:(i.muted=a,i.playsInline=o,i.srcObject=s))};return{stream:t,video:i,stop:d}}dispose(){this.primaryInferenceWorker?.terminate(),this.rotationInferenceWorker?.terminate(),this.primaryInferenceWorker=void 0,this.rotationInferenceWorker=void 0,this.loadPromise=void 0,this.lastParallelPhoneRotationFallbackAt=-1/0,this.resetPhoneCandidateHistory(),this.services.detectionEvents.reset()}confirmTemporalPhoneCandidates(e){if(e.phoneCandidates.length===0)return this.prunePhoneCandidateHistory(performance.now()),e;let t=performance.now(),n=this.getRecentPhoneCandidateHistory(t),r=e.phoneCandidates.filter(e=>this.isTemporallyConfirmedPhoneCandidate(e,n));if(this.phoneCandidateHistory=[...n,...e.phoneCandidates.map(e=>({candidate:e,observedAt:t}))],r.length===0)return e;let i=[...e.phoneDetections,...r.map(me)];return{...e,phoneDetected:!0,phoneCount:i.length,phoneDetections:i}}isTemporallyConfirmedPhoneCandidate(e,t){return q(e)?t.some(({candidate:t})=>pe(e,t)&&he(e,t)>=ue):!1}getRecentPhoneCandidateHistory(e){return this.phoneCandidateHistory.filter(t=>e-t.observedAt<=le)}prunePhoneCandidateHistory(e){this.phoneCandidateHistory=this.getRecentPhoneCandidateHistory(e)}resetPhoneCandidateHistory(){this.phoneCandidateHistory=[]}async runParallelPhoneRotationFallback(e,t,n){let r=e.sourceWidth,i=e.sourceHeight,[a,o]=await Promise.all([this.runWorkerInference(this.getInferenceWorker(),e,n,{inputRotation:`none`,phoneRotationFallback:!1}),this.runWorkerInference(this.getRotationInferenceWorker(),t,n,{inputRotation:`rotate180`,phoneRotationFallback:!1})]);return V(a,o,r,i)}runWorkerInference(e,t,n,r){return e.run({source:t,inputSize:n.inputSize,inputType:n.inputType,confidenceThreshold:n.confidenceThreshold,faceConfidenceThreshold:n.faceConfidenceThreshold,iouThreshold:n.iouThreshold,maxDetections:n.maxDetections,debug:!!n.debug,inputRotation:r.inputRotation,phoneRotationFallback:r.phoneRotationFallback,phoneRotationFallbackMinIntervalMs:n.phoneRotationFallbackMinIntervalMs})}handleDetectionLoopError(e){this.isInferenceMemoryError(e)&&this.recoverFromInferenceMemoryError(),this.services.options.onError?.(e)}recoverFromInferenceMemoryError(){this.degradeModelForMemoryPressure(),this.resetInferenceWorker();let e=Date.now();e-this.lastInferenceMemoryErrorAt<1e4||(this.lastInferenceMemoryErrorAt=e)}resetInferenceWorker(){this.primaryInferenceWorker?.terminate(),this.rotationInferenceWorker?.terminate(),this.primaryInferenceWorker=void 0,this.rotationInferenceWorker=void 0,this.loadPromise=void 0,this.lastParallelPhoneRotationFallbackAt=-1/0}degradeModelForMemoryPressure(){this.services.degradeInferenceModelForMemoryPressure()}isInferenceMemoryError(e){let t=e instanceof Error?e.message:String(e);return/out of memory|oom|memory access out of bounds|cannot enlarge memory|array buffer allocation|bad allocation|allocation failed/i.test(t)}getInferenceWorker(){return this.primaryInferenceWorker||=new N,this.primaryInferenceWorker}getRotationInferenceWorker(){return this.rotationInferenceWorker||=new N,this.rotationInferenceWorker}shouldUseParallelPhoneRotationFallback(e){return e.phoneRotationFallback&&e.phoneRotationFallbackExecution===`parallel`}shouldRunParallelPhoneRotationFallback(e){let t=performance.now();return t-this.lastParallelPhoneRotationFallbackAt<e.phoneRotationFallbackMinIntervalMs?!1:(this.lastParallelPhoneRotationFallbackAt=t,!0)}async loadInferenceWorker(){try{await this.loadInferenceWorkerWithOptions()}catch(e){if(!this.isInferenceMemoryError(e))throw e;this.recoverFromInferenceMemoryError();try{await this.loadInferenceWorkerWithOptions()}catch(e){throw this.recoverFromInferenceMemoryError(),e}}}async loadInferenceWorkerWithOptions(){let e=await this.services.auth.getValidAccessToken();if(!e)throw Error(`MonitorDog login is required before loading detection.`);let t=this.services.options,n={apiBaseUrl:t.apiBaseUrl,accessToken:e,inputSize:t.inputSize,runtimeAssetBaseUrl:t.runtimeAssetBaseUrl};if(this.shouldUseParallelPhoneRotationFallback(t)){await Promise.all([this.getInferenceWorker().load(n),this.getRotationInferenceWorker().load(n)]);return}await this.getInferenceWorker().load(n)}};function q(e){return e.handOverlapped||e.score>=de}function pe(e,t){return e.handOverlapped===t.handOverlapped?q(t):!1}function me(e){return{classId:e.classId,label:e.label,score:e.score,confidence:e.confidence,x:e.x,y:e.y,width:e.width,height:e.height}}function he(e,t){let n=e.x+e.width,r=e.y+e.height,i=t.x+t.width,a=t.y+t.height,o=Math.max(0,Math.min(n,i)-Math.max(e.x,t.x))*Math.max(0,Math.min(r,a)-Math.max(e.y,t.y)),s=e.width*e.height+t.width*t.height-o;return s<=0?0:o/s}function J(e){return Math.round(e*10)/10}var ge=class{services;isWebcamRunning=!1;stopCurrentWebcam;startPromise;shouldRun=!1;resumeWhenUnpaused=!1;handleVisibilityChange=()=>{this.syncPauseState()};constructor(e){this.services=e,this.services.onCameraDisconnect=()=>this.handleCameraDisconnect()}get isRunning(){return this.isWebcamRunning}get isStarting(){return!!this.startPromise}async start(){if(this.services.auth.assertAuthenticated(),this.shouldRun=!0,this.addVisibilityListener(),!this.isWebcamRunning){if(this.services.shouldPauseDetection){this.resumeWhenUnpaused=!0;return}await this.ensureStarted()}}stop(){this.shouldRun=!1,this.resumeWhenUnpaused=!1,this.removeVisibilityListener(),this.services.detectionEvents.reset(),this.stopWebcam()}async retryConnection(){await this.start()}async syncPauseState(){if(this.shouldRun){if(this.services.shouldPauseDetection){this.isWebcamRunning&&(this.resumeWhenUnpaused=!0,this.stopWebcam());return}if(this.resumeWhenUnpaused&&!this.isWebcamRunning){this.resumeWhenUnpaused=!1;try{await this.ensureStarted()}catch{this.shouldRun=!1}}}}syncBackgroundPreference(){this.services.options.runInBackground?this.removeVisibilityListener():this.shouldRun&&this.addVisibilityListener(),this.syncPauseState()}async ensureStarted(){this.isWebcamRunning||(this.startPromise||=this.startWebcam().finally(()=>{this.startPromise=void 0}),await this.startPromise)}async startWebcam(){try{let e=await this.services.detect.startWebcamDetection();if(!this.shouldRun){e.stop();return}this.isWebcamRunning=!0,this.stopCurrentWebcam=e.stop}catch(e){throw this.services.options.onError?.(e),e}}stopWebcam(){this.stopCurrentWebcam?.(),this.stopCurrentWebcam=void 0,this.isWebcamRunning=!1}async handleCameraDisconnect(){if(this.isWebcamRunning&&(this.stopWebcam(),this.shouldRun)){if(this.services.shouldPauseDetection){this.resumeWhenUnpaused=!0;return}try{await this.ensureStarted()}catch(e){this.services.options.onError?.(e)}}}addVisibilityListener(){this.services.options.runInBackground||typeof document>`u`||document.addEventListener(`visibilitychange`,this.handleVisibilityChange)}removeVisibilityListener(){typeof document>`u`||document.removeEventListener(`visibilitychange`,this.handleVisibilityChange)}},_e=class{auth;camera;detectionEvents;detect;webcam;onCameraDisconnect;onLoginRequired;#e=0;#t=Y();#n;constructor(e){this.#n=e,this.camera=new ce,this.auth=new ae(this),this.detectionEvents=new A(this),this.detect=new fe(this),this.webcam=new ge(this)}get options(){return Object.freeze({...this.#n()})}get noFaceEventDelayMs(){return this.#e}get userPreferences(){return Object.freeze({...this.#t})}updateNoFaceEventDelayFromServer(e){this.#e=e}updateRequiredPasswordFromServer(e){this.#n().requiredPassword=e}setPhoneConfidenceThreshold(e){this.#n().phoneConfidenceThreshold=e,this.syncEffectivePhoneConfidenceThreshold()}degradeInferenceModelForMemoryPressure(){let e=this.#n();if(e.inputSize>416){e.inputSize=416;return}e.inputSize>320&&(e.inputSize=320)}get shouldPauseForHiddenPage(){return!this.options.runInBackground&&typeof document<`u`&&document.hidden}get shouldPauseDetection(){return this.shouldPauseForHiddenPage}applyPolicyItems(e,t){let n={...this.#t};for(let t of e)ye(n,t);this.#t=n,this.syncEffectivePhoneConfidenceThreshold(),this.#n().faceConfidenceThreshold=n.faceDetectionThreshold,this.updateRequiredPasswordFromServer(!1)}resetAuthenticatedState(){this.#e=0,this.#t=Y(),this.#n().requiredPassword=!1,this.syncEffectivePhoneConfidenceThreshold(),this.#n().faceConfidenceThreshold=this.#t.faceDetectionThreshold}syncEffectivePhoneConfidenceThreshold(){let e=this.#n();e.confidenceThreshold=e.phoneConfidenceThreshold??this.#t.mobileDetectionThreshold}};function Y(){return{mobileDetectionThreshold:.8,faceDetectionThreshold:.8,language:`en`,mobileDetectionEnabled:!0,noPersonDetectionEnabled:!0,multiPersonDetectionEnabled:!0,noPersonLockDuration:0,multiPersonLockDuration:0,mobileDetectionLockDuration:0,mobileDetectionMode:`auto_lock`,noPersonDetectionMode:`auto_lock`,multiPersonDetectionMode:`auto_lock`}}function ve(e){return e===`none`}function ye(e,t){let n=t.policy,r=!ve(t.policy),i=t.seconds*1e3,a=`sensitivity`in t?1-t.sensitivity/100:void 0;switch(t.basic_event_uuid){case E(`mobileDevice`):e.mobileDetectionEnabled=r,e.mobileDetectionLockDuration=i,e.mobileDetectionMode=n,a!==void 0&&(e.mobileDetectionThreshold=a);break;case E(`twoFacesDetected`):e.multiPersonDetectionEnabled=r,e.multiPersonLockDuration=i,e.multiPersonDetectionMode=n,a!==void 0&&(e.faceDetectionThreshold=a);break;case E(`noFaceDetected`):e.noPersonDetectionEnabled=r,e.noPersonLockDuration=i,e.noPersonDetectionMode=n;break;default:break}}var X=class e{#e;#t;#n=`anonymous`;#r=`idle`;#i=`idle`;#a;#o;static async init(t){return Z=new e(t),Z}constructor(e){Q(e);let t=we(),n=_(e.runtimeAssetBaseUrl);this.#t={...e,apiBaseUrl:m(e.apiBaseUrl),runtimeAssetBaseUrl:n,confidenceThreshold:e.phoneConfidenceThreshold??.8,faceConfidenceThreshold:l,iouThreshold:u,maxDetections:100,inputSize:re(e.modelInputSize,t),inputType:`float32`,detectionIntervalMs:e.detectionIntervalMs??300,requiredPassword:!1,eventReportingEnabled:e.eventReportingEnabled??!0,phoneRotationFallback:e.phoneRotationFallback??!1,phoneRotationFallbackMinIntervalMs:e.phoneRotationFallbackMinIntervalMs??500,phoneRotationFallbackExecution:e.phoneRotationFallbackExecution??`sequential`},this.#e=new _e(()=>this.#t),this.#e.onLoginRequired=()=>this.resetLoggedOutState(),Object.freeze(this)}async load(){this.assertUsable(),this.assertAuthenticated(`loading detection`);try{this.updateState({runtime:`loading`,lastError:void 0}),await this.#e.detect.load(),this.updateState({runtime:`idle`,lastError:void 0})}catch(e){let t=this.createSdkError(`MODEL_LOAD_FAILED`,`MonitorDog detection model failed to load.`,e);throw this.transitionToError(t),this.#t.onError?.(t),t}}async login(e){this.assertUsable();let t;try{t=await this.#e.auth.login(e)}catch(e){let t=this.createSdkError(`SESSION_TOKEN_FAILED`,`MonitorDog SDK session token could not be created.`,e);throw this.transitionToError(t),this.#t.onAuthError?.(t),t}try{await this.completeAuthenticatedLogin()}catch(e){let t=this.createSdkError(`ACCOUNT_POLICY_FAILED`,`MonitorDog account or policy could not be loaded.`,e);throw this.rollbackAuthenticatedLogin(),this.transitionToError(t),this.#t.onAuthError?.(t),t}return this.updateState({session:`authenticated`,runtime:this.#r===`stopped`?`stopped`:`idle`,camera:this.#e.webcam.isRunning?`active`:`idle`,lastError:void 0}),t}async logout(){if(this.assertUsable(),this.isDetectorRunning())throw this.lifecycleError(`DETECTOR_RUNNING`,`MonitorDog detector is running. Call stop() before logout().`);if(!this.#e.auth.getToken()){this.clearSessionState();return}try{await this.#e.auth.logout(),this.clearSessionState()}catch(e){let t=this.createSdkError(`LOGOUT_FAILED`,`MonitorDog logout failed.`,e);throw this.transitionToError(t),this.#t.onAuthError?.(t),t}finally{this.#e.auth.getToken()||(this.#n=`anonymous`)}}async start(){this.assertUsable(),this.assertAuthenticated(`starting detection`),!this.isDetectorRunning()&&(this.#o||=this.startRuntime().finally(()=>{this.#o=void 0}),await this.#o)}stop(){this.assertUsable(),this.hasRuntimeToStop()&&(this.stopLocalRuntime(),this.updateState({runtime:this.#n===`authenticated`?`stopped`:`idle`,camera:`idle`,lastError:void 0}))}setRunInBackground(e){this.assertUsable(),this.#t.runInBackground=e,this.#e.webcam.syncBackgroundPreference()}setEventReportingEnabled(e){this.assertUsable(),this.#t.eventReportingEnabled=e,e||this.#e.detectionEvents.reset()}setPhoneConfidenceThreshold(e){this.assertUsable(),$(e),this.#e.setPhoneConfidenceThreshold(e)}getUserPreferences(){return this.assertUsable(),this.#e.userPreferences}getState(){return Object.freeze({session:this.#n,runtime:this.#r,camera:this.#i,tokenExpiresAt:this.#e.auth.getTokenExpiresAt(),email:this.#e.auth.getSessionEmail(),lastError:this.#a})}dispose(){this.#r!==`disposed`&&(this.stopLocalRuntime(),this.#e.detect.dispose(),this.#e.auth.dispose(),this.#e.resetAuthenticatedState(),this.updateState({session:`anonymous`,runtime:`disposed`,camera:`idle`,lastError:void 0}))}async completeAuthenticatedLogin(){let e=await this.#e.auth.authorizedFetch(`/account/me`,{method:`GET`,headers:{"content-type":`application/json`}});if(!e.ok)throw Error(`MonitorDog account request failed: ${e.status}`);let t=await e.json(),n=await this.#e.auth.authorizedFetch(`/account/${t.uuid}/lock_policy`,{method:`GET`,headers:{"content-type":`application/json`}});if(!n.ok)throw Error(`MonitorDog lock policy request failed: ${n.status}`);let r=await n.json();this.#e.applyPolicyItems(r,t)}resetLoggedOutState(){this.stopLocalRuntime(),this.#e.detect.dispose(),this.#e.resetAuthenticatedState(),this.updateState({session:`anonymous`,runtime:`idle`,camera:`idle`,lastError:void 0})}rollbackAuthenticatedLogin(){this.#e.auth.dispose(),this.#e.resetAuthenticatedState()}async startRuntime(){try{this.updateState({runtime:`loading`,camera:`idle`,lastError:void 0}),await this.#e.detect.load(),this.updateState({runtime:`starting`,camera:`requesting`}),await this.#e.webcam.start(),this.updateState({runtime:`running`,camera:this.#e.shouldPauseDetection?`idle`:`active`,lastError:void 0})}catch(e){let t=this.createSdkError(this.getStartErrorCode(e),this.getStartErrorMessage(e),e);throw this.transitionToError(t,be(t.code)?`blocked`:`idle`),this.#t.onError?.(t),t}}clearSessionState(){this.#e.resetAuthenticatedState(),this.updateState({session:`anonymous`,runtime:`idle`,camera:`idle`,lastError:void 0})}stopLocalRuntime(){this.#e.webcam.stop()}assertUsable(){if(this.#r===`disposed`)throw this.lifecycleError(`ALREADY_DISPOSED`,`MonitorDog detector instance has already been disposed.`,void 0,`disposed`)}assertAuthenticated(e){if(!this.#e.auth.isAuthenticated())throw this.lifecycleError(`NOT_LOGGED_IN`,`MonitorDog login is required before ${e}.`)}lifecycleError(e,t,n,r=`error`){let i=this.createSdkError(e,t,n);return r===`error`?this.transitionToError(i):this.updateState({runtime:r,lastError:i},!0),i}transitionToError(e,t=this.#i){this.updateState({runtime:`error`,camera:t,lastError:e},!0)}updateState(e,t=!1){let n=!1;e.session!==void 0&&e.session!==this.#n&&(this.#n=e.session,n=!0),e.runtime!==void 0&&e.runtime!==this.#r&&(this.#r=e.runtime,n=!0),e.camera!==void 0&&e.camera!==this.#i&&(this.#i=e.camera,n=!0),`lastError`in e&&e.lastError!==this.#a&&(this.#a=e.lastError,n=!0),(n||t)&&this.notifyStatusChange()}notifyStatusChange(){try{this.#t.onStatusChange?.(this.getState())}catch(e){this.#t.onError?.(e)}}createSdkError(e,t,n){return y(n)?n:new v(e,t,n)}isDetectorRunning(){return this.#r===`running`||this.#r===`starting`||!!this.#o||this.#e.webcam.isRunning||this.#e.webcam.isStarting}hasRuntimeToStop(){return this.#r===`running`||this.#r===`starting`||this.#i!==`idle`||!!this.#o||this.#e.webcam.isRunning||this.#e.webcam.isStarting}getStartErrorCode(e){return this.isCameraPermissionError(e)?`CAMERA_PERMISSION_DENIED`:this.isCameraReadError(e)?`CAMERA_READ_FAILED`:this.#r===`loading`?`MODEL_LOAD_FAILED`:`RUNTIME_START_FAILED`}getStartErrorMessage(e){switch(this.getStartErrorCode(e)){case`CAMERA_PERMISSION_DENIED`:return`MonitorDog camera permission was denied.`;case`CAMERA_READ_FAILED`:return`MonitorDog camera could not be read.`;case`MODEL_LOAD_FAILED`:return`MonitorDog detection model failed to load.`;default:return`MonitorDog detector runtime failed to start.`}}isCameraPermissionError(e){return typeof DOMException<`u`&&e instanceof DOMException&&(e.name===`NotAllowedError`||e.name===`SecurityError`)}isCameraReadError(e){return typeof DOMException<`u`&&e instanceof DOMException&&e.name===`NotReadableError`}};function be(e){return e===`CAMERA_PERMISSION_DENIED`||e===`CAMERA_READ_FAILED`}var Z;function xe(e){return Z=new X(e),Z}async function Se(e){return X.init(e)}function Ce(){if(!Z)throw Error(`MonitorDog is not configured. Call MonitorDogDetector.init(), createMonitorDogDetector(), or configureMonitorDogDetector() before using getDetector().`);return Z}function we(){if(typeof navigator>`u`)return!1;let e=h();return e===`mobile`||e===`tablet`}function Q(e){if(!e||typeof e.apiBaseUrl!=`string`||e.apiBaseUrl.length===0)throw Error(`MonitorDog apiBaseUrl is required.`);if(typeof e.sessionTokenProvider!=`function`)throw Error(`MonitorDog sessionTokenProvider is required.`);if(typeof e.onDetect!=`function`)throw Error(`MonitorDog onDetect callback is required.`);if(e.runtimeAssetBaseUrl!==void 0&&_(e.runtimeAssetBaseUrl),e.modelInputSize!==void 0&&!ne(e.modelInputSize))throw Error(`MonitorDog modelInputSize must be "auto", 320, 416, or 640.`);if(e.phoneRotationFallback!==void 0&&typeof e.phoneRotationFallback!=`boolean`)throw Error(`MonitorDog phoneRotationFallback must be a boolean when provided.`);if(e.phoneRotationFallbackMinIntervalMs!==void 0&&!Te(e.phoneRotationFallbackMinIntervalMs))throw Error(`MonitorDog phoneRotationFallbackMinIntervalMs must be a non-negative finite number when provided.`);if(e.phoneRotationFallbackExecution!==void 0&&e.phoneRotationFallbackExecution!==`sequential`&&e.phoneRotationFallbackExecution!==`parallel`)throw Error(`MonitorDog phoneRotationFallbackExecution must be "sequential" or "parallel" when provided.`);$(e.phoneConfidenceThreshold)}function Te(e){return typeof e==`number`&&Number.isFinite(e)&&e>=0}function $(e){if(e!==void 0&&(typeof e!=`number`||!Number.isFinite(e)||e<0||e>1))throw Error(`MonitorDog phoneConfidenceThreshold must be a finite number between 0 and 1 when provided.`)}e.MonitorDogDetector=X,e.MonitorDogSdkError=v,e.configureMonitorDogDetector=xe,e.createMonitorDogDetector=Se,e.getDetector=Ce,e.isMonitorDogSdkError=y});
|
|
@@ -7,6 +7,7 @@ import { WebcamService } from "./webcam-service";
|
|
|
7
7
|
export type DetectorOptions = Required<Pick<MonitorDogDetectorOptions, "apiBaseUrl">> & MonitorDogDetectorOptions & {
|
|
8
8
|
confidenceThreshold: number;
|
|
9
9
|
faceConfidenceThreshold: number;
|
|
10
|
+
phoneConfidenceThreshold?: number;
|
|
10
11
|
requiredPassword: boolean;
|
|
11
12
|
iouThreshold: number;
|
|
12
13
|
maxDetections: number;
|
|
@@ -33,9 +34,11 @@ export declare class ServiceContainer {
|
|
|
33
34
|
get userPreferences(): Readonly<UserPreferences>;
|
|
34
35
|
updateNoFaceEventDelayFromServer(delayMs: number): void;
|
|
35
36
|
updateRequiredPasswordFromServer(requiredPassword: boolean): void;
|
|
37
|
+
setPhoneConfidenceThreshold(threshold: number | undefined): void;
|
|
36
38
|
degradeInferenceModelForMemoryPressure(): void;
|
|
37
39
|
get shouldPauseForHiddenPage(): boolean;
|
|
38
40
|
get shouldPauseDetection(): boolean;
|
|
39
41
|
applyPolicyItems(items: LockPolicyResponse[], _account: AccountResponse): void;
|
|
40
42
|
resetAuthenticatedState(): void;
|
|
43
|
+
private syncEffectivePhoneConfidenceThreshold;
|
|
41
44
|
}
|
|
@@ -7,6 +7,7 @@ export declare class DetectService {
|
|
|
7
7
|
private loadPromise?;
|
|
8
8
|
private lastInferenceMemoryErrorAt;
|
|
9
9
|
private lastParallelPhoneRotationFallbackAt;
|
|
10
|
+
private phoneCandidateHistory;
|
|
10
11
|
constructor(services: ServiceContainer);
|
|
11
12
|
load(): Promise<void>;
|
|
12
13
|
detect(source: MonitorDogImageSource): Promise<InternalDetectionResult>;
|
|
@@ -14,6 +15,11 @@ export declare class DetectService {
|
|
|
14
15
|
detectVideo(video: HTMLVideoElement): MonitorDogVideoDetectionController;
|
|
15
16
|
startWebcamDetection(): Promise<MonitorDogWebcamDetectionController>;
|
|
16
17
|
dispose(): void;
|
|
18
|
+
private confirmTemporalPhoneCandidates;
|
|
19
|
+
private isTemporallyConfirmedPhoneCandidate;
|
|
20
|
+
private getRecentPhoneCandidateHistory;
|
|
21
|
+
private prunePhoneCandidateHistory;
|
|
22
|
+
private resetPhoneCandidateHistory;
|
|
17
23
|
private runParallelPhoneRotationFallback;
|
|
18
24
|
private runWorkerInference;
|
|
19
25
|
private handleDetectionLoopError;
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,9 @@ export interface MonitorDogDetection {
|
|
|
11
11
|
width: number;
|
|
12
12
|
height: number;
|
|
13
13
|
}
|
|
14
|
+
export interface InternalPhoneDetectionCandidate extends MonitorDogDetection {
|
|
15
|
+
handOverlapped: boolean;
|
|
16
|
+
}
|
|
14
17
|
export interface MonitorDogDetectionResult {
|
|
15
18
|
faceDetected: boolean;
|
|
16
19
|
phoneDetected: boolean;
|
|
@@ -19,7 +22,9 @@ export interface MonitorDogDetectionResult {
|
|
|
19
22
|
faceDetections: MonitorDogDetection[];
|
|
20
23
|
phoneDetections: MonitorDogDetection[];
|
|
21
24
|
}
|
|
22
|
-
export
|
|
25
|
+
export interface InternalDetectionResult extends MonitorDogDetectionResult {
|
|
26
|
+
phoneCandidates: InternalPhoneDetectionCandidate[];
|
|
27
|
+
}
|
|
23
28
|
export interface DetectionEvent {
|
|
24
29
|
type: MonitorDogDetectionEventType;
|
|
25
30
|
payload: CreateEventPayload;
|
|
@@ -70,6 +75,7 @@ export interface MonitorDogDetectorOptions {
|
|
|
70
75
|
detectionIntervalMs?: number;
|
|
71
76
|
runtimeAssetBaseUrl?: string;
|
|
72
77
|
modelInputSize?: MonitorDogModelInputSize;
|
|
78
|
+
phoneConfidenceThreshold?: number;
|
|
73
79
|
phoneRotationFallback?: boolean;
|
|
74
80
|
phoneRotationFallbackMinIntervalMs?: number;
|
|
75
81
|
phoneRotationFallbackExecution?: MonitorDogPhoneRotationFallbackExecution;
|