@principal-ai/principal-view-core 0.5.16 → 0.6.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.
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Execution File Discovery
3
+ *
4
+ * Discovers execution artifacts and canvas files in a repository with monorepo awareness.
5
+ * Uses codebase-composition to intelligently detect package boundaries and workspace patterns.
6
+ */
7
+ import { PackageLayerModule } from '@principal-ai/codebase-composition';
8
+ /**
9
+ * Execution File Discovery Engine
10
+ *
11
+ * Discovers execution artifacts and canvas files using monorepo-aware logic.
12
+ */
13
+ export class ExecutionFileDiscovery {
14
+ constructor(options = {}) {
15
+ this.packageLayerModule = new PackageLayerModule();
16
+ this.options = {
17
+ includePackagePrincipalViews: options.includePackagePrincipalViews ?? true,
18
+ executionFolders: options.executionFolders ?? ['__executions__'],
19
+ canvasExtensions: options.canvasExtensions ?? ['.otel.canvas'],
20
+ };
21
+ }
22
+ /**
23
+ * Find all execution artifact files in the repository
24
+ */
25
+ async findExecutionFiles(files) {
26
+ const executionFiles = [];
27
+ // Detect packages in the repository
28
+ const packages = await this.detectPackages(files);
29
+ // Search for execution files in each package
30
+ for (const pkg of packages) {
31
+ const pkgExecutions = this.findExecutionsInPackage(pkg, files);
32
+ executionFiles.push(...pkgExecutions);
33
+ }
34
+ // Search in root-level locations
35
+ const rootExecutions = this.findExecutionsInRoot(files);
36
+ executionFiles.push(...rootExecutions);
37
+ // Sort by package name, then by basename
38
+ return this.sortExecutionFiles(executionFiles);
39
+ }
40
+ /**
41
+ * Find all canvas files in the repository
42
+ */
43
+ async findCanvasFiles(files) {
44
+ const canvasFiles = [];
45
+ // Detect packages in the repository
46
+ const packages = await this.detectPackages(files);
47
+ // Search for canvas files in root .principal-views/
48
+ const rootCanvases = this.findCanvasesInDirectory('.principal-views', files);
49
+ canvasFiles.push(...rootCanvases);
50
+ // Search for canvas files in package-level .principal-views/ if enabled
51
+ if (this.options.includePackagePrincipalViews) {
52
+ for (const pkg of packages) {
53
+ const pkgCanvases = this.findCanvasesInDirectory(`${pkg.packageData.path}/.principal-views`, files, this.createPackageContext(pkg));
54
+ canvasFiles.push(...pkgCanvases);
55
+ }
56
+ }
57
+ // Sort by name
58
+ return canvasFiles.sort((a, b) => a.name.localeCompare(b.name));
59
+ }
60
+ /**
61
+ * Find execution artifact for a given canvas
62
+ * Now package-aware - can disambiguate between packages
63
+ */
64
+ findExecutionForCanvas(canvas, executionFiles) {
65
+ // First, try exact package match if canvas has package context
66
+ if (canvas.packageContext) {
67
+ const exactMatch = executionFiles.find(exec => exec.canvasBasename === canvas.basename &&
68
+ exec.packageContext?.name === canvas.packageContext?.name);
69
+ if (exactMatch)
70
+ return exactMatch;
71
+ }
72
+ // Fallback to basename-only match (for root-level canvases)
73
+ return executionFiles.find(exec => exec.canvasBasename === canvas.basename) || null;
74
+ }
75
+ /**
76
+ * Find canvas file for a given execution
77
+ */
78
+ findCanvasForExecution(execution, canvasFiles) {
79
+ // First, try exact package match if execution has package context
80
+ if (execution.packageContext) {
81
+ const exactMatch = canvasFiles.find(canvas => canvas.basename === execution.canvasBasename &&
82
+ canvas.packageContext?.name === execution.packageContext?.name);
83
+ if (exactMatch)
84
+ return exactMatch;
85
+ }
86
+ // Fallback to basename-only match (for root-level executions)
87
+ return canvasFiles.find(canvas => canvas.basename === execution.canvasBasename) || null;
88
+ }
89
+ /**
90
+ * Parse execution artifact JSON
91
+ */
92
+ static parseExecutionArtifact(content) {
93
+ try {
94
+ const parsed = JSON.parse(content);
95
+ return parsed;
96
+ }
97
+ catch (error) {
98
+ throw new Error(`Failed to parse execution artifact JSON: ${error.message}`);
99
+ }
100
+ }
101
+ /**
102
+ * Get spans from an artifact
103
+ */
104
+ static getSpans(artifact) {
105
+ return artifact.spans || [];
106
+ }
107
+ /**
108
+ * Extract metadata from an execution artifact
109
+ */
110
+ static getExecutionMetadata(artifact) {
111
+ const spans = ExecutionFileDiscovery.getSpans(artifact);
112
+ const spanCount = spans.length;
113
+ const eventCount = spans.reduce((total, span) => {
114
+ return total + (span.events?.length || 0);
115
+ }, 0);
116
+ const metadata = artifact.metadata;
117
+ let status = 'success';
118
+ if (metadata?.status) {
119
+ status = metadata.status;
120
+ }
121
+ else if (spans.length > 0) {
122
+ const hasError = spans.some(s => s.status === 'ERROR' || s.status === 'error' || s.status === 'FAILED');
123
+ status = hasError ? 'error' : 'OK';
124
+ }
125
+ return {
126
+ name: metadata?.canvasName || 'Untitled Execution',
127
+ canvasName: metadata?.canvasName,
128
+ exportedAt: metadata?.exportedAt,
129
+ source: metadata?.source,
130
+ framework: metadata?.framework,
131
+ status,
132
+ spanCount,
133
+ eventCount,
134
+ };
135
+ }
136
+ // Private helper methods
137
+ /**
138
+ * Detect packages using codebase-composition
139
+ */
140
+ async detectPackages(files) {
141
+ try {
142
+ // Convert files to the format expected by codebase-composition
143
+ const fileTree = this.convertToFileTree(files);
144
+ const packages = await this.packageLayerModule.discoverPackages(fileTree);
145
+ return packages || [];
146
+ }
147
+ catch (error) {
148
+ // If package detection fails, continue with fallback patterns
149
+ console.warn('Package detection failed, using fallback patterns:', error);
150
+ return [];
151
+ }
152
+ }
153
+ /**
154
+ * Convert file entries to file tree format for codebase-composition
155
+ */
156
+ convertToFileTree(files) {
157
+ // This is a simplified conversion - codebase-composition expects a specific format
158
+ // In practice, the caller should provide a proper FileTree from repository-abstraction
159
+ return {
160
+ files: files.map(f => ({
161
+ path: f.relativePath || f.path || '',
162
+ name: f.name || (f.relativePath || f.path || '').split('/').pop() || '',
163
+ })),
164
+ };
165
+ }
166
+ /**
167
+ * Find executions in a specific package
168
+ */
169
+ findExecutionsInPackage(pkg, files) {
170
+ const executions = [];
171
+ const pkgPath = pkg.packageData.path || '';
172
+ const packageContext = this.createPackageContext(pkg);
173
+ for (const execFolder of this.options.executionFolders) {
174
+ const execPath = pkgPath ? `${pkgPath}/${execFolder}` : execFolder;
175
+ for (const file of files) {
176
+ const filePath = file.relativePath || file.path || '';
177
+ const fileName = file.name || filePath.split('/').pop() || '';
178
+ if (filePath.startsWith(execPath + '/') && this.isExecutionFile(fileName)) {
179
+ const basename = this.extractExecutionBasename(fileName);
180
+ executions.push({
181
+ id: this.generateExecutionId(basename, packageContext),
182
+ name: this.formatDisplayName(basename),
183
+ path: filePath,
184
+ canvasBasename: basename,
185
+ packageContext,
186
+ });
187
+ }
188
+ }
189
+ }
190
+ return executions;
191
+ }
192
+ /**
193
+ * Find executions in root-level locations
194
+ */
195
+ findExecutionsInRoot(files) {
196
+ const executions = [];
197
+ // Root __executions__/
198
+ for (const execFolder of this.options.executionFolders) {
199
+ for (const file of files) {
200
+ const filePath = file.relativePath || file.path || '';
201
+ const fileName = file.name || filePath.split('/').pop() || '';
202
+ if (filePath.startsWith(`${execFolder}/`) && this.isExecutionFile(fileName)) {
203
+ const basename = this.extractExecutionBasename(fileName);
204
+ executions.push({
205
+ id: `root-${basename}`,
206
+ name: this.formatDisplayName(basename),
207
+ path: filePath,
208
+ canvasBasename: basename,
209
+ });
210
+ }
211
+ }
212
+ }
213
+ // .principal-views/__executions__/
214
+ const pvExecPath = '.principal-views/__executions__';
215
+ for (const file of files) {
216
+ const filePath = file.relativePath || file.path || '';
217
+ const fileName = file.name || filePath.split('/').pop() || '';
218
+ if (filePath.startsWith(pvExecPath + '/') && this.isExecutionFile(fileName)) {
219
+ const basename = this.extractExecutionBasename(fileName);
220
+ executions.push({
221
+ id: `pv-${basename}`,
222
+ name: this.formatDisplayName(basename),
223
+ path: filePath,
224
+ canvasBasename: basename,
225
+ });
226
+ }
227
+ }
228
+ return executions;
229
+ }
230
+ /**
231
+ * Find canvas files in a specific directory
232
+ */
233
+ findCanvasesInDirectory(directory, files, packageContext) {
234
+ const canvases = [];
235
+ for (const file of files) {
236
+ const filePath = file.relativePath || file.path || '';
237
+ const fileName = file.name || filePath.split('/').pop() || '';
238
+ if (filePath.startsWith(directory + '/') && this.isCanvasFile(fileName)) {
239
+ const basename = this.extractCanvasBasename(fileName);
240
+ canvases.push({
241
+ id: this.generateCanvasId(basename, packageContext),
242
+ name: this.formatDisplayName(basename),
243
+ path: filePath,
244
+ basename,
245
+ packageContext,
246
+ });
247
+ }
248
+ }
249
+ return canvases;
250
+ }
251
+ /**
252
+ * Create package context from PackageLayer
253
+ */
254
+ createPackageContext(pkg) {
255
+ return {
256
+ name: pkg.packageData.name || 'unknown',
257
+ packagePath: pkg.packageData.path || '',
258
+ packageType: pkg.type,
259
+ };
260
+ }
261
+ /**
262
+ * Check if filename is an execution file
263
+ */
264
+ isExecutionFile(filename) {
265
+ return /\.(spans|execution|events)\.json$/.test(filename);
266
+ }
267
+ /**
268
+ * Check if filename is a canvas file
269
+ */
270
+ isCanvasFile(filename) {
271
+ return this.options.canvasExtensions.some(ext => filename.endsWith(ext));
272
+ }
273
+ /**
274
+ * Extract basename from execution filename
275
+ */
276
+ extractExecutionBasename(filename) {
277
+ return filename.replace(/\.(spans|execution|events)\.json$/, '');
278
+ }
279
+ /**
280
+ * Extract basename from canvas filename
281
+ */
282
+ extractCanvasBasename(filename) {
283
+ for (const ext of this.options.canvasExtensions) {
284
+ if (filename.endsWith(ext)) {
285
+ return filename.slice(0, -ext.length);
286
+ }
287
+ }
288
+ return filename;
289
+ }
290
+ /**
291
+ * Format display name from basename (kebab-case to Title Case)
292
+ */
293
+ formatDisplayName(basename) {
294
+ return basename
295
+ .split('-')
296
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
297
+ .join(' ');
298
+ }
299
+ /**
300
+ * Generate unique ID for execution file
301
+ */
302
+ generateExecutionId(basename, packageContext) {
303
+ if (packageContext) {
304
+ return `${packageContext.name}-${basename}`;
305
+ }
306
+ return basename;
307
+ }
308
+ /**
309
+ * Generate unique ID for canvas file
310
+ */
311
+ generateCanvasId(basename, packageContext) {
312
+ if (packageContext) {
313
+ return `${packageContext.name}-${basename}`;
314
+ }
315
+ return basename;
316
+ }
317
+ /**
318
+ * Sort execution files by package name, then by basename
319
+ */
320
+ sortExecutionFiles(files) {
321
+ return files.sort((a, b) => {
322
+ // Sort by package name first
323
+ if (a.packageContext && b.packageContext) {
324
+ const pkgCompare = a.packageContext.name.localeCompare(b.packageContext.name);
325
+ if (pkgCompare !== 0)
326
+ return pkgCompare;
327
+ }
328
+ else if (a.packageContext) {
329
+ return -1;
330
+ }
331
+ else if (b.packageContext) {
332
+ return 1;
333
+ }
334
+ // Then by basename
335
+ return a.canvasBasename.localeCompare(b.canvasBasename);
336
+ });
337
+ }
338
+ }
339
+ //# sourceMappingURL=ExecutionFileDiscovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExecutionFileDiscovery.js","sourceRoot":"","sources":["../../src/utils/ExecutionFileDiscovery.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/principal-view-core",
3
- "version": "0.5.16",
3
+ "version": "0.6.0",
4
4
  "description": "Core logic and types for graph-based principal view framework",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -28,6 +28,7 @@
28
28
  "zod": "^3.22.0",
29
29
  "date-fns": "^3.0.0",
30
30
  "@principal-ai/repository-abstraction": "^0.2.5",
31
+ "@principal-ai/codebase-composition": "^0.2.31",
31
32
  "js-yaml": "^4.1.0"
32
33
  },
33
34
  "devDependencies": {
package/src/index.ts CHANGED
@@ -29,6 +29,19 @@ export type { LogEntry } from './PathBasedEventProcessor';
29
29
  export { PathMatcher } from './utils/PathMatcher';
30
30
  export { GraphConverter } from './utils/GraphConverter';
31
31
 
32
+ // Export execution file discovery
33
+ export { ExecutionFileDiscovery } from './utils/ExecutionFileDiscovery';
34
+ export type {
35
+ ExecutionFile,
36
+ CanvasFile,
37
+ PackageContext,
38
+ ExecutionMetadata,
39
+ ExecutionSpan,
40
+ ExecutionArtifact,
41
+ FileTreeEntry,
42
+ DiscoveryOptions,
43
+ } from './utils/ExecutionFileDiscovery';
44
+
32
45
  // Export Canvas types and converter
33
46
  export * from './types/canvas';
34
47
  export { CanvasConverter } from './utils/CanvasConverter';
@@ -38,6 +51,10 @@ export type { ReactFlowNode, ReactFlowEdge } from './utils/CanvasConverter';
38
51
  export { EventValidator, createValidatedEmitter, EventValidationError } from './telemetry/event-validator';
39
52
  export type { ValidationResult } from './telemetry/event-validator';
40
53
 
54
+ // Export telemetry coverage analysis
55
+ export { analyzeCoverage } from './telemetry/coverage';
56
+ export type { CoverageMetrics, NodeCoverage, CanvasNode as CoverageCanvasNode } from './telemetry/coverage';
57
+
41
58
  // Export code generation
42
59
  export { generateTypes, TypeScriptGenerator, generatorRegistry } from './codegen/type-generator';
43
60
  export type { CodegenOptions, CodegenResult, CodeGenerator } from './codegen/type-generator';
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Telemetry Coverage Analysis
3
+ *
4
+ * Measures observability coverage by analyzing which canvas nodes have
5
+ * OpenTelemetry instrumentation in their source files.
6
+ */
7
+
8
+ import { readFile, access } from 'fs/promises';
9
+ import { resolve } from 'path';
10
+ import { glob } from 'glob';
11
+
12
+ export interface CanvasNode {
13
+ id: string;
14
+ text?: string;
15
+ anchors?: Array<{ path?: string }>;
16
+ [key: string]: any;
17
+ }
18
+
19
+ export interface Canvas {
20
+ nodes?: CanvasNode[];
21
+ [key: string]: any;
22
+ }
23
+
24
+ export interface NodeCoverage {
25
+ nodeId: string;
26
+ filePaths: string[];
27
+ hasInstrumentation: boolean;
28
+ instrumentedFiles: string[];
29
+ missingFiles: string[];
30
+ }
31
+
32
+ export interface CoverageMetrics {
33
+ totalNodes: number;
34
+ nodesWithFiles: number;
35
+ nodesWithInstrumentation: number;
36
+ coveragePercentage: number;
37
+ nodeCoverage: NodeCoverage[];
38
+ canvasFiles: string[];
39
+ }
40
+
41
+ /**
42
+ * Extract file paths from a canvas node anchors (REQUIRED)
43
+ */
44
+ function extractFilePaths(node: CanvasNode): string[] {
45
+ const paths: string[] = [];
46
+
47
+ if (node.anchors) {
48
+ for (const anchor of node.anchors) {
49
+ if (anchor.path) {
50
+ paths.push(anchor.path);
51
+ }
52
+ }
53
+ }
54
+
55
+ return paths;
56
+ }
57
+
58
+ /**
59
+ * Check if a file has OpenTelemetry instrumentation
60
+ */
61
+ async function hasInstrumentation(filePath: string): Promise<boolean> {
62
+ try {
63
+ const content = await readFile(filePath, 'utf-8');
64
+
65
+ const hasOtelImport = content.includes('@opentelemetry/api');
66
+ const hasTracer = /getTracer|startSpan|addEvent/.test(content);
67
+ const hasTestOtel = /['"]\.\.?\/.*test\/otel-setup['"]/.test(content);
68
+
69
+ return hasOtelImport || hasTracer || hasTestOtel;
70
+ } catch {
71
+ return false;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Check if file exists
77
+ */
78
+ async function fileExists(filePath: string): Promise<boolean> {
79
+ try {
80
+ await access(filePath);
81
+ return true;
82
+ } catch {
83
+ return false;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Analyze coverage for a single canvas node
89
+ */
90
+ async function analyzeNodeCoverage(
91
+ node: CanvasNode,
92
+ rootDir: string
93
+ ): Promise<NodeCoverage> {
94
+ const filePaths = extractFilePaths(node);
95
+ const instrumentedFiles: string[] = [];
96
+ const missingFiles: string[] = [];
97
+
98
+ for (const path of filePaths) {
99
+ const fullPath = resolve(rootDir, path);
100
+ const exists = await fileExists(fullPath);
101
+
102
+ if (!exists) {
103
+ missingFiles.push(path);
104
+ continue;
105
+ }
106
+
107
+ const instrumented = await hasInstrumentation(fullPath);
108
+ if (instrumented) {
109
+ instrumentedFiles.push(path);
110
+ }
111
+ }
112
+
113
+ return {
114
+ nodeId: node.id,
115
+ filePaths,
116
+ hasInstrumentation: instrumentedFiles.length > 0,
117
+ instrumentedFiles,
118
+ missingFiles
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Generate telemetry coverage report from canvas files
124
+ */
125
+ export async function analyzeCoverage(rootDir: string): Promise<CoverageMetrics> {
126
+ const canvasFiles = await glob('**/*.otel.canvas', {
127
+ cwd: rootDir,
128
+ absolute: true,
129
+ dot: true,
130
+ ignore: ['**/node_modules/**']
131
+ });
132
+
133
+ const allNodeCoverage: NodeCoverage[] = [];
134
+
135
+ for (const canvasFile of canvasFiles) {
136
+ const content = await readFile(canvasFile, 'utf-8');
137
+ const canvas: Canvas = JSON.parse(content);
138
+
139
+ if (!canvas.nodes || canvas.nodes.length === 0) {
140
+ continue;
141
+ }
142
+
143
+ for (const node of canvas.nodes) {
144
+ const coverage = await analyzeNodeCoverage(node, rootDir);
145
+ allNodeCoverage.push(coverage);
146
+ }
147
+ }
148
+
149
+ const nodesWithFiles = allNodeCoverage.filter(n => n.filePaths.length > 0);
150
+ const nodesWithInstrumentation = allNodeCoverage.filter(n => n.hasInstrumentation);
151
+
152
+ return {
153
+ totalNodes: allNodeCoverage.length,
154
+ nodesWithFiles: nodesWithFiles.length,
155
+ nodesWithInstrumentation: nodesWithInstrumentation.length,
156
+ coveragePercentage: nodesWithFiles.length > 0
157
+ ? (nodesWithInstrumentation.length / nodesWithFiles.length) * 100
158
+ : 0,
159
+ nodeCoverage: allNodeCoverage,
160
+ canvasFiles: canvasFiles.map(f => f.replace(rootDir + '/', ''))
161
+ };
162
+ }