@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.
Files changed (34) hide show
  1. package/package.json +6 -4
  2. package/server/index.cjs +1413 -2
  3. package/server/resources/nrg-client.js +10 -0
  4. package/test/client/component/config.js +9 -9
  5. package/test/client/component/index.js +230 -42
  6. package/test/client/component/nrg.css +1 -0
  7. package/test/client/component/schemas.js +37 -0
  8. package/test/client/component/setup.js +1473 -198
  9. package/test/client/e2e/index.js +33 -35
  10. package/test/client/unit/index.js +17 -2
  11. package/test/server/integration/index.js +1222 -11
  12. package/test/server/unit/index.js +184 -2
  13. package/types/client.d.ts +1 -1
  14. package/types/server.d.ts +8 -2
  15. package/types/shims/components.d.ts +8 -8
  16. package/types/shims/{client → core/client}/types.d.ts +2 -2
  17. package/types/shims/core/schema-options.d.ts +24 -0
  18. package/types/shims/schema-options.d.ts +17 -17
  19. package/types/test-client-component-schemas.d.ts +73 -0
  20. package/types/test-client-component.d.ts +153 -7
  21. package/types/test-client-unit.d.ts +105 -4
  22. package/types/test-server-integration.d.ts +64 -63
  23. package/types/test-server-unit.d.ts +2 -1
  24. package/vite/index.js +33 -35
  25. /package/types/shims/{client → core/client}/form/components/node-red-config-input.vue.d.ts +0 -0
  26. /package/types/shims/{client → core/client}/form/components/node-red-editor-input.vue.d.ts +0 -0
  27. /package/types/shims/{client → core/client}/form/components/node-red-input-label.vue.d.ts +0 -0
  28. /package/types/shims/{client → core/client}/form/components/node-red-input.vue.d.ts +0 -0
  29. /package/types/shims/{client → core/client}/form/components/node-red-json-schema-form.vue.d.ts +0 -0
  30. /package/types/shims/{client → core/client}/form/components/node-red-select-input.vue.d.ts +0 -0
  31. /package/types/shims/{client → core/client}/form/components/node-red-toggle.vue.d.ts +0 -0
  32. /package/types/shims/{client → core/client}/form/components/node-red-typed-input.vue.d.ts +0 -0
  33. /package/types/shims/{constants.d.ts → core/constants.d.ts} +0 -0
  34. /package/types/shims/{brands.d.ts → core/types.d.ts} +0 -0
@@ -1,59 +1,247 @@
1
- import { vi as f } from "vitest";
2
- import { reactive as m, watch as _ } from "vue";
3
- import { composeValidationSchema as g, validateForm as p } from "@bonsae/nrg-runtime/internal/client";
4
- import { useFormNode as O } from "@bonsae/nrg-runtime/internal/client";
5
- let l = 0;
6
- function w(e = {}) {
7
- const n = "configs" in e || "configSchema" in e || "credentialsSchema" in e || "nodes" in e ? e : { configs: e }, s = m({
8
- id: `test-${l}`,
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-${l++}`,
196
+ type: `test-node-${w++}`,
13
197
  changed: !1,
14
198
  _def: { outputs: 1 },
15
- _: (t) => t,
16
- ...n.configs,
17
- credentials: { ...n.credentials }
18
- }), a = g(
19
- n.configSchema,
20
- n.credentialsSchema
21
- );
22
- let r;
23
- if (a) {
24
- const { $id: t, ...i } = a;
25
- r = i;
26
- }
27
- const d = v();
28
- u(d), d.nodes.clear();
29
- for (const t of n.nodes ?? [])
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 r && _(
35
- s,
215
+ return d && S(
216
+ n,
36
217
  () => {
37
- const t = p(s, r);
38
- Object.keys(c).forEach((i) => delete c[i]), Object.assign(c, t);
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: s, errors: c, RED: d, provide: {
42
- __nrg_form_node: s,
43
- __nrg_form_schema: r ?? {},
44
- __nrg_form_errors: c
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 o(e, n) {
48
- f.isMockFunction(e[n]) || f.spyOn(e, n);
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 u(e) {
51
- o(e, "_"), o(e, "notify"), o(e.editor, "createEditor"), o(e.editor, "prepareConfigNodeSelect"), o(e.editor, "validateNode"), o(e.tray, "show"), o(e.tray, "close"), o(e.popover, "tooltip"), o(e.popover, "create"), o(e.nodes, "registerType"), o(e.nodes, "node"), o(e.nodes, "add"), o(e.nodes, "remove"), o(e.nodes, "getType"), o(e.nodes, "dirty"), o(e.events, "on"), o(e.events, "off"), o(e.events, "emit"), o(e.comms, "subscribe"), o(e.comms, "unsubscribe");
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 v() {
241
+ function T() {
54
242
  return window.RED;
55
243
  }
56
244
  export {
57
- w as createNode,
58
- O as useFormNode
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
+ };