@zabaca/lattice 1.0.5 → 1.0.6

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 (2) hide show
  1. package/dist/main.js +263 -33
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -1586,6 +1586,46 @@ function collectUniqueEntities(docs) {
1586
1586
  // src/pure/validation.ts
1587
1587
  function validateDocuments(docs) {
1588
1588
  const errors = [];
1589
+ for (const doc of docs) {
1590
+ if (!doc.title || doc.title.trim() === "") {
1591
+ errors.push({
1592
+ path: doc.path,
1593
+ error: "Missing required field: title"
1594
+ });
1595
+ }
1596
+ if (!doc.summary || doc.summary.trim() === "") {
1597
+ errors.push({
1598
+ path: doc.path,
1599
+ error: "Missing required field: summary"
1600
+ });
1601
+ }
1602
+ if (!doc.created) {
1603
+ errors.push({
1604
+ path: doc.path,
1605
+ error: "Missing required field: created"
1606
+ });
1607
+ }
1608
+ if (!doc.updated) {
1609
+ errors.push({
1610
+ path: doc.path,
1611
+ error: "Missing required field: updated"
1612
+ });
1613
+ }
1614
+ if (!doc.status) {
1615
+ errors.push({
1616
+ path: doc.path,
1617
+ error: "Missing required field: status"
1618
+ });
1619
+ }
1620
+ for (const entity of doc.entities) {
1621
+ if (!entity.description || entity.description.trim() === "") {
1622
+ errors.push({
1623
+ path: doc.path,
1624
+ error: `Entity "${entity.name}" (${entity.type}) missing required field: description`
1625
+ });
1626
+ }
1627
+ }
1628
+ }
1589
1629
  const entityIndex = new Map;
1590
1630
  for (const doc of docs) {
1591
1631
  for (const entity of doc.entities) {
@@ -2501,15 +2541,175 @@ StatusCommand = __legacyDecorateClassTS([
2501
2541
  // src/commands/sync.command.ts
2502
2542
  import { watch } from "fs";
2503
2543
  import { join as join3 } from "path";
2504
- import { Injectable as Injectable13 } from "@nestjs/common";
2544
+ import { Injectable as Injectable14 } from "@nestjs/common";
2505
2545
  import { Command as Command5, CommandRunner as CommandRunner5, Option as Option4 } from "nest-commander";
2546
+
2547
+ // src/sync/graph-validator.service.ts
2548
+ import { Injectable as Injectable13, Logger as Logger6 } from "@nestjs/common";
2549
+ class GraphValidatorService {
2550
+ graph;
2551
+ logger = new Logger6(GraphValidatorService.name);
2552
+ constructor(graph) {
2553
+ this.graph = graph;
2554
+ }
2555
+ async validateGraph() {
2556
+ const issues = [];
2557
+ let totalNodes = 0;
2558
+ let documentsChecked = 0;
2559
+ let entitiesChecked = 0;
2560
+ try {
2561
+ const result = await this.graph.query("SELECT label, name, properties FROM nodes");
2562
+ totalNodes = result.resultSet.length;
2563
+ for (const row of result.resultSet) {
2564
+ const label = row[0];
2565
+ const name = row[1];
2566
+ const propertiesJson = row[2];
2567
+ let properties;
2568
+ try {
2569
+ properties = typeof propertiesJson === "string" ? JSON.parse(propertiesJson) : propertiesJson;
2570
+ } catch (_error) {
2571
+ issues.push({
2572
+ type: "error",
2573
+ nodeLabel: label,
2574
+ nodeName: name,
2575
+ field: "properties",
2576
+ message: "Invalid JSON in properties field",
2577
+ suggestion: "Re-sync this document to fix corrupted data"
2578
+ });
2579
+ continue;
2580
+ }
2581
+ if (label === "Document") {
2582
+ documentsChecked++;
2583
+ this.validateDocumentNode(name, properties, issues);
2584
+ } else {
2585
+ entitiesChecked++;
2586
+ this.validateEntityNode(label, name, properties, issues);
2587
+ }
2588
+ }
2589
+ } catch (error) {
2590
+ this.logger.error(`Graph validation failed: ${error instanceof Error ? error.message : String(error)}`);
2591
+ throw error;
2592
+ }
2593
+ const errorsFound = issues.filter((i) => i.type === "error").length;
2594
+ const warningsFound = issues.filter((i) => i.type === "warning").length;
2595
+ return {
2596
+ valid: errorsFound === 0,
2597
+ issues,
2598
+ stats: {
2599
+ totalNodes,
2600
+ documentsChecked,
2601
+ entitiesChecked,
2602
+ errorsFound,
2603
+ warningsFound
2604
+ }
2605
+ };
2606
+ }
2607
+ validateDocumentNode(name, properties, issues) {
2608
+ if ("name" in properties) {
2609
+ issues.push({
2610
+ type: "warning",
2611
+ nodeLabel: "Document",
2612
+ nodeName: name,
2613
+ field: "name",
2614
+ message: "Duplicate 'name' field in properties (already in column)",
2615
+ suggestion: "Re-sync to remove duplicate"
2616
+ });
2617
+ }
2618
+ const requiredFields = ["title", "contentHash"];
2619
+ for (const field of requiredFields) {
2620
+ if (!(field in properties)) {
2621
+ issues.push({
2622
+ type: "error",
2623
+ nodeLabel: "Document",
2624
+ nodeName: name,
2625
+ field,
2626
+ message: `Missing required field: ${field}`,
2627
+ suggestion: "Re-sync this document to populate required fields"
2628
+ });
2629
+ }
2630
+ }
2631
+ const recommendedFields = ["summary", "created", "updated", "status"];
2632
+ for (const field of recommendedFields) {
2633
+ if (!(field in properties)) {
2634
+ issues.push({
2635
+ type: "warning",
2636
+ nodeLabel: "Document",
2637
+ nodeName: name,
2638
+ field,
2639
+ message: `Missing recommended field: ${field}`,
2640
+ suggestion: `Add '${field}' to document frontmatter`
2641
+ });
2642
+ }
2643
+ }
2644
+ }
2645
+ validateEntityNode(label, name, properties, issues) {
2646
+ if ("name" in properties) {
2647
+ issues.push({
2648
+ type: "warning",
2649
+ nodeLabel: label,
2650
+ nodeName: name,
2651
+ field: "name",
2652
+ message: "Duplicate 'name' field in properties (already in column)",
2653
+ suggestion: "Re-sync to remove duplicate"
2654
+ });
2655
+ }
2656
+ if (!("description" in properties) || !properties.description) {
2657
+ issues.push({
2658
+ type: "error",
2659
+ nodeLabel: label,
2660
+ nodeName: name,
2661
+ field: "description",
2662
+ message: "Missing required field: description",
2663
+ suggestion: `Add description to ${label} entity in frontmatter`
2664
+ });
2665
+ }
2666
+ }
2667
+ async validateDocument(path2) {
2668
+ const issues = [];
2669
+ try {
2670
+ const result = await this.graph.query(`SELECT label, name, properties FROM nodes WHERE label = 'Document' AND name = '${this.escape(path2)}'`);
2671
+ if (result.resultSet.length === 0) {
2672
+ issues.push({
2673
+ type: "error",
2674
+ nodeLabel: "Document",
2675
+ nodeName: path2,
2676
+ field: "node",
2677
+ message: "Document not found in graph",
2678
+ suggestion: "Run 'lattice sync' to add this document"
2679
+ });
2680
+ return issues;
2681
+ }
2682
+ const row = result.resultSet[0];
2683
+ const propertiesJson = row[2];
2684
+ const properties = typeof propertiesJson === "string" ? JSON.parse(propertiesJson) : propertiesJson;
2685
+ this.validateDocumentNode(path2, properties, issues);
2686
+ } catch (error) {
2687
+ this.logger.error(`Failed to validate document ${path2}: ${error instanceof Error ? error.message : String(error)}`);
2688
+ throw error;
2689
+ }
2690
+ return issues;
2691
+ }
2692
+ escape(value) {
2693
+ return value.replace(/'/g, "''");
2694
+ }
2695
+ }
2696
+ GraphValidatorService = __legacyDecorateClassTS([
2697
+ Injectable13(),
2698
+ __legacyMetadataTS("design:paramtypes", [
2699
+ typeof GraphService === "undefined" ? Object : GraphService
2700
+ ])
2701
+ ], GraphValidatorService);
2702
+
2703
+ // src/commands/sync.command.ts
2506
2704
  class SyncCommand extends CommandRunner5 {
2507
2705
  syncService;
2706
+ _graphValidator;
2508
2707
  watcher = null;
2509
2708
  isShuttingDown = false;
2510
- constructor(syncService) {
2709
+ constructor(syncService, _graphValidator) {
2511
2710
  super();
2512
2711
  this.syncService = syncService;
2712
+ this._graphValidator = _graphValidator;
2513
2713
  }
2514
2714
  async run(paths, options) {
2515
2715
  if (options.watch && options.dryRun) {
@@ -2667,22 +2867,25 @@ class SyncCommand extends CommandRunner5 {
2667
2867
  });
2668
2868
  }
2669
2869
  if (result.changes && result.changes.length > 0) {
2670
- console.log(`
2870
+ const actualChanges = result.changes.filter((c) => c.changeType !== "unchanged");
2871
+ if (actualChanges.length > 0) {
2872
+ console.log(`
2671
2873
  \uD83D\uDCDD Changes:
2672
2874
  `);
2673
- const icons = {
2674
- new: "\u2795",
2675
- updated: "\uD83D\uDD04",
2676
- deleted: "\uD83D\uDDD1\uFE0F",
2677
- unchanged: "\u23ED\uFE0F"
2678
- };
2679
- result.changes.forEach((c) => {
2680
- const icon = icons[c.changeType];
2681
- console.log(` ${icon} ${c.changeType}: ${c.path}`);
2682
- if (c.reason) {
2683
- console.log(` ${c.reason}`);
2684
- }
2685
- });
2875
+ const icons = {
2876
+ new: "\u2795",
2877
+ updated: "\uD83D\uDD04",
2878
+ deleted: "\uD83D\uDDD1\uFE0F",
2879
+ unchanged: "\u23ED\uFE0F"
2880
+ };
2881
+ actualChanges.forEach((c) => {
2882
+ const icon = icons[c.changeType];
2883
+ console.log(` ${icon} ${c.changeType}: ${c.path}`);
2884
+ if (c.reason) {
2885
+ console.log(` ${c.reason}`);
2886
+ }
2887
+ });
2888
+ }
2686
2889
  }
2687
2890
  if (result.cascadeWarnings && result.cascadeWarnings.length > 0) {
2688
2891
  console.log(`
@@ -2811,28 +3014,31 @@ __legacyDecorateClassTS([
2811
3014
  __legacyMetadataTS("design:returntype", Boolean)
2812
3015
  ], SyncCommand.prototype, "parseNoEmbeddings", null);
2813
3016
  SyncCommand = __legacyDecorateClassTS([
2814
- Injectable13(),
3017
+ Injectable14(),
2815
3018
  Command5({
2816
3019
  name: "sync",
2817
3020
  arguments: "[paths...]",
2818
3021
  description: "Synchronize documents to the knowledge graph"
2819
3022
  }),
2820
3023
  __legacyMetadataTS("design:paramtypes", [
2821
- typeof SyncService === "undefined" ? Object : SyncService
3024
+ typeof SyncService === "undefined" ? Object : SyncService,
3025
+ typeof GraphValidatorService === "undefined" ? Object : GraphValidatorService
2822
3026
  ])
2823
3027
  ], SyncCommand);
2824
3028
  // src/commands/validate.command.ts
2825
- import { Injectable as Injectable14 } from "@nestjs/common";
3029
+ import { Injectable as Injectable15 } from "@nestjs/common";
2826
3030
  import { Command as Command6, CommandRunner as CommandRunner6, Option as Option5 } from "nest-commander";
2827
3031
  class ValidateCommand extends CommandRunner6 {
2828
3032
  parserService;
2829
- constructor(parserService) {
3033
+ _graphValidator;
3034
+ constructor(parserService, _graphValidator) {
2830
3035
  super();
2831
3036
  this.parserService = parserService;
3037
+ this._graphValidator = _graphValidator;
2832
3038
  }
2833
3039
  async run(_inputs, options) {
2834
3040
  try {
2835
- console.log(`Validating entities and relationships...
3041
+ console.log(`=== Document Validation ===
2836
3042
  `);
2837
3043
  const { docs, errors: schemaErrors } = await this.parserService.parseAllDocumentsWithErrors();
2838
3044
  const issues = [];
@@ -2867,7 +3073,7 @@ class ValidateCommand extends CommandRunner6 {
2867
3073
  console.log(`Found ${entityIndex.size} unique entities
2868
3074
  `);
2869
3075
  if (issues.length > 0) {
2870
- console.log(`Errors (${issues.length}):
3076
+ console.log(`Document Errors (${issues.length}):
2871
3077
  `);
2872
3078
  issues.forEach((i) => {
2873
3079
  console.log(` ${i.path}`);
@@ -2877,11 +3083,32 @@ class ValidateCommand extends CommandRunner6 {
2877
3083
  }
2878
3084
  console.log("");
2879
3085
  });
3086
+ } else {
3087
+ console.log(`\u2713 Markdown files valid (schema + relationships)
3088
+ `);
2880
3089
  }
2881
- if (issues.length === 0) {
2882
- console.log("All validations passed!");
2883
- }
2884
- process.exit(issues.length > 0 ? 1 : 0);
3090
+ const graphResult = {
3091
+ valid: true,
3092
+ issues: [],
3093
+ stats: {
3094
+ totalNodes: 0,
3095
+ documentsChecked: 0,
3096
+ entitiesChecked: 0,
3097
+ errorsFound: 0,
3098
+ warningsFound: 0
3099
+ }
3100
+ };
3101
+ const totalErrors = issues.length + graphResult.stats.errorsFound;
3102
+ const totalWarnings = graphResult.stats.warningsFound;
3103
+ console.log(`
3104
+ === Validation Summary ===`);
3105
+ console.log(`Markdown files: ${issues.length === 0 ? "\u2713 PASSED" : `\u2717 ${issues.length} errors`}`);
3106
+ console.log(`Graph database: ${graphResult.stats.errorsFound === 0 ? "\u2713 PASSED" : `\u2717 ${graphResult.stats.errorsFound} errors`}`);
3107
+ console.log(`Warnings: ${totalWarnings}`);
3108
+ console.log(`
3109
+ Overall: ${totalErrors === 0 ? "\u2713 PASSED" : "\u2717 FAILED"}${totalWarnings > 0 ? ` (${totalWarnings} warnings)` : ""}
3110
+ `);
3111
+ process.exit(totalErrors > 0 ? 1 : 0);
2885
3112
  } catch (error) {
2886
3113
  console.error("Validation failed:", error instanceof Error ? error.message : String(error));
2887
3114
  process.exit(1);
@@ -2901,13 +3128,14 @@ __legacyDecorateClassTS([
2901
3128
  __legacyMetadataTS("design:returntype", Boolean)
2902
3129
  ], ValidateCommand.prototype, "parseFix", null);
2903
3130
  ValidateCommand = __legacyDecorateClassTS([
2904
- Injectable14(),
3131
+ Injectable15(),
2905
3132
  Command6({
2906
3133
  name: "validate",
2907
3134
  description: "Validate entity references and relationships across documents"
2908
3135
  }),
2909
3136
  __legacyMetadataTS("design:paramtypes", [
2910
- typeof DocumentParserService === "undefined" ? Object : DocumentParserService
3137
+ typeof DocumentParserService === "undefined" ? Object : DocumentParserService,
3138
+ typeof GraphValidatorService === "undefined" ? Object : GraphValidatorService
2911
3139
  ])
2912
3140
  ], ValidateCommand);
2913
3141
  // src/embedding/embedding.module.ts
@@ -2938,10 +3166,10 @@ GraphModule = __legacyDecorateClassTS([
2938
3166
  import { Module as Module3 } from "@nestjs/common";
2939
3167
 
2940
3168
  // src/query/query.service.ts
2941
- import { Injectable as Injectable15, Logger as Logger6 } from "@nestjs/common";
3169
+ import { Injectable as Injectable16, Logger as Logger7 } from "@nestjs/common";
2942
3170
  class QueryService {
2943
3171
  graphService;
2944
- logger = new Logger6(QueryService.name);
3172
+ logger = new Logger7(QueryService.name);
2945
3173
  constructor(graphService) {
2946
3174
  this.graphService = graphService;
2947
3175
  }
@@ -2951,7 +3179,7 @@ class QueryService {
2951
3179
  }
2952
3180
  }
2953
3181
  QueryService = __legacyDecorateClassTS([
2954
- Injectable15(),
3182
+ Injectable16(),
2955
3183
  __legacyMetadataTS("design:paramtypes", [
2956
3184
  typeof GraphService === "undefined" ? Object : GraphService
2957
3185
  ])
@@ -2981,7 +3209,8 @@ SyncModule = __legacyDecorateClassTS([
2981
3209
  DocumentParserService,
2982
3210
  OntologyService,
2983
3211
  CascadeService,
2984
- PathResolverService
3212
+ PathResolverService,
3213
+ GraphValidatorService
2985
3214
  ],
2986
3215
  exports: [
2987
3216
  SyncService,
@@ -2989,7 +3218,8 @@ SyncModule = __legacyDecorateClassTS([
2989
3218
  DocumentParserService,
2990
3219
  OntologyService,
2991
3220
  CascadeService,
2992
- PathResolverService
3221
+ PathResolverService,
3222
+ GraphValidatorService
2993
3223
  ]
2994
3224
  })
2995
3225
  ], SyncModule);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zabaca/lattice",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Human-initiated, AI-powered knowledge graph for markdown documentation",
5
5
  "type": "module",
6
6
  "bin": {