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