@angflow/angular 0.0.18 → 0.3.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/README.md +108 -15
- package/dist/base.css +8 -0
- package/dist/esm/index.d.ts +0 -1
- package/dist/esm/lib/agent/agent-bridge.service.d.ts +3 -1
- package/dist/esm/lib/agent/agent-bridge.service.js +130 -23
- package/dist/esm/lib/agent/agent-bridge.service.js.map +1 -1
- package/dist/esm/lib/agent/chat/agent-chat.component.d.ts +0 -1
- package/dist/esm/lib/agent/chat/agent-chat.service.d.ts +0 -1
- package/dist/esm/lib/agent/chat/agent-chat.service.js +11 -2
- package/dist/esm/lib/agent/chat/agent-chat.service.js.map +1 -1
- package/dist/esm/lib/agent/chat/default-system-prompt.d.ts +1 -2
- package/dist/esm/lib/agent/chat/default-system-prompt.js +2 -1
- package/dist/esm/lib/agent/chat/default-system-prompt.js.map +1 -1
- package/dist/esm/lib/agent/chat/index.d.ts +0 -1
- package/dist/esm/lib/agent/chat/provide-agent-chat.d.ts +0 -1
- package/dist/esm/lib/agent/chat/types.d.ts +0 -1
- package/dist/esm/lib/agent/history.d.ts +0 -1
- package/dist/esm/lib/agent/index.d.ts +0 -1
- package/dist/esm/lib/agent/provide-agent-bridge.d.ts +0 -1
- package/dist/esm/lib/agent/tool-schemas.d.ts +0 -1
- package/dist/esm/lib/agent/tool-schemas.js +3 -1
- package/dist/esm/lib/agent/tool-schemas.js.map +1 -1
- package/dist/esm/lib/agent/transports/websocket.d.ts +8 -1
- package/dist/esm/lib/agent/transports/websocket.js +14 -2
- package/dist/esm/lib/agent/transports/websocket.js.map +1 -1
- package/dist/esm/lib/agent/transports/window.d.ts +0 -1
- package/dist/esm/lib/agent/transports/window.js +2 -1
- package/dist/esm/lib/agent/transports/window.js.map +1 -1
- package/dist/esm/lib/agent/types.d.ts +0 -1
- package/dist/esm/lib/components/a11y-descriptions/a11y-descriptions.component.d.ts +0 -1
- package/dist/esm/lib/components/attribution/attribution.component.d.ts +0 -1
- package/dist/esm/lib/components/background/background.component.d.ts +0 -1
- package/dist/esm/lib/components/connection-line/connection-line.component.d.ts +0 -1
- package/dist/esm/lib/components/controls/controls.component.d.ts +0 -1
- package/dist/esm/lib/components/controls/controls.component.js +2 -1
- package/dist/esm/lib/components/controls/controls.component.js.map +1 -1
- package/dist/esm/lib/components/edge-label-renderer/edge-label-renderer.component.d.ts +0 -1
- package/dist/esm/lib/components/edge-toolbar/edge-toolbar.component.d.ts +7 -2
- package/dist/esm/lib/components/edge-toolbar/edge-toolbar.component.js +11 -4
- package/dist/esm/lib/components/edge-toolbar/edge-toolbar.component.js.map +1 -1
- package/dist/esm/lib/components/edges/base-edge.component.d.ts +0 -1
- package/dist/esm/lib/components/edges/bezier-edge.component.d.ts +0 -1
- package/dist/esm/lib/components/edges/bezier-edge.component.js +2 -1
- package/dist/esm/lib/components/edges/bezier-edge.component.js.map +1 -1
- package/dist/esm/lib/components/edges/edge-text.component.d.ts +0 -1
- package/dist/esm/lib/components/edges/simple-bezier-edge.component.d.ts +0 -1
- package/dist/esm/lib/components/edges/simple-bezier-edge.component.js +2 -1
- package/dist/esm/lib/components/edges/simple-bezier-edge.component.js.map +1 -1
- package/dist/esm/lib/components/edges/smooth-step-edge.component.d.ts +0 -1
- package/dist/esm/lib/components/edges/smooth-step-edge.component.js +2 -1
- package/dist/esm/lib/components/edges/smooth-step-edge.component.js.map +1 -1
- package/dist/esm/lib/components/edges/step-edge.component.d.ts +0 -1
- package/dist/esm/lib/components/edges/step-edge.component.js +2 -1
- package/dist/esm/lib/components/edges/step-edge.component.js.map +1 -1
- package/dist/esm/lib/components/edges/straight-edge.component.d.ts +0 -1
- package/dist/esm/lib/components/edges/straight-edge.component.js +2 -1
- package/dist/esm/lib/components/edges/straight-edge.component.js.map +1 -1
- package/dist/esm/lib/components/handle/handle.component.d.ts +1 -1
- package/dist/esm/lib/components/handle/handle.component.js +13 -1
- package/dist/esm/lib/components/handle/handle.component.js.map +1 -1
- package/dist/esm/lib/components/handle-group/handle-group.component.d.ts +0 -1
- package/dist/esm/lib/components/handle-group/handle-row.component.d.ts +0 -1
- package/dist/esm/lib/components/minimap/minimap.component.d.ts +36 -25
- package/dist/esm/lib/components/minimap/minimap.component.js +197 -213
- package/dist/esm/lib/components/minimap/minimap.component.js.map +1 -1
- package/dist/esm/lib/components/ng-flow-provider/ng-flow-provider.component.d.ts +13 -5
- package/dist/esm/lib/components/ng-flow-provider/ng-flow-provider.component.js +13 -4
- package/dist/esm/lib/components/ng-flow-provider/ng-flow-provider.component.js.map +1 -1
- package/dist/esm/lib/components/node-resizer/node-resizer.component.d.ts +2 -1
- package/dist/esm/lib/components/node-resizer/node-resizer.component.js +43 -26
- package/dist/esm/lib/components/node-resizer/node-resizer.component.js.map +1 -1
- package/dist/esm/lib/components/node-toolbar/node-toolbar.component.d.ts +0 -1
- package/dist/esm/lib/components/node-toolbar/node-toolbar.component.js +1 -1
- package/dist/esm/lib/components/node-toolbar/node-toolbar.component.js.map +1 -1
- package/dist/esm/lib/components/nodes/default-node.component.d.ts +6 -15
- package/dist/esm/lib/components/nodes/default-node.component.js +13 -22
- package/dist/esm/lib/components/nodes/default-node.component.js.map +1 -1
- package/dist/esm/lib/components/nodes/group-node.component.d.ts +5 -16
- package/dist/esm/lib/components/nodes/group-node.component.js +8 -16
- package/dist/esm/lib/components/nodes/group-node.component.js.map +1 -1
- package/dist/esm/lib/components/nodes/input-node.component.d.ts +6 -15
- package/dist/esm/lib/components/nodes/input-node.component.js +11 -20
- package/dist/esm/lib/components/nodes/input-node.component.js.map +1 -1
- package/dist/esm/lib/components/nodes/output-node.component.d.ts +6 -15
- package/dist/esm/lib/components/nodes/output-node.component.js +11 -20
- package/dist/esm/lib/components/nodes/output-node.component.js.map +1 -1
- package/dist/esm/lib/components/nodes/template-node.component.d.ts +2 -14
- package/dist/esm/lib/components/nodes/template-node.component.js +16 -26
- package/dist/esm/lib/components/nodes/template-node.component.js.map +1 -1
- package/dist/esm/lib/components/panel/panel.component.d.ts +0 -1
- package/dist/esm/lib/components/selection-box/selection-box.component.d.ts +11 -1
- package/dist/esm/lib/components/selection-box/selection-box.component.js +108 -3
- package/dist/esm/lib/components/selection-box/selection-box.component.js.map +1 -1
- package/dist/esm/lib/components/viewport-portal/viewport-portal.component.d.ts +0 -1
- package/dist/esm/lib/container/edge-renderer/edge-renderer.component.d.ts +27 -3
- package/dist/esm/lib/container/edge-renderer/edge-renderer.component.js +151 -61
- package/dist/esm/lib/container/edge-renderer/edge-renderer.component.js.map +1 -1
- package/dist/esm/lib/container/ng-flow/ng-flow.component.d.ts +4 -8
- package/dist/esm/lib/container/ng-flow/ng-flow.component.js +151 -54
- package/dist/esm/lib/container/ng-flow/ng-flow.component.js.map +1 -1
- package/dist/esm/lib/container/node-renderer/node-renderer.component.d.ts +7 -1
- package/dist/esm/lib/container/node-renderer/node-renderer.component.js +70 -14
- package/dist/esm/lib/container/node-renderer/node-renderer.component.js.map +1 -1
- package/dist/esm/lib/container/pane/pane.component.d.ts +0 -1
- package/dist/esm/lib/container/pane/pane.component.js +23 -3
- package/dist/esm/lib/container/pane/pane.component.js.map +1 -1
- package/dist/esm/lib/container/viewport/viewport.component.d.ts +0 -1
- package/dist/esm/lib/directives/drag.directive.d.ts +1 -2
- package/dist/esm/lib/directives/drag.directive.js +1 -1
- package/dist/esm/lib/directives/drag.directive.js.map +1 -1
- package/dist/esm/lib/directives/drop-zone.directive.d.ts +0 -1
- package/dist/esm/lib/directives/key-handler.directive.d.ts +0 -1
- package/dist/esm/lib/directives/key-handler.directive.js +6 -2
- package/dist/esm/lib/directives/key-handler.directive.js.map +1 -1
- package/dist/esm/lib/directives/node-type.directive.d.ts +0 -1
- package/dist/esm/lib/graph/collapse.d.ts +35 -0
- package/dist/esm/lib/graph/collapse.js +102 -0
- package/dist/esm/lib/graph/collapse.js.map +1 -0
- package/dist/esm/lib/graph/group-bounds.d.ts +42 -0
- package/dist/esm/lib/graph/group-bounds.js +34 -0
- package/dist/esm/lib/graph/group-bounds.js.map +1 -0
- package/dist/esm/lib/layout/dagre-layout.d.ts +0 -1
- package/dist/esm/lib/layout/index.d.ts +1 -2
- package/dist/esm/lib/layout/index.js.map +1 -1
- package/dist/esm/lib/layout/layout-nodes.d.ts +39 -5
- package/dist/esm/lib/layout/layout-nodes.js +94 -7
- package/dist/esm/lib/layout/layout-nodes.js.map +1 -1
- package/dist/esm/lib/public-api.d.ts +4 -2
- package/dist/esm/lib/public-api.js +2 -1
- package/dist/esm/lib/public-api.js.map +1 -1
- package/dist/esm/lib/services/flow-store.service.d.ts +25 -2
- package/dist/esm/lib/services/flow-store.service.js +101 -14
- package/dist/esm/lib/services/flow-store.service.js.map +1 -1
- package/dist/esm/lib/services/ng-flow.service.d.ts +82 -7
- package/dist/esm/lib/services/ng-flow.service.js +210 -26
- package/dist/esm/lib/services/ng-flow.service.js.map +1 -1
- package/dist/esm/lib/services/tokens.d.ts +0 -1
- package/dist/esm/lib/types/edges.d.ts +0 -1
- package/dist/esm/lib/types/general.d.ts +0 -1
- package/dist/esm/lib/types/index.d.ts +0 -1
- package/dist/esm/lib/types/node-template.d.ts +2 -1
- package/dist/esm/lib/types/nodes.d.ts +4 -1
- package/dist/esm/lib/types/store.d.ts +0 -1
- package/dist/esm/lib/utils/changes.d.ts +28 -2
- package/dist/esm/lib/utils/changes.js +57 -1
- package/dist/esm/lib/utils/changes.js.map +1 -1
- package/dist/esm/lib/utils/index.d.ts +2 -2
- package/dist/esm/lib/utils/index.js +2 -1
- package/dist/esm/lib/utils/index.js.map +1 -1
- package/dist/esm/lib/utils/inject-flow-store.d.ts +16 -0
- package/dist/esm/lib/utils/inject-flow-store.js +23 -0
- package/dist/esm/lib/utils/inject-flow-store.js.map +1 -0
- package/dist/esm/lib/utils/inject-ng-flow-node.d.ts +0 -1
- package/dist/esm/lib/utils/position-tween.d.ts +0 -1
- package/dist/esm/lib/utils/template-interpolation.d.ts +0 -1
- package/dist/esm/lib/utils/type-guards.d.ts +0 -1
- package/dist/esm/lib/utils/type-guards.js +2 -0
- package/dist/esm/lib/utils/type-guards.js.map +1 -1
- package/dist/esm/test-setup.d.ts +0 -1
- package/dist/style.css +8 -0
- package/package.json +83 -78
- package/dist/esm/index.d.ts.map +0 -1
- package/dist/esm/lib/agent/agent-bridge.service.d.ts.map +0 -1
- package/dist/esm/lib/agent/chat/agent-chat.component.d.ts.map +0 -1
- package/dist/esm/lib/agent/chat/agent-chat.service.d.ts.map +0 -1
- package/dist/esm/lib/agent/chat/default-system-prompt.d.ts.map +0 -1
- package/dist/esm/lib/agent/chat/index.d.ts.map +0 -1
- package/dist/esm/lib/agent/chat/provide-agent-chat.d.ts.map +0 -1
- package/dist/esm/lib/agent/chat/types.d.ts.map +0 -1
- package/dist/esm/lib/agent/history.d.ts.map +0 -1
- package/dist/esm/lib/agent/index.d.ts.map +0 -1
- package/dist/esm/lib/agent/provide-agent-bridge.d.ts.map +0 -1
- package/dist/esm/lib/agent/tool-schemas.d.ts.map +0 -1
- package/dist/esm/lib/agent/transports/websocket.d.ts.map +0 -1
- package/dist/esm/lib/agent/transports/window.d.ts.map +0 -1
- package/dist/esm/lib/agent/types.d.ts.map +0 -1
- package/dist/esm/lib/components/a11y-descriptions/a11y-descriptions.component.d.ts.map +0 -1
- package/dist/esm/lib/components/attribution/attribution.component.d.ts.map +0 -1
- package/dist/esm/lib/components/background/background.component.d.ts.map +0 -1
- package/dist/esm/lib/components/connection-line/connection-line.component.d.ts.map +0 -1
- package/dist/esm/lib/components/controls/controls.component.d.ts.map +0 -1
- package/dist/esm/lib/components/edge-label-renderer/edge-label-renderer.component.d.ts.map +0 -1
- package/dist/esm/lib/components/edge-toolbar/edge-toolbar.component.d.ts.map +0 -1
- package/dist/esm/lib/components/edges/base-edge.component.d.ts.map +0 -1
- package/dist/esm/lib/components/edges/bezier-edge.component.d.ts.map +0 -1
- package/dist/esm/lib/components/edges/edge-text.component.d.ts.map +0 -1
- package/dist/esm/lib/components/edges/simple-bezier-edge.component.d.ts.map +0 -1
- package/dist/esm/lib/components/edges/smooth-step-edge.component.d.ts.map +0 -1
- package/dist/esm/lib/components/edges/step-edge.component.d.ts.map +0 -1
- package/dist/esm/lib/components/edges/straight-edge.component.d.ts.map +0 -1
- package/dist/esm/lib/components/handle/handle.component.d.ts.map +0 -1
- package/dist/esm/lib/components/handle-group/handle-group.component.d.ts.map +0 -1
- package/dist/esm/lib/components/handle-group/handle-row.component.d.ts.map +0 -1
- package/dist/esm/lib/components/minimap/minimap.component.d.ts.map +0 -1
- package/dist/esm/lib/components/ng-flow-provider/ng-flow-provider.component.d.ts.map +0 -1
- package/dist/esm/lib/components/node-resizer/node-resizer.component.d.ts.map +0 -1
- package/dist/esm/lib/components/node-toolbar/node-toolbar.component.d.ts.map +0 -1
- package/dist/esm/lib/components/nodes/default-node.component.d.ts.map +0 -1
- package/dist/esm/lib/components/nodes/group-node.component.d.ts.map +0 -1
- package/dist/esm/lib/components/nodes/input-node.component.d.ts.map +0 -1
- package/dist/esm/lib/components/nodes/output-node.component.d.ts.map +0 -1
- package/dist/esm/lib/components/nodes/template-node.component.d.ts.map +0 -1
- package/dist/esm/lib/components/panel/panel.component.d.ts.map +0 -1
- package/dist/esm/lib/components/selection-box/selection-box.component.d.ts.map +0 -1
- package/dist/esm/lib/components/viewport-portal/viewport-portal.component.d.ts.map +0 -1
- package/dist/esm/lib/container/edge-renderer/edge-renderer.component.d.ts.map +0 -1
- package/dist/esm/lib/container/ng-flow/ng-flow.component.d.ts.map +0 -1
- package/dist/esm/lib/container/node-renderer/node-renderer.component.d.ts.map +0 -1
- package/dist/esm/lib/container/pane/pane.component.d.ts.map +0 -1
- package/dist/esm/lib/container/viewport/viewport.component.d.ts.map +0 -1
- package/dist/esm/lib/directives/drag.directive.d.ts.map +0 -1
- package/dist/esm/lib/directives/drop-zone.directive.d.ts.map +0 -1
- package/dist/esm/lib/directives/key-handler.directive.d.ts.map +0 -1
- package/dist/esm/lib/directives/node-type.directive.d.ts.map +0 -1
- package/dist/esm/lib/layout/dagre-layout.d.ts.map +0 -1
- package/dist/esm/lib/layout/index.d.ts.map +0 -1
- package/dist/esm/lib/layout/layout-nodes.d.ts.map +0 -1
- package/dist/esm/lib/public-api.d.ts.map +0 -1
- package/dist/esm/lib/services/flow-store.service.d.ts.map +0 -1
- package/dist/esm/lib/services/ng-flow.service.d.ts.map +0 -1
- package/dist/esm/lib/services/tokens.d.ts.map +0 -1
- package/dist/esm/lib/types/edges.d.ts.map +0 -1
- package/dist/esm/lib/types/general.d.ts.map +0 -1
- package/dist/esm/lib/types/index.d.ts.map +0 -1
- package/dist/esm/lib/types/node-template.d.ts.map +0 -1
- package/dist/esm/lib/types/nodes.d.ts.map +0 -1
- package/dist/esm/lib/types/store.d.ts.map +0 -1
- package/dist/esm/lib/utils/changes.d.ts.map +0 -1
- package/dist/esm/lib/utils/index.d.ts.map +0 -1
- package/dist/esm/lib/utils/inject-ng-flow-node.d.ts.map +0 -1
- package/dist/esm/lib/utils/position-tween.d.ts.map +0 -1
- package/dist/esm/lib/utils/template-interpolation.d.ts.map +0 -1
- package/dist/esm/lib/utils/type-guards.d.ts.map +0 -1
- package/dist/esm/test-setup.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -118,38 +118,55 @@ The app ships three sections:
|
|
|
118
118
|
|
|
119
119
|
## Custom Nodes
|
|
120
120
|
|
|
121
|
-
Create any Angular component and register it as a node type. Use the `nodrag` CSS class on interactive elements (inputs, dropdowns) to prevent drag interference.
|
|
121
|
+
Create any Angular component and register it as a node type. The preferred way to read per-node state is `injectNgFlowNode<TData>()`, which returns reactive read-only signals for every property the library tracks (`id`, `data`, `selected`, `dragging`, `zIndex`, `isConnectable`, `position`, `sourcePosition`, `targetPosition`, `dragHandle`, `type`, `collapsed`). Use the `nodrag` CSS class on interactive elements (inputs, dropdowns) to prevent drag interference.
|
|
122
122
|
|
|
123
123
|
```typescript
|
|
124
|
+
import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
|
|
125
|
+
import {
|
|
126
|
+
HandleComponent,
|
|
127
|
+
Position,
|
|
128
|
+
NgFlowService,
|
|
129
|
+
injectNgFlowNode,
|
|
130
|
+
} from '@angflow/angular';
|
|
131
|
+
|
|
132
|
+
interface FormData { title: string; name: string; type: string }
|
|
133
|
+
|
|
124
134
|
@Component({
|
|
125
135
|
selector: 'app-form-node',
|
|
126
136
|
standalone: true,
|
|
137
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
127
138
|
imports: [HandleComponent],
|
|
128
139
|
template: `
|
|
129
|
-
<ng-flow-handle type="target" [position]="Position.Top" />
|
|
130
|
-
<div class="my-node">
|
|
131
|
-
<h3>{{ data()?.title }}</h3>
|
|
140
|
+
<ng-flow-handle type="target" [position]="Position.Top" [isConnectable]="node.isConnectable()" />
|
|
141
|
+
<div class="my-node" [class.selected]="node.selected()">
|
|
142
|
+
<h3>{{ node.data()?.title }}</h3>
|
|
132
143
|
<div class="nodrag">
|
|
133
|
-
<input [value]="data()?.name" (input)="onNameChange($event)" />
|
|
134
|
-
<select [value]="data()?.type" (change)="onTypeChange($event)">
|
|
144
|
+
<input [value]="node.data()?.name" (input)="onNameChange($event)" />
|
|
145
|
+
<select [value]="node.data()?.type" (change)="onTypeChange($event)">
|
|
135
146
|
<option value="string">String</option>
|
|
136
147
|
<option value="number">Number</option>
|
|
137
148
|
</select>
|
|
138
149
|
</div>
|
|
139
150
|
</div>
|
|
140
|
-
<ng-flow-handle type="source" [position]="Position.Bottom" />
|
|
151
|
+
<ng-flow-handle type="source" [position]="Position.Bottom" [isConnectable]="node.isConnectable()" />
|
|
141
152
|
`,
|
|
142
153
|
})
|
|
143
154
|
export class FormNodeComponent {
|
|
144
155
|
readonly Position = Position;
|
|
145
|
-
readonly
|
|
146
|
-
readonly data = input<any>();
|
|
147
|
-
// ... other standard inputs: type, selected, dragging, zIndex, etc.
|
|
156
|
+
readonly node = injectNgFlowNode<FormData>();
|
|
148
157
|
|
|
149
158
|
private flowService = inject(NgFlowService);
|
|
150
159
|
|
|
151
160
|
onNameChange(event: Event) {
|
|
152
|
-
this.flowService.updateNodeData(this.id(), {
|
|
161
|
+
this.flowService.updateNodeData(this.node.id(), {
|
|
162
|
+
name: (event.target as HTMLInputElement).value,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
onTypeChange(event: Event) {
|
|
167
|
+
this.flowService.updateNodeData(this.node.id(), {
|
|
168
|
+
type: (event.target as HTMLSelectElement).value,
|
|
169
|
+
});
|
|
153
170
|
}
|
|
154
171
|
}
|
|
155
172
|
```
|
|
@@ -164,6 +181,24 @@ nodeTypes = { formNode: FormNodeComponent };
|
|
|
164
181
|
<ng-flow [nodes]="nodes" [edges]="edges" [nodeTypes]="nodeTypes" ...>
|
|
165
182
|
```
|
|
166
183
|
|
|
184
|
+
#### Legacy: flat `@Input()`s
|
|
185
|
+
|
|
186
|
+
The original API — declaring one `@Input()` per tracked property — is still fully supported, so existing custom nodes keep working without changes:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
export class FormNodeComponent {
|
|
190
|
+
readonly Position = Position;
|
|
191
|
+
readonly id = input.required<string>();
|
|
192
|
+
readonly data = input<FormData>();
|
|
193
|
+
readonly selected = input(false);
|
|
194
|
+
readonly isConnectable = input(true);
|
|
195
|
+
// ...plus type, dragging, zIndex, positionAbsoluteX/Y,
|
|
196
|
+
// sourcePosition, targetPosition, dragHandle as needed
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Prefer `injectNgFlowNode()` for new code: one injection call replaces ~13 input declarations, the signals are reactive in the same way, and you only pull the fields you actually use.
|
|
201
|
+
|
|
167
202
|
## Programmatic API
|
|
168
203
|
|
|
169
204
|
Inject `NgFlowService` for viewport control, node/edge manipulation, and spatial queries:
|
|
@@ -212,10 +247,30 @@ flow.applyLayout(layoutNodes, { direction: 'LR' });
|
|
|
212
247
|
```
|
|
213
248
|
|
|
214
249
|
Options: `direction` (`'TB' | 'LR' | 'BT' | 'RL'`, default `'TB'`), `nodeSep`
|
|
215
|
-
(default 50), `rankSep` (default 80). Node dimensions resolve from
|
|
216
|
-
`
|
|
217
|
-
|
|
218
|
-
|
|
250
|
+
(default 50), `rankSep` (default 80). Node dimensions resolve from `measured` → `width`/`height` →
|
|
251
|
+
`initialWidth`/`initialHeight` → 150×40, and edge labels reserve dagre space from
|
|
252
|
+
`labelWidth`/`labelHeight` (auto-measured by `applyLayout`) or a default box when an
|
|
253
|
+
edge has a non-empty `label`. Any function with the same shape plugs into
|
|
254
|
+
`applyLayout` (elk, custom grids, …).
|
|
255
|
+
|
|
256
|
+
### Controlled mode and `measured`
|
|
257
|
+
|
|
258
|
+
In controlled mode (`[nodes]` bound, re-emitted from `(nodesChange)`), re-emitting
|
|
259
|
+
nodes that don't carry `measured` resets the stored dimensions. `applyLayout` reads
|
|
260
|
+
live DOM sizes so layout stays correct regardless — but floating edges and `fitView`
|
|
261
|
+
read the stored `measured` directly. If your app hand-handles only some changes (e.g.
|
|
262
|
+
keeps `position` authority in a journal), forward dimension changes with
|
|
263
|
+
`applyDimensionChanges` so `measured` stays current:
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
import { applyDimensionChanges } from '@angflow/angular';
|
|
267
|
+
|
|
268
|
+
onNodesChange(changes: NodeChange[]) {
|
|
269
|
+
// keep measured flowing back: floating edges, fitView, layout stay correct
|
|
270
|
+
this.nodes.update((ns) => applyDimensionChanges(changes, ns));
|
|
271
|
+
// ...your own position/data handling on top...
|
|
272
|
+
}
|
|
273
|
+
```
|
|
219
274
|
|
|
220
275
|
## Animations
|
|
221
276
|
|
|
@@ -233,6 +288,44 @@ Animated paths: `setNodePositions`, `applyLayout`, and the agent bridge's
|
|
|
233
288
|
tween on that node, and everything is disabled under `prefers-reduced-motion`.
|
|
234
289
|
Per-call override: `flow.setNodePositions(positions, { animate: false })`.
|
|
235
290
|
|
|
291
|
+
## Group Collapse
|
|
292
|
+
|
|
293
|
+
Set `collapsed: true` on a group/parent node and angflow folds it: descendants stop
|
|
294
|
+
rendering, the box drops to a header strip (`.collapsed` CSS class), and edges crossing
|
|
295
|
+
the boundary reroute onto the box (parallels merge). It is nesting-aware — edges reroute
|
|
296
|
+
to the outermost collapsed ancestor.
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
flow.setNodeCollapsed(groupId, true); // or toggleNodeCollapsed(groupId)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
`collapsed` lives on the node, so in controlled mode it round-trips through
|
|
303
|
+
`(nodesChange)` like any other field. A merged (deduped) display edge is render-only and
|
|
304
|
+
carries its underlying edge ids on `collapsedFrom`; a 1:1 rerouted edge keeps its original
|
|
305
|
+
identity. Rerouted edges attach to the box via the normal edge geometry — cleanest under
|
|
306
|
+
`edgeMode="floating"`. The collapsed box renders at `--xy-node-collapsed-height` (40px
|
|
307
|
+
default); auto-sizing the expanded box to its children is separate.
|
|
308
|
+
|
|
309
|
+
## Group Auto-Size
|
|
310
|
+
|
|
311
|
+
Compute a box that wraps a group's members, or have the service size a group for you:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import { getGroupBounds } from '@angflow/angular';
|
|
315
|
+
|
|
316
|
+
// Pure: bounds in the same coordinate space as the members you pass.
|
|
317
|
+
const box = getGroupBounds(members, { padding: 24, headerHeight: 40 });
|
|
318
|
+
|
|
319
|
+
// Imperative: size + position a group to wrap its children, keeping them pinned.
|
|
320
|
+
await flow.sizeGroupToChildren(groupId, { padding: 24, headerHeight: 40 });
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
`getGroupBounds` resolves member sizes `measured → width → 0` and applies an asymmetric top
|
|
324
|
+
(`headerHeight`) vs. other-sides (`padding`) inset. `sizeGroupToChildren` sets the group's
|
|
325
|
+
`width`/`height` and moves its top-left to wrap its `parentId` children, re-basing the children so
|
|
326
|
+
they stay visually fixed (nested groups handled via absolute-coordinate translation). It is a no-op
|
|
327
|
+
for a childless group.
|
|
328
|
+
|
|
236
329
|
## Architecture
|
|
237
330
|
|
|
238
331
|
- **Signal-based state** — Angular 17+ signals for fine-grained reactivity (no RxJS in the store)
|
package/dist/base.css
CHANGED
|
@@ -955,6 +955,14 @@ svg.xy-flow__connectionline {
|
|
|
955
955
|
}
|
|
956
956
|
|
|
957
957
|
|
|
958
|
+
.xy-flow__node.collapsed {
|
|
959
|
+
/* Folded group: drop the expanded footprint to a header strip. Overrides an
|
|
960
|
+
inline style.height set in controlled mode; apps may extend this rule. */
|
|
961
|
+
height: var(--xy-node-collapsed-height, 40px) !important;
|
|
962
|
+
overflow: hidden;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
|
|
958
966
|
/* Entry animation — applied by the node renderer when [animate] is enabled.
|
|
959
967
|
Uses the standalone `scale` property so it composes with the inline
|
|
960
968
|
translate() transform instead of overriding it. */
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ export declare class AngflowAgentBridge {
|
|
|
48
48
|
private readonly history;
|
|
49
49
|
private readonly handlers;
|
|
50
50
|
private readonly injector;
|
|
51
|
+
private readonly destroyRef;
|
|
51
52
|
private readonly layoutFn;
|
|
52
53
|
private started;
|
|
53
54
|
private nextInProcessId;
|
|
@@ -82,6 +83,8 @@ export declare class AngflowAgentBridge {
|
|
|
82
83
|
*/
|
|
83
84
|
callTool(method: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
84
85
|
private start;
|
|
86
|
+
/** Stop all transports. Invoked automatically when the owning injector is destroyed. */
|
|
87
|
+
private stop;
|
|
85
88
|
private dispatch;
|
|
86
89
|
private findFlowId;
|
|
87
90
|
private emitHistory;
|
|
@@ -92,4 +95,3 @@ export declare class AngflowAgentBridge {
|
|
|
92
95
|
static ɵfac: i0.ɵɵFactoryDeclaration<AngflowAgentBridge, [{ optional: true; }, { optional: true; }, { optional: true; }, { optional: true; }]>;
|
|
93
96
|
static ɵprov: i0.ɵɵInjectableDeclaration<AngflowAgentBridge>;
|
|
94
97
|
}
|
|
95
|
-
//# sourceMappingURL=agent-bridge.service.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Inject, Injectable, InjectionToken, Injector, Optional, effect, inject, runInInjectionContext, signal, } from '@angular/core';
|
|
1
|
+
import { DestroyRef, Inject, Injectable, InjectionToken, Injector, Optional, effect, inject, runInInjectionContext, signal, } from '@angular/core';
|
|
2
2
|
import { AgentHistory } from './history';
|
|
3
3
|
import { AGENT_TOOL_SCHEMAS } from './tool-schemas';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
@@ -27,6 +27,9 @@ const MUTATING_TOOLS = new Set([
|
|
|
27
27
|
'set_nodes',
|
|
28
28
|
'set_edges',
|
|
29
29
|
]);
|
|
30
|
+
// apply_changes is treated specially — see dispatch logic.
|
|
31
|
+
/** Max flow.state emission rate while any node drag is in progress. */
|
|
32
|
+
const DRAG_STATE_EMIT_INTERVAL_MS = 100;
|
|
30
33
|
/**
|
|
31
34
|
* Routes JSON-RPC requests from one or more transports to registered
|
|
32
35
|
* `NgFlowService` instances, and pushes change events back to the agent.
|
|
@@ -58,6 +61,7 @@ export class AngflowAgentBridge {
|
|
|
58
61
|
this.flows = new Map();
|
|
59
62
|
this.handlers = new Map();
|
|
60
63
|
this.injector = inject(Injector);
|
|
64
|
+
this.destroyRef = inject(DestroyRef);
|
|
61
65
|
this.started = false;
|
|
62
66
|
this.nextInProcessId = 1;
|
|
63
67
|
this.warnedOnBeforeDeleteBypass = false;
|
|
@@ -70,6 +74,7 @@ export class AngflowAgentBridge {
|
|
|
70
74
|
this.layoutFn = layoutFn ?? null;
|
|
71
75
|
this.installHandlers();
|
|
72
76
|
this.start();
|
|
77
|
+
this.destroyRef.onDestroy(() => this.stop());
|
|
73
78
|
}
|
|
74
79
|
reportError(err, ctx) {
|
|
75
80
|
if (!this.onError)
|
|
@@ -161,6 +166,20 @@ export class AngflowAgentBridge {
|
|
|
161
166
|
}
|
|
162
167
|
}
|
|
163
168
|
}
|
|
169
|
+
/** Stop all transports. Invoked automatically when the owning injector is destroyed. */
|
|
170
|
+
stop() {
|
|
171
|
+
if (!this.started)
|
|
172
|
+
return;
|
|
173
|
+
this.started = false;
|
|
174
|
+
for (const t of this.transports) {
|
|
175
|
+
try {
|
|
176
|
+
t.stop();
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// A transport that throws during teardown must not break the others.
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
164
183
|
async dispatch(req) {
|
|
165
184
|
const handler = this.handlers.get(req.method);
|
|
166
185
|
if (!handler) {
|
|
@@ -172,6 +191,7 @@ export class AngflowAgentBridge {
|
|
|
172
191
|
try {
|
|
173
192
|
const params = req.params ?? {};
|
|
174
193
|
if (req.method === 'list_flows') {
|
|
194
|
+
// list_flows ignores the service arg; null stub avoids resolving a flow.
|
|
175
195
|
const result = await handler(null, params);
|
|
176
196
|
return { id: req.id, result };
|
|
177
197
|
}
|
|
@@ -297,6 +317,35 @@ export class AngflowAgentBridge {
|
|
|
297
317
|
let pending = false;
|
|
298
318
|
let destroyed = false;
|
|
299
319
|
let lastSignature = '';
|
|
320
|
+
let lastEmitTime = Number.NEGATIVE_INFINITY;
|
|
321
|
+
let trailingTimer = null;
|
|
322
|
+
const emitState = (isDrag) => {
|
|
323
|
+
// Re-read at emit time so coalesced/throttled bursts see the latest state.
|
|
324
|
+
const params = {
|
|
325
|
+
flowId: id,
|
|
326
|
+
nodes: flow.getNodes(),
|
|
327
|
+
edges: flow.getEdges(),
|
|
328
|
+
viewport: flow.getViewport(),
|
|
329
|
+
selection: {
|
|
330
|
+
nodeIds: flow.selectedNodes().map((n) => n.id),
|
|
331
|
+
edgeIds: flow.selectedEdges().map((e) => e.id),
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
const sig = signatureOf(params);
|
|
335
|
+
if (sig === lastSignature)
|
|
336
|
+
return;
|
|
337
|
+
lastSignature = sig;
|
|
338
|
+
// Only record the drag-emission timestamp when actually in a drag — so a
|
|
339
|
+
// non-drag emission (register, mutation tool) does not push back the next
|
|
340
|
+
// drag frame's throttle window.
|
|
341
|
+
if (isDrag) {
|
|
342
|
+
lastEmitTime = Date.now();
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
lastEmitTime = Number.NEGATIVE_INFINITY;
|
|
346
|
+
}
|
|
347
|
+
this.emit({ event: 'flow.state', params });
|
|
348
|
+
};
|
|
300
349
|
const ref = runInInjectionContext(this.injector, () => effect(() => {
|
|
301
350
|
// Touch every signal we want to broadcast so the effect re-runs on change.
|
|
302
351
|
flow.nodes();
|
|
@@ -314,29 +363,39 @@ export class AngflowAgentBridge {
|
|
|
314
363
|
// push state for a flowId that's no longer registered.
|
|
315
364
|
if (destroyed)
|
|
316
365
|
return;
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
366
|
+
// While a drag is in flight the store re-emits nodes per pointer
|
|
367
|
+
// frame; serializing the whole graph at that rate floods every
|
|
368
|
+
// transport. Throttle to one emission per interval with a trailing
|
|
369
|
+
// emit so the final drag state is never lost. The timer only
|
|
370
|
+
// schedules the emission — no view state is written here.
|
|
371
|
+
const dragging = flow.getNodes().some((n) => n.dragging === true);
|
|
372
|
+
if (dragging) {
|
|
373
|
+
const elapsed = Date.now() - lastEmitTime;
|
|
374
|
+
if (elapsed < DRAG_STATE_EMIT_INTERVAL_MS) {
|
|
375
|
+
if (trailingTimer === null) {
|
|
376
|
+
trailingTimer = setTimeout(() => {
|
|
377
|
+
trailingTimer = null;
|
|
378
|
+
if (destroyed)
|
|
379
|
+
return;
|
|
380
|
+
emitState(true);
|
|
381
|
+
}, DRAG_STATE_EMIT_INTERVAL_MS - elapsed);
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (trailingTimer !== null) {
|
|
387
|
+
clearTimeout(trailingTimer);
|
|
388
|
+
trailingTimer = null;
|
|
389
|
+
}
|
|
390
|
+
emitState(dragging);
|
|
336
391
|
});
|
|
337
392
|
}));
|
|
338
393
|
return () => {
|
|
339
394
|
destroyed = true;
|
|
395
|
+
if (trailingTimer !== null) {
|
|
396
|
+
clearTimeout(trailingTimer);
|
|
397
|
+
trailingTimer = null;
|
|
398
|
+
}
|
|
340
399
|
ref.destroy();
|
|
341
400
|
};
|
|
342
401
|
}
|
|
@@ -586,7 +645,6 @@ export class AngflowAgentBridge {
|
|
|
586
645
|
const hasDelete = ops.some((o) => o['op'] === 'delete_elements');
|
|
587
646
|
if (hasDelete) {
|
|
588
647
|
this.warnedOnBeforeDeleteBypass = true;
|
|
589
|
-
// eslint-disable-next-line no-console
|
|
590
648
|
console.warn('[angflow] apply_changes/delete_elements bypasses onBeforeDelete. ' +
|
|
591
649
|
'Call the standalone `delete_elements` tool if you need the veto hook.');
|
|
592
650
|
}
|
|
@@ -740,6 +798,10 @@ export class AngflowAgentBridge {
|
|
|
740
798
|
width: internal?.measured?.width ?? n.width ?? 150,
|
|
741
799
|
height: internal?.measured?.height ?? n.height ?? 40,
|
|
742
800
|
position: { x: n.position.x, y: n.position.y },
|
|
801
|
+
// Forward parentId only when the parent is also being laid out, so the
|
|
802
|
+
// layout fn clusters grouped children within their group. Excluding the
|
|
803
|
+
// parent (e.g. via nodeIds) drops it — the child is then a free node.
|
|
804
|
+
parentId: n.parentId != null && idSet.has(n.parentId) ? n.parentId : undefined,
|
|
743
805
|
};
|
|
744
806
|
});
|
|
745
807
|
// Induced subgraph: only edges with BOTH endpoints in the target set.
|
|
@@ -774,7 +836,6 @@ export class AngflowAgentBridge {
|
|
|
774
836
|
applied[id] = { x: pos.x, y: pos.y };
|
|
775
837
|
}
|
|
776
838
|
if (unknownIds.length > 0) {
|
|
777
|
-
// eslint-disable-next-line no-console
|
|
778
839
|
console.warn(`[angflow] layout_nodes: layout function returned positions for unknown node ids ` +
|
|
779
840
|
`(ignored): ${unknownIds.join(', ')}`);
|
|
780
841
|
}
|
|
@@ -789,7 +850,10 @@ export class AngflowAgentBridge {
|
|
|
789
850
|
}
|
|
790
851
|
// Honors the host's [animate] input: positions tween when it's on, and
|
|
791
852
|
// the await keeps the subsequent fitView measuring settled positions.
|
|
792
|
-
|
|
853
|
+
// Layout fns emit flow-absolute coordinates, so apply in absolute space:
|
|
854
|
+
// setNodePositions resolves each parented child against its parent's new
|
|
855
|
+
// position, keeping grouped children inside their group.
|
|
856
|
+
await flow.setNodePositions(actuallyApplied, { coordinateSpace: 'absolute' });
|
|
793
857
|
const shouldFit = params['fitView'] !== false;
|
|
794
858
|
if (shouldFit && Object.keys(actuallyApplied).length > 0) {
|
|
795
859
|
try {
|
|
@@ -899,6 +963,9 @@ function executeOp(flow, op) {
|
|
|
899
963
|
const nodes = op['nodes'];
|
|
900
964
|
if (!Array.isArray(nodes))
|
|
901
965
|
throw new InvalidParamsError('add_nodes: "nodes" must be an array.');
|
|
966
|
+
if (nodes.length > MAX_BULK_ELEMENTS) {
|
|
967
|
+
throw new InvalidParamsError(`add_nodes: "nodes" exceeds the maximum of ${MAX_BULK_ELEMENTS} elements per call (got ${nodes.length}).`);
|
|
968
|
+
}
|
|
902
969
|
const validated = nodes.map((n, i) => validateNodeShape(n, `apply_changes/add_nodes[${i}]`));
|
|
903
970
|
flow.addNodes(validated);
|
|
904
971
|
return validated.map((n) => flow.getNode(n.id)).filter((n) => !!n);
|
|
@@ -912,6 +979,9 @@ function executeOp(flow, op) {
|
|
|
912
979
|
const edges = op['edges'];
|
|
913
980
|
if (!Array.isArray(edges))
|
|
914
981
|
throw new InvalidParamsError('add_edges: "edges" must be an array.');
|
|
982
|
+
if (edges.length > MAX_BULK_ELEMENTS) {
|
|
983
|
+
throw new InvalidParamsError(`add_edges: "edges" exceeds the maximum of ${MAX_BULK_ELEMENTS} elements per call (got ${edges.length}).`);
|
|
984
|
+
}
|
|
915
985
|
const validated = edges.map((e, i) => validateEdgeShape(e, `apply_changes/add_edges[${i}]`));
|
|
916
986
|
flow.addEdges(validated);
|
|
917
987
|
return validated.map((e) => flow.getEdge(e.id)).filter((e) => !!e);
|
|
@@ -1020,10 +1090,15 @@ function requireObject(params, key) {
|
|
|
1020
1090
|
}
|
|
1021
1091
|
return value;
|
|
1022
1092
|
}
|
|
1093
|
+
/** Hard cap on elements per bulk call (nodes, edges, apply_changes ops). */
|
|
1094
|
+
const MAX_BULK_ELEMENTS = 5000;
|
|
1023
1095
|
function requireArray(params, key) {
|
|
1024
1096
|
const value = params[key];
|
|
1025
1097
|
if (!Array.isArray(value))
|
|
1026
1098
|
throw new InvalidParamsError(`Param "${key}" must be an array.`);
|
|
1099
|
+
if (value.length > MAX_BULK_ELEMENTS) {
|
|
1100
|
+
throw new InvalidParamsError(`Param "${key}" exceeds the maximum of ${MAX_BULK_ELEMENTS} elements per call (got ${value.length}).`);
|
|
1101
|
+
}
|
|
1027
1102
|
return value;
|
|
1028
1103
|
}
|
|
1029
1104
|
function optionalStringArray(params, key) {
|
|
@@ -1033,6 +1108,9 @@ function optionalStringArray(params, key) {
|
|
|
1033
1108
|
if (!Array.isArray(value) || value.some((v) => typeof v !== 'string')) {
|
|
1034
1109
|
throw new InvalidParamsError(`Param "${key}" must be an array of strings.`);
|
|
1035
1110
|
}
|
|
1111
|
+
if (value.length > MAX_BULK_ELEMENTS) {
|
|
1112
|
+
throw new InvalidParamsError(`Param "${key}" exceeds the maximum of ${MAX_BULK_ELEMENTS} elements per call (got ${value.length}).`);
|
|
1113
|
+
}
|
|
1036
1114
|
return value;
|
|
1037
1115
|
}
|
|
1038
1116
|
const BADGE_COLOR_SET = new Set(['slate', 'indigo', 'emerald', 'amber', 'rose']);
|
|
@@ -1116,6 +1194,33 @@ function validateTemplateSpec(value, ctx) {
|
|
|
1116
1194
|
}
|
|
1117
1195
|
return value;
|
|
1118
1196
|
}
|
|
1197
|
+
/**
|
|
1198
|
+
* CSS values that can fetch remote resources (`url(`) or execute in legacy
|
|
1199
|
+
* engines (`expression(`). Angular's style sanitization already blocks
|
|
1200
|
+
* script execution, so this is a narrow redressing/beaconing guard rather
|
|
1201
|
+
* than a full CSS allowlist.
|
|
1202
|
+
*/
|
|
1203
|
+
const CSS_VALUE_BLOCKLIST = /url\s*\(|expression\s*\(/i;
|
|
1204
|
+
/** Shared style/className validation for agent-supplied nodes and edges. */
|
|
1205
|
+
function validateStyleAndClassName(o, ctx, kind) {
|
|
1206
|
+
const style = o['style'];
|
|
1207
|
+
if (style !== undefined) {
|
|
1208
|
+
if (!style || typeof style !== 'object' || Array.isArray(style)) {
|
|
1209
|
+
throw new InvalidParamsError(`${ctx}: ${kind}.style must be a plain object of CSS property/value pairs.`);
|
|
1210
|
+
}
|
|
1211
|
+
for (const [prop, raw] of Object.entries(style)) {
|
|
1212
|
+
if (typeof raw !== 'string' && typeof raw !== 'number') {
|
|
1213
|
+
throw new InvalidParamsError(`${ctx}: ${kind}.style["${prop}"] must be a string or number.`);
|
|
1214
|
+
}
|
|
1215
|
+
if (typeof raw === 'string' && CSS_VALUE_BLOCKLIST.test(raw)) {
|
|
1216
|
+
throw new InvalidParamsError(`${ctx}: ${kind}.style["${prop}"] must not contain "url(" or "expression(".`);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
if (o['className'] !== undefined && typeof o['className'] !== 'string') {
|
|
1221
|
+
throw new InvalidParamsError(`${ctx}: ${kind}.className must be a string.`);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1119
1224
|
/** Validate that `value` is a structurally valid Node payload for add_*. */
|
|
1120
1225
|
function validateNodeShape(value, ctx) {
|
|
1121
1226
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
@@ -1136,6 +1241,7 @@ function validateNodeShape(value, ctx) {
|
|
|
1136
1241
|
if (!Number.isFinite(p['x']) || !Number.isFinite(p['y'])) {
|
|
1137
1242
|
throw new InvalidParamsError(`${ctx}: node.position.{x,y} must be finite (no NaN/Infinity).`);
|
|
1138
1243
|
}
|
|
1244
|
+
validateStyleAndClassName(n, ctx, 'node');
|
|
1139
1245
|
return value;
|
|
1140
1246
|
}
|
|
1141
1247
|
/** Validate that `value` is a structurally valid Edge payload for add_*. */
|
|
@@ -1153,6 +1259,7 @@ function validateEdgeShape(value, ctx) {
|
|
|
1153
1259
|
if (typeof e['target'] !== 'string' || e['target'].length === 0) {
|
|
1154
1260
|
throw new InvalidParamsError(`${ctx}: edge.target must be a non-empty string.`);
|
|
1155
1261
|
}
|
|
1262
|
+
validateStyleAndClassName(e, ctx, 'edge');
|
|
1156
1263
|
return value;
|
|
1157
1264
|
}
|
|
1158
1265
|
//# sourceMappingURL=agent-bridge.service.js.map
|