@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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/server/index.cjs +86 -9
  3. package/server/resources/nrg-client.js +2020 -1987
  4. package/test/client/component/config.js +11 -0
  5. package/test/client/component/index.js +218 -235
  6. package/test/client/component/nrg.css +1 -0
  7. package/test/client/component/setup.js +1549 -140
  8. package/test/client/e2e/index.js +720 -369
  9. package/test/client/unit/index.js +204 -16
  10. package/test/client/unit/setup.js +209 -19
  11. package/test/server/unit/index.js +25 -4
  12. package/tsconfig/core/client.json +1 -1
  13. package/tsconfig/test/client/component.json +1 -1
  14. package/types/client.d.ts +98 -18
  15. package/types/server.d.ts +50 -12
  16. package/types/shims/brands.d.ts +32 -0
  17. package/types/shims/{form → client/form}/components/node-red-editor-input.vue.d.ts +1 -1
  18. package/types/shims/{form → client/form}/components/node-red-json-schema-form.vue.d.ts +21 -2
  19. package/types/shims/{form → client/form}/components/node-red-select-input.vue.d.ts +1 -0
  20. package/types/shims/{form → client/form}/components/node-red-typed-input.vue.d.ts +1 -0
  21. package/types/shims/client/types.d.ts +206 -0
  22. package/types/shims/components.d.ts +8 -8
  23. package/types/shims/constants.d.ts +4 -0
  24. package/types/shims/schema-options.d.ts +23 -10
  25. package/types/shims/typebox.d.ts +2 -2
  26. package/types/test-client-component.d.ts +170 -55
  27. package/types/test-client-e2e.d.ts +50 -0
  28. package/types/test-client-unit.d.ts +86 -22
  29. package/types/test-server-unit.d.ts +3 -1
  30. package/types/vite.d.ts +38 -9
  31. package/vite/index.js +732 -530
  32. /package/types/shims/{form → client/form}/components/node-red-config-input.vue.d.ts +0 -0
  33. /package/types/shims/{form → client/form}/components/node-red-input-label.vue.d.ts +0 -0
  34. /package/types/shims/{form → client/form}/components/node-red-input.vue.d.ts +0 -0
  35. /package/types/shims/{form → client/form}/components/node-red-toggle.vue.d.ts +0 -0
  36. /package/types/shims/{globals.d.ts → client/globals.d.ts} +0 -0
@@ -1,11 +1,24 @@
1
- export interface NrgSchemaExtensions {
2
- format?: "node-id" | "flow-id" | "topic-path" | (string & {});
3
- exportable?: boolean;
4
- "x-nrg-node-type"?: string;
5
- "x-nrg-form"?: {
6
- icon?: string;
7
- typedInputTypes?: string[];
8
- editorLanguage?: string;
9
- toggle?: boolean;
10
- };
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 brands.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
+ };
11
24
  }
@@ -2,9 +2,9 @@
2
2
  // TypeScript module augmentation (declare module) to work correctly.
3
3
  // Without it, the file is treated as a global script and the
4
4
  // augmentation silently fails.
5
- import type { NrgSchemaExtensions } from "./schema-options";
5
+ import type { JsonSchemaObjectExtensions } from "./schema-options";
6
6
 
7
7
  declare module "@sinclair/typebox" {
8
8
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
9
- interface SchemaOptions extends NrgSchemaExtensions {}
9
+ interface SchemaOptions extends JsonSchemaObjectExtensions {}
10
10
  }
@@ -1,55 +1,36 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
- import { SchemaOptions, Static, TSchema } from '@sinclair/typebox';
3
+ import { Static, TSchema } from '@sinclair/typebox';
4
+ import { SchemaObject } from 'ajv';
4
5
  import { App } from 'vue';
5
6
 
6
- export interface MockEditor {
7
- getValue(): string;
8
- setValue(val: string): void;
9
- getSession(): {
10
- on(event: string, cb: (...args: any[]) => any): void;
11
- };
12
- focus(): void;
13
- destroy(): void;
14
- saveView(): void;
15
- restoreView(): void;
16
- }
17
- export interface MockRED {
18
- _(key: string): string;
19
- editor: {
20
- createEditor(options: any): MockEditor;
21
- prepareConfigNodeSelect(...args: any[]): void;
22
- validateNode(...args: any[]): boolean;
23
- };
24
- tray: {
25
- show(...args: any[]): void;
26
- close(): void;
27
- };
28
- popover: {
29
- tooltip(...args: any[]): {
30
- delete(): void;
31
- setAction(...args: any[]): void;
32
- };
33
- };
34
- nodes: {
35
- registerType(...args: any[]): void;
36
- node(...args: any[]): null;
37
- dirty(...args: any[]): boolean;
38
- };
39
- events: {
40
- on(...args: any[]): void;
41
- off(...args: any[]): void;
42
- emit(...args: any[]): void;
43
- };
44
- settings: Record<string, any>;
45
- notify(...args: any[]): void;
46
- }
47
- export declare function createRED(): MockRED;
48
- export declare function createJQuery(): (selector: any, attrs?: Record<string, any>) => any;
49
7
  interface NodeRefResolved<T = any> {
50
8
  readonly __nrg_node_ref: true;
51
9
  readonly __instance: T;
52
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
+ }
53
34
  interface NodeRedNodeButtonDefinition {
54
35
  toggle: string;
55
36
  onclick: () => void;
@@ -95,22 +76,143 @@ interface NodeRedNode {
95
76
  _newState?: NodeRedNode;
96
77
  _app?: App | null;
97
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
+ /** injected when the node has an outputsSchema */
84
+ validateOutput?: boolean;
85
+ /** present when the node's configSchema declares SchemaType.ReturnProperty() */
86
+ returnProperty?: string;
87
+ /** built-in port toggles, present when declared in the configSchema */
88
+ errorPort?: boolean;
89
+ completePort?: boolean;
90
+ statusPort?: boolean;
98
91
  [key: string]: any;
99
92
  }
93
+ interface JsonPropertySchema extends JsonSchemaObjectExtensions {
94
+ type?: string | string[];
95
+ properties?: Record<string, JsonPropertySchema>;
96
+ required?: string[];
97
+ enum?: unknown[];
98
+ anyOf?: JsonPropertySchema[];
99
+ const?: unknown;
100
+ items?: JsonPropertySchema;
101
+ title?: string;
102
+ description?: string;
103
+ default?: unknown;
104
+ }
105
+ interface JsonSchemaObject extends SchemaObject {
106
+ type: "object";
107
+ properties?: Record<string, JsonPropertySchema>;
108
+ required?: string[];
109
+ }
100
110
  interface TypedInputValue {
101
111
  value: string;
102
112
  type: string;
103
113
  }
104
- type _ToClient<T> = T extends NodeRefResolved<any> ? string : T extends {
105
- resolve(...args: any[]): any;
106
- value: unknown;
107
- type: string;
108
- } ? TypedInputValue : T extends (...args: any[]) => any ? T : T extends Array<infer I> ? _ToClient<I>[] : T extends object ? {
109
- [K in keyof T]: _ToClient<T[K]>;
114
+ 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 ? {
115
+ [K in keyof T]: EditorStatic<T[K]>;
110
116
  } : T;
117
+ export interface MockEditor {
118
+ getValue(): string;
119
+ setValue(val: string): void;
120
+ getSession(): {
121
+ on(event: string, cb: (...args: any[]) => any): void;
122
+ };
123
+ focus(): void;
124
+ destroy(): void;
125
+ saveView(): void;
126
+ restoreView(): void;
127
+ }
128
+ export interface MockNotification {
129
+ update(msg: string, options?: Record<string, any>): void;
130
+ close(): void;
131
+ }
132
+ export interface MockPopover {
133
+ readonly element: null;
134
+ open(): MockPopover;
135
+ close(): MockPopover;
136
+ setContent(content: any): MockPopover;
137
+ move(options: Record<string, any>): void;
138
+ }
139
+ export interface MockTooltip extends MockPopover {
140
+ delete(): void;
141
+ setAction(action: string): void;
142
+ }
143
+ /**
144
+ * Mirrors the real RED.settings: exportable settings appear as direct
145
+ * properties, and get/set/remove manage user settings on the same store.
146
+ */
147
+ export interface MockSettings {
148
+ get(key: string): any;
149
+ set(key: string, value: any): any;
150
+ remove(key: string): any;
151
+ [key: string]: any;
152
+ }
153
+ export interface MockRED {
154
+ _(key: string, substitutions?: Record<string, string>): string;
155
+ editor: {
156
+ createEditor(options: any): MockEditor;
157
+ prepareConfigNodeSelect(...args: any[]): void;
158
+ validateNode(...args: any[]): boolean;
159
+ };
160
+ tray: {
161
+ show(...args: any[]): void;
162
+ close(): void;
163
+ };
164
+ popover: {
165
+ create(options: any): MockPopover;
166
+ tooltip(...args: any[]): MockTooltip;
167
+ };
168
+ nodes: {
169
+ registerType(type: string, definition: any): void;
170
+ getType(type: string): any;
171
+ node(id: string): any;
172
+ add(node: any): any;
173
+ remove(id: string): {
174
+ links: any[];
175
+ nodes: any[];
176
+ };
177
+ /** Test-only: empties the node registry. */
178
+ clear(): void;
179
+ eachNode(callback: (node: any) => void | false): void;
180
+ eachConfig(callback: (node: any) => void | false): void;
181
+ filterNodes(filter: {
182
+ z?: string;
183
+ type?: string;
184
+ }): any[];
185
+ filterLinks(filter: {
186
+ source?: any;
187
+ target?: any;
188
+ }): any[];
189
+ /** Test-only: registers a link for filterLinks lookups. */
190
+ addLink(link: {
191
+ source: any;
192
+ target: any;
193
+ sourcePort?: number;
194
+ }): void;
195
+ dirty(): boolean;
196
+ dirty(dirty: boolean): void;
197
+ id(): string;
198
+ };
199
+ events: {
200
+ on(event: string, listener: (...args: any[]) => void): void;
201
+ off(event: string, listener?: (...args: any[]) => void): void;
202
+ emit(event: string, ...args: any[]): void;
203
+ };
204
+ comms: {
205
+ subscribe(topic: string, callback: (topic: string, msg: any) => void): void;
206
+ unsubscribe(topic: string, callback: (topic: string, msg: any) => void): void;
207
+ /** Test-only: simulates a runtime message. Supports `+` and `#` wildcards in subscriptions. */
208
+ publish(topic: string, msg: any): void;
209
+ };
210
+ settings: MockSettings;
211
+ notify(message: any, options?: Record<string, any>): MockNotification;
212
+ }
111
213
  interface FormNode<TConfig extends TSchema = TSchema, TCredentials extends TSchema = TSchema> {
112
- node: NodeRedNode & _ToClient<Static<TConfig>> & {
113
- credentials: _ToClient<Static<TCredentials>> & Record<string, any>;
214
+ node: NodeRedNode & EditorStatic<Static<TConfig>> & {
215
+ credentials: EditorStatic<Static<TCredentials>> & Record<string, any>;
114
216
  };
115
217
  schema: Record<string, any>;
116
218
  errors: Record<string, string>;
@@ -138,18 +240,31 @@ export interface TestNode {
138
240
  changed: boolean;
139
241
  _def: Record<string, any>;
140
242
  _: (key: string) => string;
243
+ credentials?: Record<string, any>;
141
244
  [key: string]: any;
142
245
  }
143
- interface FormProvide {
246
+ export interface FormProvide {
144
247
  __nrg_form_node: TestNode;
145
248
  __nrg_form_schema: Record<string, any>;
146
249
  __nrg_form_errors: Record<string, string>;
147
250
  }
148
- interface CreateNodeResult {
251
+ export interface CreateNodeResult {
149
252
  node: TestNode;
253
+ errors: Record<string, string>;
150
254
  RED: MockRED;
151
255
  provide: FormProvide;
152
256
  }
153
- export declare function createNode(overrides?: Record<string, any>): CreateNodeResult;
257
+ export interface CreateNodeOptions {
258
+ configs?: Record<string, any>;
259
+ credentials?: Record<string, any>;
260
+ configSchema?: JsonSchemaObject;
261
+ credentialsSchema?: JsonSchemaObject;
262
+ /** Fake config nodes resolvable via RED.nodes.node(id) — required for NodeRef field validation. */
263
+ nodes?: Array<{
264
+ id: string;
265
+ type: string;
266
+ } & Record<string, any>>;
267
+ }
268
+ export declare function createNode(options?: CreateNodeOptions | Record<string, any>): CreateNodeResult;
154
269
 
155
270
  export {};
@@ -93,6 +93,7 @@ export declare const defaultConfig: {
93
93
  export declare function setup(options?: SetupOptions): Promise<void>;
94
94
  export declare function teardown(): Promise<void>;
95
95
  export declare class NodeRedEditor {
96
+ #private;
96
97
  readonly page: Page;
97
98
  readonly port: number;
98
99
  readonly errors: string[];
@@ -106,7 +107,43 @@ export declare class NodeRedEditor {
106
107
  editNode(nodeId: string): Promise<void>;
107
108
  clickDone(): Promise<void>;
108
109
  clickCancel(): Promise<void>;
110
+ /**
111
+ * Best-effort close of every open tray — call from afterEach so a failed
112
+ * test never leaves a tray open for the next one.
113
+ */
114
+ closeAllTrays(): Promise<void>;
115
+ /**
116
+ * Closes the config-node tray (stacked above the node tray) with its own
117
+ * Done button. The node tray underneath stays open — use clickDone() for it.
118
+ */
119
+ clickConfigDone(): Promise<void>;
120
+ clickConfigCancel(): Promise<void>;
109
121
  field(label: string): NodeRedField;
122
+ /**
123
+ * Returns a JSON-safe snapshot of a node in the editor's model — use it to
124
+ * assert values persisted after clickDone(). Functions, internals
125
+ * (underscore-prefixed keys like `_def`), config `users` back-references,
126
+ * and any circular references are stripped.
127
+ */
128
+ getNode(nodeId: string): Promise<Record<string, any> | null>;
129
+ /**
130
+ * Clicks the editor's Deploy button and waits until the workspace is clean.
131
+ * Flows with invalid or unused nodes trigger a confirmation dialog — it is
132
+ * confirmed automatically.
133
+ */
134
+ clickDeploy(): Promise<void>;
135
+ /** Fetches the currently deployed flow from the runtime (GET /flows). */
136
+ getDeployedFlow(): Promise<Record<string, unknown>[]>;
137
+ /** Counts the output ports rendered for a node on the canvas. */
138
+ getNodePortCount(nodeId: string): Promise<number>;
139
+ /** Returns the node's label text as rendered on the canvas. */
140
+ getNodeLabel(nodeId: string): Promise<string>;
141
+ /** Returns the status text rendered under a node, or "" when none is set. */
142
+ getNodeStatus(nodeId: string): Promise<string>;
143
+ /**
144
+ * Asserts no uncaught page errors occurred, then clears the collected list
145
+ * so each test only fails for its own errors — call it from afterEach.
146
+ */
110
147
  expectNoPageErrors(): void;
111
148
  get tray(): Locator;
112
149
  }
@@ -134,10 +171,23 @@ export declare class NodeRedField {
134
171
  get select(): Locator;
135
172
  get editButton(): Locator;
136
173
  get addButton(): Locator;
174
+ /** Clicks the + button of a config field and waits for the config tray. */
175
+ openAddConfig(): Promise<void>;
176
+ /** Clicks the pencil button of a config field and waits for the config tray. */
177
+ openEditConfig(): Promise<void>;
137
178
  getSelectedOption(): Promise<string>;
138
179
  getSelectedOptionLabel(): Promise<string>;
139
180
  getOptions(): Promise<string[]>;
140
181
  get editorWrapper(): Locator;
182
+ /** Reads the value of the field's code editor (Monaco, ACE fallback). */
183
+ getEditorValue(): Promise<string>;
184
+ /** Sets the value of the field's code editor (Monaco, ACE fallback). */
185
+ setEditorValue(value: string): Promise<void>;
186
+ /**
187
+ * Types into the field and returns the labels of the autocomplete
188
+ * suggestions that appear (TypedInput types with an `autoComplete` source).
189
+ */
190
+ getAutoCompleteSuggestions(prefix: string): Promise<string[]>;
141
191
  get expandButton(): Locator;
142
192
  get textarea(): Locator;
143
193
  get requiredIndicator(): Locator;
@@ -1,6 +1,6 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
- import { SchemaOptions, Static, TSchema } from '@sinclair/typebox';
3
+ import { Static, TSchema } from '@sinclair/typebox';
4
4
  import { App } from 'vue';
5
5
 
6
6
  export interface MockEditor {
@@ -14,8 +14,29 @@ export interface MockEditor {
14
14
  saveView(): void;
15
15
  restoreView(): void;
16
16
  }
17
+ interface MockNotification {
18
+ update(msg: string, options?: Record<string, any>): void;
19
+ close(): void;
20
+ }
21
+ interface MockPopover {
22
+ readonly element: null;
23
+ open(): MockPopover;
24
+ close(): MockPopover;
25
+ setContent(content: any): MockPopover;
26
+ move(options: Record<string, any>): void;
27
+ }
28
+ interface MockTooltip extends MockPopover {
29
+ delete(): void;
30
+ setAction(action: string): void;
31
+ }
32
+ interface MockSettings {
33
+ get(key: string): any;
34
+ set(key: string, value: any): any;
35
+ remove(key: string): any;
36
+ [key: string]: any;
37
+ }
17
38
  export interface MockRED {
18
- _(key: string): string;
39
+ _(key: string, substitutions?: Record<string, string>): string;
19
40
  editor: {
20
41
  createEditor(options: any): MockEditor;
21
42
  prepareConfigNodeSelect(...args: any[]): void;
@@ -26,23 +47,53 @@ export interface MockRED {
26
47
  close(): void;
27
48
  };
28
49
  popover: {
29
- tooltip(...args: any[]): {
30
- delete(): void;
31
- setAction(...args: any[]): void;
32
- };
50
+ create(options: any): MockPopover;
51
+ tooltip(...args: any[]): MockTooltip;
33
52
  };
34
53
  nodes: {
35
- registerType(...args: any[]): void;
36
- node(...args: any[]): null;
37
- dirty(...args: any[]): boolean;
54
+ registerType(type: string, definition: any): void;
55
+ getType(type: string): any;
56
+ node(id: string): any;
57
+ add(node: any): any;
58
+ remove(id: string): {
59
+ links: any[];
60
+ nodes: any[];
61
+ };
62
+ /** Test-only: empties the node registry. */
63
+ clear(): void;
64
+ eachNode(callback: (node: any) => void | false): void;
65
+ eachConfig(callback: (node: any) => void | false): void;
66
+ filterNodes(filter: {
67
+ z?: string;
68
+ type?: string;
69
+ }): any[];
70
+ filterLinks(filter: {
71
+ source?: any;
72
+ target?: any;
73
+ }): any[];
74
+ /** Test-only: registers a link for filterLinks lookups. */
75
+ addLink(link: {
76
+ source: any;
77
+ target: any;
78
+ sourcePort?: number;
79
+ }): void;
80
+ dirty(): boolean;
81
+ dirty(dirty: boolean): void;
82
+ id(): string;
38
83
  };
39
84
  events: {
40
- on(...args: any[]): void;
41
- off(...args: any[]): void;
42
- emit(...args: any[]): void;
85
+ on(event: string, listener: (...args: any[]) => void): void;
86
+ off(event: string, listener?: (...args: any[]) => void): void;
87
+ emit(event: string, ...args: any[]): void;
43
88
  };
44
- settings: Record<string, any>;
45
- notify(...args: any[]): void;
89
+ comms: {
90
+ subscribe(topic: string, callback: (topic: string, msg: any) => void): void;
91
+ unsubscribe(topic: string, callback: (topic: string, msg: any) => void): void;
92
+ /** Test-only: simulates a runtime message. Supports `+` and `#` wildcards in subscriptions. */
93
+ publish(topic: string, msg: any): void;
94
+ };
95
+ settings: MockSettings;
96
+ notify(message: any, options?: Record<string, any>): MockNotification;
46
97
  }
47
98
  export declare function createRED(): MockRED;
48
99
  export declare function createJQuery(): (selector: any, attrs?: Record<string, any>) => any;
@@ -50,6 +101,11 @@ interface NodeRefResolved<T = any> {
50
101
  readonly __nrg_node_ref: true;
51
102
  readonly __instance: T;
52
103
  }
104
+ interface TypedInputResolved {
105
+ resolve(...args: any[]): any;
106
+ value: unknown;
107
+ type: string;
108
+ }
53
109
  interface NodeRedNodeButtonDefinition {
54
110
  toggle: string;
55
111
  onclick: () => void;
@@ -95,22 +151,30 @@ interface NodeRedNode {
95
151
  _newState?: NodeRedNode;
96
152
  _app?: App | null;
97
153
  _: (str: string) => string;
154
+ /** dynamic port count (base outputs + enabled built-in ports) */
155
+ outputs?: number;
156
+ /** injected when the node has an inputSchema */
157
+ validateInput?: boolean;
158
+ /** injected when the node has an outputsSchema */
159
+ validateOutput?: boolean;
160
+ /** present when the node's configSchema declares SchemaType.ReturnProperty() */
161
+ returnProperty?: string;
162
+ /** built-in port toggles, present when declared in the configSchema */
163
+ errorPort?: boolean;
164
+ completePort?: boolean;
165
+ statusPort?: boolean;
98
166
  [key: string]: any;
99
167
  }
100
168
  interface TypedInputValue {
101
169
  value: string;
102
170
  type: string;
103
171
  }
104
- type _ToClient<T> = T extends NodeRefResolved<any> ? string : T extends {
105
- resolve(...args: any[]): any;
106
- value: unknown;
107
- type: string;
108
- } ? TypedInputValue : T extends (...args: any[]) => any ? T : T extends Array<infer I> ? _ToClient<I>[] : T extends object ? {
109
- [K in keyof T]: _ToClient<T[K]>;
172
+ 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 ? {
173
+ [K in keyof T]: EditorStatic<T[K]>;
110
174
  } : T;
111
175
  interface FormNode<TConfig extends TSchema = TSchema, TCredentials extends TSchema = TSchema> {
112
- node: NodeRedNode & _ToClient<Static<TConfig>> & {
113
- credentials: _ToClient<Static<TCredentials>> & Record<string, any>;
176
+ node: NodeRedNode & EditorStatic<Static<TConfig>> & {
177
+ credentials: EditorStatic<Static<TCredentials>> & Record<string, any>;
114
178
  };
115
179
  schema: Record<string, any>;
116
180
  errors: Record<string, string>;
@@ -214,7 +214,7 @@ interface NodeConstructor<T = any, TConfig = any, TCredentials = any> {
214
214
  readonly credentialsSchema?: Schema;
215
215
  readonly settingsSchema?: Schema;
216
216
  readonly inputSchema?: Schema;
217
- readonly outputsSchema?: Schema | Schema[] | Record<string, Schema>;
217
+ readonly outputsSchema?: TSchema | TSchema[] | Record<string, TSchema>;
218
218
  readonly validateInput?: boolean;
219
219
  readonly validateOutput?: boolean;
220
220
  readonly name: string;
@@ -304,6 +304,8 @@ interface NodeRedNodes {
304
304
  }) | undefined;
305
305
  createNode(node: NodeRedNode, config: Record<string, any>): void;
306
306
  getCredentials(id: string): Record<string, any> | undefined;
307
+ /** Merge credentials into a node's stored credential set (runtime API). */
308
+ addCredentials(id: string, credentials: Record<string, any>): void;
307
309
  eachNode(callback: (node: any) => void): void;
308
310
  getType(type: string): any;
309
311
  getNodeInfo(type: string): any;
package/types/vite.d.ts CHANGED
@@ -75,22 +75,47 @@ export interface NodeRedLauncherOptions {
75
75
  args?: string[];
76
76
  }
77
77
  /**
78
- * Options for the `nodeRed()` Vite plugin.
79
- *
80
- * All options are optional — defaults work for the standard `src/` directory layout.
78
+ * Options for the dev server that runs and fronts the local Node-RED instance.
79
+ */
80
+ export interface ServerOptions {
81
+ /** Options for the Node-RED dev server launcher. */
82
+ nodeRed?: NodeRedLauncherOptions;
83
+ /**
84
+ * URL-safe path slug the dev server mounts the Node-RED editor under, so
85
+ * several `nrg dev` instances can be told apart by path (e.g. behind a
86
+ * shared reverse proxy) — the editor is served at
87
+ * `http://host:port/<slug>/`. Must match `/^[a-z0-9]+(?:-[a-z0-9]+)*$/`;
88
+ * an invalid value is rejected rather than rewritten.
89
+ * @default the slugified project folder name
90
+ */
91
+ slug?: string;
92
+ }
93
+ /**
94
+ * Options for building the distributable Node-RED package.
81
95
  */
82
- export interface NodeRedPluginOptions {
96
+ export interface BuildOptions {
83
97
  /** Output directory for the built Node-RED package. @default "./dist" */
84
98
  outDir?: string;
85
- /** Options for building the client-side editor UI. */
86
- clientBuildOptions?: ClientBuildOptions;
87
99
  /** Options for building the server-side node runtime. */
88
- serverBuildOptions?: ServerBuildOptions;
89
- /** Options for the Node-RED dev server launcher. */
90
- nodeRedLauncherOptions?: NodeRedLauncherOptions;
100
+ server?: ServerBuildOptions;
101
+ /** Options for building the client-side editor UI. */
102
+ client?: ClientBuildOptions;
91
103
  /** Extra files to copy into the output directory (e.g., LICENSE, README). */
92
104
  extraFilesCopyTargets?: CopyTarget[];
93
105
  }
106
+ /**
107
+ * Options for the `nrg()` Vite plugin.
108
+ *
109
+ * All options are optional — defaults work for the standard `src/` directory
110
+ * layout. Options are grouped by concern: `server` for the dev server and
111
+ * `build` for the production bundle.
112
+ */
113
+ export interface NrgPluginOptions {
114
+ /** Dev server options (Node-RED launcher, editor path slug). */
115
+ server?: ServerOptions;
116
+ /** Production build options (output dir, server/client bundles, copies). */
117
+ build?: BuildOptions;
118
+ }
94
119
  export interface PackageJson {
95
120
  name: string;
96
121
  version: string;
@@ -152,6 +177,10 @@ export interface NodeRedLauncher {
152
177
  cleanup(): void;
153
178
  flushLogs(): void;
154
179
  readonly preferredPort: number;
180
+ /** URL-safe path slug the editor is mounted under (empty for root). */
181
+ readonly slug: string;
182
+ /** Path prefix derived from the slug — `/<slug>/`, or `/` when no slug. */
183
+ readonly basePath: string;
155
184
  readonly restartDelay: number;
156
185
  readonly pid: number | null;
157
186
  }