@incodetech/core 2.0.0 → 2.0.1-rc.0
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/Actor-CI32dTbG.d.ts +2 -0
- package/dist/BaseWasmProvider-C_DLEI40.esm.js +1118 -0
- package/dist/BrowserStorageProvider-CuOW1Er2.esm.js +55 -0
- package/dist/BrowserTimerProvider-DhNc_x02.esm.js +22 -0
- package/dist/ITimerCapability-C67ZRskg.esm.js +7 -0
- package/dist/IpifyProvider-D7jx52AL.esm.js +139 -0
- package/dist/Manager-C8PrhBOx.d.ts +19 -0
- package/dist/MotionSensorProvider-4v7xkqAp.esm.js +254 -0
- package/dist/OpenViduRecordingProvider-CMu6XVdc.esm.js +87 -0
- package/dist/StateMachine-BCQrZJhf.d.ts +2 -0
- package/dist/WasmUtilProvider-j98OJf-S.esm.js +114 -0
- package/dist/addressSearch-BpTbTWCa.esm.js +430 -0
- package/dist/ae-signature-DDDZmWXj.esm.js +12 -0
- package/dist/ae-signature.d.ts +25 -0
- package/dist/ae-signature.esm.js +8 -0
- package/dist/antifraud.d.ts +57 -0
- package/dist/antifraud.esm.js +45 -0
- package/dist/antifraudStateMachine-O0TMf6yc.esm.js +39 -0
- package/dist/api-CESGtpbH.esm.js +53 -0
- package/dist/authentication.d.ts +12 -0
- package/dist/authentication.esm.js +25 -0
- package/dist/authenticationManager-5M-fKzXx.esm.js +67 -0
- package/dist/authenticationManager-C83GNIhl.d.ts +66 -0
- package/dist/authenticationStateMachine-BMZqatiF.esm.js +139 -0
- package/dist/backCameraStream-DMdMeGk2.esm.js +346 -0
- package/dist/browserSimulation-gxD8cSpM.esm.js +20 -0
- package/dist/camera-DBSxa6ML.d.ts +4 -0
- package/dist/camera-PA2Ljri3.esm.js +22 -0
- package/dist/camera.d.ts +15 -0
- package/dist/camera.esm.js +5 -0
- package/dist/chunk-CRF6K_H_.esm.js +49 -0
- package/dist/consent.d.ts +398 -0
- package/dist/consent.esm.js +79 -0
- package/dist/consentStateMachine-CCT-B60O.esm.js +151 -0
- package/dist/cpf-PPz2Njto.esm.js +38 -0
- package/dist/cpf-ocr.d.ts +204 -0
- package/dist/cpf-ocr.esm.js +177 -0
- package/dist/cross-document-data-match.d.ts +34 -0
- package/dist/cross-document-data-match.esm.js +71 -0
- package/dist/curp-validation.d.ts +188 -0
- package/dist/curp-validation.esm.js +110 -0
- package/dist/curpValidationStateMachine-CitWLr2c.esm.js +595 -0
- package/dist/custom-fields.d.ts +115 -0
- package/dist/custom-fields.esm.js +177 -0
- package/dist/custom-watchlist.d.ts +66 -0
- package/dist/custom-watchlist.esm.js +86 -0
- package/dist/dateUtils-UoN5xswP.esm.js +23 -0
- package/dist/deepsightLoader-Cm4JIT_z.esm.js +52 -0
- package/dist/deepsightService-CEVxzehb.d.ts +412 -0
- package/dist/deepsightService-O74l4Y__.esm.js +489 -0
- package/dist/device.d.ts +46 -0
- package/dist/device.esm.js +106 -0
- package/dist/displayErrors-DqJ_IbsG.d.ts +39 -0
- package/dist/document-capture.d.ts +906 -0
- package/dist/document-capture.esm.js +156 -0
- package/dist/document-upload.d.ts +331 -0
- package/dist/document-upload.esm.js +203 -0
- package/dist/documentCaptureStateMachine-BqzTDy9k.esm.js +394 -0
- package/dist/dynamic-forms.d.ts +178 -0
- package/dist/dynamic-forms.esm.js +323 -0
- package/dist/ekyb.d.ts +148 -0
- package/dist/ekyb.esm.js +127 -0
- package/dist/ekybStateMachine-BR2let5f.esm.js +674 -0
- package/dist/ekyc.d.ts +164 -0
- package/dist/ekyc.esm.js +104 -0
- package/dist/ekycStateMachine-oeO0Iekd.esm.js +10626 -0
- package/dist/electronic-signature.d.ts +4 -0
- package/dist/electronic-signature.esm.js +7 -0
- package/dist/electronicSignatureManager-D9OHzTpG.esm.js +428 -0
- package/dist/email.d.ts +4 -0
- package/dist/email.esm.js +9 -0
- package/dist/emailManager-DIfnS5g1.d.ts +352 -0
- package/dist/emailManager-wAV0LE-H.esm.js +238 -0
- package/dist/emailStateMachine-DOf4j58N.esm.js +292 -0
- package/dist/endpoints-CnN3SyDa.esm.js +87 -0
- package/dist/events-D6-e4vok.esm.js +596 -0
- package/dist/events.d.ts +265 -0
- package/dist/events.esm.js +4 -0
- package/dist/extensibility.d.ts +122 -0
- package/dist/extensibility.esm.js +43 -0
- package/dist/face-match.d.ts +228 -0
- package/dist/face-match.esm.js +173 -0
- package/dist/faceCaptureManagerFactory-Dh2PdGlF.esm.js +290 -0
- package/dist/faceCaptureManagerFactory-yqtpxjnN.d.ts +690 -0
- package/dist/faceCaptureSetup-B3faSpYA.esm.js +873 -0
- package/dist/faceMatchStateMachine-DNFrxTFS.esm.js +127 -0
- package/dist/flow-events.d.ts +6 -0
- package/dist/flow-events.esm.js +0 -0
- package/dist/flow.d.ts +358 -0
- package/dist/flow.esm.js +825 -0
- package/dist/flowCompletionService-DhkT4SRY.d.ts +10 -0
- package/dist/flowCompletionService-P54yzGvA.esm.js +13 -0
- package/dist/flowServices-DG3IdWw6.esm.js +188 -0
- package/dist/geolocation.d.ts +127 -0
- package/dist/geolocation.esm.js +89 -0
- package/dist/geolocationStateMachine-asasuHY2.esm.js +105 -0
- package/dist/getBrowser-BSXUTWXw.esm.js +41 -0
- package/dist/getDeviceClass-BSntT9_j.esm.js +14 -0
- package/dist/government-validation.d.ts +67 -0
- package/dist/government-validation.esm.js +81 -0
- package/dist/governmentValidationStateMachine-BDDYrJTo.esm.js +271 -0
- package/dist/home.d.ts +99 -0
- package/dist/home.esm.js +61 -0
- package/dist/http.d.ts +68 -0
- package/dist/http.esm.js +3 -0
- package/dist/id-ocr.d.ts +635 -0
- package/dist/id-ocr.esm.js +86 -0
- package/dist/id-verification.d.ts +190 -0
- package/dist/id-verification.esm.js +43 -0
- package/dist/id.d.ts +24 -0
- package/dist/id.esm.js +164 -0
- package/dist/idCaptureManager-Fyd0eam-.d.ts +958 -0
- package/dist/idCaptureManager-ZPkD7Gjk.esm.js +581 -0
- package/dist/idCaptureStateMachine-BK0bPHoc.esm.js +2963 -0
- package/dist/idOcrStateMachine-YbjjC_Gg.esm.js +388 -0
- package/dist/idVerificationStateMachine-xbw9HP1Z.esm.js +71 -0
- package/dist/identity-reuse.d.ts +530 -0
- package/dist/identity-reuse.esm.js +274 -0
- package/dist/index-BcRG8rtJ.d.ts +97 -0
- package/dist/index-DZoqeAo9.d.ts +1177 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.esm.js +12 -0
- package/dist/invokeOnCaptureCallback-rc6kBHo5.esm.js +30 -0
- package/dist/lib-BB0B_qQX.esm.js +12499 -0
- package/dist/mandatory-consent.d.ts +412 -0
- package/dist/mandatory-consent.esm.js +78 -0
- package/dist/mandatoryConsentStateMachine-Cnco1jvn.esm.js +126 -0
- package/dist/openviduLazy-Cm0XFh_v.esm.js +3 -0
- package/dist/openviduLazy-Cok70ZSg.esm.js +12 -0
- package/dist/permissionServices-D_i6nzEw.esm.js +50 -0
- package/dist/phone.d.ts +4 -0
- package/dist/phone.esm.js +9 -0
- package/dist/phoneManager-B6M30hKE.d.ts +397 -0
- package/dist/phoneManager-DAJbGhlY.esm.js +256 -0
- package/dist/phoneStateMachine-CuPARRaT.esm.js +351 -0
- package/dist/platform-CfrjKhmi.esm.js +83 -0
- package/dist/qe-signature-DFo_Cc-I.esm.js +12 -0
- package/dist/qe-signature.d.ts +25 -0
- package/dist/qe-signature.esm.js +8 -0
- package/dist/recordingService-Ig2UgbLv.esm.js +1003 -0
- package/dist/redirect-to-mobile.d.ts +107 -0
- package/dist/redirect-to-mobile.esm.js +102 -0
- package/dist/redirectToMobileStateMachine-BOEqe46A.esm.js +249 -0
- package/dist/runChildModule-CqqwqAkW.esm.js +219 -0
- package/dist/selfie.d.ts +26 -0
- package/dist/selfie.esm.js +146 -0
- package/dist/selfieManager-D0lSgd-J.d.ts +68 -0
- package/dist/selfieManager-Duisl7qN.esm.js +60 -0
- package/dist/selfieStateMachine-D76whWEf.esm.js +68 -0
- package/dist/session-CGtQJJzB.esm.js +3206 -0
- package/dist/session.d.ts +217 -0
- package/dist/session.esm.js +9 -0
- package/dist/setup-C5AITV8m.d.ts +254 -0
- package/dist/setup-DsM8IG7k.esm.js +887 -0
- package/dist/signature.d.ts +94 -0
- package/dist/signature.esm.js +66 -0
- package/dist/signatureStateMachine-B5-QVUve.esm.js +132 -0
- package/dist/stats-CIfiPzb1.esm.js +16 -0
- package/dist/stats.d.ts +16 -0
- package/dist/stats.esm.js +4 -0
- package/dist/trust-graph.d.ts +54 -0
- package/dist/trust-graph.esm.js +56 -0
- package/dist/types-B06Ypu2F.d.ts +49 -0
- package/dist/types-BP1m8VRw.d.ts +340 -0
- package/dist/types-CAD4va6a.d.ts +5 -0
- package/dist/types-CFV9G_7j.d.ts +24 -0
- package/dist/warmup-CEcppFiS.d.ts +63 -0
- package/dist/wasm.d.ts +15 -0
- package/dist/wasm.esm.js +12 -0
- package/dist/watchlist-for-business.d.ts +79 -0
- package/dist/watchlist-for-business.esm.js +148 -0
- package/dist/watchlist.d.ts +62 -0
- package/dist/watchlist.esm.js +86 -0
- package/dist/watchlistServices-DMbUhkBX.esm.js +12 -0
- package/dist/workflow.d.ts +907 -0
- package/dist/workflow.esm.js +702 -0
- package/dist/xstate.esm-B70JrNqo.esm.js +3404 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1118 @@
|
|
|
1
|
+
//#region ../infra/src/providers/browser/SemaphoreProvider.ts
|
|
2
|
+
const DEFAULT_ACQUIRE_TIMEOUT_MS = 3e4;
|
|
3
|
+
/**
|
|
4
|
+
* Counting-semaphore implementation of {@link IConcurrencyCapability}.
|
|
5
|
+
*
|
|
6
|
+
* Default `maxConcurrent=1` makes it behave as a mutex; set higher for
|
|
7
|
+
* parallel-bounded access. Each acquire returns a numeric `threadId` for
|
|
8
|
+
* tracing — the slot index, in `[0, maxConcurrent)` — that must be passed back
|
|
9
|
+
* to `release`.
|
|
10
|
+
*
|
|
11
|
+
* If all slots stay occupied longer than `acquireTimeoutMs` (default 30s), the
|
|
12
|
+
* pending wait still resolves when a slot frees, but `console.error` is logged
|
|
13
|
+
* as a deadlock-watchdog signal. Misuse — out-of-range or double-released
|
|
14
|
+
* `threadId`s — is also logged and treated as a no-op rather than thrown.
|
|
15
|
+
*/
|
|
16
|
+
var SemaphoreProvider = class {
|
|
17
|
+
constructor(maxConcurrent = 1, acquireTimeoutMs = DEFAULT_ACQUIRE_TIMEOUT_MS) {
|
|
18
|
+
this.pending = [];
|
|
19
|
+
this.available = [];
|
|
20
|
+
this.processing = /* @__PURE__ */ new Set();
|
|
21
|
+
this.maxId = maxConcurrent - 1;
|
|
22
|
+
this.acquireTimeoutMs = acquireTimeoutMs;
|
|
23
|
+
for (let i = 0; i < maxConcurrent; i++) this.available.push(i);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Acquire a slot. Resolves immediately with the slot's `threadId` if one is
|
|
27
|
+
* free, otherwise queues FIFO and resolves when a holder releases.
|
|
28
|
+
*/
|
|
29
|
+
async acquire() {
|
|
30
|
+
if (this.available.length > 0) {
|
|
31
|
+
const threadId = this.available.shift();
|
|
32
|
+
this.processing.add(threadId);
|
|
33
|
+
return threadId;
|
|
34
|
+
}
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const entry = {
|
|
37
|
+
resolve: (threadId) => {
|
|
38
|
+
clearTimeout(entry.timer);
|
|
39
|
+
this.processing.add(threadId);
|
|
40
|
+
resolve(threadId);
|
|
41
|
+
},
|
|
42
|
+
reject: (error) => {
|
|
43
|
+
clearTimeout(entry.timer);
|
|
44
|
+
reject(error);
|
|
45
|
+
},
|
|
46
|
+
timer: setTimeout(() => {
|
|
47
|
+
console.error(`SemaphoreProvider: all slots occupied for ${this.acquireTimeoutMs}ms, ${this.pending.length} request(s) waiting`);
|
|
48
|
+
}, this.acquireTimeoutMs)
|
|
49
|
+
};
|
|
50
|
+
this.pending.push(entry);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Return a slot to the pool. The next pending waiter (if any) is resolved
|
|
55
|
+
* with this `threadId`; otherwise the slot is added back to `available`.
|
|
56
|
+
* Releasing an out-of-range or already-released `threadId` logs an error and
|
|
57
|
+
* is otherwise a no-op.
|
|
58
|
+
*/
|
|
59
|
+
release(threadId) {
|
|
60
|
+
if (threadId < 0 || threadId > this.maxId) {
|
|
61
|
+
console.error(`SemaphoreProvider: invalid threadId ${threadId}`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!this.processing.has(threadId)) {
|
|
65
|
+
console.error(`SemaphoreProvider: double-release of threadId ${threadId}`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
this.processing.delete(threadId);
|
|
69
|
+
if (this.pending.length > 0) this.pending.shift().resolve(threadId);
|
|
70
|
+
else this.available.unshift(threadId);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Cancel all pending acquires with a reset error and replenish all slots.
|
|
74
|
+
* Existing holders are not contacted — a `release` after reset will log a
|
|
75
|
+
* double-release warning rather than disrupt the new pool.
|
|
76
|
+
*/
|
|
77
|
+
reset() {
|
|
78
|
+
const waiters = this.pending;
|
|
79
|
+
this.pending = [];
|
|
80
|
+
this.available = [];
|
|
81
|
+
this.processing = /* @__PURE__ */ new Set();
|
|
82
|
+
for (let i = 0; i <= this.maxId; i++) this.available.push(i);
|
|
83
|
+
for (const waiter of waiters) waiter.reject(/* @__PURE__ */ new Error("SemaphoreProvider reset: pending acquire cancelled"));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Acquire a slot, invoke `fn(threadId, ...args)`, then release the slot in a
|
|
87
|
+
* `finally` block. Errors thrown by `fn` propagate to the caller after the
|
|
88
|
+
* slot is released.
|
|
89
|
+
*/
|
|
90
|
+
async withLock(fn, ...args) {
|
|
91
|
+
const threadId = await this.acquire();
|
|
92
|
+
try {
|
|
93
|
+
return await fn(threadId, ...args);
|
|
94
|
+
} finally {
|
|
95
|
+
this.release(threadId);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region ../infra/src/wasm/apiUtilities.ts
|
|
102
|
+
if (typeof window !== "undefined" && window.wasmArrayBuffer === void 0) window.wasmArrayBuffer = null;
|
|
103
|
+
let loadPromise = null;
|
|
104
|
+
let loadedResult = null;
|
|
105
|
+
async function importGlue(glueCodePath) {
|
|
106
|
+
const mod = await import(
|
|
107
|
+
/* @vite-ignore */
|
|
108
|
+
/* webpackIgnore: true */
|
|
109
|
+
glueCodePath
|
|
110
|
+
);
|
|
111
|
+
if (typeof mod.Module !== "function") throw new Error(`WASM glue at ${glueCodePath} did not export a Module factory`);
|
|
112
|
+
return mod.Module;
|
|
113
|
+
}
|
|
114
|
+
async function fetchWasmBinary(libPath) {
|
|
115
|
+
const response = await fetch(libPath);
|
|
116
|
+
if (!response.ok) throw new Error(`Failed to fetch WASM binary at ${libPath}: ${response.status} ${response.statusText}`);
|
|
117
|
+
const buffer = await response.arrayBuffer();
|
|
118
|
+
return new Uint8Array(buffer);
|
|
119
|
+
}
|
|
120
|
+
async function instantiate(factory, libPath) {
|
|
121
|
+
window.wasmArrayBuffer = await fetchWasmBinary(libPath);
|
|
122
|
+
return await factory();
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Derive a sibling `.js` path from a `.wasm` path by replacing the trailing
|
|
126
|
+
* extension. Used as a last-resort default when callers pass a `wasmSimdPath`
|
|
127
|
+
* without an accompanying `glueCodeSimdPath` — matches the legacy CDN naming
|
|
128
|
+
* convention (`webLibSimd.wasm` <-> `webLibSimd.js`).
|
|
129
|
+
*/
|
|
130
|
+
function deriveSiblingJsPath(wasmPath) {
|
|
131
|
+
return wasmPath.replace(/\.wasm($|\?)/, ".js$1");
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Load the WASM module, memoized. Subsequent calls return the same instance.
|
|
135
|
+
*
|
|
136
|
+
* SIMD-first with fallback: if `useSimd !== false` and a `wasmSimdPath` is
|
|
137
|
+
* provided, the SIMD binary + SIMD glue are tried first; on instantiation
|
|
138
|
+
* failure the non-SIMD binary + non-SIMD glue are used. Each `.wasm` is
|
|
139
|
+
* paired with its matching `.js` glue — they cannot be mixed.
|
|
140
|
+
*/
|
|
141
|
+
async function loadWasmModule(config) {
|
|
142
|
+
if (loadedResult) return loadedResult;
|
|
143
|
+
if (loadPromise) return loadPromise;
|
|
144
|
+
loadPromise = (async () => {
|
|
145
|
+
try {
|
|
146
|
+
const wantSimd = config.useSimd !== false;
|
|
147
|
+
const simdBinary = config.wasmSimdPath ?? config.wasmPath;
|
|
148
|
+
if (wantSimd && simdBinary !== config.wasmPath) try {
|
|
149
|
+
const simdFactory = await importGlue(config.glueCodeSimdPath ?? deriveSiblingJsPath(simdBinary));
|
|
150
|
+
window.Module = simdFactory;
|
|
151
|
+
const result$1 = {
|
|
152
|
+
wasmModule: await instantiate(simdFactory, simdBinary),
|
|
153
|
+
simdUsed: true
|
|
154
|
+
};
|
|
155
|
+
loadedResult = result$1;
|
|
156
|
+
return result$1;
|
|
157
|
+
} catch (simdError) {
|
|
158
|
+
console.warn("[Incode SDK] SIMD WASM load failed, falling back to non-SIMD", simdError);
|
|
159
|
+
}
|
|
160
|
+
const nonSimdFactory = await importGlue(config.glueCodePath);
|
|
161
|
+
window.Module = nonSimdFactory;
|
|
162
|
+
const result = {
|
|
163
|
+
wasmModule: await instantiate(nonSimdFactory, config.wasmPath),
|
|
164
|
+
simdUsed: false
|
|
165
|
+
};
|
|
166
|
+
loadedResult = result;
|
|
167
|
+
return result;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
loadPromise = null;
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
})();
|
|
173
|
+
return loadPromise;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region ../infra/src/wasm/IdCaptureModelType.ts
|
|
178
|
+
let IdCaptureModelType = /* @__PURE__ */ function(IdCaptureModelType$1) {
|
|
179
|
+
IdCaptureModelType$1[IdCaptureModelType$1["IdCaptureV1x"] = 0] = "IdCaptureV1x";
|
|
180
|
+
IdCaptureModelType$1[IdCaptureModelType$1["IdCaptureV2x"] = 1] = "IdCaptureV2x";
|
|
181
|
+
IdCaptureModelType$1[IdCaptureModelType$1["IdCaptureV3x"] = 2] = "IdCaptureV3x";
|
|
182
|
+
return IdCaptureModelType$1;
|
|
183
|
+
}({});
|
|
184
|
+
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region ../infra/src/wasm/WasmPipelineType.ts
|
|
187
|
+
let WasmPipelineType = /* @__PURE__ */ function(WasmPipelineType$1) {
|
|
188
|
+
WasmPipelineType$1[WasmPipelineType$1["IdBlurGlarePipeline"] = 0] = "IdBlurGlarePipeline";
|
|
189
|
+
WasmPipelineType$1[WasmPipelineType$1["IdBarcodeAndTextQualityPipeline"] = 1] = "IdBarcodeAndTextQualityPipeline";
|
|
190
|
+
WasmPipelineType$1[WasmPipelineType$1["IdVideoSelfiePipeline"] = 2] = "IdVideoSelfiePipeline";
|
|
191
|
+
WasmPipelineType$1[WasmPipelineType$1["SelfieWithAggregationMetrics"] = 3] = "SelfieWithAggregationMetrics";
|
|
192
|
+
WasmPipelineType$1[WasmPipelineType$1["SelfieWithQualityMetrics"] = 4] = "SelfieWithQualityMetrics";
|
|
193
|
+
WasmPipelineType$1[WasmPipelineType$1["OnDeviceSelfieWorkflow"] = 5] = "OnDeviceSelfieWorkflow";
|
|
194
|
+
return WasmPipelineType$1;
|
|
195
|
+
}({});
|
|
196
|
+
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region ../infra/src/wasm/wasmCallUtils.ts
|
|
199
|
+
async function wasmCallWrapper(concurrency, wasmMethod, useSemaphore = true, ...args) {
|
|
200
|
+
const innerCall = async () => {
|
|
201
|
+
while (true) {
|
|
202
|
+
if (wasmMethod(...args)) return;
|
|
203
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
if (useSemaphore) {
|
|
207
|
+
await concurrency.withLock(innerCall);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
await innerCall();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region ../infra/src/wasm/wasmWebClient.ts
|
|
215
|
+
/**
|
|
216
|
+
* JavaScript bridge for the C++ `WebClient` exposed by `webLib.wasm`.
|
|
217
|
+
*
|
|
218
|
+
* The pinned WASM bundle ships a `WebClient` class alongside the ML pipeline
|
|
219
|
+
* APIs. This module instantiates it once (sharing the singleton `wasmModule`
|
|
220
|
+
* with `mlWasmJSApi`) and exposes a small TypeScript-friendly surface that
|
|
221
|
+
* `createWasmApi` can drive.
|
|
222
|
+
*
|
|
223
|
+
* Concurrency model mirrors the legacy implementation:
|
|
224
|
+
* - 4-slot semaphore keyed by per-request `threadId` so multiple HTTP calls
|
|
225
|
+
* can run in parallel without trampling shared C++ state.
|
|
226
|
+
* - Each WASM-side method that performs in-binary HTTP (request methods AND
|
|
227
|
+
* `createApi`'s in-binary `/e2ee/key` handshake) returns `false` while in
|
|
228
|
+
* flight and `true` once the C++ side has finished; the JS bridge awaits
|
|
229
|
+
* completion via {@link wasmCallWrapper}.
|
|
230
|
+
* - Result extraction (`getRequestResult`) is per-thread for HTTP requests
|
|
231
|
+
* and uses `threadId = -1` for the session-level `createApi` handshake.
|
|
232
|
+
*
|
|
233
|
+
* Encryption support: `setupConnection` accepts an `encryptionEnabled` flag
|
|
234
|
+
* that triggers the in-binary RSA/AES handshake. The OAEP MGF1 hash is
|
|
235
|
+
* controlled by the `useSha256` flag forwarded to the C++ `createApi`:
|
|
236
|
+
* `true` = SHA-256 MGF1, `false` = SHA-1 MGF1 (Java-compat default).
|
|
237
|
+
* State lives in the C++ binary and (for SDK config) in
|
|
238
|
+
* `@incodetech/core`'s `setup.ts` — this module caches nothing.
|
|
239
|
+
*/
|
|
240
|
+
const HTTP_CONCURRENCY = 4;
|
|
241
|
+
/**
|
|
242
|
+
* Sentinel `threadId` used by the session-level `createApi` handshake when we
|
|
243
|
+
* read its result via `getRequestResult`. Per-request HTTP traffic uses real
|
|
244
|
+
* thread ids handed out by the semaphore; the handshake has no slot of its
|
|
245
|
+
* own, so the C++ side stores its result under `-1`. Mirrors legacy
|
|
246
|
+
* `checkError(threadId = -1)`.
|
|
247
|
+
*/
|
|
248
|
+
const SETUP_THREAD_ID = -1;
|
|
249
|
+
var WasmWebClientError = class extends Error {
|
|
250
|
+
constructor(message, statusCode, data) {
|
|
251
|
+
super(message);
|
|
252
|
+
this.name = "WasmWebClientError";
|
|
253
|
+
this.status = statusCode;
|
|
254
|
+
this.statusCode = statusCode;
|
|
255
|
+
if (statusCode > 0 || data !== void 0) this.response = {
|
|
256
|
+
status: statusCode,
|
|
257
|
+
data
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
let webClientApi = null;
|
|
262
|
+
let initPromise = null;
|
|
263
|
+
let semaphore = new SemaphoreProvider(HTTP_CONCURRENCY);
|
|
264
|
+
const isWebClientWasm = (mod) => {
|
|
265
|
+
return typeof mod.WebClient === "function";
|
|
266
|
+
};
|
|
267
|
+
const toStringRecord = (obj) => {
|
|
268
|
+
if (!obj) return void 0;
|
|
269
|
+
const result = {};
|
|
270
|
+
for (const key of Object.keys(obj)) {
|
|
271
|
+
const value = obj[key];
|
|
272
|
+
if (value === void 0 || value === null) continue;
|
|
273
|
+
result[key] = String(value);
|
|
274
|
+
}
|
|
275
|
+
return result;
|
|
276
|
+
};
|
|
277
|
+
/**
|
|
278
|
+
* Loads the shared WASM module (if not already loaded) and instantiates the
|
|
279
|
+
* C++ `WebClient`. Idempotent — repeated calls reuse the existing instance.
|
|
280
|
+
*
|
|
281
|
+
* `glueCodeSimdPath` is paired with `wasmSimdPath` (`webLibSimd.js` for
|
|
282
|
+
* `webLibSimd.wasm`). Pass `''` to derive it automatically from
|
|
283
|
+
* `wasmSimdPath` by replacing the `.wasm` extension with `.js`.
|
|
284
|
+
*/
|
|
285
|
+
async function initializeWasmWebClient(wasmPath, wasmSimdPath, glueCodePath, glueCodeSimdPath, useSimd) {
|
|
286
|
+
if (webClientApi) return;
|
|
287
|
+
if (initPromise) return initPromise;
|
|
288
|
+
initPromise = (async () => {
|
|
289
|
+
try {
|
|
290
|
+
const { wasmModule } = await loadWasmModule({
|
|
291
|
+
wasmPath,
|
|
292
|
+
wasmSimdPath,
|
|
293
|
+
glueCodePath,
|
|
294
|
+
glueCodeSimdPath: glueCodeSimdPath || void 0,
|
|
295
|
+
useSimd
|
|
296
|
+
});
|
|
297
|
+
if (!isWebClientWasm(wasmModule)) throw new Error("WASM module does not expose a WebClient class. Update webLib.wasm to a build that includes WebClient.");
|
|
298
|
+
webClientApi = new wasmModule.WebClient();
|
|
299
|
+
} catch (error) {
|
|
300
|
+
initPromise = null;
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
303
|
+
})();
|
|
304
|
+
return initPromise;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Configures the C++ WebClient connection. Safe to call multiple times — each
|
|
308
|
+
* call resets the underlying session and re-applies default headers.
|
|
309
|
+
*
|
|
310
|
+
* `defaultHeaders` are applied **before** `createApi`, so the in-binary RSA
|
|
311
|
+
* handshake (`GET /e2ee/key/v2` + `POST /e2ee/key`) sees the same headers as
|
|
312
|
+
* data requests. Without this ordering, environments that require auth/version
|
|
313
|
+
* headers on the handshake (e.g. `x-api-key`, `api-version`) would 401/4xx
|
|
314
|
+
* before any encrypted traffic ever flowed.
|
|
315
|
+
*
|
|
316
|
+
* The C++ `createApi` returns `false` while the in-binary handshake is still
|
|
317
|
+
* in flight and `true` once both the `/e2ee/key/v2` GET and the `/e2ee/key`
|
|
318
|
+
* POST have completed (encryption on) or once the WebClient is configured
|
|
319
|
+
* (encryption off — same boolean convention, just no network traffic). The
|
|
320
|
+
* JS bridge waits on that boolean via {@link wasmCallWrapper}, then surfaces
|
|
321
|
+
* any handshake error captured on the session-level (`threadId = -1`)
|
|
322
|
+
* request slot. Mirrors legacy
|
|
323
|
+
* `ms-incodesmile-web/packages/incode-welcome/src/wasmUtils/webClientApi.ts`
|
|
324
|
+
* `setupConnection`.
|
|
325
|
+
*
|
|
326
|
+
* `useSha256` selects the OAEP MGF1 hash for the in-binary RSA wrap:
|
|
327
|
+
* - `true` → MGF1 = SHA-256. Caller is responsible for attaching the
|
|
328
|
+
* `X-RSA-Encryption-Scheme: RSA/NONE/OAEPWITHSHA-256ANDMGF1PADDING`
|
|
329
|
+
* header on subsequent encrypted requests so the server matches.
|
|
330
|
+
* - `false` → MGF1 = SHA-1 (Java-compat default). No scheme header needed;
|
|
331
|
+
* the server falls back to the legacy Java default.
|
|
332
|
+
*/
|
|
333
|
+
async function setupWasmConnection(apiURL, token, encryptionEnabled, defaultHeaders, useSha256) {
|
|
334
|
+
const api = ensureReady();
|
|
335
|
+
api.resetSessionState?.();
|
|
336
|
+
api.setDefaultHeaders?.(defaultHeaders);
|
|
337
|
+
await wasmCallWrapper(semaphore, () => api.createApi(apiURL, token, useSha256, encryptionEnabled), false);
|
|
338
|
+
const result = api.getRequestResult(SETUP_THREAD_ID);
|
|
339
|
+
if (result.hasError) throw new WasmWebClientError(result.errorMessage || "WebClient handshake failed", result.statusCode, result.result);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Executes a single HTTP request through the WASM WebClient.
|
|
343
|
+
*
|
|
344
|
+
* Acquires a semaphore slot for `threadId`, registers progress callbacks, runs
|
|
345
|
+
* the request via {@link wasmCallWrapper} (busy-polls until the WASM method
|
|
346
|
+
* returns `true`), then extracts the per-thread result. Throws
|
|
347
|
+
* {@link WasmWebClientError} on non-2xx or transport errors.
|
|
348
|
+
*
|
|
349
|
+
* The `ie` (inline-encryption) flag is forwarded to the C++ WebClient for
|
|
350
|
+
* mutating methods. Default `false` — the SDK HTTP path leaves payloads
|
|
351
|
+
* unencrypted at the WebClient layer. The C++ `WebApi.postFaceResults`
|
|
352
|
+
* pre-encrypts its JSON via `SessionEncryptor` and calls back into this
|
|
353
|
+
* function with `ie=true`; see {@link createWebApiHostAdapter}.
|
|
354
|
+
*/
|
|
355
|
+
async function executeWasmRequest(method, url, body, headers, params, timeout, onUploadProgress, ie = false) {
|
|
356
|
+
const api = ensureReady();
|
|
357
|
+
const stringParams = toStringRecord(params);
|
|
358
|
+
return semaphore.withLock(async (threadId) => {
|
|
359
|
+
if (onUploadProgress && api.setOnUploadProgress) api.setOnUploadProgress((event) => {
|
|
360
|
+
try {
|
|
361
|
+
onUploadProgress(event);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.error("WasmWebClient: error in onUploadProgress callback", error);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
try {
|
|
367
|
+
await wasmCallWrapper(semaphore, () => callWasmMethod(api, method, threadId, url, body, headers, stringParams, timeout, ie), false);
|
|
368
|
+
return readResult(api, threadId);
|
|
369
|
+
} finally {
|
|
370
|
+
api.clearOnUploadProgress?.(threadId);
|
|
371
|
+
api.clearOnDownloadProgress?.(threadId);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function callWasmMethod(api, method, threadId, url, body, headers, params, timeout, ie) {
|
|
376
|
+
switch (method) {
|
|
377
|
+
case "GET": return api.get(threadId, url, params, headers, timeout);
|
|
378
|
+
case "POST": return api.post(threadId, url, body ?? {}, headers, params, timeout, ie);
|
|
379
|
+
case "PUT": return api.put(threadId, url, body ?? {}, headers, params, timeout, ie);
|
|
380
|
+
case "PATCH": return api.patch(threadId, url, body ?? {}, headers, params, timeout, ie);
|
|
381
|
+
case "DELETE": return api.del(threadId, url, body ?? {}, headers, params, timeout, ie);
|
|
382
|
+
default: throw new Error(`Unsupported WASM HTTP method: ${method}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const DEFAULT_WEBAPI_TIMEOUT_MS = 3e4;
|
|
386
|
+
let webApiHttpTransport = null;
|
|
387
|
+
function setWebApiHttpTransport(transport) {
|
|
388
|
+
webApiHttpTransport = transport;
|
|
389
|
+
}
|
|
390
|
+
function createWebApiHostAdapter() {
|
|
391
|
+
return { post(url, body, config) {
|
|
392
|
+
const effectiveConfig = config ?? {};
|
|
393
|
+
const transport = webApiHttpTransport;
|
|
394
|
+
if (transport) return transport(url, body, effectiveConfig);
|
|
395
|
+
return executeWasmRequest("POST", url, body, effectiveConfig.headers ?? {}, effectiveConfig.params, effectiveConfig.timeout ?? DEFAULT_WEBAPI_TIMEOUT_MS, void 0, effectiveConfig.ie === true);
|
|
396
|
+
} };
|
|
397
|
+
}
|
|
398
|
+
function readResult(api, threadId) {
|
|
399
|
+
const response = api.getRequestResult(threadId);
|
|
400
|
+
if (response.hasError) throw new WasmWebClientError(response.errorMessage || "WasmWebClient request failed", response.statusCode, response.result);
|
|
401
|
+
return {
|
|
402
|
+
data: response.result,
|
|
403
|
+
status: response.statusCode
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function ensureReady() {
|
|
407
|
+
if (!webClientApi) throw new Error("WasmWebClient not initialized. Call initializeWasmWebClient() first.");
|
|
408
|
+
return webClientApi;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
//#endregion
|
|
412
|
+
//#region ../infra/src/wasm/mlWasmJSApi.ts
|
|
413
|
+
var MlWasmJSApi = class MlWasmJSApi {
|
|
414
|
+
constructor() {
|
|
415
|
+
this.versionsFile = null;
|
|
416
|
+
this.modelsBuffers = null;
|
|
417
|
+
this.inputImageBuffer = null;
|
|
418
|
+
this.wasmModule = null;
|
|
419
|
+
this.workflowApiUtilities = null;
|
|
420
|
+
this.utilityApi = null;
|
|
421
|
+
this.idCaptureWasmApi = null;
|
|
422
|
+
this.faceProcessingWasmApi = null;
|
|
423
|
+
this.webApi = null;
|
|
424
|
+
this.imageWidth_ = null;
|
|
425
|
+
this.imageHeight_ = null;
|
|
426
|
+
this.pipelines_ = null;
|
|
427
|
+
this.isInitialized_ = false;
|
|
428
|
+
this.inspectorOpened_ = false;
|
|
429
|
+
this.wasmCallSemaphore = new SemaphoreProvider();
|
|
430
|
+
this.Module = null;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Initiallization method, use it when the application starts
|
|
434
|
+
* TODO: this method can return the list of promises to be awaited together with other initialization code
|
|
435
|
+
* @param {string} webLibPath - The path to the webassembly binary
|
|
436
|
+
* @param {string} webLibPathSimd - The path to the SIMD version webassembly binary
|
|
437
|
+
* @param {string} glueCodePath - The path to the non-SIMD glue (`webLib.js`)
|
|
438
|
+
* @param {string} glueCodeSimdPath - The path to the SIMD-paired glue (`webLibSimd.js`); pass `''` to derive from `webLibPathSimd`
|
|
439
|
+
* @param {boolean} useSimd - Indicates whether SIMD optimizations should be used
|
|
440
|
+
* @param {string} versionsFile - The path to the file containing versioning information
|
|
441
|
+
* @param {Map.<WasmPipelineType, Array<string>>} pipelines - pipelines for initialization and corresponding models paths
|
|
442
|
+
* @returns {Promise<boolean>} A promise that resolves to a boolean indicating whether SIMD is enabled once the initialization is complete.
|
|
443
|
+
* @throws {Error} Throws an error if the initialization fails.
|
|
444
|
+
*/
|
|
445
|
+
async initialize(webLibPath, webLibPathSimd, glueCodePath, glueCodeSimdPath, useSimd, versionsFile, pipelines) {
|
|
446
|
+
await this.freeResources();
|
|
447
|
+
this.pipelines_ = pipelines;
|
|
448
|
+
this.versionsFile = versionsFile;
|
|
449
|
+
const { wasmModule, simdUsed } = await loadWasmModule({
|
|
450
|
+
glueCodePath,
|
|
451
|
+
glueCodeSimdPath: glueCodeSimdPath || void 0,
|
|
452
|
+
wasmPath: webLibPath,
|
|
453
|
+
wasmSimdPath: webLibPathSimd,
|
|
454
|
+
useSimd
|
|
455
|
+
});
|
|
456
|
+
this.wasmModule = wasmModule;
|
|
457
|
+
this.Module = window.Module;
|
|
458
|
+
this.workflowApiUtilities = new this.wasmModule.WorkflowApiUtilities();
|
|
459
|
+
this.utilityApi = new this.wasmModule.UtilityApi();
|
|
460
|
+
this.idCaptureWasmApi = new this.wasmModule.IdCaptureApi();
|
|
461
|
+
this.faceProcessingWasmApi = new this.wasmModule.FaceProcessingApi();
|
|
462
|
+
this.webApi = new this.wasmModule.WebApi(createWebApiHostAdapter());
|
|
463
|
+
this.isInitialized_ = true;
|
|
464
|
+
return simdUsed;
|
|
465
|
+
}
|
|
466
|
+
static getInstance() {
|
|
467
|
+
if (!MlWasmJSApi.instance) MlWasmJSApi.instance = new MlWasmJSApi();
|
|
468
|
+
return MlWasmJSApi.instance;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Returns initialization status
|
|
472
|
+
* @returns
|
|
473
|
+
*/
|
|
474
|
+
isInitialized() {
|
|
475
|
+
return this.workflowApiUtilities != null && this.utilityApi != null && this.idCaptureWasmApi != null && this.faceProcessingWasmApi != null && this.isInitialized_;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Allocate shared memory for image buffers (RGBA format)
|
|
479
|
+
* Use it every time when the frame dimentions changes
|
|
480
|
+
* @param {number} imageWidth - image width
|
|
481
|
+
* @param {number} imageHeight - image height
|
|
482
|
+
*/
|
|
483
|
+
async allocateImageBuffers(imageWidth, imageHeight) {
|
|
484
|
+
this.checkWasmInitialization("Unable to allocate image buffers, cpp API hasn't been initialized");
|
|
485
|
+
if (imageWidth && imageHeight && imageWidth == this.imageWidth_ && imageHeight == this.imageHeight_ && this.inputImageBuffer && imageWidth * imageHeight * 4 === this.inputImageBuffer.length) return;
|
|
486
|
+
this.imageWidth_ = imageWidth;
|
|
487
|
+
this.imageHeight_ = imageHeight;
|
|
488
|
+
this.inputImageBuffer = this.workflowApiUtilities.allocateInputImageBuffer(imageWidth, imageHeight, 4);
|
|
489
|
+
}
|
|
490
|
+
async handleDetectionCallAndUpdateState(type) {
|
|
491
|
+
this.checkWasmInitialization("Unable to update pipeline state, cpp API hasn't been initialized");
|
|
492
|
+
this.workflowApiUtilities.handleDetectionCallAndUpdateState(this.pipelineTypeToWasmEnum(type));
|
|
493
|
+
}
|
|
494
|
+
ens(image) {
|
|
495
|
+
this.checkWasmInitialization("Unable to encrypt the image, cpp API hasn't been initialized");
|
|
496
|
+
return this.utilityApi.ens(image);
|
|
497
|
+
}
|
|
498
|
+
isVirtualCamera(label) {
|
|
499
|
+
this.checkWasmInitialization("Unable to check if the camera is virtual, cpp API hasn't been initialized");
|
|
500
|
+
if (label) return this.utilityApi.isVirtualCamera(label);
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
estimatePerformance() {
|
|
504
|
+
this.checkWasmInitialization("Unable to estimate performance, cpp API hasn't been initialized");
|
|
505
|
+
return this.utilityApi.estimatePerformance();
|
|
506
|
+
}
|
|
507
|
+
async analyzeFrame(image) {
|
|
508
|
+
this.checkWasmInitialization("Unable to analyze the frame, cpp API hasn't been initialized");
|
|
509
|
+
await this.allocateImageBuffers(image.width, image.height);
|
|
510
|
+
this.inputImageBuffer.set(image.data);
|
|
511
|
+
this.utilityApi.analyzeFrame();
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Free allocated memory
|
|
515
|
+
*/
|
|
516
|
+
async freeResources() {
|
|
517
|
+
this.versionsFile = null;
|
|
518
|
+
this.modelsBuffers = null;
|
|
519
|
+
this.inputImageBuffer = null;
|
|
520
|
+
this.imageWidth_ = null;
|
|
521
|
+
this.imageHeight_ = null;
|
|
522
|
+
this.pipelines_ = null;
|
|
523
|
+
window.wasmArrayBuffer = null;
|
|
524
|
+
if (this.workflowApiUtilities && typeof this.workflowApiUtilities.delete === "function") {
|
|
525
|
+
this.workflowApiUtilities.delete();
|
|
526
|
+
this.workflowApiUtilities = null;
|
|
527
|
+
}
|
|
528
|
+
if (this.utilityApi && typeof this.utilityApi.delete === "function") {
|
|
529
|
+
this.utilityApi.delete();
|
|
530
|
+
this.utilityApi = null;
|
|
531
|
+
}
|
|
532
|
+
if (this.idCaptureWasmApi && typeof this.idCaptureWasmApi.delete === "function") {
|
|
533
|
+
this.idCaptureWasmApi.delete();
|
|
534
|
+
this.idCaptureWasmApi = null;
|
|
535
|
+
}
|
|
536
|
+
if (this.faceProcessingWasmApi && typeof this.faceProcessingWasmApi.delete === "function") {
|
|
537
|
+
this.faceProcessingWasmApi.delete();
|
|
538
|
+
this.faceProcessingWasmApi = null;
|
|
539
|
+
}
|
|
540
|
+
if (this.webApi && typeof this.webApi.delete === "function") this.webApi.delete();
|
|
541
|
+
this.webApi = null;
|
|
542
|
+
this.wasmModule = null;
|
|
543
|
+
this.isInitialized_ = false;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Clear pipeline state
|
|
547
|
+
*/
|
|
548
|
+
resetPipeline(type) {
|
|
549
|
+
this.checkWasmInitialization("Unable to reset pipeline, cpp API hasn't been initialized");
|
|
550
|
+
this.workflowApiUtilities.resetWorkflow(this.pipelineTypeToWasmEnum(type));
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Clear all pipelines states
|
|
554
|
+
*/
|
|
555
|
+
resetAllPipelines() {
|
|
556
|
+
this.checkWasmInitialization("Unable to reset pipelines, cpp API hasn't been initialized");
|
|
557
|
+
for (const [type, model] of this.pipelines_) this.resetPipeline(type);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Clear all states, not related to ml pipelines
|
|
561
|
+
*/
|
|
562
|
+
resetOther() {
|
|
563
|
+
this.checkWasmInitialization("Unable to reset other states, cpp API hasn't been initialized");
|
|
564
|
+
this.utilityApi.resetOther();
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Full reset
|
|
568
|
+
*/
|
|
569
|
+
reset() {
|
|
570
|
+
this.resetAllPipelines();
|
|
571
|
+
this.resetOther();
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Process one frame using one of the pipelines
|
|
575
|
+
* @param {ImageData} image - input image
|
|
576
|
+
* @param {WasmPipelineType} type - pipeline type
|
|
577
|
+
* @returns
|
|
578
|
+
*/
|
|
579
|
+
async process(image, type) {
|
|
580
|
+
this.checkWasmInitialization("Unable to process the image, cpp API hasn't been initialized");
|
|
581
|
+
if (this.inputImageBuffer === null) throw new Error("Unable to process the image, buffers haven't been allocated!");
|
|
582
|
+
this.inputImageBuffer.set(image.data);
|
|
583
|
+
switch (type) {
|
|
584
|
+
case WasmPipelineType.IdBlurGlarePipeline:
|
|
585
|
+
case WasmPipelineType.IdBarcodeAndTextQualityPipeline: return this.idCaptureWasmApi.runIdCaptureWorkflow(this.pipelineTypeToWasmEnum(type));
|
|
586
|
+
case WasmPipelineType.IdVideoSelfiePipeline: return this.idCaptureWasmApi.runIdVideoSelfieWorkflow();
|
|
587
|
+
case WasmPipelineType.SelfieWithAggregationMetrics:
|
|
588
|
+
case WasmPipelineType.SelfieWithQualityMetrics:
|
|
589
|
+
case WasmPipelineType.OnDeviceSelfieWorkflow: return this.faceProcessingWasmApi.runSelfieWorkflow(this.pipelineTypeToWasmEnum(type));
|
|
590
|
+
default: throw new Error("Unknown pipeline type");
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async runIdCapture(image) {
|
|
594
|
+
this.checkWasmInitialization("Unable to run Id Capture, cpp API hasn't been initialized");
|
|
595
|
+
if (this.inputImageBuffer === null) throw new Error("Unable to process the image, buffers haven't been allocated!");
|
|
596
|
+
this.inputImageBuffer.set(image.data);
|
|
597
|
+
return this.idCaptureWasmApi.runIdCapture();
|
|
598
|
+
}
|
|
599
|
+
async runSelfieCapture(image) {
|
|
600
|
+
this.checkWasmInitialization("Unable to run Selfie Capture, cpp API hasn't been initialized");
|
|
601
|
+
if (this.inputImageBuffer === null) throw new Error("Unable to process the image, buffers haven't been allocated!");
|
|
602
|
+
this.inputImageBuffer.set(image.data);
|
|
603
|
+
return this.faceProcessingWasmApi.runSelfieCapture();
|
|
604
|
+
}
|
|
605
|
+
async setFacePositionConstraints(type, minX, minY, maxX, maxY) {
|
|
606
|
+
this.checkWasmInitialization("Unable to set face position constraints, cpp API hasn't been initialized");
|
|
607
|
+
this.faceProcessingWasmApi.setFacePositionConstraints(this.pipelineTypeToWasmEnum(type), minX, minY, maxX, maxY);
|
|
608
|
+
}
|
|
609
|
+
async setFaceDetectionThresholds(type, brightnessThreshold, blurrinessThreshold, tiltRotationAngleThreshold, minMagicCropSize, autocaptureInterval, minFaceQualityScore, faceOcclusionThreshold, getReadyDelay, framesAggregationInterval, minFramesWithFace) {
|
|
610
|
+
this.checkWasmInitialization("Unable to set face detection thresholds, cpp API hasn't been initialized");
|
|
611
|
+
this.faceProcessingWasmApi.setFaceProcessingThresholds(this.pipelineTypeToWasmEnum(type), brightnessThreshold, blurrinessThreshold, tiltRotationAngleThreshold, minMagicCropSize, autocaptureInterval, minFaceQualityScore, faceOcclusionThreshold, getReadyDelay, framesAggregationInterval, minFramesWithFace);
|
|
612
|
+
}
|
|
613
|
+
async setFaceAttributesThresholds(type, headwearThreshold, lensesThreshold, closedEyesThreshold, maskThreshold) {
|
|
614
|
+
this.checkWasmInitialization("Unable to set face attributes thresholds, cpp API hasn't been initialized");
|
|
615
|
+
this.faceProcessingWasmApi.setFaceAttributesThresholds(this.pipelineTypeToWasmEnum(type), headwearThreshold, lensesThreshold, closedEyesThreshold, maskThreshold);
|
|
616
|
+
}
|
|
617
|
+
async setFaceChecksEnabled(type, isLensesCheckEnabled, isMaskCheckEnabled, isClosedEyesCheckEnabled, isHeadWearCheckEnabled, isOcclusionCheckEnabled) {
|
|
618
|
+
this.checkWasmInitialization("Unable to set face checks enabled flags, cpp API hasn't been initialized");
|
|
619
|
+
this.faceProcessingWasmApi.setFaceChecksEnabled(this.pipelineTypeToWasmEnum(type), isLensesCheckEnabled, isMaskCheckEnabled, isClosedEyesCheckEnabled, isHeadWearCheckEnabled, isOcclusionCheckEnabled);
|
|
620
|
+
}
|
|
621
|
+
async setFaceDetectionMode(type, isVideoSelfie) {
|
|
622
|
+
this.checkWasmInitialization("Unable to set face detection mode, cpp API hasn't been initialized");
|
|
623
|
+
this.faceProcessingWasmApi.setFaceProcessingMode(this.pipelineTypeToWasmEnum(type), isVideoSelfie);
|
|
624
|
+
}
|
|
625
|
+
async setFaceDetectionCallbacks(type, onFarAway, onTooClose, onTooManyFaces, onNoFace, onCapture, onGetReady, onGetReadyFinished, onCenterFace, onDark, onBlur, onFaceAngle, onLenses, onMask, onEyesClosed, onHeadWear, onSwitchToManualCapture, onFaceOccluded) {
|
|
626
|
+
this.checkWasmInitialization("Unable to set face detection callbacks, cpp API hasn't been initialized");
|
|
627
|
+
this.faceProcessingWasmApi.setFaceProcessingCallbacks(this.pipelineTypeToWasmEnum(type), onFarAway, onTooClose, onTooManyFaces, onNoFace, onCapture, onGetReady, onGetReadyFinished, onCenterFace, onDark, onBlur, onFaceAngle, onLenses, onMask, onEyesClosed, onHeadWear, onSwitchToManualCapture, onFaceOccluded);
|
|
628
|
+
}
|
|
629
|
+
async setIdCaptureThresholds(type, blurThreshold, blurChangeThreshold, glareThreshold, clsThreshold, sideThreshold, iouThreshold, idDetectedTimeout, autocaptureTimeout, framesAggregationInterval) {
|
|
630
|
+
this.checkWasmInitialization("Unable to set thresholds, cpp API hasn't been initialized");
|
|
631
|
+
this.idCaptureWasmApi.setIdCaptureThresholds(this.pipelineTypeToWasmEnum(type), blurThreshold, blurChangeThreshold, glareThreshold, clsThreshold, sideThreshold, iouThreshold, idDetectedTimeout, autocaptureTimeout, framesAggregationInterval);
|
|
632
|
+
}
|
|
633
|
+
async setIdCaptureCallbacks(type, onFarAway, onDetectionStarted, onMaskChange, onBlur, onGlare, onCapturing, onCapture, onBestFrame, onIDNotDetected, onSwitchToManualCapture, onIDTypeChange, onIDSideChange, onCapturingCounterValueChange) {
|
|
634
|
+
this.checkWasmInitialization("Unable to set callbacks, cpp API hasn't been initialized");
|
|
635
|
+
this.idCaptureWasmApi.setIdCaptureCallbacks(this.pipelineTypeToWasmEnum(type), onFarAway, onDetectionStarted, onMaskChange, onBlur, onGlare, onCapturing, onCapture, onBestFrame, onIDNotDetected, onSwitchToManualCapture, onIDTypeChange, onIDSideChange, onCapturingCounterValueChange);
|
|
636
|
+
}
|
|
637
|
+
async setIdCaptureGeometryParams(type, areaDown, areaUp, areaIOSPassportUp, areaIOSPassportDown, widthIOSUp, widthIOSDown, widthDown, widthUp, windowOuterWidth, windowOuterHeight, windowInnerWidth, windowInnerHeight) {
|
|
638
|
+
this.checkWasmInitialization("Unable to set geometry params, cpp API hasn't been initialized");
|
|
639
|
+
this.idCaptureWasmApi.setIdCaptureGeometryParams(this.pipelineTypeToWasmEnum(type), areaDown, areaUp, areaIOSPassportUp, areaIOSPassportDown, widthIOSUp, widthIOSDown, widthDown, widthUp, windowOuterWidth, windowOuterHeight, windowInnerWidth, windowInnerHeight);
|
|
640
|
+
}
|
|
641
|
+
async setIdCaptureConfigParams(type, isFixedMask, isIPhone14OrHigher, idType, isBlurCheckEnabled, isGlareCheckEnabled, _isIdFaceQualityCheckEnabled, isIouCheckEnabled, isFPSLimitEnabled) {
|
|
642
|
+
this.checkWasmInitialization("Unable to set config params, cpp API hasn't been initialized");
|
|
643
|
+
this.idCaptureWasmApi.setIdCaptureConfigParams(this.pipelineTypeToWasmEnum(type), isFixedMask, isIPhone14OrHigher, idType, isBlurCheckEnabled, isGlareCheckEnabled, isIouCheckEnabled, isFPSLimitEnabled);
|
|
644
|
+
}
|
|
645
|
+
setIdCaptureModelType(pipelineType, modelType) {
|
|
646
|
+
this.checkWasmInitialization("Unable to set model type, cpp API hasn't been initialized");
|
|
647
|
+
this.idCaptureWasmApi.setIdCaptureModelType(this.pipelineTypeToWasmEnum(pipelineType), this.IdCaptureModelTypeToWasmEnum(modelType));
|
|
648
|
+
}
|
|
649
|
+
IdPerspectiveTransform(image, frameRect) {
|
|
650
|
+
this.checkWasmInitialization("Unable to perform perspective transform, cpp API hasn't been initialized");
|
|
651
|
+
return this.idCaptureWasmApi.IdPerspectiveTransform(image, frameRect);
|
|
652
|
+
}
|
|
653
|
+
async getVersions() {
|
|
654
|
+
const text = await (await fetch(this.versionsFile)).text();
|
|
655
|
+
return JSON.parse(text);
|
|
656
|
+
}
|
|
657
|
+
async loadModels() {
|
|
658
|
+
this.checkWasmInitialization("Unable load the models, cpp API hasn't been initialized");
|
|
659
|
+
const buffSizes = new this.wasmModule.WorkflowTypeToIntMap();
|
|
660
|
+
const modelData = /* @__PURE__ */ new Map();
|
|
661
|
+
const loadedModels = /* @__PURE__ */ new Map();
|
|
662
|
+
for (const [type, modelsPaths] of this.pipelines_) {
|
|
663
|
+
const wasmType = this.pipelineTypeToWasmEnum(type);
|
|
664
|
+
const pipelineBuffers = [];
|
|
665
|
+
const sizes = new this.wasmModule.VectorInt();
|
|
666
|
+
for (const path of modelsPaths) {
|
|
667
|
+
if (!loadedModels.has(path)) {
|
|
668
|
+
const model$1 = await fetch(path).then((response) => response.arrayBuffer()).then((buffer) => new Uint8Array(buffer));
|
|
669
|
+
loadedModels.set(path, model$1);
|
|
670
|
+
}
|
|
671
|
+
const model = loadedModels.get(path);
|
|
672
|
+
sizes.push_back(model.byteLength);
|
|
673
|
+
pipelineBuffers.push(model);
|
|
674
|
+
}
|
|
675
|
+
buffSizes.set(wasmType, sizes);
|
|
676
|
+
modelData.set(wasmType, pipelineBuffers);
|
|
677
|
+
}
|
|
678
|
+
this.modelsBuffers = this.workflowApiUtilities.allocateModelsBuffers(buffSizes);
|
|
679
|
+
for (const [type, data] of modelData) {
|
|
680
|
+
const buffers = this.modelsBuffers?.get(type);
|
|
681
|
+
for (let k = 0; k < buffers.size(); k++) {
|
|
682
|
+
const buffer = buffers?.get(k);
|
|
683
|
+
if (buffer) buffer.set(data[k]);
|
|
684
|
+
else throw new Error("Unable to get model buffer from shared memory!");
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
async initializePipelines() {
|
|
689
|
+
this.checkWasmInitialization("Unable to initialize pipelines, cpp API hasn't been initialized");
|
|
690
|
+
if (!this.modelsBuffers) await this.loadModels();
|
|
691
|
+
this.workflowApiUtilities.initializeWorkflows();
|
|
692
|
+
}
|
|
693
|
+
async setProductionMode(productionMode) {
|
|
694
|
+
this.checkWasmInitialization("Unable to set production mode, cpp API hasn't been initialized");
|
|
695
|
+
this.utilityApi.setProductionMode(productionMode);
|
|
696
|
+
}
|
|
697
|
+
getPipelineState() {
|
|
698
|
+
this.checkWasmInitialization("Unable to get pipeline state, cpp API hasn't been initialized");
|
|
699
|
+
return this.workflowApiUtilities.getWorkflowState();
|
|
700
|
+
}
|
|
701
|
+
getCurrentPipeline() {
|
|
702
|
+
this.checkWasmInitialization("Unable to get current pipeline, cpp API hasn't been initialized");
|
|
703
|
+
return this.pipelineTypeFromWasmEnum(this.workflowApiUtilities.getCurrentWorkflow());
|
|
704
|
+
}
|
|
705
|
+
setSdkVersion(sdkVersion) {
|
|
706
|
+
this.checkWasmInitialization("Unable to set sdk version, cpp API hasn't been initialized");
|
|
707
|
+
this.utilityApi.setSdkVersion(sdkVersion);
|
|
708
|
+
}
|
|
709
|
+
setSdkPlatform(sdkPlatform) {
|
|
710
|
+
this.checkWasmInitialization("Unable to set device type, cpp API hasn't been initialized");
|
|
711
|
+
this.utilityApi.setSdkPlatform(sdkPlatform);
|
|
712
|
+
}
|
|
713
|
+
setDeviceInfo(deviceInfo, overrideExisting = true) {
|
|
714
|
+
this.checkWasmInitialization("Unable to set device info, cpp API hasn't been initialized");
|
|
715
|
+
this.utilityApi.setDeviceInfo(deviceInfo, overrideExisting);
|
|
716
|
+
}
|
|
717
|
+
setBrowserInfo(browserInfo, overrideExisting = true) {
|
|
718
|
+
this.checkWasmInitialization("Unable to set browser info, cpp API hasn't been initialized");
|
|
719
|
+
this.utilityApi.setBrowserInfo(browserInfo, overrideExisting);
|
|
720
|
+
}
|
|
721
|
+
setCameraInfo(cameraInfo, overrideExisting = true) {
|
|
722
|
+
this.checkWasmInitialization("Unable to set camera info, cpp API hasn't been initialized");
|
|
723
|
+
this.utilityApi.setCameraInfo(cameraInfo, overrideExisting);
|
|
724
|
+
}
|
|
725
|
+
setZc(zc) {
|
|
726
|
+
this.checkWasmInitialization("Unable to set zc, cpp API hasn't been initialized");
|
|
727
|
+
this.utilityApi.setZc(zc);
|
|
728
|
+
}
|
|
729
|
+
setMotionStatus(motionStatus) {
|
|
730
|
+
this.checkWasmInitialization("Unable to set motion status, cpp API hasn't been initialized");
|
|
731
|
+
this.utilityApi.setMotionStatus(motionStatus);
|
|
732
|
+
}
|
|
733
|
+
setMetadataField(key, value) {
|
|
734
|
+
this.checkWasmInitialization("Unable to set metadata field, cpp API hasn't been initialized");
|
|
735
|
+
this.utilityApi.setMetadataField(key, value);
|
|
736
|
+
}
|
|
737
|
+
setInspectorOpened(inspectorOpened) {
|
|
738
|
+
this.inspectorOpened_ = inspectorOpened;
|
|
739
|
+
}
|
|
740
|
+
setBackgroundMode(backgroundMode) {
|
|
741
|
+
this.checkWasmInitialization("Unable to set background mode, cpp API hasn't been initialized");
|
|
742
|
+
this.utilityApi.setBackgroundMode(backgroundMode);
|
|
743
|
+
}
|
|
744
|
+
getCheck() {
|
|
745
|
+
this.checkWasmInitialization("Unable to get check, cpp API hasn't been initialized");
|
|
746
|
+
return this.utilityApi.getCheck();
|
|
747
|
+
}
|
|
748
|
+
getMetadata() {
|
|
749
|
+
this.checkWasmInitialization("Unable to get metadata, cpp API hasn't been initialized");
|
|
750
|
+
this.utilityApi.setInspectorOpened(this.inspectorOpened_);
|
|
751
|
+
return this.utilityApi.getMetadata();
|
|
752
|
+
}
|
|
753
|
+
async prc() {
|
|
754
|
+
this.checkWasmInitialization("Unable to prc, cpp API hasn't been initialized");
|
|
755
|
+
await wasmCallWrapper(this.wasmCallSemaphore, () => this.utilityApi.prc(), true);
|
|
756
|
+
}
|
|
757
|
+
async poc(output) {
|
|
758
|
+
this.checkWasmInitialization("Unable to poc, cpp API hasn't been initialized");
|
|
759
|
+
await wasmCallWrapper(this.wasmCallSemaphore, () => this.utilityApi.poc(output), true);
|
|
760
|
+
}
|
|
761
|
+
ckvcks(data) {
|
|
762
|
+
this.checkWasmInitialization("Unable to ckvcks, cpp API hasn't been initialized");
|
|
763
|
+
this.utilityApi.ckvcks(data);
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Synchronous manual-capture entry point for the OnDevice selfie workflow.
|
|
767
|
+
* Runs face detection + the full on-device quality / liveness / age pipeline
|
|
768
|
+
* on a single canvas image and stages the results inside the WASM `WebApi`
|
|
769
|
+
* for the next {@link postFaceResults} call. No polling, no semaphore wrap.
|
|
770
|
+
*
|
|
771
|
+
* Mirrors `ml-wasm-kit/WasmApi.processPhoto`.
|
|
772
|
+
*/
|
|
773
|
+
processPhoto(canvas) {
|
|
774
|
+
this.checkWasmInitialization("Unable to process photo, cpp API hasn't been initialized");
|
|
775
|
+
this.faceProcessingWasmApi.processPhoto(canvas);
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* POSTs the staged on-device face-results JSON to `/omni/add/face-results`
|
|
779
|
+
* via the C++ `WebApi`. The C++ side encrypts the body with
|
|
780
|
+
* `SessionEncryptor` and routes through the WASM `WebClient` with inline
|
|
781
|
+
* encryption (`ie=true`) — this does NOT depend on the WebClient E2EE
|
|
782
|
+
* session setup, so it works whether `setupWasmConnection` was called with
|
|
783
|
+
* `encryptionEnabled` true or false.
|
|
784
|
+
*
|
|
785
|
+
* Reads from the `WebApi` instance constructed in {@link initialize}. The
|
|
786
|
+
* C++ `OnDeviceSelfieWorkflow` has already staged the aggregated best-shot
|
|
787
|
+
* results into that instance via `WebApi::setFaceResults(...)` during
|
|
788
|
+
* per-frame processing (auto-capture) or via {@link processPhoto}
|
|
789
|
+
* (manual-capture).
|
|
790
|
+
*/
|
|
791
|
+
postFaceResults(config) {
|
|
792
|
+
this.checkWasmInitialization("Unable to post face results, cpp API hasn't been initialized");
|
|
793
|
+
return this.webApi.postFaceResults(config ?? {});
|
|
794
|
+
}
|
|
795
|
+
pipelineTypeToWasmEnum(type) {
|
|
796
|
+
switch (type) {
|
|
797
|
+
case WasmPipelineType.IdBlurGlarePipeline: return this.wasmModule.WorkflowType.IdBlurGlareWorkflow;
|
|
798
|
+
case WasmPipelineType.IdBarcodeAndTextQualityPipeline: return this.wasmModule.WorkflowType.IdBarcodeAndTextQualityWorkflow;
|
|
799
|
+
case WasmPipelineType.IdVideoSelfiePipeline: return this.wasmModule.WorkflowType.IdVideoSelfieWorkflow;
|
|
800
|
+
case WasmPipelineType.SelfieWithAggregationMetrics: return this.wasmModule.WorkflowType.SelfieWithAggregationMetrics;
|
|
801
|
+
case WasmPipelineType.SelfieWithQualityMetrics: return this.wasmModule.WorkflowType.SelfieWithQualityMetrics;
|
|
802
|
+
case WasmPipelineType.OnDeviceSelfieWorkflow: return this.wasmModule.WorkflowType.OnDeviceSelfieWorkflow;
|
|
803
|
+
default: throw new Error("Unknown pipeline type");
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
pipelineTypeFromWasmEnum(type) {
|
|
807
|
+
switch (type) {
|
|
808
|
+
case this.wasmModule.WorkflowType.IdBlurGlareWorkflow: return WasmPipelineType.IdBlurGlarePipeline;
|
|
809
|
+
case this.wasmModule.WorkflowType.IdBarcodeAndTextQualityWorkflow: return WasmPipelineType.IdBarcodeAndTextQualityPipeline;
|
|
810
|
+
case this.wasmModule.WorkflowType.IdVideoSelfieWorkflow: return WasmPipelineType.IdVideoSelfiePipeline;
|
|
811
|
+
case this.wasmModule.WorkflowType.SelfieWithAggregationMetrics: return WasmPipelineType.SelfieWithAggregationMetrics;
|
|
812
|
+
case this.wasmModule.WorkflowType.SelfieWithQualityMetrics: return WasmPipelineType.SelfieWithQualityMetrics;
|
|
813
|
+
case this.wasmModule.WorkflowType.OnDeviceSelfieWorkflow: return WasmPipelineType.OnDeviceSelfieWorkflow;
|
|
814
|
+
default: throw new Error("Unknown pipeline type");
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
IdCaptureModelTypeToWasmEnum(type) {
|
|
818
|
+
switch (type) {
|
|
819
|
+
case IdCaptureModelType.IdCaptureV1x: return this.wasmModule.IdCaptureModelType.IdCaptureV1x;
|
|
820
|
+
case IdCaptureModelType.IdCaptureV2x: return this.wasmModule.IdCaptureModelType.IdCaptureV2x;
|
|
821
|
+
case IdCaptureModelType.IdCaptureV3x: return this.wasmModule.IdCaptureModelType.IdCaptureV3x;
|
|
822
|
+
default: throw new Error("Unknown Id Capture model type");
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
IdCaptureModelTypeFromWasmEnum(type) {
|
|
826
|
+
switch (type) {
|
|
827
|
+
case this.wasmModule.IdCaptureModelType.IdCaptureV1x: return IdCaptureModelType.IdCaptureV1x;
|
|
828
|
+
case this.wasmModule.IdCaptureModelType.IdCaptureV2x: return IdCaptureModelType.IdCaptureV2x;
|
|
829
|
+
case this.wasmModule.IdCaptureModelType.IdCaptureV3x: return IdCaptureModelType.IdCaptureV3x;
|
|
830
|
+
default: throw new Error("Unknown Id Capture model type");
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
checkWasmInitialization(message) {
|
|
834
|
+
if (!this.isInitialized()) throw new Error(message);
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
var mlWasmJSApi_default = MlWasmJSApi.getInstance();
|
|
838
|
+
|
|
839
|
+
//#endregion
|
|
840
|
+
//#region ../infra/src/wasm/warmup.ts
|
|
841
|
+
/**
|
|
842
|
+
* Pipelines preloaded when no explicit `pipelines` array is passed to
|
|
843
|
+
* {@link warmupWasm}. The on-device selfie bundle is opt-in (loaded only when
|
|
844
|
+
* a SELFIE/AUTH module has `onDeviceFaceResultsSubmissionEnabled: true`), so
|
|
845
|
+
* it is intentionally excluded here to keep the default warmup small.
|
|
846
|
+
*/
|
|
847
|
+
const WASM_PIPELINES = ["selfie", "idCapture"];
|
|
848
|
+
/**
|
|
849
|
+
* Default model files for each pipeline.
|
|
850
|
+
* These are the ONNX model files required by each ML pipeline.
|
|
851
|
+
*/
|
|
852
|
+
const DEFAULT_PIPELINE_MODELS = {
|
|
853
|
+
selfie: [
|
|
854
|
+
"selfie_bf_angles_192x192_opset9_fp16.ortmodelv2",
|
|
855
|
+
"face_attributes_v1_3_fp16.ortmodelv2",
|
|
856
|
+
"mls_regressor_4773007c657b4f05a460321456740d0f_fp16.ortmodelv2",
|
|
857
|
+
"face_occlusion_v0_2_fp16.ortmodelv2"
|
|
858
|
+
],
|
|
859
|
+
onDeviceSelfie: [
|
|
860
|
+
"selfie_bf_angles_192x192_opset9_fp16.ortmodelv2",
|
|
861
|
+
"face_attributes_v1_3_fp16.ortmodelv2",
|
|
862
|
+
"mls_regressor_4773007c657b4f05a460321456740d0f_fp16.ortmodelv2",
|
|
863
|
+
"face_occlusion_v0_2_fp16.ortmodelv2",
|
|
864
|
+
"ondevice_physical_liveness_v2_1_fp16.ortmodelv2",
|
|
865
|
+
"age_estimation_v2_2_2_fp16.ortmodelv2"
|
|
866
|
+
],
|
|
867
|
+
idCapture: ["id_capture_2_01_fp16.ortmodelv2", "id_fiqa_19a81a0b9bf6492eb03b4667f6db4c85_fp16.ortmodelv2"]
|
|
868
|
+
};
|
|
869
|
+
let state = "idle";
|
|
870
|
+
let loadingPromise = null;
|
|
871
|
+
let wasmError = null;
|
|
872
|
+
let loadedPipelines = [];
|
|
873
|
+
let lastConfig = null;
|
|
874
|
+
function mapPipelineToWasmType(pipeline) {
|
|
875
|
+
switch (pipeline) {
|
|
876
|
+
case "selfie": return WasmPipelineType.SelfieWithQualityMetrics;
|
|
877
|
+
case "onDeviceSelfie": return WasmPipelineType.OnDeviceSelfieWorkflow;
|
|
878
|
+
case "idCapture": return WasmPipelineType.IdBlurGlarePipeline;
|
|
879
|
+
default: throw new Error(`Unknown pipeline: ${pipeline}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Derives the models base path from the WASM path.
|
|
884
|
+
* Assumes models are in a 'models' subdirectory relative to the WASM binary location.
|
|
885
|
+
* e.g., '/wasm/v2/binary.wasm' -> '/wasm/v2/models'
|
|
886
|
+
*/
|
|
887
|
+
function deriveModelsBasePath(wasmPath) {
|
|
888
|
+
const lastSlashIndex = wasmPath.lastIndexOf("/");
|
|
889
|
+
if (lastSlashIndex === -1) return "models";
|
|
890
|
+
return `${wasmPath.substring(0, lastSlashIndex)}/models`;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Builds the pipelines map with model paths for WASM initialization.
|
|
894
|
+
* @param pipelines - Array of pipeline types to initialize
|
|
895
|
+
* @param modelsBasePath - Base path where model files are located
|
|
896
|
+
* @param customModels - Optional custom model files per pipeline
|
|
897
|
+
*/
|
|
898
|
+
function buildPipelinesMap(pipelines, modelsBasePath, customModels) {
|
|
899
|
+
const pipelinesMap = /* @__PURE__ */ new Map();
|
|
900
|
+
for (const pipeline of pipelines) {
|
|
901
|
+
const wasmType = mapPipelineToWasmType(pipeline);
|
|
902
|
+
if (!pipelinesMap.has(wasmType)) {
|
|
903
|
+
const modelPaths = (customModels?.[pipeline] ?? DEFAULT_PIPELINE_MODELS[pipeline]).map((modelFile) => `${modelsBasePath}/${modelFile}`);
|
|
904
|
+
pipelinesMap.set(wasmType, modelPaths);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return pipelinesMap;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Preloads WASM binary and ML models at app startup.
|
|
911
|
+
* Runs asynchronously - app can continue while WASM loads in background.
|
|
912
|
+
*
|
|
913
|
+
* Model files are automatically loaded based on the pipeline type.
|
|
914
|
+
* By default, models are expected in a 'models' subdirectory relative to the WASM binary.
|
|
915
|
+
*
|
|
916
|
+
* @param config - Configuration for WASM warmup
|
|
917
|
+
* @returns Promise that resolves when WASM is ready
|
|
918
|
+
*
|
|
919
|
+
* @example
|
|
920
|
+
* ```ts
|
|
921
|
+
* // Basic usage - models loaded from /wasm/v2/models/
|
|
922
|
+
* warmupWasm({
|
|
923
|
+
* wasmPath: '/wasm/v2/ml-wasm.wasm',
|
|
924
|
+
* glueCodePath: '/wasm/v2/ml-wasm.js',
|
|
925
|
+
* pipelines: ['selfie', 'idCapture'],
|
|
926
|
+
* });
|
|
927
|
+
*
|
|
928
|
+
* // With explicit models path
|
|
929
|
+
* warmupWasm({
|
|
930
|
+
* wasmPath: '/wasm/v2/ml-wasm.wasm',
|
|
931
|
+
* glueCodePath: '/wasm/v2/ml-wasm.js',
|
|
932
|
+
* modelsBasePath: 'https://cdn.example.com/wasm/v2/models',
|
|
933
|
+
* pipelines: ['selfie'],
|
|
934
|
+
* });
|
|
935
|
+
* ```
|
|
936
|
+
*/
|
|
937
|
+
async function warmupWasm(config) {
|
|
938
|
+
const pipelines = config.pipelines ?? [...WASM_PIPELINES];
|
|
939
|
+
const modelsBasePath = config.modelsBasePath ?? deriveModelsBasePath(config.wasmPath);
|
|
940
|
+
if (state === "loading" && loadingPromise) return loadingPromise;
|
|
941
|
+
if (state === "ready") {
|
|
942
|
+
if (pipelines.filter((p) => !loadedPipelines.includes(p)).length === 0) return;
|
|
943
|
+
if (!lastConfig) throw new Error("Cannot add pipelines: original warmup config not available");
|
|
944
|
+
if (lastConfig.wasmPath !== config.wasmPath || lastConfig.glueCodePath !== config.glueCodePath || (lastConfig.wasmSimdPath ?? lastConfig.wasmPath) !== (config.wasmSimdPath ?? config.wasmPath) || (lastConfig.glueCodeSimdPath ?? "") !== (config.glueCodeSimdPath ?? "") || (lastConfig.useSimd ?? true) !== (config.useSimd ?? true)) throw new Error("Cannot add pipelines: WASM config mismatch. Use same wasmPath, glueCodePath, and useSimd settings.");
|
|
945
|
+
const combinedPipelines = [...new Set([...loadedPipelines, ...pipelines])];
|
|
946
|
+
state = "loading";
|
|
947
|
+
wasmError = null;
|
|
948
|
+
loadingPromise = (async () => {
|
|
949
|
+
try {
|
|
950
|
+
const pipelinesMap = buildPipelinesMap(combinedPipelines, lastConfig.modelsBasePath ?? deriveModelsBasePath(lastConfig.wasmPath), lastConfig.pipelineModels);
|
|
951
|
+
await mlWasmJSApi_default.initialize(lastConfig.wasmPath, lastConfig.wasmSimdPath ?? lastConfig.wasmPath, lastConfig.glueCodePath, lastConfig.glueCodeSimdPath ?? "", lastConfig.useSimd ?? true, "", pipelinesMap);
|
|
952
|
+
await mlWasmJSApi_default.loadModels();
|
|
953
|
+
await mlWasmJSApi_default.initializePipelines();
|
|
954
|
+
state = "ready";
|
|
955
|
+
loadedPipelines = combinedPipelines;
|
|
956
|
+
lastConfig = {
|
|
957
|
+
...lastConfig,
|
|
958
|
+
pipelines: combinedPipelines
|
|
959
|
+
};
|
|
960
|
+
loadingPromise = null;
|
|
961
|
+
} catch (error) {
|
|
962
|
+
state = "error";
|
|
963
|
+
wasmError = error instanceof Error ? error : new Error(String(error));
|
|
964
|
+
loadingPromise = null;
|
|
965
|
+
throw wasmError;
|
|
966
|
+
}
|
|
967
|
+
})();
|
|
968
|
+
return loadingPromise;
|
|
969
|
+
}
|
|
970
|
+
state = "loading";
|
|
971
|
+
wasmError = null;
|
|
972
|
+
loadingPromise = (async () => {
|
|
973
|
+
try {
|
|
974
|
+
const pipelinesMap = buildPipelinesMap(pipelines, modelsBasePath, config.pipelineModels);
|
|
975
|
+
await mlWasmJSApi_default.initialize(config.wasmPath, config.wasmSimdPath ?? config.wasmPath, config.glueCodePath, config.glueCodeSimdPath ?? "", config.useSimd ?? true, "", pipelinesMap);
|
|
976
|
+
await mlWasmJSApi_default.loadModels();
|
|
977
|
+
await mlWasmJSApi_default.initializePipelines();
|
|
978
|
+
state = "ready";
|
|
979
|
+
loadedPipelines = [...pipelines];
|
|
980
|
+
lastConfig = {
|
|
981
|
+
...config,
|
|
982
|
+
pipelines
|
|
983
|
+
};
|
|
984
|
+
loadingPromise = null;
|
|
985
|
+
} catch (error) {
|
|
986
|
+
state = "error";
|
|
987
|
+
wasmError = error instanceof Error ? error : new Error(String(error));
|
|
988
|
+
loadingPromise = null;
|
|
989
|
+
throw wasmError;
|
|
990
|
+
}
|
|
991
|
+
})();
|
|
992
|
+
return loadingPromise;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Returns the current warmup status.
|
|
996
|
+
* Useful for showing loading indicators.
|
|
997
|
+
*
|
|
998
|
+
* @returns Current warmup status
|
|
999
|
+
*/
|
|
1000
|
+
function getWasmStatus() {
|
|
1001
|
+
return {
|
|
1002
|
+
isReady: state === "ready",
|
|
1003
|
+
isLoading: state === "loading",
|
|
1004
|
+
error: wasmError ?? void 0,
|
|
1005
|
+
loadedPipelines: [...loadedPipelines]
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Returns true if WASM is fully initialized and ready.
|
|
1010
|
+
*
|
|
1011
|
+
* @returns True if WASM is ready
|
|
1012
|
+
*/
|
|
1013
|
+
function isWasmReady() {
|
|
1014
|
+
return state === "ready";
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Returns a promise that resolves when WASM is ready.
|
|
1018
|
+
* - If ready: resolves immediately
|
|
1019
|
+
* - If loading: waits for completion, then resolves
|
|
1020
|
+
* - If not started: rejects with error
|
|
1021
|
+
*
|
|
1022
|
+
* @returns Promise that resolves when WASM is ready
|
|
1023
|
+
* @throws Error if WASM is in error state or was never started
|
|
1024
|
+
*/
|
|
1025
|
+
function waitForWasm() {
|
|
1026
|
+
if (state === "ready") return Promise.resolve();
|
|
1027
|
+
if (state === "loading" && loadingPromise) return loadingPromise;
|
|
1028
|
+
if (state === "error") return Promise.reject(wasmError ?? /* @__PURE__ */ new Error("WASM initialization failed"));
|
|
1029
|
+
return Promise.reject(/* @__PURE__ */ new Error("WASM warmup was not started. Call warmupWasm() first."));
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
//#endregion
|
|
1033
|
+
//#region ../infra/src/providers/wasm/BaseWasmProvider.ts
|
|
1034
|
+
/**
|
|
1035
|
+
* Base provider class that abstracts common WASM functionality.
|
|
1036
|
+
* This serves as a foundation for specific ML capability providers
|
|
1037
|
+
* like FaceDetectionProvider and IdCaptureProvider.
|
|
1038
|
+
*/
|
|
1039
|
+
var BaseWasmProvider = class {
|
|
1040
|
+
/**
|
|
1041
|
+
* Creates a new BaseWasmProvider
|
|
1042
|
+
* @param pipelineType - The WASM pipeline type this provider uses
|
|
1043
|
+
*/
|
|
1044
|
+
constructor(pipelineType) {
|
|
1045
|
+
this._isInitialized = false;
|
|
1046
|
+
this.pipelineType = pipelineType;
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Returns whether this provider has been initialized.
|
|
1050
|
+
*/
|
|
1051
|
+
get initialized() {
|
|
1052
|
+
return this._isInitialized;
|
|
1053
|
+
}
|
|
1054
|
+
getPipelineType() {
|
|
1055
|
+
if (this.pipelineType === void 0) throw new Error(`${this.constructor.name} has no pipeline type configured.`);
|
|
1056
|
+
return this.pipelineType;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Initializes the provider by ensuring WASM is loaded
|
|
1060
|
+
* @param config - Provider configuration
|
|
1061
|
+
* @param pipeline - The pipeline type to warm up ('selfie', 'idCapture', etc.)
|
|
1062
|
+
*/
|
|
1063
|
+
async initializeBase(config, pipeline) {
|
|
1064
|
+
if (this._isInitialized) return;
|
|
1065
|
+
const status = getWasmStatus();
|
|
1066
|
+
if (status.isLoading || isWasmReady() || status.error) await waitForWasm();
|
|
1067
|
+
else {
|
|
1068
|
+
if (!config.wasmPath || !config.glueCodePath) throw new Error("WASM path and glue code path are required. Call warmupWasm() first.");
|
|
1069
|
+
await warmupWasm({
|
|
1070
|
+
wasmPath: config.wasmPath,
|
|
1071
|
+
wasmSimdPath: config.wasmSimdPath,
|
|
1072
|
+
glueCodePath: config.glueCodePath,
|
|
1073
|
+
glueCodeSimdPath: config.glueCodeSimdPath,
|
|
1074
|
+
useSimd: config.useSimd,
|
|
1075
|
+
modelsBasePath: config.modelsBasePath,
|
|
1076
|
+
pipelines: [pipeline]
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
this._isInitialized = true;
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Ensures the provider is initialized before performing operations.
|
|
1083
|
+
* @throws Error if not initialized
|
|
1084
|
+
*/
|
|
1085
|
+
ensureInitialized() {
|
|
1086
|
+
if (!this._isInitialized) throw new Error(`${this.constructor.name} not initialized. Call initialize() first.`);
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Processes a frame through the WASM pipeline
|
|
1090
|
+
* @param image - Image data to process
|
|
1091
|
+
* @returns The pipeline result (type depends on pipeline - WASM returns any)
|
|
1092
|
+
*/
|
|
1093
|
+
async processFrameWasm(image) {
|
|
1094
|
+
this.ensureInitialized();
|
|
1095
|
+
const pipelineType = this.getPipelineType();
|
|
1096
|
+
await mlWasmJSApi_default.allocateImageBuffers(image.width, image.height);
|
|
1097
|
+
await mlWasmJSApi_default.handleDetectionCallAndUpdateState(pipelineType);
|
|
1098
|
+
return await mlWasmJSApi_default.process(image, pipelineType);
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Resets the pipeline to its initial state.
|
|
1102
|
+
* Safe to call even if not initialized (no-op in that case).
|
|
1103
|
+
*/
|
|
1104
|
+
reset() {
|
|
1105
|
+
if (this._isInitialized && this.pipelineType !== void 0) mlWasmJSApi_default.resetPipeline(this.pipelineType);
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Disposes of resources and resets initialization state.
|
|
1109
|
+
* Safe to call even if not initialized.
|
|
1110
|
+
*/
|
|
1111
|
+
async dispose() {
|
|
1112
|
+
this.reset();
|
|
1113
|
+
this._isInitialized = false;
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
//#endregion
|
|
1118
|
+
export { executeWasmRequest as a, setupWasmConnection as c, WasmWebClientError as i, WasmPipelineType as l, warmupWasm as n, initializeWasmWebClient as o, mlWasmJSApi_default as r, setWebApiHttpTransport as s, BaseWasmProvider as t, IdCaptureModelType as u };
|