@bonsae/nrg 0.3.0 → 0.5.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.
@@ -1,38 +1,204 @@
1
- import type { TObject } from "@sinclair/typebox";
1
+ import type { EventEmitter } from "events";
2
+ import type { NodeRedRuntimeSettings } from "../../../types";
2
3
 
3
- interface RED {
4
- _: (key: string, substitutions?: Record<string, string>) => string;
5
- log: {
6
- info(msg: any): void;
7
- warn(msg: any): void;
8
- error(message: string, error: any): void;
9
- debug(msg: any): void;
10
- trace(msg: any): void;
11
- };
12
- nodes: {
13
- registerType: (type: string, def: any, opts?: any) => void;
14
- getNode: <T = any>(id: string) => T | undefined;
15
- createNode: (node: any, config: Record<string, any>) => void;
16
- getCredentials: (id: string) => Record<string, any> | undefined;
17
- };
18
- httpAdmin: {
19
- get(path: string, handler: (req: any, res: any) => void): void;
20
- post(path: string, handler: (req: any, res: any) => void): void;
21
- put(path: string, handler: (req: any, res: any) => void): void;
22
- delete(path: string, handler: (req: any, res: any) => void): void;
23
- };
24
- util: {
25
- evaluateNodeProperty(
26
- value: any,
27
- type: string,
28
- node: any,
29
- msg: Record<string, any> | undefined,
30
- callback: (err: Error | null, result: any) => void,
31
- ): void;
4
+ // ---------------------------------------------------------------------------
5
+ // RED.log
6
+ // ---------------------------------------------------------------------------
7
+
8
+ interface NodeRedLog {
9
+ info(msg: any): void;
10
+ warn(msg: any): void;
11
+ error(msg: any, error?: any): void;
12
+ debug(msg: any): void;
13
+ trace(msg: any): void;
14
+ log(msg: { level: number; msg: string }): void;
15
+ metric(): boolean;
16
+ audit(msg: Record<string, any>, req?: any): void;
17
+ addHandler(handler: (msg: any) => void): void;
18
+ removeHandler(handler: (msg: any) => void): void;
19
+ FATAL: 10;
20
+ ERROR: 20;
21
+ WARN: 30;
22
+ INFO: 40;
23
+ DEBUG: 50;
24
+ TRACE: 60;
25
+ AUDIT: 98;
26
+ METRIC: 99;
27
+ }
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Node-RED runtime node (the raw node object from RED.nodes.createNode)
31
+ // ---------------------------------------------------------------------------
32
+
33
+ interface NodeRedNode {
34
+ id: string;
35
+ type: string;
36
+ name?: string;
37
+ z?: string;
38
+ x: number;
39
+ y: number;
40
+ g?: string;
41
+ wires: string[][];
42
+ credentials: any;
43
+ _node?: any;
44
+ send(msg: any): void;
45
+ receive(msg: any): void;
46
+ status(
47
+ status: string | { fill?: string; shape?: string; text?: string },
48
+ ): void;
49
+ updateWires(wires: string[][]): void;
50
+ on(event: string, callback: (...args: any[]) => void): void;
51
+ log(msg: any): void;
52
+ warn(msg: any): void;
53
+ error(msg: any, errorMsg?: any): void;
54
+ context(): NodeRedNodeContext;
55
+ [key: string]: any;
56
+ }
57
+
58
+ interface NodeRedNodeContext extends NodeRedContextStore {
59
+ flow: NodeRedContextStore;
60
+ global: NodeRedContextStore;
61
+ }
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // RED.nodes
65
+ // ---------------------------------------------------------------------------
66
+
67
+ interface NodeRedNodes {
68
+ registerType(type: string, constructor: any, opts?: any): void;
69
+ getNode(id: string): (NodeRedNode & { _node?: any }) | undefined;
70
+ createNode(node: NodeRedNode, config: Record<string, any>): void;
71
+ getCredentials(id: string): Record<string, any> | undefined;
72
+ eachNode(callback: (node: any) => void): void;
73
+ getType(type: string): any;
74
+ getNodeInfo(type: string): any;
75
+ getNodeList(filter?: any): any[];
76
+ getModuleInfo(module: string): any;
77
+ installModule(module: string, version?: string): Promise<any>;
78
+ uninstallModule(module: string): Promise<any>;
79
+ enableNode(id: string): Promise<any>;
80
+ disableNode(id: string): Promise<any>;
81
+ }
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // RED.util
85
+ // ---------------------------------------------------------------------------
86
+
87
+ interface NodeRedUtil {
88
+ evaluateNodeProperty(
89
+ value: any,
90
+ type: string,
91
+ node: any,
92
+ msg: Record<string, any> | undefined,
93
+ callback: (err: Error | null, result: any) => void,
94
+ ): void;
95
+ generateId(): string;
96
+ cloneMessage<T = any>(msg: T): T;
97
+ ensureString(o: any): string;
98
+ ensureBuffer(o: any): Buffer;
99
+ compareObjects(obj1: any, obj2: any): boolean;
100
+ getMessageProperty(msg: any, expr: string): any;
101
+ setMessageProperty(
102
+ msg: any,
103
+ prop: string,
104
+ value: any,
105
+ createMissing?: boolean,
106
+ ): void;
107
+ getObjectProperty(obj: any, expr: string): any;
108
+ setObjectProperty(
109
+ obj: any,
110
+ prop: string,
111
+ value: any,
112
+ createMissing?: boolean,
113
+ ): void;
114
+ normalisePropertyExpression(
115
+ str: string,
116
+ msg?: any,
117
+ toString?: boolean,
118
+ ): string[];
119
+ normaliseNodeTypeName(name: string): string;
120
+ prepareJSONataExpression(value: string, node: any): any;
121
+ evaluateJSONataExpression(
122
+ expr: any,
123
+ msg: any,
124
+ callback: (err: Error | null, result: any) => void,
125
+ ): void;
126
+ parseContextStore(key: string): {
127
+ store: string | undefined;
128
+ key: string;
32
129
  };
33
- settings: Record<string, any>;
130
+ getSetting(node: any, name: string, flow?: any): any;
131
+ encodeObject(obj: any): any;
132
+ }
133
+
134
+ // ---------------------------------------------------------------------------
135
+ // RED.hooks
136
+ // ---------------------------------------------------------------------------
137
+
138
+ interface NodeRedHooks {
139
+ add(hookId: string, callback: (event: any) => void | Promise<void>): void;
140
+ remove(hookId: string): void;
141
+ trigger(
142
+ hookId: string,
143
+ event: any,
144
+ callback?: (err?: Error) => void,
145
+ ): void | Promise<void>;
146
+ has(hookId: string): boolean;
147
+ clear(): void;
148
+ }
149
+
150
+ // ---------------------------------------------------------------------------
151
+ // Express-like HTTP app (httpAdmin / httpNode)
152
+ // ---------------------------------------------------------------------------
153
+
154
+ type NodeRedRequestHandler = (req: any, res: any, next?: () => void) => void;
155
+
156
+ interface NodeRedExpressApp {
157
+ get(path: string, ...handlers: NodeRedRequestHandler[]): void;
158
+ post(path: string, ...handlers: NodeRedRequestHandler[]): void;
159
+ put(path: string, ...handlers: NodeRedRequestHandler[]): void;
160
+ delete(path: string, ...handlers: NodeRedRequestHandler[]): void;
161
+ patch(path: string, ...handlers: NodeRedRequestHandler[]): void;
162
+ options(path: string, ...handlers: NodeRedRequestHandler[]): void;
163
+ head(path: string, ...handlers: NodeRedRequestHandler[]): void;
164
+ use(
165
+ path: string | NodeRedRequestHandler,
166
+ ...handlers: NodeRedRequestHandler[]
167
+ ): void;
168
+ all(path: string, ...handlers: NodeRedRequestHandler[]): void;
169
+ }
170
+
171
+ // ---------------------------------------------------------------------------
172
+ // RED (main interface)
173
+ // ---------------------------------------------------------------------------
174
+
175
+ interface RED {
176
+ /** Internationalization function */
177
+ _(key: string, substitutions?: Record<string, string>): string;
178
+ /** Logging API */
179
+ log: NodeRedLog;
180
+ /** Node registry and management */
181
+ nodes: NodeRedNodes;
182
+ /** Utility functions */
183
+ util: NodeRedUtil;
184
+ /** Hook system for message lifecycle and module events */
185
+ hooks: NodeRedHooks;
186
+ /** Runtime event emitter */
187
+ events: EventEmitter;
188
+ /** Express app for admin HTTP endpoints */
189
+ httpAdmin: NodeRedExpressApp;
190
+ /** Express app for node HTTP endpoints */
191
+ httpNode: NodeRedExpressApp;
192
+ /** Runtime settings (user-provided settings plus node-registered settings) */
193
+ settings: NodeRedRuntimeSettings & Record<string, any>;
194
+ /** Node-RED version string */
195
+ version(): string;
34
196
  }
35
197
 
198
+ // ---------------------------------------------------------------------------
199
+ // Context store (internal)
200
+ // ---------------------------------------------------------------------------
201
+
36
202
  interface NodeRedContextStore {
37
203
  get(
38
204
  key: string,
@@ -51,23 +217,4 @@ interface NodeRedContextStore {
51
217
  ): void;
52
218
  }
53
219
 
54
- interface NodeDefinitionApiResponse {
55
- type: string;
56
- align?: "left" | "right";
57
- category?: "config" | string;
58
- color?: `#${string}`;
59
- icon?: string;
60
- labelStyle?: "node_label" | "node_label_italic" | string;
61
- paletteLabel?: string;
62
- inputs?: number;
63
- outputs?: number;
64
- inputLabels?: string | string[];
65
- outputLabels?: string | string[];
66
- configSchema: TObject | null;
67
- credentialsSchema: TObject | null;
68
- inputSchema?: TObject | null;
69
- outputsSchema?: TObject | null;
70
- settingsSchema?: TObject | null;
71
- }
72
-
73
- export { RED, NodeDefinitionApiResponse, NodeRedContextStore };
220
+ export { RED, NodeRedNode, NodeRedNodeContext, NodeRedContextStore };
@@ -1,7 +1,7 @@
1
1
  import type { TObject, SchemaOptions } from "@sinclair/typebox";
2
2
 
3
3
  interface NodeSchemaOptions extends SchemaOptions {
4
- "node-type"?: string;
4
+ "x-nrg-node-type"?: string;
5
5
  default?: any;
6
6
  format?: string;
7
7
  }
@@ -25,7 +25,7 @@ function getDefaultsFromSchema(
25
25
  required: false,
26
26
  value: property.default ?? undefined,
27
27
  // NOTE: I'm using a custom json schema keyword to determine the node type
28
- type: property["node-type"],
28
+ type: property["x-nrg-node-type"],
29
29
  };
30
30
  }
31
31
 
@@ -5,9 +5,13 @@ class NodeRedValidator extends Validator {
5
5
  constructor(RED: RED) {
6
6
  super({
7
7
  customKeywords: [
8
- { keyword: "skip-validation", schemaType: "boolean", valid: true },
9
8
  {
10
- keyword: "node-type",
9
+ keyword: "x-nrg-skip-validation",
10
+ schemaType: "boolean",
11
+ valid: true,
12
+ },
13
+ {
14
+ keyword: "x-nrg-node-type",
11
15
  type: "string",
12
16
  validate: (schemaValue: string, dataValue: string) => {
13
17
  if (!dataValue) return true;
@@ -39,13 +39,15 @@ async function build(
39
39
  if (fs.existsSync(physicalEntryPath)) {
40
40
  entryPath = physicalEntryPath;
41
41
  } else {
42
- // No physical entry — create a minimal empty file that the inliner
43
- // will prepend auto-registration code into.
44
- if (!fs.existsSync(path.dirname(physicalEntryPath))) {
45
- fs.mkdirSync(path.dirname(physicalEntryPath), { recursive: true });
42
+ // No physical entry — create a minimal empty file in the cache directory
43
+ // so the file watcher on srcDir is not triggered by the create/delete cycle.
44
+ const cacheDir = path.resolve("node_modules", ".nrg", "client");
45
+ const cachedEntryPath = path.resolve(cacheDir, entry);
46
+ if (!fs.existsSync(cacheDir)) {
47
+ fs.mkdirSync(cacheDir, { recursive: true });
46
48
  }
47
- fs.writeFileSync(physicalEntryPath, "// auto-generated entry\n");
48
- entryPath = physicalEntryPath;
49
+ fs.writeFileSync(cachedEntryPath, "// auto-generated entry\n");
50
+ entryPath = cachedEntryPath;
49
51
  generatedEntry = true;
50
52
  }
51
53
 
@@ -215,7 +217,7 @@ async function build(
215
217
  throw new BuildError("client", error as Error);
216
218
  } finally {
217
219
  if (generatedEntry) {
218
- fs.unlinkSync(physicalEntryPath);
220
+ fs.unlinkSync(entryPath);
219
221
  }
220
222
  }
221
223
  }
@@ -28,7 +28,7 @@ function getDefaultsFromSchema(
28
28
  result[key] = {
29
29
  required: false,
30
30
  value: prop.default ?? undefined,
31
- type: prop["node-type"],
31
+ type: prop["x-nrg-node-type"],
32
32
  };
33
33
  }
34
34
  return result;
@@ -1,17 +0,0 @@
1
- import { type NodeDefinitionApiResponse } from "../../server/types";
2
-
3
- async function fetchNodeDefinition(
4
- type: string,
5
- ): Promise<NodeDefinitionApiResponse> {
6
- const response = await fetch(`/nrg/nodes/${type}`);
7
-
8
- if (!response.ok) {
9
- throw new Error(
10
- `Failed to fetch node definition for "${type}": ${response.status}`,
11
- );
12
- }
13
-
14
- return response.json() as Promise<NodeDefinitionApiResponse>;
15
- }
16
-
17
- export { fetchNodeDefinition };
@@ -1,5 +0,0 @@
1
- declare module "virtual:nrg/node-definitions" {
2
- import type { NodeDefinitionApiResponse } from "../server/types";
3
- const definitions: Record<string, NodeDefinitionApiResponse>;
4
- export default definitions;
5
- }