@bonsae/nrg 0.6.0 → 0.6.2
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/README.md +5 -5
- package/package.json +14 -75
- package/{build/server → server}/index.cjs +1 -1
- package/{src/core/client → shims}/components.d.ts +2 -0
- package/{src/tsconfig → tsconfig}/client.json +3 -3
- package/types/client.d.ts +37 -0
- package/types/index.d.ts +211 -0
- package/types/server.d.ts +2293 -0
- package/types/vite.d.ts +12 -0
- package/{build/vite → vite}/index.js +95 -0
- package/build/vite/utils.js +0 -56
- package/src/core/client/app.vue +0 -185
- package/src/core/client/components/node-red-config-input.vue +0 -79
- package/src/core/client/components/node-red-editor-input.vue +0 -307
- package/src/core/client/components/node-red-input-label.vue +0 -53
- package/src/core/client/components/node-red-input.vue +0 -93
- package/src/core/client/components/node-red-json-schema-form.vue +0 -444
- package/src/core/client/components/node-red-select-input.vue +0 -108
- package/src/core/client/components/node-red-toggle.vue +0 -115
- package/src/core/client/components/node-red-typed-input.vue +0 -158
- package/src/core/client/index.ts +0 -500
- package/src/core/client/tsconfig.json +0 -18
- package/src/core/constants.ts +0 -18
- package/src/core/errors.ts +0 -9
- package/src/core/server/api/index.ts +0 -1
- package/src/core/server/api/serve-nrg-resources.ts +0 -54
- package/src/core/server/index.ts +0 -190
- package/src/core/server/nodes/config-node.ts +0 -67
- package/src/core/server/nodes/factories.ts +0 -133
- package/src/core/server/nodes/index.ts +0 -5
- package/src/core/server/nodes/io-node.ts +0 -179
- package/src/core/server/nodes/node.ts +0 -259
- package/src/core/server/nodes/types/config-node.ts +0 -28
- package/src/core/server/nodes/types/factories.ts +0 -115
- package/src/core/server/nodes/types/index.ts +0 -4
- package/src/core/server/nodes/types/io-node.ts +0 -40
- package/src/core/server/nodes/types/node.ts +0 -41
- package/src/core/server/nodes/utils.ts +0 -106
- package/src/core/server/schemas/base.ts +0 -66
- package/src/core/server/schemas/index.ts +0 -3
- package/src/core/server/schemas/type.ts +0 -95
- package/src/core/server/schemas/types/index.ts +0 -82
- package/src/core/server/tsconfig.json +0 -17
- package/src/core/server/types/index.ts +0 -220
- package/src/core/server/utils.ts +0 -56
- package/src/core/server/validator.ts +0 -36
- package/src/core/validator.ts +0 -222
- package/src/index.ts +0 -2
- package/src/types.ts +0 -189
- package/src/utils.ts +0 -20
- package/src/vite/async-utils.ts +0 -61
- package/src/vite/client/build.ts +0 -227
- package/src/vite/client/index.ts +0 -1
- package/src/vite/client/plugins/html-generator.ts +0 -75
- package/src/vite/client/plugins/index.ts +0 -5
- package/src/vite/client/plugins/locales-generator.ts +0 -126
- package/src/vite/client/plugins/minifier.ts +0 -23
- package/src/vite/client/plugins/node-definitions-inliner.ts +0 -275
- package/src/vite/client/plugins/static-copy.ts +0 -43
- package/src/vite/defaults.ts +0 -77
- package/src/vite/errors.ts +0 -37
- package/src/vite/index.ts +0 -2
- package/src/vite/logger.ts +0 -94
- package/src/vite/node-red-launcher.ts +0 -344
- package/src/vite/plugin.ts +0 -61
- package/src/vite/plugins/build.ts +0 -85
- package/src/vite/plugins/index.ts +0 -2
- package/src/vite/plugins/server.ts +0 -267
- package/src/vite/server/build.ts +0 -124
- package/src/vite/server/index.ts +0 -1
- package/src/vite/server/plugins/index.ts +0 -3
- package/src/vite/server/plugins/output-wrapper.ts +0 -109
- package/src/vite/server/plugins/package-json-generator.ts +0 -203
- package/src/vite/server/plugins/type-generator.ts +0 -285
- package/src/vite/types.ts +0 -174
- package/src/vite/utils.ts +0 -72
- /package/{build/index.js → index.js} +0 -0
- /package/{build/server → server}/resources/nrg-client.js +0 -0
- /package/{build/server → server}/resources/vue.esm-browser.js +0 -0
- /package/{build/server → server}/resources/vue.esm-browser.prod.js +0 -0
- /package/{src/core/client → shims}/globals.d.ts +0 -0
- /package/{src/core/client → shims}/shims-vue.d.ts +0 -0
- /package/{src/tsconfig → tsconfig}/base.json +0 -0
- /package/{src/tsconfig → tsconfig}/server.json +0 -0
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div style="display: flex; flex-direction: column; width: 100%">
|
|
3
|
-
<slot name="label">
|
|
4
|
-
<NodeRedInputLabel
|
|
5
|
-
v-if="label"
|
|
6
|
-
:label="label"
|
|
7
|
-
:icon="icon"
|
|
8
|
-
:required="required"
|
|
9
|
-
/>
|
|
10
|
-
</slot>
|
|
11
|
-
<input
|
|
12
|
-
ref="typedInput"
|
|
13
|
-
type="text"
|
|
14
|
-
class="node-red-typed-input"
|
|
15
|
-
style="flex: 1; width: 100%"
|
|
16
|
-
/>
|
|
17
|
-
<div v-if="error" class="node-red-vue-input-error-message">
|
|
18
|
-
{{ error }}
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
</template>
|
|
22
|
-
|
|
23
|
-
<script lang="ts">
|
|
24
|
-
import { defineComponent } from "vue";
|
|
25
|
-
import NodeRedInputLabel from "./node-red-input-label.vue";
|
|
26
|
-
import { TYPED_INPUT_TYPES } from "../../constants";
|
|
27
|
-
|
|
28
|
-
export default defineComponent({
|
|
29
|
-
components: { NodeRedInputLabel },
|
|
30
|
-
props: {
|
|
31
|
-
value: {
|
|
32
|
-
type: Object,
|
|
33
|
-
required: true,
|
|
34
|
-
validator: function (obj) {
|
|
35
|
-
if (typeof obj !== "object" || obj === null) {
|
|
36
|
-
console.warn(
|
|
37
|
-
"[WARN] Invalid value for 'value' property. It must be an object.",
|
|
38
|
-
);
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
const isValid =
|
|
42
|
-
typeof obj?.value === "string" && typeof obj?.type === "string";
|
|
43
|
-
if (!isValid) {
|
|
44
|
-
console.warn(
|
|
45
|
-
"[WARN] Invalid value for 'value' property. It must be an object with 'value' and 'type' properties being strings.",
|
|
46
|
-
obj,
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
return isValid;
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
types: {
|
|
53
|
-
type: Array,
|
|
54
|
-
default: () => TYPED_INPUT_TYPES,
|
|
55
|
-
},
|
|
56
|
-
label: {
|
|
57
|
-
type: String,
|
|
58
|
-
default: "",
|
|
59
|
-
},
|
|
60
|
-
icon: {
|
|
61
|
-
type: String,
|
|
62
|
-
default: "",
|
|
63
|
-
},
|
|
64
|
-
required: {
|
|
65
|
-
type: Boolean,
|
|
66
|
-
default: false,
|
|
67
|
-
},
|
|
68
|
-
error: {
|
|
69
|
-
type: String,
|
|
70
|
-
default: "",
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
emits: ["update:value"],
|
|
74
|
-
computed: {
|
|
75
|
-
isProvidedValueTypeValid() {
|
|
76
|
-
const type = this.value.type;
|
|
77
|
-
const types = this.types;
|
|
78
|
-
|
|
79
|
-
return types.includes(type);
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
watch: {
|
|
83
|
-
isProvidedValueTypeValid: {
|
|
84
|
-
handler(newValue) {
|
|
85
|
-
if (!newValue) {
|
|
86
|
-
console.warn(
|
|
87
|
-
`Validation failed: this.value.type (${this.value.type}) must be one of the provided types (${this.types}).`,
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
immediate: true,
|
|
92
|
-
},
|
|
93
|
-
error(newVal) {
|
|
94
|
-
this.$nextTick(() => {
|
|
95
|
-
const targetDiv = this.$el.querySelector(
|
|
96
|
-
".red-ui-typedInput-container",
|
|
97
|
-
);
|
|
98
|
-
if (newVal) {
|
|
99
|
-
targetDiv.classList.add("input-error");
|
|
100
|
-
} else {
|
|
101
|
-
targetDiv.classList.remove("input-error");
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
mounted() {
|
|
107
|
-
const inputElement = this.$refs.typedInput;
|
|
108
|
-
this.$input = $(inputElement).typedInput({
|
|
109
|
-
default: this.value.type || this.types[0],
|
|
110
|
-
types: this.types,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
this.$input.typedInput("value", this.value.value || "");
|
|
114
|
-
this.$input.typedInput("type", this.value.type || this.types[0]);
|
|
115
|
-
|
|
116
|
-
// NOTE: when typed input is just a text input, it isn't emiting change while typing because it is updating the value in a hidden input
|
|
117
|
-
this.$nextTick(() => {
|
|
118
|
-
const observer = new MutationObserver((mutations) => {
|
|
119
|
-
for (const mutation of mutations) {
|
|
120
|
-
if (mutation.attributeName === "value") {
|
|
121
|
-
this.onChange();
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
observer.observe(inputElement, {
|
|
127
|
-
attributes: true,
|
|
128
|
-
attributeFilter: ["value"],
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
this._observer = observer;
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// NOTE: this emits changes to all types that lose focus when choosing a value, but text inputs
|
|
135
|
-
this.$input.on("change", () => {
|
|
136
|
-
this.onChange();
|
|
137
|
-
});
|
|
138
|
-
},
|
|
139
|
-
beforeUnmount() {
|
|
140
|
-
if (this._observer) {
|
|
141
|
-
this._observer.disconnect();
|
|
142
|
-
this._observer = null;
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
methods: {
|
|
146
|
-
onChange() {
|
|
147
|
-
const newValue = this.$input.typedInput("value");
|
|
148
|
-
const newType = this.$input.typedInput("type");
|
|
149
|
-
if (this.value.value !== newValue || this.value.type !== newType) {
|
|
150
|
-
this.$emit("update:value", {
|
|
151
|
-
value: newValue,
|
|
152
|
-
type: newType,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
</script>
|
package/src/core/client/index.ts
DELETED
|
@@ -1,500 +0,0 @@
|
|
|
1
|
-
import type { Component, App } from "vue";
|
|
2
|
-
import { createApp } from "vue";
|
|
3
|
-
import { cloneDeep, isEqual } from "es-toolkit";
|
|
4
|
-
import type { JSONSchemaType } from "ajv";
|
|
5
|
-
import NodeRedVueApp from "./app.vue";
|
|
6
|
-
import NodeRedInput from "./components/node-red-input.vue";
|
|
7
|
-
import NodeRedTypedInput from "./components/node-red-typed-input.vue";
|
|
8
|
-
import NodeRedConfigInput from "./components/node-red-config-input.vue";
|
|
9
|
-
import NodeRedSelectInput from "./components/node-red-select-input.vue";
|
|
10
|
-
import NodeRedEditorInput from "./components/node-red-editor-input.vue";
|
|
11
|
-
import NodeRedInputLabel from "./components/node-red-input-label.vue";
|
|
12
|
-
import NodeRedToggle from "./components/node-red-toggle.vue";
|
|
13
|
-
import NodeRedJsonSchemaForm from "./components/node-red-json-schema-form.vue";
|
|
14
|
-
|
|
15
|
-
const _schemas: Record<string, any> = {};
|
|
16
|
-
const _forms: Record<string, Component> = {};
|
|
17
|
-
|
|
18
|
-
function __setSchemas(schemas: Record<string, any>): void {
|
|
19
|
-
Object.assign(_schemas, schemas);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function __setForms(forms: Record<string, Component>): void {
|
|
23
|
-
Object.assign(_forms, forms);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface NodeStateCredentials {
|
|
27
|
-
[key: string]: any;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface NodeState {
|
|
31
|
-
credentials: NodeStateCredentials;
|
|
32
|
-
[key: string]: any;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface Node {
|
|
36
|
-
id: string;
|
|
37
|
-
type: string;
|
|
38
|
-
name: string;
|
|
39
|
-
category: string;
|
|
40
|
-
x: string;
|
|
41
|
-
y: string;
|
|
42
|
-
g: string;
|
|
43
|
-
z: string;
|
|
44
|
-
credentials: Record<string, any>;
|
|
45
|
-
_def: {
|
|
46
|
-
defaults: Record<
|
|
47
|
-
string,
|
|
48
|
-
{ value: string; type?: string; label?: string; required?: boolean }
|
|
49
|
-
>;
|
|
50
|
-
credentials: Record<
|
|
51
|
-
string,
|
|
52
|
-
{
|
|
53
|
-
value: string;
|
|
54
|
-
type?: "password" | "text";
|
|
55
|
-
label?: string;
|
|
56
|
-
required?: boolean;
|
|
57
|
-
}
|
|
58
|
-
>;
|
|
59
|
-
category: string;
|
|
60
|
-
color?: string;
|
|
61
|
-
icon?: string;
|
|
62
|
-
label?: ((this: Node) => string) | string;
|
|
63
|
-
inputs?: number;
|
|
64
|
-
outputs?: number;
|
|
65
|
-
paletteLabel?: ((this: Node) => string) | string;
|
|
66
|
-
labelStyle?: ((this: Node) => string) | string;
|
|
67
|
-
inputLabels?: ((this: Node) => string) | string;
|
|
68
|
-
outputLabels?: ((this: Node) => string) | string;
|
|
69
|
-
align?: "left" | "right";
|
|
70
|
-
button?: NodeButtonDefinition;
|
|
71
|
-
onPaletteAdd?: (this: Node) => void;
|
|
72
|
-
onPaletteRemove?: (this: Node) => void;
|
|
73
|
-
form: NodeFormDefinition;
|
|
74
|
-
};
|
|
75
|
-
_newState?: Node;
|
|
76
|
-
_app?: App | null;
|
|
77
|
-
_: (str: string) => string;
|
|
78
|
-
[key: string]: any;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Interface representing the button configuration for a Node.
|
|
83
|
-
*
|
|
84
|
-
* @interface NodeButtonDefinition
|
|
85
|
-
* @property {string} toggle - Text to display when toggling the button.
|
|
86
|
-
* @property {function(): void} onclick - Function to execute when the button is clicked.
|
|
87
|
-
* @property {function(): boolean} [enabled] - Function that determines whether the button should be
|
|
88
|
-
* enabled. Returns true if the button should be enabled, false otherwise.
|
|
89
|
-
* @property {function(): boolean} [visible] - Function that determines whether the button should be
|
|
90
|
-
* visible. Returns true if the button should be visible, false otherwise.
|
|
91
|
-
*/
|
|
92
|
-
interface NodeButtonDefinition {
|
|
93
|
-
toggle: string;
|
|
94
|
-
onclick: () => void;
|
|
95
|
-
enabled?: () => boolean;
|
|
96
|
-
visible?: () => boolean;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Interface representing the form configuration for a Node.
|
|
101
|
-
*
|
|
102
|
-
* @interface NodeFormDefinition
|
|
103
|
-
* @property {Component} [component] - Vue 3 component.
|
|
104
|
-
*/
|
|
105
|
-
interface NodeFormDefinition {
|
|
106
|
-
component?: Component;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Interface representing the Node options used during registration
|
|
111
|
-
*
|
|
112
|
-
* @type NodeDefinition
|
|
113
|
-
* @property {string} type - The unique identifier for this node type.
|
|
114
|
-
* @property {string} category - The category this node belongs to in the palette.
|
|
115
|
-
* @property {string} [color] - The color associated with this node, in hex format.
|
|
116
|
-
* @property {string} [icon] - The icon to display for this node.
|
|
117
|
-
* @property {(function(): string)|string} [label] - The label to display on the node. Can be a static string or a function returning a string.
|
|
118
|
-
* @property {number} [inputs] - Number of input ports the node should have.
|
|
119
|
-
* @property {number} [outputs] - Number of output ports the node should have.
|
|
120
|
-
* @property {(function(): string)|string} [paletteLabel] - The label to show in the palette. Can be a static string or a function returning a string.
|
|
121
|
-
* @property {(function(): string)|string} [labelStyle] - CSS style to apply to the node label. Can be a static string or a function returning a string.
|
|
122
|
-
* @property {(function(): string)|string} [inputLabels] - Labels for the input ports. Can be a static string or a function returning a string.
|
|
123
|
-
* @property {(function(): string)|string} [outputLabels] - Labels for the output ports. Can be a static string or a function returning a string.
|
|
124
|
-
* @property {"left"|"right"} [align] - Alignment of the node content.
|
|
125
|
-
* @property {NodeButtonDefinition} [button] - Configuration for a button on the node.
|
|
126
|
-
* @property {function(): void} [onPaletteAdd] - Function called when the node is added to the palette.
|
|
127
|
-
* @property {function(): void} [onPaletteRemove] - Function called when the node is removed from the palette.
|
|
128
|
-
* @property {NodeFormDefinition} form - The form component to use for configuring the node.
|
|
129
|
-
* @property {JSONSchemaType} [schema] - Schema definition for validation.
|
|
130
|
-
*/
|
|
131
|
-
interface NodeDefinition {
|
|
132
|
-
type: string;
|
|
133
|
-
category?: string;
|
|
134
|
-
color?: string;
|
|
135
|
-
icon?: ((this: Node) => string) | string;
|
|
136
|
-
label?: ((this: Node) => string) | string;
|
|
137
|
-
inputs?: number;
|
|
138
|
-
outputs?: number;
|
|
139
|
-
paletteLabel?: ((this: Node) => string) | string;
|
|
140
|
-
labelStyle?: ((this: Node) => string) | string;
|
|
141
|
-
inputLabels?: ((this: Node) => string) | string;
|
|
142
|
-
outputLabels?: ((this: Node) => string) | string;
|
|
143
|
-
align?: "left" | "right";
|
|
144
|
-
button?: NodeButtonDefinition;
|
|
145
|
-
onEditResize?: (this: Node, size: { width: number; height: number }) => void;
|
|
146
|
-
onPaletteAdd?: (this: Node) => void;
|
|
147
|
-
onPaletteRemove?: (this: Node) => void;
|
|
148
|
-
form?: NodeFormDefinition;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
interface NodeFeatures {
|
|
152
|
-
hasInputSchema: boolean;
|
|
153
|
-
hasOutputSchema: boolean;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function createNodeRedVueApp(
|
|
157
|
-
node: Node,
|
|
158
|
-
form: NodeFormDefinition | undefined,
|
|
159
|
-
schema: JSONSchemaType<any>,
|
|
160
|
-
features: NodeFeatures,
|
|
161
|
-
): App<Element> {
|
|
162
|
-
const app = createApp(NodeRedVueApp, {
|
|
163
|
-
node,
|
|
164
|
-
schema,
|
|
165
|
-
features,
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
app.component("NodeRedInputLabel", NodeRedInputLabel);
|
|
169
|
-
app.component("NodeRedToggle", NodeRedToggle);
|
|
170
|
-
app.component("NodeRedInput", NodeRedInput);
|
|
171
|
-
app.component("NodeRedTypedInput", NodeRedTypedInput);
|
|
172
|
-
app.component("NodeRedConfigInput", NodeRedConfigInput);
|
|
173
|
-
app.component("NodeRedSelectInput", NodeRedSelectInput);
|
|
174
|
-
app.component("NodeRedEditorInput", NodeRedEditorInput);
|
|
175
|
-
app.component("NodeRedJsonSchemaForm", NodeRedJsonSchemaForm);
|
|
176
|
-
app.component("NodeRedNodeForm", form?.component ?? NodeRedJsonSchemaForm);
|
|
177
|
-
|
|
178
|
-
// NOTE: now every form can use $i18n to access Node-RED built in i18n features
|
|
179
|
-
app.config.globalProperties.$i18n = (label: string) =>
|
|
180
|
-
node._(`${node.type}.${label}`);
|
|
181
|
-
return app;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function mountApp(
|
|
185
|
-
node: Node,
|
|
186
|
-
form: NodeFormDefinition | undefined,
|
|
187
|
-
schema: JSONSchemaType<any>,
|
|
188
|
-
features: NodeFeatures,
|
|
189
|
-
containerId: string,
|
|
190
|
-
) {
|
|
191
|
-
$(`#${containerId}`).empty();
|
|
192
|
-
node._newState = cloneDeep(node);
|
|
193
|
-
node._app = createNodeRedVueApp(node._newState, form, schema, features);
|
|
194
|
-
node._app.mount(`#${containerId}`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function unmountApp(node: Node) {
|
|
198
|
-
if (node._app) {
|
|
199
|
-
node._app.unmount();
|
|
200
|
-
node._app = null;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function getNodeState(node: Node): NodeState {
|
|
205
|
-
const state: NodeState = {
|
|
206
|
-
credentials: {},
|
|
207
|
-
};
|
|
208
|
-
Object.keys(node._def.defaults ?? {}).forEach((prop) => {
|
|
209
|
-
state[prop] = node[prop];
|
|
210
|
-
});
|
|
211
|
-
if (node._def.credentials) {
|
|
212
|
-
Object.keys(node._def.credentials).forEach((prop) => {
|
|
213
|
-
state.credentials[prop] = node.credentials?.[prop];
|
|
214
|
-
|
|
215
|
-
if (node._def.credentials[prop].type === "password") {
|
|
216
|
-
state.credentials[`has_${prop}`] =
|
|
217
|
-
node.credentials?.[`has_${prop}`] || false;
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return state;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function getChanges(
|
|
226
|
-
o: Record<any, any>,
|
|
227
|
-
n: Record<any, any>,
|
|
228
|
-
): Record<string, any> {
|
|
229
|
-
const changes: Record<string, any> = {};
|
|
230
|
-
|
|
231
|
-
const allKeys = new Set([...Object.keys(o), ...Object.keys(n ?? {})]);
|
|
232
|
-
allKeys.forEach((prop) => {
|
|
233
|
-
const _o = o[prop];
|
|
234
|
-
const _n = (n ?? {})[prop];
|
|
235
|
-
|
|
236
|
-
if (!Array.isArray(_o) && typeof _o === "object" && _o !== null) {
|
|
237
|
-
const _changes = getChanges(_o, _n);
|
|
238
|
-
if (Object.keys(_changes).length) {
|
|
239
|
-
changes[prop] = _changes;
|
|
240
|
-
}
|
|
241
|
-
} else if (!isEqual(_o, _n)) {
|
|
242
|
-
changes[prop] = _o;
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
return changes;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Deep-merge source into target, but replace arrays wholesale instead of
|
|
250
|
-
// merging them element-by-element (es-toolkit merge keeps old array items
|
|
251
|
-
// when the source is shorter, e.g. going from ["a","b"] to [] keeps ["a","b"]).
|
|
252
|
-
function applyState(target: any, source: any): void {
|
|
253
|
-
for (const key of Object.keys(source)) {
|
|
254
|
-
const srcVal = source[key];
|
|
255
|
-
if (Array.isArray(srcVal)) {
|
|
256
|
-
target[key] = [...srcVal];
|
|
257
|
-
} else if (srcVal !== null && typeof srcVal === "object") {
|
|
258
|
-
if (
|
|
259
|
-
!target[key] ||
|
|
260
|
-
typeof target[key] !== "object" ||
|
|
261
|
-
Array.isArray(target[key])
|
|
262
|
-
) {
|
|
263
|
-
target[key] = {};
|
|
264
|
-
}
|
|
265
|
-
applyState(target[key], srcVal);
|
|
266
|
-
} else {
|
|
267
|
-
target[key] = srcVal;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function defineNode<T extends NodeDefinition>(options: T): T {
|
|
273
|
-
return options;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Prepares a node registration function using the provided base configuration.
|
|
278
|
-
*
|
|
279
|
-
* This is a higher-order function that returns a function which can be used
|
|
280
|
-
* to register the node with a specific type at runtime.
|
|
281
|
-
*
|
|
282
|
-
* @param {Object} options - The static configuration shared by all nodes of this kind
|
|
283
|
-
* @param {string} [options.category="undefined"] - The category this node belongs to in the palette
|
|
284
|
-
* @param {string} [options.color="#FFFFFF"] - The color associated with this node, in hex format
|
|
285
|
-
* @param {string} [options.icon] - The icon to display for this node
|
|
286
|
-
* @param {(function(): string)|string} [options.label] - The label to display on the node
|
|
287
|
-
* @param {number} [options.inputs=0] - Number of input ports the node should have
|
|
288
|
-
* @param {number} [options.outputs=0] - Number of output ports the node should have
|
|
289
|
-
* @param {(function(): string)|string} [options.paletteLabel] - The label to show in the palette
|
|
290
|
-
* @param {(function(): string)|string} [options.labelStyle] - CSS style to apply to the node label
|
|
291
|
-
* @param {(function(): string)|string} [options.inputLabels] - Labels for the input ports
|
|
292
|
-
* @param {(function(): string)|string} [options.outputLabels] - Labels for the output ports
|
|
293
|
-
* @param {"left"|"right"} [options.align="left"] - Alignment of the node content
|
|
294
|
-
* @param {NodeButtonDefinition} [options.button] - Configuration for a button on the node
|
|
295
|
-
* @param {function(): void} [options.onPaletteAdd] - Function called when the node is added to the palette
|
|
296
|
-
* @param {function(): void} [options.onPaletteRemove] - Function called when the node is removed from the palette
|
|
297
|
-
* @param {Component} options.form - The form component to use for configuring the node
|
|
298
|
-
* @param {JSONSchemaType} [options.schema] - Schema definition for validation
|
|
299
|
-
*
|
|
300
|
-
* @returns A function that registers the node with the specified type
|
|
301
|
-
*/
|
|
302
|
-
async function registerType(definition: NodeDefinition): Promise<void> {
|
|
303
|
-
const { type } = definition;
|
|
304
|
-
try {
|
|
305
|
-
const nodeDefinition = {
|
|
306
|
-
...(_schemas[type] ?? {}),
|
|
307
|
-
...definition,
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
// defaults and credentials are pre-computed at build time by the inliner
|
|
311
|
-
const defaults = nodeDefinition.defaults ?? undefined;
|
|
312
|
-
const credentials = nodeDefinition.credentials ?? undefined;
|
|
313
|
-
|
|
314
|
-
const appContainerId = `nrg-app-${type}`;
|
|
315
|
-
|
|
316
|
-
$("<script>", {
|
|
317
|
-
type: "text/html",
|
|
318
|
-
"data-template-name": type,
|
|
319
|
-
html: `<div id="${appContainerId}"></div>`,
|
|
320
|
-
}).appendTo("body");
|
|
321
|
-
|
|
322
|
-
function oneditprepare(this: Node) {
|
|
323
|
-
const validationSchema =
|
|
324
|
-
nodeDefinition.configSchema &&
|
|
325
|
-
nodeDefinition.credentialsSchema?.properties
|
|
326
|
-
? {
|
|
327
|
-
...nodeDefinition.configSchema,
|
|
328
|
-
properties: {
|
|
329
|
-
...nodeDefinition.configSchema.properties,
|
|
330
|
-
credentials: {
|
|
331
|
-
type: "object",
|
|
332
|
-
properties: nodeDefinition.credentialsSchema.properties,
|
|
333
|
-
},
|
|
334
|
-
},
|
|
335
|
-
}
|
|
336
|
-
: nodeDefinition.configSchema;
|
|
337
|
-
|
|
338
|
-
const form =
|
|
339
|
-
definition.form ??
|
|
340
|
-
(_forms[type] ? { component: _forms[type] } : undefined);
|
|
341
|
-
const features: NodeFeatures = {
|
|
342
|
-
hasInputSchema: !!nodeDefinition.inputSchema,
|
|
343
|
-
hasOutputSchema: !!nodeDefinition.outputsSchema,
|
|
344
|
-
};
|
|
345
|
-
mountApp(this, form, validationSchema, features, appContainerId);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function oneditsave(this: Node) {
|
|
349
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
350
|
-
const node = this;
|
|
351
|
-
unmountApp(node);
|
|
352
|
-
|
|
353
|
-
const newState = getNodeState(node._newState!);
|
|
354
|
-
const oldState = getNodeState(node);
|
|
355
|
-
const changes = getChanges(oldState, newState);
|
|
356
|
-
const changed = !!Object.keys(changes)?.length;
|
|
357
|
-
if (!changed) return false;
|
|
358
|
-
|
|
359
|
-
Object.keys(node._def.defaults ?? {}).forEach((prop) => {
|
|
360
|
-
if (!node._def.defaults?.[prop]?.type) return;
|
|
361
|
-
const oldConfigNodeId: string = node[prop] as string;
|
|
362
|
-
const newConfigNodeId: string = node._newState![prop] as string;
|
|
363
|
-
if (oldConfigNodeId === newConfigNodeId) return;
|
|
364
|
-
const oldConfigNode = RED.nodes.node(oldConfigNodeId);
|
|
365
|
-
if (oldConfigNode && oldConfigNode._def.category === "config") {
|
|
366
|
-
const idx = oldConfigNode.users.findIndex(
|
|
367
|
-
(_node) => _node.id === node.id,
|
|
368
|
-
);
|
|
369
|
-
if (idx !== -1) {
|
|
370
|
-
oldConfigNode.users.splice(idx, 1);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
Object.keys(node._def.defaults ?? {}).forEach((prop) => {
|
|
376
|
-
if (!node._def.defaults?.[prop]?.type) return;
|
|
377
|
-
const newConfigNodeId: string = node._newState![prop] as string;
|
|
378
|
-
if (!newConfigNodeId) return;
|
|
379
|
-
const newConfigNode = RED.nodes.node(newConfigNodeId);
|
|
380
|
-
if (newConfigNode && newConfigNode._def.category === "config") {
|
|
381
|
-
const idx = newConfigNode.users.findIndex(
|
|
382
|
-
(_node) => _node.id === node.id,
|
|
383
|
-
);
|
|
384
|
-
if (idx === -1) {
|
|
385
|
-
newConfigNode.users.push(node);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
applyState(node, newState);
|
|
391
|
-
|
|
392
|
-
// For config nodes, populate the standard Node-RED input elements so
|
|
393
|
-
// pane.apply() can read the new values after oneditsave returns.
|
|
394
|
-
// Regular nodes must NOT have hidden inputs created here: pane.apply()
|
|
395
|
-
// reads them back via input.val() which coerces arrays/objects to strings,
|
|
396
|
-
// overwriting the correctly-typed values already set by merge() above.
|
|
397
|
-
const isConfigNode = definition.category === "config";
|
|
398
|
-
if (isConfigNode) {
|
|
399
|
-
Object.keys(node._def.defaults ?? {}).forEach((prop) => {
|
|
400
|
-
if (node._def.defaults[prop].type) return; // config-node refs handled separately
|
|
401
|
-
const inputId = `node-config-input-${prop}`;
|
|
402
|
-
let input = $(`#${inputId}`);
|
|
403
|
-
if (!input.length) {
|
|
404
|
-
input = $("<input>", { type: "hidden", id: inputId });
|
|
405
|
-
$(`#${appContainerId}`).append(input);
|
|
406
|
-
}
|
|
407
|
-
input.val(newState[prop] ?? "");
|
|
408
|
-
});
|
|
409
|
-
return undefined;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return {
|
|
413
|
-
changed,
|
|
414
|
-
history: [
|
|
415
|
-
{
|
|
416
|
-
t: "edit",
|
|
417
|
-
node,
|
|
418
|
-
changes,
|
|
419
|
-
links: [],
|
|
420
|
-
dirty: RED.nodes.dirty(),
|
|
421
|
-
changed,
|
|
422
|
-
},
|
|
423
|
-
],
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function oneditcancel(this: Node) {
|
|
428
|
-
unmountApp(this);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function oneditdelete(this: Node) {
|
|
432
|
-
unmountApp(this);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
RED.nodes.registerType(type, {
|
|
436
|
-
type,
|
|
437
|
-
defaults,
|
|
438
|
-
credentials,
|
|
439
|
-
category: nodeDefinition.category,
|
|
440
|
-
color: nodeDefinition.color || "#FFFFFF",
|
|
441
|
-
icon: nodeDefinition.icon,
|
|
442
|
-
inputs: nodeDefinition.inputs || 0,
|
|
443
|
-
outputs: nodeDefinition.outputs || 0,
|
|
444
|
-
label:
|
|
445
|
-
nodeDefinition.label ||
|
|
446
|
-
function (this: Node) {
|
|
447
|
-
return this.name || this._(`${type}.label`);
|
|
448
|
-
},
|
|
449
|
-
paletteLabel: nodeDefinition.paletteLabel || type,
|
|
450
|
-
labelStyle: nodeDefinition.labelStyle,
|
|
451
|
-
inputLabels: nodeDefinition.inputLabels,
|
|
452
|
-
outputLabels: nodeDefinition.outputLabels,
|
|
453
|
-
align: nodeDefinition.align || "left",
|
|
454
|
-
button: nodeDefinition.button,
|
|
455
|
-
oneditprepare,
|
|
456
|
-
oneditsave,
|
|
457
|
-
oneditcancel,
|
|
458
|
-
oneditdelete,
|
|
459
|
-
oneditresize: nodeDefinition.onEditResize,
|
|
460
|
-
onpaletteadd: nodeDefinition.onPaletteAdd,
|
|
461
|
-
onpaletteremove: nodeDefinition.onPaletteRemove,
|
|
462
|
-
});
|
|
463
|
-
} catch (error) {
|
|
464
|
-
console.error(`Error while registering node type ${type}:`, error);
|
|
465
|
-
throw error;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Registers multiple nodes with Node-RED in parallel.
|
|
471
|
-
*
|
|
472
|
-
* @param {Array<[string, NodeDefinition]>} nodes - Array of tuples containing node type and definition
|
|
473
|
-
* @returns Resolves when all nodes are registered
|
|
474
|
-
*
|
|
475
|
-
* @example
|
|
476
|
-
* await registerTypes([
|
|
477
|
-
* ["remote-server", remoteServer],
|
|
478
|
-
* ["your-node", yourNode],
|
|
479
|
-
* ]);
|
|
480
|
-
*/
|
|
481
|
-
async function registerTypes(nodes: NodeDefinition[]): Promise<void> {
|
|
482
|
-
try {
|
|
483
|
-
await Promise.all(nodes.map((definition) => registerType(definition)));
|
|
484
|
-
} catch (error) {
|
|
485
|
-
console.error("Error registering node types:", error);
|
|
486
|
-
throw error;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
export {
|
|
491
|
-
__setSchemas,
|
|
492
|
-
__setForms,
|
|
493
|
-
defineNode,
|
|
494
|
-
registerType,
|
|
495
|
-
registerTypes,
|
|
496
|
-
NodeDefinition,
|
|
497
|
-
NodeButtonDefinition,
|
|
498
|
-
NodeFormDefinition,
|
|
499
|
-
Node,
|
|
500
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"strict": true,
|
|
8
|
-
"esModuleInterop": true,
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"noEmit": true,
|
|
11
|
-
"noImplicitAny": false,
|
|
12
|
-
"noImplicitOverride": true,
|
|
13
|
-
"resolveJsonModule": true
|
|
14
|
-
},
|
|
15
|
-
"files": ["shims-vue.d.ts", "components.d.ts", "globals.d.ts"],
|
|
16
|
-
"include": ["**/*.ts", "**/*.vue", "../constants.ts", "../validator.ts"],
|
|
17
|
-
"exclude": ["node_modules"]
|
|
18
|
-
}
|
package/src/core/constants.ts
DELETED
package/src/core/errors.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { serveNrgResources } from "./serve-nrg-resources";
|