@prsm/devtools 1.0.2 → 1.1.0

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.
@@ -7,8 +7,8 @@
7
7
  <style>
8
8
  body { margin: 0; background: #0a0a0a; }
9
9
  </style>
10
- <script type="module" crossorigin src="./assets/index-CkwmeR8W.js"></script>
11
- <link rel="stylesheet" crossorigin href="./assets/index-p_4czaPS.css">
10
+ <script type="module" crossorigin src="./assets/index-LvnrgtvZ.js"></script>
11
+ <link rel="stylesheet" crossorigin href="./assets/index-Dj0vLuDk.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="app"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prsm/devtools",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Read-only Express middleware dashboard for observing @prsm infrastructure at runtime",
5
5
  "type": "module",
6
6
  "exports": {
@@ -31,6 +31,7 @@
31
31
  "express": "^4.21.0"
32
32
  },
33
33
  "devDependencies": {
34
+ "@prsm/cells": "^1.2.0",
34
35
  "@prsm/cron": "^1.0.3",
35
36
  "@prsm/limit": "^1.1.1",
36
37
  "@prsm/queue": "^3.0.8",
package/src/index.js CHANGED
@@ -1,11 +1,17 @@
1
1
  import { Router, static as serveStatic } from 'express'
2
2
  import { fileURLToPath } from 'node:url'
3
3
  import { resolve, dirname } from 'node:path'
4
- import { existsSync } from 'node:fs'
4
+ import { existsSync, readFileSync } from 'node:fs'
5
5
 
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url))
7
7
  const clientDir = resolve(__dirname, '..', 'dist', 'client')
8
8
 
9
+ function normalizeCellGraphs(cells) {
10
+ if (!cells) return null
11
+ if (typeof cells.cell === 'function') return { default: cells }
12
+ return cells
13
+ }
14
+
9
15
  function patternToString(p) {
10
16
  if (typeof p === 'string') return p
11
17
  if (p instanceof RegExp) return p.toString()
@@ -24,6 +30,7 @@ function patternToString(p) {
24
30
  export function prsmDevtools(options = {}) {
25
31
  const router = Router()
26
32
  const { queue, cron, limit, workflow, realtime } = options
33
+ const cellGraphs = normalizeCellGraphs(options.cells)
27
34
  const sseClients = new Set()
28
35
 
29
36
  function broadcast(event, data) {
@@ -44,6 +51,14 @@ export function prsmDevtools(options = {}) {
44
51
  cron.on('error', (data) => broadcast('cron:error', { name: data.name, error: data.error?.message }))
45
52
  }
46
53
 
54
+ if (cellGraphs) {
55
+ for (const [graphName, g] of Object.entries(cellGraphs)) {
56
+ g.on((name, value, state) => {
57
+ broadcast('cells:change', { graph: graphName, name, value, status: state.status, updatedAt: state.updatedAt })
58
+ })
59
+ }
60
+ }
61
+
47
62
  if (workflow) {
48
63
  for (const event of [
49
64
  'execution:queued',
@@ -68,6 +83,7 @@ export function prsmDevtools(options = {}) {
68
83
  limit: limit ? Object.keys(limit) : [],
69
84
  workflow: !!workflow,
70
85
  realtime: !!realtime,
86
+ cells: cellGraphs ? Object.keys(cellGraphs) : [],
71
87
  })
72
88
  })
73
89
 
@@ -326,10 +342,45 @@ export function prsmDevtools(options = {}) {
326
342
  })
327
343
  }
328
344
 
345
+ if (cellGraphs) {
346
+ router.get('/api/cells/:graph', (req, res) => {
347
+ const g = cellGraphs[req.params.graph]
348
+ if (!g) return res.status(404).json({ error: 'graph not found' })
349
+ const topology = g.cells()
350
+ const snapshot = g.snapshot()
351
+ const enriched = topology.map((c) => {
352
+ const state = g.get(c.name)
353
+ return {
354
+ ...c,
355
+ value: snapshot[c.name],
356
+ error: state?.error ? String(state.error.message || state.error) : null,
357
+ updatedAt: state?.updatedAt ?? null,
358
+ computeTime: state?.computeTime ?? null,
359
+ }
360
+ })
361
+ res.json({ graph: req.params.graph, cells: enriched })
362
+ })
363
+
364
+ router.get('/api/cells/:graph/:name/history', (req, res) => {
365
+ const g = cellGraphs[req.params.graph]
366
+ if (!g) return res.status(404).json({ error: 'graph not found' })
367
+ const limit = req.query.limit ? Number(req.query.limit) : undefined
368
+ try {
369
+ const entries = g.history(req.params.name, limit)
370
+ res.json({ graph: req.params.graph, name: req.params.name, entries })
371
+ } catch (err) {
372
+ res.status(500).json({ error: err.message })
373
+ }
374
+ })
375
+ }
376
+
329
377
  if (existsSync(clientDir)) {
330
- router.use(serveStatic(clientDir))
331
- router.get('*', (_req, res) => {
332
- res.sendFile(resolve(clientDir, 'index.html'))
378
+ const indexPath = resolve(clientDir, 'index.html')
379
+ const indexHtml = readFileSync(indexPath, 'utf8')
380
+ router.use(serveStatic(clientDir, { index: false }))
381
+ router.get('*', (req, res) => {
382
+ const base = req.baseUrl ? `${req.baseUrl}/` : '/'
383
+ res.type('html').send(indexHtml.replace('<head>', `<head>\n <base href="${base}">`))
333
384
  })
334
385
  }
335
386