@dbcube/schema-builder 1.0.16 → 1.0.18

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/index.cjs CHANGED
@@ -30,15 +30,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ CubeValidator: () => CubeValidator,
34
+ DependencyResolver: () => DependencyResolver,
33
35
  Schema: () => Schema,
36
+ UIUtils: () => UIUtils,
34
37
  default: () => index_default
35
38
  });
36
39
  module.exports = __toCommonJS(index_exports);
37
40
 
38
41
  // src/lib/Schema.ts
39
- var import_fs2 = __toESM(require("fs"));
42
+ var import_fs4 = __toESM(require("fs"));
40
43
  var import_core = require("@dbcube/core");
41
- var import_path = __toESM(require("path"));
44
+ var import_path3 = __toESM(require("path"));
42
45
 
43
46
  // src/lib/FileUtils.ts
44
47
  var fs = __toESM(require("fs"));
@@ -285,6 +288,550 @@ ${import_chalk.default.red("\u{1F6AB}")} ${import_chalk.default.bold.red("ERRORS
285
288
  }
286
289
  };
287
290
 
291
+ // src/lib/CubeValidator.ts
292
+ var import_fs2 = __toESM(require("fs"));
293
+ var import_path = __toESM(require("path"));
294
+ var CubeValidator = class {
295
+ validTypes = ["varchar", "int", "string", "text", "boolean", "date", "datetime", "timestamp", "decimal", "float", "double", "enum", "json"];
296
+ validOptions = ["not null", "primary", "autoincrement", "unique", "zerofill", "index", "required", "unsigned"];
297
+ validProperties = ["type", "length", "options", "value", "defaultValue", "foreign", "enumValues", "description"];
298
+ knownAnnotations = ["database", "table", "meta", "columns", "fields", "dataset", "beforeAdd", "afterAdd", "beforeUpdate", "afterUpdate", "beforeDelete", "afterDelete", "compute", "column"];
299
+ /**
300
+ * Validates a cube file comprehensively
301
+ */
302
+ validateCubeFile(filePath) {
303
+ const errors = [];
304
+ try {
305
+ const content = import_fs2.default.readFileSync(filePath, "utf8");
306
+ const lines = content.split("\n");
307
+ const fileName = import_path.default.basename(filePath, import_path.default.extname(filePath));
308
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
309
+ const line = lines[lineIndex];
310
+ if (line.trim() === "" || line.trim().startsWith("//")) {
311
+ continue;
312
+ }
313
+ this.validateAnnotations(line, lineIndex + 1, filePath, fileName, errors);
314
+ this.validateDataTypes(line, lineIndex + 1, filePath, fileName, errors, content);
315
+ this.validateColumnOptions(line, lineIndex + 1, filePath, fileName, errors, lines);
316
+ this.validateColumnProperties(line, lineIndex + 1, filePath, fileName, errors, content);
317
+ this.validateRequiredColumnProperties(lines, lineIndex + 1, filePath, fileName, errors);
318
+ this.validateGeneralSyntax(line, lineIndex + 1, filePath, fileName, errors);
319
+ }
320
+ this.validateOverallStructure(content, filePath, fileName, errors);
321
+ } catch (error) {
322
+ errors.push({
323
+ itemName: import_path.default.basename(filePath, import_path.default.extname(filePath)),
324
+ error: `Failed to read cube file: ${error.message}`,
325
+ filePath,
326
+ lineNumber: 1
327
+ });
328
+ }
329
+ return {
330
+ isValid: errors.length === 0,
331
+ errors
332
+ };
333
+ }
334
+ validateAnnotations(line, lineNumber, filePath, fileName, errors) {
335
+ const annotationRegex = /@(\w+)/g;
336
+ let match;
337
+ while ((match = annotationRegex.exec(line)) !== null) {
338
+ const annotation = match[1];
339
+ if (!this.knownAnnotations.includes(annotation)) {
340
+ errors.push({
341
+ itemName: fileName,
342
+ error: `Unknown annotation '@${annotation}'. Valid annotations: ${this.knownAnnotations.join(", ")}`,
343
+ filePath,
344
+ lineNumber
345
+ });
346
+ }
347
+ }
348
+ }
349
+ validateDataTypes(line, lineNumber, filePath, fileName, errors, content) {
350
+ const typeRegex = /type:\s*["'](\w+)["']/g;
351
+ let match;
352
+ while ((match = typeRegex.exec(line)) !== null) {
353
+ const type = match[1];
354
+ if (!this.validTypes.includes(type)) {
355
+ errors.push({
356
+ itemName: fileName,
357
+ error: `Invalid data type '${type}'. Valid types: ${this.validTypes.join(", ")}`,
358
+ filePath,
359
+ lineNumber
360
+ });
361
+ }
362
+ }
363
+ if (line.includes('type: "varchar"')) {
364
+ const lines = content.split("\n");
365
+ const hasLengthNearby = lines.slice(Math.max(0, lineNumber - 1), Math.min(lineNumber + 4, lines.length)).some((nextLine) => nextLine.includes("length:"));
366
+ if (!hasLengthNearby) {
367
+ errors.push({
368
+ itemName: fileName,
369
+ error: "VARCHAR type requires a length specification",
370
+ filePath,
371
+ lineNumber
372
+ });
373
+ }
374
+ }
375
+ }
376
+ validateColumnOptions(line, lineNumber, filePath, fileName, errors, lines) {
377
+ const optionsMatch = line.match(/^\s*options\s*:\s*\[(.*)\]\s*;?\s*$/);
378
+ if (!optionsMatch) return;
379
+ const optionsContent = optionsMatch[1].trim();
380
+ const invalidSyntaxMatch = optionsContent.match(/[^",\s]+(?![^"]*")/);
381
+ if (invalidSyntaxMatch) {
382
+ errors.push({
383
+ itemName: fileName,
384
+ error: `Invalid syntax '${invalidSyntaxMatch[0]}' in options array. All values must be quoted strings`,
385
+ filePath,
386
+ lineNumber
387
+ });
388
+ return;
389
+ }
390
+ const optionMatches = optionsContent.match(/"([^"]*)"/g);
391
+ if (optionMatches) {
392
+ const columnType = this.getColumnTypeForOptions(lines, lineNumber - 1);
393
+ optionMatches.forEach((optionMatch) => {
394
+ const option = optionMatch.replace(/"/g, "");
395
+ if (option.trim() === "") {
396
+ errors.push({
397
+ itemName: fileName,
398
+ error: "Empty option found in options array. All options must have a value",
399
+ filePath,
400
+ lineNumber
401
+ });
402
+ } else if (!this.validOptions.includes(option)) {
403
+ errors.push({
404
+ itemName: fileName,
405
+ error: `Invalid option '${option}'. Valid options: ${this.validOptions.join(", ")}`,
406
+ filePath,
407
+ lineNumber
408
+ });
409
+ } else if (columnType !== "unknown" && !this.isOptionCompatibleWithType(option, columnType)) {
410
+ errors.push({
411
+ itemName: fileName,
412
+ error: `Option '${option}' is not compatible with type '${columnType}'`,
413
+ filePath,
414
+ lineNumber
415
+ });
416
+ }
417
+ });
418
+ }
419
+ }
420
+ validateColumnProperties(line, lineNumber, filePath, fileName, errors, content) {
421
+ const propertyKeyRegex = /^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:/;
422
+ const propMatch = propertyKeyRegex.exec(line);
423
+ if (!propMatch) return;
424
+ const propertyName = propMatch[1];
425
+ if (/^\s*[a-zA-Z_][a-zA-Z0-9_]*\s*:\s*\{/.test(line)) {
426
+ return;
427
+ }
428
+ if (this.isInsideForeignKeyObject(content, lineNumber - 1)) {
429
+ const validForeignKeyProperties = ["table", "column"];
430
+ if (!validForeignKeyProperties.includes(propertyName)) {
431
+ errors.push({
432
+ itemName: fileName,
433
+ error: `Invalid foreign key property '${propertyName}'. Valid foreign key properties: ${validForeignKeyProperties.join(", ")}`,
434
+ filePath,
435
+ lineNumber
436
+ });
437
+ }
438
+ return;
439
+ }
440
+ if (this.isInsideColumnsBlock(content, lineNumber - 1)) {
441
+ if (!this.validProperties.includes(propertyName)) {
442
+ errors.push({
443
+ itemName: fileName,
444
+ error: `Invalid property '${propertyName}'. Valid properties: ${this.validProperties.join(", ")}`,
445
+ filePath,
446
+ lineNumber
447
+ });
448
+ }
449
+ }
450
+ if (/^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*$/.test(line)) {
451
+ errors.push({
452
+ itemName: fileName,
453
+ error: `Property '${propertyName}' is missing a value`,
454
+ filePath,
455
+ lineNumber
456
+ });
457
+ }
458
+ }
459
+ validateRequiredColumnProperties(lines, lineNumber, filePath, fileName, errors) {
460
+ const line = lines[lineNumber - 1];
461
+ if (!/^\s*\}\s*;?\s*$/.test(line)) {
462
+ return;
463
+ }
464
+ let columnStartLine = -1;
465
+ let columnName = "";
466
+ for (let i = lineNumber - 2; i >= 0; i--) {
467
+ const currentLine = lines[i];
468
+ const columnDefMatch = currentLine.match(/^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*\{/);
469
+ if (columnDefMatch) {
470
+ let openBraces = 0;
471
+ let closeBraces = 0;
472
+ for (let j = i; j < lineNumber; j++) {
473
+ openBraces += (lines[j].match(/\{/g) || []).length;
474
+ closeBraces += (lines[j].match(/\}/g) || []).length;
475
+ }
476
+ if (openBraces === closeBraces) {
477
+ columnStartLine = i;
478
+ columnName = columnDefMatch[1];
479
+ break;
480
+ }
481
+ }
482
+ }
483
+ if (columnStartLine === -1 || !columnName) return;
484
+ let hasType = false;
485
+ for (let i = columnStartLine + 1; i < lineNumber - 1; i++) {
486
+ if (lines[i].match(/^\s*type\s*:/)) {
487
+ hasType = true;
488
+ break;
489
+ }
490
+ }
491
+ if (!hasType && columnName !== "foreign" && columnName !== "defaultValue") {
492
+ errors.push({
493
+ itemName: fileName,
494
+ error: `Column '${columnName}' is missing required 'type' property`,
495
+ filePath,
496
+ lineNumber: columnStartLine + 1
497
+ });
498
+ }
499
+ }
500
+ validateGeneralSyntax(line, lineNumber, filePath, fileName, errors) {
501
+ const quotes = line.match(/["']/g);
502
+ if (quotes && quotes.length % 2 !== 0) {
503
+ errors.push({
504
+ itemName: fileName,
505
+ error: "Mismatched quotes detected",
506
+ filePath,
507
+ lineNumber
508
+ });
509
+ }
510
+ if (line.includes("@database") || line.includes("@table")) {
511
+ const stringAnnotationRegex = /@(database|table)\s*\(\s*"([^"]*)"\s*\)/;
512
+ if (!stringAnnotationRegex.test(line)) {
513
+ errors.push({
514
+ itemName: fileName,
515
+ error: 'Invalid annotation syntax. Expected format: @annotation("value")',
516
+ filePath,
517
+ lineNumber
518
+ });
519
+ }
520
+ }
521
+ if (line.includes("@meta")) {
522
+ const metaObjectRegex = /@meta\s*\(\s*\{/;
523
+ if (!metaObjectRegex.test(line)) {
524
+ errors.push({
525
+ itemName: fileName,
526
+ error: "Invalid @meta syntax. Expected format: @meta({ ... })",
527
+ filePath,
528
+ lineNumber
529
+ });
530
+ }
531
+ }
532
+ }
533
+ validateOverallStructure(content, filePath, fileName, errors) {
534
+ const lines = content.split("\n");
535
+ const hasDatabase = lines.some((line) => line.includes("@database"));
536
+ if (!hasDatabase) {
537
+ errors.push({
538
+ itemName: fileName,
539
+ error: "Missing required @database annotation",
540
+ filePath,
541
+ lineNumber: 1
542
+ });
543
+ }
544
+ if (filePath.includes(".table.cube")) {
545
+ const hasColumns = lines.some((line) => line.includes("@columns"));
546
+ if (!hasColumns) {
547
+ errors.push({
548
+ itemName: fileName,
549
+ error: "Table cube files require @columns annotation",
550
+ filePath,
551
+ lineNumber: 1
552
+ });
553
+ }
554
+ }
555
+ }
556
+ getColumnTypeForOptions(lines, optionsLineIndex) {
557
+ for (let i = optionsLineIndex - 1; i >= 0; i--) {
558
+ const line = lines[i];
559
+ const typeMatch = line.match(/^\s*type\s*:\s*"([^"]+)"/);
560
+ if (typeMatch) {
561
+ return typeMatch[1];
562
+ }
563
+ if (/^\s*[a-zA-Z_][a-zA-Z0-9_]*\s*:\s*\{/.test(line)) {
564
+ break;
565
+ }
566
+ }
567
+ return "unknown";
568
+ }
569
+ isOptionCompatibleWithType(option, type) {
570
+ const compatibilityRules = {
571
+ "zerofill": ["int", "decimal", "float", "double"],
572
+ "unsigned": ["int", "decimal", "float", "double"],
573
+ "autoincrement": ["int"],
574
+ "primary": ["int", "varchar", "string"],
575
+ "not null": ["int", "varchar", "string", "text", "boolean", "date", "datetime", "timestamp", "decimal", "float", "double"],
576
+ "unique": ["int", "varchar", "string", "text"],
577
+ "index": ["int", "varchar", "string", "text", "date", "datetime", "timestamp"],
578
+ "required": ["int", "varchar", "string", "text", "boolean", "date", "datetime", "timestamp", "decimal", "float", "double"]
579
+ };
580
+ const compatibleTypes = compatibilityRules[option];
581
+ if (!compatibleTypes) {
582
+ return true;
583
+ }
584
+ return compatibleTypes.includes(type);
585
+ }
586
+ isInsideColumnsBlock(content, lineIndex) {
587
+ const lines = content.split("\n");
588
+ let columnsStartLine = -1;
589
+ let columnsEndLine = -1;
590
+ for (let i = 0; i < lines.length; i++) {
591
+ if (lines[i].includes("@columns")) {
592
+ columnsStartLine = i;
593
+ let braceCount = 0;
594
+ for (let j = i; j < lines.length; j++) {
595
+ const currentLine = lines[j];
596
+ braceCount += (currentLine.match(/\{/g) || []).length;
597
+ braceCount -= (currentLine.match(/\}/g) || []).length;
598
+ if (braceCount === 0 && j > i) {
599
+ columnsEndLine = j;
600
+ break;
601
+ }
602
+ }
603
+ break;
604
+ }
605
+ }
606
+ return columnsStartLine !== -1 && columnsEndLine !== -1 && lineIndex > columnsStartLine && lineIndex < columnsEndLine;
607
+ }
608
+ isInsideForeignKeyObject(content, lineIndex) {
609
+ const lines = content.split("\n");
610
+ for (let i = lineIndex; i >= 0; i--) {
611
+ const line = lines[i];
612
+ if (/foreign\s*:\s*\{/.test(line)) {
613
+ let braceCount = 0;
614
+ for (let j = i; j <= lineIndex; j++) {
615
+ const currentLine = lines[j];
616
+ const openBraces = (currentLine.match(/\{/g) || []).length;
617
+ const closeBraces = (currentLine.match(/\}/g) || []).length;
618
+ braceCount += openBraces - closeBraces;
619
+ if (braceCount === 0 && j > i) {
620
+ return false;
621
+ }
622
+ }
623
+ return braceCount > 0;
624
+ }
625
+ if (line.trim() === "}" || line.includes("};")) {
626
+ break;
627
+ }
628
+ }
629
+ return false;
630
+ }
631
+ };
632
+
633
+ // src/lib/DependencyResolver.ts
634
+ var import_fs3 = __toESM(require("fs"));
635
+ var import_path2 = __toESM(require("path"));
636
+ var DependencyResolver = class {
637
+ /**
638
+ * Resolves table dependencies and creates execution order
639
+ */
640
+ static resolveDependencies(cubeFiles, cubeType = "table") {
641
+ const tableDependencies = this.extractDependencies(cubeFiles, cubeType);
642
+ const orderedTables = this.topologicalSort(tableDependencies);
643
+ const executionOrder = {
644
+ tables: cubeType === "table" ? orderedTables : [],
645
+ seeders: cubeType === "seeder" ? orderedTables : [],
646
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
647
+ };
648
+ this.saveExecutionOrder(executionOrder);
649
+ return executionOrder;
650
+ }
651
+ /**
652
+ * Extracts dependencies from cube files
653
+ */
654
+ static extractDependencies(cubeFiles, cubeType) {
655
+ const dependencies = [];
656
+ for (const file of cubeFiles) {
657
+ let filePath;
658
+ if (import_path2.default.isAbsolute(file)) {
659
+ filePath = file;
660
+ } else if (import_fs3.default.existsSync(file)) {
661
+ filePath = import_path2.default.resolve(file);
662
+ } else {
663
+ filePath = import_path2.default.join(process.cwd(), "dbcube", "cubes", file);
664
+ }
665
+ try {
666
+ const tableNameResult = FileUtils_default.extracTableNameFromCube(filePath);
667
+ const tableName = tableNameResult.status === 200 ? tableNameResult.message : import_path2.default.basename(file, `.${cubeType}.cube`);
668
+ const deps = this.extractForeignKeyReferences(filePath);
669
+ dependencies.push({
670
+ tableName,
671
+ filePath,
672
+ dependencies: deps
673
+ });
674
+ } catch (error) {
675
+ console.error(`Error processing ${filePath}:`, error);
676
+ }
677
+ }
678
+ return dependencies;
679
+ }
680
+ /**
681
+ * Extracts foreign key references from a cube file
682
+ */
683
+ static extractForeignKeyReferences(filePath) {
684
+ const dependencies = [];
685
+ try {
686
+ const content = import_fs3.default.readFileSync(filePath, "utf8");
687
+ const lines = content.split("\n");
688
+ let insideForeignKey = false;
689
+ let braceCount = 0;
690
+ for (const line of lines) {
691
+ if (/foreign\s*:\s*\{/.test(line)) {
692
+ insideForeignKey = true;
693
+ braceCount = 1;
694
+ const sameLineMatch = line.match(/table\s*:\s*["']([^"']+)["']/);
695
+ if (sameLineMatch) {
696
+ dependencies.push(sameLineMatch[1]);
697
+ insideForeignKey = false;
698
+ braceCount = 0;
699
+ }
700
+ continue;
701
+ }
702
+ if (insideForeignKey) {
703
+ braceCount += (line.match(/\{/g) || []).length;
704
+ braceCount -= (line.match(/\}/g) || []).length;
705
+ const tableMatch = line.match(/table\s*:\s*["']([^"']+)["']/);
706
+ if (tableMatch) {
707
+ dependencies.push(tableMatch[1]);
708
+ }
709
+ if (braceCount === 0) {
710
+ insideForeignKey = false;
711
+ }
712
+ }
713
+ }
714
+ } catch (error) {
715
+ console.error(`Error reading file ${filePath}:`, error);
716
+ }
717
+ return dependencies;
718
+ }
719
+ /**
720
+ * Performs topological sort to determine execution order
721
+ */
722
+ static topologicalSort(dependencies) {
723
+ const graph = /* @__PURE__ */ new Map();
724
+ const inDegree = /* @__PURE__ */ new Map();
725
+ const tableMap = /* @__PURE__ */ new Map();
726
+ for (const dep of dependencies) {
727
+ graph.set(dep.tableName, dep.dependencies);
728
+ inDegree.set(dep.tableName, 0);
729
+ tableMap.set(dep.tableName, dep);
730
+ }
731
+ for (const dep of dependencies) {
732
+ for (const dependency of dep.dependencies) {
733
+ if (inDegree.has(dependency)) {
734
+ inDegree.set(dep.tableName, (inDegree.get(dep.tableName) || 0) + 1);
735
+ }
736
+ }
737
+ }
738
+ const queue = [];
739
+ const result = [];
740
+ for (const [table, degree] of inDegree) {
741
+ if (degree === 0) {
742
+ queue.push(table);
743
+ }
744
+ }
745
+ while (queue.length > 0) {
746
+ const current = queue.shift();
747
+ result.push(current);
748
+ const currentDeps = graph.get(current) || [];
749
+ for (const neighbor of currentDeps) {
750
+ if (inDegree.has(neighbor)) {
751
+ const newDegree = (inDegree.get(neighbor) || 0) - 1;
752
+ inDegree.set(neighbor, newDegree);
753
+ if (newDegree === 0) {
754
+ queue.push(neighbor);
755
+ }
756
+ }
757
+ }
758
+ }
759
+ if (result.length !== dependencies.length) {
760
+ console.warn("\u26A0\uFE0F Circular dependencies detected in tables. Some tables may not execute in optimal order.");
761
+ for (const dep of dependencies) {
762
+ if (!result.includes(dep.tableName)) {
763
+ result.push(dep.tableName);
764
+ }
765
+ }
766
+ }
767
+ return result;
768
+ }
769
+ /**
770
+ * Saves the execution order to .dbcube/orderexecute.json
771
+ */
772
+ static saveExecutionOrder(order) {
773
+ try {
774
+ const projectRoot = process.cwd();
775
+ const dbcubeDir = import_path2.default.join(projectRoot, ".dbcube");
776
+ const orderFile = import_path2.default.join(dbcubeDir, "orderexecute.json");
777
+ if (!import_fs3.default.existsSync(dbcubeDir)) {
778
+ import_fs3.default.mkdirSync(dbcubeDir, { recursive: true });
779
+ }
780
+ import_fs3.default.writeFileSync(orderFile, JSON.stringify(order, null, 2), "utf8");
781
+ console.log(`\u{1F4C4} Execution order saved to: ${import_path2.default.relative(projectRoot, orderFile)}`);
782
+ } catch (error) {
783
+ console.error("\u274C Failed to save execution order:", error);
784
+ }
785
+ }
786
+ /**
787
+ * Loads the execution order from .dbcube/orderexecute.json
788
+ */
789
+ static loadExecutionOrder() {
790
+ try {
791
+ const projectRoot = process.cwd();
792
+ const orderFile = import_path2.default.join(projectRoot, ".dbcube", "orderexecute.json");
793
+ if (!import_fs3.default.existsSync(orderFile)) {
794
+ return null;
795
+ }
796
+ const content = import_fs3.default.readFileSync(orderFile, "utf8");
797
+ return JSON.parse(content);
798
+ } catch (error) {
799
+ console.error("\u274C Failed to load execution order:", error);
800
+ return null;
801
+ }
802
+ }
803
+ /**
804
+ * Orders cube files based on saved execution order
805
+ */
806
+ static orderCubeFiles(cubeFiles, cubeType) {
807
+ const executionOrder = this.loadExecutionOrder();
808
+ if (!executionOrder) {
809
+ console.log("\u{1F4C4} No execution order found, processing files in current order");
810
+ return cubeFiles;
811
+ }
812
+ const orderList = cubeType === "table" ? executionOrder.tables : executionOrder.seeders;
813
+ const orderedFiles = [];
814
+ const fileMap = /* @__PURE__ */ new Map();
815
+ for (const file of cubeFiles) {
816
+ const filePath = import_path2.default.isAbsolute(file) ? file : import_path2.default.join(process.cwd(), "dbcube", "cubes", file);
817
+ const tableNameResult = FileUtils_default.extracTableNameFromCube(filePath);
818
+ const tableName = tableNameResult.status === 200 ? tableNameResult.message : import_path2.default.basename(file, `.${cubeType}.cube`);
819
+ fileMap.set(tableName, file);
820
+ }
821
+ for (const tableName of orderList) {
822
+ if (fileMap.has(tableName)) {
823
+ orderedFiles.push(fileMap.get(tableName));
824
+ fileMap.delete(tableName);
825
+ }
826
+ }
827
+ for (const [, file] of fileMap) {
828
+ orderedFiles.push(file);
829
+ }
830
+ console.log(`\u{1F4CB} Using dependency order: ${orderList.join(" \u2192 ")}`);
831
+ return orderedFiles;
832
+ }
833
+ };
834
+
288
835
  // src/lib/Schema.ts
289
836
  var Schema = class {
290
837
  name;
@@ -294,18 +841,27 @@ var Schema = class {
294
841
  this.engine = new import_core.Engine(name);
295
842
  }
296
843
  /**
297
- * Validates that the database specified in cube file exists in configuration
844
+ * Validates cube file comprehensively including syntax, database configuration, and structure
298
845
  * @param filePath - Path to the cube file
299
- * @returns true if valid, throws error if invalid
846
+ * @returns validation result with any errors found
300
847
  */
301
848
  validateDatabaseConfiguration(filePath) {
302
849
  try {
850
+ const cubeValidator = new CubeValidator();
851
+ const cubeValidation = cubeValidator.validateCubeFile(filePath);
852
+ if (!cubeValidation.isValid && cubeValidation.errors.length > 0) {
853
+ return {
854
+ isValid: false,
855
+ error: cubeValidation.errors[0]
856
+ // Return the first error found
857
+ };
858
+ }
303
859
  const dbResult = FileUtils_default.extractDatabaseNameFromCube(filePath);
304
860
  if (dbResult.status !== 200) {
305
861
  return {
306
862
  isValid: false,
307
863
  error: {
308
- itemName: import_path.default.basename(filePath, import_path.default.extname(filePath)),
864
+ itemName: import_path3.default.basename(filePath, import_path3.default.extname(filePath)),
309
865
  error: `Error reading database directive: ${dbResult.message}`,
310
866
  filePath,
311
867
  lineNumber: this.findDatabaseLineNumber(filePath)
@@ -314,7 +870,7 @@ var Schema = class {
314
870
  }
315
871
  const cubeDbName = dbResult.message;
316
872
  const configInstance = new import_core.Config();
317
- const configFilePath = import_path.default.resolve(process.cwd(), "dbcube.config.js");
873
+ const configFilePath = import_path3.default.resolve(process.cwd(), "dbcube.config.js");
318
874
  const configFn = require(configFilePath);
319
875
  if (typeof configFn === "function") {
320
876
  configFn(configInstance);
@@ -341,7 +897,7 @@ var Schema = class {
341
897
  return {
342
898
  isValid: false,
343
899
  error: {
344
- itemName: import_path.default.basename(filePath, import_path.default.extname(filePath)),
900
+ itemName: import_path3.default.basename(filePath, import_path3.default.extname(filePath)),
345
901
  error: `Database configuration '${cubeDbName}' not found in dbcube.config.js. Available: ${availableText}`,
346
902
  filePath,
347
903
  lineNumber: this.findDatabaseLineNumber(filePath)
@@ -353,7 +909,7 @@ var Schema = class {
353
909
  return {
354
910
  isValid: false,
355
911
  error: {
356
- itemName: import_path.default.basename(filePath, import_path.default.extname(filePath)),
912
+ itemName: import_path3.default.basename(filePath, import_path3.default.extname(filePath)),
357
913
  error: `Database configuration validation failed: ${error.message}`,
358
914
  filePath,
359
915
  lineNumber: this.findDatabaseLineNumber(filePath)
@@ -366,7 +922,7 @@ var Schema = class {
366
922
  */
367
923
  findDatabaseLineNumber(filePath) {
368
924
  try {
369
- const content = import_fs2.default.readFileSync(filePath, "utf8");
925
+ const content = import_fs4.default.readFileSync(filePath, "utf8");
370
926
  const lines = content.split("\n");
371
927
  for (let i = 0; i < lines.length; i++) {
372
928
  if (lines[i].includes("@database")) {
@@ -380,7 +936,7 @@ var Schema = class {
380
936
  }
381
937
  async createDatabase() {
382
938
  const startTime = Date.now();
383
- const rootPath = import_path.default.resolve(process.cwd());
939
+ const rootPath = import_path3.default.resolve(process.cwd());
384
940
  UIUtils.showOperationHeader(" CREATING DATABASE", this.name, "\u{1F5C4}\uFE0F");
385
941
  await UIUtils.showItemProgress("Preparando e instalando base de datos", 1, 1);
386
942
  try {
@@ -424,28 +980,31 @@ var Schema = class {
424
980
  }
425
981
  async refreshTables() {
426
982
  const startTime = Date.now();
427
- const cubesDir = import_path.default.join(process.cwd(), "dbcube", "cubes");
428
- if (!import_fs2.default.existsSync(cubesDir)) {
983
+ const cubesDir = import_path3.default.join(process.cwd(), "dbcube", "cubes");
984
+ if (!import_fs4.default.existsSync(cubesDir)) {
429
985
  throw new Error("\u274C The cubes folder does not exist");
430
986
  }
431
987
  const cubeFiles = FileUtils_default.getCubeFilesRecursively("dbcube", "table.cube");
432
988
  if (cubeFiles.length === 0) {
433
989
  throw new Error("\u274C There are no cubes to execute");
434
990
  }
991
+ console.log("\u{1F504} Resolving table dependencies...");
992
+ DependencyResolver.resolveDependencies(cubeFiles, "table");
993
+ const orderedCubeFiles = DependencyResolver.orderCubeFiles(cubeFiles, "table");
435
994
  UIUtils.showOperationHeader("EXECUTING REFRESH TABLES", this.name, "\u{1F504}");
436
995
  let totalTablesProcessed = 0;
437
996
  let successCount = 0;
438
997
  let errorCount = 0;
439
998
  const processedTables = [];
440
999
  const errors = [];
441
- for (let index = 0; index < cubeFiles.length; index++) {
442
- const file = cubeFiles[index];
443
- const filePath = import_path.default.isAbsolute(file) ? file : import_path.default.join(cubesDir, file);
444
- const stats = import_fs2.default.statSync(filePath);
1000
+ for (let index = 0; index < orderedCubeFiles.length; index++) {
1001
+ const file = orderedCubeFiles[index];
1002
+ const filePath = import_path3.default.isAbsolute(file) ? file : import_path3.default.join(cubesDir, file);
1003
+ const stats = import_fs4.default.statSync(filePath);
445
1004
  if (stats.isFile()) {
446
1005
  const getTableName = FileUtils_default.extracTableNameFromCube(filePath);
447
- const tableName = getTableName.status === 200 ? getTableName.message : import_path.default.basename(file, ".table.cube");
448
- await UIUtils.showItemProgress(tableName, index + 1, cubeFiles.length);
1006
+ const tableName = getTableName.status === 200 ? getTableName.message : import_path3.default.basename(file, ".table.cube");
1007
+ await UIUtils.showItemProgress(tableName, index + 1, orderedCubeFiles.length);
449
1008
  try {
450
1009
  const validation = this.validateDatabaseConfiguration(filePath);
451
1010
  if (!validation.isValid && validation.error) {
@@ -526,28 +1085,31 @@ var Schema = class {
526
1085
  }
527
1086
  async freshTables() {
528
1087
  const startTime = Date.now();
529
- const cubesDir = import_path.default.join(process.cwd(), "dbcube", "cubes");
530
- if (!import_fs2.default.existsSync(cubesDir)) {
1088
+ const cubesDir = import_path3.default.join(process.cwd(), "dbcube", "cubes");
1089
+ if (!import_fs4.default.existsSync(cubesDir)) {
531
1090
  throw new Error("\u274C The cubes folder does not exist");
532
1091
  }
533
1092
  const cubeFiles = FileUtils_default.getCubeFilesRecursively("dbcube", "table.cube");
534
1093
  if (cubeFiles.length === 0) {
535
1094
  throw new Error("\u274C There are no cubes to execute");
536
1095
  }
1096
+ console.log("\u{1F504} Resolving table dependencies...");
1097
+ DependencyResolver.resolveDependencies(cubeFiles, "table");
1098
+ const orderedCubeFiles = DependencyResolver.orderCubeFiles(cubeFiles, "table");
537
1099
  UIUtils.showOperationHeader("EXECUTING FRESH TABLES", this.name);
538
1100
  let totalTablesProcessed = 0;
539
1101
  let successCount = 0;
540
1102
  let errorCount = 0;
541
1103
  const processedTables = [];
542
1104
  const errors = [];
543
- for (let index = 0; index < cubeFiles.length; index++) {
544
- const file = cubeFiles[index];
545
- const filePath = import_path.default.isAbsolute(file) ? file : import_path.default.join(cubesDir, file);
546
- const stats = import_fs2.default.statSync(filePath);
1105
+ for (let index = 0; index < orderedCubeFiles.length; index++) {
1106
+ const file = orderedCubeFiles[index];
1107
+ const filePath = import_path3.default.isAbsolute(file) ? file : import_path3.default.join(cubesDir, file);
1108
+ const stats = import_fs4.default.statSync(filePath);
547
1109
  if (stats.isFile()) {
548
1110
  const getTableName = FileUtils_default.extracTableNameFromCube(filePath);
549
- const tableName = getTableName.status === 200 ? getTableName.message : import_path.default.basename(file, ".table.cube");
550
- await UIUtils.showItemProgress(tableName, index + 1, cubeFiles.length);
1111
+ const tableName = getTableName.status === 200 ? getTableName.message : import_path3.default.basename(file, ".table.cube");
1112
+ await UIUtils.showItemProgress(tableName, index + 1, orderedCubeFiles.length);
551
1113
  try {
552
1114
  const validation = this.validateDatabaseConfiguration(filePath);
553
1115
  if (!validation.isValid && validation.error) {
@@ -626,28 +1188,29 @@ var Schema = class {
626
1188
  }
627
1189
  async executeSeeders() {
628
1190
  const startTime = Date.now();
629
- const cubesDir = import_path.default.join(process.cwd(), "dbcube", "cubes");
630
- if (!import_fs2.default.existsSync(cubesDir)) {
1191
+ const cubesDir = import_path3.default.join(process.cwd(), "dbcube", "cubes");
1192
+ if (!import_fs4.default.existsSync(cubesDir)) {
631
1193
  throw new Error("\u274C The cubes folder does not exist");
632
1194
  }
633
1195
  const cubeFiles = FileUtils_default.getCubeFilesRecursively("dbcube", "seeder.cube");
634
1196
  if (cubeFiles.length === 0) {
635
1197
  throw new Error("\u274C There are no cubes to execute");
636
1198
  }
1199
+ const orderedCubeFiles = DependencyResolver.orderCubeFiles(cubeFiles, "seeder");
637
1200
  UIUtils.showOperationHeader("EXECUTING SEEDERS", this.name, "\u{1F331}");
638
1201
  let totalSeedersProcessed = 0;
639
1202
  let successCount = 0;
640
1203
  let errorCount = 0;
641
1204
  const processedSeeders = [];
642
1205
  const errors = [];
643
- for (let index = 0; index < cubeFiles.length; index++) {
644
- const file = cubeFiles[index];
645
- const filePath = import_path.default.isAbsolute(file) ? file : import_path.default.join(cubesDir, file);
646
- const stats = import_fs2.default.statSync(filePath);
1206
+ for (let index = 0; index < orderedCubeFiles.length; index++) {
1207
+ const file = orderedCubeFiles[index];
1208
+ const filePath = import_path3.default.isAbsolute(file) ? file : import_path3.default.join(cubesDir, file);
1209
+ const stats = import_fs4.default.statSync(filePath);
647
1210
  if (stats.isFile()) {
648
1211
  const getSeederName = FileUtils_default.extracTableNameFromCube(filePath);
649
- const seederName = getSeederName.status === 200 ? getSeederName.message : import_path.default.basename(file, ".seeder.cube");
650
- await UIUtils.showItemProgress(seederName, index + 1, cubeFiles.length);
1212
+ const seederName = getSeederName.status === 200 ? getSeederName.message : import_path3.default.basename(file, ".seeder.cube");
1213
+ await UIUtils.showItemProgress(seederName, index + 1, orderedCubeFiles.length);
651
1214
  try {
652
1215
  const validation = this.validateDatabaseConfiguration(filePath);
653
1216
  if (!validation.isValid && validation.error) {
@@ -697,9 +1260,9 @@ var Schema = class {
697
1260
  }
698
1261
  async executeTriggers() {
699
1262
  const startTime = Date.now();
700
- const cubesDir = import_path.default.join(process.cwd(), "dbcube", "cubes");
701
- const triggersDirExit = import_path.default.join(process.cwd(), "dbcube", "triggers");
702
- if (!import_fs2.default.existsSync(cubesDir)) {
1263
+ const cubesDir = import_path3.default.join(process.cwd(), "dbcube", "cubes");
1264
+ const triggersDirExit = import_path3.default.join(process.cwd(), "dbcube", "triggers");
1265
+ if (!import_fs4.default.existsSync(cubesDir)) {
703
1266
  throw new Error("\u274C The cubes folder does not exist");
704
1267
  }
705
1268
  const cubeFiles = FileUtils_default.getCubeFilesRecursively("dbcube", "trigger.cube");
@@ -714,11 +1277,11 @@ var Schema = class {
714
1277
  const errors = [];
715
1278
  for (let index = 0; index < cubeFiles.length; index++) {
716
1279
  const file = cubeFiles[index];
717
- const filePath = import_path.default.isAbsolute(file) ? file : import_path.default.join(cubesDir, file);
718
- const stats = import_fs2.default.statSync(filePath);
1280
+ const filePath = import_path3.default.isAbsolute(file) ? file : import_path3.default.join(cubesDir, file);
1281
+ const stats = import_fs4.default.statSync(filePath);
719
1282
  if (stats.isFile()) {
720
1283
  const getTriggerName = FileUtils_default.extracTableNameFromCube(filePath);
721
- const triggerName = getTriggerName.status === 200 ? getTriggerName.message : import_path.default.basename(file, ".trigger.cube");
1284
+ const triggerName = getTriggerName.status === 200 ? getTriggerName.message : import_path3.default.basename(file, ".trigger.cube");
722
1285
  await UIUtils.showItemProgress(triggerName, index + 1, cubeFiles.length);
723
1286
  try {
724
1287
  const validation = this.validateDatabaseConfiguration(filePath);
@@ -789,7 +1352,7 @@ ${import_chalk2.default.red("\u{1F6AB}")} ${import_chalk2.default.bold.red("ERRO
789
1352
  const errorLocation = `${filePath}:${lineStr}:${columnStr}`;
790
1353
  console.log(`${import_chalk2.default.cyan("[code]")} ${import_chalk2.default.yellow(errorLocation)}`);
791
1354
  try {
792
- const codeLines = import_fs2.default.readFileSync(filePath, "utf-8").split("\n");
1355
+ const codeLines = import_fs4.default.readFileSync(filePath, "utf-8").split("\n");
793
1356
  const start = Math.max(0, lineNum - 3);
794
1357
  const end = Math.min(codeLines.length, lineNum + 2);
795
1358
  for (let i = start; i < end; i++) {
@@ -803,6 +1366,7 @@ ${import_chalk2.default.red("\u{1F6AB}")} ${import_chalk2.default.bold.red("ERRO
803
1366
  }
804
1367
  }
805
1368
  }
1369
+ console.log("");
806
1370
  process.exit(1);
807
1371
  }
808
1372
 
@@ -810,6 +1374,9 @@ ${import_chalk2.default.red("\u{1F6AB}")} ${import_chalk2.default.bold.red("ERRO
810
1374
  var index_default = Schema;
811
1375
  // Annotate the CommonJS export names for ESM import in node:
812
1376
  0 && (module.exports = {
813
- Schema
1377
+ CubeValidator,
1378
+ DependencyResolver,
1379
+ Schema,
1380
+ UIUtils
814
1381
  });
815
1382
  //# sourceMappingURL=index.cjs.map