web-mojo 2.1.1108 → 2.1.1110

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 (87) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +18 -16
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -1
  7. package/dist/charts.cjs.js +1 -1
  8. package/dist/charts.css +118 -0
  9. package/dist/charts.es.js +4 -4
  10. package/dist/chat.css +3 -18
  11. package/dist/chunks/{ChatView-CTe1wlQP.js → ChatView-BsANcDiL.js} +31 -8
  12. package/dist/chunks/ChatView-BsANcDiL.js.map +1 -0
  13. package/dist/chunks/{ChatView-BL4kPmQd.js → ChatView-DSEEMnNt.js} +2 -2
  14. package/dist/chunks/ChatView-DSEEMnNt.js.map +1 -0
  15. package/dist/chunks/{Collection-Ds4EOzap.js → Collection-CqLcHgQG.js} +2 -2
  16. package/dist/chunks/{Collection-Ds4EOzap.js.map → Collection-CqLcHgQG.js.map} +1 -1
  17. package/dist/chunks/{Collection-bgdNt0wV.js → Collection-vtImpCcS.js} +2 -2
  18. package/dist/chunks/{Collection-bgdNt0wV.js.map → Collection-vtImpCcS.js.map} +1 -1
  19. package/dist/chunks/{ContextMenu-DwvxeWLt.js → ContextMenu-BY3BgxM5.js} +2 -2
  20. package/dist/chunks/{ContextMenu-DwvxeWLt.js.map → ContextMenu-BY3BgxM5.js.map} +1 -1
  21. package/dist/chunks/{ContextMenu-KHTc-Ubo.js → ContextMenu-DfrWFc5y.js} +3 -3
  22. package/dist/chunks/{ContextMenu-KHTc-Ubo.js.map → ContextMenu-DfrWFc5y.js.map} +1 -1
  23. package/dist/chunks/{DataView-DbexZltc.js → DataView-CHvoEI4M.js} +2 -2
  24. package/dist/chunks/{DataView-DbexZltc.js.map → DataView-CHvoEI4M.js.map} +1 -1
  25. package/dist/chunks/{DataView-DAGqpR7b.js → DataView-CsYXM9vF.js} +2 -2
  26. package/dist/chunks/{DataView-DAGqpR7b.js.map → DataView-CsYXM9vF.js.map} +1 -1
  27. package/dist/chunks/{Dialog-DE-21f_8.js → Dialog-BKPwBCDC.js} +2 -2
  28. package/dist/chunks/Dialog-BKPwBCDC.js.map +1 -0
  29. package/dist/chunks/{Dialog-CXOsYNLf.js → Dialog-D4DsB-N1.js} +6 -6
  30. package/dist/chunks/Dialog-D4DsB-N1.js.map +1 -0
  31. package/dist/chunks/{FormView-Dya3nBrn.js → FormView-B9nIO_AX.js} +2 -2
  32. package/dist/chunks/{FormView-Dya3nBrn.js.map → FormView-B9nIO_AX.js.map} +1 -1
  33. package/dist/chunks/{FormView-DbPZwhZ6.js → FormView-DYX_yeho.js} +2 -2
  34. package/dist/chunks/{FormView-DbPZwhZ6.js.map → FormView-DYX_yeho.js.map} +1 -1
  35. package/dist/chunks/{ListView-DFP4ie99.js → ListView-BtZ7fylv.js} +3 -3
  36. package/dist/chunks/{ListView-DFP4ie99.js.map → ListView-BtZ7fylv.js.map} +1 -1
  37. package/dist/chunks/{ListView-BQgv3OvQ.js → ListView-D2vt0koT.js} +2 -2
  38. package/dist/chunks/{ListView-BQgv3OvQ.js.map → ListView-D2vt0koT.js.map} +1 -1
  39. package/dist/chunks/{MetricsMiniChartWidget-DOcsPk5u.js → MetricsMiniChartWidget-BJJY9R-s.js} +2 -2
  40. package/dist/chunks/MetricsMiniChartWidget-BJJY9R-s.js.map +1 -0
  41. package/dist/chunks/{MetricsMiniChartWidget-Dan8l57G.js → MetricsMiniChartWidget-bmnV82d1.js} +340 -9
  42. package/dist/chunks/{MetricsMiniChartWidget-Dan8l57G.js.map → MetricsMiniChartWidget-bmnV82d1.js.map} +1 -1
  43. package/dist/chunks/{PDFViewer-B1g6Q6ae.js → PDFViewer-Bzifr-dn.js} +3 -3
  44. package/dist/chunks/{PDFViewer-B1g6Q6ae.js.map → PDFViewer-Bzifr-dn.js.map} +1 -1
  45. package/dist/chunks/{PDFViewer-DDb7xLGo.js → PDFViewer-Cgr3T15i.js} +2 -2
  46. package/dist/chunks/{PDFViewer-DDb7xLGo.js.map → PDFViewer-Cgr3T15i.js.map} +1 -1
  47. package/dist/chunks/{Rest-C3KvOEeY.js → Rest-C3fPzCIA.js} +2 -2
  48. package/dist/chunks/Rest-C3fPzCIA.js.map +1 -0
  49. package/dist/chunks/{Rest-BIyXKaFn.js → Rest-DYPLEzNy.js} +2 -2
  50. package/dist/chunks/Rest-DYPLEzNy.js.map +1 -0
  51. package/dist/chunks/{TokenManager-DUmZuAKG.js → TokenManager-CKkIWgzy.js} +6 -6
  52. package/dist/chunks/TokenManager-CKkIWgzy.js.map +1 -0
  53. package/dist/chunks/TokenManager-DSyRWlvc.js +2 -0
  54. package/dist/chunks/TokenManager-DSyRWlvc.js.map +1 -0
  55. package/dist/chunks/{WebSocketClient-CGQLSIgP.js → WebSocketClient-CkAL55qy.js} +2 -2
  56. package/dist/chunks/{WebSocketClient-CGQLSIgP.js.map → WebSocketClient-CkAL55qy.js.map} +1 -1
  57. package/dist/chunks/{WebSocketClient-sEN2DmCF.js → WebSocketClient-Dzwprd15.js} +2 -2
  58. package/dist/chunks/{WebSocketClient-sEN2DmCF.js.map → WebSocketClient-Dzwprd15.js.map} +1 -1
  59. package/dist/chunks/{version-DIk6UpNb.js → version-C5lFa1F0.js} +2 -2
  60. package/dist/chunks/{version-DIk6UpNb.js.map → version-C5lFa1F0.js.map} +1 -1
  61. package/dist/chunks/{version-CrckCqr_.js → version-FRgkiWti.js} +4 -4
  62. package/dist/chunks/{version-CrckCqr_.js.map → version-FRgkiWti.js.map} +1 -1
  63. package/dist/core.css +58 -52
  64. package/dist/css/web-mojo.css +1 -1
  65. package/dist/docit.cjs.js +1 -1
  66. package/dist/docit.es.js +6 -6
  67. package/dist/index.cjs.js +1 -1
  68. package/dist/index.cjs.js.map +1 -1
  69. package/dist/index.es.js +71 -132
  70. package/dist/index.es.js.map +1 -1
  71. package/dist/lightbox.cjs.js +1 -1
  72. package/dist/lightbox.es.js +5 -5
  73. package/dist/map.cjs.js +1 -1
  74. package/dist/map.es.js +2 -2
  75. package/dist/timeline.cjs.js +1 -1
  76. package/dist/timeline.es.js +4 -4
  77. package/package.json +1 -1
  78. package/dist/chunks/ChatView-BL4kPmQd.js.map +0 -1
  79. package/dist/chunks/ChatView-CTe1wlQP.js.map +0 -1
  80. package/dist/chunks/Dialog-CXOsYNLf.js.map +0 -1
  81. package/dist/chunks/Dialog-DE-21f_8.js.map +0 -1
  82. package/dist/chunks/MetricsMiniChartWidget-DOcsPk5u.js.map +0 -1
  83. package/dist/chunks/Rest-BIyXKaFn.js.map +0 -1
  84. package/dist/chunks/Rest-C3KvOEeY.js.map +0 -1
  85. package/dist/chunks/TokenManager-B0Z_dd7k.js +0 -2
  86. package/dist/chunks/TokenManager-B0Z_dd7k.js.map +0 -1
  87. package/dist/chunks/TokenManager-DUmZuAKG.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"WebSocketClient-CGQLSIgP.js","sources":["../../src/core/services/WebSocketClient.js"],"sourcesContent":["/**\n * WebSocketClient - Simple, robust WebSocket client with auto-reconnect\n *\n * Features:\n * - Auto-reconnect with exponential backoff\n * - Heartbeat ping/pong with timeout disconnect\n * - Event-driven architecture using EventEmitter\n * - Token-based authentication\n *\n * Usage:\n * const ws = new WebSocketClient({\n * url: \"wss://api.example.com/ws/realtime\",\n * tokenPrefix: \"bearer\",\n * getToken: () => app.tokenManager.getToken()\n * });\n *\n * ws.on('connected', () => console.log('Connected!'));\n * ws.on('message', (data) => console.log('Received:', data));\n * ws.connect();\n */\n\nimport EventEmitter from '@core/mixins/EventEmitter.js';\n\nclass WebSocketClient {\n constructor(options = {}) {\n // Connection\n this.url = options.url;\n this.socket = null;\n this.isConnected = false;\n this.isConnecting = false;\n\n // Auth\n this.getToken = options.getToken || null;\n this.tokenPrefix = options.tokenPrefix || 'bearer';\n\n // Reconnection\n this.shouldReconnect = options.autoReconnect !== false;\n this.maxReconnectAttempts = options.maxReconnectAttempts || Infinity;\n this.reconnectInterval = options.reconnectInterval || 3000;\n this.reconnectBackoff = options.reconnectBackoff || 1.5;\n this.reconnectAttempts = 0;\n this.reconnectTimer = null;\n\n // Heartbeat\n this.pingInterval = options.pingInterval || 30000;\n this.pongTimeout = options.pongTimeout || 5000;\n this.pingTimer = null;\n this.pongTimer = null;\n\n // Debug\n this.debug = options.debug || false;\n }\n\n /**\n * Connect to WebSocket server\n */\n async connect(url = null) {\n if (url) this.url = url;\n if (!this.url) throw new Error('WebSocket URL is required');\n if (this.isConnected || this.isConnecting) return;\n\n this.isConnecting = true;\n this._log('Connecting to:', this.url);\n\n return new Promise((resolve, reject) => {\n try {\n this.socket = new WebSocket(this.url);\n\n this.socket.onopen = () => {\n this._log('Connected');\n this.isConnected = true;\n this.isConnecting = false;\n this.reconnectAttempts = 0;\n\n this._authenticate();\n this._startHeartbeat();\n\n this.emit('connected');\n resolve();\n };\n\n this.socket.onmessage = (event) => this._handleMessage(event);\n this.socket.onerror = (event) => this._handleError(event, reject);\n this.socket.onclose = (event) => this._handleClose(event);\n\n } catch (error) {\n this.isConnecting = false;\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from server\n */\n disconnect() {\n this.shouldReconnect = false;\n this._clearTimers();\n\n if (this.socket) {\n this._log('Disconnecting');\n this.socket.close(1000, 'Client disconnect');\n }\n }\n\n /**\n * Send data (auto-stringifies objects)\n */\n send(data) {\n if (!this.isConnected) {\n throw new Error('WebSocket not connected');\n }\n\n const message = typeof data === 'string' ? data : JSON.stringify(data);\n this.socket.send(message);\n this._log('Sent:', message);\n }\n\n // Private methods\n\n _authenticate() {\n const token = this.getToken ? this.getToken() : null;\n if (!token) {\n console.warn('[WebSocket] No token available');\n return;\n }\n\n this.send({\n type: 'authenticate',\n token,\n prefix: this.tokenPrefix\n });\n }\n\n _handleMessage(event) {\n this._log('Received:', event.data);\n\n let data;\n try {\n data = JSON.parse(event.data);\n } catch (error) {\n data = event.data;\n }\n\n // Handle pong response\n if (data?.type === 'pong') {\n this._clearPongTimeout();\n return;\n }\n\n // Emit specific event types\n if (data?.type) {\n this.emit(`message:${data.type}`, data);\n }\n\n // Always emit generic message event\n this.emit('message', data);\n }\n\n _handleError(event, rejectFn) {\n console.error('[WebSocket] Error:', event);\n this.emit('error', event);\n\n if (rejectFn) {\n rejectFn(new Error('WebSocket connection failed'));\n }\n }\n\n _handleClose(event) {\n this._log('Closed:', event.code, event.reason);\n\n this.isConnected = false;\n this.isConnecting = false;\n this._clearTimers();\n this.socket = null;\n\n this.emit('disconnected', {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean\n });\n\n // Auto-reconnect (except for clean closes)\n if (this.shouldReconnect && event.code !== 1000) {\n this._reconnect();\n }\n }\n\n _reconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this._log('Max reconnect attempts reached');\n this.emit('reconnect-failed', { attempts: this.reconnectAttempts });\n return;\n }\n\n this.reconnectAttempts++;\n const delay = this.reconnectInterval * Math.pow(this.reconnectBackoff, this.reconnectAttempts - 1);\n\n this._log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n this.emit('reconnecting', { attempt: this.reconnectAttempts, delay });\n\n this.reconnectTimer = setTimeout(() => {\n if (this.shouldReconnect) {\n this.connect().catch(err => console.error('[WebSocket] Reconnect failed:', err));\n }\n }, delay);\n }\n\n _startHeartbeat() {\n if (!this.pingInterval) return;\n\n this.pingTimer = setInterval(() => {\n if (this.isConnected) {\n this.send({ action: 'ping' });\n this._startPongTimeout();\n }\n }, this.pingInterval);\n }\n\n _startPongTimeout() {\n this._clearPongTimeout();\n\n this.pongTimer = setTimeout(() => {\n console.warn('[WebSocket] Pong timeout - closing connection');\n if (this.socket) {\n this.socket.close(1006, 'Pong timeout');\n }\n }, this.pongTimeout);\n }\n\n _clearPongTimeout() {\n if (this.pongTimer) {\n clearTimeout(this.pongTimer);\n this.pongTimer = null;\n }\n }\n\n _clearTimers() {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n this._clearPongTimeout();\n }\n\n _log(...args) {\n if (this.debug) {\n console.log('[WebSocket]', ...args);\n }\n }\n\n // Static methods\n\n /**\n * Convert REST API base URL to WebSocket URL\n * @param {string} baseURL - REST API base URL (http/https)\n * @param {string} path - WebSocket path (default: '/ws')\n * @returns {string} WebSocket URL (ws/wss)\n *\n * @example\n * WebSocketClient.deriveURL('https://api.example.com', '/ws/realtime')\n * // Returns: 'wss://api.example.com/ws/realtime'\n *\n * WebSocketClient.deriveURL('http://localhost:3000')\n * // Returns: 'ws://localhost:3000/ws'\n */\n static deriveURL(baseURL, path = '/ws/realtime/') {\n if (!baseURL) throw new Error('baseURL is required');\n\n // Parse the base URL\n const url = new URL(baseURL);\n\n // Convert http(s) to ws(s)\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';\n\n // Set the path (ensure it starts with /)\n url.pathname = path.startsWith('/') ? path : `/${path}`;\n\n return url.toString();\n }\n}\n\n// Add EventEmitter mixin\nObject.assign(WebSocketClient.prototype, EventEmitter);\n\nexport default WebSocketClient;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WebSocketClient-CkAL55qy.js","sources":["../../src/core/services/WebSocketClient.js"],"sourcesContent":["/**\n * WebSocketClient - Simple, robust WebSocket client with auto-reconnect\n *\n * Features:\n * - Auto-reconnect with exponential backoff\n * - Heartbeat ping/pong with timeout disconnect\n * - Event-driven architecture using EventEmitter\n * - Token-based authentication\n *\n * Usage:\n * const ws = new WebSocketClient({\n * url: \"wss://api.example.com/ws/realtime\",\n * tokenPrefix: \"bearer\",\n * getToken: () => app.tokenManager.getToken()\n * });\n *\n * ws.on('connected', () => console.log('Connected!'));\n * ws.on('message', (data) => console.log('Received:', data));\n * ws.connect();\n */\n\nimport EventEmitter from '@core/mixins/EventEmitter.js';\n\nclass WebSocketClient {\n constructor(options = {}) {\n // Connection\n this.url = options.url;\n this.socket = null;\n this.isConnected = false;\n this.isConnecting = false;\n\n // Auth\n this.getToken = options.getToken || null;\n this.tokenPrefix = options.tokenPrefix || 'bearer';\n\n // Reconnection\n this.shouldReconnect = options.autoReconnect !== false;\n this.maxReconnectAttempts = options.maxReconnectAttempts || Infinity;\n this.reconnectInterval = options.reconnectInterval || 3000;\n this.reconnectBackoff = options.reconnectBackoff || 1.5;\n this.reconnectAttempts = 0;\n this.reconnectTimer = null;\n\n // Heartbeat\n this.pingInterval = options.pingInterval || 30000;\n this.pongTimeout = options.pongTimeout || 5000;\n this.pingTimer = null;\n this.pongTimer = null;\n\n // Debug\n this.debug = options.debug || false;\n }\n\n /**\n * Connect to WebSocket server\n */\n async connect(url = null) {\n if (url) this.url = url;\n if (!this.url) throw new Error('WebSocket URL is required');\n if (this.isConnected || this.isConnecting) return;\n\n this.isConnecting = true;\n this._log('Connecting to:', this.url);\n\n return new Promise((resolve, reject) => {\n try {\n this.socket = new WebSocket(this.url);\n\n this.socket.onopen = () => {\n this._log('Connected');\n this.isConnected = true;\n this.isConnecting = false;\n this.reconnectAttempts = 0;\n\n this._authenticate();\n this._startHeartbeat();\n\n this.emit('connected');\n resolve();\n };\n\n this.socket.onmessage = (event) => this._handleMessage(event);\n this.socket.onerror = (event) => this._handleError(event, reject);\n this.socket.onclose = (event) => this._handleClose(event);\n\n } catch (error) {\n this.isConnecting = false;\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from server\n */\n disconnect() {\n this.shouldReconnect = false;\n this._clearTimers();\n\n if (this.socket) {\n this._log('Disconnecting');\n this.socket.close(1000, 'Client disconnect');\n }\n }\n\n /**\n * Send data (auto-stringifies objects)\n */\n send(data) {\n if (!this.isConnected) {\n throw new Error('WebSocket not connected');\n }\n\n const message = typeof data === 'string' ? data : JSON.stringify(data);\n this.socket.send(message);\n this._log('Sent:', message);\n }\n\n // Private methods\n\n _authenticate() {\n const token = this.getToken ? this.getToken() : null;\n if (!token) {\n console.warn('[WebSocket] No token available');\n return;\n }\n\n this.send({\n type: 'authenticate',\n token,\n prefix: this.tokenPrefix\n });\n }\n\n _handleMessage(event) {\n this._log('Received:', event.data);\n\n let data;\n try {\n data = JSON.parse(event.data);\n } catch (error) {\n data = event.data;\n }\n\n // Handle pong response\n if (data?.type === 'pong') {\n this._clearPongTimeout();\n return;\n }\n\n // Emit specific event types\n if (data?.type) {\n this.emit(`message:${data.type}`, data);\n }\n\n // Always emit generic message event\n this.emit('message', data);\n }\n\n _handleError(event, rejectFn) {\n console.error('[WebSocket] Error:', event);\n this.emit('error', event);\n\n if (rejectFn) {\n rejectFn(new Error('WebSocket connection failed'));\n }\n }\n\n _handleClose(event) {\n this._log('Closed:', event.code, event.reason);\n\n this.isConnected = false;\n this.isConnecting = false;\n this._clearTimers();\n this.socket = null;\n\n this.emit('disconnected', {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean\n });\n\n // Auto-reconnect (except for clean closes)\n if (this.shouldReconnect && event.code !== 1000) {\n this._reconnect();\n }\n }\n\n _reconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this._log('Max reconnect attempts reached');\n this.emit('reconnect-failed', { attempts: this.reconnectAttempts });\n return;\n }\n\n this.reconnectAttempts++;\n const delay = this.reconnectInterval * Math.pow(this.reconnectBackoff, this.reconnectAttempts - 1);\n\n this._log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n this.emit('reconnecting', { attempt: this.reconnectAttempts, delay });\n\n this.reconnectTimer = setTimeout(() => {\n if (this.shouldReconnect) {\n this.connect().catch(err => console.error('[WebSocket] Reconnect failed:', err));\n }\n }, delay);\n }\n\n _startHeartbeat() {\n if (!this.pingInterval) return;\n\n this.pingTimer = setInterval(() => {\n if (this.isConnected) {\n this.send({ action: 'ping' });\n this._startPongTimeout();\n }\n }, this.pingInterval);\n }\n\n _startPongTimeout() {\n this._clearPongTimeout();\n\n this.pongTimer = setTimeout(() => {\n console.warn('[WebSocket] Pong timeout - closing connection');\n if (this.socket) {\n this.socket.close(1006, 'Pong timeout');\n }\n }, this.pongTimeout);\n }\n\n _clearPongTimeout() {\n if (this.pongTimer) {\n clearTimeout(this.pongTimer);\n this.pongTimer = null;\n }\n }\n\n _clearTimers() {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n this._clearPongTimeout();\n }\n\n _log(...args) {\n if (this.debug) {\n console.log('[WebSocket]', ...args);\n }\n }\n\n // Static methods\n\n /**\n * Convert REST API base URL to WebSocket URL\n * @param {string} baseURL - REST API base URL (http/https)\n * @param {string} path - WebSocket path (default: '/ws')\n * @returns {string} WebSocket URL (ws/wss)\n *\n * @example\n * WebSocketClient.deriveURL('https://api.example.com', '/ws/realtime')\n * // Returns: 'wss://api.example.com/ws/realtime'\n *\n * WebSocketClient.deriveURL('http://localhost:3000')\n * // Returns: 'ws://localhost:3000/ws'\n */\n static deriveURL(baseURL, path = '/ws/realtime/') {\n if (!baseURL) throw new Error('baseURL is required');\n\n // Parse the base URL\n const url = new URL(baseURL);\n\n // Convert http(s) to ws(s)\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';\n\n // Set the path (ensure it starts with /)\n url.pathname = path.startsWith('/') ? path : `/${path}`;\n\n return url.toString();\n }\n}\n\n// Add EventEmitter mixin\nObject.assign(WebSocketClient.prototype, EventEmitter);\n\nexport default WebSocketClient;\n"],"names":[],"mappings":";AAuBA,MAAM,gBAAgB;AAAA,EACpB,YAAY,UAAU,IAAI;AAExB,SAAK,MAAM,QAAQ;AACnB,SAAK,SAAS;AACd,SAAK,cAAc;AACnB,SAAK,eAAe;AAGpB,SAAK,WAAW,QAAQ,YAAY;AACpC,SAAK,cAAc,QAAQ,eAAe;AAG1C,SAAK,kBAAkB,QAAQ,kBAAkB;AACjD,SAAK,uBAAuB,QAAQ,wBAAwB;AAC5D,SAAK,oBAAoB,QAAQ,qBAAqB;AACtD,SAAK,mBAAmB,QAAQ,oBAAoB;AACpD,SAAK,oBAAoB;AACzB,SAAK,iBAAiB;AAGtB,SAAK,eAAe,QAAQ,gBAAgB;AAC5C,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,YAAY;AACjB,SAAK,YAAY;AAGjB,SAAK,QAAQ,QAAQ,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,MAAM,MAAM;AACxB,QAAI,IAAK,MAAK,MAAM;AACpB,QAAI,CAAC,KAAK,IAAK,OAAM,IAAI,MAAM,2BAA2B;AAC1D,QAAI,KAAK,eAAe,KAAK,aAAc;AAE3C,SAAK,eAAe;AACpB,SAAK,KAAK,kBAAkB,KAAK,GAAG;AAEpC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AACF,aAAK,SAAS,IAAI,UAAU,KAAK,GAAG;AAEpC,aAAK,OAAO,SAAS,MAAM;AACzB,eAAK,KAAK,WAAW;AACrB,eAAK,cAAc;AACnB,eAAK,eAAe;AACpB,eAAK,oBAAoB;AAEzB,eAAK,cAAa;AAClB,eAAK,gBAAe;AAEpB,eAAK,KAAK,WAAW;AACrB,kBAAO;AAAA,QACT;AAEA,aAAK,OAAO,YAAY,CAAC,UAAU,KAAK,eAAe,KAAK;AAC5D,aAAK,OAAO,UAAU,CAAC,UAAU,KAAK,aAAa,OAAO,MAAM;AAChE,aAAK,OAAO,UAAU,CAAC,UAAU,KAAK,aAAa,KAAK;AAAA,MAE1D,SAAS,OAAO;AACd,aAAK,eAAe;AACpB,eAAO,KAAK;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa;AACX,SAAK,kBAAkB;AACvB,SAAK,aAAY;AAEjB,QAAI,KAAK,QAAQ;AACf,WAAK,KAAK,eAAe;AACzB,WAAK,OAAO,MAAM,KAAM,mBAAmB;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,MAAM;AACT,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,UAAM,UAAU,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AACrE,SAAK,OAAO,KAAK,OAAO;AACxB,SAAK,KAAK,SAAS,OAAO;AAAA,EAC5B;AAAA;AAAA,EAIA,gBAAgB;AACd,UAAM,QAAQ,KAAK,WAAW,KAAK,SAAQ,IAAK;AAChD,QAAI,CAAC,OAAO;AACV,cAAQ,KAAK,gCAAgC;AAC7C;AAAA,IACF;AAEA,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,KAAK;AAAA,IACnB,CAAK;AAAA,EACH;AAAA,EAEA,eAAe,OAAO;AACpB,SAAK,KAAK,aAAa,MAAM,IAAI;AAEjC,QAAI;AACJ,QAAI;AACF,aAAO,KAAK,MAAM,MAAM,IAAI;AAAA,IAC9B,SAAS,OAAO;AACd,aAAO,MAAM;AAAA,IACf;AAGA,QAAI,MAAM,SAAS,QAAQ;AACzB,WAAK,kBAAiB;AACtB;AAAA,IACF;AAGA,QAAI,MAAM,MAAM;AACd,WAAK,KAAK,WAAW,KAAK,IAAI,IAAI,IAAI;AAAA,IACxC;AAGA,SAAK,KAAK,WAAW,IAAI;AAAA,EAC3B;AAAA,EAEA,aAAa,OAAO,UAAU;AAC5B,YAAQ,MAAM,sBAAsB,KAAK;AACzC,SAAK,KAAK,SAAS,KAAK;AAExB,QAAI,UAAU;AACZ,eAAS,IAAI,MAAM,6BAA6B,CAAC;AAAA,IACnD;AAAA,EACF;AAAA,EAEA,aAAa,OAAO;AAClB,SAAK,KAAK,WAAW,MAAM,MAAM,MAAM,MAAM;AAE7C,SAAK,cAAc;AACnB,SAAK,eAAe;AACpB,SAAK,aAAY;AACjB,SAAK,SAAS;AAEd,SAAK,KAAK,gBAAgB;AAAA,MACxB,MAAM,MAAM;AAAA,MACZ,QAAQ,MAAM;AAAA,MACd,UAAU,MAAM;AAAA,IACtB,CAAK;AAGD,QAAI,KAAK,mBAAmB,MAAM,SAAS,KAAM;AAC/C,WAAK,WAAU;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,aAAa;AACX,QAAI,KAAK,qBAAqB,KAAK,sBAAsB;AACvD,WAAK,KAAK,gCAAgC;AAC1C,WAAK,KAAK,oBAAoB,EAAE,UAAU,KAAK,mBAAmB;AAClE;AAAA,IACF;AAEA,SAAK;AACL,UAAM,QAAQ,KAAK,oBAAoB,KAAK,IAAI,KAAK,kBAAkB,KAAK,oBAAoB,CAAC;AAEjG,SAAK,KAAK,mBAAmB,KAAK,eAAe,KAAK,iBAAiB,GAAG;AAC1E,SAAK,KAAK,gBAAgB,EAAE,SAAS,KAAK,mBAAmB,OAAO;AAEpE,SAAK,iBAAiB,WAAW,MAAM;AACrC,UAAI,KAAK,iBAAiB;AACxB,aAAK,QAAO,EAAG,MAAM,SAAO,QAAQ,MAAM,iCAAiC,GAAG,CAAC;AAAA,MACjF;AAAA,IACF,GAAG,KAAK;AAAA,EACV;AAAA,EAEA,kBAAkB;AAChB,QAAI,CAAC,KAAK,aAAc;AAExB,SAAK,YAAY,YAAY,MAAM;AACjC,UAAI,KAAK,aAAa;AACpB,aAAK,KAAK,EAAE,QAAQ,OAAM,CAAE;AAC5B,aAAK,kBAAiB;AAAA,MACxB;AAAA,IACF,GAAG,KAAK,YAAY;AAAA,EACtB;AAAA,EAEA,oBAAoB;AAClB,SAAK,kBAAiB;AAEtB,SAAK,YAAY,WAAW,MAAM;AAChC,cAAQ,KAAK,+CAA+C;AAC5D,UAAI,KAAK,QAAQ;AACf,aAAK,OAAO,MAAM,MAAM,cAAc;AAAA,MACxC;AAAA,IACF,GAAG,KAAK,WAAW;AAAA,EACrB;AAAA,EAEA,oBAAoB;AAClB,QAAI,KAAK,WAAW;AAClB,mBAAa,KAAK,SAAS;AAC3B,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,eAAe;AACb,QAAI,KAAK,gBAAgB;AACvB,mBAAa,KAAK,cAAc;AAChC,WAAK,iBAAiB;AAAA,IACxB;AACA,QAAI,KAAK,WAAW;AAClB,oBAAc,KAAK,SAAS;AAC5B,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,kBAAiB;AAAA,EACxB;AAAA,EAEA,QAAQ,MAAM;AACZ,QAAI,KAAK,OAAO;AACd,cAAQ,IAAI,eAAe,GAAG,IAAI;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,OAAO,UAAU,SAAS,OAAO,iBAAiB;AAChD,QAAI,CAAC,QAAS,OAAM,IAAI,MAAM,qBAAqB;AAGnD,UAAM,MAAM,IAAI,IAAI,OAAO;AAG3B,QAAI,WAAW,IAAI,aAAa,WAAW,SAAS;AAGpD,QAAI,WAAW,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AAErD,WAAO,IAAI,SAAQ;AAAA,EACrB;AACF;AAGA,OAAO,OAAO,gBAAgB,WAAW,YAAY;"}
@@ -1,2 +1,2 @@
1
- "use strict";const t=require("./Rest-BIyXKaFn.js");class WebSocketClient{constructor(t={}){this.url=t.url,this.socket=null,this.isConnected=!1,this.isConnecting=!1,this.getToken=t.getToken||null,this.tokenPrefix=t.tokenPrefix||"bearer",this.shouldReconnect=!1!==t.autoReconnect,this.maxReconnectAttempts=t.maxReconnectAttempts||1/0,this.reconnectInterval=t.reconnectInterval||3e3,this.reconnectBackoff=t.reconnectBackoff||1.5,this.reconnectAttempts=0,this.reconnectTimer=null,this.pingInterval=t.pingInterval||3e4,this.pongTimeout=t.pongTimeout||5e3,this.pingTimer=null,this.pongTimer=null,this.debug=t.debug||!1}async connect(t=null){if(t&&(this.url=t),!this.url)throw new Error("WebSocket URL is required");if(!this.isConnected&&!this.isConnecting)return this.isConnecting=!0,this._log("Connecting to:",this.url),new Promise((t,e)=>{try{this.socket=new WebSocket(this.url),this.socket.onopen=()=>{this._log("Connected"),this.isConnected=!0,this.isConnecting=!1,this.reconnectAttempts=0,this._authenticate(),this._startHeartbeat(),this.emit("connected"),t()},this.socket.onmessage=t=>this._handleMessage(t),this.socket.onerror=t=>this._handleError(t,e),this.socket.onclose=t=>this._handleClose(t)}catch(n){this.isConnecting=!1,e(n)}})}disconnect(){this.shouldReconnect=!1,this._clearTimers(),this.socket&&(this._log("Disconnecting"),this.socket.close(1e3,"Client disconnect"))}send(t){if(!this.isConnected)throw new Error("WebSocket not connected");const e="string"==typeof t?t:JSON.stringify(t);this.socket.send(e),this._log("Sent:",e)}_authenticate(){const t=this.getToken?this.getToken():null;t?this.send({type:"authenticate",token:t,prefix:this.tokenPrefix}):console.warn("[WebSocket] No token available")}_handleMessage(t){let e;this._log("Received:",t.data);try{e=JSON.parse(t.data)}catch(n){e=t.data}"pong"!==e?.type?(e?.type&&this.emit(`message:${e.type}`,e),this.emit("message",e)):this._clearPongTimeout()}_handleError(t,e){console.error("[WebSocket] Error:",t),this.emit("error",t),e&&e(new Error("WebSocket connection failed"))}_handleClose(t){this._log("Closed:",t.code,t.reason),this.isConnected=!1,this.isConnecting=!1,this._clearTimers(),this.socket=null,this.emit("disconnected",{code:t.code,reason:t.reason,wasClean:t.wasClean}),this.shouldReconnect&&1e3!==t.code&&this._reconnect()}_reconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts)return this._log("Max reconnect attempts reached"),void this.emit("reconnect-failed",{attempts:this.reconnectAttempts});this.reconnectAttempts++;const t=this.reconnectInterval*Math.pow(this.reconnectBackoff,this.reconnectAttempts-1);this._log(`Reconnecting in ${t}ms (attempt ${this.reconnectAttempts})`),this.emit("reconnecting",{attempt:this.reconnectAttempts,delay:t}),this.reconnectTimer=setTimeout(()=>{this.shouldReconnect&&this.connect().catch(t=>console.error("[WebSocket] Reconnect failed:",t))},t)}_startHeartbeat(){this.pingInterval&&(this.pingTimer=setInterval(()=>{this.isConnected&&(this.send({action:"ping"}),this._startPongTimeout())},this.pingInterval))}_startPongTimeout(){this._clearPongTimeout(),this.pongTimer=setTimeout(()=>{console.warn("[WebSocket] Pong timeout - closing connection"),this.socket&&this.socket.close(1006,"Pong timeout")},this.pongTimeout)}_clearPongTimeout(){this.pongTimer&&(clearTimeout(this.pongTimer),this.pongTimer=null)}_clearTimers(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null),this._clearPongTimeout()}_log(...t){this.debug}static deriveURL(t,e="/ws/realtime/"){if(!t)throw new Error("baseURL is required");const n=new URL(t);return n.protocol="https:"===n.protocol?"wss:":"ws:",n.pathname=e.startsWith("/")?e:`/${e}`,n.toString()}}Object.assign(WebSocketClient.prototype,t.EventEmitter),exports.WebSocketClient=WebSocketClient;
2
- //# sourceMappingURL=WebSocketClient-sEN2DmCF.js.map
1
+ "use strict";const t=require("./Rest-DYPLEzNy.js");class WebSocketClient{constructor(t={}){this.url=t.url,this.socket=null,this.isConnected=!1,this.isConnecting=!1,this.getToken=t.getToken||null,this.tokenPrefix=t.tokenPrefix||"bearer",this.shouldReconnect=!1!==t.autoReconnect,this.maxReconnectAttempts=t.maxReconnectAttempts||1/0,this.reconnectInterval=t.reconnectInterval||3e3,this.reconnectBackoff=t.reconnectBackoff||1.5,this.reconnectAttempts=0,this.reconnectTimer=null,this.pingInterval=t.pingInterval||3e4,this.pongTimeout=t.pongTimeout||5e3,this.pingTimer=null,this.pongTimer=null,this.debug=t.debug||!1}async connect(t=null){if(t&&(this.url=t),!this.url)throw new Error("WebSocket URL is required");if(!this.isConnected&&!this.isConnecting)return this.isConnecting=!0,this._log("Connecting to:",this.url),new Promise((t,e)=>{try{this.socket=new WebSocket(this.url),this.socket.onopen=()=>{this._log("Connected"),this.isConnected=!0,this.isConnecting=!1,this.reconnectAttempts=0,this._authenticate(),this._startHeartbeat(),this.emit("connected"),t()},this.socket.onmessage=t=>this._handleMessage(t),this.socket.onerror=t=>this._handleError(t,e),this.socket.onclose=t=>this._handleClose(t)}catch(n){this.isConnecting=!1,e(n)}})}disconnect(){this.shouldReconnect=!1,this._clearTimers(),this.socket&&(this._log("Disconnecting"),this.socket.close(1e3,"Client disconnect"))}send(t){if(!this.isConnected)throw new Error("WebSocket not connected");const e="string"==typeof t?t:JSON.stringify(t);this.socket.send(e),this._log("Sent:",e)}_authenticate(){const t=this.getToken?this.getToken():null;t?this.send({type:"authenticate",token:t,prefix:this.tokenPrefix}):console.warn("[WebSocket] No token available")}_handleMessage(t){let e;this._log("Received:",t.data);try{e=JSON.parse(t.data)}catch(n){e=t.data}"pong"!==e?.type?(e?.type&&this.emit(`message:${e.type}`,e),this.emit("message",e)):this._clearPongTimeout()}_handleError(t,e){console.error("[WebSocket] Error:",t),this.emit("error",t),e&&e(new Error("WebSocket connection failed"))}_handleClose(t){this._log("Closed:",t.code,t.reason),this.isConnected=!1,this.isConnecting=!1,this._clearTimers(),this.socket=null,this.emit("disconnected",{code:t.code,reason:t.reason,wasClean:t.wasClean}),this.shouldReconnect&&1e3!==t.code&&this._reconnect()}_reconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts)return this._log("Max reconnect attempts reached"),void this.emit("reconnect-failed",{attempts:this.reconnectAttempts});this.reconnectAttempts++;const t=this.reconnectInterval*Math.pow(this.reconnectBackoff,this.reconnectAttempts-1);this._log(`Reconnecting in ${t}ms (attempt ${this.reconnectAttempts})`),this.emit("reconnecting",{attempt:this.reconnectAttempts,delay:t}),this.reconnectTimer=setTimeout(()=>{this.shouldReconnect&&this.connect().catch(t=>console.error("[WebSocket] Reconnect failed:",t))},t)}_startHeartbeat(){this.pingInterval&&(this.pingTimer=setInterval(()=>{this.isConnected&&(this.send({action:"ping"}),this._startPongTimeout())},this.pingInterval))}_startPongTimeout(){this._clearPongTimeout(),this.pongTimer=setTimeout(()=>{console.warn("[WebSocket] Pong timeout - closing connection"),this.socket&&this.socket.close(1006,"Pong timeout")},this.pongTimeout)}_clearPongTimeout(){this.pongTimer&&(clearTimeout(this.pongTimer),this.pongTimer=null)}_clearTimers(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null),this._clearPongTimeout()}_log(...t){this.debug}static deriveURL(t,e="/ws/realtime/"){if(!t)throw new Error("baseURL is required");const n=new URL(t);return n.protocol="https:"===n.protocol?"wss:":"ws:",n.pathname=e.startsWith("/")?e:`/${e}`,n.toString()}}Object.assign(WebSocketClient.prototype,t.EventEmitter),exports.WebSocketClient=WebSocketClient;
2
+ //# sourceMappingURL=WebSocketClient-Dzwprd15.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WebSocketClient-sEN2DmCF.js","sources":["../../src/core/services/WebSocketClient.js"],"sourcesContent":["/**\n * WebSocketClient - Simple, robust WebSocket client with auto-reconnect\n *\n * Features:\n * - Auto-reconnect with exponential backoff\n * - Heartbeat ping/pong with timeout disconnect\n * - Event-driven architecture using EventEmitter\n * - Token-based authentication\n *\n * Usage:\n * const ws = new WebSocketClient({\n * url: \"wss://api.example.com/ws/realtime\",\n * tokenPrefix: \"bearer\",\n * getToken: () => app.tokenManager.getToken()\n * });\n *\n * ws.on('connected', () => console.log('Connected!'));\n * ws.on('message', (data) => console.log('Received:', data));\n * ws.connect();\n */\n\nimport EventEmitter from '@core/mixins/EventEmitter.js';\n\nclass WebSocketClient {\n constructor(options = {}) {\n // Connection\n this.url = options.url;\n this.socket = null;\n this.isConnected = false;\n this.isConnecting = false;\n\n // Auth\n this.getToken = options.getToken || null;\n this.tokenPrefix = options.tokenPrefix || 'bearer';\n\n // Reconnection\n this.shouldReconnect = options.autoReconnect !== false;\n this.maxReconnectAttempts = options.maxReconnectAttempts || Infinity;\n this.reconnectInterval = options.reconnectInterval || 3000;\n this.reconnectBackoff = options.reconnectBackoff || 1.5;\n this.reconnectAttempts = 0;\n this.reconnectTimer = null;\n\n // Heartbeat\n this.pingInterval = options.pingInterval || 30000;\n this.pongTimeout = options.pongTimeout || 5000;\n this.pingTimer = null;\n this.pongTimer = null;\n\n // Debug\n this.debug = options.debug || false;\n }\n\n /**\n * Connect to WebSocket server\n */\n async connect(url = null) {\n if (url) this.url = url;\n if (!this.url) throw new Error('WebSocket URL is required');\n if (this.isConnected || this.isConnecting) return;\n\n this.isConnecting = true;\n this._log('Connecting to:', this.url);\n\n return new Promise((resolve, reject) => {\n try {\n this.socket = new WebSocket(this.url);\n\n this.socket.onopen = () => {\n this._log('Connected');\n this.isConnected = true;\n this.isConnecting = false;\n this.reconnectAttempts = 0;\n\n this._authenticate();\n this._startHeartbeat();\n\n this.emit('connected');\n resolve();\n };\n\n this.socket.onmessage = (event) => this._handleMessage(event);\n this.socket.onerror = (event) => this._handleError(event, reject);\n this.socket.onclose = (event) => this._handleClose(event);\n\n } catch (error) {\n this.isConnecting = false;\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from server\n */\n disconnect() {\n this.shouldReconnect = false;\n this._clearTimers();\n\n if (this.socket) {\n this._log('Disconnecting');\n this.socket.close(1000, 'Client disconnect');\n }\n }\n\n /**\n * Send data (auto-stringifies objects)\n */\n send(data) {\n if (!this.isConnected) {\n throw new Error('WebSocket not connected');\n }\n\n const message = typeof data === 'string' ? data : JSON.stringify(data);\n this.socket.send(message);\n this._log('Sent:', message);\n }\n\n // Private methods\n\n _authenticate() {\n const token = this.getToken ? this.getToken() : null;\n if (!token) {\n console.warn('[WebSocket] No token available');\n return;\n }\n\n this.send({\n type: 'authenticate',\n token,\n prefix: this.tokenPrefix\n });\n }\n\n _handleMessage(event) {\n this._log('Received:', event.data);\n\n let data;\n try {\n data = JSON.parse(event.data);\n } catch (error) {\n data = event.data;\n }\n\n // Handle pong response\n if (data?.type === 'pong') {\n this._clearPongTimeout();\n return;\n }\n\n // Emit specific event types\n if (data?.type) {\n this.emit(`message:${data.type}`, data);\n }\n\n // Always emit generic message event\n this.emit('message', data);\n }\n\n _handleError(event, rejectFn) {\n console.error('[WebSocket] Error:', event);\n this.emit('error', event);\n\n if (rejectFn) {\n rejectFn(new Error('WebSocket connection failed'));\n }\n }\n\n _handleClose(event) {\n this._log('Closed:', event.code, event.reason);\n\n this.isConnected = false;\n this.isConnecting = false;\n this._clearTimers();\n this.socket = null;\n\n this.emit('disconnected', {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean\n });\n\n // Auto-reconnect (except for clean closes)\n if (this.shouldReconnect && event.code !== 1000) {\n this._reconnect();\n }\n }\n\n _reconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this._log('Max reconnect attempts reached');\n this.emit('reconnect-failed', { attempts: this.reconnectAttempts });\n return;\n }\n\n this.reconnectAttempts++;\n const delay = this.reconnectInterval * Math.pow(this.reconnectBackoff, this.reconnectAttempts - 1);\n\n this._log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n this.emit('reconnecting', { attempt: this.reconnectAttempts, delay });\n\n this.reconnectTimer = setTimeout(() => {\n if (this.shouldReconnect) {\n this.connect().catch(err => console.error('[WebSocket] Reconnect failed:', err));\n }\n }, delay);\n }\n\n _startHeartbeat() {\n if (!this.pingInterval) return;\n\n this.pingTimer = setInterval(() => {\n if (this.isConnected) {\n this.send({ action: 'ping' });\n this._startPongTimeout();\n }\n }, this.pingInterval);\n }\n\n _startPongTimeout() {\n this._clearPongTimeout();\n\n this.pongTimer = setTimeout(() => {\n console.warn('[WebSocket] Pong timeout - closing connection');\n if (this.socket) {\n this.socket.close(1006, 'Pong timeout');\n }\n }, this.pongTimeout);\n }\n\n _clearPongTimeout() {\n if (this.pongTimer) {\n clearTimeout(this.pongTimer);\n this.pongTimer = null;\n }\n }\n\n _clearTimers() {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n this._clearPongTimeout();\n }\n\n _log(...args) {\n if (this.debug) {\n console.log('[WebSocket]', ...args);\n }\n }\n\n // Static methods\n\n /**\n * Convert REST API base URL to WebSocket URL\n * @param {string} baseURL - REST API base URL (http/https)\n * @param {string} path - WebSocket path (default: '/ws')\n * @returns {string} WebSocket URL (ws/wss)\n *\n * @example\n * WebSocketClient.deriveURL('https://api.example.com', '/ws/realtime')\n * // Returns: 'wss://api.example.com/ws/realtime'\n *\n * WebSocketClient.deriveURL('http://localhost:3000')\n * // Returns: 'ws://localhost:3000/ws'\n */\n static deriveURL(baseURL, path = '/ws/realtime/') {\n if (!baseURL) throw new Error('baseURL is required');\n\n // Parse the base URL\n const url = new URL(baseURL);\n\n // Convert http(s) to ws(s)\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';\n\n // Set the path (ensure it starts with /)\n url.pathname = path.startsWith('/') ? path : `/${path}`;\n\n return url.toString();\n }\n}\n\n// Add EventEmitter mixin\nObject.assign(WebSocketClient.prototype, EventEmitter);\n\nexport default WebSocketClient;\n"],"names":["WebSocketClient","constructor","options","this","url","socket","isConnected","isConnecting","getToken","tokenPrefix","shouldReconnect","autoReconnect","maxReconnectAttempts","Infinity","reconnectInterval","reconnectBackoff","reconnectAttempts","reconnectTimer","pingInterval","pongTimeout","pingTimer","pongTimer","debug","connect","Error","_log","Promise","resolve","reject","WebSocket","onopen","_authenticate","_startHeartbeat","emit","onmessage","event","_handleMessage","onerror","_handleError","onclose","_handleClose","error","disconnect","_clearTimers","close","send","data","message","JSON","stringify","token","type","prefix","console","warn","parse","_clearPongTimeout","rejectFn","code","reason","wasClean","_reconnect","attempts","delay","Math","pow","attempt","setTimeout","catch","err","setInterval","action","_startPongTimeout","clearTimeout","clearInterval","args","deriveURL","baseURL","path","URL","protocol","pathname","startsWith","toString","Object","assign","prototype","EventEmitter"],"mappings":"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"}
1
+ {"version":3,"file":"WebSocketClient-Dzwprd15.js","sources":["../../src/core/services/WebSocketClient.js"],"sourcesContent":["/**\n * WebSocketClient - Simple, robust WebSocket client with auto-reconnect\n *\n * Features:\n * - Auto-reconnect with exponential backoff\n * - Heartbeat ping/pong with timeout disconnect\n * - Event-driven architecture using EventEmitter\n * - Token-based authentication\n *\n * Usage:\n * const ws = new WebSocketClient({\n * url: \"wss://api.example.com/ws/realtime\",\n * tokenPrefix: \"bearer\",\n * getToken: () => app.tokenManager.getToken()\n * });\n *\n * ws.on('connected', () => console.log('Connected!'));\n * ws.on('message', (data) => console.log('Received:', data));\n * ws.connect();\n */\n\nimport EventEmitter from '@core/mixins/EventEmitter.js';\n\nclass WebSocketClient {\n constructor(options = {}) {\n // Connection\n this.url = options.url;\n this.socket = null;\n this.isConnected = false;\n this.isConnecting = false;\n\n // Auth\n this.getToken = options.getToken || null;\n this.tokenPrefix = options.tokenPrefix || 'bearer';\n\n // Reconnection\n this.shouldReconnect = options.autoReconnect !== false;\n this.maxReconnectAttempts = options.maxReconnectAttempts || Infinity;\n this.reconnectInterval = options.reconnectInterval || 3000;\n this.reconnectBackoff = options.reconnectBackoff || 1.5;\n this.reconnectAttempts = 0;\n this.reconnectTimer = null;\n\n // Heartbeat\n this.pingInterval = options.pingInterval || 30000;\n this.pongTimeout = options.pongTimeout || 5000;\n this.pingTimer = null;\n this.pongTimer = null;\n\n // Debug\n this.debug = options.debug || false;\n }\n\n /**\n * Connect to WebSocket server\n */\n async connect(url = null) {\n if (url) this.url = url;\n if (!this.url) throw new Error('WebSocket URL is required');\n if (this.isConnected || this.isConnecting) return;\n\n this.isConnecting = true;\n this._log('Connecting to:', this.url);\n\n return new Promise((resolve, reject) => {\n try {\n this.socket = new WebSocket(this.url);\n\n this.socket.onopen = () => {\n this._log('Connected');\n this.isConnected = true;\n this.isConnecting = false;\n this.reconnectAttempts = 0;\n\n this._authenticate();\n this._startHeartbeat();\n\n this.emit('connected');\n resolve();\n };\n\n this.socket.onmessage = (event) => this._handleMessage(event);\n this.socket.onerror = (event) => this._handleError(event, reject);\n this.socket.onclose = (event) => this._handleClose(event);\n\n } catch (error) {\n this.isConnecting = false;\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from server\n */\n disconnect() {\n this.shouldReconnect = false;\n this._clearTimers();\n\n if (this.socket) {\n this._log('Disconnecting');\n this.socket.close(1000, 'Client disconnect');\n }\n }\n\n /**\n * Send data (auto-stringifies objects)\n */\n send(data) {\n if (!this.isConnected) {\n throw new Error('WebSocket not connected');\n }\n\n const message = typeof data === 'string' ? data : JSON.stringify(data);\n this.socket.send(message);\n this._log('Sent:', message);\n }\n\n // Private methods\n\n _authenticate() {\n const token = this.getToken ? this.getToken() : null;\n if (!token) {\n console.warn('[WebSocket] No token available');\n return;\n }\n\n this.send({\n type: 'authenticate',\n token,\n prefix: this.tokenPrefix\n });\n }\n\n _handleMessage(event) {\n this._log('Received:', event.data);\n\n let data;\n try {\n data = JSON.parse(event.data);\n } catch (error) {\n data = event.data;\n }\n\n // Handle pong response\n if (data?.type === 'pong') {\n this._clearPongTimeout();\n return;\n }\n\n // Emit specific event types\n if (data?.type) {\n this.emit(`message:${data.type}`, data);\n }\n\n // Always emit generic message event\n this.emit('message', data);\n }\n\n _handleError(event, rejectFn) {\n console.error('[WebSocket] Error:', event);\n this.emit('error', event);\n\n if (rejectFn) {\n rejectFn(new Error('WebSocket connection failed'));\n }\n }\n\n _handleClose(event) {\n this._log('Closed:', event.code, event.reason);\n\n this.isConnected = false;\n this.isConnecting = false;\n this._clearTimers();\n this.socket = null;\n\n this.emit('disconnected', {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean\n });\n\n // Auto-reconnect (except for clean closes)\n if (this.shouldReconnect && event.code !== 1000) {\n this._reconnect();\n }\n }\n\n _reconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this._log('Max reconnect attempts reached');\n this.emit('reconnect-failed', { attempts: this.reconnectAttempts });\n return;\n }\n\n this.reconnectAttempts++;\n const delay = this.reconnectInterval * Math.pow(this.reconnectBackoff, this.reconnectAttempts - 1);\n\n this._log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n this.emit('reconnecting', { attempt: this.reconnectAttempts, delay });\n\n this.reconnectTimer = setTimeout(() => {\n if (this.shouldReconnect) {\n this.connect().catch(err => console.error('[WebSocket] Reconnect failed:', err));\n }\n }, delay);\n }\n\n _startHeartbeat() {\n if (!this.pingInterval) return;\n\n this.pingTimer = setInterval(() => {\n if (this.isConnected) {\n this.send({ action: 'ping' });\n this._startPongTimeout();\n }\n }, this.pingInterval);\n }\n\n _startPongTimeout() {\n this._clearPongTimeout();\n\n this.pongTimer = setTimeout(() => {\n console.warn('[WebSocket] Pong timeout - closing connection');\n if (this.socket) {\n this.socket.close(1006, 'Pong timeout');\n }\n }, this.pongTimeout);\n }\n\n _clearPongTimeout() {\n if (this.pongTimer) {\n clearTimeout(this.pongTimer);\n this.pongTimer = null;\n }\n }\n\n _clearTimers() {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n this._clearPongTimeout();\n }\n\n _log(...args) {\n if (this.debug) {\n console.log('[WebSocket]', ...args);\n }\n }\n\n // Static methods\n\n /**\n * Convert REST API base URL to WebSocket URL\n * @param {string} baseURL - REST API base URL (http/https)\n * @param {string} path - WebSocket path (default: '/ws')\n * @returns {string} WebSocket URL (ws/wss)\n *\n * @example\n * WebSocketClient.deriveURL('https://api.example.com', '/ws/realtime')\n * // Returns: 'wss://api.example.com/ws/realtime'\n *\n * WebSocketClient.deriveURL('http://localhost:3000')\n * // Returns: 'ws://localhost:3000/ws'\n */\n static deriveURL(baseURL, path = '/ws/realtime/') {\n if (!baseURL) throw new Error('baseURL is required');\n\n // Parse the base URL\n const url = new URL(baseURL);\n\n // Convert http(s) to ws(s)\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';\n\n // Set the path (ensure it starts with /)\n url.pathname = path.startsWith('/') ? path : `/${path}`;\n\n return url.toString();\n }\n}\n\n// Add EventEmitter mixin\nObject.assign(WebSocketClient.prototype, EventEmitter);\n\nexport default WebSocketClient;\n"],"names":["WebSocketClient","constructor","options","this","url","socket","isConnected","isConnecting","getToken","tokenPrefix","shouldReconnect","autoReconnect","maxReconnectAttempts","Infinity","reconnectInterval","reconnectBackoff","reconnectAttempts","reconnectTimer","pingInterval","pongTimeout","pingTimer","pongTimer","debug","connect","Error","_log","Promise","resolve","reject","WebSocket","onopen","_authenticate","_startHeartbeat","emit","onmessage","event","_handleMessage","onerror","_handleError","onclose","_handleClose","error","disconnect","_clearTimers","close","send","data","message","JSON","stringify","token","type","prefix","console","warn","parse","_clearPongTimeout","rejectFn","code","reason","wasClean","_reconnect","attempts","delay","Math","pow","attempt","setTimeout","catch","err","setInterval","action","_startPongTimeout","clearTimeout","clearInterval","args","deriveURL","baseURL","path","URL","protocol","pathname","startsWith","toString","Object","assign","prototype","EventEmitter"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";const O="2.1.1108",o="2025-11-26T18:41:30.376Z",i={full:O,major:2,minor:1,revision:1108,buildTime:o,toString(){return this.full},compare(O){const o=O=>O.split(".").map(Number),[i,r,t]=o(this.full),[e,n,s]=o(O);return i!==e?i-e:r!==n?r-n:t-s}};"undefined"!=typeof window&&(window.MOJO=window.MOJO||{},window.MOJO.VERSION=O,window.MOJO.VERSION_INFO=i,window.MOJO.version=O),exports.BUILD_TIME=o,exports.VERSION=O,exports.VERSION_INFO=i,exports.VERSION_MAJOR=2,exports.VERSION_MINOR=1,exports.VERSION_REVISION=1108;
2
- //# sourceMappingURL=version-DIk6UpNb.js.map
1
+ "use strict";const O="2.1.1110",o="2025-11-28T17:43:02.016Z",i={full:O,major:2,minor:1,revision:1110,buildTime:o,toString(){return this.full},compare(O){const o=O=>O.split(".").map(Number),[i,r,t]=o(this.full),[e,n,s]=o(O);return i!==e?i-e:r!==n?r-n:t-s}};"undefined"!=typeof window&&(window.MOJO=window.MOJO||{},window.MOJO.VERSION=O,window.MOJO.VERSION_INFO=i,window.MOJO.version=O),exports.BUILD_TIME=o,exports.VERSION=O,exports.VERSION_INFO=i,exports.VERSION_MAJOR=2,exports.VERSION_MINOR=1,exports.VERSION_REVISION=1110;
2
+ //# sourceMappingURL=version-C5lFa1F0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version-DIk6UpNb.js","sources":["../../src/version.js"],"sourcesContent":["/**\n * MOJO Framework Version Information\n * Auto-generated on 2025-11-26T18:41:30.376Z\n */\n\nexport const VERSION = '2.1.1108';\nexport const VERSION_MAJOR = 2;\nexport const VERSION_MINOR = 1;\nexport const VERSION_REVISION = 1108;\nexport const BUILD_TIME = '2025-11-26T18:41:30.376Z';\n\n// Version object for easy access\nexport const VERSION_INFO = {\n full: VERSION,\n major: VERSION_MAJOR,\n minor: VERSION_MINOR,\n revision: VERSION_REVISION,\n buildTime: BUILD_TIME,\n toString() {\n return this.full;\n },\n compare(other) {\n const parseVer = (v) => v.split('.').map(Number);\n const [a1, a2, a3] = parseVer(this.full);\n const [b1, b2, b3] = parseVer(other);\n\n if (a1 !== b1) return a1 - b1;\n if (a2 !== b2) return a2 - b2;\n return a3 - b3;\n }\n};\n\n// Make version globally available if in browser\nif (typeof window !== 'undefined') {\n window.MOJO = window.MOJO || {};\n window.MOJO.VERSION = VERSION;\n window.MOJO.VERSION_INFO = VERSION_INFO;\n\n // Also add to MOJO.version for convenience\n window.MOJO.version = VERSION;\n}\n\nexport default VERSION_INFO;\n"],"names":["VERSION","BUILD_TIME","VERSION_INFO","full","major","minor","revision","buildTime","toString","this","compare","other","parseVer","v","split","map","Number","a1","a2","a3","b1","b2","b3","window","MOJO","version"],"mappings":"aAKY,MAACA,EAAU,WAIVC,EAAa,2BAGbC,EAAe,CACxBC,KAAMH,EACNI,MARyB,EASzBC,MARyB,EASzBC,SAR4B,KAS5BC,UAAWN,EACX,QAAAO,GACI,OAAOC,KAAKN,IAChB,EACA,OAAAO,CAAQC,GACJ,MAAMC,EAAYC,GAAMA,EAAEC,MAAM,KAAKC,IAAIC,SAClCC,EAAIC,EAAIC,GAAMP,EAASH,KAAKN,OAC5BiB,EAAIC,EAAIC,GAAMV,EAASD,GAE9B,OAAIM,IAAOG,EAAWH,EAAKG,EACvBF,IAAOG,EAAWH,EAAKG,EACpBF,EAAKG,CAChB,GAIkB,oBAAXC,SACPA,OAAOC,KAAOD,OAAOC,MAAQ,CAAA,EAC7BD,OAAOC,KAAKxB,QAAUA,EACtBuB,OAAOC,KAAKtB,aAAeA,EAG3BqB,OAAOC,KAAKC,QAAUzB,uFAjCG,wBACA,2BACG"}
1
+ {"version":3,"file":"version-C5lFa1F0.js","sources":["../../src/version.js"],"sourcesContent":["/**\n * MOJO Framework Version Information\n * Auto-generated on 2025-11-28T17:43:02.016Z\n */\n\nexport const VERSION = '2.1.1110';\nexport const VERSION_MAJOR = 2;\nexport const VERSION_MINOR = 1;\nexport const VERSION_REVISION = 1110;\nexport const BUILD_TIME = '2025-11-28T17:43:02.016Z';\n\n// Version object for easy access\nexport const VERSION_INFO = {\n full: VERSION,\n major: VERSION_MAJOR,\n minor: VERSION_MINOR,\n revision: VERSION_REVISION,\n buildTime: BUILD_TIME,\n toString() {\n return this.full;\n },\n compare(other) {\n const parseVer = (v) => v.split('.').map(Number);\n const [a1, a2, a3] = parseVer(this.full);\n const [b1, b2, b3] = parseVer(other);\n\n if (a1 !== b1) return a1 - b1;\n if (a2 !== b2) return a2 - b2;\n return a3 - b3;\n }\n};\n\n// Make version globally available if in browser\nif (typeof window !== 'undefined') {\n window.MOJO = window.MOJO || {};\n window.MOJO.VERSION = VERSION;\n window.MOJO.VERSION_INFO = VERSION_INFO;\n\n // Also add to MOJO.version for convenience\n window.MOJO.version = VERSION;\n}\n\nexport default VERSION_INFO;\n"],"names":["VERSION","BUILD_TIME","VERSION_INFO","full","major","minor","revision","buildTime","toString","this","compare","other","parseVer","v","split","map","Number","a1","a2","a3","b1","b2","b3","window","MOJO","version"],"mappings":"aAKY,MAACA,EAAU,WAIVC,EAAa,2BAGbC,EAAe,CACxBC,KAAMH,EACNI,MARyB,EASzBC,MARyB,EASzBC,SAR4B,KAS5BC,UAAWN,EACX,QAAAO,GACI,OAAOC,KAAKN,IAChB,EACA,OAAAO,CAAQC,GACJ,MAAMC,EAAYC,GAAMA,EAAEC,MAAM,KAAKC,IAAIC,SAClCC,EAAIC,EAAIC,GAAMP,EAASH,KAAKN,OAC5BiB,EAAIC,EAAIC,GAAMV,EAASD,GAE9B,OAAIM,IAAOG,EAAWH,EAAKG,EACvBF,IAAOG,EAAWH,EAAKG,EACpBF,EAAKG,CAChB,GAIkB,oBAAXC,SACPA,OAAOC,KAAOD,OAAOC,MAAQ,CAAA,EAC7BD,OAAOC,KAAKxB,QAAUA,EACtBuB,OAAOC,KAAKtB,aAAeA,EAG3BqB,OAAOC,KAAKC,QAAUzB,uFAjCG,wBACA,2BACG"}
@@ -1,8 +1,8 @@
1
- const VERSION = "2.1.1108";
1
+ const VERSION = "2.1.1110";
2
2
  const VERSION_MAJOR = 2;
3
3
  const VERSION_MINOR = 1;
4
- const VERSION_REVISION = 1108;
5
- const BUILD_TIME = "2025-11-26T18:41:30.376Z";
4
+ const VERSION_REVISION = 1110;
5
+ const BUILD_TIME = "2025-11-28T17:43:02.016Z";
6
6
  const VERSION_INFO = {
7
7
  full: VERSION,
8
8
  major: VERSION_MAJOR,
@@ -35,4 +35,4 @@ export {
35
35
  VERSION_MINOR as c,
36
36
  VERSION_REVISION as d
37
37
  };
38
- //# sourceMappingURL=version-CrckCqr_.js.map
38
+ //# sourceMappingURL=version-FRgkiWti.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version-CrckCqr_.js","sources":["../../src/version.js"],"sourcesContent":["/**\n * MOJO Framework Version Information\n * Auto-generated on 2025-11-26T18:41:30.376Z\n */\n\nexport const VERSION = '2.1.1108';\nexport const VERSION_MAJOR = 2;\nexport const VERSION_MINOR = 1;\nexport const VERSION_REVISION = 1108;\nexport const BUILD_TIME = '2025-11-26T18:41:30.376Z';\n\n// Version object for easy access\nexport const VERSION_INFO = {\n full: VERSION,\n major: VERSION_MAJOR,\n minor: VERSION_MINOR,\n revision: VERSION_REVISION,\n buildTime: BUILD_TIME,\n toString() {\n return this.full;\n },\n compare(other) {\n const parseVer = (v) => v.split('.').map(Number);\n const [a1, a2, a3] = parseVer(this.full);\n const [b1, b2, b3] = parseVer(other);\n\n if (a1 !== b1) return a1 - b1;\n if (a2 !== b2) return a2 - b2;\n return a3 - b3;\n }\n};\n\n// Make version globally available if in browser\nif (typeof window !== 'undefined') {\n window.MOJO = window.MOJO || {};\n window.MOJO.VERSION = VERSION;\n window.MOJO.VERSION_INFO = VERSION_INFO;\n\n // Also add to MOJO.version for convenience\n window.MOJO.version = VERSION;\n}\n\nexport default VERSION_INFO;\n"],"names":[],"mappings":"AAKY,MAAC,UAAU;AACX,MAAC,gBAAgB;AACjB,MAAC,gBAAgB;AACjB,MAAC,mBAAmB;AACpB,MAAC,aAAa;AAGd,MAAC,eAAe;AAAA,EACxB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,WAAW;AAAA,EACX,WAAW;AACP,WAAO,KAAK;AAAA,EAChB;AAAA,EACA,QAAQ,OAAO;AACX,UAAM,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAC/C,UAAM,CAAC,IAAI,IAAI,EAAE,IAAI,SAAS,KAAK,IAAI;AACvC,UAAM,CAAC,IAAI,IAAI,EAAE,IAAI,SAAS,KAAK;AAEnC,QAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,QAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,WAAO,KAAK;AAAA,EAChB;AACJ;AAGA,IAAI,OAAO,WAAW,aAAa;AAC/B,SAAO,OAAO,OAAO,QAAQ,CAAA;AAC7B,SAAO,KAAK,UAAU;AACtB,SAAO,KAAK,eAAe;AAG3B,SAAO,KAAK,UAAU;AAC1B;"}
1
+ {"version":3,"file":"version-FRgkiWti.js","sources":["../../src/version.js"],"sourcesContent":["/**\n * MOJO Framework Version Information\n * Auto-generated on 2025-11-28T17:43:02.016Z\n */\n\nexport const VERSION = '2.1.1110';\nexport const VERSION_MAJOR = 2;\nexport const VERSION_MINOR = 1;\nexport const VERSION_REVISION = 1110;\nexport const BUILD_TIME = '2025-11-28T17:43:02.016Z';\n\n// Version object for easy access\nexport const VERSION_INFO = {\n full: VERSION,\n major: VERSION_MAJOR,\n minor: VERSION_MINOR,\n revision: VERSION_REVISION,\n buildTime: BUILD_TIME,\n toString() {\n return this.full;\n },\n compare(other) {\n const parseVer = (v) => v.split('.').map(Number);\n const [a1, a2, a3] = parseVer(this.full);\n const [b1, b2, b3] = parseVer(other);\n\n if (a1 !== b1) return a1 - b1;\n if (a2 !== b2) return a2 - b2;\n return a3 - b3;\n }\n};\n\n// Make version globally available if in browser\nif (typeof window !== 'undefined') {\n window.MOJO = window.MOJO || {};\n window.MOJO.VERSION = VERSION;\n window.MOJO.VERSION_INFO = VERSION_INFO;\n\n // Also add to MOJO.version for convenience\n window.MOJO.version = VERSION;\n}\n\nexport default VERSION_INFO;\n"],"names":[],"mappings":"AAKY,MAAC,UAAU;AACX,MAAC,gBAAgB;AACjB,MAAC,gBAAgB;AACjB,MAAC,mBAAmB;AACpB,MAAC,aAAa;AAGd,MAAC,eAAe;AAAA,EACxB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,WAAW;AAAA,EACX,WAAW;AACP,WAAO,KAAK;AAAA,EAChB;AAAA,EACA,QAAQ,OAAO;AACX,UAAM,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAC/C,UAAM,CAAC,IAAI,IAAI,EAAE,IAAI,SAAS,KAAK,IAAI;AACvC,UAAM,CAAC,IAAI,IAAI,EAAE,IAAI,SAAS,KAAK;AAEnC,QAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,QAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,WAAO,KAAK;AAAA,EAChB;AACJ;AAGA,IAAI,OAAO,WAAW,aAAa;AAC/B,SAAO,OAAO,OAAO,QAAQ,CAAA;AAC7B,SAAO,KAAK,UAAU;AACtB,SAAO,KAAK,eAAe;AAG3B,SAAO,KAAK,UAAU;AAC1B;"}
package/dist/core.css CHANGED
@@ -1764,32 +1764,34 @@
1764
1764
  GroupSearchView - Hierarchical Tree Styles
1765
1765
  ============================================ */
1766
1766
 
1767
+ /* Hide chevron in group search - it's a nav tree, not a menu */
1768
+ .group-search-view .bi-chevron-right {
1769
+ display: none !important;
1770
+ }
1771
+
1772
+ /* Tree item wrapper - flat list with visual hierarchy */
1767
1773
  .group-search-view .tree-item-wrapper {
1768
1774
  display: flex;
1769
1775
  align-items: center;
1770
1776
  position: relative;
1771
1777
  }
1772
1778
 
1773
- .group-search-view .tree-indent {
1774
- position: relative;
1779
+ /* Container for tree line segments */
1780
+ .group-search-view .tree-lines {
1781
+ display: flex;
1775
1782
  flex-shrink: 0;
1776
1783
  align-self: stretch;
1777
1784
  }
1778
1785
 
1779
- .group-search-view .tree-connector,
1780
- .group-search-view .tree-connector-last {
1781
- position: absolute;
1782
- left: 0;
1783
- top: 0;
1784
- bottom: 0;
1785
- width: 100%;
1786
- height: 100%;
1787
- display: block;
1788
- pointer-events: none;
1786
+ /* Each segment is a fixed width column */
1787
+ .group-search-view .tree-seg {
1788
+ width: 24px;
1789
+ position: relative;
1790
+ align-self: stretch;
1789
1791
  }
1790
1792
 
1791
- /* Vertical line (├ has full height, only to middle) */
1792
- .group-search-view .tree-connector::before {
1793
+ /* Vertical line segment - continues through (for ancestors that aren't last) */
1794
+ .group-search-view .tree-seg-vert::before {
1793
1795
  content: "";
1794
1796
  position: absolute;
1795
1797
  left: 12px;
@@ -1799,7 +1801,8 @@
1799
1801
  background: var(--bs-border-color);
1800
1802
  }
1801
1803
 
1802
- .group-search-view .tree-connector-last::before {
1804
+ /* Vertical line segment ending - only draws top half (for last item in branch) */
1805
+ .group-search-view .tree-seg-vert-end::before {
1803
1806
  content: "";
1804
1807
  position: absolute;
1805
1808
  left: 12px;
@@ -1809,49 +1812,59 @@
1809
1812
  background: var(--bs-border-color);
1810
1813
  }
1811
1814
 
1812
- /* Horizontal line (── part) */
1813
- .group-search-view .tree-connector::after,
1814
- .group-search-view .tree-connector-last::after {
1815
+ /* Middle segment - vertical line full height + horizontal branch */
1816
+ .group-search-view .tree-seg-mid::before {
1817
+ content: "";
1818
+ position: absolute;
1819
+ left: 12px;
1820
+ top: 0;
1821
+ bottom: 0;
1822
+ width: 2px;
1823
+ background: var(--bs-border-color);
1824
+ }
1825
+ .group-search-view .tree-seg-mid::after {
1815
1826
  content: "";
1816
1827
  position: absolute;
1817
1828
  left: 12px;
1818
1829
  top: 50%;
1819
- width: 35px;
1830
+ width: 12px;
1820
1831
  height: 2px;
1821
1832
  background: var(--bs-border-color);
1822
1833
  }
1823
1834
 
1824
- .group-search-view .tree-item-expand {
1825
- flex-shrink: 0;
1826
- width: 24px;
1827
- display: flex;
1828
- align-items: center;
1829
- justify-content: center;
1835
+ /* Last segment - vertical line stops at middle + horizontal branch */
1836
+ .group-search-view .tree-seg-last::before {
1837
+ content: "";
1838
+ position: absolute;
1839
+ left: 12px;
1840
+ top: 0;
1841
+ height: 50%;
1842
+ width: 2px;
1843
+ background: var(--bs-border-color);
1830
1844
  }
1831
-
1832
- .group-search-view .tree-expand-btn {
1833
- background: none;
1834
- border: none;
1835
- padding: 4px;
1836
- cursor: pointer;
1837
- color: var(--bs-secondary);
1838
- display: flex;
1839
- align-items: center;
1840
- justify-content: center;
1841
- transition: color 0.15s ease;
1845
+ .group-search-view .tree-seg-last::after {
1846
+ content: "";
1847
+ position: absolute;
1848
+ left: 12px;
1849
+ top: 50%;
1850
+ width: 12px;
1851
+ height: 2px;
1852
+ background: var(--bs-border-color);
1842
1853
  }
1843
1854
 
1844
- .group-search-view .tree-expand-btn:hover {
1845
- color: var(--bs-primary);
1855
+ /* Bold text for parents with children */
1856
+ .group-search-view .tree-item-wrapper.has-children .tree-item-name {
1857
+ font-weight: 600;
1846
1858
  }
1847
1859
 
1848
- .group-search-view .tree-expand-btn i {
1849
- font-size: 0.875rem;
1860
+ /* Hover state - highlight text */
1861
+ .group-search-view .simple-search-item:hover .tree-item-name {
1862
+ color: var(--bs-primary);
1850
1863
  }
1851
1864
 
1852
- .group-search-view .tree-expand-spacer {
1853
- display: inline-block;
1854
- width: 24px;
1865
+ .group-search-view .simple-search-item:hover .tree-item-kind {
1866
+ background: var(--bs-primary);
1867
+ color: white;
1855
1868
  }
1856
1869
 
1857
1870
  .group-search-view .tree-item-body {
@@ -1890,16 +1903,9 @@
1890
1903
  .group-search-view .simple-search-item {
1891
1904
  cursor: pointer;
1892
1905
  transition: background-color 0.15s ease;
1906
+ /*border: 1px dotted #efefef;*/
1893
1907
  }
1894
1908
 
1895
1909
  .group-search-view .simple-search-item:hover {
1896
- background-color: var(--bs-primary-border-subtle);
1897
- }
1898
-
1899
- .group-search-view .simple-search-item .bi-chevron-right {
1900
- opacity: 0.5;
1901
- }
1902
-
1903
- .group-search-view .simple-search-item:hover .bi-chevron-right {
1904
- opacity: 1;
1910
+ background-color: var(--bs-light);
1905
1911
  }