@principal-ai/principal-view-core 0.6.4 → 0.7.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 (92) hide show
  1. package/dist/ConfigurationLoader.js +2 -1
  2. package/dist/ConfigurationLoader.js.map +1 -1
  3. package/dist/ConfigurationValidator.js.map +1 -1
  4. package/dist/EventProcessor.js.map +1 -1
  5. package/dist/EventRecorderService.js.map +1 -1
  6. package/dist/LibraryLoader.js.map +1 -1
  7. package/dist/PathBasedEventProcessor.js.map +1 -1
  8. package/dist/SessionManager.js +1 -1
  9. package/dist/SessionManager.js.map +1 -1
  10. package/dist/ValidationEngine.js.map +1 -1
  11. package/dist/cli/codegen.js.map +1 -1
  12. package/dist/codegen/type-generator.js.map +1 -1
  13. package/dist/codegen/usage-example.js.map +1 -1
  14. package/dist/helpers/GraphInstrumentationHelper.js +2 -2
  15. package/dist/helpers/GraphInstrumentationHelper.js.map +1 -1
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +2 -2
  19. package/dist/index.js.map +1 -1
  20. package/dist/narrative/example.d.ts +11 -0
  21. package/dist/narrative/example.d.ts.map +1 -0
  22. package/dist/narrative/example.js +331 -0
  23. package/dist/narrative/example.js.map +1 -0
  24. package/dist/narrative/index.d.ts +12 -0
  25. package/dist/narrative/index.d.ts.map +1 -0
  26. package/dist/narrative/index.js +14 -0
  27. package/dist/narrative/index.js.map +1 -0
  28. package/dist/narrative/scenario-matcher.d.ts +87 -0
  29. package/dist/narrative/scenario-matcher.d.ts.map +1 -0
  30. package/dist/narrative/scenario-matcher.js +269 -0
  31. package/dist/narrative/scenario-matcher.js.map +1 -0
  32. package/dist/narrative/template-parser.d.ts +33 -0
  33. package/dist/narrative/template-parser.d.ts.map +1 -0
  34. package/dist/narrative/template-parser.js +288 -0
  35. package/dist/narrative/template-parser.js.map +1 -0
  36. package/dist/narrative/template-renderer.d.ts +18 -0
  37. package/dist/narrative/template-renderer.d.ts.map +1 -0
  38. package/dist/narrative/template-renderer.js +367 -0
  39. package/dist/narrative/template-renderer.js.map +1 -0
  40. package/dist/narrative/types.d.ts +268 -0
  41. package/dist/narrative/types.d.ts.map +1 -0
  42. package/dist/narrative/types.js +10 -0
  43. package/dist/narrative/types.js.map +1 -0
  44. package/dist/rules/config.js.map +1 -1
  45. package/dist/rules/engine.js.map +1 -1
  46. package/dist/rules/implementations/connection-type-references.js.map +1 -1
  47. package/dist/rules/implementations/dead-end-states.js.map +1 -1
  48. package/dist/rules/implementations/library-node-type-match.js.map +1 -1
  49. package/dist/rules/implementations/minimum-node-sources.js.map +1 -1
  50. package/dist/rules/implementations/no-unknown-fields.js.map +1 -1
  51. package/dist/rules/implementations/orphaned-edge-types.js.map +1 -1
  52. package/dist/rules/implementations/orphaned-node-types.js.map +1 -1
  53. package/dist/rules/implementations/required-metadata.js.map +1 -1
  54. package/dist/rules/implementations/state-transition-references.js.map +1 -1
  55. package/dist/rules/implementations/unreachable-states.js.map +1 -1
  56. package/dist/rules/implementations/valid-action-patterns.js.map +1 -1
  57. package/dist/rules/implementations/valid-color-format.js.map +1 -1
  58. package/dist/rules/implementations/valid-edge-types.js.map +1 -1
  59. package/dist/rules/implementations/valid-node-types.js.map +1 -1
  60. package/dist/rules/types.js.map +1 -1
  61. package/dist/telemetry/coverage.js.map +1 -1
  62. package/dist/telemetry/event-validator.js.map +1 -1
  63. package/dist/types/audit.js.map +1 -1
  64. package/dist/types/canvas.js +5 -5
  65. package/dist/types/canvas.js.map +1 -1
  66. package/dist/types/otel.js.map +1 -1
  67. package/dist/types/resource-match.js.map +1 -1
  68. package/dist/utils/CanvasConverter.js.map +1 -1
  69. package/dist/utils/GraphConverter.js.map +1 -1
  70. package/dist/utils/LibraryConverter.js.map +1 -1
  71. package/dist/utils/PathMatcher.js.map +1 -1
  72. package/dist/utils/TraceToCanvas.js +7 -7
  73. package/dist/utils/TraceToCanvas.js.map +1 -1
  74. package/dist/utils/YamlParser.js.map +1 -1
  75. package/package.json +15 -15
  76. package/src/index.ts +31 -13
  77. package/src/narrative/README.md +381 -0
  78. package/src/narrative/__tests__/scenario-matcher.test.ts +368 -0
  79. package/src/narrative/__tests__/template-parser.test.ts +235 -0
  80. package/src/narrative/__tests__/template-renderer.test.ts +377 -0
  81. package/src/narrative/example.ts +349 -0
  82. package/src/narrative/index.ts +35 -0
  83. package/src/narrative/scenario-matcher.ts +331 -0
  84. package/src/narrative/template-parser.ts +298 -0
  85. package/src/narrative/template-renderer.ts +423 -0
  86. package/src/narrative/types.ts +368 -0
  87. package/src/utils/GraphConverter.test.ts +0 -79
  88. package/dist/utils/ExecutionFileDiscovery.d.ts +0 -206
  89. package/dist/utils/ExecutionFileDiscovery.d.ts.map +0 -1
  90. package/dist/utils/ExecutionFileDiscovery.js +0 -340
  91. package/dist/utils/ExecutionFileDiscovery.js.map +0 -1
  92. package/src/utils/ExecutionFileDiscovery.ts +0 -522
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Tests for template renderer (end-to-end narrative generation)
3
+ */
4
+
5
+ import { renderNarrative } from '../template-renderer';
6
+ import type { NarrativeTemplate, OtelEvent } from '../types';
7
+
8
+ describe('renderNarrative', () => {
9
+ describe('span-tree mode', () => {
10
+ const template: NarrativeTemplate = {
11
+ version: '1.0.0',
12
+ canvas: 'test.otel.canvas',
13
+ name: 'Span Tree Test',
14
+ description: 'Test span tree rendering',
15
+ mode: 'span-tree',
16
+ scenarioSelection: 'first-match',
17
+ scenarios: [
18
+ {
19
+ id: 'default',
20
+ priority: 1,
21
+ description: 'Default scenario',
22
+ condition: { default: true },
23
+ template: {
24
+ introduction: '✅ Execution Trace',
25
+ span: '→ {span.name}',
26
+ children: 'recurse',
27
+ summary: '✅ Complete',
28
+ },
29
+ },
30
+ ],
31
+ };
32
+
33
+ it('should render simple span tree', () => {
34
+ const events: OtelEvent[] = [
35
+ {
36
+ name: 'parent.span',
37
+ timestamp: 0,
38
+ type: 'span',
39
+ spanId: 'span1',
40
+ traceId: 'trace1',
41
+ },
42
+ {
43
+ name: 'child.span',
44
+ timestamp: 10,
45
+ type: 'span',
46
+ spanId: 'span2',
47
+ parentSpanId: 'span1',
48
+ traceId: 'trace1',
49
+ },
50
+ ];
51
+
52
+ const result = renderNarrative(template, events);
53
+
54
+ expect(result.text).toContain('✅ Execution Trace');
55
+ expect(result.text).toContain('→ parent.span');
56
+ expect(result.text).toContain('→ child.span');
57
+ expect(result.text).toContain('✅ Complete');
58
+ expect(result.scenarioId).toBe('default');
59
+ });
60
+
61
+ it('should indent child spans correctly', () => {
62
+ const events: OtelEvent[] = [
63
+ { name: 'root', type: 'span', spanId: '1', traceId: 't1', timestamp: 0 },
64
+ { name: 'child1', type: 'span', spanId: '2', parentSpanId: '1', traceId: 't1', timestamp: 10 },
65
+ { name: 'grandchild', type: 'span', spanId: '3', parentSpanId: '2', traceId: 't1', timestamp: 20 },
66
+ ];
67
+
68
+ const result = renderNarrative(template, events);
69
+ const lines = result.text.split('\n');
70
+
71
+ // Check for proper indentation (2 spaces per level)
72
+ expect(lines).toContainEqual(expect.stringMatching(/^→ root$/));
73
+ expect(lines).toContainEqual(expect.stringMatching(/^ → child1$/));
74
+ expect(lines).toContainEqual(expect.stringMatching(/^ → grandchild$/));
75
+ });
76
+
77
+ it('should attach logs to spans when showLogsPerSpan is true', () => {
78
+ const templateWithLogs: NarrativeTemplate = {
79
+ ...template,
80
+ showLogsPerSpan: true,
81
+ scenarios: [
82
+ {
83
+ ...template.scenarios[0],
84
+ template: {
85
+ ...template.scenarios[0].template,
86
+ logs: {
87
+ info: ' 📝 {log.body}',
88
+ error: ' ❌ {log.body}',
89
+ },
90
+ },
91
+ },
92
+ ],
93
+ };
94
+
95
+ const events: OtelEvent[] = [
96
+ { name: 'test.span', type: 'span', spanId: 'span1', traceId: 't1', timestamp: 0 },
97
+ {
98
+ name: 'log.info',
99
+ type: 'log',
100
+ spanId: 'span1',
101
+ traceId: 't1',
102
+ timestamp: 5,
103
+ severityText: 'INFO',
104
+ severityNumber: 9,
105
+ body: 'Processing started',
106
+ },
107
+ {
108
+ name: 'log.error',
109
+ type: 'log',
110
+ spanId: 'span1',
111
+ traceId: 't1',
112
+ timestamp: 15,
113
+ severityText: 'ERROR',
114
+ severityNumber: 17,
115
+ body: 'Something failed',
116
+ },
117
+ ];
118
+
119
+ const result = renderNarrative(templateWithLogs, events);
120
+
121
+ expect(result.text).toContain('→ test.span');
122
+ expect(result.text).toContain('📝 Processing started');
123
+ expect(result.text).toContain('❌ Something failed');
124
+ });
125
+ });
126
+
127
+ describe('timeline mode', () => {
128
+ const template: NarrativeTemplate = {
129
+ version: '1.0.0',
130
+ canvas: 'test.otel.canvas',
131
+ name: 'Timeline Test',
132
+ description: 'Test timeline rendering',
133
+ mode: 'timeline',
134
+ scenarioSelection: 'first-match',
135
+ scenarios: [
136
+ {
137
+ id: 'default',
138
+ priority: 1,
139
+ description: 'Default scenario',
140
+ condition: { default: true },
141
+ template: {
142
+ introduction: 'Timeline',
143
+ events: {
144
+ 'test.started': '🔵 Started',
145
+ 'test.complete': '🔵 Completed',
146
+ 'log.info': '📝 {log.body}',
147
+ },
148
+ summary: 'Done',
149
+ },
150
+ },
151
+ ],
152
+ formatting: {
153
+ showTimestamps: true,
154
+ timestampFormat: 'HH:mm:ss.SSS',
155
+ },
156
+ };
157
+
158
+ it('should render events in chronological order', () => {
159
+ const events: OtelEvent[] = [
160
+ { name: 'test.started', timestamp: '2025-01-15T10:00:00.000Z', type: 'span' },
161
+ { name: 'log.info', timestamp: '2025-01-15T10:00:00.050Z', type: 'log', body: 'Processing', severityText: 'INFO' },
162
+ { name: 'test.complete', timestamp: '2025-01-15T10:00:00.100Z', type: 'span' },
163
+ ];
164
+
165
+ const result = renderNarrative(template, events);
166
+
167
+ expect(result.text).toContain('[10:00:00.000] 🔵 Started');
168
+ expect(result.text).toContain('[10:00:00.050] 📝 Processing');
169
+ expect(result.text).toContain('[10:00:00.100] 🔵 Completed');
170
+
171
+ // Check order
172
+ const startedIndex = result.text.indexOf('🔵 Started');
173
+ const processingIndex = result.text.indexOf('📝 Processing');
174
+ const completedIndex = result.text.indexOf('🔵 Completed');
175
+ expect(startedIndex).toBeLessThan(processingIndex);
176
+ expect(processingIndex).toBeLessThan(completedIndex);
177
+ });
178
+ });
179
+
180
+ describe('summary-only mode', () => {
181
+ const template: NarrativeTemplate = {
182
+ version: '1.0.0',
183
+ canvas: 'test.otel.canvas',
184
+ name: 'Summary Test',
185
+ description: 'Test summary-only rendering',
186
+ mode: 'summary-only',
187
+ scenarioSelection: 'first-match',
188
+ scenarios: [
189
+ {
190
+ id: 'with-violations',
191
+ priority: 1,
192
+ description: 'Has violations',
193
+ condition: {
194
+ requires: ['test.complete'],
195
+ assertions: { 'result.violations.total': { $gt: 0 } },
196
+ },
197
+ template: {
198
+ introduction: '⚠️ Test Results',
199
+ summary: 'Found {result.violations.total} violations',
200
+ },
201
+ },
202
+ {
203
+ id: 'no-violations',
204
+ priority: 2,
205
+ description: 'No violations',
206
+ condition: {
207
+ requires: ['test.complete'],
208
+ assertions: { 'result.violations.total': { $eq: 0 } },
209
+ },
210
+ template: {
211
+ introduction: '✅ Test Results',
212
+ summary: 'All tests passed',
213
+ },
214
+ },
215
+ {
216
+ id: 'fallback',
217
+ priority: 99,
218
+ description: 'Fallback',
219
+ condition: { default: true },
220
+ template: {
221
+ summary: 'Unknown result',
222
+ },
223
+ },
224
+ ],
225
+ };
226
+
227
+ it('should only show introduction and summary', () => {
228
+ const events: OtelEvent[] = [
229
+ {
230
+ name: 'test.complete',
231
+ timestamp: 100,
232
+ type: 'span',
233
+ attributes: { 'result.violations.total': 5 },
234
+ },
235
+ ];
236
+
237
+ const result = renderNarrative(template, events);
238
+
239
+ expect(result.text).toContain('⚠️ Test Results');
240
+ expect(result.text).toContain('Found 5 violations');
241
+ expect(result.scenarioId).toBe('with-violations');
242
+ });
243
+ });
244
+
245
+ describe('scenario selection', () => {
246
+ const template: NarrativeTemplate = {
247
+ version: '1.0.0',
248
+ canvas: 'test.otel.canvas',
249
+ name: 'Scenario Test',
250
+ description: 'Test scenario selection',
251
+ mode: 'summary-only',
252
+ scenarioSelection: 'first-match',
253
+ scenarios: [
254
+ {
255
+ id: 'error',
256
+ priority: 1,
257
+ description: 'Error occurred',
258
+ condition: { requires: ['*.error'] },
259
+ template: { summary: '❌ Error' },
260
+ },
261
+ {
262
+ id: 'warnings',
263
+ priority: 2,
264
+ description: 'Has warnings',
265
+ condition: {
266
+ requires: ['test.complete'],
267
+ assertions: { 'result.warnings': { $gt: 0 } },
268
+ },
269
+ template: { summary: '⚠️ Warnings' },
270
+ },
271
+ {
272
+ id: 'success',
273
+ priority: 3,
274
+ description: 'Success',
275
+ condition: { requires: ['test.complete'] },
276
+ template: { summary: '✅ Success' },
277
+ },
278
+ {
279
+ id: 'fallback',
280
+ priority: 99,
281
+ description: 'Fallback',
282
+ condition: { default: true },
283
+ template: { summary: 'Unknown' },
284
+ },
285
+ ],
286
+ };
287
+
288
+ it('should select error scenario (highest priority)', () => {
289
+ const events: OtelEvent[] = [
290
+ { name: 'test.error', timestamp: 50, type: 'span' },
291
+ { name: 'test.complete', timestamp: 100, type: 'span', attributes: { 'result.warnings': 2 } },
292
+ ];
293
+
294
+ const result = renderNarrative(template, events);
295
+ expect(result.scenarioId).toBe('error');
296
+ expect(result.text).toContain('❌ Error');
297
+ });
298
+
299
+ it('should select warnings scenario', () => {
300
+ const events: OtelEvent[] = [
301
+ { name: 'test.complete', timestamp: 100, type: 'span', attributes: { 'result.warnings': 3 } },
302
+ ];
303
+
304
+ const result = renderNarrative(template, events);
305
+ expect(result.scenarioId).toBe('warnings');
306
+ expect(result.text).toContain('⚠️ Warnings');
307
+ });
308
+
309
+ it('should select success scenario', () => {
310
+ const events: OtelEvent[] = [
311
+ { name: 'test.complete', timestamp: 100, type: 'span', attributes: { 'result.warnings': 0 } },
312
+ ];
313
+
314
+ const result = renderNarrative(template, events);
315
+ expect(result.scenarioId).toBe('success');
316
+ expect(result.text).toContain('✅ Success');
317
+ });
318
+
319
+ it('should select fallback when no other matches', () => {
320
+ const events: OtelEvent[] = [{ name: 'unknown.event', timestamp: 0, type: 'span' }];
321
+
322
+ const result = renderNarrative(template, events);
323
+ expect(result.scenarioId).toBe('fallback');
324
+ expect(result.text).toContain('Unknown');
325
+ });
326
+ });
327
+
328
+ describe('metadata', () => {
329
+ const template: NarrativeTemplate = {
330
+ version: '1.0.0',
331
+ canvas: 'test.otel.canvas',
332
+ name: 'Metadata Test',
333
+ description: 'Test metadata generation',
334
+ mode: 'summary-only',
335
+ scenarioSelection: 'first-match',
336
+ scenarios: [
337
+ {
338
+ id: 'default',
339
+ priority: 1,
340
+ description: 'Default',
341
+ condition: { default: true },
342
+ template: { summary: 'Done' },
343
+ },
344
+ ],
345
+ };
346
+
347
+ it('should include event counts', () => {
348
+ const events: OtelEvent[] = [
349
+ { name: 'span1', timestamp: 0, type: 'span' },
350
+ { name: 'span2', timestamp: 10, type: 'span' },
351
+ { name: 'log1', timestamp: 5, type: 'log', body: 'test' },
352
+ { name: 'log2', timestamp: 15, type: 'log', body: 'test' },
353
+ { name: 'log3', timestamp: 20, type: 'log', body: 'test' },
354
+ ];
355
+
356
+ const result = renderNarrative(template, events);
357
+
358
+ expect(result.metadata.eventCount).toBe(5);
359
+ expect(result.metadata.spanCount).toBe(2);
360
+ expect(result.metadata.logCount).toBe(3);
361
+ });
362
+
363
+ it('should include time range', () => {
364
+ const events: OtelEvent[] = [
365
+ { name: 'event1', timestamp: 1000, type: 'span' },
366
+ { name: 'event2', timestamp: 2000, type: 'span' },
367
+ { name: 'event3', timestamp: 1500, type: 'log', body: 'test' },
368
+ ];
369
+
370
+ const result = renderNarrative(template, events);
371
+
372
+ expect(result.metadata.timeRange).toBeDefined();
373
+ expect(result.metadata.timeRange?.start).toBe(1000);
374
+ expect(result.metadata.timeRange?.end).toBe(2000);
375
+ });
376
+ });
377
+ });