@flowfuse/node-red-dashboard 1.31.0 → 1.31.1-0abe89c-202609211346.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.
- package/nodes/config/ui_base.js +15 -1
- package/nodes/store/clients.js +71 -0
- package/nodes/utils/index.js +8 -1
- package/nodes/widgets/locales/en-US/ui_control.json +2 -1
- package/nodes/widgets/ui_chart.js +7 -3
- package/nodes/widgets/ui_control.html +1 -1
- package/nodes/widgets/ui_control.js +28 -0
- package/package.json +4 -4
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/abnfDiagram-N423BO3Z-BOzAedUe.js +0 -1
- package/dist/assets/architectureDiagram-T3A2C74G-zEM8gtfO.js +0 -36
- package/dist/assets/blockDiagram-VBNYF7ZC-eKuDT4u2.js +0 -132
- package/dist/assets/c4Diagram-5PPSVZJV-CRPvpwR2.js +0 -10
- package/dist/assets/chunk-2GRJ4B5K-BUP9p1Ar.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-DASHHxaC.js +0 -1
- package/dist/assets/chunk-5RXB4S5H-C76llYJF.js +0 -231
- package/dist/assets/chunk-5VM5RSS4-DnmzQEFn.js +0 -15
- package/dist/assets/chunk-6Q2QTUOP-DpGB6o5S.js +0 -88
- package/dist/assets/chunk-GF5L2VYU-Bhzwn3VL.js +0 -206
- package/dist/assets/chunk-JWPE2WC7-UNqcBwu_.js +0 -1
- package/dist/assets/chunk-KBJHAD2P-C38IW_UU.js +0 -1
- package/dist/assets/chunk-RYQCIY6F-COKxTTcO.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-BS0ojEzX.js +0 -1
- package/dist/assets/classDiagram-JCYQIIEL-CxMqqBtQ.js +0 -1
- package/dist/assets/classDiagram-v2-OCEON4UE-CxMqqBtQ.js +0 -1
- package/dist/assets/cose-bilkent-JH36ORCC-9jRlXuji.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-BDPznSFQ.js +0 -178
- package/dist/assets/cynefinDiagram-MW4NZA55-lSqlEkCZ.js +0 -62
- package/dist/assets/cytoscape.esm-D_LviqZs.js +0 -331
- package/dist/assets/dagre-VZM6K2ZE-Dfoe_jp9.js +0 -4
- package/dist/assets/diagram-7IWD3JNH-Df2T__dV.js +0 -30
- package/dist/assets/diagram-B4RE2ZJO-DoGtC0h7.js +0 -3
- package/dist/assets/diagram-LBJQPF4R-cH8DVJ6C.js +0 -24
- package/dist/assets/diagram-Q27KOJAE-8JJAGHRF.js +0 -24
- package/dist/assets/diagram-UB23O5K3-BGCDqdpG.js +0 -41
- package/dist/assets/disconnected-BuxohaUu.png +0 -0
- package/dist/assets/ebnfDiagram-BXEA7PRR-r2bgA64b.js +0 -1
- package/dist/assets/echarts-BB8VCqSB.js +0 -73
- package/dist/assets/erDiagram-JOGREHBK-BExdsKTd.js +0 -85
- package/dist/assets/flowDiagram-UKHOOZJN-B8wwUqLz.js +0 -156
- package/dist/assets/ganttDiagram-PKOTCBZU-SViiTCQN.js +0 -292
- package/dist/assets/gitGraphDiagram-DS77QQ5N-CmktWysd.js +0 -106
- package/dist/assets/graph-C9eacEi8.js +0 -1
- package/dist/assets/index-CizlQbSv.css +0 -13
- package/dist/assets/index-CrLoQPpq.js +0 -124
- package/dist/assets/infoDiagram-6WML65LV-BwUFVmXD.js +0 -2
- package/dist/assets/ishikawaDiagram-WSZJBQD7-Df6P2A6T.js +0 -70
- package/dist/assets/journeyDiagram-NVQOT4AX-Cjk4fifu.js +0 -139
- package/dist/assets/kanban-definition-27J2QSJJ-BcoyAznM.js +0 -89
- package/dist/assets/katex-C5jXJg4s.js +0 -257
- package/dist/assets/layout-DEXfKzaS.js +0 -1
- package/dist/assets/logo-DIAzbBuw.png +0 -0
- package/dist/assets/map-Czzmt4hB.js +0 -1
- package/dist/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
- package/dist/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
- package/dist/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
- package/dist/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
- package/dist/assets/mermaid-CX4mRs5N.js +0 -314
- package/dist/assets/mindmap-definition-FAOFIHXS-D6njotiS.js +0 -96
- package/dist/assets/pegDiagram-VL7TDLO6-CaA_iXxT.js +0 -1
- package/dist/assets/pieDiagram-7S7Q4E2Y-aYh5694F.js +0 -39
- package/dist/assets/quadrantDiagram-CIZ2JOQS-CaWyb4Jw.js +0 -7
- package/dist/assets/railroadDiagram-AXF67PYL-Bd_vMLsE.js +0 -1
- package/dist/assets/requirementDiagram-LRYGKXZP-CMxISoyd.js +0 -84
- package/dist/assets/sankeyDiagram-W5VNT64P-DLbFAyXe.js +0 -40
- package/dist/assets/sequenceDiagram-SI44F4Z6-CzdhMpvP.js +0 -162
- package/dist/assets/sizeCapture-X5ZJPWSS-CnezfYZ6.js +0 -1
- package/dist/assets/stateDiagram-OKZ733FA-5R9RLDEM.js +0 -1
- package/dist/assets/stateDiagram-v2-UEYNNEHI-Ck4l-gcT.js +0 -1
- package/dist/assets/swimlanes-SLNWSIFB-CeEnGZwJ.js +0 -2
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-DSebamlh.js +0 -8
- package/dist/assets/timeline-definition-Z64GVDOM-ZMWCENQJ.js +0 -120
- package/dist/assets/vennDiagram-T6HMQDX7-DVr1jz1I.js +0 -34
- package/dist/assets/vue-vendor-Bmy9n80X.js +0 -49
- package/dist/assets/wardleyDiagram-T6FBY63Y-DNp6yzs5.js +0 -78
- package/dist/assets/workbox-window.prod.es5-BIl4cyR9.js +0 -2
- package/dist/assets/xychartDiagram-ELKLHX3M-DC4NZzl1.js +0 -7
- package/dist/favicon.ico +0 -0
- package/dist/favicon.svg +0 -482
- package/dist/index.html +0 -24
- package/dist/logo-512x512.png +0 -0
- package/dist/logo.svg +0 -482
- package/dist/maskable-icon-512x512.png +0 -0
- package/dist/pwa-192x192.png +0 -0
- package/dist/pwa-512x512.png +0 -0
- package/dist/pwa-64x64.png +0 -0
- package/dist/sw.js +0 -2
package/nodes/config/ui_base.js
CHANGED
|
@@ -4,9 +4,10 @@ const path = require('path')
|
|
|
4
4
|
const axios = require('axios')
|
|
5
5
|
|
|
6
6
|
const v = require('../../package.json').version
|
|
7
|
+
const { createClientStore } = require('../store/clients.js')
|
|
7
8
|
const datastore = require('../store/data.js')
|
|
8
9
|
const statestore = require('../store/state.js')
|
|
9
|
-
const { appendTopic, addConnectionCredentials, getThirdPartyWidgets } = require('../utils/index.js')
|
|
10
|
+
const { appendTopic, addConnectionCredentials, normalizeClientId, getThirdPartyWidgets } = require('../utils/index.js')
|
|
10
11
|
|
|
11
12
|
// from: https://stackoverflow.com/a/28592528/3016654
|
|
12
13
|
function join (...paths) {
|
|
@@ -59,6 +60,7 @@ module.exports = function (RED) {
|
|
|
59
60
|
ioServer: null,
|
|
60
61
|
/** @type {Object.<string, Socket>} */
|
|
61
62
|
connections: {},
|
|
63
|
+
clientStore: createClientStore(),
|
|
62
64
|
settings: {},
|
|
63
65
|
contribs: {}
|
|
64
66
|
}
|
|
@@ -413,6 +415,7 @@ module.exports = function (RED) {
|
|
|
413
415
|
socket.on('widget-action', onAction.bind(null, socket))
|
|
414
416
|
socket.on('widget-change', onChange.bind(null, socket))
|
|
415
417
|
socket.on('widget-load', onLoad.bind(null, socket))
|
|
418
|
+
socket.on('disconnect', () => uiShared.clientStore.disconnect(socket._clientId, socket.id))
|
|
416
419
|
}
|
|
417
420
|
}
|
|
418
421
|
/** @type {NodeJS.Timeout} */
|
|
@@ -472,6 +475,10 @@ module.exports = function (RED) {
|
|
|
472
475
|
// we only send comms on the connection that matches that id
|
|
473
476
|
checks.push(msg._client?.socketId === conn.id)
|
|
474
477
|
}
|
|
478
|
+
if (msg._client?.clientId) {
|
|
479
|
+
// clientId is the stable per-client key (spans a client's tabs/reconnects)
|
|
480
|
+
checks.push(normalizeClientId(msg._client.clientId) === conn._clientId)
|
|
481
|
+
}
|
|
475
482
|
// ensure all checks validate sending this
|
|
476
483
|
return !checks.length || !checks.includes(false)
|
|
477
484
|
}
|
|
@@ -591,6 +598,7 @@ module.exports = function (RED) {
|
|
|
591
598
|
socket.on('disconnect', reason => {
|
|
592
599
|
cleanupEventHandlers(socket)
|
|
593
600
|
delete uiShared.connections[socket.id]
|
|
601
|
+
uiShared.clientStore.disconnect(socket._clientId, socket.id)
|
|
594
602
|
node.log(`Disconnected ${socket.id} due to ${reason}`)
|
|
595
603
|
})
|
|
596
604
|
}
|
|
@@ -606,6 +614,9 @@ module.exports = function (RED) {
|
|
|
606
614
|
// node.connections[socket.id] = socket // store the connection for later use
|
|
607
615
|
uiShared.connections[socket.id] = socket // store the connection for later use
|
|
608
616
|
|
|
617
|
+
socket._clientId = normalizeClientId(socket.handshake?.query?.clientId)
|
|
618
|
+
uiShared.clientStore.connect(socket._clientId, socket.id)
|
|
619
|
+
|
|
609
620
|
emitConfig(socket)
|
|
610
621
|
|
|
611
622
|
// clean up then re-register listeners
|
|
@@ -846,6 +857,9 @@ module.exports = function (RED) {
|
|
|
846
857
|
node.on('close', (removed, done) => {
|
|
847
858
|
uiShared.ioServer?.off('connection', onConnection)
|
|
848
859
|
for (const conn of Object.values(uiShared.connections)) {
|
|
860
|
+
if (removed && conn._baseId === node.id) {
|
|
861
|
+
uiShared.clientStore.dropSocket(conn._clientId, conn.id)
|
|
862
|
+
}
|
|
849
863
|
cleanupEventHandlers(conn)
|
|
850
864
|
}
|
|
851
865
|
close(node, function (err) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const { EventEmitter } = require('events')
|
|
2
|
+
|
|
3
|
+
const DEFAULT_GRACE_MS = 20000
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Tracks client presence across reconnects (socket.id churns every reconnect; clientId is stable).
|
|
7
|
+
* A client may hold several live sockets (one per tab); events fire on its socket count crossing
|
|
8
|
+
* zero, not per socket. Emits { event, clientId, socketId? }:
|
|
9
|
+
* connected: count 0 -> 1, no grace pending (new client)
|
|
10
|
+
* reconnected: count 0 -> 1 during the grace window (returned before being declared gone)
|
|
11
|
+
* gone: count -> 0 and the grace window elapses
|
|
12
|
+
* Extra tabs (1 -> 2, 2 -> 1) emit nothing; the client is still present.
|
|
13
|
+
*/
|
|
14
|
+
function createClientStore ({ graceMs = DEFAULT_GRACE_MS, setTimeoutFn = setTimeout, clearTimeoutFn = clearTimeout } = {}) {
|
|
15
|
+
const clients = {} // clientId -> { sockets: Set<socketId>, graceTO }
|
|
16
|
+
const events = new EventEmitter()
|
|
17
|
+
events.setMaxListeners(0)
|
|
18
|
+
|
|
19
|
+
function connect (clientId, socketId) {
|
|
20
|
+
if (!clientId) { return }
|
|
21
|
+
let entry = clients[clientId]
|
|
22
|
+
const returning = !!(entry && entry.graceTO)
|
|
23
|
+
if (!entry) {
|
|
24
|
+
entry = clients[clientId] = { sockets: new Set(), graceTO: null }
|
|
25
|
+
}
|
|
26
|
+
if (entry.graceTO) {
|
|
27
|
+
clearTimeoutFn(entry.graceTO)
|
|
28
|
+
entry.graceTO = null
|
|
29
|
+
}
|
|
30
|
+
const wasEmpty = entry.sockets.size === 0
|
|
31
|
+
entry.sockets.add(socketId)
|
|
32
|
+
if (wasEmpty) {
|
|
33
|
+
events.emit('client', { event: returning ? 'reconnected' : 'connected', clientId, socketId })
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function disconnect (clientId, socketId) {
|
|
38
|
+
if (!clientId) { return }
|
|
39
|
+
const entry = clients[clientId]
|
|
40
|
+
if (!entry) { return }
|
|
41
|
+
entry.sockets.delete(socketId)
|
|
42
|
+
if (entry.sockets.size === 0 && !entry.graceTO) {
|
|
43
|
+
entry.graceTO = setTimeoutFn(() => {
|
|
44
|
+
delete clients[clientId]
|
|
45
|
+
events.emit('client', { event: 'gone', clientId })
|
|
46
|
+
}, graceMs)
|
|
47
|
+
if (typeof entry.graceTO?.unref === 'function') { entry.graceTO.unref() }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function dropSocket (clientId, socketId) {
|
|
52
|
+
if (!clientId) { return }
|
|
53
|
+
const entry = clients[clientId]
|
|
54
|
+
if (!entry) { return }
|
|
55
|
+
entry.sockets.delete(socketId)
|
|
56
|
+
if (entry.sockets.size === 0) {
|
|
57
|
+
if (entry.graceTO) { clearTimeoutFn(entry.graceTO) }
|
|
58
|
+
delete clients[clientId]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function list () {
|
|
63
|
+
return Object.entries(clients)
|
|
64
|
+
.filter(([, entry]) => entry.sockets.size > 0)
|
|
65
|
+
.map(([clientId, entry]) => ({ clientId, socketId: entry.sockets.values().next().value }))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { connect, disconnect, dropSocket, list, events }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { createClientStore }
|
package/nodes/utils/index.js
CHANGED
|
@@ -40,6 +40,11 @@ async function getTopic (RED, config, wNode, msg) {
|
|
|
40
40
|
return topic
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
function normalizeClientId (raw) {
|
|
44
|
+
if (Array.isArray(raw)) { raw = raw[0] }
|
|
45
|
+
return (raw === undefined || raw === null || raw === '') ? undefined : String(raw)
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
/**
|
|
44
49
|
* Adds socket/client data to a msg payload, if enabled
|
|
45
50
|
*
|
|
@@ -60,7 +65,8 @@ function addConnectionCredentials (RED, msg, conn, config) {
|
|
|
60
65
|
...item._client,
|
|
61
66
|
...{
|
|
62
67
|
socketId: conn.id,
|
|
63
|
-
socketIp: conn.handshake?.address
|
|
68
|
+
socketIp: conn.handshake?.address,
|
|
69
|
+
clientId: normalizeClientId(conn.handshake?.query?.clientId)
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
72
|
return item
|
|
@@ -127,5 +133,6 @@ module.exports = {
|
|
|
127
133
|
appendTopic,
|
|
128
134
|
getTopic,
|
|
129
135
|
addConnectionCredentials,
|
|
136
|
+
normalizeClientId,
|
|
130
137
|
getThirdPartyWidgets
|
|
131
138
|
}
|
|
@@ -63,10 +63,12 @@ module.exports = function (RED) {
|
|
|
63
63
|
const ago = (removeOlder * removeOlderUnit) * 1000 // milliseconds ago
|
|
64
64
|
const cutOff = (new Date()).getTime() - ago
|
|
65
65
|
const filterFn = (msg) => {
|
|
66
|
-
|
|
66
|
+
// msg._datapoint may be a single point or, in the case of multiple series in one msg, an array
|
|
67
|
+
// if it is an array then all the elements will have the same timestamp so use the first one
|
|
68
|
+
let timestamp = Array.isArray(msg._datapoint) ? msg._datapoint[0]?.x : msg._datapoint.x
|
|
67
69
|
// is x already a millisecond timestamp?
|
|
68
|
-
if (typeof (
|
|
69
|
-
timestamp = (new Date(
|
|
70
|
+
if (typeof (timestamp) === 'string') {
|
|
71
|
+
timestamp = (new Date(timestamp)).getTime()
|
|
70
72
|
}
|
|
71
73
|
return timestamp > cutOff
|
|
72
74
|
}
|
|
@@ -165,6 +167,8 @@ module.exports = function (RED) {
|
|
|
165
167
|
// Deep merge new options in with old
|
|
166
168
|
const mergedOptions = deepMerge(currentOptions, updates.chartOptions)
|
|
167
169
|
statestore.set(group.getBase(), node, msg, 'chartOptions', mergedOptions)
|
|
170
|
+
// pass the full set to the clients so they don't need to do the merge
|
|
171
|
+
msg.ui_update.chartOptions = mergedOptions
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
oneditprepare: function () {
|
|
25
25
|
const node = this
|
|
26
26
|
const sel = $('#node-input-events')
|
|
27
|
-
for (const name of ['all', 'change', 'connect']) {
|
|
27
|
+
for (const name of ['all', 'change', 'connect', 'clients']) {
|
|
28
28
|
const text = c_('events.' + name)
|
|
29
29
|
$('<option/>').val(name).text(text).appendTo(sel)
|
|
30
30
|
}
|
|
@@ -411,7 +411,35 @@ module.exports = function (RED) {
|
|
|
411
411
|
node.error('No UI configured')
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
+
let onClientEvent = null
|
|
415
|
+
let clientEventStore = null
|
|
416
|
+
if (ui && (config.events === 'all' || config.events === 'clients')) {
|
|
417
|
+
clientEventStore = ui.uiShared?.clientStore
|
|
418
|
+
if (clientEventStore) {
|
|
419
|
+
onClientEvent = (e) => {
|
|
420
|
+
const wNode = RED.nodes.getNode(node.id)
|
|
421
|
+
if (wNode && typeof wNode.send === 'function') {
|
|
422
|
+
const client = { clientId: e.clientId }
|
|
423
|
+
if (e.socketId) { client.socketId = e.socketId }
|
|
424
|
+
let msg = { payload: 'client-' + e.event, _client: client }
|
|
425
|
+
const conn = e.socketId ? ui.uiShared?.connections?.[e.socketId] : null
|
|
426
|
+
if (conn) { msg = addConnectionCredentials(RED, msg, conn, ui) }
|
|
427
|
+
wNode.send(msg)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
clientEventStore.events.on('client', onClientEvent)
|
|
431
|
+
setImmediate(() => {
|
|
432
|
+
for (const c of clientEventStore.list()) {
|
|
433
|
+
onClientEvent({ event: 'connected', clientId: c.clientId, socketId: c.socketId })
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
414
439
|
node.on('close', function (removed, done) {
|
|
440
|
+
if (onClientEvent && clientEventStore) {
|
|
441
|
+
clientEventStore.events.off('client', onClientEvent)
|
|
442
|
+
}
|
|
415
443
|
if (removed) {
|
|
416
444
|
// handle node being removed
|
|
417
445
|
ui?.deregister(null, null, node)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowfuse/node-red-dashboard",
|
|
3
|
-
"version": "1.31.0",
|
|
3
|
+
"version": "1.31.1-0abe89c-202609211346.0",
|
|
4
4
|
"description": "FlowFuse Dashboard - A collection of Node-RED nodes that provide functionality to build your own UI applications (inc. forms, buttons, charts).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node-red"
|
|
@@ -98,12 +98,12 @@
|
|
|
98
98
|
"marked-highlight": "^2.2.3",
|
|
99
99
|
"medium-zoom": "^1.1.0",
|
|
100
100
|
"mermaid": "^11.12.2",
|
|
101
|
-
"mocha": "^
|
|
101
|
+
"mocha": "^12.0.1",
|
|
102
102
|
"node-red": "^4.1.8",
|
|
103
103
|
"node-red-node-test-helper": "^0.3.5",
|
|
104
104
|
"npm-run-all": "^4.1.5",
|
|
105
105
|
"sass": "^1.96.0",
|
|
106
|
-
"sharp": "^0.
|
|
106
|
+
"sharp": "^0.35.4",
|
|
107
107
|
"should": "^13.2.3",
|
|
108
108
|
"should-sinon": "^0.0.6",
|
|
109
109
|
"sinon": "^21.0.0",
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
},
|
|
168
168
|
"esbuild": ">=0.25.0",
|
|
169
169
|
"cross-spawn": "~7.0.5",
|
|
170
|
-
"js-yaml": ">=4.1
|
|
170
|
+
"js-yaml": ">=5.4.1",
|
|
171
171
|
"tar": ">=7.5.4"
|
|
172
172
|
}
|
|
173
173
|
}
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-DpGB6o5S.js";import{p as f}from"./chunk-JWPE2WC7-UNqcBwu_.js";import{_ as n,l as o}from"./mermaid-CX4mRs5N.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-BDPznSFQ.js";var v=d().RailroadAbnf.parser.LangiumParser,l=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),i=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:i,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return l(e.element);case"AbnfOptionalGroup":return{type:"optional",element:l(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:l(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},w={parser:R,db:a,renderer:u,styles:p};export{w as diagram};
|