@principal-ai/principal-view-cli 0.1.20 → 0.1.22

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.
@@ -113,7 +113,7 @@ function addVVHook(repoPath) {
113
113
  const vvContent = getVVHookContent();
114
114
  if (existsSync(hookPath)) {
115
115
  // Append to existing hook
116
- let existingContent = readFileSync(hookPath, 'utf8');
116
+ const existingContent = readFileSync(hookPath, 'utf8');
117
117
  // Check if already has PV hook
118
118
  if (existingContent.includes(VV_HOOK_MARKER)) {
119
119
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/commands/schema.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2UpC,wBAAgB,mBAAmB,IAAI,OAAO,CA0B7C"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/commands/schema.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8UpC,wBAAgB,mBAAmB,IAAI,OAAO,CA0B7C"}
@@ -96,10 +96,10 @@ ${chalk.bold('Required Fields:')}
96
96
  ${chalk.green('id')} ${chalk.dim('string')} Unique identifier
97
97
  ${chalk.green('fromNode')} ${chalk.dim('string')} Source node ID
98
98
  ${chalk.green('toNode')} ${chalk.dim('string')} Target node ID
99
-
100
- ${chalk.bold('Optional Fields:')}
101
99
  ${chalk.green('fromSide')} ${chalk.dim('string')} Side of source: top, right, bottom, left
102
100
  ${chalk.green('toSide')} ${chalk.dim('string')} Side of target: top, right, bottom, left
101
+
102
+ ${chalk.bold('Optional Fields:')}
103
103
  ${chalk.green('fromEnd')} ${chalk.dim('string')} Source endpoint: none, arrow
104
104
  ${chalk.green('toEnd')} ${chalk.dim('string')} Target endpoint: none, arrow (default)
105
105
  ${chalk.green('color')} ${chalk.dim('string')} Edge color (hex or preset)
@@ -109,6 +109,7 @@ ${chalk.bold('PV Edge Extension:')}
109
109
  ${chalk.dim('{')}
110
110
  ${chalk.green('"id"')}: "edge-1",
111
111
  ${chalk.green('"fromNode"')}: "api", ${chalk.green('"toNode"')}: "db",
112
+ ${chalk.green('"fromSide"')}: "right", ${chalk.green('"toSide"')}: "left",
112
113
  ${chalk.green('"pv"')}: {
113
114
  ${chalk.yellow('"edgeType"')}: "query" ${chalk.dim('// Must be defined in vv.edgeTypes')}
114
115
  }
@@ -286,6 +287,8 @@ ${chalk.dim('─'.repeat(50))}
286
287
  "id": "api-to-db",
287
288
  "fromNode": "api",
288
289
  "toNode": "db",
290
+ "fromSide": "right",
291
+ "toSide": "left",
289
292
  "pv": { "edgeType": "query" }
290
293
  }
291
294
  ],
@@ -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;AA26BpC,wBAAgB,qBAAqB,IAAI,OAAO,CAsH/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;AA4iCpC,wBAAgB,qBAAqB,IAAI,OAAO,CAsH/C"}
@@ -51,6 +51,8 @@ function validateLibrary(library) {
51
51
  if (compDef && typeof compDef === 'object') {
52
52
  const comp = compDef;
53
53
  checkUnknownFields(comp, ALLOWED_LIBRARY_FIELDS.nodeComponent, `nodeComponents.${compId}`, issues);
54
+ // Validate icon name format (must be PascalCase for Lucide icons)
55
+ validateIconName(comp.icon, `nodeComponents.${compId}.icon`, issues);
54
56
  // Check nested fields
55
57
  if (comp.size && typeof comp.size === 'object') {
56
58
  checkUnknownFields(comp.size, ALLOWED_LIBRARY_FIELDS.nodeComponentSize, `nodeComponents.${compId}.size`, issues);
@@ -59,6 +61,9 @@ function validateLibrary(library) {
59
61
  for (const [stateId, stateDef] of Object.entries(comp.states)) {
60
62
  if (stateDef && typeof stateDef === 'object') {
61
63
  checkUnknownFields(stateDef, ALLOWED_LIBRARY_FIELDS.nodeComponentState, `nodeComponents.${compId}.states.${stateId}`, issues);
64
+ // Validate state icon name format
65
+ const state = stateDef;
66
+ validateIconName(state.icon, `nodeComponents.${compId}.states.${stateId}.icon`, issues);
62
67
  }
63
68
  }
64
69
  }
@@ -121,6 +126,55 @@ const STANDARD_CANVAS_TYPES = ['text', 'group', 'file', 'link'];
121
126
  */
122
127
  const VALID_NODE_SHAPES = ['circle', 'rectangle', 'hexagon', 'diamond', 'custom'];
123
128
  // ============================================================================
129
+ // Icon Validation
130
+ // ============================================================================
131
+ /**
132
+ * Convert kebab-case to PascalCase
133
+ * e.g., "file-text" -> "FileText", "alert-circle" -> "AlertCircle"
134
+ */
135
+ function kebabToPascalCase(str) {
136
+ return str
137
+ .split('-')
138
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
139
+ .join('');
140
+ }
141
+ /**
142
+ * Check if a string looks like kebab-case (has hyphens and lowercase)
143
+ */
144
+ function isKebabCase(str) {
145
+ return str.includes('-') && str === str.toLowerCase();
146
+ }
147
+ /**
148
+ * Validate an icon name and return issues if invalid
149
+ * Icons should be in PascalCase (e.g., "FileText", "Database", "AlertCircle")
150
+ */
151
+ function validateIconName(iconValue, path, issues) {
152
+ if (typeof iconValue !== 'string' || !iconValue) {
153
+ return; // No icon specified, that's fine
154
+ }
155
+ // Check if it looks like kebab-case
156
+ if (isKebabCase(iconValue)) {
157
+ const suggested = kebabToPascalCase(iconValue);
158
+ issues.push({
159
+ type: 'error',
160
+ message: `Invalid icon name "${iconValue}" - icons must be in PascalCase`,
161
+ path,
162
+ suggestion: `Use "${suggested}" instead of "${iconValue}"`,
163
+ });
164
+ return;
165
+ }
166
+ // Check if first character is lowercase (common mistake)
167
+ if (iconValue[0] === iconValue[0].toLowerCase() && iconValue[0] !== iconValue[0].toUpperCase()) {
168
+ const suggested = iconValue.charAt(0).toUpperCase() + iconValue.slice(1);
169
+ issues.push({
170
+ type: 'error',
171
+ message: `Invalid icon name "${iconValue}" - icons must start with uppercase`,
172
+ path,
173
+ suggestion: `Use "${suggested}" instead of "${iconValue}"`,
174
+ });
175
+ }
176
+ }
177
+ // ============================================================================
124
178
  // Allowed Fields Definitions
125
179
  // ============================================================================
126
180
  /**
@@ -353,6 +407,9 @@ function validateCanvas(canvas, filePath, library) {
353
407
  for (const [typeId, typeDef] of Object.entries(pv.nodeTypes)) {
354
408
  if (typeDef && typeof typeDef === 'object') {
355
409
  checkUnknownFields(typeDef, ALLOWED_CANVAS_FIELDS.pvNodeType, `pv.nodeTypes.${typeId}`, issues);
410
+ // Validate icon name format
411
+ const nodeType = typeDef;
412
+ validateIconName(nodeType.icon, `pv.nodeTypes.${typeId}.icon`, issues);
356
413
  }
357
414
  }
358
415
  }
@@ -515,11 +572,16 @@ function validateCanvas(canvas, filePath, library) {
515
572
  const nodePv = n.pv;
516
573
  // Check unknown fields in node pv extension
517
574
  checkUnknownFields(nodePv, ALLOWED_CANVAS_FIELDS.nodePv, `${nodePath}.pv`, issues);
575
+ // Validate icon name format (must be PascalCase for Lucide icons)
576
+ validateIconName(nodePv.icon, `${nodePath}.pv.icon`, issues);
518
577
  // Check nested pv fields
519
578
  if (nodePv.states && typeof nodePv.states === 'object') {
520
579
  for (const [stateId, stateDef] of Object.entries(nodePv.states)) {
521
580
  if (stateDef && typeof stateDef === 'object') {
522
581
  checkUnknownFields(stateDef, ALLOWED_CANVAS_FIELDS.nodePvState, `${nodePath}.pv.states.${stateId}`, issues);
582
+ // Validate state icon name format
583
+ const state = stateDef;
584
+ validateIconName(state.icon, `${nodePath}.pv.states.${stateId}.icon`, issues);
523
585
  }
524
586
  }
525
587
  }
@@ -628,6 +690,62 @@ function validateCanvas(canvas, filePath, library) {
628
690
  path: `${edgePath}.toNode`,
629
691
  });
630
692
  }
693
+ // Validate fromSide and toSide are present and valid
694
+ const VALID_SIDES = ['top', 'right', 'bottom', 'left'];
695
+ if (typeof e.fromSide !== 'string') {
696
+ issues.push({
697
+ type: 'error',
698
+ message: `Edge "${edgeLabel}" must have a "fromSide" field`,
699
+ path: `${edgePath}.fromSide`,
700
+ suggestion: `Specify which side of the source node the edge starts from: ${VALID_SIDES.join(', ')}`,
701
+ });
702
+ }
703
+ else if (!VALID_SIDES.includes(e.fromSide)) {
704
+ issues.push({
705
+ type: 'error',
706
+ message: `Edge "${edgeLabel}" has invalid fromSide "${e.fromSide}"`,
707
+ path: `${edgePath}.fromSide`,
708
+ suggestion: `Valid values: ${VALID_SIDES.join(', ')}`,
709
+ });
710
+ }
711
+ if (typeof e.toSide !== 'string') {
712
+ issues.push({
713
+ type: 'error',
714
+ message: `Edge "${edgeLabel}" must have a "toSide" field`,
715
+ path: `${edgePath}.toSide`,
716
+ suggestion: `Specify which side of the target node the edge connects to: ${VALID_SIDES.join(', ')}`,
717
+ });
718
+ }
719
+ else if (!VALID_SIDES.includes(e.toSide)) {
720
+ issues.push({
721
+ type: 'error',
722
+ message: `Edge "${edgeLabel}" has invalid toSide "${e.toSide}"`,
723
+ path: `${edgePath}.toSide`,
724
+ suggestion: `Valid values: ${VALID_SIDES.join(', ')}`,
725
+ });
726
+ }
727
+ // Validate pv extension is present with edgeType
728
+ if (!e.pv || typeof e.pv !== 'object') {
729
+ issues.push({
730
+ type: 'error',
731
+ message: `Edge "${edgeLabel}" must have a "pv" extension with edgeType`,
732
+ path: `${edgePath}.pv`,
733
+ suggestion: 'Add: "pv": { "edgeType": "your-edge-type" }',
734
+ });
735
+ }
736
+ else {
737
+ const edgePv = e.pv;
738
+ if (typeof edgePv.edgeType !== 'string' || !edgePv.edgeType) {
739
+ issues.push({
740
+ type: 'error',
741
+ message: `Edge "${edgeLabel}" must have a "pv.edgeType" field`,
742
+ path: `${edgePath}.pv.edgeType`,
743
+ suggestion: allDefinedEdgeTypes.length > 0
744
+ ? `Available types: ${allDefinedEdgeTypes.join(', ')}`
745
+ : 'Define edge types in canvas pv.edgeTypes or library.yaml edgeComponents',
746
+ });
747
+ }
748
+ }
631
749
  // Validate edge pv extension fields
632
750
  if (e.pv && typeof e.pv === 'object') {
633
751
  const edgePv = e.pv;
package/dist/index.cjs CHANGED
@@ -13490,6 +13490,7 @@ function validateLibrary(library) {
13490
13490
  `nodeComponents.${compId}`,
13491
13491
  issues
13492
13492
  );
13493
+ validateIconName(comp.icon, `nodeComponents.${compId}.icon`, issues);
13493
13494
  if (comp.size && typeof comp.size === "object") {
13494
13495
  checkUnknownFields(
13495
13496
  comp.size,
@@ -13509,6 +13510,8 @@ function validateLibrary(library) {
13509
13510
  `nodeComponents.${compId}.states.${stateId}`,
13510
13511
  issues
13511
13512
  );
13513
+ const state = stateDef;
13514
+ validateIconName(state.icon, `nodeComponents.${compId}.states.${stateId}.icon`, issues);
13512
13515
  }
13513
13516
  }
13514
13517
  }
@@ -13603,6 +13606,36 @@ function validateLibrary(library) {
13603
13606
  }
13604
13607
  var STANDARD_CANVAS_TYPES = ["text", "group", "file", "link"];
13605
13608
  var VALID_NODE_SHAPES = ["circle", "rectangle", "hexagon", "diamond", "custom"];
13609
+ function kebabToPascalCase(str2) {
13610
+ return str2.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
13611
+ }
13612
+ function isKebabCase(str2) {
13613
+ return str2.includes("-") && str2 === str2.toLowerCase();
13614
+ }
13615
+ function validateIconName(iconValue, path4, issues) {
13616
+ if (typeof iconValue !== "string" || !iconValue) {
13617
+ return;
13618
+ }
13619
+ if (isKebabCase(iconValue)) {
13620
+ const suggested = kebabToPascalCase(iconValue);
13621
+ issues.push({
13622
+ type: "error",
13623
+ message: `Invalid icon name "${iconValue}" - icons must be in PascalCase`,
13624
+ path: path4,
13625
+ suggestion: `Use "${suggested}" instead of "${iconValue}"`
13626
+ });
13627
+ return;
13628
+ }
13629
+ if (iconValue[0] === iconValue[0].toLowerCase() && iconValue[0] !== iconValue[0].toUpperCase()) {
13630
+ const suggested = iconValue.charAt(0).toUpperCase() + iconValue.slice(1);
13631
+ issues.push({
13632
+ type: "error",
13633
+ message: `Invalid icon name "${iconValue}" - icons must start with uppercase`,
13634
+ path: path4,
13635
+ suggestion: `Use "${suggested}" instead of "${iconValue}"`
13636
+ });
13637
+ }
13638
+ }
13606
13639
  var ALLOWED_CANVAS_FIELDS = {
13607
13640
  root: ["nodes", "edges", "pv"],
13608
13641
  pv: ["version", "name", "description", "nodeTypes", "edgeTypes", "pathConfig", "display"],
@@ -13817,6 +13850,8 @@ function validateCanvas(canvas, filePath, library) {
13817
13850
  `pv.nodeTypes.${typeId}`,
13818
13851
  issues
13819
13852
  );
13853
+ const nodeType = typeDef;
13854
+ validateIconName(nodeType.icon, `pv.nodeTypes.${typeId}.icon`, issues);
13820
13855
  }
13821
13856
  }
13822
13857
  }
@@ -13981,6 +14016,7 @@ function validateCanvas(canvas, filePath, library) {
13981
14016
  if (n.pv && typeof n.pv === "object") {
13982
14017
  const nodePv = n.pv;
13983
14018
  checkUnknownFields(nodePv, ALLOWED_CANVAS_FIELDS.nodePv, `${nodePath2}.pv`, issues);
14019
+ validateIconName(nodePv.icon, `${nodePath2}.pv.icon`, issues);
13984
14020
  if (nodePv.states && typeof nodePv.states === "object") {
13985
14021
  for (const [stateId, stateDef] of Object.entries(
13986
14022
  nodePv.states
@@ -13992,6 +14028,8 @@ function validateCanvas(canvas, filePath, library) {
13992
14028
  `${nodePath2}.pv.states.${stateId}`,
13993
14029
  issues
13994
14030
  );
14031
+ const state = stateDef;
14032
+ validateIconName(state.icon, `${nodePath2}.pv.states.${stateId}.icon`, issues);
13995
14033
  }
13996
14034
  }
13997
14035
  }
@@ -14107,6 +14145,55 @@ function validateCanvas(canvas, filePath, library) {
14107
14145
  path: `${edgePath}.toNode`
14108
14146
  });
14109
14147
  }
14148
+ const VALID_SIDES = ["top", "right", "bottom", "left"];
14149
+ if (typeof e.fromSide !== "string") {
14150
+ issues.push({
14151
+ type: "error",
14152
+ message: `Edge "${edgeLabel}" must have a "fromSide" field`,
14153
+ path: `${edgePath}.fromSide`,
14154
+ suggestion: `Specify which side of the source node the edge starts from: ${VALID_SIDES.join(", ")}`
14155
+ });
14156
+ } else if (!VALID_SIDES.includes(e.fromSide)) {
14157
+ issues.push({
14158
+ type: "error",
14159
+ message: `Edge "${edgeLabel}" has invalid fromSide "${e.fromSide}"`,
14160
+ path: `${edgePath}.fromSide`,
14161
+ suggestion: `Valid values: ${VALID_SIDES.join(", ")}`
14162
+ });
14163
+ }
14164
+ if (typeof e.toSide !== "string") {
14165
+ issues.push({
14166
+ type: "error",
14167
+ message: `Edge "${edgeLabel}" must have a "toSide" field`,
14168
+ path: `${edgePath}.toSide`,
14169
+ suggestion: `Specify which side of the target node the edge connects to: ${VALID_SIDES.join(", ")}`
14170
+ });
14171
+ } else if (!VALID_SIDES.includes(e.toSide)) {
14172
+ issues.push({
14173
+ type: "error",
14174
+ message: `Edge "${edgeLabel}" has invalid toSide "${e.toSide}"`,
14175
+ path: `${edgePath}.toSide`,
14176
+ suggestion: `Valid values: ${VALID_SIDES.join(", ")}`
14177
+ });
14178
+ }
14179
+ if (!e.pv || typeof e.pv !== "object") {
14180
+ issues.push({
14181
+ type: "error",
14182
+ message: `Edge "${edgeLabel}" must have a "pv" extension with edgeType`,
14183
+ path: `${edgePath}.pv`,
14184
+ suggestion: 'Add: "pv": { "edgeType": "your-edge-type" }'
14185
+ });
14186
+ } else {
14187
+ const edgePv = e.pv;
14188
+ if (typeof edgePv.edgeType !== "string" || !edgePv.edgeType) {
14189
+ issues.push({
14190
+ type: "error",
14191
+ message: `Edge "${edgeLabel}" must have a "pv.edgeType" field`,
14192
+ path: `${edgePath}.pv.edgeType`,
14193
+ suggestion: allDefinedEdgeTypes.length > 0 ? `Available types: ${allDefinedEdgeTypes.join(", ")}` : "Define edge types in canvas pv.edgeTypes or library.yaml edgeComponents"
14194
+ });
14195
+ }
14196
+ }
14110
14197
  if (e.pv && typeof e.pv === "object") {
14111
14198
  const edgePv = e.pv;
14112
14199
  checkUnknownFields(edgePv, ALLOWED_CANVAS_FIELDS.edgePv, `${edgePath}.pv`, issues);
@@ -14695,10 +14782,10 @@ ${source_default.bold("Required Fields:")}
14695
14782
  ${source_default.green("id")} ${source_default.dim("string")} Unique identifier
14696
14783
  ${source_default.green("fromNode")} ${source_default.dim("string")} Source node ID
14697
14784
  ${source_default.green("toNode")} ${source_default.dim("string")} Target node ID
14698
-
14699
- ${source_default.bold("Optional Fields:")}
14700
14785
  ${source_default.green("fromSide")} ${source_default.dim("string")} Side of source: top, right, bottom, left
14701
14786
  ${source_default.green("toSide")} ${source_default.dim("string")} Side of target: top, right, bottom, left
14787
+
14788
+ ${source_default.bold("Optional Fields:")}
14702
14789
  ${source_default.green("fromEnd")} ${source_default.dim("string")} Source endpoint: none, arrow
14703
14790
  ${source_default.green("toEnd")} ${source_default.dim("string")} Target endpoint: none, arrow (default)
14704
14791
  ${source_default.green("color")} ${source_default.dim("string")} Edge color (hex or preset)
@@ -14708,6 +14795,7 @@ ${source_default.bold("PV Edge Extension:")}
14708
14795
  ${source_default.dim("{")}
14709
14796
  ${source_default.green('"id"')}: "edge-1",
14710
14797
  ${source_default.green('"fromNode"')}: "api", ${source_default.green('"toNode"')}: "db",
14798
+ ${source_default.green('"fromSide"')}: "right", ${source_default.green('"toSide"')}: "left",
14711
14799
  ${source_default.green('"pv"')}: {
14712
14800
  ${source_default.yellow('"edgeType"')}: "query" ${source_default.dim("// Must be defined in vv.edgeTypes")}
14713
14801
  }
@@ -14889,6 +14977,8 @@ ${source_default.dim("\u2500".repeat(50))}
14889
14977
  "id": "api-to-db",
14890
14978
  "fromNode": "api",
14891
14979
  "toNode": "db",
14980
+ "fromSide": "right",
14981
+ "toSide": "left",
14892
14982
  "pv": { "edgeType": "query" }
14893
14983
  }
14894
14984
  ],
@@ -15264,7 +15354,7 @@ function addVVHook(repoPath) {
15264
15354
  const hookPath = (0, import_node_path9.join)(repoPath, HUSKY_DIR, PRE_COMMIT_HOOK);
15265
15355
  const vvContent = getVVHookContent();
15266
15356
  if ((0, import_node_fs8.existsSync)(hookPath)) {
15267
- let existingContent = (0, import_node_fs8.readFileSync)(hookPath, "utf8");
15357
+ const existingContent = (0, import_node_fs8.readFileSync)(hookPath, "utf8");
15268
15358
  if (existingContent.includes(VV_HOOK_MARKER)) {
15269
15359
  return;
15270
15360
  }
@@ -15842,7 +15932,11 @@ function createRulesEngine(rules) {
15842
15932
  }
15843
15933
 
15844
15934
  // ../core/dist/rules/config.js
15845
- var DEFAULT_INCLUDE_PATTERNS = [".principal-views/**/*.yaml", ".principal-views/**/*.yml", ".principal-views/**/*.json"];
15935
+ var DEFAULT_INCLUDE_PATTERNS = [
15936
+ ".principal-views/**/*.yaml",
15937
+ ".principal-views/**/*.yml",
15938
+ ".principal-views/**/*.json"
15939
+ ];
15846
15940
  var DEFAULT_EXCLUDE_PATTERNS = ["**/node_modules/**", "**/*.test.*"];
15847
15941
  var VALID_SEVERITIES = [
15848
15942
  "off",