@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.
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 +364 -11
  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 +254 -6
  7. package/dist/collection/components/my-component/my-component.js.map +1 -1
  8. package/dist/collection/services/DetectionService.js +146 -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 +367 -12
  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 +364 -11
  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-8e25497e.entry.js +2 -0
  20. package/dist/jaak-stamps-webcomponent/p-8e25497e.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
@@ -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
- this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, sessionOptions);
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
- this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, fallbackOptions);
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
- this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, sessionOptions);
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
- this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, fallbackOptions);
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
- // Static method to clear all canvas pools (useful for memory management)
1061
- static clearCanvasPools() {
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
@@ -1537,7 +1676,13 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1537
1676
  }
1538
1677
  try {
1539
1678
  this.exitSession();
1540
- return { success: true };
1679
+ // Aggressive cleanup when stopping capture
1680
+ const cleanupResult = await this.aggressiveResourceCleanup();
1681
+ return {
1682
+ success: true,
1683
+ resourcesFreed: cleanupResult.freed,
1684
+ cleanupErrors: cleanupResult.errors
1685
+ };
1541
1686
  }
1542
1687
  catch (error) {
1543
1688
  return { success: false, error: error.message };
@@ -1762,6 +1907,29 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1762
1907
  // Always allow getting capture delay, even during initialization
1763
1908
  return this.captureDelay;
1764
1909
  }
1910
+ async getResourceReport() {
1911
+ // Public method to get resource usage report
1912
+ return this.getResourceReportInternal();
1913
+ }
1914
+ async forceResourceCleanup() {
1915
+ // Public method to force aggressive resource cleanup
1916
+ const readyCheck = this.isComponentReady();
1917
+ if (!readyCheck.ready) {
1918
+ return { success: false, error: readyCheck.message };
1919
+ }
1920
+ try {
1921
+ const cleanupResult = await this.aggressiveResourceCleanup();
1922
+ return {
1923
+ success: true,
1924
+ resourcesFreed: cleanupResult.freed,
1925
+ errors: cleanupResult.errors,
1926
+ timestamp: new Date().toISOString()
1927
+ };
1928
+ }
1929
+ catch (error) {
1930
+ return { success: false, error: error.message };
1931
+ }
1932
+ }
1765
1933
  // Memory and device capability detection
1766
1934
  checkDeviceCapabilities() {
1767
1935
  const deviceMemory = navigator.deviceMemory;
@@ -2097,11 +2265,182 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2097
2265
  }
2098
2266
  // Clear canvas pool
2099
2267
  this.canvasPool = [];
2268
+ // Clean DOM references and memory leaks
2269
+ this.cleanupDOMReferences();
2100
2270
  this.detectionBoxes = [];
2101
2271
  this.alignmentStartTime = undefined;
2102
2272
  this.hasDocumentDetected = false;
2103
2273
  this.serviceContainer?.cleanup();
2104
2274
  }
2275
+ // AGGRESSIVE RESOURCE CLEANUP METHODS
2276
+ // Clean DOM references and memory leaks
2277
+ cleanupDOMReferences() {
2278
+ // Remove event listeners that could maintain references
2279
+ if (this.videoRef) {
2280
+ // Remove common event listeners (if any were added)
2281
+ this.videoRef.srcObject = null;
2282
+ this.videoRef = undefined;
2283
+ }
2284
+ // Clean detection container
2285
+ if (this.detectionContainer) {
2286
+ this.detectionContainer = undefined;
2287
+ }
2288
+ // Clean references to images in the DOM and revoke blob URLs
2289
+ const images = this.el.shadowRoot?.querySelectorAll('img');
2290
+ images?.forEach(img => {
2291
+ if (img.src.startsWith('blob:')) {
2292
+ URL.revokeObjectURL(img.src);
2293
+ }
2294
+ img.src = '';
2295
+ });
2296
+ }
2297
+ // Clear browser cache and storage
2298
+ async clearBrowserCache() {
2299
+ try {
2300
+ // 1. Clear Service Worker cache if exists
2301
+ if ('serviceWorker' in navigator) {
2302
+ const registrations = await navigator.serviceWorker.getRegistrations();
2303
+ await Promise.all(registrations.map(registration => registration.unregister()));
2304
+ }
2305
+ // 2. Clear Cache API specific to the component
2306
+ const cacheNames = await caches.keys();
2307
+ const componentCaches = cacheNames.filter(name => name.includes('jaak') || name.includes('stamps'));
2308
+ await Promise.all(componentCaches.map(cacheName => caches.delete(cacheName)));
2309
+ // 3. Force garbage collection if available (only in dev)
2310
+ if (this.debug && window.gc) {
2311
+ window.gc();
2312
+ }
2313
+ }
2314
+ catch (error) {
2315
+ console.warn('Error clearing browser cache:', error);
2316
+ }
2317
+ }
2318
+ // Clear local storage specific to the component
2319
+ async clearLocalStorage() {
2320
+ try {
2321
+ // Clear localStorage specific to the component
2322
+ const keysToRemove = [];
2323
+ for (let i = 0; i < localStorage.length; i++) {
2324
+ const key = localStorage.key(i);
2325
+ if (key && (key.includes('jaak') || key.includes('onnx') || key.includes('stamps'))) {
2326
+ keysToRemove.push(key);
2327
+ }
2328
+ }
2329
+ keysToRemove.forEach(key => localStorage.removeItem(key));
2330
+ // Clear sessionStorage
2331
+ const sessionKeysToRemove = [];
2332
+ for (let i = 0; i < sessionStorage.length; i++) {
2333
+ const key = sessionStorage.key(i);
2334
+ if (key && (key.includes('jaak') || key.includes('onnx'))) {
2335
+ sessionKeysToRemove.push(key);
2336
+ }
2337
+ }
2338
+ sessionKeysToRemove.forEach(key => sessionStorage.removeItem(key));
2339
+ }
2340
+ catch (error) {
2341
+ console.warn('Error clearing local storage:', error);
2342
+ }
2343
+ }
2344
+ // Aggressive resource cleanup combining all strategies
2345
+ async aggressiveResourceCleanup() {
2346
+ const freed = [];
2347
+ const errors = [];
2348
+ try {
2349
+ // 1. Release ONNX resources
2350
+ this.releaseOnnxResources();
2351
+ freed.push('ONNX models');
2352
+ // 2. Clear model cache in detection service
2353
+ if (this.detectionService) {
2354
+ await this.detectionService.clearModelCache();
2355
+ freed.push('Model cache');
2356
+ }
2357
+ // 3. Clear browser cache
2358
+ await this.clearBrowserCache();
2359
+ freed.push('Browser cache');
2360
+ // 4. Clear local storage
2361
+ await this.clearLocalStorage();
2362
+ freed.push('Local storage');
2363
+ // 5. Clean DOM references
2364
+ this.cleanupDOMReferences();
2365
+ freed.push('DOM references');
2366
+ // 6. Optimize canvas pools
2367
+ if (this.detectionService) {
2368
+ this.detectionService.optimizeCanvasPool(0);
2369
+ freed.push('Canvas pools');
2370
+ }
2371
+ // 7. Clear component canvas pool
2372
+ this.canvasPool = [];
2373
+ freed.push('Component canvas pool');
2374
+ // 8. Force garbage collection (if available)
2375
+ if (this.debug && window.gc) {
2376
+ window.gc();
2377
+ freed.push('Garbage collection');
2378
+ }
2379
+ }
2380
+ catch (error) {
2381
+ errors.push(error.message);
2382
+ }
2383
+ return { freed, errors };
2384
+ }
2385
+ // Progressive resource release after front capture
2386
+ async onFrontCaptureComplete() {
2387
+ try {
2388
+ // Release classification model if not needed for back
2389
+ if (this.detectionService && !this.useDocumentClassification) {
2390
+ await this.detectionService.releaseMobileNetResources();
2391
+ }
2392
+ // Optimize canvas pool to minimum needed for back capture
2393
+ if (this.detectionService) {
2394
+ this.detectionService.optimizeCanvasPool(1);
2395
+ }
2396
+ // Reduce component canvas pool
2397
+ while (this.canvasPool.length > 1) {
2398
+ const canvas = this.canvasPool.pop();
2399
+ if (canvas) {
2400
+ const ctx = canvas.getContext('2d');
2401
+ if (ctx) {
2402
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
2403
+ }
2404
+ }
2405
+ }
2406
+ if (this.debug) {
2407
+ console.log('🧹 Progressive cleanup after front capture completed');
2408
+ }
2409
+ }
2410
+ catch (error) {
2411
+ console.warn('Error in progressive cleanup:', error);
2412
+ }
2413
+ }
2414
+ // Resource reporting for debugging
2415
+ getResourceReportInternal() {
2416
+ const report = {
2417
+ timestamp: new Date().toISOString(),
2418
+ component: {
2419
+ hasVideoStream: !!this.videoStream,
2420
+ hasAnimationFrame: !!this.animationId,
2421
+ canvasPoolSize: this.canvasPool.length,
2422
+ detectionBoxesCount: this.detectionBoxes.length,
2423
+ performanceMonitorActive: !!this.performanceUpdateInterval
2424
+ }
2425
+ };
2426
+ // Add detection service stats if available
2427
+ if (this.detectionService) {
2428
+ report.detectionService = {
2429
+ isModelLoaded: this.detectionService.isModelLoaded(),
2430
+ canvasPoolStats: this.detectionService.getPoolStats()
2431
+ };
2432
+ }
2433
+ // Add memory info if available
2434
+ if ('memory' in performance) {
2435
+ const memInfo = performance.memory;
2436
+ report.memory = {
2437
+ usedJSHeapSize: Math.round(memInfo.usedJSHeapSize / 1048576), // MB
2438
+ totalJSHeapSize: Math.round(memInfo.totalJSHeapSize / 1048576), // MB
2439
+ jsHeapSizeLimit: Math.round(memInfo.jsHeapSizeLimit / 1048576) // MB
2440
+ };
2441
+ }
2442
+ return report;
2443
+ }
2105
2444
  render() {
2106
2445
  const captureState = this.stateManager?.getCaptureState() || {
2107
2446
  isVideoActive: false,
@@ -2111,7 +2450,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2111
2450
  isCapturing: false
2112
2451
  };
2113
2452
  const cameraInfo = this.cameraInfoWithAutofocus;
2114
- 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: {
2453
+ 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: {
2115
2454
  position: 'absolute',
2116
2455
  left: `${box.x}px`,
2117
2456
  top: `${box.y}px`,
@@ -2120,9 +2459,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2120
2459
  border: '2px solid #32406C',
2121
2460
  pointerEvents: 'none',
2122
2461
  boxSizing: 'border-box'
2123
- } })))), 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
2462
+ } })))), 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
2124
2463
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
2125
- : '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" })))));
2464
+ : '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" })))));
2126
2465
  }
2127
2466
  // Utility methods
2128
2467
  updateDetectionBoxes(boxes) {
@@ -2346,6 +2685,8 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2346
2685
  showFlipAnimation: true
2347
2686
  });
2348
2687
  this.triggerRerender();
2688
+ // Progressive resource cleanup after front capture
2689
+ await this.onFrontCaptureComplete();
2349
2690
  setTimeout(() => {
2350
2691
  this.stateManager.updateCaptureState({
2351
2692
  showFlipAnimation: false,
@@ -2421,6 +2762,8 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2421
2762
  isDetectionPaused: true,
2422
2763
  showFlipAnimation: true
2423
2764
  });
2765
+ // Progressive resource cleanup after front capture
2766
+ await this.onFrontCaptureComplete();
2424
2767
  setTimeout(() => {
2425
2768
  this.stateManager.updateCaptureState({
2426
2769
  showFlipAnimation: false,
@@ -2598,6 +2941,16 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2598
2941
  setTimeout(() => {
2599
2942
  this.stateManager.updateCaptureState({ showSuccessAnimation: false });
2600
2943
  }, 3000);
2944
+ // Aggressive cleanup after process completion with delay
2945
+ setTimeout(async () => {
2946
+ const cleanupResult = await this.aggressiveResourceCleanup();
2947
+ if (this.debug) {
2948
+ console.log('🧹 Post-completion cleanup:', cleanupResult.freed);
2949
+ if (cleanupResult.errors.length > 0) {
2950
+ console.warn('⚠️ Cleanup errors:', cleanupResult.errors);
2951
+ }
2952
+ }
2953
+ }, 1000); // Wait 1 second after completion
2601
2954
  }
2602
2955
  stopDetection() {
2603
2956
  if (this.animationId) {
@@ -2889,7 +3242,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2889
3242
  "getCameraInfo": [64],
2890
3243
  "setPreferredCamera": [64],
2891
3244
  "setCaptureDelay": [64],
2892
- "getCaptureDelay": [64]
3245
+ "getCaptureDelay": [64],
3246
+ "getResourceReport": [64],
3247
+ "forceResourceCleanup": [64]
2893
3248
  }]);
2894
3249
  function defineCustomElement$1() {
2895
3250
  if (typeof customElements === "undefined") {