@jaak.ai/stamps 2.1.0-dev.7 → 2.1.0-dev.9
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 +377 -17
- 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 +265 -12
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/DetectionService.js +148 -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 +380 -18
- 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 +377 -17
- 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-c115e01a.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-c115e01a.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,28 @@ class DetectionService {
|
|
|
667
671
|
return;
|
|
668
672
|
}
|
|
669
673
|
try {
|
|
674
|
+
if (typeof window.ort === 'undefined') {
|
|
675
|
+
throw new Error('ONNX Runtime not available in window object');
|
|
676
|
+
}
|
|
677
|
+
// Create AbortController for this load operation
|
|
678
|
+
this.modelLoadController = new AbortController();
|
|
670
679
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
671
680
|
try {
|
|
672
|
-
|
|
681
|
+
// Load model with abort signal support
|
|
682
|
+
const response = await fetch(this.MODEL_PATH, {
|
|
683
|
+
signal: this.modelLoadController.signal
|
|
684
|
+
});
|
|
685
|
+
if (!response.ok) {
|
|
686
|
+
throw new Error(`Failed to fetch model: ${response.status}`);
|
|
687
|
+
}
|
|
688
|
+
const modelBuffer = await response.arrayBuffer();
|
|
689
|
+
this.session = await window.ort.InferenceSession.create(modelBuffer, sessionOptions);
|
|
673
690
|
}
|
|
674
691
|
catch (error) {
|
|
692
|
+
// Handle abort signal
|
|
693
|
+
if (error.name === 'AbortError') {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
675
696
|
// Múltiples tipos de errores de memoria que pueden ocurrir
|
|
676
697
|
const isMemoryError = error.message.includes('failed to allocate a buffer') ||
|
|
677
698
|
error.message.includes('Out of memory') ||
|
|
@@ -693,7 +714,15 @@ class DetectionService {
|
|
|
693
714
|
sessionLogVerbosityLevel: 0,
|
|
694
715
|
};
|
|
695
716
|
try {
|
|
696
|
-
|
|
717
|
+
// Retry with fallback options and abort signal
|
|
718
|
+
const fallbackResponse = await fetch(this.MODEL_PATH, {
|
|
719
|
+
signal: this.modelLoadController.signal
|
|
720
|
+
});
|
|
721
|
+
if (!fallbackResponse.ok) {
|
|
722
|
+
throw new Error(`Failed to fetch model with fallback: ${fallbackResponse.status}`);
|
|
723
|
+
}
|
|
724
|
+
const fallbackBuffer = await fallbackResponse.arrayBuffer();
|
|
725
|
+
this.session = await window.ort.InferenceSession.create(fallbackBuffer, fallbackOptions);
|
|
697
726
|
}
|
|
698
727
|
catch (fallbackError) {
|
|
699
728
|
throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
|
|
@@ -714,9 +743,13 @@ class DetectionService {
|
|
|
714
743
|
return;
|
|
715
744
|
}
|
|
716
745
|
try {
|
|
746
|
+
// Create AbortController for classification model loading
|
|
747
|
+
this.classificationLoadController = new AbortController();
|
|
717
748
|
// Try to load class map (optional - SqueezeNet may not need it for basic classification)
|
|
718
749
|
try {
|
|
719
|
-
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH
|
|
750
|
+
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH, {
|
|
751
|
+
signal: this.classificationLoadController.signal
|
|
752
|
+
});
|
|
720
753
|
if (classResponse.ok) {
|
|
721
754
|
this.mobileNetClassMap = await classResponse.json();
|
|
722
755
|
}
|
|
@@ -734,9 +767,22 @@ class DetectionService {
|
|
|
734
767
|
// Load model
|
|
735
768
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
736
769
|
try {
|
|
737
|
-
|
|
770
|
+
// Load classification model with abort signal support
|
|
771
|
+
const response = await fetch(this.MOBILENET_MODEL_PATH, {
|
|
772
|
+
signal: this.classificationLoadController.signal
|
|
773
|
+
});
|
|
774
|
+
if (!response.ok) {
|
|
775
|
+
throw new Error(`Failed to fetch classification model: ${response.status}`);
|
|
776
|
+
}
|
|
777
|
+
const modelBuffer = await response.arrayBuffer();
|
|
778
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(modelBuffer, sessionOptions);
|
|
738
779
|
}
|
|
739
780
|
catch (error) {
|
|
781
|
+
// Handle abort signal
|
|
782
|
+
if (error.name === 'AbortError') {
|
|
783
|
+
console.log('Classification model loading was cancelled');
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
740
786
|
// Múltiples tipos de errores de memoria que pueden ocurrir
|
|
741
787
|
const isMemoryError = error.message.includes('failed to allocate a buffer') ||
|
|
742
788
|
error.message.includes('Out of memory') ||
|
|
@@ -758,7 +804,15 @@ class DetectionService {
|
|
|
758
804
|
sessionLogVerbosityLevel: 0,
|
|
759
805
|
};
|
|
760
806
|
try {
|
|
761
|
-
|
|
807
|
+
// Retry with fallback options and abort signal
|
|
808
|
+
const fallbackResponse = await fetch(this.MOBILENET_MODEL_PATH, {
|
|
809
|
+
signal: this.classificationLoadController.signal
|
|
810
|
+
});
|
|
811
|
+
if (!fallbackResponse.ok) {
|
|
812
|
+
throw new Error(`Failed to fetch classification model with fallback: ${fallbackResponse.status}`);
|
|
813
|
+
}
|
|
814
|
+
const fallbackBuffer = await fallbackResponse.arrayBuffer();
|
|
815
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(fallbackBuffer, fallbackOptions);
|
|
762
816
|
}
|
|
763
817
|
catch (fallbackError) {
|
|
764
818
|
throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
|
|
@@ -948,6 +1002,9 @@ class DetectionService {
|
|
|
948
1002
|
return alignment.top && alignment.right && alignment.bottom && alignment.left;
|
|
949
1003
|
}
|
|
950
1004
|
cleanup() {
|
|
1005
|
+
// Cancel any ongoing requests
|
|
1006
|
+
this.abortPendingRequests();
|
|
1007
|
+
// Cleanup ONNX resources
|
|
951
1008
|
this.cleanupCanvasPool();
|
|
952
1009
|
if (this.session) {
|
|
953
1010
|
this.session.release?.();
|
|
@@ -959,6 +1016,8 @@ class DetectionService {
|
|
|
959
1016
|
}
|
|
960
1017
|
this.mobileNetClassMap = undefined;
|
|
961
1018
|
this.modelLoaded = false;
|
|
1019
|
+
// Cleanup blob URLs
|
|
1020
|
+
this.revokeModelBlobUrls();
|
|
962
1021
|
}
|
|
963
1022
|
initializeCanvasPool() {
|
|
964
1023
|
this.preprocessCanvas = document.createElement('canvas');
|
|
@@ -1056,11 +1115,93 @@ class DetectionService {
|
|
|
1056
1115
|
const pool = DetectionService.canvasPool.get(key);
|
|
1057
1116
|
// Only return to pool if not at max capacity
|
|
1058
1117
|
if (pool.length < DetectionService.MAX_POOL_SIZE) {
|
|
1118
|
+
const ctx = canvas.getContext('2d');
|
|
1119
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1059
1120
|
pool.push(canvas);
|
|
1060
1121
|
}
|
|
1122
|
+
// If pool is full, let the canvas be garbage collected
|
|
1123
|
+
}
|
|
1124
|
+
// Resource management methods for cache and browser optimization
|
|
1125
|
+
async clearModelCache() {
|
|
1126
|
+
try {
|
|
1127
|
+
// Clear cache specific to models
|
|
1128
|
+
const cacheNames = await caches.keys();
|
|
1129
|
+
const modelCaches = cacheNames.filter(name => name.includes('onnx') ||
|
|
1130
|
+
name.includes('jaak-static') ||
|
|
1131
|
+
name.includes('squeezenet') ||
|
|
1132
|
+
name.includes('ddmyp'));
|
|
1133
|
+
await Promise.all(modelCaches.map(cacheName => caches.delete(cacheName)));
|
|
1134
|
+
if (this.debug) {
|
|
1135
|
+
console.log(`🧹 Cleared ${modelCaches.length} model caches`);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
catch (error) {
|
|
1139
|
+
console.warn('Error clearing model cache:', error);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
abortPendingRequests() {
|
|
1143
|
+
// Cancel any fetch in progress
|
|
1144
|
+
if (this.modelLoadController) {
|
|
1145
|
+
this.modelLoadController.abort();
|
|
1146
|
+
this.modelLoadController = undefined;
|
|
1147
|
+
}
|
|
1148
|
+
if (this.classificationLoadController) {
|
|
1149
|
+
this.classificationLoadController.abort();
|
|
1150
|
+
this.classificationLoadController = undefined;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
revokeModelBlobUrls() {
|
|
1154
|
+
// Revoke any blob URLs created for models
|
|
1155
|
+
this.modelBlobUrls.forEach(url => URL.revokeObjectURL(url));
|
|
1156
|
+
this.modelBlobUrls.clear();
|
|
1157
|
+
}
|
|
1158
|
+
// Progressive resource release for optimization
|
|
1159
|
+
async releaseMobileNetResources() {
|
|
1160
|
+
if (this.mobileNetSession) {
|
|
1161
|
+
this.mobileNetSession.release?.();
|
|
1162
|
+
this.mobileNetSession = undefined;
|
|
1163
|
+
}
|
|
1164
|
+
this.mobileNetClassMap = undefined;
|
|
1165
|
+
if (this.classificationLoadController) {
|
|
1166
|
+
this.classificationLoadController.abort();
|
|
1167
|
+
this.classificationLoadController = undefined;
|
|
1168
|
+
}
|
|
1169
|
+
if (this.debug) {
|
|
1170
|
+
console.log('🧹 Released MobileNet classification resources');
|
|
1171
|
+
}
|
|
1061
1172
|
}
|
|
1062
|
-
//
|
|
1063
|
-
|
|
1173
|
+
// Optimize canvas pool by reducing to specific size
|
|
1174
|
+
optimizeCanvasPool(maxSize) {
|
|
1175
|
+
DetectionService.canvasPool.forEach((pool, key) => {
|
|
1176
|
+
while (pool.length > maxSize) {
|
|
1177
|
+
const canvas = pool.pop();
|
|
1178
|
+
// Explicitly clear and nullify for garbage collection
|
|
1179
|
+
if (canvas) {
|
|
1180
|
+
const ctx = canvas.getContext('2d');
|
|
1181
|
+
if (ctx) {
|
|
1182
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
if (pool.length === 0) {
|
|
1187
|
+
DetectionService.canvasPool.delete(key);
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
if (this.debug) {
|
|
1191
|
+
console.log(`🧹 Optimized canvas pool to max size: ${maxSize}`);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
// Clear all static canvas pools
|
|
1195
|
+
static clearAllCanvasPools() {
|
|
1196
|
+
DetectionService.canvasPool.forEach((pool) => {
|
|
1197
|
+
pool.forEach(canvas => {
|
|
1198
|
+
const ctx = canvas.getContext('2d');
|
|
1199
|
+
if (ctx) {
|
|
1200
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
pool.length = 0;
|
|
1204
|
+
});
|
|
1064
1205
|
DetectionService.canvasPool.clear();
|
|
1065
1206
|
}
|
|
1066
1207
|
// Method to get pool statistics for debugging
|
|
@@ -1537,7 +1678,13 @@ const JaakStamps = class {
|
|
|
1537
1678
|
}
|
|
1538
1679
|
try {
|
|
1539
1680
|
this.exitSession();
|
|
1540
|
-
|
|
1681
|
+
// Aggressive cleanup when stopping capture
|
|
1682
|
+
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
1683
|
+
return {
|
|
1684
|
+
success: true,
|
|
1685
|
+
resourcesFreed: cleanupResult.freed,
|
|
1686
|
+
cleanupErrors: cleanupResult.errors
|
|
1687
|
+
};
|
|
1541
1688
|
}
|
|
1542
1689
|
catch (error) {
|
|
1543
1690
|
return { success: false, error: error.message };
|
|
@@ -1762,6 +1909,29 @@ const JaakStamps = class {
|
|
|
1762
1909
|
// Always allow getting capture delay, even during initialization
|
|
1763
1910
|
return this.captureDelay;
|
|
1764
1911
|
}
|
|
1912
|
+
async getResourceReport() {
|
|
1913
|
+
// Public method to get resource usage report
|
|
1914
|
+
return this.getResourceReportInternal();
|
|
1915
|
+
}
|
|
1916
|
+
async forceResourceCleanup() {
|
|
1917
|
+
// Public method to force aggressive resource cleanup
|
|
1918
|
+
const readyCheck = this.isComponentReady();
|
|
1919
|
+
if (!readyCheck.ready) {
|
|
1920
|
+
return { success: false, error: readyCheck.message };
|
|
1921
|
+
}
|
|
1922
|
+
try {
|
|
1923
|
+
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
1924
|
+
return {
|
|
1925
|
+
success: true,
|
|
1926
|
+
resourcesFreed: cleanupResult.freed,
|
|
1927
|
+
errors: cleanupResult.errors,
|
|
1928
|
+
timestamp: new Date().toISOString()
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
catch (error) {
|
|
1932
|
+
return { success: false, error: error.message };
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1765
1935
|
// Memory and device capability detection
|
|
1766
1936
|
checkDeviceCapabilities() {
|
|
1767
1937
|
const deviceMemory = navigator.deviceMemory;
|
|
@@ -1785,13 +1955,18 @@ const JaakStamps = class {
|
|
|
1785
1955
|
memoryMB: null
|
|
1786
1956
|
};
|
|
1787
1957
|
}
|
|
1788
|
-
// Safari
|
|
1958
|
+
// Safari moderno soporta WASM, solo bloquearlo en versiones muy antiguas
|
|
1789
1959
|
if (isSafari) {
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1960
|
+
const safariMatch = navigator.userAgent.match(/Version\/(\d+)/);
|
|
1961
|
+
const safariVersion = safariMatch ? parseInt(safariMatch[1]) : 0;
|
|
1962
|
+
// Solo bloquear Safari muy antiguo (< version 14)
|
|
1963
|
+
if (safariVersion > 0 && safariVersion < 14) {
|
|
1964
|
+
return {
|
|
1965
|
+
canUseML: false,
|
|
1966
|
+
reason: 'Safari antiguo detectado. Modo manual activado por compatibilidad.',
|
|
1967
|
+
memoryMB: null
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1795
1970
|
}
|
|
1796
1971
|
return {
|
|
1797
1972
|
canUseML: true,
|
|
@@ -2097,11 +2272,182 @@ const JaakStamps = class {
|
|
|
2097
2272
|
}
|
|
2098
2273
|
// Clear canvas pool
|
|
2099
2274
|
this.canvasPool = [];
|
|
2275
|
+
// Clean DOM references and memory leaks
|
|
2276
|
+
this.cleanupDOMReferences();
|
|
2100
2277
|
this.detectionBoxes = [];
|
|
2101
2278
|
this.alignmentStartTime = undefined;
|
|
2102
2279
|
this.hasDocumentDetected = false;
|
|
2103
2280
|
this.serviceContainer?.cleanup();
|
|
2104
2281
|
}
|
|
2282
|
+
// AGGRESSIVE RESOURCE CLEANUP METHODS
|
|
2283
|
+
// Clean DOM references and memory leaks
|
|
2284
|
+
cleanupDOMReferences() {
|
|
2285
|
+
// Remove event listeners that could maintain references
|
|
2286
|
+
if (this.videoRef) {
|
|
2287
|
+
// Remove common event listeners (if any were added)
|
|
2288
|
+
this.videoRef.srcObject = null;
|
|
2289
|
+
this.videoRef = undefined;
|
|
2290
|
+
}
|
|
2291
|
+
// Clean detection container
|
|
2292
|
+
if (this.detectionContainer) {
|
|
2293
|
+
this.detectionContainer = undefined;
|
|
2294
|
+
}
|
|
2295
|
+
// Clean references to images in the DOM and revoke blob URLs
|
|
2296
|
+
const images = this.el.shadowRoot?.querySelectorAll('img');
|
|
2297
|
+
images?.forEach(img => {
|
|
2298
|
+
if (img.src.startsWith('blob:')) {
|
|
2299
|
+
URL.revokeObjectURL(img.src);
|
|
2300
|
+
}
|
|
2301
|
+
img.src = '';
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
// Clear browser cache and storage
|
|
2305
|
+
async clearBrowserCache() {
|
|
2306
|
+
try {
|
|
2307
|
+
// 1. Clear Service Worker cache if exists
|
|
2308
|
+
if ('serviceWorker' in navigator) {
|
|
2309
|
+
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
2310
|
+
await Promise.all(registrations.map(registration => registration.unregister()));
|
|
2311
|
+
}
|
|
2312
|
+
// 2. Clear Cache API specific to the component
|
|
2313
|
+
const cacheNames = await caches.keys();
|
|
2314
|
+
const componentCaches = cacheNames.filter(name => name.includes('jaak') || name.includes('stamps'));
|
|
2315
|
+
await Promise.all(componentCaches.map(cacheName => caches.delete(cacheName)));
|
|
2316
|
+
// 3. Force garbage collection if available (only in dev)
|
|
2317
|
+
if (this.debug && window.gc) {
|
|
2318
|
+
window.gc();
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
catch (error) {
|
|
2322
|
+
console.warn('Error clearing browser cache:', error);
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
// Clear local storage specific to the component
|
|
2326
|
+
async clearLocalStorage() {
|
|
2327
|
+
try {
|
|
2328
|
+
// Clear localStorage specific to the component
|
|
2329
|
+
const keysToRemove = [];
|
|
2330
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
2331
|
+
const key = localStorage.key(i);
|
|
2332
|
+
if (key && (key.includes('jaak') || key.includes('onnx') || key.includes('stamps'))) {
|
|
2333
|
+
keysToRemove.push(key);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
2337
|
+
// Clear sessionStorage
|
|
2338
|
+
const sessionKeysToRemove = [];
|
|
2339
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
2340
|
+
const key = sessionStorage.key(i);
|
|
2341
|
+
if (key && (key.includes('jaak') || key.includes('onnx'))) {
|
|
2342
|
+
sessionKeysToRemove.push(key);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
sessionKeysToRemove.forEach(key => sessionStorage.removeItem(key));
|
|
2346
|
+
}
|
|
2347
|
+
catch (error) {
|
|
2348
|
+
console.warn('Error clearing local storage:', error);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
// Aggressive resource cleanup combining all strategies
|
|
2352
|
+
async aggressiveResourceCleanup() {
|
|
2353
|
+
const freed = [];
|
|
2354
|
+
const errors = [];
|
|
2355
|
+
try {
|
|
2356
|
+
// 1. Release ONNX resources
|
|
2357
|
+
this.releaseOnnxResources();
|
|
2358
|
+
freed.push('ONNX models');
|
|
2359
|
+
// 2. Clear model cache in detection service
|
|
2360
|
+
if (this.detectionService) {
|
|
2361
|
+
await this.detectionService.clearModelCache();
|
|
2362
|
+
freed.push('Model cache');
|
|
2363
|
+
}
|
|
2364
|
+
// 3. Clear browser cache
|
|
2365
|
+
await this.clearBrowserCache();
|
|
2366
|
+
freed.push('Browser cache');
|
|
2367
|
+
// 4. Clear local storage
|
|
2368
|
+
await this.clearLocalStorage();
|
|
2369
|
+
freed.push('Local storage');
|
|
2370
|
+
// 5. Clean DOM references
|
|
2371
|
+
this.cleanupDOMReferences();
|
|
2372
|
+
freed.push('DOM references');
|
|
2373
|
+
// 6. Optimize canvas pools
|
|
2374
|
+
if (this.detectionService) {
|
|
2375
|
+
this.detectionService.optimizeCanvasPool(0);
|
|
2376
|
+
freed.push('Canvas pools');
|
|
2377
|
+
}
|
|
2378
|
+
// 7. Clear component canvas pool
|
|
2379
|
+
this.canvasPool = [];
|
|
2380
|
+
freed.push('Component canvas pool');
|
|
2381
|
+
// 8. Force garbage collection (if available)
|
|
2382
|
+
if (this.debug && window.gc) {
|
|
2383
|
+
window.gc();
|
|
2384
|
+
freed.push('Garbage collection');
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
catch (error) {
|
|
2388
|
+
errors.push(error.message);
|
|
2389
|
+
}
|
|
2390
|
+
return { freed, errors };
|
|
2391
|
+
}
|
|
2392
|
+
// Progressive resource release after front capture
|
|
2393
|
+
async onFrontCaptureComplete() {
|
|
2394
|
+
try {
|
|
2395
|
+
// Release classification model if not needed for back
|
|
2396
|
+
if (this.detectionService && !this.useDocumentClassification) {
|
|
2397
|
+
await this.detectionService.releaseMobileNetResources();
|
|
2398
|
+
}
|
|
2399
|
+
// Optimize canvas pool to minimum needed for back capture
|
|
2400
|
+
if (this.detectionService) {
|
|
2401
|
+
this.detectionService.optimizeCanvasPool(1);
|
|
2402
|
+
}
|
|
2403
|
+
// Reduce component canvas pool
|
|
2404
|
+
while (this.canvasPool.length > 1) {
|
|
2405
|
+
const canvas = this.canvasPool.pop();
|
|
2406
|
+
if (canvas) {
|
|
2407
|
+
const ctx = canvas.getContext('2d');
|
|
2408
|
+
if (ctx) {
|
|
2409
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
if (this.debug) {
|
|
2414
|
+
console.log('🧹 Progressive cleanup after front capture completed');
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
catch (error) {
|
|
2418
|
+
console.warn('Error in progressive cleanup:', error);
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
// Resource reporting for debugging
|
|
2422
|
+
getResourceReportInternal() {
|
|
2423
|
+
const report = {
|
|
2424
|
+
timestamp: new Date().toISOString(),
|
|
2425
|
+
component: {
|
|
2426
|
+
hasVideoStream: !!this.videoStream,
|
|
2427
|
+
hasAnimationFrame: !!this.animationId,
|
|
2428
|
+
canvasPoolSize: this.canvasPool.length,
|
|
2429
|
+
detectionBoxesCount: this.detectionBoxes.length,
|
|
2430
|
+
performanceMonitorActive: !!this.performanceUpdateInterval
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2433
|
+
// Add detection service stats if available
|
|
2434
|
+
if (this.detectionService) {
|
|
2435
|
+
report.detectionService = {
|
|
2436
|
+
isModelLoaded: this.detectionService.isModelLoaded(),
|
|
2437
|
+
canvasPoolStats: this.detectionService.getPoolStats()
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
// Add memory info if available
|
|
2441
|
+
if ('memory' in performance) {
|
|
2442
|
+
const memInfo = performance.memory;
|
|
2443
|
+
report.memory = {
|
|
2444
|
+
usedJSHeapSize: Math.round(memInfo.usedJSHeapSize / 1048576), // MB
|
|
2445
|
+
totalJSHeapSize: Math.round(memInfo.totalJSHeapSize / 1048576), // MB
|
|
2446
|
+
jsHeapSizeLimit: Math.round(memInfo.jsHeapSizeLimit / 1048576) // MB
|
|
2447
|
+
};
|
|
2448
|
+
}
|
|
2449
|
+
return report;
|
|
2450
|
+
}
|
|
2105
2451
|
render() {
|
|
2106
2452
|
const captureState = this.stateManager?.getCaptureState() || {
|
|
2107
2453
|
isVideoActive: false,
|
|
@@ -2111,7 +2457,7 @@ const JaakStamps = class {
|
|
|
2111
2457
|
isCapturing: false
|
|
2112
2458
|
};
|
|
2113
2459
|
const cameraInfo = this.cameraInfoWithAutofocus;
|
|
2114
|
-
return (index.h("div", { key: '
|
|
2460
|
+
return (index.h("div", { key: 'd32941c10b3b7193b2113691dae712a80315b430', class: "detector-container" }, index.h("div", { key: '7704c993cb9e6e3d29edf6b0adfe341dc1cc8e04', class: "video-container" }, index.h("video", { key: '2d0d46f1973c86e91f9fe9ede0aa938326c6071f', 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: '9d62ea5221271a18938a9f80535ec7d960745ddb', 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
2461
|
position: 'absolute',
|
|
2116
2462
|
left: `${box.x}px`,
|
|
2117
2463
|
top: `${box.y}px`,
|
|
@@ -2120,9 +2466,9 @@ const JaakStamps = class {
|
|
|
2120
2466
|
border: '2px solid #32406C',
|
|
2121
2467
|
pointerEvents: 'none',
|
|
2122
2468
|
boxSizing: 'border-box'
|
|
2123
|
-
} })))), this.isMaskReady && (index.h("div", { key: '
|
|
2469
|
+
} })))), this.isMaskReady && (index.h("div", { key: '38ebb5be5c55a184644411f06fe20221bdbdf693', class: "overlay-mask" }, index.h("div", { key: '6200c59e7a1212267e7335e1495cf9d9db2bca62', class: "card-outline" }, index.h("div", { key: 'c5321d2b5c09734cbca69fc8bb7849eef465a0ce', class: "side side-top" }), index.h("div", { key: 'd73b5afcb6781307460b8827c491673ebbfc40ef', class: "side side-right" }), index.h("div", { key: '787dfa0792c5a00f8a83feaa423ca22092e0a8bf', class: "side side-bottom" }), index.h("div", { key: '50736a6b9c7c8847d1dabaa22464e26f284da11e', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: 'ec8cc90cd589e4f713becb57b6787ce4f57318d4', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: 'd9d391c02855ac754607cbf77c119153d1f6e56f', class: "back-capture-section" }, index.h("div", { key: 'a3ebe694f74958b7910fc355a66cbf1394fb5a2a', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (index.h("button", { key: 'bc6baf4e00199dfdd50bbfbf8abbcede0fb719e9', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (index.h("span", { key: '3a56f9132fce454c792818925c8bed5557e69eed', class: "button-spinner" })), "Capturar Reverso")), index.h("button", { key: '2fe7e44bc91b73581d6333b7c404ec018a390372', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (index.h("span", { key: 'ebb2d40cbcb646c9bd70ebe278b0643f9335779b', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
2124
2470
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
2125
|
-
: 'Saltar reverso')))), captureState.isVideoActive && (index.h("div", { key: '
|
|
2471
|
+
: 'Saltar reverso')))), captureState.isVideoActive && (index.h("div", { key: '4373e4f9ab73d93ce32ae52fbdc14f7bcb69e68e', class: "camera-controls" }, index.h("button", { key: '5d2afe5b71b02d379fe2ec82bd57a86f614fbea6', 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: '4fe5e10260a1299a0dc8517646bed8134fe38d9a', class: "camera-selector-dropdown" }, index.h("div", { key: 'b041acd0b0292864d20ad256301e7be714854424', class: "camera-selector-header" }, index.h("span", { key: '2769170dc9ce382b420a719cad6f9bb1f5e4420f' }, "Seleccionar C\u00E1mara"), index.h("button", { key: '0c54f73bb29041ba88e1838c5b1c8a54eafdc67d', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: 'd72ae015c5e89cc3396c14406591308eda4d4c4a', 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: '5933bb5c8345debf2eb78fc2cd3900cb09a29335', class: "device-info" }, index.h("small", { key: 'a126d886feaede4f9657abee9b45d1eff1910e25' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '62ab98c6afa629583ff13f1792422ef1c5012883', class: "manual-capture-section" }, index.h("button", { key: '93a42f7b0626a63ab795652eb9eb81657f4fec13', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (index.h("span", { key: '77b5aaf62edab4fe8eb68f37083121d9f8493797', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (index.h("div", { key: '399242238fd4a2a4985af0f30faed504d73a0adc', class: "capture-animation" })), captureState.showFlipAnimation && (index.h("div", { key: '06add02bc244511dc0dc82bf206f41d9ce3cdcd3', class: "flip-animation" }, index.h("div", { key: 'e3949d8e41bc496bf503d1dcd34df53d2bf876aa', class: "id-card-icon" }), index.h("div", { key: 'e8ebdd9d29e0b33dcfc506b40e67e0d4af8cb0bb', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (index.h("div", { key: 'f72f6fc4d42c7b10d98c8aff7541596afe131883', class: "success-animation" }, index.h("div", { key: '8d0d422b37caa97f695f62c0094c52c3c12c7bda', class: "check-icon" }), index.h("div", { key: 'd9d673dbfd2af4a1b8ca1a775e5b318326d70446', class: "success-text" }, "\u00A1Proceso completado!"))), index.h("div", { key: 'fdfa1239ddea40bbbabf032542a7160fae3f3c96', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (index.h("div", { key: '34bb9d5b83e4383c4f640ccf11189484514e4efd', class: "status-spinner" })), index.h("div", { key: '131bcd252fc7e1f6eb27332d64fd249b5889aa9d', class: "status-content" }, index.h("div", { key: '911b7324456e84842fa3c14c9a6fd3ba475804cf', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (index.h("div", { key: 'e938b9294ee3c6125b133bf737caa4a33a016872', class: "status-description" }, this.currentStatus.description)))), this.debug && (index.h("div", { key: 'cab385a12f6ccf8579d2d073c1ed987bf815efeb', class: "performance-monitor" }, index.h("div", { key: '903f8a7005a98c4b99eb7d4c91662911cee8beae', class: "performance-expanded" }, index.h("div", { key: 'f1ba17573d53442a23212e2c8fdd132bd3ddf4f0', class: "metrics-row" }, index.h("div", { key: '71357107dcc19749e5af2d5668df535db65cb986', class: "metric-compact" }, index.h("span", { key: '9ad542fb9445340c669c3d18fc293e2364fc5dcc', class: "metric-label" }, "FPS"), index.h("span", { key: 'ab0fb401df5dc792c0a641a574851aec48093862', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), index.h("div", { key: 'a650ab2a1af27829d4a63a264f00907dfba363c6', class: "metric-compact" }, index.h("span", { key: '56e195b0fd468f2a457a66b2342ed6a80fbef0a0', class: "metric-label" }, "MEM"), index.h("span", { key: '265445131d4492c6e2f7571d367f52c93e00b711', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), index.h("div", { key: '56023a82a1978c7c2f8f08edac729f39efcafa63', class: "metrics-row" }, index.h("div", { key: '6f1ceb668fc246ec11bfeee3f14c745f21d4986e', class: "metric-compact" }, index.h("span", { key: '92efba3ee31c5b9a382f1e205584950217482722', class: "metric-label" }, "INF"), index.h("span", { key: 'fc2baec34fd2f0696a216c487753b94b8bacaf7e', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), index.h("div", { key: '683eeb875e96c94cf36c403385c2764239fb5a4d', class: "metric-compact" }, index.h("span", { key: '33ba3f357868f7feaaeefab35e3ecc35d3b5b726', class: "metric-label" }, "FRAME"), index.h("span", { key: 'f496be674b0328556d27a9a9b2f24ffa50ff76ea', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), index.h("div", { key: '521f275481d35c2132dbcf138e4bbda84a0b97b2', class: "metrics-row" }, index.h("div", { key: '17acc0238696816077b80b66aee317ae9ab20e75', class: "metric-compact" }, index.h("span", { key: 'cac1a4ee661de14739623a1a6f0149ba72fa918c', class: "metric-label" }, "DET"), index.h("span", { key: '70071c404c5e50001d813f1f3e0d1002776ed887', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), index.h("div", { key: '26a00ad1c95e6c2b8feaafc36103fb7fb869039f', class: "metric-compact" }, index.h("span", { key: 'a1c7fb80b6795f10111c1ed403de933aadcbe450', class: "metric-label" }, "RATE"), index.h("span", { key: '49471b7bfa2d6ca6357a39bf611738fd6344764a', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), index.h("div", { key: '3870b7399d2d5068e7f59177225476b00bef7fc7', class: "watermark" }, index.h("img", { key: '5030e9f14eebd4546207ced129d7d00d85abb189', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
2126
2472
|
}
|
|
2127
2473
|
// Utility methods
|
|
2128
2474
|
updateDetectionBoxes(boxes) {
|
|
@@ -2346,6 +2692,8 @@ const JaakStamps = class {
|
|
|
2346
2692
|
showFlipAnimation: true
|
|
2347
2693
|
});
|
|
2348
2694
|
this.triggerRerender();
|
|
2695
|
+
// Progressive resource cleanup after front capture
|
|
2696
|
+
await this.onFrontCaptureComplete();
|
|
2349
2697
|
setTimeout(() => {
|
|
2350
2698
|
this.stateManager.updateCaptureState({
|
|
2351
2699
|
showFlipAnimation: false,
|
|
@@ -2421,6 +2769,8 @@ const JaakStamps = class {
|
|
|
2421
2769
|
isDetectionPaused: true,
|
|
2422
2770
|
showFlipAnimation: true
|
|
2423
2771
|
});
|
|
2772
|
+
// Progressive resource cleanup after front capture
|
|
2773
|
+
await this.onFrontCaptureComplete();
|
|
2424
2774
|
setTimeout(() => {
|
|
2425
2775
|
this.stateManager.updateCaptureState({
|
|
2426
2776
|
showFlipAnimation: false,
|
|
@@ -2598,6 +2948,16 @@ const JaakStamps = class {
|
|
|
2598
2948
|
setTimeout(() => {
|
|
2599
2949
|
this.stateManager.updateCaptureState({ showSuccessAnimation: false });
|
|
2600
2950
|
}, 3000);
|
|
2951
|
+
// Aggressive cleanup after process completion with delay
|
|
2952
|
+
setTimeout(async () => {
|
|
2953
|
+
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
2954
|
+
if (this.debug) {
|
|
2955
|
+
console.log('🧹 Post-completion cleanup:', cleanupResult.freed);
|
|
2956
|
+
if (cleanupResult.errors.length > 0) {
|
|
2957
|
+
console.warn('⚠️ Cleanup errors:', cleanupResult.errors);
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
}, 1000); // Wait 1 second after completion
|
|
2601
2961
|
}
|
|
2602
2962
|
stopDetection() {
|
|
2603
2963
|
if (this.animationId) {
|