PageSourceSearch

https://souqmadar.com/js/image-search-diagnostics.js?v=msekocwu.43r

js souqmadar.com collected 2026-10-02 01:44:12 UTC 17,028 bytes, 397 lines download raw bytes

1/* ═══════════════════════════════════════════════════════════════════════
2   IMAGE SEARCH DIAGNOSTICS & TELEMETRY (Phase 6)
3   ───────────────────────────────────────────────────────────────────────
4   COMPLETELY ISOLATED. This file does not change what any other module
5   does, returns, or how long it takes — it only OBSERVES already-existing
6   public functions/objects by wrapping them (classic decorator pattern):
7   every wrapper calls straight through to the original implementation
8   with the exact same arguments and returns/throws the exact same
9   result/error. Nothing here can alter the real Image Search flow.
10
11   ZERO modification was made to ANY other file for this phase — not even
12   an additive line — Search Pipeline, Image Intake, Validation, Quality,
13   Optimization, and Image Search UX (Phase 1–5, all files) are all
14   byte-for-byte untouched. This module attaches itself purely from the
15   outside, after those modules have already defined their public globals
16   (ImageIntake, ImageValidation, ImageQuality, window.fetch,
17   _compressImageForSearch, renderImageSearchResults, showAlert).
18
19   Disabled by default (production users see zero difference — every
20   wrapper's FIRST line is an isEnabled() check that, when false, calls
21   straight through with no extra work at all — same function call, same
22   number of ticks, no new Promises allocated). Enable for local debugging:
23     localStorage.setItem('sm_image_diag', '1')      (persists)
24     ...or open the page with ?diag=1 / ?imageDiag=1  (this tab only)
25     ...or window.ImageSearchDiagnostics.enable()
26
27   Output: window.ImageSearchDiagnostics — no UI, no modal, no console
28   spam, no alerts. Pure read API for devtools / a future admin dashboard.
29   ═══════════════════════════════════════════════════════════════════ */
30
31(function (global) {
32  'use strict';
33
34  var MAX_TRACES = 50;
35  var _traces = [];
36  var _activeTraceId = null;
37  var _pendingSource = 'unknown';
38
39  // ── Enable / disable (debug-only; production default = OFF) ───────────
40
41  function _isEnabled() {
42    try {
43      if (global.__SM_IMAGE_DIAG_FORCE__ === true) return true;
44      if (global.__SM_IMAGE_DIAG_FORCE__ === false) return false;
45      if (window.localStorage && window.localStorage.getItem('sm_image_diag') === '1') return true;
46      var qs = new URLSearchParams(window.location.search || '');
47      if (qs.get('diag') === '1' || qs.get('imageDiag') === '1') return true;
48    } catch (_) { /* ignore — default to disabled on any error */ }
49    return false;
50  }
51
52  function enable()  { try { window.localStorage.setItem('sm_image_diag', '1'); } catch (_) {} }
53  function disable() { try { window.localStorage.removeItem('sm_image_diag'); } catch (_) {} }
54
55  // ── Trace storage (bounded ring buffer, in-memory only — no network,
56  //    no localStorage persistence of trace CONTENT, no server calls) ────
57
58  function _uuid() {
59    return 'trc_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 10);
60  }
61
62  function _newTrace(sourceHint) {
63    var t = {
64      traceId:      _uuid(),
65      timestamp:    new Date().toISOString(),
66      browser:      (navigator.userAgent || ''),
67      platform:     (navigator.userAgentData && navigator.userAgentData.platform) || navigator.platform || '',
68      imageSource:  sourceHint || 'unknown',
69      stages:       {},   // stageKey -> { start, end, duration, status, errorCode, data }
70      original:     {},
71      normalized:   {},
72      finalStatus:  'in_progress',
73      failedStage:  null,
74      failureErrorCode: null,
75      startedAt:    performance.now(),
76      totalDuration: null,
77    };
78    _traces.push(t);
79    if (_traces.length > MAX_TRACES) _traces.shift();
80    _activeTraceId = t.traceId;
81    return t;
82  }
83
84  function _find(traceId) {
85    for (var i = _traces.length - 1; i >= 0; i--) if (_traces[i].traceId === traceId) return _traces[i];
86    return null;
87  }
88
89  function _stageStart(traceId, stageKey) {
90    var t = _find(traceId);
91    if (!t) return;
92    t.stages[stageKey] = t.stages[stageKey] || {};
93    t.stages[stageKey].start = performance.now();
94    t.stages[stageKey].status = 'running';
95  }
96
97  function _stageEnd(traceId, stageKey, opts) {
98    var t = _find(traceId);
99    if (!t) return;
100    opts = opts || {};
101    var s = t.stages[stageKey] = t.stages[stageKey] || {};
102    s.end = performance.now();
103    s.start = (typeof s.start === 'number') ? s.start : s.end;
104    s.duration = Math.round((s.end - s.start) * 100) / 100;
105    s.status = opts.status || 'ok';
106    if (opts.errorCode) s.errorCode = opts.errorCode;
107    if (opts.data) s.data = opts.data;
108    if (s.status === 'fail' && t.finalStatus === 'in_progress') {
109      t.finalStatus = 'failed';
110      t.failedStage = stageKey;
111      t.failureErrorCode = opts.errorCode || 'UNKNOWN_ERROR';
112      t.totalDuration = Math.round((performance.now() - t.startedAt) * 100) / 100;
113    }
114  }
115
116  function _finishSuccess(traceId) {
117    var t = _find(traceId);
118    if (!t || t.finalStatus !== 'in_progress') return;
119    t.finalStatus = 'success';
120    t.totalDuration = Math.round((performance.now() - t.startedAt) * 100) / 100;
121  }
122
123  // ── Public read API ─────────────────────────────────────────────────────
124
125  function getTrace(traceId) { return _find(traceId); }
126  function getAllTraces()    { return _traces.slice(); }
127  function getLastTrace()    { return _traces.length ? _traces[_traces.length - 1] : null; }
128  function clear()            { _traces.length = 0; _activeTraceId = null; }
129
130  // ── Source detection (independent, passive listeners — does not touch
131  //    or depend on homepage-core.js's own listeners in any way; multiple
132  //    listeners on the same element/event are standard DOM behavior and
133  //    never conflict) ─────────────────────────────────────────────────────
134
135  function _attachSourceListeners() {
136    var fileInput = document.getElementById('imgFileInput');
137    var dropZone  = document.getElementById('imgDropZone');
138    if (fileInput) {
139      fileInput.addEventListener('change', function (e) {
140        if (e.target.files && e.target.files[0]) _pendingSource = 'file-picker';
141      });
142    }
143    if (dropZone) {
144      dropZone.addEventListener('drop', function () { _pendingSource = 'drag-drop'; });
145    }
146    document.addEventListener('paste', function (e) {
147      var items = e.clipboardData && e.clipboardData.items;
148      if (!items) return;
149      for (var i = 0; i < items.length; i++) {
150        if (items[i].type && items[i].type.indexOf('image/') === 0) { _pendingSource = 'clipboard'; break; }
151      }
152    });
153  }
154
155  // ── Wrappers — each is a pure decorator: same args in, same result out,
156  //    only side-channel timing/data recorded to the active trace ────────
157
158  function _wrapIntake() {
159    if (!global.ImageIntake || typeof global.ImageIntake.normalize !== 'function') return;
160    if (global.ImageIntake.normalize.__smDiagWrapped) return;
161    var _orig = global.ImageIntake.normalize;
162    var wrapped = function (file) {
163      if (!_isEnabled()) return _orig.apply(this, arguments);
164      var t = _newTrace(_pendingSource);
165      t.original = { format: (file && file.type) || '', size: (file && file.size) || 0 };
166      var traceId = t.traceId;
167      _stageStart(traceId, 'intake');
168      return _orig.apply(this, arguments).then(
169        function (normalized) {
170          var tr = _find(traceId);
171          if (tr) tr.normalized = { format: normalized.type || '', size: normalized.size || 0 };
172          _stageEnd(traceId, 'intake', { status: 'ok' });
173          return normalized;
174        },
175        function (err) {
176          _stageEnd(traceId, 'intake', { status: 'fail', errorCode: (err && err.code) || 'INTAKE_ERROR' });
177          throw err;
178        }
179      );
180    };
181    wrapped.__smDiagWrapped = true;
182    global.ImageIntake.normalize = wrapped;
183  }
184
185  function _wrapValidation() {
186    if (!global.ImageValidation || typeof global.ImageValidation.validate !== 'function') return;
187    if (global.ImageValidation.validate.__smDiagWrapped) return;
188    var _orig = global.ImageValidation.validate;
189    var wrapped = function (file) {
190      if (!_isEnabled()) return _orig.apply(this, arguments);
191      var traceId = _activeTraceId;
192      _stageStart(traceId, 'validation');
193      return _orig.apply(this, arguments).then(function (result) {
194        var tr = _find(traceId);
195        if (tr && result && result.ok) {
196          tr.normalized.width = result.width;
197          tr.normalized.height = result.height;
198        }
199        _stageEnd(traceId, 'validation', {
200          status: result && result.ok ? 'ok' : 'fail',
201          errorCode: result && !result.ok ? result.code : undefined,
202          data: { code: result && result.code },
203        });
204        return result;
205      });
206    };
207    wrapped.__smDiagWrapped = true;
208    global.ImageValidation.validate = wrapped;
209  }
210
211  function _wrapQuality() {
212    if (!global.ImageQuality || typeof global.ImageQuality.assess !== 'function') return;
213    if (global.ImageQuality.assess.__smDiagWrapped) return;
214    var _orig = global.ImageQuality.assess;
215    var wrapped = function (file) {
216      if (!_isEnabled()) return _orig.apply(this, arguments);
217      var traceId = _activeTraceId;
218      _stageStart(traceId, 'quality');
219      return _orig.apply(this, arguments).then(function (result) {
220        var m = (result && result.metrics) || {};
221        _stageEnd(traceId, 'quality', {
222          status: result && result.ok ? 'ok' : 'fail',
223          errorCode: result && !result.ok ? result.code : undefined,
224          data: {
225            code: result && result.code,
226            blurScore: m.blurVariance,
227            brightnessScore: m.mean,
228            contrastScore: m.stdDev,
229            entropyScore: m.entropy,
230            coverageScore: m.coverageRatio,
231            noiseScore: m.noiseSigma,
232          },
233        });
234        return result;
235      });
236    };
237    wrapped.__smDiagWrapped = true;
238    global.ImageQuality.assess = wrapped;
239  }
240
241  function _wrapCompression() {
242    if (typeof global._compressImageForSearch !== 'function') return;
243    if (global._compressImageForSearch.__smDiagWrapped) return;
244    var _orig = global._compressImageForSearch;
245    var wrapped = function (file) {
246      if (!_isEnabled()) return _orig.apply(this, arguments);
247      var traceId = _activeTraceId;
248      var originalSize = (file && file.size) || 0;
249      _stageStart(traceId, 'optimization');
250      return _orig.apply(this, arguments).then(
251        function (b64) {
252          var finalSize = b64 ? Math.round(b64.length * 0.75) : 0; // base64 → approx raw bytes
253          _stageEnd(traceId, 'optimization', {
254            status: 'ok',
255            data: {
256              finalJpegSize: finalSize,
257              compressionRatio: (originalSize > 0 && finalSize > 0)
258                ? Math.round((originalSize / finalSize) * 100) / 100 : null,
259            },
260          });
261          return b64;
262        },
263        function (err) {
264          _stageEnd(traceId, 'optimization', { status: 'fail', errorCode: 'OPTIMIZATION_ERROR' });
265          throw err;
266        }
267      );
268    };
269    wrapped.__smDiagWrapped = true;
270    global._compressImageForSearch = wrapped;
271  }
272
273  function _wrapFetch() {
274    if (typeof global.fetch !== 'function' || global.fetch.__smDiagWrapped) return;
275    var _orig = global.fetch;
276    var wrapped = function (input, init) {
277      if (!_isEnabled()) return _orig.apply(this, arguments);
278      var url = (typeof input === 'string') ? input : ((input && input.url) || '');
279      var isImageSearch = url.indexOf('/api/image-search') === 0 && url.indexOf('/api/image-search-more') !== 0;
280      if (!isImageSearch) return _orig.apply(this, arguments);
281
282      var traceId = _activeTraceId;
283      // "Uploading" and "AliExpress searching" share one opaque fetch()
284      // round trip (no upload-progress API without switching to XHR, which
285      // would mean touching AliExpress Integration — forbidden). Both
286      // stages are recorded against the same real measured window,
287      // documented exactly like Phase 5's identical, honest finding.
288      _stageStart(traceId, 'upload');
289      _stageStart(traceId, 'search');
290      return _orig.apply(this, arguments).then(
291        function (res) {
292          _stageEnd(traceId, 'upload', { status: 'ok' });
293          try {
294            // .clone() so the REAL response body stream is left completely
295            // untouched for the actual caller (startImageSearch()) to read.
296            res.clone().json().then(function (data) {
297              _stageEnd(traceId, 'search', {
298                status: data && data.ok ? 'ok' : 'fail',
299                errorCode: data && !data.ok ? (data.code || 'SEARCH_ERROR') : undefined,
300                data: { candidateCount: (data && data.candidates && data.candidates.length) || 0 },
301              });
302            }).catch(function () {
303              _stageEnd(traceId, 'search', { status: 'ok' });
304            });
305          } catch (_) {
306            _stageEnd(traceId, 'search', { status: 'ok' });
307          }
308          return res;
309        },
310        function (err) {
311          _stageEnd(traceId, 'upload',  { status: 'fail', errorCode: 'NETWORK_E
311RROR' });
312          _stageEnd(traceId, 'search',  { status: 'fail', errorCode: 'NETWORK_ERROR' });
313          throw err;
314        }
315      );
316    };
317    wrapped.__smDiagWrapped = true;
318    global.fetch = wrapped;
319  }
320
321  function _wrapRender() {
322    if (typeof global.renderImageSearchResults !== 'function') return;
323    if (global.renderImageSearchResults.__smDiagWrapped) return;
324    var _orig = global.renderImageSearchResults;
325    var wrapped = function (data, meta, opts) {
326      if (!_isEnabled()) return _orig.apply(this, arguments);
327      var traceId = _activeTraceId;
328      _stageStart(traceId, 'rendering');
329      var ret = _orig.apply(this, arguments);
330      _stageEnd(traceId, 'rendering', { status: 'ok' });
331      _finishSuccess(traceId);
332      return ret;
333    };
334    wrapped.__smDiagWrapped = true;
335    global.renderImageSearchResults = wrapped;
336  }
337
338  function _wrapAlertSafetyNet() {
339    // Safety net only: if a failure occurs through a code path this file
340    // does not directly instrument (e.g. handleImageFile()'s own separate,
341    // pre-existing internal dimension check), the trace is still marked
342    // failed instead of being silently left "in_progress" forever. This
343    // never changes what showAlert displays — purely observes it.
344    if (typeof global.showAlert !== 'function' || global.showAlert.__smDiagWrapped) return;
345    var _orig = global.showAlert;
346    var wrapped = function (type, title, desc) {
347      if (_isEnabled() && type === 'error' && _activeTraceId) {
348        var t = _find(_activeTraceId);
349        if (t && t.finalStatus === 'in_progress') {
350          t.finalStatus = 'failed';
351          t.failedStage = t.failedStage || 'unknown';
352          t.failureErrorCode = t.failureErrorCode || (title || 'UNKNOWN_ERROR');
353          t.totalDuration = Math.round((performance.now() - t.startedAt) * 100) / 100;
354        }
355      }
356      return _orig.apply(this, arguments);
357    };
358    wrapped.__smDiagWrapped = true;
359    global.showAlert = wrapped;
360  }
361
362  function _install() {
363    _wrapIntake();
364    _wrapValidation();
365    _wrapQuality();
366    _wrapCompression();
367    _wrapFetch();
368    _wrapRender();
369    _wrapAlertSafetyNet();
370  }
371
372  if (document.readyState === 'loading') {
373    document.addEventListener('DOMContentLoaded', _attachSourceListeners);
374  } else {
375    _attachSourceListeners();
376  }
377
378  // This script tag is placed after every other Image Search module's tag
379  // (Phases 1–5 + homepage-image.js) in server.js, so all of the globals
380  // above already exist by the time this deferred script runs. A single
381  // install pass is therefore sufficient; a best-effort retry on the first
382  // user interaction is kept only as a defensive safety net for the rare
383  // fallback lazy-load path in homepage-core.js (_ensureImageSearch).
384  _install();
385  document.addEventListener('change', _install, { capture: true });
386  document.addEventListener('drop', _install, { capture: true });
387
388  global.ImageSearchDiagnostics = {
389    isEnabled: _isEnabled,
390    enable: enable,
391    disable: disable,
392    getTrace: getTrace,
393    getAllTraces: getAllTraces,
394    getLastTrace: getLastTrace,
395    clear: clear,
396  };
397})(window);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.