@zabaca/lattice 1.0.5 → 1.0.7

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