@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.
Files changed (27) hide show
  1. package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
  2. package/dist/cjs/jaak-stamps.cjs.entry.js +377 -17
  3. package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
  4. package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/collection/components/my-component/my-component.js +265 -12
  7. package/dist/collection/components/my-component/my-component.js.map +1 -1
  8. package/dist/collection/services/DetectionService.js +148 -7
  9. package/dist/collection/services/DetectionService.js.map +1 -1
  10. package/dist/collection/services/interfaces/IDetectionService.js.map +1 -1
  11. package/dist/components/jaak-stamps.js +380 -18
  12. package/dist/components/jaak-stamps.js.map +1 -1
  13. package/dist/esm/jaak-stamps-webcomponent.js +1 -1
  14. package/dist/esm/jaak-stamps.entry.js +377 -17
  15. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  16. package/dist/esm/loader.js +1 -1
  17. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  18. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  19. package/dist/jaak-stamps-webcomponent/p-c115e01a.entry.js +2 -0
  20. package/dist/jaak-stamps-webcomponent/p-c115e01a.entry.js.map +1 -0
  21. package/dist/types/components/my-component/my-component.d.ts +27 -0
  22. package/dist/types/components.d.ts +3 -1
  23. package/dist/types/services/DetectionService.d.ts +9 -1
  24. package/dist/types/services/interfaces/IDetectionService.d.ts +6 -0
  25. package/package.json +1 -1
  26. package/dist/jaak-stamps-webcomponent/p-be6b6ca3.entry.js +0 -2
  27. package/dist/jaak-stamps-webcomponent/p-be6b6ca3.entry.js.map +0 -1
@@ -5,7 +5,7 @@ var index = require('./index-BfhtOB0D.js');
5
5
  const defineCustomElements = async (win, options) => {
6
6
  if (typeof window === 'undefined') return undefined;
7
7
  await index.globalScripts();
8
- 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);
8
+ 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);
9
9
  };
10
10
 
11
11
  exports.setNonce = index.setNonce;
@@ -411,7 +411,13 @@ export class JaakStamps {
411
411
  }
412
412
  try {
413
413
  this.exitSession();
414
- return { success: true };
414
+ // Aggressive cleanup when stopping capture
415
+ const cleanupResult = await this.aggressiveResourceCleanup();
416
+ return {
417
+ success: true,
418
+ resourcesFreed: cleanupResult.freed,
419
+ cleanupErrors: cleanupResult.errors
420
+ };
415
421
  }
416
422
  catch (error) {
417
423
  return { success: false, error: error.message };
@@ -636,6 +642,29 @@ export class JaakStamps {
636
642
  // Always allow getting capture delay, even during initialization
637
643
  return this.captureDelay;
638
644
  }
645
+ async getResourceReport() {
646
+ // Public method to get resource usage report
647
+ return this.getResourceReportInternal();
648
+ }
649
+ async forceResourceCleanup() {
650
+ // Public method to force aggressive resource cleanup
651
+ const readyCheck = this.isComponentReady();
652
+ if (!readyCheck.ready) {
653
+ return { success: false, error: readyCheck.message };
654
+ }
655
+ try {
656
+ const cleanupResult = await this.aggressiveResourceCleanup();
657
+ return {
658
+ success: true,
659
+ resourcesFreed: cleanupResult.freed,
660
+ errors: cleanupResult.errors,
661
+ timestamp: new Date().toISOString()
662
+ };
663
+ }
664
+ catch (error) {
665
+ return { success: false, error: error.message };
666
+ }
667
+ }
639
668
  // Memory and device capability detection
640
669
  checkDeviceCapabilities() {
641
670
  const deviceMemory = navigator.deviceMemory;
@@ -659,13 +688,18 @@ export class JaakStamps {
659
688
  memoryMB: null
660
689
  };
661
690
  }
662
- // Safari tiene limitaciones conocidas con WASM
691
+ // Safari moderno soporta WASM, solo bloquearlo en versiones muy antiguas
663
692
  if (isSafari) {
664
- return {
665
- canUseML: false,
666
- reason: 'Safari detectado. Modo manual activado por compatibilidad optimizada con este navegador.',
667
- memoryMB: null
668
- };
693
+ const safariMatch = navigator.userAgent.match(/Version\/(\d+)/);
694
+ const safariVersion = safariMatch ? parseInt(safariMatch[1]) : 0;
695
+ // Solo bloquear Safari muy antiguo (< version 14)
696
+ if (safariVersion > 0 && safariVersion < 14) {
697
+ return {
698
+ canUseML: false,
699
+ reason: 'Safari antiguo detectado. Modo manual activado por compatibilidad.',
700
+ memoryMB: null
701
+ };
702
+ }
669
703
  }
670
704
  return {
671
705
  canUseML: true,
@@ -971,11 +1005,182 @@ export class JaakStamps {
971
1005
  }
972
1006
  // Clear canvas pool
973
1007
  this.canvasPool = [];
1008
+ // Clean DOM references and memory leaks
1009
+ this.cleanupDOMReferences();
974
1010
  this.detectionBoxes = [];
975
1011
  this.alignmentStartTime = undefined;
976
1012
  this.hasDocumentDetected = false;
977
1013
  this.serviceContainer?.cleanup();
978
1014
  }
1015
+ // AGGRESSIVE RESOURCE CLEANUP METHODS
1016
+ // Clean DOM references and memory leaks
1017
+ cleanupDOMReferences() {
1018
+ // Remove event listeners that could maintain references
1019
+ if (this.videoRef) {
1020
+ // Remove common event listeners (if any were added)
1021
+ this.videoRef.srcObject = null;
1022
+ this.videoRef = undefined;
1023
+ }
1024
+ // Clean detection container
1025
+ if (this.detectionContainer) {
1026
+ this.detectionContainer = undefined;
1027
+ }
1028
+ // Clean references to images in the DOM and revoke blob URLs
1029
+ const images = this.el.shadowRoot?.querySelectorAll('img');
1030
+ images?.forEach(img => {
1031
+ if (img.src.startsWith('blob:')) {
1032
+ URL.revokeObjectURL(img.src);
1033
+ }
1034
+ img.src = '';
1035
+ });
1036
+ }
1037
+ // Clear browser cache and storage
1038
+ async clearBrowserCache() {
1039
+ try {
1040
+ // 1. Clear Service Worker cache if exists
1041
+ if ('serviceWorker' in navigator) {
1042
+ const registrations = await navigator.serviceWorker.getRegistrations();
1043
+ await Promise.all(registrations.map(registration => registration.unregister()));
1044
+ }
1045
+ // 2. Clear Cache API specific to the component
1046
+ const cacheNames = await caches.keys();
1047
+ const componentCaches = cacheNames.filter(name => name.includes('jaak') || name.includes('stamps'));
1048
+ await Promise.all(componentCaches.map(cacheName => caches.delete(cacheName)));
1049
+ // 3. Force garbage collection if available (only in dev)
1050
+ if (this.debug && window.gc) {
1051
+ window.gc();
1052
+ }
1053
+ }
1054
+ catch (error) {
1055
+ console.warn('Error clearing browser cache:', error);
1056
+ }
1057
+ }
1058
+ // Clear local storage specific to the component
1059
+ async clearLocalStorage() {
1060
+ try {
1061
+ // Clear localStorage specific to the component
1062
+ const keysToRemove = [];
1063
+ for (let i = 0; i < localStorage.length; i++) {
1064
+ const key = localStorage.key(i);
1065
+ if (key && (key.includes('jaak') || key.includes('onnx') || key.includes('stamps'))) {
1066
+ keysToRemove.push(key);
1067
+ }
1068
+ }
1069
+ keysToRemove.forEach(key => localStorage.removeItem(key));
1070
+ // Clear sessionStorage
1071
+ const sessionKeysToRemove = [];
1072
+ for (let i = 0; i < sessionStorage.length; i++) {
1073
+ const key = sessionStorage.key(i);
1074
+ if (key && (key.includes('jaak') || key.includes('onnx'))) {
1075
+ sessionKeysToRemove.push(key);
1076
+ }
1077
+ }
1078
+ sessionKeysToRemove.forEach(key => sessionStorage.removeItem(key));
1079
+ }
1080
+ catch (error) {
1081
+ console.warn('Error clearing local storage:', error);
1082
+ }
1083
+ }
1084
+ // Aggressive resource cleanup combining all strategies
1085
+ async aggressiveResourceCleanup() {
1086
+ const freed = [];
1087
+ const errors = [];
1088
+ try {
1089
+ // 1. Release ONNX resources
1090
+ this.releaseOnnxResources();
1091
+ freed.push('ONNX models');
1092
+ // 2. Clear model cache in detection service
1093
+ if (this.detectionService) {
1094
+ await this.detectionService.clearModelCache();
1095
+ freed.push('Model cache');
1096
+ }
1097
+ // 3. Clear browser cache
1098
+ await this.clearBrowserCache();
1099
+ freed.push('Browser cache');
1100
+ // 4. Clear local storage
1101
+ await this.clearLocalStorage();
1102
+ freed.push('Local storage');
1103
+ // 5. Clean DOM references
1104
+ this.cleanupDOMReferences();
1105
+ freed.push('DOM references');
1106
+ // 6. Optimize canvas pools
1107
+ if (this.detectionService) {
1108
+ this.detectionService.optimizeCanvasPool(0);
1109
+ freed.push('Canvas pools');
1110
+ }
1111
+ // 7. Clear component canvas pool
1112
+ this.canvasPool = [];
1113
+ freed.push('Component canvas pool');
1114
+ // 8. Force garbage collection (if available)
1115
+ if (this.debug && window.gc) {
1116
+ window.gc();
1117
+ freed.push('Garbage collection');
1118
+ }
1119
+ }
1120
+ catch (error) {
1121
+ errors.push(error.message);
1122
+ }
1123
+ return { freed, errors };
1124
+ }
1125
+ // Progressive resource release after front capture
1126
+ async onFrontCaptureComplete() {
1127
+ try {
1128
+ // Release classification model if not needed for back
1129
+ if (this.detectionService && !this.useDocumentClassification) {
1130
+ await this.detectionService.releaseMobileNetResources();
1131
+ }
1132
+ // Optimize canvas pool to minimum needed for back capture
1133
+ if (this.detectionService) {
1134
+ this.detectionService.optimizeCanvasPool(1);
1135
+ }
1136
+ // Reduce component canvas pool
1137
+ while (this.canvasPool.length > 1) {
1138
+ const canvas = this.canvasPool.pop();
1139
+ if (canvas) {
1140
+ const ctx = canvas.getContext('2d');
1141
+ if (ctx) {
1142
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1143
+ }
1144
+ }
1145
+ }
1146
+ if (this.debug) {
1147
+ console.log('🧹 Progressive cleanup after front capture completed');
1148
+ }
1149
+ }
1150
+ catch (error) {
1151
+ console.warn('Error in progressive cleanup:', error);
1152
+ }
1153
+ }
1154
+ // Resource reporting for debugging
1155
+ getResourceReportInternal() {
1156
+ const report = {
1157
+ timestamp: new Date().toISOString(),
1158
+ component: {
1159
+ hasVideoStream: !!this.videoStream,
1160
+ hasAnimationFrame: !!this.animationId,
1161
+ canvasPoolSize: this.canvasPool.length,
1162
+ detectionBoxesCount: this.detectionBoxes.length,
1163
+ performanceMonitorActive: !!this.performanceUpdateInterval
1164
+ }
1165
+ };
1166
+ // Add detection service stats if available
1167
+ if (this.detectionService) {
1168
+ report.detectionService = {
1169
+ isModelLoaded: this.detectionService.isModelLoaded(),
1170
+ canvasPoolStats: this.detectionService.getPoolStats()
1171
+ };
1172
+ }
1173
+ // Add memory info if available
1174
+ if ('memory' in performance) {
1175
+ const memInfo = performance.memory;
1176
+ report.memory = {
1177
+ usedJSHeapSize: Math.round(memInfo.usedJSHeapSize / 1048576), // MB
1178
+ totalJSHeapSize: Math.round(memInfo.totalJSHeapSize / 1048576), // MB
1179
+ jsHeapSizeLimit: Math.round(memInfo.jsHeapSizeLimit / 1048576) // MB
1180
+ };
1181
+ }
1182
+ return report;
1183
+ }
979
1184
  render() {
980
1185
  const captureState = this.stateManager?.getCaptureState() || {
981
1186
  isVideoActive: false,
@@ -986,7 +1191,7 @@ export class JaakStamps {
986
1191
  isCapturing: false
987
1192
  };
988
1193
  const cameraInfo = this.cameraInfoWithAutofocus;
989
- return (h("div", { key: '51cc6d0f873691852f938940b131732252b970c1', class: "detector-container" }, h("div", { key: '009480499c0a2833bf9f6bd28003142ca3dd3750', class: "video-container" }, h("video", { key: '88a99a1a90c8d817e40549a255324b2da342797f', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '7ede15ccd6f67b3081d907777de7b0d1a1b4cfd6', 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: {
1194
+ return (h("div", { key: 'd32941c10b3b7193b2113691dae712a80315b430', class: "detector-container" }, h("div", { key: '7704c993cb9e6e3d29edf6b0adfe341dc1cc8e04', class: "video-container" }, 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' } }), h("div", { key: '9d62ea5221271a18938a9f80535ec7d960745ddb', 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: {
990
1195
  position: 'absolute',
991
1196
  left: `${box.x}px`,
992
1197
  top: `${box.y}px`,
@@ -995,9 +1200,9 @@ export class JaakStamps {
995
1200
  border: '2px solid #32406C',
996
1201
  pointerEvents: 'none',
997
1202
  boxSizing: 'border-box'
998
- } })))), this.isMaskReady && (h("div", { key: '63beb4330ab171a5e63237b1d83dcace61422018', class: "overlay-mask" }, h("div", { key: '93cfac6898de6ece6c7dd8f02ba2b79ce6591755', class: "card-outline" }, h("div", { key: '3087d3c51f42ea8d720cea4170dcb68e8bb385e1', class: "side side-top" }), h("div", { key: '992e311b895da161d3b5c49dde346ae378139641', class: "side side-right" }), h("div", { key: '5fb3d7b704f08177b2b9c6e4fbaf685f251a3e53', class: "side side-bottom" }), h("div", { key: '76f6e2cb856632c58809ccdabdf3f7d9a588c9da', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '142dfc187e07100278e16b19018e4b5fb96d964a', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'bba078ce4c9585631b6a8154f878464b1f779c8b', class: "back-capture-section" }, h("div", { key: 'ec94c9ba2cb373a539f0e6cc3eb356294cebf3ea', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (h("button", { key: '30561e1fc88090c6bb4d0cd7909a33a9525b0ac2', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: '4b3e3356507c7f654705425e201d0303d727db72', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: '1abfada41312658eab4966d8fcdb711a6c4762e6', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: '3cab3ce751cb38174b6d061170a66ac654a90a38', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
1203
+ } })))), this.isMaskReady && (h("div", { key: '38ebb5be5c55a184644411f06fe20221bdbdf693', class: "overlay-mask" }, h("div", { key: '6200c59e7a1212267e7335e1495cf9d9db2bca62', class: "card-outline" }, h("div", { key: 'c5321d2b5c09734cbca69fc8bb7849eef465a0ce', class: "side side-top" }), h("div", { key: 'd73b5afcb6781307460b8827c491673ebbfc40ef', class: "side side-right" }), h("div", { key: '787dfa0792c5a00f8a83feaa423ca22092e0a8bf', class: "side side-bottom" }), h("div", { key: '50736a6b9c7c8847d1dabaa22464e26f284da11e', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'ec8cc90cd589e4f713becb57b6787ce4f57318d4', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'd9d391c02855ac754607cbf77c119153d1f6e56f', class: "back-capture-section" }, h("div", { key: 'a3ebe694f74958b7910fc355a66cbf1394fb5a2a', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (h("button", { key: 'bc6baf4e00199dfdd50bbfbf8abbcede0fb719e9', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: '3a56f9132fce454c792818925c8bed5557e69eed', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: '2fe7e44bc91b73581d6333b7c404ec018a390372', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: 'ebb2d40cbcb646c9bd70ebe278b0643f9335779b', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
999
1204
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
1000
- : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'b773fe579cc039fd90ff5e61976752bcf631d16e', class: "camera-controls" }, h("button", { key: '8535463b13cc02e0030d14c1d57390e560572b4f', 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: 'c4b159cf28c24ba2ec0a658bf6f1296c6aba47e3', class: "camera-selector-dropdown" }, h("div", { key: '033273241b77cedc414dee00b70926c3a659910a', class: "camera-selector-header" }, h("span", { key: '83fb4c7ae2387b85134a3ca0c5a156beaf73e481' }, "Seleccionar C\u00E1mara"), h("button", { key: 'f65b6e994603e616af8d69df198c3d8471125695', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'fd90d4c4db1e910c5d05b9ce19a252967c71ad1f', 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: '49faa944b3c0c052e18f48e9dda2758cd2c0ae6b', class: "device-info" }, h("small", { key: '15e95207aef60a78b2912a6c00a1b61ab760b72d' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'a67a3795837adcd307fa3666c338376cecae16d0', class: "manual-capture-section" }, h("button", { key: '7f21ccd9b622d97bfce80962038de8be7cd6e33d', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: 'a4b5e865f373cc0e7787098eedd2d4df413d310f', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '0b02e81963136dfda62a8980bc5a1f5662509fb8', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '5eb16e12a7ce16720ca7bdd11aefd902ba772e24', class: "flip-animation" }, h("div", { key: '4c6f3ed33c5080e9e18d17c4594cf394250c3517', class: "id-card-icon" }), h("div", { key: 'd848d7a98540988a8f491bcf8f6f6ad9c5dccfd4', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '94863e6d0c404188d548db428af918274d882652', class: "success-animation" }, h("div", { key: 'a90b777e7b7eb533fabec2c15f79a4bd18778f0b', class: "check-icon" }), h("div", { key: '2152e5a3756f58f996c520b58cb1b6cca1763e94', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: 'ae18e06bf291429a74d1b6c8a0c4d37d473f7b30', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: 'b6b03ed285868d7d3350a2a73e59cb1d65a47401', class: "status-spinner" })), h("div", { key: 'dc85b49dd1cfcddf7300fb3f327d2e1de8ab9bff', class: "status-content" }, h("div", { key: '82461ed0c6d70a0c95ae6b7144ff1d8b8371c6fc', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '24aa77ec92ca5a48d1bed4cf98ae591bdbff74f0', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '3b56a0b305ca07407c09a73624a4040cf6625b56', class: "performance-monitor" }, h("div", { key: '52f975da1502f6b08a0b2c828bfd0be8d10e36f8', class: "performance-expanded" }, h("div", { key: 'ea2f444255add0415c7f88cdea2733e1244b17bd', class: "metrics-row" }, h("div", { key: 'e3c2370f1a900594940cb321d5b1661690018025', class: "metric-compact" }, h("span", { key: '25c2f402c9b69f21736c494420e7a396673e9893', class: "metric-label" }, "FPS"), h("span", { key: '505de593f7927544cd874332127e84c72131da72', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '5cb1ff4bc1ac33988404e59dac3d01d5a55893b9', class: "metric-compact" }, h("span", { key: 'ce701538abbfe797b288520ab4c277bf7a1d6a05', class: "metric-label" }, "MEM"), h("span", { key: 'efc383e993ad72c20cbfc630e20b2bf9a617a02d', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: 'b6b6b832740c44d4f78cddf99ba1d139dc3d4739', class: "metrics-row" }, h("div", { key: '3a8903a05aed8adb1626f93bee6ba258fbea9a7d', class: "metric-compact" }, h("span", { key: 'aabc56057156217b3be771ab3b1e92590fa886c5', class: "metric-label" }, "INF"), h("span", { key: '1080df34860cc891cda94c5aacc754cbde18a138', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: 'ac152093d4c0cc25862aac638fc4693dd0c51a69', class: "metric-compact" }, h("span", { key: 'b23712dbc0b9b8e9cb6e2c659e5af3844ec0ebe7', class: "metric-label" }, "FRAME"), h("span", { key: '461f88519b339fbad6444f009312f72efbc6e450', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '8f99afe2c94f37fd3a6feb082cc9aa96f54fc5e1', class: "metrics-row" }, h("div", { key: 'da7ac0fd842f38bbdcac01550764f9fe1bd666a2', class: "metric-compact" }, h("span", { key: 'a5dcd9c8753ba42038f869efa50ae9602cdf6256', class: "metric-label" }, "DET"), h("span", { key: 'e98b91ea98a41637baea1fcfda0bb590cf5d24a0', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '3ac54161a712d305a098eae0a3b5d21a731c6019', class: "metric-compact" }, h("span", { key: '3c8cc465433be3b490887457a6c3967dbbda6a7d', class: "metric-label" }, "RATE"), h("span", { key: 'f2e083118fe107c7dc2a675e3f661466cea6d479', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '10b6c32b0460e194d680c63b30a078c51a26e9c9', class: "watermark" }, h("img", { key: 'f5954a0ba3f90a642e0d5c6c14d5b549cd193018', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
1205
+ : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '4373e4f9ab73d93ce32ae52fbdc14f7bcb69e68e', class: "camera-controls" }, 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 ? (h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (h("div", { key: '4fe5e10260a1299a0dc8517646bed8134fe38d9a', class: "camera-selector-dropdown" }, h("div", { key: 'b041acd0b0292864d20ad256301e7be714854424', class: "camera-selector-header" }, h("span", { key: '2769170dc9ce382b420a719cad6f9bb1f5e4420f' }, "Seleccionar C\u00E1mara"), h("button", { key: '0c54f73bb29041ba88e1838c5b1c8a54eafdc67d', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'd72ae015c5e89cc3396c14406591308eda4d4c4a', 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: '5933bb5c8345debf2eb78fc2cd3900cb09a29335', class: "device-info" }, h("small", { key: 'a126d886feaede4f9657abee9b45d1eff1910e25' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '62ab98c6afa629583ff13f1792422ef1c5012883', class: "manual-capture-section" }, h("button", { key: '93a42f7b0626a63ab795652eb9eb81657f4fec13', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: '77b5aaf62edab4fe8eb68f37083121d9f8493797', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '399242238fd4a2a4985af0f30faed504d73a0adc', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '06add02bc244511dc0dc82bf206f41d9ce3cdcd3', class: "flip-animation" }, h("div", { key: 'e3949d8e41bc496bf503d1dcd34df53d2bf876aa', class: "id-card-icon" }), h("div", { key: 'e8ebdd9d29e0b33dcfc506b40e67e0d4af8cb0bb', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'f72f6fc4d42c7b10d98c8aff7541596afe131883', class: "success-animation" }, h("div", { key: '8d0d422b37caa97f695f62c0094c52c3c12c7bda', class: "check-icon" }), h("div", { key: 'd9d673dbfd2af4a1b8ca1a775e5b318326d70446', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: 'fdfa1239ddea40bbbabf032542a7160fae3f3c96', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '34bb9d5b83e4383c4f640ccf11189484514e4efd', class: "status-spinner" })), h("div", { key: '131bcd252fc7e1f6eb27332d64fd249b5889aa9d', class: "status-content" }, h("div", { key: '911b7324456e84842fa3c14c9a6fd3ba475804cf', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'e938b9294ee3c6125b133bf737caa4a33a016872', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: 'cab385a12f6ccf8579d2d073c1ed987bf815efeb', class: "performance-monitor" }, h("div", { key: '903f8a7005a98c4b99eb7d4c91662911cee8beae', class: "performance-expanded" }, h("div", { key: 'f1ba17573d53442a23212e2c8fdd132bd3ddf4f0', class: "metrics-row" }, h("div", { key: '71357107dcc19749e5af2d5668df535db65cb986', class: "metric-compact" }, h("span", { key: '9ad542fb9445340c669c3d18fc293e2364fc5dcc', class: "metric-label" }, "FPS"), h("span", { key: 'ab0fb401df5dc792c0a641a574851aec48093862', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: 'a650ab2a1af27829d4a63a264f00907dfba363c6', class: "metric-compact" }, h("span", { key: '56e195b0fd468f2a457a66b2342ed6a80fbef0a0', class: "metric-label" }, "MEM"), h("span", { key: '265445131d4492c6e2f7571d367f52c93e00b711', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '56023a82a1978c7c2f8f08edac729f39efcafa63', class: "metrics-row" }, h("div", { key: '6f1ceb668fc246ec11bfeee3f14c745f21d4986e', class: "metric-compact" }, h("span", { key: '92efba3ee31c5b9a382f1e205584950217482722', class: "metric-label" }, "INF"), h("span", { key: 'fc2baec34fd2f0696a216c487753b94b8bacaf7e', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '683eeb875e96c94cf36c403385c2764239fb5a4d', class: "metric-compact" }, h("span", { key: '33ba3f357868f7feaaeefab35e3ecc35d3b5b726', class: "metric-label" }, "FRAME"), h("span", { key: 'f496be674b0328556d27a9a9b2f24ffa50ff76ea', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '521f275481d35c2132dbcf138e4bbda84a0b97b2', class: "metrics-row" }, h("div", { key: '17acc0238696816077b80b66aee317ae9ab20e75', class: "metric-compact" }, h("span", { key: 'cac1a4ee661de14739623a1a6f0149ba72fa918c', class: "metric-label" }, "DET"), h("span", { key: '70071c404c5e50001d813f1f3e0d1002776ed887', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '26a00ad1c95e6c2b8feaafc36103fb7fb869039f', class: "metric-compact" }, h("span", { key: 'a1c7fb80b6795f10111c1ed403de933aadcbe450', class: "metric-label" }, "RATE"), h("span", { key: '49471b7bfa2d6ca6357a39bf611738fd6344764a', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '3870b7399d2d5068e7f59177225476b00bef7fc7', class: "watermark" }, h("img", { key: '5030e9f14eebd4546207ced129d7d00d85abb189', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
1001
1206
  }
1002
1207
  // Utility methods
1003
1208
  updateDetectionBoxes(boxes) {
@@ -1221,6 +1426,8 @@ export class JaakStamps {
1221
1426
  showFlipAnimation: true
1222
1427
  });
1223
1428
  this.triggerRerender();
1429
+ // Progressive resource cleanup after front capture
1430
+ await this.onFrontCaptureComplete();
1224
1431
  setTimeout(() => {
1225
1432
  this.stateManager.updateCaptureState({
1226
1433
  showFlipAnimation: false,
@@ -1296,6 +1503,8 @@ export class JaakStamps {
1296
1503
  isDetectionPaused: true,
1297
1504
  showFlipAnimation: true
1298
1505
  });
1506
+ // Progressive resource cleanup after front capture
1507
+ await this.onFrontCaptureComplete();
1299
1508
  setTimeout(() => {
1300
1509
  this.stateManager.updateCaptureState({
1301
1510
  showFlipAnimation: false,
@@ -1473,6 +1682,16 @@ export class JaakStamps {
1473
1682
  setTimeout(() => {
1474
1683
  this.stateManager.updateCaptureState({ showSuccessAnimation: false });
1475
1684
  }, 3000);
1685
+ // Aggressive cleanup after process completion with delay
1686
+ setTimeout(async () => {
1687
+ const cleanupResult = await this.aggressiveResourceCleanup();
1688
+ if (this.debug) {
1689
+ console.log('🧹 Post-completion cleanup:', cleanupResult.freed);
1690
+ if (cleanupResult.errors.length > 0) {
1691
+ console.warn('⚠️ Cleanup errors:', cleanupResult.errors);
1692
+ }
1693
+ }
1694
+ }, 1000); // Wait 1 second after completion
1476
1695
  }
1477
1696
  stopDetection() {
1478
1697
  if (this.animationId) {
@@ -2054,7 +2273,7 @@ export class JaakStamps {
2054
2273
  },
2055
2274
  "stopCapture": {
2056
2275
  "complexType": {
2057
- "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
2276
+ "signature": "() => Promise<{ success: boolean; resourcesFreed: string[]; cleanupErrors: string[]; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; cleanupErrors?: undefined; }>",
2058
2277
  "parameters": [],
2059
2278
  "references": {
2060
2279
  "Promise": {
@@ -2062,7 +2281,7 @@ export class JaakStamps {
2062
2281
  "id": "global::Promise"
2063
2282
  }
2064
2283
  },
2065
- "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
2284
+ "return": "Promise<{ success: boolean; resourcesFreed: string[]; cleanupErrors: string[]; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; cleanupErrors?: undefined; }>"
2066
2285
  },
2067
2286
  "docs": {
2068
2287
  "text": "",
@@ -2222,6 +2441,40 @@ export class JaakStamps {
2222
2441
  "text": "",
2223
2442
  "tags": []
2224
2443
  }
2444
+ },
2445
+ "getResourceReport": {
2446
+ "complexType": {
2447
+ "signature": "() => Promise<any>",
2448
+ "parameters": [],
2449
+ "references": {
2450
+ "Promise": {
2451
+ "location": "global",
2452
+ "id": "global::Promise"
2453
+ }
2454
+ },
2455
+ "return": "Promise<any>"
2456
+ },
2457
+ "docs": {
2458
+ "text": "",
2459
+ "tags": []
2460
+ }
2461
+ },
2462
+ "forceResourceCleanup": {
2463
+ "complexType": {
2464
+ "signature": "() => Promise<{ success: boolean; resourcesFreed: string[]; errors: string[]; timestamp: string; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; errors?: undefined; timestamp?: undefined; }>",
2465
+ "parameters": [],
2466
+ "references": {
2467
+ "Promise": {
2468
+ "location": "global",
2469
+ "id": "global::Promise"
2470
+ }
2471
+ },
2472
+ "return": "Promise<{ success: boolean; resourcesFreed: string[]; errors: string[]; timestamp: string; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; errors?: undefined; timestamp?: undefined; }>"
2473
+ },
2474
+ "docs": {
2475
+ "text": "",
2476
+ "tags": []
2477
+ }
2225
2478
  }
2226
2479
  };
2227
2480
  }