@bonsae/nrg 0.25.0 → 0.26.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/package.json +6 -4
- package/server/index.cjs +1413 -2
- package/server/resources/nrg-client.js +10 -0
- package/test/client/component/config.js +9 -9
- package/test/client/component/index.js +230 -42
- package/test/client/component/nrg.css +1 -0
- package/test/client/component/schemas.js +37 -0
- package/test/client/component/setup.js +1473 -198
- package/test/client/e2e/index.js +33 -35
- package/test/client/unit/index.js +17 -2
- package/test/server/integration/index.js +1222 -11
- package/test/server/unit/index.js +184 -2
- package/types/client.d.ts +1 -1
- package/types/server.d.ts +8 -2
- package/types/shims/components.d.ts +8 -8
- package/types/shims/{client → core/client}/types.d.ts +2 -2
- package/types/shims/core/schema-options.d.ts +24 -0
- package/types/shims/schema-options.d.ts +17 -17
- package/types/test-client-component-schemas.d.ts +73 -0
- package/types/test-client-component.d.ts +153 -7
- package/types/test-client-unit.d.ts +105 -4
- package/types/test-server-integration.d.ts +64 -63
- package/types/test-server-unit.d.ts +2 -1
- package/vite/index.js +33 -35
- /package/types/shims/{client → core/client}/form/components/node-red-config-input.vue.d.ts +0 -0
- /package/types/shims/{client → core/client}/form/components/node-red-editor-input.vue.d.ts +0 -0
- /package/types/shims/{client → core/client}/form/components/node-red-input-label.vue.d.ts +0 -0
- /package/types/shims/{client → core/client}/form/components/node-red-input.vue.d.ts +0 -0
- /package/types/shims/{client → core/client}/form/components/node-red-json-schema-form.vue.d.ts +0 -0
- /package/types/shims/{client → core/client}/form/components/node-red-select-input.vue.d.ts +0 -0
- /package/types/shims/{client → core/client}/form/components/node-red-toggle.vue.d.ts +0 -0
- /package/types/shims/{client → core/client}/form/components/node-red-typed-input.vue.d.ts +0 -0
- /package/types/shims/{constants.d.ts → core/constants.d.ts} +0 -0
- /package/types/shims/{brands.d.ts → core/types.d.ts} +0 -0
|
@@ -1,59 +1,247 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
var j = Object.defineProperty;
|
|
2
|
+
var $ = (t, e, r) => e in t ? j(t, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : t[e] = r;
|
|
3
|
+
var v = (t, e, r) => $(t, typeof e != "symbol" ? e + "" : e, r);
|
|
4
|
+
import { inject as F, vi as y } from "vitest";
|
|
5
|
+
import { inject as l, reactive as _, watch as S } from "vue";
|
|
6
|
+
import b from "jsonpointer";
|
|
7
|
+
import k from "ajv";
|
|
8
|
+
import O from "ajv-formats";
|
|
9
|
+
import P from "ajv-errors";
|
|
10
|
+
class N {
|
|
11
|
+
constructor(e) {
|
|
12
|
+
v(this, "ajv");
|
|
13
|
+
const { customKeywords: r, customFormats: o, ...a } = e || {};
|
|
14
|
+
this.ajv = new k({
|
|
15
|
+
allErrors: !0,
|
|
16
|
+
code: {
|
|
17
|
+
source: !1
|
|
18
|
+
},
|
|
19
|
+
coerceTypes: !0,
|
|
20
|
+
removeAdditional: !1,
|
|
21
|
+
strict: !1,
|
|
22
|
+
strictSchema: !1,
|
|
23
|
+
useDefaults: !0,
|
|
24
|
+
validateFormats: !0,
|
|
25
|
+
// NOTE: typebox handles validation via typescript
|
|
26
|
+
// NOTE: if true, types that are not serializable JSON, like Function, would not work
|
|
27
|
+
validateSchema: !1,
|
|
28
|
+
verbose: !0,
|
|
29
|
+
...a
|
|
30
|
+
}), O(this.ajv), P(this.ajv), this.addCustomKeywords(r || []), this.addCustomFormats(o || {});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Add custom keywords to the validator
|
|
34
|
+
*/
|
|
35
|
+
addCustomKeywords(e) {
|
|
36
|
+
e && e.forEach((r) => {
|
|
37
|
+
this.ajv.addKeyword(r);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Add custom formats to the validator
|
|
42
|
+
*/
|
|
43
|
+
addCustomFormats(e) {
|
|
44
|
+
e && Object.entries(e).forEach(([r, o]) => {
|
|
45
|
+
o instanceof RegExp ? this.ajv.addFormat(r, o) : this.ajv.addFormat(r, { validate: o });
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create a validator function with caching
|
|
50
|
+
* @param schema - JSON Schema to validate against
|
|
51
|
+
* @param cacheKey - Optional cache key for reusing validators
|
|
52
|
+
*/
|
|
53
|
+
createValidator(e, r) {
|
|
54
|
+
if (r && !e.$id && (e.$id = r), e.$id) {
|
|
55
|
+
const a = this.ajv.getSchema(e.$id);
|
|
56
|
+
if (a) return a;
|
|
57
|
+
}
|
|
58
|
+
return this.ajv.compile(e);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Validate data against a schema and return a structured result
|
|
62
|
+
*/
|
|
63
|
+
validate(e, r, o) {
|
|
64
|
+
const a = this.createValidator(r, o == null ? void 0 : o.cacheKey);
|
|
65
|
+
if (!a(e)) {
|
|
66
|
+
const i = this.formatErrors(a.errors);
|
|
67
|
+
if (o != null && o.throwOnError)
|
|
68
|
+
throw new h(i, a.errors || []);
|
|
69
|
+
return {
|
|
70
|
+
valid: !1,
|
|
71
|
+
errors: a.errors || void 0,
|
|
72
|
+
errorMessage: i
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
valid: !0,
|
|
77
|
+
data: e
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Format errors into a human-readable string
|
|
82
|
+
*/
|
|
83
|
+
formatErrors(e, r) {
|
|
84
|
+
return !e || e.length === 0 ? "No errors" : this.ajv.errorsText(e, {
|
|
85
|
+
separator: "; ",
|
|
86
|
+
dataVar: "data",
|
|
87
|
+
...r
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get detailed error information
|
|
92
|
+
*/
|
|
93
|
+
getDetailedErrors(e) {
|
|
94
|
+
return !e || e.length === 0 ? [] : e.map((r) => ({
|
|
95
|
+
field: r.instancePath || "/",
|
|
96
|
+
message: r.message || "Validation failed",
|
|
97
|
+
keyword: r.keyword,
|
|
98
|
+
params: r.params,
|
|
99
|
+
schemaPath: r.schemaPath
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Add a schema to the validator for reference
|
|
104
|
+
*/
|
|
105
|
+
addSchema(e, r) {
|
|
106
|
+
return this.ajv.addSchema(e, r), this;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Remove a schema from the validator
|
|
110
|
+
*/
|
|
111
|
+
removeSchema(e) {
|
|
112
|
+
return this.ajv.removeSchema(e), this;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
class h extends Error {
|
|
116
|
+
constructor(e, r) {
|
|
117
|
+
super(e), this.errors = r, this.name = "ValidationError", Object.setPrototypeOf(this, h.prototype);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const V = new N({
|
|
121
|
+
customKeywords: [
|
|
122
|
+
{
|
|
123
|
+
keyword: "x-nrg-skip-validation",
|
|
124
|
+
schemaType: "boolean",
|
|
125
|
+
valid: !0
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
keyword: "x-nrg-node-type",
|
|
129
|
+
type: "string",
|
|
130
|
+
validate: (t, e) => {
|
|
131
|
+
if (!e) return !0;
|
|
132
|
+
const r = RED.nodes.node(e);
|
|
133
|
+
return !!r && r.type === t;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
customFormats: {
|
|
138
|
+
"node-id": /^[a-zA-Z0-9-_]+$/,
|
|
139
|
+
"flow-id": /^[a-f0-9]{16}$/,
|
|
140
|
+
"topic-path": (t) => /^[a-zA-Z0-9/_-]+$/.test(t)
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
function x(t, e) {
|
|
144
|
+
return t && (e != null && e.properties) ? {
|
|
145
|
+
...t,
|
|
146
|
+
properties: {
|
|
147
|
+
...t.properties,
|
|
148
|
+
credentials: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: e.properties
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} : t;
|
|
154
|
+
}
|
|
155
|
+
function K(t, e) {
|
|
156
|
+
const r = V.validate(t, e, {
|
|
157
|
+
cacheKey: `node-schema-${t.type}`
|
|
158
|
+
});
|
|
159
|
+
return r.valid ? [] : r.errors ?? [];
|
|
160
|
+
}
|
|
161
|
+
function g(t, e) {
|
|
162
|
+
return K(t, e).filter((r) => {
|
|
163
|
+
var a;
|
|
164
|
+
return ((a = r.parentSchema) == null ? void 0 : a.format) !== "password" ? !0 : b.get(t, r.instancePath) !== "__PWD__";
|
|
165
|
+
}).reduce(
|
|
166
|
+
(r, o) => {
|
|
167
|
+
var i;
|
|
168
|
+
let a = o.instancePath;
|
|
169
|
+
o.keyword === "required" && ((i = o.params) != null && i.missingProperty) && (a = `${a}/${o.params.missingProperty}`);
|
|
170
|
+
const n = `node${a.replaceAll("/", ".")}`;
|
|
171
|
+
return r[n] = o.message ?? "Invalid", r;
|
|
172
|
+
},
|
|
173
|
+
{}
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
function B() {
|
|
177
|
+
const t = l("__nrg_form_node"), e = l("__nrg_form_schema"), r = l("__nrg_form_errors");
|
|
178
|
+
if (!t)
|
|
179
|
+
throw new Error(
|
|
180
|
+
"useFormNode() must be called inside a form component mounted by NRG."
|
|
181
|
+
);
|
|
182
|
+
return {
|
|
183
|
+
node: t,
|
|
184
|
+
schema: e,
|
|
185
|
+
errors: r
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
let w = 0;
|
|
189
|
+
function H(t = {}) {
|
|
190
|
+
var p;
|
|
191
|
+
const e = "type" in t || "configs" in t || "configSchema" in t || "credentialsSchema" in t || "nodes" in t ? t : { configs: t }, r = e.type ? (p = A()) == null ? void 0 : p[e.type] : void 0, o = e.configSchema ?? (r == null ? void 0 : r.configSchema), a = e.credentialsSchema ?? (r == null ? void 0 : r.credentialsSchema), n = _({
|
|
192
|
+
id: `test-${w}`,
|
|
9
193
|
// Unique type per node: validateForm caches compiled schemas by
|
|
10
194
|
// `node-schema-${subject.type}`, so a shared type would silently reuse
|
|
11
195
|
// the first schema for every later createNode call in the same file.
|
|
12
|
-
type: `test-node-${
|
|
196
|
+
type: `test-node-${w++}`,
|
|
13
197
|
changed: !1,
|
|
14
198
|
_def: { outputs: 1 },
|
|
15
|
-
_: (
|
|
16
|
-
...
|
|
17
|
-
credentials: { ...
|
|
18
|
-
}),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
d.nodes.add(t);
|
|
31
|
-
const c = m(
|
|
32
|
-
r ? p(s, r) : {}
|
|
199
|
+
_: (c) => c,
|
|
200
|
+
...e.configs,
|
|
201
|
+
credentials: { ...e.credentials }
|
|
202
|
+
}), i = x(o, a);
|
|
203
|
+
let d;
|
|
204
|
+
if (i) {
|
|
205
|
+
const { $id: c, ...u } = i;
|
|
206
|
+
d = u;
|
|
207
|
+
}
|
|
208
|
+
const f = T();
|
|
209
|
+
C(f), f.nodes.clear();
|
|
210
|
+
for (const c of e.nodes ?? [])
|
|
211
|
+
f.nodes.add(c);
|
|
212
|
+
const m = _(
|
|
213
|
+
d ? g(n, d) : {}
|
|
33
214
|
);
|
|
34
|
-
return
|
|
35
|
-
|
|
215
|
+
return d && S(
|
|
216
|
+
n,
|
|
36
217
|
() => {
|
|
37
|
-
const
|
|
38
|
-
Object.keys(
|
|
218
|
+
const c = g(n, d);
|
|
219
|
+
Object.keys(m).forEach((u) => delete m[u]), Object.assign(m, c);
|
|
39
220
|
},
|
|
40
221
|
{ deep: !0 }
|
|
41
|
-
), { node:
|
|
42
|
-
__nrg_form_node:
|
|
43
|
-
__nrg_form_schema:
|
|
44
|
-
__nrg_form_errors:
|
|
222
|
+
), { node: n, errors: m, RED: f, provide: {
|
|
223
|
+
__nrg_form_node: n,
|
|
224
|
+
__nrg_form_schema: d ?? {},
|
|
225
|
+
__nrg_form_errors: m
|
|
45
226
|
} };
|
|
46
227
|
}
|
|
47
|
-
function
|
|
48
|
-
|
|
228
|
+
function A() {
|
|
229
|
+
try {
|
|
230
|
+
return F("__nrg_schemas");
|
|
231
|
+
} catch {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function s(t, e) {
|
|
236
|
+
y.isMockFunction(t[e]) || y.spyOn(t, e);
|
|
49
237
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
238
|
+
function C(t) {
|
|
239
|
+
s(t, "_"), s(t, "notify"), s(t.editor, "createEditor"), s(t.editor, "prepareConfigNodeSelect"), s(t.editor, "validateNode"), s(t.tray, "show"), s(t.tray, "close"), s(t.popover, "tooltip"), s(t.popover, "create"), s(t.nodes, "registerType"), s(t.nodes, "node"), s(t.nodes, "add"), s(t.nodes, "remove"), s(t.nodes, "getType"), s(t.nodes, "dirty"), s(t.events, "on"), s(t.events, "off"), s(t.events, "emit"), s(t.comms, "subscribe"), s(t.comms, "unsubscribe");
|
|
52
240
|
}
|
|
53
|
-
function
|
|
241
|
+
function T() {
|
|
54
242
|
return window.RED;
|
|
55
243
|
}
|
|
56
244
|
export {
|
|
57
|
-
|
|
58
|
-
|
|
245
|
+
H as createNode,
|
|
246
|
+
B as useFormNode
|
|
59
247
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.nrg-label[data-v-864b02f2]{display:inline-block;width:100%;margin-bottom:4px;cursor:default}.nrg-label i[data-v-864b02f2]{margin-right:5px}.nrg-required[data-v-864b02f2]{color:var(--red-ui-text-color-error);margin-left:2px}.editor-wrapper[data-v-ea93e881]{position:relative}.expand-button[data-v-ea93e881]{position:absolute;top:-23px;right:0;z-index:10;transition:color .3s ease;cursor:pointer}.nrg-toggle-wrapper[data-v-724dcae4]{display:inline-flex;align-items:center}.nrg-toggle[data-v-724dcae4]{position:relative;display:inline-flex!important;flex-direction:column;align-items:flex-start;cursor:pointer;gap:4px;-webkit-user-select:none;user-select:none}.nrg-toggle__input[data-v-724dcae4]{position:absolute;opacity:0;width:0;height:0}.nrg-toggle__slider[data-v-724dcae4]{position:relative;display:inline-block;width:36px;min-width:36px;height:20px;background-color:var(--red-ui-secondary-border-color, #ccc);border-radius:10px;transition:background-color .2s ease}.nrg-toggle__slider[data-v-724dcae4]:after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;background-color:#fff;border-radius:50%;transition:transform .2s ease}.nrg-toggle--checked .nrg-toggle__slider[data-v-724dcae4]{background-color:var(--red-ui-text-color-link, #0070d2)}.nrg-toggle--checked .nrg-toggle__slider[data-v-724dcae4]:after{transform:translate(16px)}.nrg-toggle__label[data-v-724dcae4]{cursor:default;white-space:nowrap}.nrg-toggle__label i[data-v-724dcae4]{margin-right:2px}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/test/client/component/schemas.ts
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
function serialize(schema) {
|
|
5
|
+
return schema == null ? void 0 : JSON.parse(JSON.stringify(schema));
|
|
6
|
+
}
|
|
7
|
+
function serializeRegistry(registry) {
|
|
8
|
+
const map = {};
|
|
9
|
+
for (const NodeClass of registry?.nodes ?? []) {
|
|
10
|
+
const type = NodeClass?.type;
|
|
11
|
+
if (!type) continue;
|
|
12
|
+
map[type] = {
|
|
13
|
+
configSchema: serialize(NodeClass.configSchema),
|
|
14
|
+
credentialsSchema: serialize(NodeClass.credentialsSchema)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return map;
|
|
18
|
+
}
|
|
19
|
+
function provideSchemas(registry) {
|
|
20
|
+
return ({ provide }) => {
|
|
21
|
+
provide("__nrg_schemas", serializeRegistry(registry));
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function loadRegistry(cwd = process.cwd()) {
|
|
25
|
+
const entry = pathToFileURL(path.resolve(cwd, "src/server/index.ts")).href;
|
|
26
|
+
const mod = await import(entry);
|
|
27
|
+
return mod.default ?? mod;
|
|
28
|
+
}
|
|
29
|
+
async function schemas_default({ provide }) {
|
|
30
|
+
provide("__nrg_schemas", serializeRegistry(await loadRegistry()));
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
schemas_default as default,
|
|
34
|
+
loadRegistry,
|
|
35
|
+
provideSchemas,
|
|
36
|
+
serializeRegistry
|
|
37
|
+
};
|