@principal-ai/principal-view-core 0.28.1 → 0.28.3
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/events/ScopeEventsValidator.d.ts +3 -0
- package/dist/events/ScopeEventsValidator.d.ts.map +1 -1
- package/dist/events/ScopeEventsValidator.js +5 -4
- package/dist/events/ScopeEventsValidator.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +4 -2
- package/dist/node.js.map +1 -1
- package/dist/scopes/ScopePathIndex.d.ts +56 -0
- package/dist/scopes/ScopePathIndex.d.ts.map +1 -0
- package/dist/scopes/ScopePathIndex.js +67 -0
- package/dist/scopes/ScopePathIndex.js.map +1 -0
- package/dist/scopes/ScopesCanvasValidator.d.ts.map +1 -1
- package/dist/scopes/ScopesCanvasValidator.js +64 -0
- package/dist/scopes/ScopesCanvasValidator.js.map +1 -1
- package/dist/scopes/index.d.ts +2 -0
- package/dist/scopes/index.d.ts.map +1 -1
- package/dist/scopes/index.js +5 -1
- package/dist/scopes/index.js.map +1 -1
- package/dist/scopes/validateScopeNamespaceNesting.d.ts +38 -0
- package/dist/scopes/validateScopeNamespaceNesting.d.ts.map +1 -0
- package/dist/scopes/validateScopeNamespaceNesting.js +69 -0
- package/dist/scopes/validateScopeNamespaceNesting.js.map +1 -0
- package/dist/types/canvas.d.ts +15 -0
- package/dist/types/canvas.d.ts.map +1 -1
- package/dist/types/canvas.js.map +1 -1
- package/dist/workflow/validator.d.ts +4 -1
- package/dist/workflow/validator.d.ts.map +1 -1
- package/dist/workflow/validator.js +14 -12
- package/dist/workflow/validator.js.map +1 -1
- package/package.json +1 -1
- package/src/events/ScopeEventsValidator.test.ts +173 -0
- package/src/events/ScopeEventsValidator.ts +5 -4
- package/src/index.ts +6 -0
- package/src/node.ts +6 -0
- package/src/scopes/ScopePathIndex.test.ts +94 -0
- package/src/scopes/ScopePathIndex.ts +89 -0
- package/src/scopes/ScopesCanvasValidator.test.ts +126 -0
- package/src/scopes/ScopesCanvasValidator.ts +68 -0
- package/src/scopes/index.ts +12 -0
- package/src/scopes/validateScopeNamespaceNesting.test.ts +127 -0
- package/src/scopes/validateScopeNamespaceNesting.ts +88 -0
- package/src/types/canvas.ts +15 -0
- package/src/workflow/__tests__/validator.test.ts +120 -1
- package/src/workflow/validator.ts +14 -12
|
@@ -7,6 +7,7 @@ import type { WorkflowTemplate, WorkflowValidationContext } from '../validator';
|
|
|
7
7
|
import type { ExtendedCanvas } from '../../types/canvas';
|
|
8
8
|
import type { ComponentLibrary } from '../../types/library';
|
|
9
9
|
import { EventRegistry } from '../../registry/EventRegistry';
|
|
10
|
+
import { NodeFileSystemAdapter } from '@principal-ai/repository-abstraction/node';
|
|
10
11
|
import { mkdtempSync, writeFileSync, rmSync } from 'fs';
|
|
11
12
|
import { join } from 'path';
|
|
12
13
|
import { tmpdir } from 'os';
|
|
@@ -16,7 +17,7 @@ describe('WorkflowValidator', () => {
|
|
|
16
17
|
let tempDir: string;
|
|
17
18
|
|
|
18
19
|
beforeEach(() => {
|
|
19
|
-
validator = new WorkflowValidator();
|
|
20
|
+
validator = new WorkflowValidator(new NodeFileSystemAdapter());
|
|
20
21
|
// Create a temporary directory for test files
|
|
21
22
|
tempDir = mkdtempSync(join(tmpdir(), 'workflow-validator-test-'));
|
|
22
23
|
});
|
|
@@ -303,6 +304,124 @@ describe('WorkflowValidator', () => {
|
|
|
303
304
|
});
|
|
304
305
|
});
|
|
305
306
|
|
|
307
|
+
// ============================================================================
|
|
308
|
+
// Implementation Files Existence Tests (workflow-files-exist)
|
|
309
|
+
// ============================================================================
|
|
310
|
+
|
|
311
|
+
describe('workflow-files-exist', () => {
|
|
312
|
+
// Create the referenced canvas so unrelated canvas-exists violations stay silent.
|
|
313
|
+
function setupCanvas(): string {
|
|
314
|
+
const canvasPath = join(tempDir, 'test.otel.canvas');
|
|
315
|
+
writeFileSync(canvasPath, JSON.stringify(createValidCanvas()));
|
|
316
|
+
writeFileSync(join(tempDir, 'test.md'), '# Test');
|
|
317
|
+
return canvasPath;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
it('should pass when status is implemented and all files exist', async () => {
|
|
321
|
+
const canvasPath = setupCanvas();
|
|
322
|
+
writeFileSync(join(tempDir, 'impl.ts'), '// impl');
|
|
323
|
+
|
|
324
|
+
const context = createContext(
|
|
325
|
+
{ status: 'implemented', scope: 'test-scope', files: ['impl.ts'] },
|
|
326
|
+
{ canvasPath }
|
|
327
|
+
);
|
|
328
|
+
const result = await validator.validate(context);
|
|
329
|
+
|
|
330
|
+
const fileViolations = result.violations.filter(
|
|
331
|
+
(v) => v.ruleId === 'workflow-files-exist'
|
|
332
|
+
);
|
|
333
|
+
expect(fileViolations).toHaveLength(0);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('should flag each missing file when status is implemented', async () => {
|
|
337
|
+
const canvasPath = setupCanvas();
|
|
338
|
+
|
|
339
|
+
const context = createContext(
|
|
340
|
+
{
|
|
341
|
+
status: 'implemented',
|
|
342
|
+
scope: 'test-scope',
|
|
343
|
+
files: ['missing-one.ts', 'missing-two.ts'],
|
|
344
|
+
},
|
|
345
|
+
{ canvasPath }
|
|
346
|
+
);
|
|
347
|
+
const result = await validator.validate(context);
|
|
348
|
+
|
|
349
|
+
const fileViolations = result.violations.filter(
|
|
350
|
+
(v) => v.ruleId === 'workflow-files-exist'
|
|
351
|
+
);
|
|
352
|
+
expect(fileViolations).toHaveLength(2);
|
|
353
|
+
expect(fileViolations).toContainEqual(
|
|
354
|
+
expect.objectContaining({
|
|
355
|
+
ruleId: 'workflow-files-exist',
|
|
356
|
+
severity: 'error',
|
|
357
|
+
path: 'files',
|
|
358
|
+
message: expect.stringContaining('missing-one.ts'),
|
|
359
|
+
})
|
|
360
|
+
);
|
|
361
|
+
expect(fileViolations).toContainEqual(
|
|
362
|
+
expect.objectContaining({
|
|
363
|
+
ruleId: 'workflow-files-exist',
|
|
364
|
+
message: expect.stringContaining('missing-two.ts'),
|
|
365
|
+
})
|
|
366
|
+
);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('should flag only the missing files when some exist and some do not', async () => {
|
|
370
|
+
const canvasPath = setupCanvas();
|
|
371
|
+
writeFileSync(join(tempDir, 'present.ts'), '// present');
|
|
372
|
+
|
|
373
|
+
const context = createContext(
|
|
374
|
+
{
|
|
375
|
+
status: 'implemented',
|
|
376
|
+
scope: 'test-scope',
|
|
377
|
+
files: ['present.ts', 'absent.ts'],
|
|
378
|
+
},
|
|
379
|
+
{ canvasPath }
|
|
380
|
+
);
|
|
381
|
+
const result = await validator.validate(context);
|
|
382
|
+
|
|
383
|
+
const fileViolations = result.violations.filter(
|
|
384
|
+
(v) => v.ruleId === 'workflow-files-exist'
|
|
385
|
+
);
|
|
386
|
+
expect(fileViolations).toHaveLength(1);
|
|
387
|
+
expect(fileViolations[0].message).toContain('absent.ts');
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('should not check file existence when status is draft', async () => {
|
|
391
|
+
const canvasPath = setupCanvas();
|
|
392
|
+
|
|
393
|
+
const context = createContext(
|
|
394
|
+
{ status: 'draft', files: ['does-not-exist.ts'] },
|
|
395
|
+
{ canvasPath }
|
|
396
|
+
);
|
|
397
|
+
const result = await validator.validate(context);
|
|
398
|
+
|
|
399
|
+
const fileViolations = result.violations.filter(
|
|
400
|
+
(v) => v.ruleId === 'workflow-files-exist'
|
|
401
|
+
);
|
|
402
|
+
expect(fileViolations).toHaveLength(0);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should not check file existence when status is approved', async () => {
|
|
406
|
+
const canvasPath = setupCanvas();
|
|
407
|
+
|
|
408
|
+
const context = createContext(
|
|
409
|
+
{
|
|
410
|
+
status: 'approved',
|
|
411
|
+
scope: 'test-scope',
|
|
412
|
+
files: ['does-not-exist.ts'],
|
|
413
|
+
},
|
|
414
|
+
{ canvasPath }
|
|
415
|
+
);
|
|
416
|
+
const result = await validator.validate(context);
|
|
417
|
+
|
|
418
|
+
const fileViolations = result.violations.filter(
|
|
419
|
+
(v) => v.ruleId === 'workflow-files-exist'
|
|
420
|
+
);
|
|
421
|
+
expect(fileViolations).toHaveLength(0);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
306
425
|
// ============================================================================
|
|
307
426
|
// Scenario Validation Tests
|
|
308
427
|
// ============================================================================
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* Validates .workflow.json files against their corresponding .otel.canvas files
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { FileSystemAdapter } from '@principal-ai/repository-abstraction';
|
|
6
7
|
import type { WorkflowTemplate, WorkflowScenario, ScenarioTemplate } from './types';
|
|
7
8
|
import { getEventTemplateString } from './types';
|
|
8
9
|
import type { ExtendedCanvas, OtelEventNode } from '../types/canvas';
|
|
9
10
|
import { isOtelEventNode } from '../types/canvas';
|
|
10
|
-
import { existsSync, readFileSync } from 'fs';
|
|
11
11
|
import { resolve, basename } from 'path';
|
|
12
12
|
import type { EventRegistry } from '../registry/EventRegistry';
|
|
13
13
|
import type { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer/build/src/trace/internal-types';
|
|
@@ -125,6 +125,8 @@ export interface WorkflowValidationResult {
|
|
|
125
125
|
// ============================================================================
|
|
126
126
|
|
|
127
127
|
export class WorkflowValidator {
|
|
128
|
+
constructor(private fsAdapter: FileSystemAdapter) {}
|
|
129
|
+
|
|
128
130
|
/**
|
|
129
131
|
* Validate a workflow template
|
|
130
132
|
*/
|
|
@@ -134,8 +136,8 @@ export class WorkflowValidator {
|
|
|
134
136
|
const violations: WorkflowViolation[] = [];
|
|
135
137
|
|
|
136
138
|
// Run all validation rules
|
|
137
|
-
violations.push(...this.checkSchema(context));
|
|
138
|
-
violations.push(...this.checkCanvasExists(context));
|
|
139
|
+
violations.push(...(await this.checkSchema(context)));
|
|
140
|
+
violations.push(...(await this.checkCanvasExists(context)));
|
|
139
141
|
violations.push(...this.checkCanvasNodeLabels(context));
|
|
140
142
|
violations.push(...this.checkCanvasCrossReference(context));
|
|
141
143
|
violations.push(...this.checkDeprecatedFields(context));
|
|
@@ -162,7 +164,7 @@ export class WorkflowValidator {
|
|
|
162
164
|
|
|
163
165
|
// Check execution data completeness if execution files are provided
|
|
164
166
|
if (context.executionFiles && context.executionFiles.length > 0) {
|
|
165
|
-
violations.push(...this.checkExecutionDataCompleteness(context));
|
|
167
|
+
violations.push(...(await this.checkExecutionDataCompleteness(context)));
|
|
166
168
|
}
|
|
167
169
|
|
|
168
170
|
return this.aggregateResults(violations);
|
|
@@ -221,7 +223,7 @@ export class WorkflowValidator {
|
|
|
221
223
|
/**
|
|
222
224
|
* Check schema validity (required fields, valid values)
|
|
223
225
|
*/
|
|
224
|
-
private checkSchema(context: WorkflowValidationContext): WorkflowViolation[] {
|
|
226
|
+
private async checkSchema(context: WorkflowValidationContext): Promise<WorkflowViolation[]> {
|
|
225
227
|
const violations: WorkflowViolation[] = [];
|
|
226
228
|
const { workflow, workflowPath } = context;
|
|
227
229
|
|
|
@@ -426,7 +428,7 @@ export class WorkflowValidator {
|
|
|
426
428
|
if (status === 'implemented' && workflow.files && workflow.files.length > 0) {
|
|
427
429
|
for (const file of workflow.files) {
|
|
428
430
|
const filePath = resolve(context.basePath, file);
|
|
429
|
-
if (!
|
|
431
|
+
if (!(await this.fsAdapter.exists(filePath))) {
|
|
430
432
|
violations.push({
|
|
431
433
|
ruleId: 'workflow-files-exist',
|
|
432
434
|
severity: 'error',
|
|
@@ -501,7 +503,7 @@ export class WorkflowValidator {
|
|
|
501
503
|
/**
|
|
502
504
|
* Check that the referenced canvas file exists
|
|
503
505
|
*/
|
|
504
|
-
private checkCanvasExists(context: WorkflowValidationContext): WorkflowViolation[] {
|
|
506
|
+
private async checkCanvasExists(context: WorkflowValidationContext): Promise<WorkflowViolation[]> {
|
|
505
507
|
const violations: WorkflowViolation[] = [];
|
|
506
508
|
const { workflow, workflowPath, basePath, canvasPath } = context;
|
|
507
509
|
|
|
@@ -513,7 +515,7 @@ export class WorkflowValidator {
|
|
|
513
515
|
// Resolve canvas path
|
|
514
516
|
const resolvedPath = canvasPath || resolve(basePath, workflow.canvas);
|
|
515
517
|
|
|
516
|
-
if (!
|
|
518
|
+
if (!(await this.fsAdapter.exists(resolvedPath))) {
|
|
517
519
|
violations.push({
|
|
518
520
|
ruleId: 'workflow-canvas-exists',
|
|
519
521
|
severity: 'error',
|
|
@@ -2302,7 +2304,7 @@ export class WorkflowValidator {
|
|
|
2302
2304
|
* Validates that co-located execution files contain the events and attributes
|
|
2303
2305
|
* that workflow templates reference.
|
|
2304
2306
|
*/
|
|
2305
|
-
private checkExecutionDataCompleteness(context: WorkflowValidationContext): WorkflowViolation[] {
|
|
2307
|
+
private async checkExecutionDataCompleteness(context: WorkflowValidationContext): Promise<WorkflowViolation[]> {
|
|
2306
2308
|
const violations: WorkflowViolation[] = [];
|
|
2307
2309
|
const { workflow, workflowPath, executionFiles } = context;
|
|
2308
2310
|
|
|
@@ -2314,7 +2316,7 @@ export class WorkflowValidator {
|
|
|
2314
2316
|
const executions: Array<{path: string; data: IExportTraceServiceRequest}> = [];
|
|
2315
2317
|
for (const execPath of executionFiles) {
|
|
2316
2318
|
try {
|
|
2317
|
-
const content =
|
|
2319
|
+
const content = await this.fsAdapter.readFile(execPath);
|
|
2318
2320
|
const data = JSON.parse(content) as IExportTraceServiceRequest;
|
|
2319
2321
|
executions.push({ path: execPath, data });
|
|
2320
2322
|
} catch (error) {
|
|
@@ -2730,6 +2732,6 @@ export class WorkflowValidator {
|
|
|
2730
2732
|
/**
|
|
2731
2733
|
* Create a validator instance
|
|
2732
2734
|
*/
|
|
2733
|
-
export function createWorkflowValidator(): WorkflowValidator {
|
|
2734
|
-
return new WorkflowValidator();
|
|
2735
|
+
export function createWorkflowValidator(fsAdapter: FileSystemAdapter): WorkflowValidator {
|
|
2736
|
+
return new WorkflowValidator(fsAdapter);
|
|
2735
2737
|
}
|