@principal-ai/principal-view-core 0.6.3 → 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,298 @@
1
+ /**
2
+ * Template Expression Parser
3
+ *
4
+ * Parses and evaluates template expressions like:
5
+ * - Simple: "{config.nodeTypes}"
6
+ * - Conditional: "{result.violations.total > 0 ? '❌ FAILED' : '✅ PASSED'}"
7
+ * - Function calls: "{'━'.repeat(50)}"
8
+ */
9
+
10
+ import { getNestedValue } from './scenario-matcher';
11
+
12
+ /**
13
+ * Parse and evaluate a template string with embedded expressions
14
+ *
15
+ * Expressions are enclosed in curly braces: {expression}
16
+ *
17
+ * Supported syntax:
18
+ * - Property access: {config.nodeTypes}
19
+ * - Ternary: {count > 0 ? 'yes' : 'no'}
20
+ * - String methods: {'━'.repeat(50)}
21
+ * - Arithmetic: {duration.ms / 1000}
22
+ *
23
+ * @param template - Template string with {expressions}
24
+ * @param context - Data context for variable lookup
25
+ * @returns Evaluated string
26
+ */
27
+ export function parseTemplate(template: string, context: Record<string, unknown>): string {
28
+ // Find all {expression} patterns, handling nested braces
29
+ let result = '';
30
+ let i = 0;
31
+
32
+ while (i < template.length) {
33
+ const start = template.indexOf('{', i);
34
+ if (start === -1) {
35
+ // No more expressions
36
+ result += template.substring(i);
37
+ break;
38
+ }
39
+
40
+ // Add text before the expression
41
+ result += template.substring(i, start);
42
+
43
+ // Find matching closing brace
44
+ let depth = 1;
45
+ let end = start + 1;
46
+ while (end < template.length && depth > 0) {
47
+ if (template[end] === '{') depth++;
48
+ if (template[end] === '}') depth--;
49
+ end++;
50
+ }
51
+
52
+ if (depth === 0) {
53
+ // Found matching brace
54
+ const expression = template.substring(start + 1, end - 1);
55
+ try {
56
+ const value = evaluateExpression(expression.trim(), context);
57
+ // Check if evaluation failed (returned undefined when it shouldn't)
58
+ if (value === undefined && !expression.trim().includes('undefined')) {
59
+ // If the expression doesn't contain 'undefined' but returned undefined,
60
+ // it likely failed to parse/evaluate
61
+ const isValidExpression =
62
+ expression.trim() in context || // Simple variable
63
+ /^[\w.]+$/.test(expression.trim()) || // Property path
64
+ expression.includes('?') || // Ternary
65
+ /[+\-*/<>=!]/.test(expression); // Operations
66
+
67
+ if (!isValidExpression && expression.includes('(')) {
68
+ result += `{ERROR: Unable to evaluate '${expression}'}`;
69
+ } else {
70
+ result += formatValue(value);
71
+ }
72
+ } else {
73
+ result += formatValue(value);
74
+ }
75
+ } catch (error) {
76
+ // Return error placeholder instead of throwing
77
+ result += `{ERROR: ${error instanceof Error ? error.message : 'unknown'}}`;
78
+ }
79
+ i = end;
80
+ } else {
81
+ // Unmatched brace
82
+ result += '{';
83
+ i = start + 1;
84
+ }
85
+ }
86
+
87
+ return result;
88
+ }
89
+
90
+ /**
91
+ * Evaluate a single expression in the given context
92
+ *
93
+ * @param expression - Expression to evaluate
94
+ * @param context - Data context
95
+ * @returns Evaluated value
96
+ */
97
+ export function evaluateExpression(expression: string, context: Record<string, unknown>): unknown {
98
+ // Handle literals FIRST (before operators to avoid false matches)
99
+
100
+ // Handle string literals
101
+ if ((expression.startsWith("'") && expression.endsWith("'")) || (expression.startsWith('"') && expression.endsWith('"'))) {
102
+ return expression.slice(1, -1);
103
+ }
104
+
105
+ // Handle number literals (including negative numbers)
106
+ if (/^-?\d+(\.\d+)?$/.test(expression)) {
107
+ return Number(expression);
108
+ }
109
+
110
+ // Handle boolean literals
111
+ if (expression === 'true') return true;
112
+ if (expression === 'false') return false;
113
+ if (expression === 'null') return null;
114
+ if (expression === 'undefined') return undefined;
115
+
116
+ // Handle ternary operator: condition ? true : false
117
+ const ternaryMatch = expression.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/);
118
+ if (ternaryMatch) {
119
+ const [, condition, trueValue, falseValue] = ternaryMatch;
120
+ const conditionResult = evaluateExpression(condition, context);
121
+ const result = isTruthy(conditionResult)
122
+ ? evaluateExpression(trueValue, context)
123
+ : evaluateExpression(falseValue, context);
124
+
125
+ // If the result is a string containing {expressions}, recursively parse it
126
+ if (typeof result === 'string' && result.includes('{') && result.includes('}')) {
127
+ return parseTemplate(result, context);
128
+ }
129
+ return result;
130
+ }
131
+
132
+ // Handle comparison operators
133
+ if (expression.includes('>=')) {
134
+ const [left, right] = expression.split('>=').map((s) => s.trim());
135
+ return Number(evaluateExpression(left, context)) >= Number(evaluateExpression(right, context));
136
+ }
137
+ if (expression.includes('<=')) {
138
+ const [left, right] = expression.split('<=').map((s) => s.trim());
139
+ return Number(evaluateExpression(left, context)) <= Number(evaluateExpression(right, context));
140
+ }
141
+ if (expression.includes('>')) {
142
+ const [left, right] = expression.split('>').map((s) => s.trim());
143
+ return Number(evaluateExpression(left, context)) > Number(evaluateExpression(right, context));
144
+ }
145
+ if (expression.includes('<')) {
146
+ const [left, right] = expression.split('<').map((s) => s.trim());
147
+ return Number(evaluateExpression(left, context)) < Number(evaluateExpression(right, context));
148
+ }
149
+ if (expression.includes('===')) {
150
+ const [left, right] = expression.split('===').map((s) => s.trim());
151
+ return evaluateExpression(left, context) === evaluateExpression(right, context);
152
+ }
153
+ if (expression.includes('!==')) {
154
+ const [left, right] = expression.split('!==').map((s) => s.trim());
155
+ return evaluateExpression(left, context) !== evaluateExpression(right, context);
156
+ }
157
+ if (expression.includes('==')) {
158
+ const [left, right] = expression.split('==').map((s) => s.trim());
159
+ // eslint-disable-next-line eqeqeq
160
+ return evaluateExpression(left, context) == evaluateExpression(right, context);
161
+ }
162
+ if (expression.includes('!=')) {
163
+ const [left, right] = expression.split('!=').map((s) => s.trim());
164
+ // eslint-disable-next-line eqeqeq
165
+ return evaluateExpression(left, context) != evaluateExpression(right, context);
166
+ }
167
+
168
+ // Handle arithmetic operators (check for operators not at start to avoid matching negative numbers)
169
+ if (expression.includes('+') && !expression.includes("'") && !expression.includes('"')) {
170
+ const parts = expression.split('+');
171
+ if (parts.length > 1) {
172
+ const [left, right] = parts.map((s) => s.trim());
173
+ return Number(evaluateExpression(left, context)) + Number(evaluateExpression(right, context));
174
+ }
175
+ }
176
+ if (expression.includes('-') && !expression.includes("'") && !expression.includes('"')) {
177
+ // Only treat as subtraction if there's content before the minus (to avoid matching negative numbers)
178
+ const minusIndex = expression.indexOf('-');
179
+ if (minusIndex > 0) {
180
+ const beforeMinus = expression.substring(0, minusIndex).trim();
181
+ if (beforeMinus.length > 0) {
182
+ const [left, right] = expression.split('-').map((s) => s.trim());
183
+ return Number(evaluateExpression(left, context)) - Number(evaluateExpression(right, context));
184
+ }
185
+ }
186
+ }
187
+ if (expression.includes('*') && !expression.includes("'") && !expression.includes('"')) {
188
+ const [left, right] = expression.split('*').map((s) => s.trim());
189
+ return Number(evaluateExpression(left, context)) * Number(evaluateExpression(right, context));
190
+ }
191
+ if (expression.includes('/') && !expression.includes("'") && !expression.includes('"')) {
192
+ const [left, right] = expression.split('/').map((s) => s.trim());
193
+ return Number(evaluateExpression(left, context)) / Number(evaluateExpression(right, context));
194
+ }
195
+
196
+ // Handle method calls (limited to safe string methods)
197
+ if (expression.includes('.')) {
198
+ // Support both single and double quotes
199
+ const methodMatch = expression.match(/^(['"])(.+)\1\.(\w+)\(([^)]*)\)$/);
200
+ if (methodMatch) {
201
+ const [, , str, method, argsStr] = methodMatch;
202
+ return callStringMethod(str, method, argsStr);
203
+ }
204
+
205
+ // Property access with method call
206
+ const propMethodMatch = expression.match(/^([\w.[\]']+)\.(\w+)\(([^)]*)\)$/);
207
+ if (propMethodMatch) {
208
+ const [, propPath, method, argsStr] = propMethodMatch;
209
+ const value = evaluateExpression(propPath, context);
210
+ if (typeof value === 'string') {
211
+ return callStringMethod(value, method, argsStr);
212
+ }
213
+ }
214
+
215
+ // Simple property access
216
+ return getNestedValue(context, expression);
217
+ }
218
+
219
+ // Simple variable lookup
220
+ return context[expression];
221
+ }
222
+
223
+ /**
224
+ * Call a safe string method
225
+ *
226
+ * Only allows specific string methods to prevent code injection.
227
+ *
228
+ * @param str - String to operate on
229
+ * @param method - Method name
230
+ * @param argsStr - Arguments string
231
+ * @returns Result of method call
232
+ */
233
+ function callStringMethod(str: string, method: string, argsStr: string): unknown {
234
+ const args = argsStr.split(',').map((s) => s.trim());
235
+
236
+ switch (method) {
237
+ case 'repeat': {
238
+ const count = Number(args[0]);
239
+ return str.repeat(count);
240
+ }
241
+ case 'substring': {
242
+ const start = Number(args[0]);
243
+ const end = args[1] !== undefined ? Number(args[1]) : undefined;
244
+ return str.substring(start, end);
245
+ }
246
+ case 'slice': {
247
+ const start = Number(args[0]);
248
+ const end = args[1] !== undefined ? Number(args[1]) : undefined;
249
+ return str.slice(start, end);
250
+ }
251
+ case 'toUpperCase':
252
+ return str.toUpperCase();
253
+ case 'toLowerCase':
254
+ return str.toLowerCase();
255
+ case 'trim':
256
+ return str.trim();
257
+ case 'replace': {
258
+ const search = args[0].replace(/^['"]|['"]$/g, '');
259
+ const replace = args[1].replace(/^['"]|['"]$/g, '');
260
+ return str.replace(search, replace);
261
+ }
262
+ default:
263
+ throw new Error(`Method ${method} is not allowed in templates`);
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Check if a value is truthy
269
+ *
270
+ * @param value - Value to check
271
+ * @returns True if value is truthy
272
+ */
273
+ function isTruthy(value: unknown): boolean {
274
+ if (value === null || value === undefined) return false;
275
+ if (typeof value === 'boolean') return value;
276
+ if (typeof value === 'number') return value !== 0;
277
+ if (typeof value === 'string') return value.length > 0;
278
+ return true;
279
+ }
280
+
281
+ /**
282
+ * Format a value for output
283
+ *
284
+ * @param value - Value to format
285
+ * @returns Formatted string
286
+ */
287
+ function formatValue(value: unknown): string {
288
+ if (value === null || value === undefined) {
289
+ return '';
290
+ }
291
+ if (typeof value === 'boolean') {
292
+ return value ? 'true' : 'false';
293
+ }
294
+ if (typeof value === 'object') {
295
+ return JSON.stringify(value);
296
+ }
297
+ return String(value);
298
+ }