@principal-ai/principal-view-core 0.26.24 → 0.26.26
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/dist/codegen/type-generator.d.ts.map +1 -1
- package/dist/codegen/type-generator.js +45 -17
- package/dist/codegen/type-generator.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/matchers/SpanMatcher.d.ts.map +1 -1
- package/dist/matchers/SpanMatcher.js +10 -2
- package/dist/matchers/SpanMatcher.js.map +1 -1
- package/dist/registry/EventRegistry.d.ts.map +1 -1
- package/dist/registry/EventRegistry.js +39 -16
- package/dist/registry/EventRegistry.js.map +1 -1
- package/dist/spans/utils.d.ts.map +1 -1
- package/dist/spans/utils.js +21 -0
- package/dist/spans/utils.js.map +1 -1
- package/dist/storyboard/builder.d.ts.map +1 -1
- package/dist/storyboard/builder.js +10 -5
- package/dist/storyboard/builder.js.map +1 -1
- package/dist/telemetry/coverage.d.ts.map +1 -1
- package/dist/telemetry/coverage.js +29 -15
- package/dist/telemetry/coverage.js.map +1 -1
- package/dist/telemetry/event-validator.d.ts.map +1 -1
- package/dist/telemetry/event-validator.js +24 -9
- package/dist/telemetry/event-validator.js.map +1 -1
- package/dist/types/canvas.d.ts +4 -6
- package/dist/types/canvas.d.ts.map +1 -1
- package/dist/types/canvas.js +8 -1
- package/dist/types/canvas.js.map +1 -1
- package/dist/types/library.d.ts +21 -0
- package/dist/types/library.d.ts.map +1 -1
- package/dist/utils/CanvasConverter.d.ts +20 -0
- package/dist/utils/CanvasConverter.d.ts.map +1 -1
- package/dist/utils/CanvasConverter.js +209 -187
- package/dist/utils/CanvasConverter.js.map +1 -1
- package/dist/workflow/edge-validation.d.ts.map +1 -1
- package/dist/workflow/edge-validation.js +13 -4
- package/dist/workflow/edge-validation.js.map +1 -1
- package/package.json +1 -1
- package/src/codegen/type-generator.ts +46 -20
- package/src/index.ts +2 -0
- package/src/matchers/SpanMatcher.ts +11 -2
- package/src/registry/EventRegistry.ts +41 -17
- package/src/spans/utils.ts +22 -0
- package/src/storyboard/builder.ts +11 -4
- package/src/telemetry/coverage.ts +28 -15
- package/src/telemetry/event-validator.ts +26 -11
- package/src/types/canvas.ts +9 -6
- package/src/types/library.ts +22 -0
- package/src/utils/CanvasConverter.ts +218 -181
- package/src/workflow/edge-validation.ts +13 -4
|
@@ -8,6 +8,9 @@ import type {
|
|
|
8
8
|
ExtendedCanvas,
|
|
9
9
|
ExtendedCanvasNode,
|
|
10
10
|
ExtendedCanvasTextNode,
|
|
11
|
+
ExtendedCanvasFileNode,
|
|
12
|
+
ExtendedCanvasLinkNode,
|
|
13
|
+
ExtendedCanvasGroupNode,
|
|
11
14
|
ExtendedCanvasEdge,
|
|
12
15
|
PVEdgeTypeDefinition,
|
|
13
16
|
PVNodeShape,
|
|
@@ -18,6 +21,7 @@ import type {
|
|
|
18
21
|
PVOtelExtension,
|
|
19
22
|
PVEventSchema,
|
|
20
23
|
PVBoundaryExtension,
|
|
24
|
+
OtelNode,
|
|
21
25
|
} from '../types/canvas';
|
|
22
26
|
import type { JsonValue } from '../types';
|
|
23
27
|
import { resolveCanvasColor, isOtelNode } from '../types/canvas';
|
|
@@ -160,6 +164,74 @@ export class CanvasConverter {
|
|
|
160
164
|
* Convert a single canvas node to React Flow node
|
|
161
165
|
*/
|
|
162
166
|
private static convertNode(node: ExtendedCanvasNode, _canvas: ExtendedCanvas): ReactFlowNode {
|
|
167
|
+
// Handle OTEL nodes separately (they never use pv field)
|
|
168
|
+
if (isOtelNode(node)) {
|
|
169
|
+
return this.convertOtelNode(node);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Handle standard JSON Canvas nodes (may use pv extensions)
|
|
173
|
+
return this.convertStandardNode(node);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Convert an OTEL node to React Flow node
|
|
178
|
+
* OTEL nodes use top-level fields (label, otel, event, etc.) and never use pv
|
|
179
|
+
*/
|
|
180
|
+
private static convertOtelNode(node: OtelNode): ReactFlowNode {
|
|
181
|
+
const color = resolveCanvasColor(node.color);
|
|
182
|
+
|
|
183
|
+
const data: ReactFlowNode['data'] = {
|
|
184
|
+
name: node.label,
|
|
185
|
+
nodeType: node.type,
|
|
186
|
+
canvasType: node.type,
|
|
187
|
+
shape: node.shape || 'rectangle',
|
|
188
|
+
icon: node.icon,
|
|
189
|
+
color: node.fill || color,
|
|
190
|
+
width: node.width,
|
|
191
|
+
height: node.height,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
if ('description' in node && node.description) data.description = node.description;
|
|
195
|
+
|
|
196
|
+
// Add OTEL-specific data from top-level fields
|
|
197
|
+
if ('otel' in node && node.otel) {
|
|
198
|
+
(data as Record<string, unknown>).otel = node.otel;
|
|
199
|
+
if (node.otel.status) data.status = node.otel.status;
|
|
200
|
+
if (node.otel.references) data.references = node.otel.references;
|
|
201
|
+
}
|
|
202
|
+
if ('event' in node && node.event) {
|
|
203
|
+
(data as Record<string, unknown>).event = node.event;
|
|
204
|
+
}
|
|
205
|
+
if ('eventRef' in node && node.eventRef) {
|
|
206
|
+
(data as Record<string, unknown>).eventRef = node.eventRef;
|
|
207
|
+
}
|
|
208
|
+
if ('dataSchema' in node && node.dataSchema) {
|
|
209
|
+
data.dataSchema = node.dataSchema;
|
|
210
|
+
}
|
|
211
|
+
if ('boundary' in node && node.boundary) {
|
|
212
|
+
(data as Record<string, unknown>).boundary = node.boundary;
|
|
213
|
+
}
|
|
214
|
+
if ('workflowChips' in node && node.workflowChips) {
|
|
215
|
+
(data as Record<string, unknown>).workflowChips = node.workflowChips;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
id: node.id,
|
|
220
|
+
type: node.shape || 'default',
|
|
221
|
+
position: { x: node.x, y: node.y },
|
|
222
|
+
data,
|
|
223
|
+
style: {
|
|
224
|
+
width: node.width,
|
|
225
|
+
height: node.height,
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Convert a standard JSON Canvas node to React Flow node
|
|
232
|
+
* Standard nodes (text/file/link/group) may use pv extensions
|
|
233
|
+
*/
|
|
234
|
+
private static convertStandardNode(node: ExtendedCanvasTextNode | ExtendedCanvasFileNode | ExtendedCanvasLinkNode | ExtendedCanvasGroupNode): ReactFlowNode {
|
|
163
235
|
const pv = node.pv;
|
|
164
236
|
const color = resolveCanvasColor(node.color);
|
|
165
237
|
|
|
@@ -178,25 +250,17 @@ export class CanvasConverter {
|
|
|
178
250
|
case 'group':
|
|
179
251
|
nodeName = node.label || 'Group';
|
|
180
252
|
break;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
case 'otel-span-convention':
|
|
184
|
-
case 'otel-scope':
|
|
185
|
-
case 'otel-resource':
|
|
186
|
-
case 'otel-boundary':
|
|
187
|
-
nodeName = node.label;
|
|
253
|
+
default:
|
|
254
|
+
nodeName = (node as { id: string }).id;
|
|
188
255
|
break;
|
|
189
256
|
}
|
|
190
257
|
|
|
191
|
-
// Build the data object based on canvas node type
|
|
192
|
-
// For OTEL nodes, use the node type directly; for standard canvas types, use pv or default
|
|
193
|
-
const isOtelType = isOtelNode(node);
|
|
194
258
|
const data: ReactFlowNode['data'] = {
|
|
195
259
|
name: nodeName,
|
|
196
|
-
nodeType:
|
|
260
|
+
nodeType: pv?.nodeType || node.type,
|
|
197
261
|
canvasType: node.type,
|
|
198
|
-
shape:
|
|
199
|
-
icon:
|
|
262
|
+
shape: pv?.shape || 'rectangle',
|
|
263
|
+
icon: pv?.icon,
|
|
200
264
|
color: pv?.states?.idle?.color || color,
|
|
201
265
|
width: node.width,
|
|
202
266
|
height: node.height,
|
|
@@ -211,48 +275,8 @@ export class CanvasConverter {
|
|
|
211
275
|
data.url = node.url;
|
|
212
276
|
}
|
|
213
277
|
|
|
214
|
-
//
|
|
215
|
-
if (
|
|
216
|
-
node.type === 'otel-event' ||
|
|
217
|
-
node.type === 'otel-span-convention' ||
|
|
218
|
-
node.type === 'otel-scope' ||
|
|
219
|
-
node.type === 'otel-resource' ||
|
|
220
|
-
node.type === 'otel-boundary'
|
|
221
|
-
) {
|
|
222
|
-
// OTEL nodes use top-level fields, not pv wrapper
|
|
223
|
-
data.nodeType = node.type;
|
|
224
|
-
if (node.icon) data.icon = node.icon;
|
|
225
|
-
if (node.fill) data.color = node.fill;
|
|
226
|
-
if (node.shape) data.shape = node.shape;
|
|
227
|
-
if ('description' in node && node.description) data.description = node.description;
|
|
228
|
-
|
|
229
|
-
// Add OTEL-specific data
|
|
230
|
-
if ('otel' in node && node.otel) {
|
|
231
|
-
(data as Record<string, unknown>).otel = node.otel;
|
|
232
|
-
if (node.otel.status) data.status = node.otel.status;
|
|
233
|
-
if (node.otel.references) data.references = node.otel.references;
|
|
234
|
-
}
|
|
235
|
-
if ('event' in node && node.event) {
|
|
236
|
-
(data as Record<string, unknown>).event = node.event;
|
|
237
|
-
}
|
|
238
|
-
if ('eventRef' in node && node.eventRef) {
|
|
239
|
-
(data as Record<string, unknown>).eventRef = node.eventRef;
|
|
240
|
-
}
|
|
241
|
-
if ('dataSchema' in node && node.dataSchema) {
|
|
242
|
-
data.dataSchema = node.dataSchema;
|
|
243
|
-
}
|
|
244
|
-
if ('boundary' in node && node.boundary) {
|
|
245
|
-
(data as Record<string, unknown>).boundary = node.boundary;
|
|
246
|
-
}
|
|
247
|
-
// Pass through workflow chips if present (for span convention nodes)
|
|
248
|
-
if ('workflowChips' in node && node.workflowChips) {
|
|
249
|
-
(data as Record<string, unknown>).workflowChips = node.workflowChips;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Add PV extensions if present (for standard canvas nodes only)
|
|
254
|
-
// OTEL nodes use top-level fields, not pv wrapper
|
|
255
|
-
if (pv && !isOtelType) {
|
|
278
|
+
// Add PV extensions if present
|
|
279
|
+
if (pv) {
|
|
256
280
|
if (pv.states) data.states = pv.states;
|
|
257
281
|
if (pv.sources) data.sources = pv.sources; // deprecated, use references
|
|
258
282
|
if (pv.references) data.references = pv.references;
|
|
@@ -260,17 +284,9 @@ export class CanvasConverter {
|
|
|
260
284
|
if (pv.dataSchema) data.dataSchema = pv.dataSchema;
|
|
261
285
|
}
|
|
262
286
|
|
|
263
|
-
// Determine React Flow node type from shape
|
|
264
|
-
let reactFlowType = 'default';
|
|
265
|
-
if (isOtelType && node.shape) {
|
|
266
|
-
reactFlowType = node.shape;
|
|
267
|
-
} else if (pv?.shape) {
|
|
268
|
-
reactFlowType = pv.shape;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
287
|
return {
|
|
272
288
|
id: node.id,
|
|
273
|
-
type:
|
|
289
|
+
type: pv?.shape || 'default',
|
|
274
290
|
position: { x: node.x, y: node.y },
|
|
275
291
|
data,
|
|
276
292
|
style: {
|
|
@@ -340,122 +356,12 @@ export class CanvasConverter {
|
|
|
340
356
|
// Convert nodes
|
|
341
357
|
if (canvas.nodes) {
|
|
342
358
|
for (const node of canvas.nodes) {
|
|
343
|
-
|
|
344
|
-
// Get name and description based on node type
|
|
345
|
-
// For text nodes: line 1 = name, rest = description
|
|
346
|
-
let nodeName: string;
|
|
347
|
-
let nodeDescription: string | undefined;
|
|
348
|
-
switch (node.type) {
|
|
349
|
-
case 'text': {
|
|
350
|
-
const lines = node.text?.split('\n') || [];
|
|
351
|
-
nodeName = lines[0]?.replace(/^#+ /, '').substring(0, 50) || 'Text';
|
|
352
|
-
// Description is everything after the first line, trimmed
|
|
353
|
-
nodeDescription = lines.slice(1).join('\n').trim() || undefined;
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
case 'file':
|
|
357
|
-
nodeName = node.file?.split('/').pop() || node.file || 'File';
|
|
358
|
-
break;
|
|
359
|
-
case 'link':
|
|
360
|
-
nodeName = node.url || 'Link';
|
|
361
|
-
break;
|
|
362
|
-
case 'group':
|
|
363
|
-
nodeName = node.label || 'Group';
|
|
364
|
-
break;
|
|
365
|
-
// OTEL node types use the label field directly
|
|
366
|
-
case 'otel-event':
|
|
367
|
-
case 'otel-span-convention':
|
|
368
|
-
case 'otel-scope':
|
|
369
|
-
case 'otel-resource':
|
|
370
|
-
case 'otel-boundary':
|
|
371
|
-
nodeName = node.label;
|
|
372
|
-
break;
|
|
373
|
-
default:
|
|
374
|
-
// For unknown custom types, use node id
|
|
375
|
-
nodeName = (node as { id: string }).id;
|
|
376
|
-
break;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// pv.name takes priority over parsed name from text
|
|
380
|
-
const finalName = pv?.name || nodeName;
|
|
381
|
-
// pv.description takes priority over parsed description from text
|
|
382
|
-
const finalDescription = pv?.description || nodeDescription;
|
|
383
|
-
|
|
384
|
-
// Check if this is an OTEL node type
|
|
385
|
-
const isOtelType = isOtelNode(node);
|
|
386
|
-
|
|
387
|
-
// Build data object, filtering out undefined values
|
|
388
|
-
const nodeData: Record<string, JsonValue> = {
|
|
389
|
-
description: finalDescription || '',
|
|
390
|
-
shape: isOtelType ? (node.shape || 'rectangle') : (pv?.shape || 'rectangle'),
|
|
391
|
-
color: isOtelType ? (node.fill || resolveCanvasColor(node.color) || '') : (pv?.fill || resolveCanvasColor(node.color) || ''),
|
|
392
|
-
width: node.width,
|
|
393
|
-
height: node.height,
|
|
394
|
-
sources: pv?.sources || [], // deprecated, use references
|
|
395
|
-
references: pv?.references || [],
|
|
396
|
-
canvasType: node.type,
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
// Add optional pv properties for standard canvas nodes only
|
|
400
|
-
// Note: pv.otel and pv.resourceMatch are removed - use otel-* node types instead
|
|
401
|
-
if (!isOtelType && pv) {
|
|
402
|
-
if (pv.icon) nodeData.icon = pv.icon;
|
|
403
|
-
if (pv.stroke) nodeData.stroke = pv.stroke;
|
|
404
|
-
if (pv.states) nodeData.states = pv.states as JsonValue;
|
|
405
|
-
if (pv.status) nodeData.status = pv.status;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Add type-specific data
|
|
409
|
-
if (node.type === 'text' && node.text) nodeData.text = node.text;
|
|
410
|
-
if (node.type === 'file' && node.file) nodeData.file = node.file;
|
|
411
|
-
if (node.type === 'link' && node.url) nodeData.url = node.url;
|
|
412
|
-
|
|
413
|
-
// Handle OTEL node types (new format with top-level fields)
|
|
359
|
+
// Handle OTEL nodes separately (they never use pv field)
|
|
414
360
|
if (isOtelNode(node)) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (node.shape) nodeData.shape = node.shape;
|
|
419
|
-
if ('description' in node && node.description) nodeData.description = node.description;
|
|
420
|
-
|
|
421
|
-
// Add OTEL-specific data from top-level fields
|
|
422
|
-
if ('otel' in node && node.otel) {
|
|
423
|
-
nodeData.otel = node.otel as JsonValue;
|
|
424
|
-
if (node.otel.status) nodeData.status = node.otel.status;
|
|
425
|
-
if (node.otel.references) nodeData.references = node.otel.references;
|
|
426
|
-
}
|
|
427
|
-
if ('event' in node && node.event) {
|
|
428
|
-
nodeData.event = node.event as unknown as JsonValue;
|
|
429
|
-
}
|
|
430
|
-
if ('eventRef' in node && node.eventRef) {
|
|
431
|
-
nodeData.eventRef = node.eventRef;
|
|
432
|
-
}
|
|
433
|
-
if ('dataSchema' in node && node.dataSchema) {
|
|
434
|
-
nodeData.dataSchema = node.dataSchema as JsonValue;
|
|
435
|
-
}
|
|
436
|
-
if ('boundary' in node && node.boundary) {
|
|
437
|
-
nodeData.boundary = node.boundary as unknown as JsonValue;
|
|
438
|
-
}
|
|
439
|
-
// Pass through workflow chips if present (for span convention nodes)
|
|
440
|
-
if ('workflowChips' in node && node.workflowChips) {
|
|
441
|
-
nodeData.workflowChips = node.workflowChips as unknown as JsonValue;
|
|
442
|
-
}
|
|
361
|
+
nodes.push(this.convertOtelNodeToGraph(node, now));
|
|
362
|
+
} else {
|
|
363
|
+
nodes.push(this.convertStandardNodeToGraph(node, now));
|
|
443
364
|
}
|
|
444
|
-
|
|
445
|
-
nodes.push({
|
|
446
|
-
id: node.id,
|
|
447
|
-
type: pv?.nodeType || node.type,
|
|
448
|
-
name: finalName,
|
|
449
|
-
data: nodeData,
|
|
450
|
-
position: { x: node.x, y: node.y },
|
|
451
|
-
// Persist node dimensions at top level for xyflow
|
|
452
|
-
width: node.width,
|
|
453
|
-
height: node.height,
|
|
454
|
-
// Don't set a default state - only show state labels when explicitly set via events
|
|
455
|
-
state: undefined,
|
|
456
|
-
createdAt: now,
|
|
457
|
-
updatedAt: now,
|
|
458
|
-
});
|
|
459
365
|
}
|
|
460
366
|
}
|
|
461
367
|
|
|
@@ -502,6 +408,136 @@ export class CanvasConverter {
|
|
|
502
408
|
return { nodes, edges };
|
|
503
409
|
}
|
|
504
410
|
|
|
411
|
+
/**
|
|
412
|
+
* Convert an OTEL node to NodeState format
|
|
413
|
+
* OTEL nodes use top-level fields and never use pv
|
|
414
|
+
*/
|
|
415
|
+
private static convertOtelNodeToGraph(node: OtelNode, now: number): NodeState {
|
|
416
|
+
const nodeData: Record<string, JsonValue> = {
|
|
417
|
+
description: ('description' in node && node.description) || '',
|
|
418
|
+
shape: node.shape || 'rectangle',
|
|
419
|
+
color: node.fill || resolveCanvasColor(node.color) || '',
|
|
420
|
+
width: node.width,
|
|
421
|
+
height: node.height,
|
|
422
|
+
sources: [], // not used for OTEL nodes
|
|
423
|
+
references: [],
|
|
424
|
+
canvasType: node.type,
|
|
425
|
+
nodeType: node.type,
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
if (node.icon) nodeData.icon = node.icon;
|
|
429
|
+
if (node.stroke) nodeData.stroke = node.stroke;
|
|
430
|
+
|
|
431
|
+
// Add OTEL-specific data from top-level fields
|
|
432
|
+
if ('otel' in node && node.otel) {
|
|
433
|
+
nodeData.otel = node.otel as JsonValue;
|
|
434
|
+
if (node.otel.status) nodeData.status = node.otel.status;
|
|
435
|
+
if (node.otel.references) nodeData.references = node.otel.references;
|
|
436
|
+
}
|
|
437
|
+
if ('event' in node && node.event) {
|
|
438
|
+
nodeData.event = node.event as unknown as JsonValue;
|
|
439
|
+
}
|
|
440
|
+
if ('eventRef' in node && node.eventRef) {
|
|
441
|
+
nodeData.eventRef = node.eventRef;
|
|
442
|
+
}
|
|
443
|
+
if ('dataSchema' in node && node.dataSchema) {
|
|
444
|
+
nodeData.dataSchema = node.dataSchema as JsonValue;
|
|
445
|
+
}
|
|
446
|
+
if ('boundary' in node && node.boundary) {
|
|
447
|
+
nodeData.boundary = node.boundary as unknown as JsonValue;
|
|
448
|
+
}
|
|
449
|
+
if ('workflowChips' in node && node.workflowChips) {
|
|
450
|
+
nodeData.workflowChips = node.workflowChips as unknown as JsonValue;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return {
|
|
454
|
+
id: node.id,
|
|
455
|
+
type: node.type,
|
|
456
|
+
name: node.label,
|
|
457
|
+
data: nodeData,
|
|
458
|
+
position: { x: node.x, y: node.y },
|
|
459
|
+
width: node.width,
|
|
460
|
+
height: node.height,
|
|
461
|
+
state: undefined,
|
|
462
|
+
createdAt: now,
|
|
463
|
+
updatedAt: now,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Convert a standard JSON Canvas node to NodeState format
|
|
469
|
+
* Standard nodes may use pv extensions
|
|
470
|
+
*/
|
|
471
|
+
private static convertStandardNodeToGraph(node: ExtendedCanvasTextNode | ExtendedCanvasFileNode | ExtendedCanvasLinkNode | ExtendedCanvasGroupNode, now: number): NodeState {
|
|
472
|
+
const pv = node.pv;
|
|
473
|
+
|
|
474
|
+
// Get name and description based on node type
|
|
475
|
+
let nodeName: string;
|
|
476
|
+
let nodeDescription: string | undefined;
|
|
477
|
+
|
|
478
|
+
switch (node.type) {
|
|
479
|
+
case 'text': {
|
|
480
|
+
const lines = node.text?.split('\n') || [];
|
|
481
|
+
nodeName = lines[0]?.replace(/^#+ /, '').substring(0, 50) || 'Text';
|
|
482
|
+
nodeDescription = lines.slice(1).join('\n').trim() || undefined;
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
case 'file':
|
|
486
|
+
nodeName = node.file?.split('/').pop() || node.file || 'File';
|
|
487
|
+
break;
|
|
488
|
+
case 'link':
|
|
489
|
+
nodeName = node.url || 'Link';
|
|
490
|
+
break;
|
|
491
|
+
case 'group':
|
|
492
|
+
nodeName = node.label || 'Group';
|
|
493
|
+
break;
|
|
494
|
+
default:
|
|
495
|
+
nodeName = (node as { id: string }).id;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// pv.name/description take priority over parsed values
|
|
500
|
+
const finalName = pv?.name || nodeName;
|
|
501
|
+
const finalDescription = pv?.description || nodeDescription;
|
|
502
|
+
|
|
503
|
+
const nodeData: Record<string, JsonValue> = {
|
|
504
|
+
description: finalDescription || '',
|
|
505
|
+
shape: pv?.shape || 'rectangle',
|
|
506
|
+
color: pv?.fill || resolveCanvasColor(node.color) || '',
|
|
507
|
+
width: node.width,
|
|
508
|
+
height: node.height,
|
|
509
|
+
sources: pv?.sources || [],
|
|
510
|
+
references: pv?.references || [],
|
|
511
|
+
canvasType: node.type,
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
// Add optional pv properties
|
|
515
|
+
if (pv) {
|
|
516
|
+
if (pv.icon) nodeData.icon = pv.icon;
|
|
517
|
+
if (pv.stroke) nodeData.stroke = pv.stroke;
|
|
518
|
+
if (pv.states) nodeData.states = pv.states as JsonValue;
|
|
519
|
+
if (pv.status) nodeData.status = pv.status;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Add type-specific data
|
|
523
|
+
if (node.type === 'text' && node.text) nodeData.text = node.text;
|
|
524
|
+
if (node.type === 'file' && node.file) nodeData.file = node.file;
|
|
525
|
+
if (node.type === 'link' && node.url) nodeData.url = node.url;
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
id: node.id,
|
|
529
|
+
type: pv?.nodeType || node.type,
|
|
530
|
+
name: finalName,
|
|
531
|
+
data: nodeData,
|
|
532
|
+
position: { x: node.x, y: node.y },
|
|
533
|
+
width: node.width,
|
|
534
|
+
height: node.height,
|
|
535
|
+
state: undefined,
|
|
536
|
+
createdAt: now,
|
|
537
|
+
updatedAt: now,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
505
541
|
/**
|
|
506
542
|
* Convert React Flow nodes/edges back to Extended Canvas format
|
|
507
543
|
*/
|
|
@@ -540,9 +576,10 @@ export class CanvasConverter {
|
|
|
540
576
|
canvasNode.color = node.data.color;
|
|
541
577
|
}
|
|
542
578
|
|
|
543
|
-
// Add PV extension if there's custom data
|
|
544
|
-
if (
|
|
545
|
-
|
|
579
|
+
// Add PV extension if there's custom data (only for standard canvas nodes)
|
|
580
|
+
if (!isOtelNode(canvasNode as ExtendedCanvasNode) &&
|
|
581
|
+
(node.data.nodeType || node.data.shape || node.data.sources?.length || node.data.references?.length)) {
|
|
582
|
+
(canvasNode as ExtendedCanvasTextNode).pv = {
|
|
546
583
|
nodeType: (node.data.nodeType as string) || node.id,
|
|
547
584
|
shape: node.data.shape as PVNodeShape | undefined,
|
|
548
585
|
icon: node.data.icon as string | undefined,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { ExtendedCanvas, ExtendedCanvasNode } from '../types/canvas';
|
|
11
|
+
import { isOtelSpanConventionNode, isStandardCanvasNode } from '../types/canvas';
|
|
11
12
|
import type { DerivedEdge } from './edge-derivation';
|
|
12
13
|
import { minimatch } from 'minimatch';
|
|
13
14
|
|
|
@@ -145,12 +146,20 @@ export function extractSpanNodes(canvas: ExtendedCanvas): SpanNodeInfo[] {
|
|
|
145
146
|
const spanNodes: SpanNodeInfo[] = [];
|
|
146
147
|
|
|
147
148
|
for (const node of canvas.nodes) {
|
|
148
|
-
|
|
149
|
-
if (spanPattern) {
|
|
149
|
+
// Check OTEL span convention nodes (top-level otel.spanPattern)
|
|
150
|
+
if (isOtelSpanConventionNode(node) && node.otel?.spanPattern) {
|
|
150
151
|
spanNodes.push({
|
|
151
152
|
nodeId: node.id,
|
|
152
|
-
spanPattern,
|
|
153
|
-
name:
|
|
153
|
+
spanPattern: node.otel.spanPattern,
|
|
154
|
+
name: node.label,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// Check standard nodes (pv.otel.spanPattern)
|
|
158
|
+
else if (isStandardCanvasNode(node) && node.pv?.otel?.spanPattern) {
|
|
159
|
+
spanNodes.push({
|
|
160
|
+
nodeId: node.id,
|
|
161
|
+
spanPattern: node.pv.otel.spanPattern,
|
|
162
|
+
name: node.pv.name,
|
|
154
163
|
});
|
|
155
164
|
}
|
|
156
165
|
}
|