@bonsae/nrg 0.16.0 → 0.18.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/README.md +112 -19
- package/package.json +38 -6
- package/server/index.cjs +38 -24
- package/server/resources/nrg-client.js +3667 -3521
- package/test/client/component/index.js +244 -0
- package/test/client/component/setup.js +201 -0
- package/test/client/e2e/index.js +2698 -0
- package/test/client/unit/index.js +200 -0
- package/test/client/unit/setup.js +199 -0
- package/test/{index.js → server/unit/index.js} +23 -8
- package/tsconfig/core/client.json +11 -0
- package/tsconfig/{server.json → core/server.json} +1 -1
- package/tsconfig/test/client/component.json +11 -0
- package/tsconfig/test/client/e2e.json +6 -0
- package/tsconfig/test/client/unit.json +6 -0
- package/tsconfig/test/server/unit.json +6 -0
- package/types/client.d.ts +66 -0
- package/types/server.d.ts +74 -58
- package/types/shims/form/components/node-red-config-input.vue.d.ts +15 -2
- package/types/shims/form/components/node-red-editor-input.vue.d.ts +18 -3
- package/types/shims/form/components/node-red-input.vue.d.ts +17 -5
- package/types/shims/form/components/node-red-json-schema-form.vue.d.ts +117 -19
- package/types/shims/form/components/node-red-select-input.vue.d.ts +15 -1
- package/types/shims/form/components/node-red-typed-input.vue.d.ts +52 -8
- package/types/test-client-component.d.ts +70 -0
- package/types/test-client-e2e.d.ts +152 -0
- package/types/test-client-unit.d.ts +52 -0
- package/types/{test.d.ts → test-server-unit.d.ts} +12 -2
- package/vite/index.js +15 -7
- package/tsconfig/client.json +0 -11
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// src/test/client/mocks.ts
|
|
2
|
+
function createRED() {
|
|
3
|
+
return {
|
|
4
|
+
_: (key) => key,
|
|
5
|
+
editor: {
|
|
6
|
+
createEditor(options) {
|
|
7
|
+
let currentValue = options.value || "";
|
|
8
|
+
const session = {
|
|
9
|
+
on(_event, _cb) {
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
getValue: () => currentValue,
|
|
14
|
+
setValue: (val) => {
|
|
15
|
+
currentValue = val;
|
|
16
|
+
},
|
|
17
|
+
getSession: () => session,
|
|
18
|
+
focus: () => {
|
|
19
|
+
},
|
|
20
|
+
destroy: () => {
|
|
21
|
+
},
|
|
22
|
+
saveView: () => {
|
|
23
|
+
},
|
|
24
|
+
restoreView: () => {
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
prepareConfigNodeSelect: () => {
|
|
29
|
+
},
|
|
30
|
+
validateNode: () => true
|
|
31
|
+
},
|
|
32
|
+
tray: {
|
|
33
|
+
show: () => {
|
|
34
|
+
},
|
|
35
|
+
close: () => {
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
popover: {
|
|
39
|
+
tooltip: () => ({ delete: () => {
|
|
40
|
+
}, setAction: () => {
|
|
41
|
+
} })
|
|
42
|
+
},
|
|
43
|
+
nodes: {
|
|
44
|
+
registerType: () => {
|
|
45
|
+
},
|
|
46
|
+
node: () => null,
|
|
47
|
+
dirty: () => false
|
|
48
|
+
},
|
|
49
|
+
events: {
|
|
50
|
+
on: () => {
|
|
51
|
+
},
|
|
52
|
+
off: () => {
|
|
53
|
+
},
|
|
54
|
+
emit: () => {
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
settings: {},
|
|
58
|
+
notify: () => {
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function ensureState(el) {
|
|
63
|
+
if (el && !el.__jqState) {
|
|
64
|
+
el.__jqState = {
|
|
65
|
+
typedInput: { value: "", type: "" },
|
|
66
|
+
listeners: {}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return el ? el.__jqState : { typedInput: { value: "", type: "" }, listeners: {} };
|
|
70
|
+
}
|
|
71
|
+
function createJQ(el) {
|
|
72
|
+
const state = ensureState(el);
|
|
73
|
+
const jq = {
|
|
74
|
+
0: el,
|
|
75
|
+
length: el ? 1 : 0,
|
|
76
|
+
typedInput(action, value) {
|
|
77
|
+
if (typeof action === "object") {
|
|
78
|
+
state.typedInput = { value: "", type: action.default || "" };
|
|
79
|
+
return jq;
|
|
80
|
+
}
|
|
81
|
+
if (action === "value") {
|
|
82
|
+
if (value !== void 0) {
|
|
83
|
+
state.typedInput.value = String(value);
|
|
84
|
+
if (el) el.setAttribute("value", String(value));
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
return state.typedInput.value;
|
|
88
|
+
}
|
|
89
|
+
if (action === "type") {
|
|
90
|
+
if (value !== void 0) {
|
|
91
|
+
state.typedInput.type = String(value);
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
94
|
+
return state.typedInput.type;
|
|
95
|
+
}
|
|
96
|
+
return jq;
|
|
97
|
+
},
|
|
98
|
+
on(event, cb) {
|
|
99
|
+
if (!state.listeners[event]) state.listeners[event] = [];
|
|
100
|
+
state.listeners[event].push(cb);
|
|
101
|
+
return jq;
|
|
102
|
+
},
|
|
103
|
+
off(event) {
|
|
104
|
+
if (event) {
|
|
105
|
+
delete state.listeners[event];
|
|
106
|
+
} else {
|
|
107
|
+
for (const key of Object.keys(state.listeners)) {
|
|
108
|
+
delete state.listeners[key];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return jq;
|
|
112
|
+
},
|
|
113
|
+
val(value) {
|
|
114
|
+
if (value !== void 0) {
|
|
115
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {
|
|
116
|
+
el.value = String(value);
|
|
117
|
+
}
|
|
118
|
+
return jq;
|
|
119
|
+
}
|
|
120
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {
|
|
121
|
+
return el.value;
|
|
122
|
+
}
|
|
123
|
+
return "";
|
|
124
|
+
},
|
|
125
|
+
find(selector) {
|
|
126
|
+
return createJQ(el?.querySelector(selector) ?? null);
|
|
127
|
+
},
|
|
128
|
+
append(child) {
|
|
129
|
+
const childEl = child?.[0] || child;
|
|
130
|
+
if (el && childEl instanceof Element) el.appendChild(childEl);
|
|
131
|
+
return jq;
|
|
132
|
+
},
|
|
133
|
+
appendTo(target) {
|
|
134
|
+
const t = target?.[0] || target;
|
|
135
|
+
if (t instanceof Element && el) t.appendChild(el);
|
|
136
|
+
return jq;
|
|
137
|
+
},
|
|
138
|
+
html(content) {
|
|
139
|
+
if (el) el.innerHTML = content;
|
|
140
|
+
return jq;
|
|
141
|
+
},
|
|
142
|
+
empty() {
|
|
143
|
+
if (el) el.innerHTML = "";
|
|
144
|
+
return jq;
|
|
145
|
+
},
|
|
146
|
+
i18n() {
|
|
147
|
+
return jq;
|
|
148
|
+
},
|
|
149
|
+
addClass(cls) {
|
|
150
|
+
el?.classList.add(cls);
|
|
151
|
+
return jq;
|
|
152
|
+
},
|
|
153
|
+
removeClass(cls) {
|
|
154
|
+
el?.classList.remove(cls);
|
|
155
|
+
return jq;
|
|
156
|
+
},
|
|
157
|
+
__trigger(event) {
|
|
158
|
+
(state.listeners[event] || []).forEach((cb) => cb());
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
return jq;
|
|
162
|
+
}
|
|
163
|
+
function createJQuery() {
|
|
164
|
+
return function $(selector, attrs) {
|
|
165
|
+
if (typeof selector === "string") {
|
|
166
|
+
if (selector.trim().startsWith("<")) {
|
|
167
|
+
const tpl = document.createElement("template");
|
|
168
|
+
tpl.innerHTML = selector.trim();
|
|
169
|
+
const el = tpl.content.firstElementChild;
|
|
170
|
+
if (el && attrs) {
|
|
171
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
172
|
+
if (key === "html") {
|
|
173
|
+
el.innerHTML = String(value);
|
|
174
|
+
} else {
|
|
175
|
+
el.setAttribute(key, String(value));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return createJQ(el);
|
|
180
|
+
}
|
|
181
|
+
return createJQ(document.querySelector(selector));
|
|
182
|
+
}
|
|
183
|
+
if (selector instanceof Element) return createJQ(selector);
|
|
184
|
+
if (selector && typeof selector === "object" && selector.nodeType)
|
|
185
|
+
return createJQ(selector);
|
|
186
|
+
return createJQ(null);
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/test/client/unit/index.ts
|
|
191
|
+
var defaultConfig = {
|
|
192
|
+
testTimeout: 3e4,
|
|
193
|
+
environment: "happy-dom",
|
|
194
|
+
setupFiles: ["@bonsae/nrg/test/client/unit/setup"]
|
|
195
|
+
};
|
|
196
|
+
export {
|
|
197
|
+
createJQuery,
|
|
198
|
+
createRED,
|
|
199
|
+
defaultConfig
|
|
200
|
+
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// src/test/client/unit/setup.ts
|
|
2
|
+
import { beforeEach } from "vitest";
|
|
3
|
+
|
|
4
|
+
// src/test/client/mocks.ts
|
|
5
|
+
function createRED() {
|
|
6
|
+
return {
|
|
7
|
+
_: (key) => key,
|
|
8
|
+
editor: {
|
|
9
|
+
createEditor(options) {
|
|
10
|
+
let currentValue = options.value || "";
|
|
11
|
+
const session = {
|
|
12
|
+
on(_event, _cb) {
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
getValue: () => currentValue,
|
|
17
|
+
setValue: (val) => {
|
|
18
|
+
currentValue = val;
|
|
19
|
+
},
|
|
20
|
+
getSession: () => session,
|
|
21
|
+
focus: () => {
|
|
22
|
+
},
|
|
23
|
+
destroy: () => {
|
|
24
|
+
},
|
|
25
|
+
saveView: () => {
|
|
26
|
+
},
|
|
27
|
+
restoreView: () => {
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
prepareConfigNodeSelect: () => {
|
|
32
|
+
},
|
|
33
|
+
validateNode: () => true
|
|
34
|
+
},
|
|
35
|
+
tray: {
|
|
36
|
+
show: () => {
|
|
37
|
+
},
|
|
38
|
+
close: () => {
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
popover: {
|
|
42
|
+
tooltip: () => ({ delete: () => {
|
|
43
|
+
}, setAction: () => {
|
|
44
|
+
} })
|
|
45
|
+
},
|
|
46
|
+
nodes: {
|
|
47
|
+
registerType: () => {
|
|
48
|
+
},
|
|
49
|
+
node: () => null,
|
|
50
|
+
dirty: () => false
|
|
51
|
+
},
|
|
52
|
+
events: {
|
|
53
|
+
on: () => {
|
|
54
|
+
},
|
|
55
|
+
off: () => {
|
|
56
|
+
},
|
|
57
|
+
emit: () => {
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
settings: {},
|
|
61
|
+
notify: () => {
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function ensureState(el) {
|
|
66
|
+
if (el && !el.__jqState) {
|
|
67
|
+
el.__jqState = {
|
|
68
|
+
typedInput: { value: "", type: "" },
|
|
69
|
+
listeners: {}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return el ? el.__jqState : { typedInput: { value: "", type: "" }, listeners: {} };
|
|
73
|
+
}
|
|
74
|
+
function createJQ(el) {
|
|
75
|
+
const state = ensureState(el);
|
|
76
|
+
const jq = {
|
|
77
|
+
0: el,
|
|
78
|
+
length: el ? 1 : 0,
|
|
79
|
+
typedInput(action, value) {
|
|
80
|
+
if (typeof action === "object") {
|
|
81
|
+
state.typedInput = { value: "", type: action.default || "" };
|
|
82
|
+
return jq;
|
|
83
|
+
}
|
|
84
|
+
if (action === "value") {
|
|
85
|
+
if (value !== void 0) {
|
|
86
|
+
state.typedInput.value = String(value);
|
|
87
|
+
if (el) el.setAttribute("value", String(value));
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
return state.typedInput.value;
|
|
91
|
+
}
|
|
92
|
+
if (action === "type") {
|
|
93
|
+
if (value !== void 0) {
|
|
94
|
+
state.typedInput.type = String(value);
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
return state.typedInput.type;
|
|
98
|
+
}
|
|
99
|
+
return jq;
|
|
100
|
+
},
|
|
101
|
+
on(event, cb) {
|
|
102
|
+
if (!state.listeners[event]) state.listeners[event] = [];
|
|
103
|
+
state.listeners[event].push(cb);
|
|
104
|
+
return jq;
|
|
105
|
+
},
|
|
106
|
+
off(event) {
|
|
107
|
+
if (event) {
|
|
108
|
+
delete state.listeners[event];
|
|
109
|
+
} else {
|
|
110
|
+
for (const key of Object.keys(state.listeners)) {
|
|
111
|
+
delete state.listeners[key];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return jq;
|
|
115
|
+
},
|
|
116
|
+
val(value) {
|
|
117
|
+
if (value !== void 0) {
|
|
118
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {
|
|
119
|
+
el.value = String(value);
|
|
120
|
+
}
|
|
121
|
+
return jq;
|
|
122
|
+
}
|
|
123
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {
|
|
124
|
+
return el.value;
|
|
125
|
+
}
|
|
126
|
+
return "";
|
|
127
|
+
},
|
|
128
|
+
find(selector) {
|
|
129
|
+
return createJQ(el?.querySelector(selector) ?? null);
|
|
130
|
+
},
|
|
131
|
+
append(child) {
|
|
132
|
+
const childEl = child?.[0] || child;
|
|
133
|
+
if (el && childEl instanceof Element) el.appendChild(childEl);
|
|
134
|
+
return jq;
|
|
135
|
+
},
|
|
136
|
+
appendTo(target) {
|
|
137
|
+
const t = target?.[0] || target;
|
|
138
|
+
if (t instanceof Element && el) t.appendChild(el);
|
|
139
|
+
return jq;
|
|
140
|
+
},
|
|
141
|
+
html(content) {
|
|
142
|
+
if (el) el.innerHTML = content;
|
|
143
|
+
return jq;
|
|
144
|
+
},
|
|
145
|
+
empty() {
|
|
146
|
+
if (el) el.innerHTML = "";
|
|
147
|
+
return jq;
|
|
148
|
+
},
|
|
149
|
+
i18n() {
|
|
150
|
+
return jq;
|
|
151
|
+
},
|
|
152
|
+
addClass(cls) {
|
|
153
|
+
el?.classList.add(cls);
|
|
154
|
+
return jq;
|
|
155
|
+
},
|
|
156
|
+
removeClass(cls) {
|
|
157
|
+
el?.classList.remove(cls);
|
|
158
|
+
return jq;
|
|
159
|
+
},
|
|
160
|
+
__trigger(event) {
|
|
161
|
+
(state.listeners[event] || []).forEach((cb) => cb());
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
return jq;
|
|
165
|
+
}
|
|
166
|
+
function createJQuery() {
|
|
167
|
+
return function $(selector, attrs) {
|
|
168
|
+
if (typeof selector === "string") {
|
|
169
|
+
if (selector.trim().startsWith("<")) {
|
|
170
|
+
const tpl = document.createElement("template");
|
|
171
|
+
tpl.innerHTML = selector.trim();
|
|
172
|
+
const el = tpl.content.firstElementChild;
|
|
173
|
+
if (el && attrs) {
|
|
174
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
175
|
+
if (key === "html") {
|
|
176
|
+
el.innerHTML = String(value);
|
|
177
|
+
} else {
|
|
178
|
+
el.setAttribute(key, String(value));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return createJQ(el);
|
|
183
|
+
}
|
|
184
|
+
return createJQ(document.querySelector(selector));
|
|
185
|
+
}
|
|
186
|
+
if (selector instanceof Element) return createJQ(selector);
|
|
187
|
+
if (selector && typeof selector === "object" && selector.nodeType)
|
|
188
|
+
return createJQ(selector);
|
|
189
|
+
return createJQ(null);
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/test/client/unit/setup.ts
|
|
194
|
+
var RED = createRED();
|
|
195
|
+
window.$ = createJQuery();
|
|
196
|
+
window.RED = RED;
|
|
197
|
+
beforeEach(() => {
|
|
198
|
+
RED.settings = {};
|
|
199
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// src/test/index.ts
|
|
1
|
+
// src/test/server/unit/index.ts
|
|
2
2
|
import { vi as vi2 } from "vitest";
|
|
3
3
|
|
|
4
|
-
// src/test/mocks.ts
|
|
4
|
+
// src/test/server/unit/mocks.ts
|
|
5
5
|
import { vi } from "vitest";
|
|
6
|
-
function
|
|
6
|
+
function createRED(options = {}) {
|
|
7
7
|
const { settings = {} } = options;
|
|
8
8
|
const nodes = {};
|
|
9
9
|
const red = {
|
|
@@ -398,7 +398,8 @@ function initValidator(RED) {
|
|
|
398
398
|
// src/core/server/nodes/symbols.ts
|
|
399
399
|
var WIRE_HANDLERS = Symbol.for("nrg.wireHandlers");
|
|
400
400
|
|
|
401
|
-
// src/test/index.ts
|
|
401
|
+
// src/test/server/unit/index.ts
|
|
402
|
+
import { Kind } from "@sinclair/typebox";
|
|
402
403
|
function buildConfig(NodeClass, userConfig = {}) {
|
|
403
404
|
const defaults = {};
|
|
404
405
|
if (NodeClass.configSchema?.properties) {
|
|
@@ -413,7 +414,7 @@ function buildConfig(NodeClass, userConfig = {}) {
|
|
|
413
414
|
}
|
|
414
415
|
return { ...defaults, ...userConfig };
|
|
415
416
|
}
|
|
416
|
-
function attachHelpers(node, nodeRedNode) {
|
|
417
|
+
function attachHelpers(node, nodeRedNode, NodeClass) {
|
|
417
418
|
const sentMessages = [];
|
|
418
419
|
const statusCalls = [];
|
|
419
420
|
nodeRedNode.send.mockImplementation((msg) => {
|
|
@@ -449,6 +450,15 @@ function attachHelpers(node, nodeRedNode) {
|
|
|
449
450
|
},
|
|
450
451
|
sent(port) {
|
|
451
452
|
if (port === void 0) return [...sentMessages];
|
|
453
|
+
if (typeof port === "string") {
|
|
454
|
+
const schema = NodeClass.outputsSchema;
|
|
455
|
+
if (!schema || Array.isArray(schema) || typeof schema === "object" && Kind in schema) {
|
|
456
|
+
return [];
|
|
457
|
+
}
|
|
458
|
+
const idx = Object.keys(schema).indexOf(port);
|
|
459
|
+
if (idx === -1) return [];
|
|
460
|
+
return sentMessages.map((msg) => Array.isArray(msg) ? msg[idx] : void 0).filter((msg) => msg != null);
|
|
461
|
+
}
|
|
452
462
|
return sentMessages.map(
|
|
453
463
|
(msg) => Array.isArray(msg) ? msg[port] : port === 0 ? msg : void 0
|
|
454
464
|
).filter((msg) => msg != null);
|
|
@@ -497,7 +507,7 @@ async function createNode(NodeClass, options = {}) {
|
|
|
497
507
|
resolvedConfig[key] = value;
|
|
498
508
|
}
|
|
499
509
|
}
|
|
500
|
-
const RED =
|
|
510
|
+
const RED = createRED({ settings });
|
|
501
511
|
initValidator(RED);
|
|
502
512
|
for (const [id, value] of Object.entries(configNodes)) {
|
|
503
513
|
RED.registerNrgNode(id, value);
|
|
@@ -523,12 +533,17 @@ async function createNode(NodeClass, options = {}) {
|
|
|
523
533
|
NodeClass.validateSettings(RED);
|
|
524
534
|
await Promise.resolve(NodeClass.registered?.(RED));
|
|
525
535
|
const node = new NodeClass(RED, nodeRedNode, config, credentials);
|
|
526
|
-
const augmented = attachHelpers(
|
|
536
|
+
const augmented = attachHelpers(
|
|
537
|
+
node,
|
|
538
|
+
nodeRedNode,
|
|
539
|
+
NodeClass
|
|
540
|
+
);
|
|
527
541
|
const createdPromise = Promise.resolve(node.created?.());
|
|
528
542
|
node[WIRE_HANDLERS](nodeRedNode, createdPromise);
|
|
529
543
|
await createdPromise;
|
|
530
544
|
return { node: augmented, RED };
|
|
531
545
|
}
|
|
532
546
|
export {
|
|
533
|
-
createNode
|
|
547
|
+
createNode,
|
|
548
|
+
createRED
|
|
534
549
|
};
|
package/types/client.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// Generated by dts-bundle-generator v9.5.1
|
|
2
2
|
|
|
3
|
+
import { SchemaOptions, Static, TSchema } from '@sinclair/typebox';
|
|
3
4
|
import { App, Component } from 'vue';
|
|
4
5
|
|
|
6
|
+
interface NodeRefResolved<T = any> {
|
|
7
|
+
readonly __nrg_node_ref: true;
|
|
8
|
+
readonly __instance: T;
|
|
9
|
+
}
|
|
5
10
|
export interface NodeStateCredentials {
|
|
6
11
|
[key: string]: any;
|
|
7
12
|
}
|
|
@@ -87,13 +92,74 @@ export interface NodeDefinition {
|
|
|
87
92
|
onPaletteRemove?: (this: NodeRedNode) => void;
|
|
88
93
|
form?: NodeFormDefinition;
|
|
89
94
|
}
|
|
95
|
+
export interface NodeDefaults {
|
|
96
|
+
[key: string]: {
|
|
97
|
+
value: any;
|
|
98
|
+
type?: string;
|
|
99
|
+
label?: string;
|
|
100
|
+
required?: boolean;
|
|
101
|
+
validate?: (this: NodeRedNode, value: any, opt: any) => any;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export interface NodeCredentials {
|
|
105
|
+
[key: string]: {
|
|
106
|
+
value?: string;
|
|
107
|
+
type?: "password" | "text";
|
|
108
|
+
label?: string;
|
|
109
|
+
required?: boolean;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export interface MergedNodeDefinition extends NodeDefinition {
|
|
113
|
+
defaults?: NodeDefaults;
|
|
114
|
+
credentials?: NodeCredentials;
|
|
115
|
+
configSchema?: Record<string, any>;
|
|
116
|
+
credentialsSchema?: {
|
|
117
|
+
properties?: Record<string, any>;
|
|
118
|
+
};
|
|
119
|
+
outputsSchema?: Record<string, any>;
|
|
120
|
+
inputSchema?: Record<string, any>;
|
|
121
|
+
}
|
|
90
122
|
export interface NodeFeatures {
|
|
91
123
|
hasInputSchema: boolean;
|
|
92
124
|
hasOutputSchema: boolean;
|
|
93
125
|
}
|
|
126
|
+
/** Client-side representation of a TypedInput field: the raw value string and its type selector. */
|
|
127
|
+
export interface TypedInputValue {
|
|
128
|
+
value: string;
|
|
129
|
+
type: string;
|
|
130
|
+
}
|
|
131
|
+
type _ToClient<T> = T extends NodeRefResolved<any> ? string : T extends {
|
|
132
|
+
resolve(...args: any[]): any;
|
|
133
|
+
value: unknown;
|
|
134
|
+
type: string;
|
|
135
|
+
} ? TypedInputValue : T extends (...args: any[]) => any ? T : T extends Array<infer I> ? _ToClient<I>[] : T extends object ? {
|
|
136
|
+
[K in keyof T]: _ToClient<T[K]>;
|
|
137
|
+
} : T;
|
|
138
|
+
/**
|
|
139
|
+
* Infers the client-side TypeScript type from a TypeBox schema.
|
|
140
|
+
*
|
|
141
|
+
* Resolves schema types to their client form representations:
|
|
142
|
+
* - `NodeRef<T>` → `string` (node ID in the editor)
|
|
143
|
+
* - `TypedInput<T>` → `{ value: string; type: string }`
|
|
144
|
+
* - All other types resolve via TypeBox's `Static<T>`
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* import type { Infer } from "@bonsae/nrg/client";
|
|
149
|
+
* import type { ConfigSchema } from "../schemas/my-node";
|
|
150
|
+
*
|
|
151
|
+
* type Config = Infer<typeof ConfigSchema>;
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export type Infer<T extends TSchema> = _ToClient<Static<T>>;
|
|
94
155
|
|
|
95
156
|
export {};
|
|
96
157
|
|
|
97
158
|
export declare function defineNode<T extends NodeDefinition>(options: T): T;
|
|
98
159
|
export declare function registerType(definition: NodeDefinition): Promise<void>;
|
|
99
160
|
export declare function registerTypes(nodes: NodeDefinition[]): Promise<void>;
|
|
161
|
+
export declare function useFormNode<TConfig extends TSchema = TSchema, TCredentials extends TSchema = TSchema>(): {
|
|
162
|
+
node: NodeRedNode & Infer<TConfig> & { credentials: Infer<TCredentials> & Record<string, any> };
|
|
163
|
+
schema: Record<string, any>;
|
|
164
|
+
errors: Record<string, string>;
|
|
165
|
+
};
|