@bonsae/nrg 0.23.0 → 0.25.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bonsae/nrg",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "NRG framework — build Node-RED nodes with Vue 3, TypeScript, and JSON Schema",
5
5
  "author": "Allan Oricil <allanoricil@duck.com>",
6
6
  "license": "MIT",
@@ -119,7 +119,7 @@
119
119
  }
120
120
  },
121
121
  "dependencies": {
122
- "@bonsae/nrg-runtime": "0.23.0",
122
+ "@bonsae/nrg-runtime": "0.25.0",
123
123
  "@clack/prompts": "^1.0.1",
124
124
  "@sinclair/typebox": "^0.34.33",
125
125
  "@vitejs/plugin-vue": "^5.2.3",
@@ -239,6 +239,18 @@ function createNodeRedNode(options = {}) {
239
239
  import { initValidator } from "@bonsae/nrg-runtime/internal/server";
240
240
  import { WIRE_HANDLERS } from "@bonsae/nrg-runtime/internal/server";
241
241
  import { Kind } from "@sinclair/typebox";
242
+ function builtinPortIndex(node, name) {
243
+ if (name !== "error" && name !== "complete" && name !== "status") {
244
+ return void 0;
245
+ }
246
+ const config = node.config;
247
+ const base = node.baseOutputs;
248
+ if (name === "error") return config.errorPort ? base : -1;
249
+ let index = base + (config.errorPort ? 1 : 0);
250
+ if (name === "complete") return config.completePort ? index : -1;
251
+ index += config.completePort ? 1 : 0;
252
+ return config.statusPort ? index : -1;
253
+ }
242
254
  function buildConfig(NodeClass, userConfig = {}) {
243
255
  const defaults = {};
244
256
  if (NodeClass.configSchema?.properties) {
@@ -289,16 +301,19 @@ function attachHelpers(node, nodeRedNode, NodeClass) {
289
301
  },
290
302
  sent(port) {
291
303
  if (port === void 0) return [...sentMessages];
304
+ const pluck = (idx) => sentMessages.map((msg) => Array.isArray(msg) ? msg[idx] : void 0).filter((msg) => msg != null);
292
305
  if (typeof port === "string") {
306
+ const builtin = builtinPortIndex(node, port);
307
+ if (builtin !== void 0) return builtin === -1 ? [] : pluck(builtin);
293
308
  const schema = NodeClass.outputsSchema;
294
309
  if (!schema || Array.isArray(schema) || typeof schema === "object" && Kind in schema) {
295
310
  return [];
296
311
  }
297
312
  const idx = Object.keys(schema).indexOf(port);
298
313
  if (idx === -1) return [];
299
- return sentMessages.map((msg) => Array.isArray(msg) ? msg[idx] : void 0).filter((msg) => msg != null);
314
+ return pluck(idx);
300
315
  }
301
- return sentMessages.map((msg) => Array.isArray(msg) ? msg[port] : void 0).filter((msg) => msg != null);
316
+ return pluck(port);
302
317
  },
303
318
  statuses() {
304
319
  return [...statusCalls];
package/types/server.d.ts CHANGED
@@ -727,7 +727,7 @@ export declare abstract class IONode<TConfig = any, TCredentials = any, TInput =
727
727
  protected readonly context: IONodeContext;
728
728
  constructor(RED: RED, node: NodeRedNode, config: IONodeConfig<TConfig>, credentials: IONodeCredentials<TCredentials>);
729
729
  [WIRE_HANDLERS](nodeRedNode: NodeRedNode, createdPromise: Promise<void>): void;
730
- input(msg: TInput): void | Promise<void>;
730
+ input(msg: TInput): unknown;
731
731
  send(msg: TOutput): void;
732
732
  get baseOutputs(): number;
733
733
  get totalOutputs(): number;
@@ -787,7 +787,7 @@ export interface IIONode<TConfig = any, TCredentials = any, TInput = any, TOutpu
787
787
  readonly y: number;
788
788
  readonly g: string | undefined;
789
789
  readonly wires: string[][];
790
- input(msg: TInput): void | Promise<void>;
790
+ input(msg: TInput): unknown;
791
791
  send(msg: TOutput): void;
792
792
  status(status: IONodeStatus): void;
793
793
  updateWires(wires: string[][]): void;
@@ -811,7 +811,7 @@ interface IONodeDefinition<TConfigSchema extends TSchema | undefined = undefined
811
811
  registered?(RED: RED): void | Promise<void>;
812
812
  created?(this: BoundIONode<TConfigSchema, TCredsSchema, TSettingsSchema, TInputSchema, TOutputsSchema>): void | Promise<void>;
813
813
  closed?(this: BoundIONode<TConfigSchema, TCredsSchema, TSettingsSchema, TInputSchema, TOutputsSchema>, removed?: boolean): void | Promise<void>;
814
- input?(this: BoundIONode<TConfigSchema, TCredsSchema, TSettingsSchema, TInputSchema, TOutputsSchema>, msg: InferOr<TInputSchema, any>): void | Promise<void>;
814
+ input?(this: BoundIONode<TConfigSchema, TCredsSchema, TSettingsSchema, TInputSchema, TOutputsSchema>, msg: InferOr<TInputSchema, any>): unknown;
815
815
  }
816
816
  /**
817
817
  * Base class for configuration nodes that are shared across multiple nodes
@@ -445,6 +445,31 @@ type PortTuple<TOutput, TInput> = IsAny<TOutput> extends true ? any[] : TOutput
445
445
  ] : string extends keyof TOutput ? WrappedPort<unknown, TInput>[] : WrappedPort<TOutput[keyof TOutput], TInput>[] : [
446
446
  WrappedPort<TOutput, TInput>
447
447
  ];
448
+ type NodeSource = {
449
+ id: string;
450
+ type: string;
451
+ name: string;
452
+ };
453
+ type ErrorPortMessage = {
454
+ error: {
455
+ name: string;
456
+ message: string;
457
+ source: NodeSource;
458
+ };
459
+ } & Record<string, unknown>;
460
+ type CompletePortMessage = {
461
+ complete: {
462
+ source: NodeSource;
463
+ };
464
+ } & Record<string, unknown>;
465
+ type StatusPortMessage = {
466
+ status: {
467
+ fill?: string;
468
+ shape?: string;
469
+ text?: string;
470
+ };
471
+ source: NodeSource;
472
+ } & Record<string, unknown>;
448
473
  interface TestNodeHelpers<TInput = any, TOutput = any> {
449
474
  /** Drive the node's input handler with a Node-RED message. For an object
450
475
  * input the declared shape is required while arbitrary extra message
@@ -455,11 +480,13 @@ interface TestNodeHelpers<TInput = any, TOutput = any> {
455
480
  close(removed?: boolean): Promise<void>;
456
481
  reset(): void;
457
482
  /** All raw emissions, each a positional array — `sent()[i][0]` is port 0 of
458
- * emission `i`, typed from the node's declared output. Built-in lifecycle
459
- * ports (error/complete/status) are emitted as their own entries here, with
460
- * slots beyond the declared ports; use `sent(port)` / `sent(name)` for typed,
461
- * per-port assertions. */
483
+ * emission `i`, typed from the node's declared output. Read one port directly
484
+ * with `sent(name)` / `sent(port)`, including the built-in lifecycle ports by
485
+ * name: `sent("error")`, `sent("complete")`, `sent("status")`. */
462
486
  sent(): PortTuple<TOutput, TInput>[];
487
+ sent(port: "error"): ErrorPortMessage[];
488
+ sent(port: "complete"): CompletePortMessage[];
489
+ sent(port: "status"): StatusPortMessage[];
463
490
  sent<P extends PortNames<TOutput>>(port: P): WrappedPort<PortMessage<TOutput, P>, TInput>[];
464
491
  sent(port: number): WrappedPort<unknown, TInput>[];
465
492
  statuses(): any[];