@principal-ai/principal-view-cli 0.5.0 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0sCpC,wBAAgB,qBAAqB,IAAI,OAAO,CAiI/C"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+uCpC,wBAAgB,qBAAqB,IAAI,OAAO,CAiI/C"}
@@ -182,6 +182,7 @@ const ALLOWED_CANVAS_FIELDS = {
182
182
  'version',
183
183
  'name',
184
184
  'description',
185
+ 'markdown',
185
186
  'nodeTypes',
186
187
  'edgeTypes',
187
188
  'pathConfig',
@@ -346,7 +347,7 @@ function findSimilarField(field, allowedFields) {
346
347
  * Check if a canvas has OTEL-related features
347
348
  * Returns true if the canvas contains any of:
348
349
  * 1. Nodes with pv.otel extension (kind, category)
349
- * 2. Event schema (pv.event with validation)
350
+ * 2. Event schema (pv.event or pv.eventRef with validation)
350
351
  * 3. Canvas scope/audit config (OTEL log routing)
351
352
  * 4. Resource matching for OTEL logs
352
353
  */
@@ -373,8 +374,8 @@ function hasOtelFeatures(canvas) {
373
374
  if (nodePv.otel !== undefined) {
374
375
  return true;
375
376
  }
376
- // Check for event schema (pv.event)
377
- if (nodePv.event !== undefined) {
377
+ // Check for event schema (pv.event or pv.eventRef)
378
+ if (nodePv.event !== undefined || nodePv.eventRef !== undefined) {
378
379
  return true;
379
380
  }
380
381
  // Check for resourceMatch (OTEL log routing)
@@ -634,19 +635,28 @@ function validateCanvas(canvas, filePath, library, repositoryPath) {
634
635
  if (nodePv.otel && typeof nodePv.otel === 'object') {
635
636
  checkUnknownFields(nodePv.otel, ALLOWED_CANVAS_FIELDS.nodePvOtel, `${nodePath}.pv.otel`, issues);
636
637
  }
637
- // For .otel.canvas files: require event field on nodes with pv extension (except groups)
638
+ // Check for conflict: node cannot have both event and eventRef
639
+ if (nodePv.event !== undefined && nodePv.eventRef !== undefined) {
640
+ issues.push({
641
+ type: 'error',
642
+ message: `Node "${nodeLabel}" has both "pv.event" and "pv.eventRef" - only one is allowed`,
643
+ path: `${nodePath}.pv`,
644
+ suggestion: 'Use "event" for inline event definition, or "eventRef" to reference a library event schema. Remove one of them.',
645
+ });
646
+ }
647
+ // For .otel.canvas files: require event or eventRef field on nodes with pv extension (except groups)
638
648
  if (filePath.endsWith('.otel.canvas') && nodeType !== 'group') {
639
- if (nodePv.event === undefined) {
649
+ if (nodePv.event === undefined && nodePv.eventRef === undefined) {
640
650
  issues.push({
641
651
  type: 'error',
642
- message: `Node "${nodeLabel}" in .otel.canvas file must have "pv.event" field`,
643
- path: `${nodePath}.pv.event`,
644
- suggestion: 'Add event name, e.g.: "event": "user.login" or "event": "order.created"',
652
+ message: `Node "${nodeLabel}" in .otel.canvas file must have either "pv.event" or "pv.eventRef" field`,
653
+ path: `${nodePath}.pv`,
654
+ suggestion: 'Add inline event schema with "event": {...} or reference library event with "eventRef": "event.name"',
645
655
  });
646
656
  }
647
657
  }
648
658
  // Validate source file references for OTEL event nodes
649
- const hasOtelFeatures = nodePv.otel !== undefined || nodePv.event !== undefined;
659
+ const hasOtelFeatures = nodePv.otel !== undefined || nodePv.event !== undefined || nodePv.eventRef !== undefined;
650
660
  if (hasOtelFeatures) {
651
661
  // OTEL nodes must have at least one source file reference
652
662
  if (!Array.isArray(nodePv.sources) || nodePv.sources.length === 0) {
@@ -657,11 +667,11 @@ function validateCanvas(canvas, filePath, library, repositoryPath) {
657
667
  suggestion: 'Add at least one source file reference, e.g.: "sources": ["src/services/MyService.ts"]',
658
668
  });
659
669
  }
660
- // For .otel.canvas files: nodes with event must have pv.otel for UI rendering
661
- if (filePath.endsWith('.otel.canvas') && nodePv.event !== undefined && nodePv.otel === undefined) {
670
+ // For .otel.canvas files: nodes with event or eventRef must have pv.otel for UI rendering
671
+ if (filePath.endsWith('.otel.canvas') && (nodePv.event !== undefined || nodePv.eventRef !== undefined) && nodePv.otel === undefined) {
662
672
  issues.push({
663
673
  type: 'error',
664
- message: `Node "${nodeLabel}" in .otel.canvas file has event but is missing "pv.otel" field required for UI badges`,
674
+ message: `Node "${nodeLabel}" in .otel.canvas file has event schema but is missing "pv.otel" field required for UI badges`,
665
675
  path: `${nodePath}.pv.otel`,
666
676
  suggestion: 'Add OTEL metadata for UI rendering, e.g.: "otel": { "kind": "event", "category": "lifecycle", "isNew": true }',
667
677
  });
@@ -921,6 +931,32 @@ function validateCanvas(canvas, filePath, library, repositoryPath) {
921
931
  suggestion: 'Either add OTEL features (pv.otel, pv.events, pv.scope, pv.audit, resourceMatch) or rename to .canvas',
922
932
  });
923
933
  }
934
+ // Validate markdown field for .otel.canvas files
935
+ if (isOtelCanvas) {
936
+ const pv = c.pv;
937
+ if (!pv || typeof pv.markdown !== 'string' || !pv.markdown) {
938
+ issues.push({
939
+ type: 'error',
940
+ message: 'OTEL canvas files must have a "pv.markdown" field pointing to documentation',
941
+ path: 'pv.markdown',
942
+ suggestion: 'Add: "markdown": ".principal-views/graph-name.md" (path relative to repository root)',
943
+ });
944
+ }
945
+ else {
946
+ // Validate that the markdown file exists (if repository path is provided)
947
+ if (repositoryPath) {
948
+ const markdownPath = resolve(repositoryPath, pv.markdown);
949
+ if (!existsSync(markdownPath)) {
950
+ issues.push({
951
+ type: 'error',
952
+ message: `Referenced markdown file does not exist: ${pv.markdown}`,
953
+ path: 'pv.markdown',
954
+ suggestion: `Create the markdown file at: ${markdownPath}`,
955
+ });
956
+ }
957
+ }
958
+ }
959
+ }
924
960
  return issues;
925
961
  }
926
962
  /**
package/dist/index.cjs CHANGED
@@ -234405,6 +234405,7 @@ var ALLOWED_CANVAS_FIELDS = {
234405
234405
  "version",
234406
234406
  "name",
234407
234407
  "description",
234408
+ "markdown",
234408
234409
  "nodeTypes",
234409
234410
  "edgeTypes",
234410
234411
  "pathConfig",
@@ -234571,7 +234572,7 @@ function hasOtelFeatures(canvas) {
234571
234572
  if (nodePv.otel !== void 0) {
234572
234573
  return true;
234573
234574
  }
234574
- if (nodePv.event !== void 0) {
234575
+ if (nodePv.event !== void 0 || nodePv.eventRef !== void 0) {
234575
234576
  return true;
234576
234577
  }
234577
234578
  if (nodePv.resourceMatch !== void 0) {
@@ -234855,17 +234856,25 @@ function validateCanvas(canvas, filePath, library, repositoryPath) {
234855
234856
  issues
234856
234857
  );
234857
234858
  }
234859
+ if (nodePv.event !== void 0 && nodePv.eventRef !== void 0) {
234860
+ issues.push({
234861
+ type: "error",
234862
+ message: `Node "${nodeLabel}" has both "pv.event" and "pv.eventRef" - only one is allowed`,
234863
+ path: `${nodePath2}.pv`,
234864
+ suggestion: 'Use "event" for inline event definition, or "eventRef" to reference a library event schema. Remove one of them.'
234865
+ });
234866
+ }
234858
234867
  if (filePath.endsWith(".otel.canvas") && nodeType !== "group") {
234859
- if (nodePv.event === void 0) {
234868
+ if (nodePv.event === void 0 && nodePv.eventRef === void 0) {
234860
234869
  issues.push({
234861
234870
  type: "error",
234862
- message: `Node "${nodeLabel}" in .otel.canvas file must have "pv.event" field`,
234863
- path: `${nodePath2}.pv.event`,
234864
- suggestion: 'Add event name, e.g.: "event": "user.login" or "event": "order.created"'
234871
+ message: `Node "${nodeLabel}" in .otel.canvas file must have either "pv.event" or "pv.eventRef" field`,
234872
+ path: `${nodePath2}.pv`,
234873
+ suggestion: 'Add inline event schema with "event": {...} or reference library event with "eventRef": "event.name"'
234865
234874
  });
234866
234875
  }
234867
234876
  }
234868
- const hasOtelFeatures2 = nodePv.otel !== void 0 || nodePv.event !== void 0;
234877
+ const hasOtelFeatures2 = nodePv.otel !== void 0 || nodePv.event !== void 0 || nodePv.eventRef !== void 0;
234869
234878
  if (hasOtelFeatures2) {
234870
234879
  if (!Array.isArray(nodePv.sources) || nodePv.sources.length === 0) {
234871
234880
  issues.push({
@@ -234875,10 +234884,10 @@ function validateCanvas(canvas, filePath, library, repositoryPath) {
234875
234884
  suggestion: 'Add at least one source file reference, e.g.: "sources": ["src/services/MyService.ts"]'
234876
234885
  });
234877
234886
  }
234878
- if (filePath.endsWith(".otel.canvas") && nodePv.event !== void 0 && nodePv.otel === void 0) {
234887
+ if (filePath.endsWith(".otel.canvas") && (nodePv.event !== void 0 || nodePv.eventRef !== void 0) && nodePv.otel === void 0) {
234879
234888
  issues.push({
234880
234889
  type: "error",
234881
- message: `Node "${nodeLabel}" in .otel.canvas file has event but is missing "pv.otel" field required for UI badges`,
234890
+ message: `Node "${nodeLabel}" in .otel.canvas file has event schema but is missing "pv.otel" field required for UI badges`,
234882
234891
  path: `${nodePath2}.pv.otel`,
234883
234892
  suggestion: 'Add OTEL metadata for UI rendering, e.g.: "otel": { "kind": "event", "category": "lifecycle", "isNew": true }'
234884
234893
  });
@@ -235126,6 +235135,29 @@ function validateCanvas(canvas, filePath, library, repositoryPath) {
235126
235135
  suggestion: "Either add OTEL features (pv.otel, pv.events, pv.scope, pv.audit, resourceMatch) or rename to .canvas"
235127
235136
  });
235128
235137
  }
235138
+ if (isOtelCanvas) {
235139
+ const pv = c.pv;
235140
+ if (!pv || typeof pv.markdown !== "string" || !pv.markdown) {
235141
+ issues.push({
235142
+ type: "error",
235143
+ message: 'OTEL canvas files must have a "pv.markdown" field pointing to documentation',
235144
+ path: "pv.markdown",
235145
+ suggestion: 'Add: "markdown": ".principal-views/graph-name.md" (path relative to repository root)'
235146
+ });
235147
+ } else {
235148
+ if (repositoryPath) {
235149
+ const markdownPath = (0, import_node_path5.resolve)(repositoryPath, pv.markdown);
235150
+ if (!(0, import_node_fs4.existsSync)(markdownPath)) {
235151
+ issues.push({
235152
+ type: "error",
235153
+ message: `Referenced markdown file does not exist: ${pv.markdown}`,
235154
+ path: "pv.markdown",
235155
+ suggestion: `Create the markdown file at: ${markdownPath}`
235156
+ });
235157
+ }
235158
+ }
235159
+ }
235160
+ }
235129
235161
  return issues;
235130
235162
  }
235131
235163
  function validateFile(filePath, library, repositoryPath) {
@@ -237881,6 +237913,132 @@ function getConfigNameFromFilename(filename) {
237881
237913
  return filename.replace(/\.(yaml|yml)$/, "");
237882
237914
  }
237883
237915
 
237916
+ // node_modules/@principal-ai/principal-view-core/dist/ConfigurationLoader.js
237917
+ var ConfigurationLoader = class _ConfigurationLoader {
237918
+ constructor(fsAdapter) {
237919
+ this.fsAdapter = fsAdapter;
237920
+ }
237921
+ /**
237922
+ * Check if the .principal-views/ configuration directory exists
237923
+ *
237924
+ * @param baseDir - Base directory to search from
237925
+ * @returns True if .principal-views/ directory exists
237926
+ */
237927
+ hasConfigDirectory(baseDir) {
237928
+ const configPath = this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
237929
+ return this.fsAdapter.exists(configPath) && this.fsAdapter.isDirectory(configPath);
237930
+ }
237931
+ /**
237932
+ * List all available configuration names in .principal-views/ folder
237933
+ *
237934
+ * @param baseDir - Base directory containing .principal-views/ folder
237935
+ * @returns Array of configuration names (without extensions)
237936
+ */
237937
+ listConfigurations(baseDir) {
237938
+ if (!this.hasConfigDirectory(baseDir)) {
237939
+ return [];
237940
+ }
237941
+ const configPath = this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
237942
+ const files = this.fsAdapter.readDir(configPath);
237943
+ return files.filter(isYamlFile).map(getConfigNameFromFilename).sort();
237944
+ }
237945
+ /**
237946
+ * Load a specific configuration by name
237947
+ *
237948
+ * @param name - Configuration name (without extension)
237949
+ * @param baseDir - Base directory containing .principal-views/ folder
237950
+ * @returns Configuration file or null if not found/invalid
237951
+ */
237952
+ loadByName(name, baseDir) {
237953
+ if (!this.hasConfigDirectory(baseDir)) {
237954
+ return null;
237955
+ }
237956
+ const configPath = this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
237957
+ for (const ext of ["yaml", "yml"]) {
237958
+ const filename = `${name}.${ext}`;
237959
+ const fullPath = this.fsAdapter.join(configPath, filename);
237960
+ if (this.fsAdapter.exists(fullPath)) {
237961
+ try {
237962
+ const content = this.fsAdapter.readFile(fullPath);
237963
+ const parseResult = parseYaml(content, filename);
237964
+ if (parseResult.success && parseResult.data) {
237965
+ return {
237966
+ name,
237967
+ path: fullPath,
237968
+ config: parseResult.data
237969
+ };
237970
+ }
237971
+ } catch (error) {
237972
+ return null;
237973
+ }
237974
+ }
237975
+ }
237976
+ return null;
237977
+ }
237978
+ /**
237979
+ * Load all configurations from .principal-views/ folder
237980
+ *
237981
+ * @param baseDir - Base directory containing .principal-views/ folder
237982
+ * @returns Result containing all loaded configs and any errors
237983
+ */
237984
+ loadAll(baseDir) {
237985
+ const result = {
237986
+ configs: [],
237987
+ errors: []
237988
+ };
237989
+ if (!this.hasConfigDirectory(baseDir)) {
237990
+ result.errors.push({
237991
+ file: ".principal-views",
237992
+ error: "Configuration directory .principal-views/ not found"
237993
+ });
237994
+ return result;
237995
+ }
237996
+ const configPath = this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
237997
+ const files = this.fsAdapter.readDir(configPath);
237998
+ for (const filename of files) {
237999
+ if (!isYamlFile(filename)) {
238000
+ continue;
238001
+ }
238002
+ const fullPath = this.fsAdapter.join(configPath, filename);
238003
+ const name = getConfigNameFromFilename(filename);
238004
+ try {
238005
+ const content = this.fsAdapter.readFile(fullPath);
238006
+ const parseResult = parseYaml(content, filename);
238007
+ if (parseResult.success && parseResult.data) {
238008
+ result.configs.push({
238009
+ name,
238010
+ path: fullPath,
238011
+ config: parseResult.data
238012
+ });
238013
+ } else if (parseResult.error) {
238014
+ result.errors.push({
238015
+ file: filename,
238016
+ error: parseResult.error
238017
+ });
238018
+ }
238019
+ } catch (error) {
238020
+ const errorMessage = error instanceof Error ? error.message : String(error);
238021
+ result.errors.push({
238022
+ file: filename,
238023
+ error: `Failed to read file: ${errorMessage}`
238024
+ });
238025
+ }
238026
+ }
238027
+ result.configs.sort((a, b) => a.name.localeCompare(b.name));
238028
+ return result;
238029
+ }
238030
+ /**
238031
+ * Get the configuration directory path
238032
+ *
238033
+ * @param baseDir - Base directory
238034
+ * @returns Full path to .principal-views/ directory
238035
+ */
238036
+ getConfigDirectoryPath(baseDir) {
238037
+ return this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
238038
+ }
238039
+ };
238040
+ ConfigurationLoader.CONFIG_DIR = ".principal-views";
238041
+
237884
238042
  // node_modules/@principal-ai/principal-view-core/dist/narrative/template-parser.js
237885
238043
  var import_handlebars = __toESM(require_lib());
237886
238044
  import_handlebars.default.registerHelper("eq", (a, b) => a === b);
@@ -241868,7 +242026,12 @@ var CanvasDiscovery = class _CanvasDiscovery {
241868
242026
  if (options.includeContent && options.fileReader) {
241869
242027
  try {
241870
242028
  const content = await options.fileReader(path4);
241871
- canvas.content = JSON.parse(content);
242029
+ const parsedContent = JSON.parse(content);
242030
+ const canvasWithContent = canvas;
242031
+ canvasWithContent.content = parsedContent;
242032
+ if (parsedContent.pv?.markdown) {
242033
+ canvas.markdownPath = parsedContent.pv.markdown;
242034
+ }
241872
242035
  } catch (error) {
241873
242036
  errors.push({
241874
242037
  path: path4,
@@ -241894,7 +242057,7 @@ var CanvasDiscovery = class _CanvasDiscovery {
241894
242057
  continue;
241895
242058
  const packageInfo = this.findPackageForPath(path4, packageMap);
241896
242059
  const id = packageInfo ? `${packageInfo.packageData.name}/${metadata.basename}` : metadata.basename;
241897
- const execution = {
242060
+ let execution = {
241898
242061
  id,
241899
242062
  name: this.toDisplayName(metadata.basename),
241900
242063
  path: path4,
@@ -241908,7 +242071,7 @@ var CanvasDiscovery = class _CanvasDiscovery {
241908
242071
  if (options.includeContent && options.fileReader) {
241909
242072
  try {
241910
242073
  const content = await options.fileReader(path4);
241911
- execution.content = JSON.parse(content);
242074
+ execution = { ...execution, content: JSON.parse(content) };
241912
242075
  } catch (error) {
241913
242076
  errors.push({
241914
242077
  path: path4,
@@ -242149,7 +242312,11 @@ function convertOtlpToExecutionData(otlp) {
242149
242312
  };
242150
242313
  }
242151
242314
  function isOtlpFormat(data) {
242152
- return typeof data === "object" && data !== null && "resourceSpans" in data && Array.isArray(data.resourceSpans);
242315
+ if (typeof data !== "object" || data === null) {
242316
+ return false;
242317
+ }
242318
+ const potentialOtlp = data;
242319
+ return "resourceSpans" in data && Array.isArray(potentialOtlp.resourceSpans);
242153
242320
  }
242154
242321
  var ExecutionValidator = class {
242155
242322
  /**
@@ -244417,7 +244584,7 @@ var TypeScriptGenerator = class {
244417
244584
  constructor() {
244418
244585
  this.language = "typescript";
244419
244586
  }
244420
- generate(canvas, options) {
244587
+ generate(canvas, options, library) {
244421
244588
  const opts = {
244422
244589
  readonly: options?.style?.readonly ?? false,
244423
244590
  strictNullChecks: options?.style?.strictNullChecks ?? true,
@@ -244440,13 +244607,23 @@ var TypeScriptGenerator = class {
244440
244607
  }
244441
244608
  if (canvas.nodes) {
244442
244609
  for (const node of canvas.nodes) {
244610
+ let eventSchema;
244443
244611
  if (node.pv?.event) {
244444
- lines.push(...this.generateNodeTypes(node.id, node.pv.event, opts));
244612
+ eventSchema = node.pv.event;
244613
+ } else if (node.pv?.eventRef) {
244614
+ eventSchema = this.resolveLibraryEvent(node.pv.eventRef, library);
244615
+ if (!eventSchema) {
244616
+ console.warn(`Node '${node.id}' references unknown library event '${node.pv.eventRef}'`);
244617
+ continue;
244618
+ }
244619
+ }
244620
+ if (eventSchema) {
244621
+ lines.push(...this.generateNodeTypes(node.id, eventSchema, opts));
244445
244622
  lines.push("");
244446
244623
  }
244447
244624
  }
244448
244625
  }
244449
- lines.push(...this.generateEventNameUnion(canvas, opts));
244626
+ lines.push(...this.generateEventNameUnion(canvas, opts, library));
244450
244627
  lines.push("");
244451
244628
  lines.push(...this.generateEmitterTypes(canvas, opts));
244452
244629
  if (opts.namespace) {
@@ -244508,13 +244685,18 @@ var TypeScriptGenerator = class {
244508
244685
  lines.push(" }");
244509
244686
  return lines;
244510
244687
  }
244511
- generateEventNameUnion(canvas, opts) {
244688
+ generateEventNameUnion(canvas, opts, library) {
244512
244689
  const lines = [];
244513
244690
  const allEventNames = /* @__PURE__ */ new Set();
244514
244691
  if (canvas.nodes) {
244515
244692
  for (const node of canvas.nodes) {
244516
244693
  if (node.pv?.event?.name) {
244517
244694
  allEventNames.add(node.pv.event.name);
244695
+ } else if (node.pv?.eventRef) {
244696
+ const eventSchema = this.resolveLibraryEvent(node.pv.eventRef, library);
244697
+ if (eventSchema) {
244698
+ allEventNames.add(eventSchema.name);
244699
+ }
244518
244700
  }
244519
244701
  }
244520
244702
  }
@@ -244599,6 +244781,23 @@ var TypeScriptGenerator = class {
244599
244781
  const kebab = name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
244600
244782
  return `${kebab}.types.ts`;
244601
244783
  }
244784
+ /**
244785
+ * Resolve an event schema from library by event name
244786
+ */
244787
+ resolveLibraryEvent(eventName, library) {
244788
+ if (!library?.eventSchemas) {
244789
+ return void 0;
244790
+ }
244791
+ const librarySchema = library.eventSchemas[eventName];
244792
+ if (!librarySchema) {
244793
+ return void 0;
244794
+ }
244795
+ return {
244796
+ name: eventName,
244797
+ description: librarySchema.description,
244798
+ attributes: librarySchema.attributes
244799
+ };
244800
+ }
244602
244801
  };
244603
244802
  var generatorRegistry = {
244604
244803
  generators: /* @__PURE__ */ new Map([
@@ -244615,132 +244814,6 @@ var generatorRegistry = {
244615
244814
  }
244616
244815
  };
244617
244816
 
244618
- // node_modules/@principal-ai/principal-view-core/dist/ConfigurationLoader.js
244619
- var ConfigurationLoader = class _ConfigurationLoader {
244620
- constructor(fsAdapter) {
244621
- this.fsAdapter = fsAdapter;
244622
- }
244623
- /**
244624
- * Check if the .principal-views/ configuration directory exists
244625
- *
244626
- * @param baseDir - Base directory to search from
244627
- * @returns True if .principal-views/ directory exists
244628
- */
244629
- hasConfigDirectory(baseDir) {
244630
- const configPath = this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
244631
- return this.fsAdapter.exists(configPath) && this.fsAdapter.isDirectory(configPath);
244632
- }
244633
- /**
244634
- * List all available configuration names in .principal-views/ folder
244635
- *
244636
- * @param baseDir - Base directory containing .principal-views/ folder
244637
- * @returns Array of configuration names (without extensions)
244638
- */
244639
- listConfigurations(baseDir) {
244640
- if (!this.hasConfigDirectory(baseDir)) {
244641
- return [];
244642
- }
244643
- const configPath = this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
244644
- const files = this.fsAdapter.readDir(configPath);
244645
- return files.filter(isYamlFile).map(getConfigNameFromFilename).sort();
244646
- }
244647
- /**
244648
- * Load a specific configuration by name
244649
- *
244650
- * @param name - Configuration name (without extension)
244651
- * @param baseDir - Base directory containing .principal-views/ folder
244652
- * @returns Configuration file or null if not found/invalid
244653
- */
244654
- loadByName(name, baseDir) {
244655
- if (!this.hasConfigDirectory(baseDir)) {
244656
- return null;
244657
- }
244658
- const configPath = this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
244659
- for (const ext of ["yaml", "yml"]) {
244660
- const filename = `${name}.${ext}`;
244661
- const fullPath = this.fsAdapter.join(configPath, filename);
244662
- if (this.fsAdapter.exists(fullPath)) {
244663
- try {
244664
- const content = this.fsAdapter.readFile(fullPath);
244665
- const parseResult = parseYaml(content, filename);
244666
- if (parseResult.success && parseResult.data) {
244667
- return {
244668
- name,
244669
- path: fullPath,
244670
- config: parseResult.data
244671
- };
244672
- }
244673
- } catch (error) {
244674
- return null;
244675
- }
244676
- }
244677
- }
244678
- return null;
244679
- }
244680
- /**
244681
- * Load all configurations from .principal-views/ folder
244682
- *
244683
- * @param baseDir - Base directory containing .principal-views/ folder
244684
- * @returns Result containing all loaded configs and any errors
244685
- */
244686
- loadAll(baseDir) {
244687
- const result = {
244688
- configs: [],
244689
- errors: []
244690
- };
244691
- if (!this.hasConfigDirectory(baseDir)) {
244692
- result.errors.push({
244693
- file: ".principal-views",
244694
- error: "Configuration directory .principal-views/ not found"
244695
- });
244696
- return result;
244697
- }
244698
- const configPath = this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
244699
- const files = this.fsAdapter.readDir(configPath);
244700
- for (const filename of files) {
244701
- if (!isYamlFile(filename)) {
244702
- continue;
244703
- }
244704
- const fullPath = this.fsAdapter.join(configPath, filename);
244705
- const name = getConfigNameFromFilename(filename);
244706
- try {
244707
- const content = this.fsAdapter.readFile(fullPath);
244708
- const parseResult = parseYaml(content, filename);
244709
- if (parseResult.success && parseResult.data) {
244710
- result.configs.push({
244711
- name,
244712
- path: fullPath,
244713
- config: parseResult.data
244714
- });
244715
- } else if (parseResult.error) {
244716
- result.errors.push({
244717
- file: filename,
244718
- error: parseResult.error
244719
- });
244720
- }
244721
- } catch (error) {
244722
- const errorMessage = error instanceof Error ? error.message : String(error);
244723
- result.errors.push({
244724
- file: filename,
244725
- error: `Failed to read file: ${errorMessage}`
244726
- });
244727
- }
244728
- }
244729
- result.configs.sort((a, b) => a.name.localeCompare(b.name));
244730
- return result;
244731
- }
244732
- /**
244733
- * Get the configuration directory path
244734
- *
244735
- * @param baseDir - Base directory
244736
- * @returns Full path to .principal-views/ directory
244737
- */
244738
- getConfigDirectoryPath(baseDir) {
244739
- return this.fsAdapter.join(baseDir, _ConfigurationLoader.CONFIG_DIR);
244740
- }
244741
- };
244742
- ConfigurationLoader.CONFIG_DIR = ".principal-views";
244743
-
244744
244817
  // node_modules/@opentelemetry/api/build/esm/platform/node/globalThis.js
244745
244818
  var _globalThis = typeof globalThis === "object" ? globalThis : global;
244746
244819
 
@@ -247687,8 +247760,13 @@ var NarrativeValidator = class {
247687
247760
  }
247688
247761
  const canvasEvents = /* @__PURE__ */ new Set();
247689
247762
  for (const node of canvas.nodes) {
247690
- if (node.pv?.event && typeof node.pv.event === "string") {
247691
- canvasEvents.add(node.pv.event);
247763
+ if (node.pv?.event) {
247764
+ if (typeof node.pv.event === "object" && node.pv.event.name) {
247765
+ canvasEvents.add(node.pv.event.name);
247766
+ }
247767
+ }
247768
+ if (node.pv?.eventRef && typeof node.pv.eventRef === "string") {
247769
+ canvasEvents.add(node.pv.eventRef);
247692
247770
  }
247693
247771
  }
247694
247772
  const narrativeEvents = /* @__PURE__ */ new Set();
@@ -247708,7 +247786,7 @@ var NarrativeValidator = class {
247708
247786
  }
247709
247787
  }
247710
247788
  }
247711
- for (const eventName of narrativeEvents) {
247789
+ for (const eventName of Array.from(narrativeEvents)) {
247712
247790
  if (!canvasEvents.has(eventName)) {
247713
247791
  violations.push({
247714
247792
  ruleId: "narrative-event-sync",
@@ -247722,7 +247800,7 @@ var NarrativeValidator = class {
247722
247800
  });
247723
247801
  }
247724
247802
  }
247725
- for (const eventName of canvasEvents) {
247803
+ for (const eventName of Array.from(canvasEvents)) {
247726
247804
  if (!narrativeEvents.has(eventName)) {
247727
247805
  violations.push({
247728
247806
  ruleId: "narrative-event-coverage",
@@ -247867,6 +247945,9 @@ var NarrativeValidator = class {
247867
247945
  checkConditionStructure(condition, file, scenarioIdx) {
247868
247946
  const violations = [];
247869
247947
  const validFields = ["requires", "excludes", "assertions", "default", "any"];
247948
+ if (typeof condition !== "object" || condition === null) {
247949
+ return violations;
247950
+ }
247870
247951
  const conditionKeys = Object.keys(condition);
247871
247952
  for (const key of conditionKeys) {
247872
247953
  if (!validFields.includes(key)) {