@bonsae/nrg 0.18.5 → 0.19.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 +1 -1
- package/server/index.cjs +86 -9
- package/server/resources/nrg-client.js +2020 -1987
- package/test/client/component/config.js +11 -0
- package/test/client/component/index.js +218 -235
- package/test/client/component/nrg.css +1 -0
- package/test/client/component/setup.js +1549 -140
- package/test/client/e2e/index.js +720 -369
- package/test/client/unit/index.js +204 -16
- package/test/client/unit/setup.js +209 -19
- package/test/server/unit/index.js +25 -4
- package/tsconfig/core/client.json +1 -1
- package/tsconfig/test/client/component.json +1 -1
- package/types/client.d.ts +98 -18
- package/types/server.d.ts +50 -12
- package/types/shims/brands.d.ts +32 -0
- package/types/shims/{form → client/form}/components/node-red-editor-input.vue.d.ts +1 -1
- package/types/shims/{form → client/form}/components/node-red-json-schema-form.vue.d.ts +21 -2
- package/types/shims/{form → client/form}/components/node-red-select-input.vue.d.ts +1 -0
- package/types/shims/{form → client/form}/components/node-red-typed-input.vue.d.ts +1 -0
- package/types/shims/client/types.d.ts +206 -0
- package/types/shims/components.d.ts +8 -8
- package/types/shims/constants.d.ts +4 -0
- package/types/shims/schema-options.d.ts +23 -10
- package/types/shims/typebox.d.ts +2 -2
- package/types/test-client-component.d.ts +170 -55
- package/types/test-client-e2e.d.ts +50 -0
- package/types/test-client-unit.d.ts +86 -22
- package/types/test-server-unit.d.ts +3 -1
- package/types/vite.d.ts +38 -9
- package/vite/index.js +732 -530
- /package/types/shims/{form → client/form}/components/node-red-config-input.vue.d.ts +0 -0
- /package/types/shims/{form → client/form}/components/node-red-input-label.vue.d.ts +0 -0
- /package/types/shims/{form → client/form}/components/node-red-input.vue.d.ts +0 -0
- /package/types/shims/{form → client/form}/components/node-red-toggle.vue.d.ts +0 -0
- /package/types/shims/{globals.d.ts → client/globals.d.ts} +0 -0
package/package.json
CHANGED
package/server/index.cjs
CHANGED
|
@@ -437,10 +437,14 @@ var Node = class _Node {
|
|
|
437
437
|
};
|
|
438
438
|
|
|
439
439
|
// src/core/server/nodes/io-node.ts
|
|
440
|
+
var RETURN_PROPERTY_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
441
|
+
var INPUT_KEY = "input";
|
|
440
442
|
var IONode = class extends Node {
|
|
441
443
|
static align;
|
|
442
444
|
static color;
|
|
443
445
|
static inputSchema;
|
|
446
|
+
// outputsSchema accepts any schema shape: with returnProperty the raw sent
|
|
447
|
+
// value is validated, and results are frequently non-objects.
|
|
444
448
|
static outputsSchema;
|
|
445
449
|
static validateInput = false;
|
|
446
450
|
static validateOutput = false;
|
|
@@ -468,6 +472,12 @@ var IONode = class extends Node {
|
|
|
468
472
|
return keys.length;
|
|
469
473
|
}
|
|
470
474
|
#send;
|
|
475
|
+
/**
|
|
476
|
+
* Most recent input message — the spread base for returnProperty wrapping. Not
|
|
477
|
+
* cleared after input() so late async sends merge with the last received
|
|
478
|
+
* message.
|
|
479
|
+
*/
|
|
480
|
+
#currentInputMsg;
|
|
471
481
|
context;
|
|
472
482
|
constructor(RED, node, config, credentials) {
|
|
473
483
|
super(RED, node, config, credentials);
|
|
@@ -480,6 +490,12 @@ var IONode = class extends Node {
|
|
|
480
490
|
fn.flow = setupContext(context.flow);
|
|
481
491
|
fn.global = setupContext(context.global);
|
|
482
492
|
this.context = fn;
|
|
493
|
+
const returnPropertyKey = this.#returnPropertyKey();
|
|
494
|
+
if (!RETURN_PROPERTY_PATTERN.test(returnPropertyKey)) {
|
|
495
|
+
throw new NrgError(
|
|
496
|
+
`Invalid returnProperty key "${returnPropertyKey}" in ${this.constructor.type} \u2014 it must be a valid JavaScript identifier (letters, digits, _, $; not starting with a digit)`
|
|
497
|
+
);
|
|
498
|
+
}
|
|
483
499
|
}
|
|
484
500
|
[WIRE_HANDLERS](nodeRedNode, createdPromise) {
|
|
485
501
|
super[WIRE_HANDLERS](nodeRedNode, createdPromise);
|
|
@@ -495,12 +511,14 @@ var IONode = class extends Node {
|
|
|
495
511
|
}
|
|
496
512
|
try {
|
|
497
513
|
nodeRedNode.log("Calling input");
|
|
514
|
+
this.#currentInputMsg = msg;
|
|
498
515
|
await Promise.resolve(this.#input(msg, send));
|
|
499
516
|
this.#sendToPort("complete", {
|
|
500
517
|
...msg,
|
|
501
518
|
complete: {
|
|
502
519
|
source: this.#nodeSource()
|
|
503
|
-
}
|
|
520
|
+
},
|
|
521
|
+
[INPUT_KEY]: msg
|
|
504
522
|
});
|
|
505
523
|
done();
|
|
506
524
|
nodeRedNode.log("Input processed");
|
|
@@ -511,7 +529,8 @@ var IONode = class extends Node {
|
|
|
511
529
|
error: {
|
|
512
530
|
message: errorMsg,
|
|
513
531
|
source: this.#nodeSource()
|
|
514
|
-
}
|
|
532
|
+
},
|
|
533
|
+
[INPUT_KEY]: msg
|
|
515
534
|
});
|
|
516
535
|
if (error instanceof Error) {
|
|
517
536
|
nodeRedNode.error(
|
|
@@ -550,8 +569,9 @@ var IONode = class extends Node {
|
|
|
550
569
|
this.#send = void 0;
|
|
551
570
|
}
|
|
552
571
|
}
|
|
553
|
-
send(msg) {
|
|
572
|
+
send(msg, contextMode = "nest") {
|
|
554
573
|
const NodeClass = this.constructor;
|
|
574
|
+
const sendsValue = this.baseOutputs <= 1;
|
|
555
575
|
const shouldValidateOutput = this.config.validateOutput ?? NodeClass.validateOutput;
|
|
556
576
|
if (shouldValidateOutput && NodeClass.outputsSchema) {
|
|
557
577
|
this.log("Validating output");
|
|
@@ -566,7 +586,7 @@ var IONode = class extends Node {
|
|
|
566
586
|
});
|
|
567
587
|
}
|
|
568
588
|
} else if (isSchemaLike(rawSchema)) {
|
|
569
|
-
if (Array.isArray(msg)) {
|
|
589
|
+
if (Array.isArray(msg) && !sendsValue) {
|
|
570
590
|
const msgs = msg;
|
|
571
591
|
for (let i = 0; i < msgs.length; i++) {
|
|
572
592
|
if (msgs[i] == null) continue;
|
|
@@ -594,13 +614,57 @@ var IONode = class extends Node {
|
|
|
594
614
|
}
|
|
595
615
|
this.log("Output is valid");
|
|
596
616
|
}
|
|
597
|
-
const
|
|
617
|
+
const truncated = Array.isArray(msg) && !sendsValue ? msg.slice(0, this.baseOutputs) : msg;
|
|
618
|
+
const out = Array.isArray(truncated) && !sendsValue ? truncated.map(
|
|
619
|
+
(m) => m == null ? m : this.#wrapOutgoing(m, contextMode)
|
|
620
|
+
) : truncated == null ? truncated : this.#wrapOutgoing(truncated, contextMode);
|
|
598
621
|
if (this.#send) {
|
|
599
622
|
this.#send(out);
|
|
600
623
|
} else {
|
|
601
624
|
this.node.send(out);
|
|
602
625
|
}
|
|
603
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* Resolves the active return key. `null` = the node did not declare
|
|
629
|
+
* `returnProperty` in its configSchema, so its code owns the outgoing message
|
|
630
|
+
* shape (no wrapping).
|
|
631
|
+
*/
|
|
632
|
+
/**
|
|
633
|
+
* Every node has a return property — `"output"` by default. Declaring
|
|
634
|
+
* `SchemaType.ReturnProperty()` in the configSchema doesn't create it; it
|
|
635
|
+
* only exposes the key to the flow author so they can override it in the
|
|
636
|
+
* editor (and lets the node pick a different default). So `this.send(x)`
|
|
637
|
+
* always means "x is the value at the return key", never "x is the whole
|
|
638
|
+
* outgoing message".
|
|
639
|
+
*/
|
|
640
|
+
#returnPropertyKey() {
|
|
641
|
+
const NodeClass = this.constructor;
|
|
642
|
+
const declared = NodeClass.configSchema?.properties?.returnProperty;
|
|
643
|
+
const configured = this.config.returnProperty;
|
|
644
|
+
if (typeof configured === "string" && configured.trim()) {
|
|
645
|
+
return configured.trim();
|
|
646
|
+
}
|
|
647
|
+
if (declared && typeof declared.default === "string" && declared.default) {
|
|
648
|
+
return declared.default;
|
|
649
|
+
}
|
|
650
|
+
return "output";
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Merges a sent value into the incoming message at the returnProperty key so
|
|
654
|
+
* upstream message properties propagate. A fresh base is built per call so
|
|
655
|
+
* multi-port sends never share an object.
|
|
656
|
+
*/
|
|
657
|
+
#wrapOutgoing(value, mode = "nest") {
|
|
658
|
+
const key = this.#returnPropertyKey();
|
|
659
|
+
const input = this.#currentInputMsg ?? {};
|
|
660
|
+
if (mode === "reset") {
|
|
661
|
+
return { [key]: value };
|
|
662
|
+
}
|
|
663
|
+
if (mode === "carry") {
|
|
664
|
+
return { ...input, [key]: value };
|
|
665
|
+
}
|
|
666
|
+
return { ...input, [key]: value, [INPUT_KEY]: input };
|
|
667
|
+
}
|
|
604
668
|
// --- Built-in port management ---
|
|
605
669
|
get baseOutputs() {
|
|
606
670
|
return this.constructor.outputs ?? 0;
|
|
@@ -623,13 +687,16 @@ var IONode = class extends Node {
|
|
|
623
687
|
* throw an error or call `this.error()` for the error port, and the complete
|
|
624
688
|
* port is sent automatically on successful input processing.
|
|
625
689
|
*/
|
|
626
|
-
sendToPort(port, msg) {
|
|
690
|
+
sendToPort(port, msg, contextMode = "nest") {
|
|
627
691
|
if (port === "error" || port === "complete" || port === "status") {
|
|
628
692
|
throw new NrgError(
|
|
629
693
|
`sendToPort("${port}") is not allowed. Built-in ports are managed by the framework.`
|
|
630
694
|
);
|
|
631
695
|
}
|
|
632
|
-
this.#sendToPort(
|
|
696
|
+
this.#sendToPort(
|
|
697
|
+
port,
|
|
698
|
+
msg == null ? msg : this.#wrapOutgoing(msg, contextMode)
|
|
699
|
+
);
|
|
633
700
|
}
|
|
634
701
|
#sendToPort(port, msg) {
|
|
635
702
|
let portIndex;
|
|
@@ -687,7 +754,8 @@ var IONode = class extends Node {
|
|
|
687
754
|
error: {
|
|
688
755
|
message,
|
|
689
756
|
source: this.#nodeSource()
|
|
690
|
-
}
|
|
757
|
+
},
|
|
758
|
+
[INPUT_KEY]: msg
|
|
691
759
|
});
|
|
692
760
|
}
|
|
693
761
|
}
|
|
@@ -1095,9 +1163,18 @@ function TypedInput2(options) {
|
|
|
1095
1163
|
[import_typebox2.Kind]: "TypedInput"
|
|
1096
1164
|
};
|
|
1097
1165
|
}
|
|
1166
|
+
function ReturnProperty(options) {
|
|
1167
|
+
return import_typebox2.Type.String({
|
|
1168
|
+
description: "Message property that receives this node's result. The rest of the incoming message is propagated unchanged, and the prior message is kept under `input`.",
|
|
1169
|
+
pattern: "^[A-Za-z_$][A-Za-z0-9_$]*$",
|
|
1170
|
+
default: "output",
|
|
1171
|
+
...options
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1098
1174
|
var SchemaType = Object.assign({}, import_typebox2.Type, {
|
|
1099
1175
|
NodeRef,
|
|
1100
|
-
TypedInput: TypedInput2
|
|
1176
|
+
TypedInput: TypedInput2,
|
|
1177
|
+
ReturnProperty
|
|
1101
1178
|
});
|
|
1102
1179
|
function markNonValidatable(schema) {
|
|
1103
1180
|
const type = schema.type;
|