@bonsae/nrg 0.25.1 → 0.26.1
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
|
@@ -235,9 +235,191 @@ function createNodeRedNode(options = {}) {
|
|
|
235
235
|
};
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
// src/core/validator.ts
|
|
239
|
+
import Ajv from "ajv";
|
|
240
|
+
import addFormats from "ajv-formats";
|
|
241
|
+
import addErrors from "ajv-errors";
|
|
242
|
+
var Validator = class {
|
|
243
|
+
ajv;
|
|
244
|
+
constructor(options) {
|
|
245
|
+
const { customKeywords, customFormats, ...ajvOptions } = options || {};
|
|
246
|
+
this.ajv = new Ajv({
|
|
247
|
+
allErrors: true,
|
|
248
|
+
code: {
|
|
249
|
+
source: false
|
|
250
|
+
},
|
|
251
|
+
coerceTypes: true,
|
|
252
|
+
removeAdditional: false,
|
|
253
|
+
strict: false,
|
|
254
|
+
strictSchema: false,
|
|
255
|
+
useDefaults: true,
|
|
256
|
+
validateFormats: true,
|
|
257
|
+
// NOTE: typebox handles validation via typescript
|
|
258
|
+
// NOTE: if true, types that are not serializable JSON, like Function, would not work
|
|
259
|
+
validateSchema: false,
|
|
260
|
+
verbose: true,
|
|
261
|
+
...ajvOptions
|
|
262
|
+
});
|
|
263
|
+
addFormats(this.ajv);
|
|
264
|
+
addErrors(this.ajv);
|
|
265
|
+
this.addCustomKeywords(customKeywords || []);
|
|
266
|
+
this.addCustomFormats(customFormats || {});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Add custom keywords to the validator
|
|
270
|
+
*/
|
|
271
|
+
addCustomKeywords(keywords) {
|
|
272
|
+
if (!keywords) return;
|
|
273
|
+
keywords.forEach((keyword) => {
|
|
274
|
+
this.ajv.addKeyword(keyword);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Add custom formats to the validator
|
|
279
|
+
*/
|
|
280
|
+
addCustomFormats(formats) {
|
|
281
|
+
if (!formats) return;
|
|
282
|
+
Object.entries(formats).forEach(([name, validator]) => {
|
|
283
|
+
if (validator instanceof RegExp) {
|
|
284
|
+
this.ajv.addFormat(name, validator);
|
|
285
|
+
} else {
|
|
286
|
+
this.ajv.addFormat(name, { validate: validator });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Create a validator function with caching
|
|
292
|
+
* @param schema - JSON Schema to validate against
|
|
293
|
+
* @param cacheKey - Optional cache key for reusing validators
|
|
294
|
+
*/
|
|
295
|
+
createValidator(schema, cacheKey) {
|
|
296
|
+
if (cacheKey && !schema.$id) {
|
|
297
|
+
schema.$id = cacheKey;
|
|
298
|
+
}
|
|
299
|
+
if (schema.$id) {
|
|
300
|
+
const cached = this.ajv.getSchema(schema.$id);
|
|
301
|
+
if (cached) return cached;
|
|
302
|
+
}
|
|
303
|
+
const validator = this.ajv.compile(schema);
|
|
304
|
+
return validator;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Validate data against a schema and return a structured result
|
|
308
|
+
*/
|
|
309
|
+
validate(data, schema, options) {
|
|
310
|
+
const validator = this.createValidator(schema, options?.cacheKey);
|
|
311
|
+
const valid = validator(data);
|
|
312
|
+
if (!valid) {
|
|
313
|
+
const errorMessage = this.formatErrors(validator.errors);
|
|
314
|
+
if (options?.throwOnError) {
|
|
315
|
+
throw new ValidationError(errorMessage, validator.errors || []);
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
valid: false,
|
|
319
|
+
errors: validator.errors || void 0,
|
|
320
|
+
errorMessage
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
valid: true,
|
|
325
|
+
data
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Format errors into a human-readable string
|
|
330
|
+
*/
|
|
331
|
+
formatErrors(errors, options) {
|
|
332
|
+
if (!errors || errors.length === 0) {
|
|
333
|
+
return "No errors";
|
|
334
|
+
}
|
|
335
|
+
return this.ajv.errorsText(errors, {
|
|
336
|
+
separator: "; ",
|
|
337
|
+
dataVar: "data",
|
|
338
|
+
...options
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get detailed error information
|
|
343
|
+
*/
|
|
344
|
+
getDetailedErrors(errors) {
|
|
345
|
+
if (!errors || errors.length === 0) return [];
|
|
346
|
+
return errors.map((error) => ({
|
|
347
|
+
field: error.instancePath || "/",
|
|
348
|
+
message: error.message || "Validation failed",
|
|
349
|
+
keyword: error.keyword,
|
|
350
|
+
params: error.params,
|
|
351
|
+
schemaPath: error.schemaPath
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Add a schema to the validator for reference
|
|
356
|
+
*/
|
|
357
|
+
addSchema(schema, key) {
|
|
358
|
+
this.ajv.addSchema(schema, key);
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Remove a schema from the validator
|
|
363
|
+
*/
|
|
364
|
+
removeSchema(key) {
|
|
365
|
+
this.ajv.removeSchema(key);
|
|
366
|
+
return this;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
var ValidationError = class _ValidationError extends Error {
|
|
370
|
+
constructor(message, errors) {
|
|
371
|
+
super(message);
|
|
372
|
+
this.errors = errors;
|
|
373
|
+
this.name = "ValidationError";
|
|
374
|
+
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// src/core/server/validation.ts
|
|
379
|
+
function initValidator(RED) {
|
|
380
|
+
if (RED.validator) return;
|
|
381
|
+
const nrg = {
|
|
382
|
+
validator: new Validator({
|
|
383
|
+
customKeywords: [
|
|
384
|
+
{
|
|
385
|
+
keyword: "x-nrg-skip-validation",
|
|
386
|
+
schemaType: "boolean",
|
|
387
|
+
valid: true
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
keyword: "x-nrg-node-type",
|
|
391
|
+
type: "string",
|
|
392
|
+
validate: (schemaValue, dataValue) => {
|
|
393
|
+
if (!dataValue) return true;
|
|
394
|
+
const node = RED.nodes.getNode(dataValue);
|
|
395
|
+
return node?.type === schemaValue;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
],
|
|
399
|
+
customFormats: {
|
|
400
|
+
"node-id": /^[a-zA-Z0-9-_]+$/,
|
|
401
|
+
"flow-id": /^[a-f0-9]{16}$/,
|
|
402
|
+
"topic-path": (data) => /^[a-zA-Z0-9/_-]+$/.test(data)
|
|
403
|
+
}
|
|
404
|
+
})
|
|
405
|
+
};
|
|
406
|
+
Object.defineProperty(RED, "_nrg", {
|
|
407
|
+
value: nrg,
|
|
408
|
+
writable: false,
|
|
409
|
+
enumerable: false,
|
|
410
|
+
configurable: false
|
|
411
|
+
});
|
|
412
|
+
Object.defineProperty(RED, "validator", {
|
|
413
|
+
get: () => nrg.validator,
|
|
414
|
+
enumerable: false,
|
|
415
|
+
configurable: false
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/core/server/nodes/symbols.ts
|
|
420
|
+
var WIRE_HANDLERS = Symbol.for("nrg.wireHandlers");
|
|
421
|
+
|
|
238
422
|
// src/test/server/unit/index.ts
|
|
239
|
-
import { initValidator } from "@bonsae/nrg-runtime/internal/server";
|
|
240
|
-
import { WIRE_HANDLERS } from "@bonsae/nrg-runtime/internal/server";
|
|
241
423
|
import { Kind } from "@sinclair/typebox";
|
|
242
424
|
function builtinPortIndex(node, name) {
|
|
243
425
|
if (name !== "error" && name !== "complete" && name !== "status") {
|
package/types/client.d.ts
CHANGED
|
@@ -228,7 +228,7 @@ export interface TypedInputValue {
|
|
|
228
228
|
/**
|
|
229
229
|
* Maps a schema's static type to the raw values the editor form holds.
|
|
230
230
|
* The server counterpart (`ResolvedStatic` in server/schemas/types) maps the
|
|
231
|
-
* same brands — shared via core/
|
|
231
|
+
* same brands — shared via core/types — to resolved runtime values instead.
|
|
232
232
|
* - `NodeRef<T>` → `string` (the referenced node's id)
|
|
233
233
|
* - `TypedInput<T>` → `TypedInputValue` (raw value + type pair)
|
|
234
234
|
* - Functions pass through, arrays and objects map recursively
|
package/types/server.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference path="./shims/typebox.d.ts" />
|
|
2
2
|
// Generated by dts-bundle-generator v9.5.1
|
|
3
3
|
|
|
4
|
-
import { Kind, ObjectOptions, SchemaOptions, Static, TArray, TBoolean, TConst, TEnum, TFunction, TInteger, TIntersect, TLiteral, TNull, TNumber, TObject, TOptional, TProperties, TRecord, TRef, TSchema, TString, TTuple, TUnion } from '@sinclair/typebox';
|
|
4
|
+
import { Kind, ObjectOptions, SchemaOptions, Static, StringFormatOption, StringOptions, TArray, TBoolean, TConst, TEnum, TFunction, TInteger, TIntersect, TLiteral, TNull, TNumber, TObject, TOptional, TProperties, TRecord, TRef, TSchema, TString, TTuple, TUnion } from '@sinclair/typebox';
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
6
6
|
import { Express as Express$1 } from 'express';
|
|
7
7
|
import { Http2ServerRequest } from 'http2';
|
|
@@ -508,9 +508,14 @@ declare function NodeRef<T extends (new (...args: any[]) => any) & {
|
|
|
508
508
|
type: string;
|
|
509
509
|
}>(nodeClass: T, options?: NrgSchemaOptions): TNodeRef<InstanceType<T>>;
|
|
510
510
|
declare function TypedInput$1<T = unknown>(options?: NrgSchemaOptions): TTypedInput<T>;
|
|
511
|
+
type NrgStringFormat = StringFormatOption | "password" | "byte" | "binary" | "url" | "duration" | "iso-time" | "iso-date-time" | "json-pointer-uri-fragment";
|
|
512
|
+
interface NrgStringOptions extends Omit<StringOptions, "format"> {
|
|
513
|
+
format?: NrgStringFormat;
|
|
514
|
+
}
|
|
515
|
+
declare function NrgString(options?: NrgStringOptions): TString;
|
|
511
516
|
declare function OutputReturnProperties(options?: NrgSchemaOptions & {
|
|
512
517
|
default?: Record<number, string>;
|
|
513
|
-
}): import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TNumber,
|
|
518
|
+
}): import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TNumber, TString>;
|
|
514
519
|
declare function OutputContextModes(options?: NrgSchemaOptions & {
|
|
515
520
|
default?: Record<number, "carry" | "trace" | "reset">;
|
|
516
521
|
}): import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TNumber, import("@sinclair/typebox").TUnion<[
|
|
@@ -530,6 +535,7 @@ declare function OutputContextModes(options?: NrgSchemaOptions & {
|
|
|
530
535
|
* use {@link NodeRef} / {@link TypedInput} instead. See the Schemas guide.
|
|
531
536
|
*/
|
|
532
537
|
export declare const SchemaType: import("@sinclair/typebox").JavaScriptTypeBuilder & {
|
|
538
|
+
String: typeof NrgString;
|
|
533
539
|
NodeRef: typeof NodeRef;
|
|
534
540
|
TypedInput: typeof TypedInput$1;
|
|
535
541
|
OutputReturnProperties: typeof OutputReturnProperties;
|
|
@@ -11,13 +11,13 @@ declare module "vue" {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface GlobalComponents {
|
|
14
|
-
NodeRedConfigInput: (typeof import("./client/form/components/node-red-config-input.vue"))["default"];
|
|
15
|
-
NodeRedEditorInput: (typeof import("./client/form/components/node-red-editor-input.vue"))["default"];
|
|
16
|
-
NodeRedInputLabel: (typeof import("./client/form/components/node-red-input-label.vue"))["default"];
|
|
17
|
-
NodeRedInput: (typeof import("./client/form/components/node-red-input.vue"))["default"];
|
|
18
|
-
NodeRedJsonSchemaForm: (typeof import("./client/form/components/node-red-json-schema-form.vue"))["default"];
|
|
19
|
-
NodeRedSelectInput: (typeof import("./client/form/components/node-red-select-input.vue"))["default"];
|
|
20
|
-
NodeRedToggle: (typeof import("./client/form/components/node-red-toggle.vue"))["default"];
|
|
21
|
-
NodeRedTypedInput: (typeof import("./client/form/components/node-red-typed-input.vue"))["default"];
|
|
14
|
+
NodeRedConfigInput: (typeof import("./core/client/form/components/node-red-config-input.vue"))["default"];
|
|
15
|
+
NodeRedEditorInput: (typeof import("./core/client/form/components/node-red-editor-input.vue"))["default"];
|
|
16
|
+
NodeRedInputLabel: (typeof import("./core/client/form/components/node-red-input-label.vue"))["default"];
|
|
17
|
+
NodeRedInput: (typeof import("./core/client/form/components/node-red-input.vue"))["default"];
|
|
18
|
+
NodeRedJsonSchemaForm: (typeof import("./core/client/form/components/node-red-json-schema-form.vue"))["default"];
|
|
19
|
+
NodeRedSelectInput: (typeof import("./core/client/form/components/node-red-select-input.vue"))["default"];
|
|
20
|
+
NodeRedToggle: (typeof import("./core/client/form/components/node-red-toggle.vue"))["default"];
|
|
21
|
+
NodeRedTypedInput: (typeof import("./core/client/form/components/node-red-typed-input.vue"))["default"];
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Component, App } from "vue";
|
|
2
2
|
import type { TSchema, Static } from "@sinclair/typebox";
|
|
3
3
|
import type { SchemaObject } from "ajv";
|
|
4
|
-
import type { NodeRefResolved, TypedInputResolved } from "../
|
|
4
|
+
import type { NodeRefResolved, TypedInputResolved } from "../types";
|
|
5
5
|
import type { JsonSchemaObjectExtensions } from "../schema-options";
|
|
6
6
|
export interface NodeStateCredentials {
|
|
7
7
|
[key: string]: any;
|
|
@@ -200,7 +200,7 @@ export interface TypedInputValue {
|
|
|
200
200
|
/**
|
|
201
201
|
* Maps a schema's static type to the raw values the editor form holds.
|
|
202
202
|
* The server counterpart (`ResolvedStatic` in server/schemas/types) maps the
|
|
203
|
-
* same brands — shared via core/
|
|
203
|
+
* same brands — shared via core/types — to resolved runtime values instead.
|
|
204
204
|
* - `NodeRef<T>` → `string` (the referenced node's id)
|
|
205
205
|
* - `TypedInput<T>` → `TypedInputValue` (raw value + type pair)
|
|
206
206
|
* - Functions pass through, arrays and objects map recursively
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NRG's JSON Schema vocabulary — the custom keywords the server emits onto
|
|
3
|
+
* serialized schemas and the client consumes for form rendering and
|
|
4
|
+
* validation. Shared at the core root (like types.ts) so both planes derive
|
|
5
|
+
* from one definition instead of drifting copies.
|
|
6
|
+
*/
|
|
7
|
+
export interface JsonSchemaObjectExtensions {
|
|
8
|
+
format?: "node-id" | "flow-id" | "topic-path" | (string & {});
|
|
9
|
+
/** expose this settings property to the editor via RED.settings */
|
|
10
|
+
exportable?: boolean;
|
|
11
|
+
/** set by SchemaType.NodeRef — the referenced config node type */
|
|
12
|
+
"x-nrg-node-type"?: string;
|
|
13
|
+
/** set by SchemaType.TypedInput — marks a TypedInput value/type pair */
|
|
14
|
+
"x-nrg-typed-input"?: boolean;
|
|
15
|
+
/** set by markNonValidatable — ajv skips this property */
|
|
16
|
+
"x-nrg-skip-validation"?: boolean;
|
|
17
|
+
/** form rendering hints consumed by the auto-generated editor form */
|
|
18
|
+
"x-nrg-form"?: {
|
|
19
|
+
icon?: string;
|
|
20
|
+
typedInputTypes?: string[];
|
|
21
|
+
editorLanguage?: string;
|
|
22
|
+
toggle?: boolean;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* NRG's JSON Schema vocabulary — the custom keywords the server emits onto
|
|
3
3
|
* serialized schemas and the client consumes for form rendering and
|
|
4
|
-
* validation. Shared at the core root (like
|
|
4
|
+
* validation. Shared at the core root (like types.ts) so both planes derive
|
|
5
5
|
* from one definition instead of drifting copies.
|
|
6
6
|
*/
|
|
7
7
|
export interface JsonSchemaObjectExtensions {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
8
|
+
format?: "node-id" | "flow-id" | "topic-path" | (string & {});
|
|
9
|
+
/** expose this settings property to the editor via RED.settings */
|
|
10
|
+
exportable?: boolean;
|
|
11
|
+
/** set by SchemaType.NodeRef — the referenced config node type */
|
|
12
|
+
"x-nrg-node-type"?: string;
|
|
13
|
+
/** set by SchemaType.TypedInput — marks a TypedInput value/type pair */
|
|
14
|
+
"x-nrg-typed-input"?: boolean;
|
|
15
|
+
/** set by markNonValidatable — ajv skips this property */
|
|
16
|
+
"x-nrg-skip-validation"?: boolean;
|
|
17
|
+
/** form rendering hints consumed by the auto-generated editor form */
|
|
18
|
+
"x-nrg-form"?: {
|
|
19
|
+
icon?: string;
|
|
20
|
+
typedInputTypes?: string[];
|
|
21
|
+
editorLanguage?: string;
|
|
22
|
+
toggle?: boolean;
|
|
23
|
+
};
|
|
24
24
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
+
|
|
3
|
+
import { ProvidedContext } from 'vitest';
|
|
4
|
+
|
|
5
|
+
interface GlobalSetupContext {
|
|
6
|
+
provide<K extends keyof ProvidedContext>(key: K, value: ProvidedContext[K]): void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A node's schemas, serialized to plain JSON. This is the exact shape the vite
|
|
10
|
+
* plugin injects into the production editor bundle (`JSON.stringify` of the
|
|
11
|
+
* TypeBox schema), so component tests validate against the same data the real
|
|
12
|
+
* form does — with no TypeBox `Kind` symbols or server runtime in the browser.
|
|
13
|
+
*/
|
|
14
|
+
export interface SerializedNodeSchemas {
|
|
15
|
+
configSchema?: Record<string, unknown>;
|
|
16
|
+
credentialsSchema?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
interface NodeClass {
|
|
19
|
+
type?: string;
|
|
20
|
+
configSchema?: unknown;
|
|
21
|
+
credentialsSchema?: unknown;
|
|
22
|
+
}
|
|
23
|
+
interface Registry {
|
|
24
|
+
nodes?: NodeClass[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Serializes every node in a registry (`defineModule({ nodes })`) to a map
|
|
28
|
+
* keyed by node `type`. Pure data in, pure data out — runs in Node.
|
|
29
|
+
*/
|
|
30
|
+
export declare function serializeRegistry(registry: Registry | undefined): Record<string, SerializedNodeSchemas>;
|
|
31
|
+
/**
|
|
32
|
+
* Builds a vitest `globalSetup` that serializes an explicitly-provided node
|
|
33
|
+
* registry and provides it to component tests. Use this when your registry is
|
|
34
|
+
* not at the conventional `src/server` entry; otherwise reference the default
|
|
35
|
+
* export of this module directly from your config.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* // tests/client/component/schemas.ts
|
|
40
|
+
* import { provideSchemas } from "@bonsae/nrg/test/client/component/schemas";
|
|
41
|
+
* import registry from "../../../src/server";
|
|
42
|
+
* export default provideSchemas(registry);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function provideSchemas(registry: Registry): ({ provide }: GlobalSetupContext) => void;
|
|
46
|
+
/**
|
|
47
|
+
* Imports a package's node registry from the conventional `src/server` entry.
|
|
48
|
+
* Resolves the default export (`defineModule({ nodes })`), or the module itself
|
|
49
|
+
* if it exposes `nodes` directly. Runs in Node, so the server import is safe.
|
|
50
|
+
*
|
|
51
|
+
* @param cwd Package root to resolve `src/server/index.ts` against. Defaults to
|
|
52
|
+
* the working directory (where vitest runs), which is the package root.
|
|
53
|
+
*/
|
|
54
|
+
export declare function loadRegistry(cwd?: string): Promise<Registry>;
|
|
55
|
+
/**
|
|
56
|
+
* Convention `globalSetup`: serializes the package's node registry — the
|
|
57
|
+
* default export of `src/server` — and provides every node's schemas as data,
|
|
58
|
+
* so component tests validate against the real schema WITHOUT value-importing
|
|
59
|
+
* the server runtime into the browser. `createNode({ type })` reads it back.
|
|
60
|
+
*
|
|
61
|
+
* Reference it directly from your vitest config — no per-package file needed:
|
|
62
|
+
*
|
|
63
|
+
* ```ts
|
|
64
|
+
* test: { globalSetup: ["@bonsae/nrg/test/client/component/schemas"] }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare function _default({ provide }: GlobalSetupContext): Promise<void>;
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
_default as default,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export {};
|
|
@@ -1,13 +1,125 @@
|
|
|
1
1
|
// Generated by dts-bundle-generator v9.5.1
|
|
2
2
|
|
|
3
|
-
import { TSchema } from '@sinclair/typebox';
|
|
3
|
+
import { Static, TSchema } from '@sinclair/typebox';
|
|
4
|
+
import { SchemaObject } from 'ajv';
|
|
5
|
+
import { App } from 'vue';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
interface NodeRefResolved<T = any> {
|
|
8
|
+
readonly __nrg_node_ref: true;
|
|
9
|
+
readonly __instance: T;
|
|
10
|
+
}
|
|
11
|
+
interface TypedInputResolved {
|
|
12
|
+
resolve(...args: any[]): any;
|
|
13
|
+
value: unknown;
|
|
14
|
+
type: string;
|
|
15
|
+
}
|
|
16
|
+
interface JsonSchemaObjectExtensions {
|
|
17
|
+
format?: "node-id" | "flow-id" | "topic-path" | (string & {});
|
|
18
|
+
/** expose this settings property to the editor via RED.settings */
|
|
19
|
+
exportable?: boolean;
|
|
20
|
+
/** set by SchemaType.NodeRef — the referenced config node type */
|
|
21
|
+
"x-nrg-node-type"?: string;
|
|
22
|
+
/** set by SchemaType.TypedInput — marks a TypedInput value/type pair */
|
|
23
|
+
"x-nrg-typed-input"?: boolean;
|
|
24
|
+
/** set by markNonValidatable — ajv skips this property */
|
|
25
|
+
"x-nrg-skip-validation"?: boolean;
|
|
26
|
+
/** form rendering hints consumed by the auto-generated editor form */
|
|
27
|
+
"x-nrg-form"?: {
|
|
28
|
+
icon?: string;
|
|
29
|
+
typedInputTypes?: string[];
|
|
30
|
+
editorLanguage?: string;
|
|
31
|
+
toggle?: boolean;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
interface NodeRedNodeButtonDefinition {
|
|
35
|
+
toggle: string;
|
|
36
|
+
onclick: () => void;
|
|
37
|
+
enabled?: () => boolean;
|
|
38
|
+
visible?: () => boolean;
|
|
39
|
+
}
|
|
40
|
+
interface NodeRedNode {
|
|
41
|
+
id: string;
|
|
42
|
+
type: string;
|
|
43
|
+
name: string;
|
|
44
|
+
category: string;
|
|
45
|
+
x: string;
|
|
46
|
+
y: string;
|
|
47
|
+
g: string;
|
|
48
|
+
z: string;
|
|
49
|
+
credentials: Record<string, any>;
|
|
50
|
+
_def: {
|
|
51
|
+
defaults: Record<string, {
|
|
52
|
+
value: string;
|
|
53
|
+
type?: string;
|
|
54
|
+
label?: string;
|
|
55
|
+
required?: boolean;
|
|
56
|
+
}>;
|
|
57
|
+
credentials: Record<string, {
|
|
58
|
+
value: string;
|
|
59
|
+
type?: "password" | "text";
|
|
60
|
+
label?: string;
|
|
61
|
+
required?: boolean;
|
|
62
|
+
}>;
|
|
63
|
+
category: string;
|
|
64
|
+
color?: string;
|
|
65
|
+
icon?: string;
|
|
66
|
+
label?: ((this: NodeRedNode) => string) | string;
|
|
67
|
+
inputs?: number;
|
|
68
|
+
outputs?: number;
|
|
69
|
+
paletteLabel?: ((this: NodeRedNode) => string) | string;
|
|
70
|
+
labelStyle?: ((this: NodeRedNode) => string) | string;
|
|
71
|
+
inputLabels?: ((this: NodeRedNode, index: number) => string) | string;
|
|
72
|
+
outputLabels?: ((this: NodeRedNode, index: number) => string) | string;
|
|
73
|
+
align?: "left" | "right";
|
|
74
|
+
button?: NodeRedNodeButtonDefinition;
|
|
75
|
+
};
|
|
76
|
+
_newState?: NodeRedNode;
|
|
77
|
+
_app?: App | null;
|
|
78
|
+
_: (str: string) => string;
|
|
79
|
+
/** dynamic port count (base outputs + enabled built-in ports) */
|
|
80
|
+
outputs?: number;
|
|
81
|
+
/** injected when the node has an inputSchema */
|
|
82
|
+
validateInput?: boolean;
|
|
83
|
+
/** built-in port toggles, present when declared in the configSchema */
|
|
84
|
+
errorPort?: boolean;
|
|
85
|
+
completePort?: boolean;
|
|
86
|
+
statusPort?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Per-port output settings, indexed by base-output port. `validateOutputs` is
|
|
89
|
+
* injected (empty) when the node has an outputsSchema; `outputReturnProperties`
|
|
90
|
+
* and `outputContextModes` are author-declared (SchemaType.*) — present only
|
|
91
|
+
* when the node opts into per-port return keys / context modes. Read at
|
|
92
|
+
* runtime by IONode.
|
|
93
|
+
*/
|
|
94
|
+
validateOutputs?: Record<number, boolean>;
|
|
95
|
+
outputContextModes?: Record<number, "carry" | "trace" | "reset">;
|
|
96
|
+
outputReturnProperties?: Record<number, string>;
|
|
97
|
+
[key: string]: any;
|
|
98
|
+
}
|
|
99
|
+
interface JsonPropertySchema extends JsonSchemaObjectExtensions {
|
|
100
|
+
type?: string | string[];
|
|
101
|
+
properties?: Record<string, JsonPropertySchema>;
|
|
102
|
+
required?: string[];
|
|
103
|
+
enum?: unknown[];
|
|
104
|
+
anyOf?: JsonPropertySchema[];
|
|
105
|
+
const?: unknown;
|
|
106
|
+
items?: JsonPropertySchema;
|
|
107
|
+
title?: string;
|
|
108
|
+
description?: string;
|
|
109
|
+
default?: unknown;
|
|
110
|
+
}
|
|
111
|
+
interface JsonSchemaObject extends SchemaObject {
|
|
112
|
+
type: "object";
|
|
113
|
+
properties?: Record<string, JsonPropertySchema>;
|
|
114
|
+
required?: string[];
|
|
115
|
+
}
|
|
116
|
+
interface TypedInputValue {
|
|
117
|
+
value: string;
|
|
118
|
+
type: string;
|
|
119
|
+
}
|
|
120
|
+
type EditorStatic<T> = T extends NodeRefResolved<any> ? string : T extends TypedInputResolved ? TypedInputValue : T extends (...args: any[]) => any ? T : T extends Array<infer I> ? EditorStatic<I>[] : T extends object ? {
|
|
121
|
+
[K in keyof T]: EditorStatic<T[K]>;
|
|
122
|
+
} : T;
|
|
11
123
|
export interface MockEditor {
|
|
12
124
|
getValue(): string;
|
|
13
125
|
setValue(val: string): void;
|
|
@@ -104,6 +216,30 @@ export interface MockRED {
|
|
|
104
216
|
settings: MockSettings;
|
|
105
217
|
notify(message: any, options?: Record<string, any>): MockNotification;
|
|
106
218
|
}
|
|
219
|
+
interface FormNode<TConfig extends TSchema = TSchema, TCredentials extends TSchema = TSchema> {
|
|
220
|
+
node: NodeRedNode & EditorStatic<Static<TConfig>> & {
|
|
221
|
+
credentials: EditorStatic<Static<TCredentials>> & Record<string, any>;
|
|
222
|
+
};
|
|
223
|
+
schema: Record<string, any>;
|
|
224
|
+
errors: Record<string, string>;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Composable that provides typed access to the form node, schema, and errors.
|
|
228
|
+
* Replaces `defineProps` in custom form components — no props declaration needed.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```vue
|
|
232
|
+
* <script setup lang="ts">
|
|
233
|
+
* import { useFormNode } from "@bonsae/nrg/client";
|
|
234
|
+
* import type { ConfigsSchema, CredentialsSchema } from "../../server/schemas/my-node";
|
|
235
|
+
*
|
|
236
|
+
* const { node, errors } = useFormNode<typeof ConfigsSchema, typeof CredentialsSchema>();
|
|
237
|
+
* node.name // string — typed from ConfigsSchema
|
|
238
|
+
* node.credentials.apiKey // string — typed from CredentialsSchema
|
|
239
|
+
* </script>
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
export declare function useFormNode<TConfig extends TSchema = TSchema, TCredentials extends TSchema = TSchema>(): FormNode<TConfig, TCredentials>;
|
|
107
243
|
export interface TestNode {
|
|
108
244
|
id: string;
|
|
109
245
|
type: string;
|
|
@@ -125,6 +261,16 @@ export interface CreateNodeResult {
|
|
|
125
261
|
provide: FormProvide;
|
|
126
262
|
}
|
|
127
263
|
export interface CreateNodeOptions {
|
|
264
|
+
/**
|
|
265
|
+
* The node's registered `type`. When set, createNode resolves each schema not
|
|
266
|
+
* passed explicitly (configSchema/credentialsSchema) from the map serialized
|
|
267
|
+
* by the `schemas` globalSetup — so the test validates against the production
|
|
268
|
+
* schema without importing it. Used only for schema lookup; the harness keeps
|
|
269
|
+
* its own unique internal node type. Note: this is a reserved options key — a
|
|
270
|
+
* raw-shorthand config object that itself carries a `type` field must be
|
|
271
|
+
* passed via the explicit `{ configs }` form.
|
|
272
|
+
*/
|
|
273
|
+
type?: string;
|
|
128
274
|
configs?: Record<string, any>;
|
|
129
275
|
credentials?: Record<string, any>;
|
|
130
276
|
configSchema?: JsonSchemaObject;
|