@mantiq/heartbeat 0.3.5 → 0.3.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantiq/heartbeat",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Observability, APM & queue monitoring for MantiqJS",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -136,29 +136,41 @@ export class HeartbeatMiddleware implements Middleware {
136
136
  }
137
137
  }
138
138
 
139
- // Attach debug stats header: duration;memory;status;queries
140
- if (process.env.APP_DEBUG === 'true' && response!) {
141
- try {
142
- const mem = (Math.abs(process.memoryUsage().rss - startMemory) / 1024 / 1024).toFixed(1)
143
- const headers = new Headers(response!.headers)
144
- headers.set('X-Heartbeat', `${Math.round(duration)}ms;${mem}MB;${response!.status};0q`)
145
- headers.set('Access-Control-Expose-Headers', [headers.get('Access-Control-Expose-Headers'), 'X-Heartbeat'].filter(Boolean).join(', '))
146
- response = new Response(response!.body, { status: response!.status, statusText: response!.statusText, headers })
147
- } catch { /* ignore */ }
148
- }
149
-
150
139
  // Flush entries (fire-and-forget)
151
140
  this.heartbeat.flush()
152
141
  }
153
142
 
154
- // Inject debug widget into HTML responses when APP_DEBUG=true
143
+ // Debug mode: attach X-Heartbeat header + inject widget
155
144
  if (process.env.APP_DEBUG === 'true' && response!) {
156
- const ct = response!.headers.get('content-type') ?? ''
157
- if (ct.includes('text/html') && response!.status < 400) {
158
- const duration = performance.now() - startTime
159
- const memUsage = process.memoryUsage().rss - startMemory
160
- response = await this.injectWidget(response!, duration, memUsage)
161
- }
145
+ const totalDuration = performance.now() - startTime
146
+ const totalMemory = Math.abs(process.memoryUsage().rss - startMemory)
147
+ const mem = (totalMemory / 1024 / 1024).toFixed(1)
148
+ const statsHeader = `${Math.round(totalDuration)}ms;${mem}MB;${response!.status};0q`
149
+
150
+ try {
151
+ const ct = response!.headers.get('content-type') ?? ''
152
+ const isHtml = ct.includes('text/html') && response!.status < 400
153
+ const cloned = response!.clone()
154
+ const body = await cloned.text()
155
+ const headers = new Headers(response!.headers)
156
+
157
+ headers.set('X-Heartbeat', statsHeader)
158
+ headers.set('Access-Control-Expose-Headers', [headers.get('Access-Control-Expose-Headers'), 'X-Heartbeat'].filter(Boolean).join(', '))
159
+
160
+ let finalBody = body
161
+ if (isHtml && this.heartbeat.config.widget?.enabled !== false && body.includes('</body>')) {
162
+ const widget = renderWidget({
163
+ duration: totalDuration,
164
+ memory: totalMemory,
165
+ status: response!.status,
166
+ queries: 0,
167
+ dashboardPath: this.heartbeat.config.dashboard.path,
168
+ })
169
+ finalBody = body.replace('</body>', widget + '\n</body>')
170
+ }
171
+
172
+ response = new Response(finalBody, { status: response!.status, statusText: response!.statusText, headers })
173
+ } catch (e) { console.error('[Heartbeat Widget]', e) }
162
174
  }
163
175
 
164
176
  return response!
@@ -225,29 +237,4 @@ export class HeartbeatMiddleware implements Middleware {
225
237
  }
226
238
  }
227
239
 
228
- private async injectWidget(response: Response, duration: number, memory: number): Promise<Response> {
229
- if (this.heartbeat.config.widget?.enabled === false) return response
230
-
231
- try {
232
- const html = await response.text()
233
- if (!html.includes('</body>')) return new Response(html, { status: response.status, statusText: response.statusText, headers: response.headers })
234
-
235
- const widget = renderWidget({
236
- duration,
237
- memory: Math.abs(memory),
238
- status: response.status,
239
- queries: 0, // TODO: wire up query count from QueryWatcher
240
- dashboardPath: this.heartbeat.config.dashboard.path,
241
- })
242
-
243
- const injected = html.replace('</body>', widget + '\n</body>')
244
- return new Response(injected, {
245
- status: response.status,
246
- statusText: response.statusText,
247
- headers: response.headers,
248
- })
249
- } catch {
250
- return response
251
- }
252
- }
253
240
  }