@jaak.ai/stamps 2.1.0-dev.7 → 2.1.0-dev.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +364 -11
- package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
- package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/my-component/my-component.js +254 -6
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/DetectionService.js +146 -7
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/collection/services/interfaces/IDetectionService.js.map +1 -1
- package/dist/components/jaak-stamps.js +367 -12
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/jaak-stamps-webcomponent.js +1 -1
- package/dist/esm/jaak-stamps.entry.js +364 -11
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-8e25497e.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-8e25497e.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +27 -0
- package/dist/types/components.d.ts +3 -1
- package/dist/types/services/DetectionService.d.ts +9 -1
- package/dist/types/services/interfaces/IDetectionService.d.ts +6 -0
- package/package.json +1 -1
- package/dist/jaak-stamps-webcomponent/p-be6b6ca3.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-be6b6ca3.entry.js.map +0 -1
|
@@ -18,7 +18,7 @@ var patchBrowser = () => {
|
|
|
18
18
|
|
|
19
19
|
patchBrowser().then(async (options) => {
|
|
20
20
|
await index.globalScripts();
|
|
21
|
-
return index.bootstrapLazy([["jaak-stamps.cjs",[[1,"jaak-stamps",{"debug":[4],"alignmentTolerance":[2,"alignment-tolerance"],"maskSize":[2,"mask-size"],"cropMargin":[2,"crop-margin"],"useDocumentClassification":[4,"use-document-classification"],"useDocumentDetector":[4,"use-document-detector"],"preferredCamera":[1,"preferred-camera"],"captureDelay":[2,"capture-delay"],"enableBackDocumentTimer":[4,"enable-back-document-timer"],"backDocumentTimerDuration":[2,"back-document-timer-duration"],"detectionBoxes":[32],"sideAlignment":[32],"isMaskReady":[32],"shouldMirrorVideo":[32],"showCameraSelector":[32],"isSwitchingCamera":[32],"hasDocumentDetected":[32],"cameraInfoWithAutofocus":[32],"currentStatus":[32],"performanceData":[32],"backDocumentTimerRemaining":[32],"showManualCaptureButton":[32],"performanceDegradedMode":[32],"showPerformanceMessage":[32],"captureStateVersion":[32],"processingButton":[32],"getCapturedImages":[64],"isProcessCompleted":[64],"startCapture":[64],"stopCapture":[64],"resetCapture":[64],"skipBackCapture":[64],"getStatus":[64],"preloadModel":[64],"getCameraInfo":[64],"setPreferredCamera":[64],"setCaptureDelay":[64],"getCaptureDelay":[64]}]]]], options);
|
|
21
|
+
return index.bootstrapLazy([["jaak-stamps.cjs",[[1,"jaak-stamps",{"debug":[4],"alignmentTolerance":[2,"alignment-tolerance"],"maskSize":[2,"mask-size"],"cropMargin":[2,"crop-margin"],"useDocumentClassification":[4,"use-document-classification"],"useDocumentDetector":[4,"use-document-detector"],"preferredCamera":[1,"preferred-camera"],"captureDelay":[2,"capture-delay"],"enableBackDocumentTimer":[4,"enable-back-document-timer"],"backDocumentTimerDuration":[2,"back-document-timer-duration"],"detectionBoxes":[32],"sideAlignment":[32],"isMaskReady":[32],"shouldMirrorVideo":[32],"showCameraSelector":[32],"isSwitchingCamera":[32],"hasDocumentDetected":[32],"cameraInfoWithAutofocus":[32],"currentStatus":[32],"performanceData":[32],"backDocumentTimerRemaining":[32],"showManualCaptureButton":[32],"performanceDegradedMode":[32],"showPerformanceMessage":[32],"captureStateVersion":[32],"processingButton":[32],"getCapturedImages":[64],"isProcessCompleted":[64],"startCapture":[64],"stopCapture":[64],"resetCapture":[64],"skipBackCapture":[64],"getStatus":[64],"preloadModel":[64],"getCameraInfo":[64],"setPreferredCamera":[64],"setCaptureDelay":[64],"getCaptureDelay":[64],"getResourceReport":[64],"forceResourceCleanup":[64]}]]]], options);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
exports.setNonce = index.setNonce;
|
|
@@ -650,6 +650,10 @@ class DetectionService {
|
|
|
650
650
|
// Static canvas pool for reuse across instances
|
|
651
651
|
static canvasPool = new Map();
|
|
652
652
|
static MAX_POOL_SIZE = 5;
|
|
653
|
+
// Resource management for optimization
|
|
654
|
+
modelLoadController;
|
|
655
|
+
classificationLoadController;
|
|
656
|
+
modelBlobUrls = new Set();
|
|
653
657
|
constructor(debug = false, useDocumentClassification = false, useDocumentDetector = true) {
|
|
654
658
|
this.debug = debug;
|
|
655
659
|
this.useDocumentClassification = useDocumentClassification;
|
|
@@ -667,11 +671,26 @@ class DetectionService {
|
|
|
667
671
|
return;
|
|
668
672
|
}
|
|
669
673
|
try {
|
|
674
|
+
// Create AbortController for this load operation
|
|
675
|
+
this.modelLoadController = new AbortController();
|
|
670
676
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
671
677
|
try {
|
|
672
|
-
|
|
678
|
+
// Load model with abort signal support
|
|
679
|
+
const response = await fetch(this.MODEL_PATH, {
|
|
680
|
+
signal: this.modelLoadController.signal
|
|
681
|
+
});
|
|
682
|
+
if (!response.ok) {
|
|
683
|
+
throw new Error(`Failed to fetch model: ${response.status}`);
|
|
684
|
+
}
|
|
685
|
+
const modelBuffer = await response.arrayBuffer();
|
|
686
|
+
this.session = await window.ort.InferenceSession.create(modelBuffer, sessionOptions);
|
|
673
687
|
}
|
|
674
688
|
catch (error) {
|
|
689
|
+
// Handle abort signal
|
|
690
|
+
if (error.name === 'AbortError') {
|
|
691
|
+
console.log('Model loading was cancelled');
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
675
694
|
// Múltiples tipos de errores de memoria que pueden ocurrir
|
|
676
695
|
const isMemoryError = error.message.includes('failed to allocate a buffer') ||
|
|
677
696
|
error.message.includes('Out of memory') ||
|
|
@@ -693,7 +712,15 @@ class DetectionService {
|
|
|
693
712
|
sessionLogVerbosityLevel: 0,
|
|
694
713
|
};
|
|
695
714
|
try {
|
|
696
|
-
|
|
715
|
+
// Retry with fallback options and abort signal
|
|
716
|
+
const fallbackResponse = await fetch(this.MODEL_PATH, {
|
|
717
|
+
signal: this.modelLoadController.signal
|
|
718
|
+
});
|
|
719
|
+
if (!fallbackResponse.ok) {
|
|
720
|
+
throw new Error(`Failed to fetch model with fallback: ${fallbackResponse.status}`);
|
|
721
|
+
}
|
|
722
|
+
const fallbackBuffer = await fallbackResponse.arrayBuffer();
|
|
723
|
+
this.session = await window.ort.InferenceSession.create(fallbackBuffer, fallbackOptions);
|
|
697
724
|
}
|
|
698
725
|
catch (fallbackError) {
|
|
699
726
|
throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
|
|
@@ -714,9 +741,13 @@ class DetectionService {
|
|
|
714
741
|
return;
|
|
715
742
|
}
|
|
716
743
|
try {
|
|
744
|
+
// Create AbortController for classification model loading
|
|
745
|
+
this.classificationLoadController = new AbortController();
|
|
717
746
|
// Try to load class map (optional - SqueezeNet may not need it for basic classification)
|
|
718
747
|
try {
|
|
719
|
-
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH
|
|
748
|
+
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH, {
|
|
749
|
+
signal: this.classificationLoadController.signal
|
|
750
|
+
});
|
|
720
751
|
if (classResponse.ok) {
|
|
721
752
|
this.mobileNetClassMap = await classResponse.json();
|
|
722
753
|
}
|
|
@@ -734,9 +765,22 @@ class DetectionService {
|
|
|
734
765
|
// Load model
|
|
735
766
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
736
767
|
try {
|
|
737
|
-
|
|
768
|
+
// Load classification model with abort signal support
|
|
769
|
+
const response = await fetch(this.MOBILENET_MODEL_PATH, {
|
|
770
|
+
signal: this.classificationLoadController.signal
|
|
771
|
+
});
|
|
772
|
+
if (!response.ok) {
|
|
773
|
+
throw new Error(`Failed to fetch classification model: ${response.status}`);
|
|
774
|
+
}
|
|
775
|
+
const modelBuffer = await response.arrayBuffer();
|
|
776
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(modelBuffer, sessionOptions);
|
|
738
777
|
}
|
|
739
778
|
catch (error) {
|
|
779
|
+
// Handle abort signal
|
|
780
|
+
if (error.name === 'AbortError') {
|
|
781
|
+
console.log('Classification model loading was cancelled');
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
740
784
|
// Múltiples tipos de errores de memoria que pueden ocurrir
|
|
741
785
|
const isMemoryError = error.message.includes('failed to allocate a buffer') ||
|
|
742
786
|
error.message.includes('Out of memory') ||
|
|
@@ -758,7 +802,15 @@ class DetectionService {
|
|
|
758
802
|
sessionLogVerbosityLevel: 0,
|
|
759
803
|
};
|
|
760
804
|
try {
|
|
761
|
-
|
|
805
|
+
// Retry with fallback options and abort signal
|
|
806
|
+
const fallbackResponse = await fetch(this.MOBILENET_MODEL_PATH, {
|
|
807
|
+
signal: this.classificationLoadController.signal
|
|
808
|
+
});
|
|
809
|
+
if (!fallbackResponse.ok) {
|
|
810
|
+
throw new Error(`Failed to fetch classification model with fallback: ${fallbackResponse.status}`);
|
|
811
|
+
}
|
|
812
|
+
const fallbackBuffer = await fallbackResponse.arrayBuffer();
|
|
813
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(fallbackBuffer, fallbackOptions);
|
|
762
814
|
}
|
|
763
815
|
catch (fallbackError) {
|
|
764
816
|
throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
|
|
@@ -948,6 +1000,9 @@ class DetectionService {
|
|
|
948
1000
|
return alignment.top && alignment.right && alignment.bottom && alignment.left;
|
|
949
1001
|
}
|
|
950
1002
|
cleanup() {
|
|
1003
|
+
// Cancel any ongoing requests
|
|
1004
|
+
this.abortPendingRequests();
|
|
1005
|
+
// Cleanup ONNX resources
|
|
951
1006
|
this.cleanupCanvasPool();
|
|
952
1007
|
if (this.session) {
|
|
953
1008
|
this.session.release?.();
|
|
@@ -959,6 +1014,8 @@ class DetectionService {
|
|
|
959
1014
|
}
|
|
960
1015
|
this.mobileNetClassMap = undefined;
|
|
961
1016
|
this.modelLoaded = false;
|
|
1017
|
+
// Cleanup blob URLs
|
|
1018
|
+
this.revokeModelBlobUrls();
|
|
962
1019
|
}
|
|
963
1020
|
initializeCanvasPool() {
|
|
964
1021
|
this.preprocessCanvas = document.createElement('canvas');
|
|
@@ -1056,11 +1113,93 @@ class DetectionService {
|
|
|
1056
1113
|
const pool = DetectionService.canvasPool.get(key);
|
|
1057
1114
|
// Only return to pool if not at max capacity
|
|
1058
1115
|
if (pool.length < DetectionService.MAX_POOL_SIZE) {
|
|
1116
|
+
const ctx = canvas.getContext('2d');
|
|
1117
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1059
1118
|
pool.push(canvas);
|
|
1060
1119
|
}
|
|
1120
|
+
// If pool is full, let the canvas be garbage collected
|
|
1121
|
+
}
|
|
1122
|
+
// Resource management methods for cache and browser optimization
|
|
1123
|
+
async clearModelCache() {
|
|
1124
|
+
try {
|
|
1125
|
+
// Clear cache specific to models
|
|
1126
|
+
const cacheNames = await caches.keys();
|
|
1127
|
+
const modelCaches = cacheNames.filter(name => name.includes('onnx') ||
|
|
1128
|
+
name.includes('jaak-static') ||
|
|
1129
|
+
name.includes('squeezenet') ||
|
|
1130
|
+
name.includes('ddmyp'));
|
|
1131
|
+
await Promise.all(modelCaches.map(cacheName => caches.delete(cacheName)));
|
|
1132
|
+
if (this.debug) {
|
|
1133
|
+
console.log(`🧹 Cleared ${modelCaches.length} model caches`);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
catch (error) {
|
|
1137
|
+
console.warn('Error clearing model cache:', error);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
abortPendingRequests() {
|
|
1141
|
+
// Cancel any fetch in progress
|
|
1142
|
+
if (this.modelLoadController) {
|
|
1143
|
+
this.modelLoadController.abort();
|
|
1144
|
+
this.modelLoadController = undefined;
|
|
1145
|
+
}
|
|
1146
|
+
if (this.classificationLoadController) {
|
|
1147
|
+
this.classificationLoadController.abort();
|
|
1148
|
+
this.classificationLoadController = undefined;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
revokeModelBlobUrls() {
|
|
1152
|
+
// Revoke any blob URLs created for models
|
|
1153
|
+
this.modelBlobUrls.forEach(url => URL.revokeObjectURL(url));
|
|
1154
|
+
this.modelBlobUrls.clear();
|
|
1155
|
+
}
|
|
1156
|
+
// Progressive resource release for optimization
|
|
1157
|
+
async releaseMobileNetResources() {
|
|
1158
|
+
if (this.mobileNetSession) {
|
|
1159
|
+
this.mobileNetSession.release?.();
|
|
1160
|
+
this.mobileNetSession = undefined;
|
|
1161
|
+
}
|
|
1162
|
+
this.mobileNetClassMap = undefined;
|
|
1163
|
+
if (this.classificationLoadController) {
|
|
1164
|
+
this.classificationLoadController.abort();
|
|
1165
|
+
this.classificationLoadController = undefined;
|
|
1166
|
+
}
|
|
1167
|
+
if (this.debug) {
|
|
1168
|
+
console.log('🧹 Released MobileNet classification resources');
|
|
1169
|
+
}
|
|
1061
1170
|
}
|
|
1062
|
-
//
|
|
1063
|
-
|
|
1171
|
+
// Optimize canvas pool by reducing to specific size
|
|
1172
|
+
optimizeCanvasPool(maxSize) {
|
|
1173
|
+
DetectionService.canvasPool.forEach((pool, key) => {
|
|
1174
|
+
while (pool.length > maxSize) {
|
|
1175
|
+
const canvas = pool.pop();
|
|
1176
|
+
// Explicitly clear and nullify for garbage collection
|
|
1177
|
+
if (canvas) {
|
|
1178
|
+
const ctx = canvas.getContext('2d');
|
|
1179
|
+
if (ctx) {
|
|
1180
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
if (pool.length === 0) {
|
|
1185
|
+
DetectionService.canvasPool.delete(key);
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
1188
|
+
if (this.debug) {
|
|
1189
|
+
console.log(`🧹 Optimized canvas pool to max size: ${maxSize}`);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
// Clear all static canvas pools
|
|
1193
|
+
static clearAllCanvasPools() {
|
|
1194
|
+
DetectionService.canvasPool.forEach((pool) => {
|
|
1195
|
+
pool.forEach(canvas => {
|
|
1196
|
+
const ctx = canvas.getContext('2d');
|
|
1197
|
+
if (ctx) {
|
|
1198
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1199
|
+
}
|
|
1200
|
+
});
|
|
1201
|
+
pool.length = 0;
|
|
1202
|
+
});
|
|
1064
1203
|
DetectionService.canvasPool.clear();
|
|
1065
1204
|
}
|
|
1066
1205
|
// Method to get pool statistics for debugging
|
|
@@ -1537,7 +1676,13 @@ const JaakStamps = class {
|
|
|
1537
1676
|
}
|
|
1538
1677
|
try {
|
|
1539
1678
|
this.exitSession();
|
|
1540
|
-
|
|
1679
|
+
// Aggressive cleanup when stopping capture
|
|
1680
|
+
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
1681
|
+
return {
|
|
1682
|
+
success: true,
|
|
1683
|
+
resourcesFreed: cleanupResult.freed,
|
|
1684
|
+
cleanupErrors: cleanupResult.errors
|
|
1685
|
+
};
|
|
1541
1686
|
}
|
|
1542
1687
|
catch (error) {
|
|
1543
1688
|
return { success: false, error: error.message };
|
|
@@ -1762,6 +1907,29 @@ const JaakStamps = class {
|
|
|
1762
1907
|
// Always allow getting capture delay, even during initialization
|
|
1763
1908
|
return this.captureDelay;
|
|
1764
1909
|
}
|
|
1910
|
+
async getResourceReport() {
|
|
1911
|
+
// Public method to get resource usage report
|
|
1912
|
+
return this.getResourceReportInternal();
|
|
1913
|
+
}
|
|
1914
|
+
async forceResourceCleanup() {
|
|
1915
|
+
// Public method to force aggressive resource cleanup
|
|
1916
|
+
const readyCheck = this.isComponentReady();
|
|
1917
|
+
if (!readyCheck.ready) {
|
|
1918
|
+
return { success: false, error: readyCheck.message };
|
|
1919
|
+
}
|
|
1920
|
+
try {
|
|
1921
|
+
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
1922
|
+
return {
|
|
1923
|
+
success: true,
|
|
1924
|
+
resourcesFreed: cleanupResult.freed,
|
|
1925
|
+
errors: cleanupResult.errors,
|
|
1926
|
+
timestamp: new Date().toISOString()
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
catch (error) {
|
|
1930
|
+
return { success: false, error: error.message };
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1765
1933
|
// Memory and device capability detection
|
|
1766
1934
|
checkDeviceCapabilities() {
|
|
1767
1935
|
const deviceMemory = navigator.deviceMemory;
|
|
@@ -2097,11 +2265,182 @@ const JaakStamps = class {
|
|
|
2097
2265
|
}
|
|
2098
2266
|
// Clear canvas pool
|
|
2099
2267
|
this.canvasPool = [];
|
|
2268
|
+
// Clean DOM references and memory leaks
|
|
2269
|
+
this.cleanupDOMReferences();
|
|
2100
2270
|
this.detectionBoxes = [];
|
|
2101
2271
|
this.alignmentStartTime = undefined;
|
|
2102
2272
|
this.hasDocumentDetected = false;
|
|
2103
2273
|
this.serviceContainer?.cleanup();
|
|
2104
2274
|
}
|
|
2275
|
+
// AGGRESSIVE RESOURCE CLEANUP METHODS
|
|
2276
|
+
// Clean DOM references and memory leaks
|
|
2277
|
+
cleanupDOMReferences() {
|
|
2278
|
+
// Remove event listeners that could maintain references
|
|
2279
|
+
if (this.videoRef) {
|
|
2280
|
+
// Remove common event listeners (if any were added)
|
|
2281
|
+
this.videoRef.srcObject = null;
|
|
2282
|
+
this.videoRef = undefined;
|
|
2283
|
+
}
|
|
2284
|
+
// Clean detection container
|
|
2285
|
+
if (this.detectionContainer) {
|
|
2286
|
+
this.detectionContainer = undefined;
|
|
2287
|
+
}
|
|
2288
|
+
// Clean references to images in the DOM and revoke blob URLs
|
|
2289
|
+
const images = this.el.shadowRoot?.querySelectorAll('img');
|
|
2290
|
+
images?.forEach(img => {
|
|
2291
|
+
if (img.src.startsWith('blob:')) {
|
|
2292
|
+
URL.revokeObjectURL(img.src);
|
|
2293
|
+
}
|
|
2294
|
+
img.src = '';
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
// Clear browser cache and storage
|
|
2298
|
+
async clearBrowserCache() {
|
|
2299
|
+
try {
|
|
2300
|
+
// 1. Clear Service Worker cache if exists
|
|
2301
|
+
if ('serviceWorker' in navigator) {
|
|
2302
|
+
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
2303
|
+
await Promise.all(registrations.map(registration => registration.unregister()));
|
|
2304
|
+
}
|
|
2305
|
+
// 2. Clear Cache API specific to the component
|
|
2306
|
+
const cacheNames = await caches.keys();
|
|
2307
|
+
const componentCaches = cacheNames.filter(name => name.includes('jaak') || name.includes('stamps'));
|
|
2308
|
+
await Promise.all(componentCaches.map(cacheName => caches.delete(cacheName)));
|
|
2309
|
+
// 3. Force garbage collection if available (only in dev)
|
|
2310
|
+
if (this.debug && window.gc) {
|
|
2311
|
+
window.gc();
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
catch (error) {
|
|
2315
|
+
console.warn('Error clearing browser cache:', error);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
// Clear local storage specific to the component
|
|
2319
|
+
async clearLocalStorage() {
|
|
2320
|
+
try {
|
|
2321
|
+
// Clear localStorage specific to the component
|
|
2322
|
+
const keysToRemove = [];
|
|
2323
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
2324
|
+
const key = localStorage.key(i);
|
|
2325
|
+
if (key && (key.includes('jaak') || key.includes('onnx') || key.includes('stamps'))) {
|
|
2326
|
+
keysToRemove.push(key);
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
2330
|
+
// Clear sessionStorage
|
|
2331
|
+
const sessionKeysToRemove = [];
|
|
2332
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
2333
|
+
const key = sessionStorage.key(i);
|
|
2334
|
+
if (key && (key.includes('jaak') || key.includes('onnx'))) {
|
|
2335
|
+
sessionKeysToRemove.push(key);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
sessionKeysToRemove.forEach(key => sessionStorage.removeItem(key));
|
|
2339
|
+
}
|
|
2340
|
+
catch (error) {
|
|
2341
|
+
console.warn('Error clearing local storage:', error);
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
// Aggressive resource cleanup combining all strategies
|
|
2345
|
+
async aggressiveResourceCleanup() {
|
|
2346
|
+
const freed = [];
|
|
2347
|
+
const errors = [];
|
|
2348
|
+
try {
|
|
2349
|
+
// 1. Release ONNX resources
|
|
2350
|
+
this.releaseOnnxResources();
|
|
2351
|
+
freed.push('ONNX models');
|
|
2352
|
+
// 2. Clear model cache in detection service
|
|
2353
|
+
if (this.detectionService) {
|
|
2354
|
+
await this.detectionService.clearModelCache();
|
|
2355
|
+
freed.push('Model cache');
|
|
2356
|
+
}
|
|
2357
|
+
// 3. Clear browser cache
|
|
2358
|
+
await this.clearBrowserCache();
|
|
2359
|
+
freed.push('Browser cache');
|
|
2360
|
+
// 4. Clear local storage
|
|
2361
|
+
await this.clearLocalStorage();
|
|
2362
|
+
freed.push('Local storage');
|
|
2363
|
+
// 5. Clean DOM references
|
|
2364
|
+
this.cleanupDOMReferences();
|
|
2365
|
+
freed.push('DOM references');
|
|
2366
|
+
// 6. Optimize canvas pools
|
|
2367
|
+
if (this.detectionService) {
|
|
2368
|
+
this.detectionService.optimizeCanvasPool(0);
|
|
2369
|
+
freed.push('Canvas pools');
|
|
2370
|
+
}
|
|
2371
|
+
// 7. Clear component canvas pool
|
|
2372
|
+
this.canvasPool = [];
|
|
2373
|
+
freed.push('Component canvas pool');
|
|
2374
|
+
// 8. Force garbage collection (if available)
|
|
2375
|
+
if (this.debug && window.gc) {
|
|
2376
|
+
window.gc();
|
|
2377
|
+
freed.push('Garbage collection');
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
catch (error) {
|
|
2381
|
+
errors.push(error.message);
|
|
2382
|
+
}
|
|
2383
|
+
return { freed, errors };
|
|
2384
|
+
}
|
|
2385
|
+
// Progressive resource release after front capture
|
|
2386
|
+
async onFrontCaptureComplete() {
|
|
2387
|
+
try {
|
|
2388
|
+
// Release classification model if not needed for back
|
|
2389
|
+
if (this.detectionService && !this.useDocumentClassification) {
|
|
2390
|
+
await this.detectionService.releaseMobileNetResources();
|
|
2391
|
+
}
|
|
2392
|
+
// Optimize canvas pool to minimum needed for back capture
|
|
2393
|
+
if (this.detectionService) {
|
|
2394
|
+
this.detectionService.optimizeCanvasPool(1);
|
|
2395
|
+
}
|
|
2396
|
+
// Reduce component canvas pool
|
|
2397
|
+
while (this.canvasPool.length > 1) {
|
|
2398
|
+
const canvas = this.canvasPool.pop();
|
|
2399
|
+
if (canvas) {
|
|
2400
|
+
const ctx = canvas.getContext('2d');
|
|
2401
|
+
if (ctx) {
|
|
2402
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
if (this.debug) {
|
|
2407
|
+
console.log('🧹 Progressive cleanup after front capture completed');
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
catch (error) {
|
|
2411
|
+
console.warn('Error in progressive cleanup:', error);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
// Resource reporting for debugging
|
|
2415
|
+
getResourceReportInternal() {
|
|
2416
|
+
const report = {
|
|
2417
|
+
timestamp: new Date().toISOString(),
|
|
2418
|
+
component: {
|
|
2419
|
+
hasVideoStream: !!this.videoStream,
|
|
2420
|
+
hasAnimationFrame: !!this.animationId,
|
|
2421
|
+
canvasPoolSize: this.canvasPool.length,
|
|
2422
|
+
detectionBoxesCount: this.detectionBoxes.length,
|
|
2423
|
+
performanceMonitorActive: !!this.performanceUpdateInterval
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
// Add detection service stats if available
|
|
2427
|
+
if (this.detectionService) {
|
|
2428
|
+
report.detectionService = {
|
|
2429
|
+
isModelLoaded: this.detectionService.isModelLoaded(),
|
|
2430
|
+
canvasPoolStats: this.detectionService.getPoolStats()
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
// Add memory info if available
|
|
2434
|
+
if ('memory' in performance) {
|
|
2435
|
+
const memInfo = performance.memory;
|
|
2436
|
+
report.memory = {
|
|
2437
|
+
usedJSHeapSize: Math.round(memInfo.usedJSHeapSize / 1048576), // MB
|
|
2438
|
+
totalJSHeapSize: Math.round(memInfo.totalJSHeapSize / 1048576), // MB
|
|
2439
|
+
jsHeapSizeLimit: Math.round(memInfo.jsHeapSizeLimit / 1048576) // MB
|
|
2440
|
+
};
|
|
2441
|
+
}
|
|
2442
|
+
return report;
|
|
2443
|
+
}
|
|
2105
2444
|
render() {
|
|
2106
2445
|
const captureState = this.stateManager?.getCaptureState() || {
|
|
2107
2446
|
isVideoActive: false,
|
|
@@ -2111,7 +2450,7 @@ const JaakStamps = class {
|
|
|
2111
2450
|
isCapturing: false
|
|
2112
2451
|
};
|
|
2113
2452
|
const cameraInfo = this.cameraInfoWithAutofocus;
|
|
2114
|
-
return (index.h("div", { key: '
|
|
2453
|
+
return (index.h("div", { key: 'ca44b9eacbde9f688f2c41aa21ad35a2b882002b', class: "detector-container" }, index.h("div", { key: '86c7b5d31ed69276b62fa43e01c92b0c0b9f298d', class: "video-container" }, index.h("video", { key: 'bb310544c17ae734d8b42ed2617748c2f69743ff', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), index.h("div", { key: '078a79a0c6386ca6c040e629acbf4f52f12d8e29', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index$1) => (index.h("div", { key: index$1, class: "detection-box", style: {
|
|
2115
2454
|
position: 'absolute',
|
|
2116
2455
|
left: `${box.x}px`,
|
|
2117
2456
|
top: `${box.y}px`,
|
|
@@ -2120,9 +2459,9 @@ const JaakStamps = class {
|
|
|
2120
2459
|
border: '2px solid #32406C',
|
|
2121
2460
|
pointerEvents: 'none',
|
|
2122
2461
|
boxSizing: 'border-box'
|
|
2123
|
-
} })))), this.isMaskReady && (index.h("div", { key: '
|
|
2462
|
+
} })))), this.isMaskReady && (index.h("div", { key: '990015ff5b2ded89405dd2b891c027aad8788f3e', class: "overlay-mask" }, index.h("div", { key: 'dd9145140987addb1b6e4cc2119b8e9f89003726', class: "card-outline" }, index.h("div", { key: 'e8f4ec692a8b80c7a98075e4f708b79a272db67b', class: "side side-top" }), index.h("div", { key: '2577253b40425ecdcb3dd51b2f4a43c15c83ca79', class: "side side-right" }), index.h("div", { key: '30f4454020c00cf4fb92308af5ecf1843ec24005', class: "side side-bottom" }), index.h("div", { key: '3459f634987b8396106fff23fa1c99b295ee7d0a', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '84f4d70947e211a9c6b296531d53420223d2ca34', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '7c28e86e8de6c33bf7d7baab3083c5f6a0ab060f', class: "back-capture-section" }, index.h("div", { key: '27bdcf9cfe9790c4bbad484423f6f4c353ccf0b3', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (index.h("button", { key: 'ed0320b1df412bf53cbe4e4119f6d769926eb2c5', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (index.h("span", { key: '0bba706c8ebda59c33faca8e97b7536146462d21', class: "button-spinner" })), "Capturar Reverso")), index.h("button", { key: 'b072c46b25d16a04c5e0a8f3975d8aa8d24576d5', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (index.h("span", { key: 'efd0827d0f19c9072d49d1b3c0046ef6c95a842c', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
2124
2463
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
2125
|
-
: 'Saltar reverso')))), captureState.isVideoActive && (index.h("div", { key: '
|
|
2464
|
+
: 'Saltar reverso')))), captureState.isVideoActive && (index.h("div", { key: 'bd0303e8ce85211e4c68fc3cd79545db7c74288b', class: "camera-controls" }, index.h("button", { key: 'e4327d260b988b67e0de3654c72f24455cd3bbbd', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (index.h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (index.h("div", { key: '35da16baf4c62466c2a66650209a1daf842c0db2', class: "camera-selector-dropdown" }, index.h("div", { key: '7035dd0e22f614cf239a015c6d59531b68fa74a9', class: "camera-selector-header" }, index.h("span", { key: 'ae026f31931f52ea0daf72391a9de4ff9e4b88f1' }, "Seleccionar C\u00E1mara"), index.h("button", { key: '40f2ba0770bafea71fedbf849938010e743be776', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: '6f26927094a724a35ebb07ef701caf14f296a837', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (index.h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, index.h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (index.h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (index.h("span", { class: "selected-indicator" }, "\u2713")))))), index.h("div", { key: '082236981c369f8fd487f036439b16e6560514be', class: "device-info" }, index.h("small", { key: 'ccbbfa0bca96395f574bda71103338ae9226f709' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '6baa487409503777e90453b39fd30649941cbbe4', class: "manual-capture-section" }, index.h("button", { key: '893f4715ca3315613f779603f178459955f686f0', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (index.h("span", { key: '7c30b680fe84f4c8227beba45d8416f1edb8348f', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (index.h("div", { key: 'd8ef052cf67d7bda4b3534febf96efa2cdcc6bf7', class: "capture-animation" })), captureState.showFlipAnimation && (index.h("div", { key: '3dae7634e770dda2655b1042ec8fb997b3cb2220', class: "flip-animation" }, index.h("div", { key: 'a977fc5e889369121bacc06cf5213dfee39d858d', class: "id-card-icon" }), index.h("div", { key: '4f052cdc8c7b493cddce90e108e976325c9bab8e', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (index.h("div", { key: '5117cd6e63e1e0763c1503f6f60f3c7ef60dd665', class: "success-animation" }, index.h("div", { key: 'd11128c45c7118915cc0dd9c25845bb55b4b3eb2', class: "check-icon" }), index.h("div", { key: '693e197bf8a4c91ac7ddedb457d15aa3aa46a5d5', class: "success-text" }, "\u00A1Proceso completado!"))), index.h("div", { key: '89caaa9d78eb522e0fadfd1c0e844e05d29b8286', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (index.h("div", { key: '3f41d40b05b71e13e7e1d0c0e980b48308cba9d6', class: "status-spinner" })), index.h("div", { key: '8674480928f4f4a77099c0600ed84f0e27903379', class: "status-content" }, index.h("div", { key: 'e88f4044ab8115a6a5cbb78cc4030975eef9149a', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (index.h("div", { key: 'ad581e42c30c304b837711549148c11b801dee5e', class: "status-description" }, this.currentStatus.description)))), this.debug && (index.h("div", { key: '1b371c1b47f3ba699885db00fef52eb448f9e245', class: "performance-monitor" }, index.h("div", { key: '8b9ac60449a15e9fb966c329b2e6c9285c0228c0', class: "performance-expanded" }, index.h("div", { key: '760c11a3ed28e248a144d53a92fd8adee6822ff2', class: "metrics-row" }, index.h("div", { key: '858ab8b106f1d12210e4e15c423ab3bab11bdb60', class: "metric-compact" }, index.h("span", { key: '65fe2479c3822d99452e7e5faa93c25a80765024', class: "metric-label" }, "FPS"), index.h("span", { key: '9faf7763d1284a6f3c1e15fb0f3c99f1d45bb4dc', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), index.h("div", { key: 'c95ed8fa8101f42bfba5cc8ca7debbad028caf42', class: "metric-compact" }, index.h("span", { key: '3555b3778cb71e959d132d6aa563f58f757915d8', class: "metric-label" }, "MEM"), index.h("span", { key: 'd72b0491147a6f80dcf33f5d72dd8dae5898f44a', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), index.h("div", { key: 'cd4dc81f28f9e59a36a0295dde7bfee89f50d3e2', class: "metrics-row" }, index.h("div", { key: 'c43d8624c5da256ce3e97f67b022bdd98fb04e07', class: "metric-compact" }, index.h("span", { key: 'adb402d83809a90580b81544f31b5331bdff59dd', class: "metric-label" }, "INF"), index.h("span", { key: 'e48c9814c08acbe321460a29e42cb49b0eab38ff', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), index.h("div", { key: '71517f9279e9a6aa0d5b02e2169db868ca3fb676', class: "metric-compact" }, index.h("span", { key: '3ddbcc56b137aea702a40f900178e8e16efd146e', class: "metric-label" }, "FRAME"), index.h("span", { key: 'd3a70afb511d3f72b485b3a1cb922876faa62b11', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), index.h("div", { key: 'a3b62cdbe830b1024ab96f7fb726825df24fecdc', class: "metrics-row" }, index.h("div", { key: '95471e5cdde748d1ce6bdee3540740dd3aa66865', class: "metric-compact" }, index.h("span", { key: 'c93e01c0bdc67621c3a666357d2477b7a5275d52', class: "metric-label" }, "DET"), index.h("span", { key: 'ef33c86a83101b0989b14c7f1ab0ce0b331b397f', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), index.h("div", { key: 'e14d56128bf96337e4c3859f8e4003e001e8e953', class: "metric-compact" }, index.h("span", { key: '8aa828632f0a977a27ed377f103bb8d5e7e0ed1a', class: "metric-label" }, "RATE"), index.h("span", { key: '59035c473b5a747a27f774e2554322a084b6d19f', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), index.h("div", { key: '4d0c2de00954416faac30ab622fb21f36c932aca', class: "watermark" }, index.h("img", { key: 'd8f775428ebb3ba86a943ca73b074db2180d0c29', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
2126
2465
|
}
|
|
2127
2466
|
// Utility methods
|
|
2128
2467
|
updateDetectionBoxes(boxes) {
|
|
@@ -2346,6 +2685,8 @@ const JaakStamps = class {
|
|
|
2346
2685
|
showFlipAnimation: true
|
|
2347
2686
|
});
|
|
2348
2687
|
this.triggerRerender();
|
|
2688
|
+
// Progressive resource cleanup after front capture
|
|
2689
|
+
await this.onFrontCaptureComplete();
|
|
2349
2690
|
setTimeout(() => {
|
|
2350
2691
|
this.stateManager.updateCaptureState({
|
|
2351
2692
|
showFlipAnimation: false,
|
|
@@ -2421,6 +2762,8 @@ const JaakStamps = class {
|
|
|
2421
2762
|
isDetectionPaused: true,
|
|
2422
2763
|
showFlipAnimation: true
|
|
2423
2764
|
});
|
|
2765
|
+
// Progressive resource cleanup after front capture
|
|
2766
|
+
await this.onFrontCaptureComplete();
|
|
2424
2767
|
setTimeout(() => {
|
|
2425
2768
|
this.stateManager.updateCaptureState({
|
|
2426
2769
|
showFlipAnimation: false,
|
|
@@ -2598,6 +2941,16 @@ const JaakStamps = class {
|
|
|
2598
2941
|
setTimeout(() => {
|
|
2599
2942
|
this.stateManager.updateCaptureState({ showSuccessAnimation: false });
|
|
2600
2943
|
}, 3000);
|
|
2944
|
+
// Aggressive cleanup after process completion with delay
|
|
2945
|
+
setTimeout(async () => {
|
|
2946
|
+
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
2947
|
+
if (this.debug) {
|
|
2948
|
+
console.log('🧹 Post-completion cleanup:', cleanupResult.freed);
|
|
2949
|
+
if (cleanupResult.errors.length > 0) {
|
|
2950
|
+
console.warn('⚠️ Cleanup errors:', cleanupResult.errors);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
}, 1000); // Wait 1 second after completion
|
|
2601
2954
|
}
|
|
2602
2955
|
stopDetection() {
|
|
2603
2956
|
if (this.animationId) {
|