@flowfuse/node-red-dashboard 1.32.0 → 1.32.1-15daa3c-202609281436.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 -0
- package/nodes/store/reactive.js +154 -0
- package/nodes/widgets/ui_gauge.js +1 -0
- package/package.json +1 -1
- 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-BzBvlpBo.js +0 -124
- package/dist/assets/index-DHk7_39t.css +0 -13
- 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-BBnX5xw4.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
|
@@ -6,6 +6,7 @@ const axios = require('axios')
|
|
|
6
6
|
const v = require('../../package.json').version
|
|
7
7
|
const { createClientStore } = require('../store/clients.js')
|
|
8
8
|
const datastore = require('../store/data.js')
|
|
9
|
+
const { attachToContext } = require('../store/reactive.js')
|
|
9
10
|
const statestore = require('../store/state.js')
|
|
10
11
|
const { appendTopic, addConnectionCredentials, matchesClient, normalizeClientId, getThirdPartyWidgets } = require('../utils/index.js')
|
|
11
12
|
|
|
@@ -401,6 +402,20 @@ module.exports = function (RED) {
|
|
|
401
402
|
|
|
402
403
|
node._created = Date.now()
|
|
403
404
|
|
|
405
|
+
try {
|
|
406
|
+
let unavailable = null
|
|
407
|
+
const store = attachToContext(node.context().global, {
|
|
408
|
+
clone: RED.util.cloneMessage,
|
|
409
|
+
onUnavailable: (err) => { unavailable = err },
|
|
410
|
+
onReplaced: () => node.warn('global.dashboardStore was replaced by a flow since the last deploy, discarding the stored value of every widget. The store has been re-created. To write your own data, set global.dashboardStore.<key> rather than replacing global.dashboardStore itself.')
|
|
411
|
+
})
|
|
412
|
+
if (!store) {
|
|
413
|
+
node.warn('Dashboard data store disabled: the global context store doesn\'t hold objects by reference (e.g. cache: false), so live state can\'t work. Use the memory store or localfilesystem with cache: true.' + (unavailable ? ' Reading it failed with: ' + unavailable.message : ''))
|
|
414
|
+
}
|
|
415
|
+
} catch (err) {
|
|
416
|
+
node.warn('Dashboard data store disabled: could not initialise it in global context (' + err.message + ').')
|
|
417
|
+
}
|
|
418
|
+
|
|
404
419
|
n.root = RED.settings.httpNodeRoot || '/'
|
|
405
420
|
|
|
406
421
|
/** @type {Object.<string, Socket>} */
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const STORE = Symbol.for('@flowfuse/node-red-dashboard/store')
|
|
2
|
+
const NAMESPACE = 'dashboardStore'
|
|
3
|
+
|
|
4
|
+
class Entry {
|
|
5
|
+
constructor () {
|
|
6
|
+
this.value = undefined
|
|
7
|
+
this.quality = 'GOOD'
|
|
8
|
+
this.timestamp = 0
|
|
9
|
+
this.history = []
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// only plain objects and arrays are deep-watched; Buffers, Dates, class instances etc. are opaque leaves
|
|
14
|
+
function isReactable (v) {
|
|
15
|
+
if (Array.isArray(v)) return true
|
|
16
|
+
if (v === null || typeof v !== 'object') return false
|
|
17
|
+
const proto = Object.getPrototypeOf(v)
|
|
18
|
+
return proto === null || Object.getPrototypeOf(proto) === null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// reads through proxies (structuredClone rejects them); production injects RED.util.cloneMessage
|
|
22
|
+
function deepClone (v, seen = new WeakMap()) {
|
|
23
|
+
if (v === null || typeof v !== 'object') return v
|
|
24
|
+
if (Buffer.isBuffer(v)) return Buffer.from(v)
|
|
25
|
+
if (v instanceof Date) return new Date(v)
|
|
26
|
+
if (seen.has(v)) return seen.get(v)
|
|
27
|
+
const out = Array.isArray(v) ? [] : {}
|
|
28
|
+
seen.set(v, out)
|
|
29
|
+
for (const k of Object.keys(v)) out[k] = deepClone(v[k], seen)
|
|
30
|
+
return out
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function deepReactive (value, notify, path, clone, seen = new WeakMap()) {
|
|
34
|
+
if (!isReactable(value)) return value
|
|
35
|
+
if (seen.has(value)) return seen.get(value)
|
|
36
|
+
const proxy = new Proxy(value, {
|
|
37
|
+
get (t, p, r) { return Reflect.get(t, p, r) },
|
|
38
|
+
set (t, p, v) {
|
|
39
|
+
if (typeof p === 'symbol') return Reflect.set(t, p, v)
|
|
40
|
+
// a push writes the new index then grows length; only a shrink is a change worth reporting
|
|
41
|
+
const shrank = p === 'length' && Array.isArray(t) && v < t.length
|
|
42
|
+
t[p] = deepReactive(clone(v), notify, `${path}.${String(p)}`, clone)
|
|
43
|
+
if (p !== 'length') notify(`${path}.${String(p)}`)
|
|
44
|
+
else if (shrank) notify(path)
|
|
45
|
+
return true
|
|
46
|
+
},
|
|
47
|
+
deleteProperty (t, p) {
|
|
48
|
+
const existed = p in t
|
|
49
|
+
const ok = delete t[p]
|
|
50
|
+
if (existed) notify(`${path}.${String(p)}`)
|
|
51
|
+
return ok
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
// registered before walking children so a cycle resolves to this proxy instead of recursing
|
|
55
|
+
seen.set(value, proxy)
|
|
56
|
+
for (const k of Object.keys(value)) value[k] = deepReactive(value[k], notify, `${path}.${k}`, clone, seen)
|
|
57
|
+
return proxy
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function createDataStore ({ maxHistory = 5, onChange, clone = deepClone, now = Date.now } = {}) {
|
|
61
|
+
const cloneValue = (v) => (v && typeof v === 'object') ? clone(v) : v
|
|
62
|
+
const emit = (prop, rec, path) => { try { onChange?.(prop, rec, path) } catch (err) {} }
|
|
63
|
+
const records = Object.create(null)
|
|
64
|
+
|
|
65
|
+
records[STORE] = true
|
|
66
|
+
|
|
67
|
+
const handler = {
|
|
68
|
+
get (target, prop, receiver) {
|
|
69
|
+
if (typeof prop === 'symbol') return Reflect.get(target, prop, receiver)
|
|
70
|
+
if (prop === 'toJSON') {
|
|
71
|
+
return () => Object.fromEntries(Object.entries(target).map(([k, r]) => [k, r.value]))
|
|
72
|
+
}
|
|
73
|
+
if (prop.startsWith('$')) {
|
|
74
|
+
const rec = target[prop.slice(1)]
|
|
75
|
+
return rec && Object.freeze({ value: rec.value, quality: rec.quality, timestamp: rec.timestamp, history: Object.freeze(rec.history.slice()) })
|
|
76
|
+
}
|
|
77
|
+
const rec = target[prop]
|
|
78
|
+
return rec ? rec.value : undefined
|
|
79
|
+
},
|
|
80
|
+
set (target, prop, value) {
|
|
81
|
+
if (typeof prop === 'symbol') return Reflect.set(target, prop, value)
|
|
82
|
+
// returning true rather than false so a strict caller picking a reserved name doesn't throw
|
|
83
|
+
if (prop === '__proto__' || prop === 'constructor' || prop === 'toJSON') return true
|
|
84
|
+
if (prop.startsWith('$')) return true // '$' is reserved for the read-only meta view
|
|
85
|
+
|
|
86
|
+
let rec = target[prop]
|
|
87
|
+
if (!rec) {
|
|
88
|
+
rec = new Entry()
|
|
89
|
+
target[prop] = rec
|
|
90
|
+
} else {
|
|
91
|
+
rec.history.push(Object.freeze({ value: cloneValue(rec.value), timestamp: rec.timestamp }))
|
|
92
|
+
if (rec.history.length > maxHistory) rec.history.shift()
|
|
93
|
+
}
|
|
94
|
+
const notify = (path) => {
|
|
95
|
+
// a replaced value keeps its proxies alive; they must not report into the record any more
|
|
96
|
+
if (rec.value !== wrapped) return
|
|
97
|
+
rec.timestamp = now()
|
|
98
|
+
emit(prop, rec, path)
|
|
99
|
+
}
|
|
100
|
+
// clone on write so a flow reusing its own object can't mutate stored state
|
|
101
|
+
const wrapped = deepReactive(cloneValue(value), notify, prop, cloneValue)
|
|
102
|
+
rec.value = wrapped
|
|
103
|
+
rec.timestamp = now()
|
|
104
|
+
emit(prop, rec, prop)
|
|
105
|
+
return true
|
|
106
|
+
},
|
|
107
|
+
ownKeys (target) { return Reflect.ownKeys(target) },
|
|
108
|
+
deleteProperty (target, prop) {
|
|
109
|
+
if (typeof prop === 'symbol') return Reflect.deleteProperty(target, prop)
|
|
110
|
+
const rec = target[prop]
|
|
111
|
+
const existed = prop in target
|
|
112
|
+
const ok = delete target[prop]
|
|
113
|
+
if (rec) rec.value = undefined
|
|
114
|
+
if (existed) emit(prop, undefined, prop)
|
|
115
|
+
return ok
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return new Proxy(records, handler)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const injected = new WeakMap()
|
|
122
|
+
|
|
123
|
+
// Inject the store into global context once. On redeploy the existing store is reused;
|
|
124
|
+
// after a restart with a persistent context store, existing plain data is rehydrated.
|
|
125
|
+
// Returns null when the context store can't hold a live proxy: a cache-off store serves
|
|
126
|
+
// only async access so a sync get throws, and a serialising store hands back a copy.
|
|
127
|
+
function attachToContext (globalContext, opts = {}) {
|
|
128
|
+
const namespace = opts.namespace || NAMESPACE
|
|
129
|
+
let existing
|
|
130
|
+
try {
|
|
131
|
+
existing = globalContext.get(namespace)
|
|
132
|
+
} catch (err) {
|
|
133
|
+
opts.onUnavailable?.(err)
|
|
134
|
+
return null
|
|
135
|
+
}
|
|
136
|
+
if (existing && existing[STORE]) {
|
|
137
|
+
injected.set(globalContext, existing)
|
|
138
|
+
return existing
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const previous = injected.get(globalContext)
|
|
142
|
+
if (previous && existing !== previous) opts.onReplaced?.()
|
|
143
|
+
|
|
144
|
+
const store = createDataStore(opts)
|
|
145
|
+
if (isReactable(existing) && !Array.isArray(existing)) {
|
|
146
|
+
for (const [k, v] of Object.entries(existing)) store[k] = v
|
|
147
|
+
}
|
|
148
|
+
globalContext.set(namespace, store)
|
|
149
|
+
if (globalContext.get(namespace) !== store) return null
|
|
150
|
+
injected.set(globalContext, store)
|
|
151
|
+
return store
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = { createDataStore, attachToContext, NAMESPACE, STORE }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowfuse/node-red-dashboard",
|
|
3
|
-
"version": "1.32.0",
|
|
3
|
+
"version": "1.32.1-15daa3c-202609281436.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"
|
|
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};
|