@vib3code/sdk 2.0.1

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 (258) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
  3. package/DOCS/CI_TESTING.md +38 -0
  4. package/DOCS/CLI_ONBOARDING.md +75 -0
  5. package/DOCS/CONTROL_REFERENCE.md +64 -0
  6. package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
  7. package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
  8. package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
  9. package/DOCS/ENV_SETUP.md +189 -0
  10. package/DOCS/EXPORT_FORMATS.md +417 -0
  11. package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
  12. package/DOCS/LICENSING_TIERS.md +275 -0
  13. package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
  14. package/DOCS/OBS_SETUP_GUIDE.md +98 -0
  15. package/DOCS/PROJECT_SETUP.md +66 -0
  16. package/DOCS/RENDERER_LIFECYCLE.md +40 -0
  17. package/DOCS/REPO_MANIFEST.md +121 -0
  18. package/DOCS/SESSION_014_PLAN.md +195 -0
  19. package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
  20. package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
  21. package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
  22. package/DOCS/SYSTEM_INVENTORY.md +520 -0
  23. package/DOCS/TELEMETRY_EXPORTS.md +34 -0
  24. package/DOCS/WEBGPU_STATUS.md +38 -0
  25. package/DOCS/XR_BENCHMARKS.md +608 -0
  26. package/LICENSE +21 -0
  27. package/README.md +426 -0
  28. package/docs/.nojekyll +0 -0
  29. package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
  30. package/docs/02-hyperspatial_ego_death.html +346 -0
  31. package/docs/03-post_cartesian_sublime.html +346 -0
  32. package/docs/04-crystalline_void_meditation.html +346 -0
  33. package/docs/05-quantum_decoherence_ballet.html +346 -0
  34. package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
  35. package/docs/07-hyperspatial_ego_death.html +346 -0
  36. package/docs/08-post_cartesian_sublime.html +346 -0
  37. package/docs/09-crystalline_void_meditation.html +346 -0
  38. package/docs/10-quantum_decoherence_ballet.html +346 -0
  39. package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
  40. package/docs/12-hyperspatial_ego_death.html +346 -0
  41. package/docs/13-post_cartesian_sublime.html +346 -0
  42. package/docs/index.html +794 -0
  43. package/docs/test-hub.html +441 -0
  44. package/docs/url-state.js +102 -0
  45. package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
  46. package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
  47. package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
  48. package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
  49. package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
  50. package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
  51. package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
  52. package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
  53. package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
  54. package/docs/vib3-exports/index.html +238 -0
  55. package/docs/webgpu-live.html +702 -0
  56. package/package.json +367 -0
  57. package/src/advanced/AIPresetGenerator.js +777 -0
  58. package/src/advanced/MIDIController.js +703 -0
  59. package/src/advanced/OffscreenWorker.js +1051 -0
  60. package/src/advanced/WebGPUCompute.js +1051 -0
  61. package/src/advanced/WebXRRenderer.js +680 -0
  62. package/src/agent/cli/AgentCLI.js +615 -0
  63. package/src/agent/cli/index.js +14 -0
  64. package/src/agent/index.js +73 -0
  65. package/src/agent/mcp/MCPServer.js +950 -0
  66. package/src/agent/mcp/index.js +9 -0
  67. package/src/agent/mcp/tools.js +548 -0
  68. package/src/agent/telemetry/EventStream.js +669 -0
  69. package/src/agent/telemetry/Instrumentation.js +618 -0
  70. package/src/agent/telemetry/TelemetryExporters.js +427 -0
  71. package/src/agent/telemetry/TelemetryService.js +464 -0
  72. package/src/agent/telemetry/index.js +52 -0
  73. package/src/benchmarks/BenchmarkRunner.js +381 -0
  74. package/src/benchmarks/MetricsCollector.js +299 -0
  75. package/src/benchmarks/index.js +9 -0
  76. package/src/benchmarks/scenes.js +259 -0
  77. package/src/cli/index.js +675 -0
  78. package/src/config/ApiConfig.js +88 -0
  79. package/src/core/CanvasManager.js +217 -0
  80. package/src/core/ErrorReporter.js +117 -0
  81. package/src/core/ParameterMapper.js +333 -0
  82. package/src/core/Parameters.js +396 -0
  83. package/src/core/RendererContracts.js +200 -0
  84. package/src/core/UnifiedResourceManager.js +370 -0
  85. package/src/core/VIB3Engine.js +636 -0
  86. package/src/core/renderers/FacetedRendererAdapter.js +32 -0
  87. package/src/core/renderers/HolographicRendererAdapter.js +29 -0
  88. package/src/core/renderers/QuantumRendererAdapter.js +29 -0
  89. package/src/core/renderers/RendererLifecycleManager.js +63 -0
  90. package/src/creative/ColorPresetsSystem.js +980 -0
  91. package/src/creative/ParameterTimeline.js +1061 -0
  92. package/src/creative/PostProcessingPipeline.js +1113 -0
  93. package/src/creative/TransitionAnimator.js +683 -0
  94. package/src/export/CSSExporter.js +226 -0
  95. package/src/export/CardGeneratorBase.js +279 -0
  96. package/src/export/ExportManager.js +580 -0
  97. package/src/export/FacetedCardGenerator.js +279 -0
  98. package/src/export/HolographicCardGenerator.js +543 -0
  99. package/src/export/LottieExporter.js +552 -0
  100. package/src/export/QuantumCardGenerator.js +315 -0
  101. package/src/export/SVGExporter.js +519 -0
  102. package/src/export/ShaderExporter.js +903 -0
  103. package/src/export/TradingCardGenerator.js +3055 -0
  104. package/src/export/TradingCardManager.js +181 -0
  105. package/src/export/VIB3PackageExporter.js +559 -0
  106. package/src/export/index.js +14 -0
  107. package/src/export/systems/TradingCardSystemFaceted.js +494 -0
  108. package/src/export/systems/TradingCardSystemHolographic.js +452 -0
  109. package/src/export/systems/TradingCardSystemQuantum.js +411 -0
  110. package/src/faceted/FacetedSystem.js +963 -0
  111. package/src/features/CollectionManager.js +433 -0
  112. package/src/gallery/CollectionManager.js +240 -0
  113. package/src/gallery/GallerySystem.js +485 -0
  114. package/src/geometry/GeometryFactory.js +314 -0
  115. package/src/geometry/GeometryLibrary.js +72 -0
  116. package/src/geometry/buffers/BufferBuilder.js +338 -0
  117. package/src/geometry/buffers/index.js +18 -0
  118. package/src/geometry/generators/Crystal.js +420 -0
  119. package/src/geometry/generators/Fractal.js +298 -0
  120. package/src/geometry/generators/KleinBottle.js +197 -0
  121. package/src/geometry/generators/Sphere.js +192 -0
  122. package/src/geometry/generators/Tesseract.js +160 -0
  123. package/src/geometry/generators/Tetrahedron.js +225 -0
  124. package/src/geometry/generators/Torus.js +304 -0
  125. package/src/geometry/generators/Wave.js +341 -0
  126. package/src/geometry/index.js +142 -0
  127. package/src/geometry/warp/HypersphereCore.js +211 -0
  128. package/src/geometry/warp/HypertetraCore.js +386 -0
  129. package/src/geometry/warp/index.js +57 -0
  130. package/src/holograms/HolographicVisualizer.js +1073 -0
  131. package/src/holograms/RealHolographicSystem.js +966 -0
  132. package/src/holograms/variantRegistry.js +69 -0
  133. package/src/integrations/FigmaPlugin.js +854 -0
  134. package/src/integrations/OBSMode.js +754 -0
  135. package/src/integrations/ThreeJsPackage.js +660 -0
  136. package/src/integrations/TouchDesignerExport.js +552 -0
  137. package/src/integrations/frameworks/Vib3React.js +591 -0
  138. package/src/integrations/frameworks/Vib3Svelte.js +654 -0
  139. package/src/integrations/frameworks/Vib3Vue.js +628 -0
  140. package/src/llm/LLMParameterInterface.js +240 -0
  141. package/src/llm/LLMParameterUI.js +577 -0
  142. package/src/math/Mat4x4.js +708 -0
  143. package/src/math/Projection.js +341 -0
  144. package/src/math/Rotor4D.js +637 -0
  145. package/src/math/Vec4.js +476 -0
  146. package/src/math/constants.js +164 -0
  147. package/src/math/index.js +68 -0
  148. package/src/math/projections.js +54 -0
  149. package/src/math/rotations.js +196 -0
  150. package/src/quantum/QuantumEngine.js +906 -0
  151. package/src/quantum/QuantumVisualizer.js +1103 -0
  152. package/src/reactivity/ReactivityConfig.js +499 -0
  153. package/src/reactivity/ReactivityManager.js +586 -0
  154. package/src/reactivity/SpatialInputSystem.js +1783 -0
  155. package/src/reactivity/index.js +93 -0
  156. package/src/render/CommandBuffer.js +465 -0
  157. package/src/render/MultiCanvasBridge.js +340 -0
  158. package/src/render/RenderCommand.js +514 -0
  159. package/src/render/RenderResourceRegistry.js +523 -0
  160. package/src/render/RenderState.js +552 -0
  161. package/src/render/RenderTarget.js +512 -0
  162. package/src/render/ShaderLoader.js +253 -0
  163. package/src/render/ShaderProgram.js +599 -0
  164. package/src/render/UnifiedRenderBridge.js +496 -0
  165. package/src/render/backends/WebGLBackend.js +1108 -0
  166. package/src/render/backends/WebGPUBackend.js +1409 -0
  167. package/src/render/commands/CommandBufferExecutor.js +607 -0
  168. package/src/render/commands/RenderCommandBuffer.js +661 -0
  169. package/src/render/commands/index.js +17 -0
  170. package/src/render/index.js +367 -0
  171. package/src/scene/Disposable.js +498 -0
  172. package/src/scene/MemoryPool.js +618 -0
  173. package/src/scene/Node4D.js +697 -0
  174. package/src/scene/ResourceManager.js +599 -0
  175. package/src/scene/Scene4D.js +540 -0
  176. package/src/scene/index.js +98 -0
  177. package/src/schemas/error.schema.json +84 -0
  178. package/src/schemas/extension.schema.json +88 -0
  179. package/src/schemas/index.js +214 -0
  180. package/src/schemas/parameters.schema.json +142 -0
  181. package/src/schemas/tool-pack.schema.json +44 -0
  182. package/src/schemas/tool-response.schema.json +127 -0
  183. package/src/shaders/common/fullscreen.vert.glsl +5 -0
  184. package/src/shaders/common/fullscreen.vert.wgsl +17 -0
  185. package/src/shaders/common/geometry24.glsl +65 -0
  186. package/src/shaders/common/geometry24.wgsl +54 -0
  187. package/src/shaders/common/rotation4d.glsl +85 -0
  188. package/src/shaders/common/rotation4d.wgsl +86 -0
  189. package/src/shaders/common/uniforms.glsl +44 -0
  190. package/src/shaders/common/uniforms.wgsl +48 -0
  191. package/src/shaders/faceted/faceted.frag.glsl +129 -0
  192. package/src/shaders/faceted/faceted.frag.wgsl +164 -0
  193. package/src/shaders/holographic/holographic.frag.glsl +406 -0
  194. package/src/shaders/holographic/holographic.frag.wgsl +185 -0
  195. package/src/shaders/quantum/quantum.frag.glsl +513 -0
  196. package/src/shaders/quantum/quantum.frag.wgsl +361 -0
  197. package/src/testing/ParallelTestFramework.js +519 -0
  198. package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
  199. package/src/testing/exportFormats.test.js +8 -0
  200. package/src/testing/projections.test.js +14 -0
  201. package/src/testing/rotations.test.js +37 -0
  202. package/src/ui/InteractivityMenu.js +516 -0
  203. package/src/ui/StatusManager.js +96 -0
  204. package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
  205. package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
  206. package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
  207. package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
  208. package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
  209. package/src/variations/VariationManager.js +431 -0
  210. package/src/viewer/AudioReactivity.js +505 -0
  211. package/src/viewer/CardBending.js +481 -0
  212. package/src/viewer/GalleryUI.js +832 -0
  213. package/src/viewer/ReactivityManager.js +590 -0
  214. package/src/viewer/TradingCardExporter.js +600 -0
  215. package/src/viewer/ViewerPortal.js +374 -0
  216. package/src/viewer/index.js +12 -0
  217. package/src/wasm/WasmLoader.js +296 -0
  218. package/src/wasm/index.js +132 -0
  219. package/tools/agentic/mcpTools.js +88 -0
  220. package/tools/cli/agent-cli.js +92 -0
  221. package/tools/export/formats.js +24 -0
  222. package/tools/math/rotation-baseline.mjs +64 -0
  223. package/tools/shader-sync-verify.js +937 -0
  224. package/tools/telemetry/manifestPipeline.js +141 -0
  225. package/tools/telemetry/telemetryEvents.js +35 -0
  226. package/types/adaptive-sdk.d.ts +185 -0
  227. package/types/advanced/AIPresetGenerator.d.ts +81 -0
  228. package/types/advanced/MIDIController.d.ts +100 -0
  229. package/types/advanced/OffscreenWorker.d.ts +82 -0
  230. package/types/advanced/WebGPUCompute.d.ts +52 -0
  231. package/types/advanced/WebXRRenderer.d.ts +77 -0
  232. package/types/advanced/index.d.ts +46 -0
  233. package/types/core/ErrorReporter.d.ts +50 -0
  234. package/types/core/VIB3Engine.d.ts +204 -0
  235. package/types/creative/ColorPresetsSystem.d.ts +91 -0
  236. package/types/creative/ParameterTimeline.d.ts +74 -0
  237. package/types/creative/PostProcessingPipeline.d.ts +109 -0
  238. package/types/creative/TransitionAnimator.d.ts +71 -0
  239. package/types/creative/index.d.ts +35 -0
  240. package/types/integrations/FigmaPlugin.d.ts +46 -0
  241. package/types/integrations/OBSMode.d.ts +74 -0
  242. package/types/integrations/ThreeJsPackage.d.ts +62 -0
  243. package/types/integrations/TouchDesignerExport.d.ts +36 -0
  244. package/types/integrations/Vib3React.d.ts +74 -0
  245. package/types/integrations/Vib3Svelte.d.ts +63 -0
  246. package/types/integrations/Vib3Vue.d.ts +55 -0
  247. package/types/integrations/index.d.ts +52 -0
  248. package/types/reactivity/SpatialInputSystem.d.ts +173 -0
  249. package/types/reactivity/index.d.ts +394 -0
  250. package/types/render/CommandBuffer.d.ts +169 -0
  251. package/types/render/RenderCommand.d.ts +312 -0
  252. package/types/render/RenderState.d.ts +279 -0
  253. package/types/render/RenderTarget.d.ts +254 -0
  254. package/types/render/ShaderProgram.d.ts +277 -0
  255. package/types/render/UnifiedRenderBridge.d.ts +143 -0
  256. package/types/render/WebGLBackend.d.ts +168 -0
  257. package/types/render/WebGPUBackend.d.ts +186 -0
  258. package/types/render/index.d.ts +141 -0
@@ -0,0 +1,669 @@
1
+ /**
2
+ * EventStream.js - Server-Sent Events (SSE) System for Real-time Telemetry
3
+ *
4
+ * Provides real-time streaming of telemetry events to connected agents.
5
+ * Supports multiple channels, filtering, and backpressure handling.
6
+ */
7
+
8
+ import { EventEmitter } from 'events';
9
+
10
+ /**
11
+ * Event types for the stream
12
+ */
13
+ export const StreamEventType = {
14
+ TELEMETRY: 'telemetry',
15
+ SPAN_START: 'span:start',
16
+ SPAN_END: 'span:end',
17
+ METRIC: 'metric',
18
+ ERROR: 'error',
19
+ HEARTBEAT: 'heartbeat',
20
+ SYSTEM: 'system'
21
+ };
22
+
23
+ /**
24
+ * SSE Event Stream Server
25
+ *
26
+ * Creates an SSE endpoint for streaming telemetry to agents.
27
+ */
28
+ export class EventStreamServer extends EventEmitter {
29
+ constructor(options = {}) {
30
+ super();
31
+
32
+ /** @type {Map<string, SSEConnection>} */
33
+ this.connections = new Map();
34
+
35
+ /** @type {number} Connection ID counter */
36
+ this.connectionIdCounter = 0;
37
+
38
+ /** @type {number} Heartbeat interval (ms) */
39
+ this.heartbeatInterval = options.heartbeatInterval || 30000;
40
+
41
+ /** @type {number} Max connections */
42
+ this.maxConnections = options.maxConnections || 100;
43
+
44
+ /** @type {number} Buffer size for each connection */
45
+ this.bufferSize = options.bufferSize || 100;
46
+
47
+ /** @type {boolean} Enable compression */
48
+ this.enableCompression = options.enableCompression || false;
49
+
50
+ /** @type {NodeJS.Timeout|null} */
51
+ this._heartbeatTimer = null;
52
+
53
+ /** @type {Array<{event: object, timestamp: number}>} */
54
+ this._replayBuffer = [];
55
+
56
+ /** @type {number} Replay buffer max size */
57
+ this._replayBufferSize = options.replayBufferSize || 1000;
58
+
59
+ this._startHeartbeat();
60
+ }
61
+
62
+ /**
63
+ * Handle incoming SSE connection request
64
+ * @param {object} req - HTTP request
65
+ * @param {object} res - HTTP response
66
+ * @param {object} options - Connection options
67
+ * @returns {string} Connection ID
68
+ */
69
+ handleConnection(req, res, options = {}) {
70
+ if (this.connections.size >= this.maxConnections) {
71
+ res.writeHead(503, { 'Content-Type': 'text/plain' });
72
+ res.end('Max connections reached');
73
+ return null;
74
+ }
75
+
76
+ const connectionId = `conn_${++this.connectionIdCounter}_${Date.now()}`;
77
+
78
+ // Set SSE headers
79
+ res.writeHead(200, {
80
+ 'Content-Type': 'text/event-stream',
81
+ 'Cache-Control': 'no-cache',
82
+ 'Connection': 'keep-alive',
83
+ 'Access-Control-Allow-Origin': options.cors || '*',
84
+ 'X-Accel-Buffering': 'no' // Disable nginx buffering
85
+ });
86
+
87
+ // Create connection object
88
+ const connection = new SSEConnection({
89
+ id: connectionId,
90
+ response: res,
91
+ filters: options.filters || {},
92
+ channels: options.channels || ['*'],
93
+ bufferSize: this.bufferSize
94
+ });
95
+
96
+ this.connections.set(connectionId, connection);
97
+
98
+ // Send initial connection event
99
+ connection.send({
100
+ type: StreamEventType.SYSTEM,
101
+ event: 'connected',
102
+ connectionId,
103
+ timestamp: new Date().toISOString()
104
+ });
105
+
106
+ // Handle replay request
107
+ if (options.replay && options.lastEventId) {
108
+ this._replayEvents(connection, options.lastEventId);
109
+ }
110
+
111
+ // Handle disconnect
112
+ req.on('close', () => {
113
+ this.connections.delete(connectionId);
114
+ this.emit('disconnect', connectionId);
115
+ });
116
+
117
+ this.emit('connect', connectionId, connection);
118
+ return connectionId;
119
+ }
120
+
121
+ /**
122
+ * Broadcast event to all connections
123
+ * @param {object} event - Event to broadcast
124
+ * @param {string} channel - Optional channel filter
125
+ */
126
+ broadcast(event, channel = 'default') {
127
+ const envelope = {
128
+ id: `evt_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
129
+ channel,
130
+ timestamp: new Date().toISOString(),
131
+ ...event
132
+ };
133
+
134
+ // Add to replay buffer
135
+ this._addToReplayBuffer(envelope);
136
+
137
+ // Send to all matching connections
138
+ for (const connection of this.connections.values()) {
139
+ if (connection.matchesChannel(channel) && connection.matchesFilters(event)) {
140
+ connection.send(envelope);
141
+ }
142
+ }
143
+
144
+ this.emit('broadcast', envelope);
145
+ }
146
+
147
+ /**
148
+ * Send event to specific connection
149
+ * @param {string} connectionId
150
+ * @param {object} event
151
+ */
152
+ sendTo(connectionId, event) {
153
+ const connection = this.connections.get(connectionId);
154
+ if (connection) {
155
+ connection.send({
156
+ id: `evt_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
157
+ timestamp: new Date().toISOString(),
158
+ ...event
159
+ });
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Stream telemetry event
165
+ * @param {object} telemetryEvent - From TelemetryService
166
+ */
167
+ streamTelemetry(telemetryEvent) {
168
+ this.broadcast({
169
+ type: StreamEventType.TELEMETRY,
170
+ data: telemetryEvent
171
+ }, 'telemetry');
172
+ }
173
+
174
+ /**
175
+ * Stream span start
176
+ * @param {object} span
177
+ */
178
+ streamSpanStart(span) {
179
+ this.broadcast({
180
+ type: StreamEventType.SPAN_START,
181
+ data: {
182
+ traceId: span.traceId,
183
+ spanId: span.spanId,
184
+ parentSpanId: span.parentSpanId,
185
+ name: span.name,
186
+ startTime: span.startTime,
187
+ attributes: span.attributes
188
+ }
189
+ }, 'spans');
190
+ }
191
+
192
+ /**
193
+ * Stream span end
194
+ * @param {object} span
195
+ */
196
+ streamSpanEnd(span) {
197
+ this.broadcast({
198
+ type: StreamEventType.SPAN_END,
199
+ data: {
200
+ traceId: span.traceId,
201
+ spanId: span.spanId,
202
+ name: span.name,
203
+ status: span.status,
204
+ durationMs: span.durationMs,
205
+ endTime: span.endTime
206
+ }
207
+ }, 'spans');
208
+ }
209
+
210
+ /**
211
+ * Stream metric update
212
+ * @param {string} name - Metric name
213
+ * @param {number} value - Metric value
214
+ * @param {object} labels - Metric labels
215
+ */
216
+ streamMetric(name, value, labels = {}) {
217
+ this.broadcast({
218
+ type: StreamEventType.METRIC,
219
+ data: { name, value, labels }
220
+ }, 'metrics');
221
+ }
222
+
223
+ /**
224
+ * Stream error
225
+ * @param {Error|object} error
226
+ */
227
+ streamError(error) {
228
+ this.broadcast({
229
+ type: StreamEventType.ERROR,
230
+ data: {
231
+ message: error.message || String(error),
232
+ stack: error.stack,
233
+ code: error.code
234
+ }
235
+ }, 'errors');
236
+ }
237
+
238
+ /**
239
+ * Get connection count
240
+ */
241
+ get connectionCount() {
242
+ return this.connections.size;
243
+ }
244
+
245
+ /**
246
+ * Get all connection IDs
247
+ */
248
+ getConnectionIds() {
249
+ return Array.from(this.connections.keys());
250
+ }
251
+
252
+ /**
253
+ * Close specific connection
254
+ * @param {string} connectionId
255
+ */
256
+ closeConnection(connectionId) {
257
+ const connection = this.connections.get(connectionId);
258
+ if (connection) {
259
+ connection.close();
260
+ this.connections.delete(connectionId);
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Close all connections and stop server
266
+ */
267
+ close() {
268
+ if (this._heartbeatTimer) {
269
+ clearInterval(this._heartbeatTimer);
270
+ this._heartbeatTimer = null;
271
+ }
272
+
273
+ for (const connection of this.connections.values()) {
274
+ connection.close();
275
+ }
276
+ this.connections.clear();
277
+ this._replayBuffer = [];
278
+ }
279
+
280
+ /**
281
+ * Start heartbeat timer
282
+ * @private
283
+ */
284
+ _startHeartbeat() {
285
+ this._heartbeatTimer = setInterval(() => {
286
+ const heartbeat = {
287
+ type: StreamEventType.HEARTBEAT,
288
+ timestamp: new Date().toISOString(),
289
+ connections: this.connections.size
290
+ };
291
+
292
+ for (const connection of this.connections.values()) {
293
+ connection.send(heartbeat);
294
+ }
295
+ }, this.heartbeatInterval);
296
+ }
297
+
298
+ /**
299
+ * Add event to replay buffer
300
+ * @private
301
+ */
302
+ _addToReplayBuffer(event) {
303
+ this._replayBuffer.push({
304
+ event,
305
+ timestamp: Date.now()
306
+ });
307
+
308
+ // Trim buffer if needed
309
+ if (this._replayBuffer.length > this._replayBufferSize) {
310
+ this._replayBuffer.shift();
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Replay missed events to connection
316
+ * @private
317
+ */
318
+ _replayEvents(connection, lastEventId) {
319
+ // Find events after lastEventId
320
+ let found = false;
321
+ for (const { event } of this._replayBuffer) {
322
+ if (found) {
323
+ connection.send(event);
324
+ } else if (event.id === lastEventId) {
325
+ found = true;
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Individual SSE Connection
333
+ */
334
+ export class SSEConnection {
335
+ constructor(options) {
336
+ this.id = options.id;
337
+ this.response = options.response;
338
+ this.filters = options.filters;
339
+ this.channels = new Set(options.channels);
340
+ this.bufferSize = options.bufferSize;
341
+
342
+ /** @type {Array<object>} Pending events buffer */
343
+ this._buffer = [];
344
+
345
+ /** @type {boolean} */
346
+ this._closed = false;
347
+
348
+ /** @type {number} Events sent counter */
349
+ this.eventsSent = 0;
350
+
351
+ /** @type {number} Creation time */
352
+ this.createdAt = Date.now();
353
+ }
354
+
355
+ /**
356
+ * Send event to connection
357
+ * @param {object} event
358
+ */
359
+ send(event) {
360
+ if (this._closed) return;
361
+
362
+ try {
363
+ const data = this._formatSSE(event);
364
+ this.response.write(data);
365
+ this.eventsSent++;
366
+ } catch (err) {
367
+ // Connection likely closed
368
+ this._closed = true;
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Check if connection subscribes to channel
374
+ * @param {string} channel
375
+ */
376
+ matchesChannel(channel) {
377
+ return this.channels.has('*') || this.channels.has(channel);
378
+ }
379
+
380
+ /**
381
+ * Check if event matches connection filters
382
+ * @param {object} event
383
+ */
384
+ matchesFilters(event) {
385
+ for (const [key, value] of Object.entries(this.filters)) {
386
+ if (event[key] !== undefined && event[key] !== value) {
387
+ return false;
388
+ }
389
+ // Check nested data
390
+ if (event.data && event.data[key] !== undefined && event.data[key] !== value) {
391
+ return false;
392
+ }
393
+ }
394
+ return true;
395
+ }
396
+
397
+ /**
398
+ * Close connection
399
+ */
400
+ close() {
401
+ if (!this._closed) {
402
+ this._closed = true;
403
+ try {
404
+ this.response.end();
405
+ } catch {
406
+ // Already closed
407
+ }
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Format event as SSE
413
+ * @private
414
+ */
415
+ _formatSSE(event) {
416
+ let output = '';
417
+
418
+ if (event.id) {
419
+ output += `id: ${event.id}\n`;
420
+ }
421
+
422
+ if (event.type) {
423
+ output += `event: ${event.type}\n`;
424
+ }
425
+
426
+ // Serialize data
427
+ const data = JSON.stringify(event);
428
+ output += `data: ${data}\n\n`;
429
+
430
+ return output;
431
+ }
432
+ }
433
+
434
+ /**
435
+ * Client-side EventStream consumer
436
+ *
437
+ * For browser/Node.js clients to consume SSE streams.
438
+ */
439
+ export class EventStreamClient extends EventEmitter {
440
+ constructor(url, options = {}) {
441
+ super();
442
+
443
+ this.url = url;
444
+ this.options = options;
445
+
446
+ /** @type {EventSource|null} */
447
+ this._source = null;
448
+
449
+ /** @type {string|null} */
450
+ this._lastEventId = null;
451
+
452
+ /** @type {boolean} */
453
+ this._reconnecting = false;
454
+
455
+ /** @type {number} */
456
+ this._reconnectAttempts = 0;
457
+
458
+ /** @type {number} */
459
+ this.maxReconnectAttempts = options.maxReconnectAttempts || 10;
460
+
461
+ /** @type {number} Base reconnect delay (ms) */
462
+ this.reconnectDelay = options.reconnectDelay || 1000;
463
+ }
464
+
465
+ /**
466
+ * Connect to event stream
467
+ */
468
+ connect() {
469
+ if (typeof EventSource === 'undefined') {
470
+ throw new Error('EventSource not available in this environment');
471
+ }
472
+
473
+ const urlWithParams = new URL(this.url);
474
+
475
+ // Add channels if specified
476
+ if (this.options.channels) {
477
+ urlWithParams.searchParams.set('channels', this.options.channels.join(','));
478
+ }
479
+
480
+ // Add last event ID for replay
481
+ if (this._lastEventId) {
482
+ urlWithParams.searchParams.set('lastEventId', this._lastEventId);
483
+ }
484
+
485
+ this._source = new EventSource(urlWithParams.toString());
486
+
487
+ this._source.onopen = () => {
488
+ this._reconnectAttempts = 0;
489
+ this.emit('open');
490
+ };
491
+
492
+ this._source.onerror = (err) => {
493
+ this.emit('error', err);
494
+ this._handleReconnect();
495
+ };
496
+
497
+ this._source.onmessage = (event) => {
498
+ this._handleMessage(event);
499
+ };
500
+
501
+ // Listen for specific event types
502
+ for (const type of Object.values(StreamEventType)) {
503
+ this._source.addEventListener(type, (event) => {
504
+ this._handleMessage(event);
505
+ });
506
+ }
507
+ }
508
+
509
+ /**
510
+ * Disconnect from stream
511
+ */
512
+ disconnect() {
513
+ if (this._source) {
514
+ this._source.close();
515
+ this._source = null;
516
+ }
517
+ this._reconnecting = false;
518
+ }
519
+
520
+ /**
521
+ * Handle incoming message
522
+ * @private
523
+ */
524
+ _handleMessage(event) {
525
+ try {
526
+ const data = JSON.parse(event.data);
527
+
528
+ // Track last event ID
529
+ if (data.id) {
530
+ this._lastEventId = data.id;
531
+ }
532
+
533
+ // Emit typed event
534
+ if (data.type) {
535
+ this.emit(data.type, data);
536
+ }
537
+
538
+ // Also emit generic message
539
+ this.emit('message', data);
540
+
541
+ } catch (err) {
542
+ this.emit('parse-error', err, event.data);
543
+ }
544
+ }
545
+
546
+ /**
547
+ * Handle reconnection with exponential backoff
548
+ * @private
549
+ */
550
+ _handleReconnect() {
551
+ if (this._reconnecting) return;
552
+ if (this._reconnectAttempts >= this.maxReconnectAttempts) {
553
+ this.emit('max-reconnect');
554
+ return;
555
+ }
556
+
557
+ this._reconnecting = true;
558
+ this._reconnectAttempts++;
559
+
560
+ const delay = this.reconnectDelay * Math.pow(2, this._reconnectAttempts - 1);
561
+
562
+ setTimeout(() => {
563
+ this._reconnecting = false;
564
+ this.connect();
565
+ }, delay);
566
+ }
567
+
568
+ /**
569
+ * Get connection state
570
+ */
571
+ get readyState() {
572
+ return this._source?.readyState ?? -1;
573
+ }
574
+
575
+ /**
576
+ * Check if connected
577
+ */
578
+ get isConnected() {
579
+ return this._source?.readyState === 1; // EventSource.OPEN
580
+ }
581
+ }
582
+
583
+ /**
584
+ * Create HTTP handler for SSE endpoint
585
+ *
586
+ * Use with Express, Koa, or native http server.
587
+ *
588
+ * @param {EventStreamServer} server
589
+ * @returns {function} HTTP handler
590
+ */
591
+ export function createSSEHandler(server) {
592
+ return (req, res) => {
593
+ // Parse query parameters
594
+ const url = new URL(req.url, `http://${req.headers.host}`);
595
+ const channels = url.searchParams.get('channels')?.split(',') || ['*'];
596
+ const lastEventId = url.searchParams.get('lastEventId') ||
597
+ req.headers['last-event-id'];
598
+
599
+ // Parse filters from query
600
+ const filters = {};
601
+ for (const [key, value] of url.searchParams) {
602
+ if (!['channels', 'lastEventId'].includes(key)) {
603
+ filters[key] = value;
604
+ }
605
+ }
606
+
607
+ server.handleConnection(req, res, {
608
+ channels,
609
+ filters,
610
+ replay: !!lastEventId,
611
+ lastEventId
612
+ });
613
+ };
614
+ }
615
+
616
+ /**
617
+ * Integration helper: Connect TelemetryService to EventStream
618
+ *
619
+ * @param {object} telemetryService - TelemetryService instance
620
+ * @param {EventStreamServer} streamServer - EventStreamServer instance
621
+ */
622
+ export function connectTelemetryToStream(telemetryService, streamServer) {
623
+ // Wrap original methods to stream events
624
+ const originalRecordEvent = telemetryService.recordEvent?.bind(telemetryService);
625
+ const originalStartSpan = telemetryService.startSpan?.bind(telemetryService);
626
+ const originalEndSpan = telemetryService.endSpan?.bind(telemetryService);
627
+
628
+ if (originalRecordEvent) {
629
+ telemetryService.recordEvent = function(...args) {
630
+ const result = originalRecordEvent(...args);
631
+ const event = telemetryService.events?.[telemetryService.events.length - 1];
632
+ if (event) {
633
+ streamServer.streamTelemetry(event);
634
+ }
635
+ return result;
636
+ };
637
+ }
638
+
639
+ if (originalStartSpan) {
640
+ telemetryService.startSpan = function(...args) {
641
+ const span = originalStartSpan(...args);
642
+ streamServer.streamSpanStart(span);
643
+ return span;
644
+ };
645
+ }
646
+
647
+ if (originalEndSpan) {
648
+ telemetryService.endSpan = function(...args) {
649
+ const result = originalEndSpan(...args);
650
+ const span = telemetryService.activeSpans?.get(args[0]) ||
651
+ telemetryService._spans?.find(s => s.spanId === args[0]);
652
+ if (span) {
653
+ streamServer.streamSpanEnd(span);
654
+ }
655
+ return result;
656
+ };
657
+ }
658
+
659
+ return streamServer;
660
+ }
661
+
662
+ export default {
663
+ StreamEventType,
664
+ EventStreamServer,
665
+ EventStreamClient,
666
+ SSEConnection,
667
+ createSSEHandler,
668
+ connectTelemetryToStream
669
+ };