@api-client/core 0.18.13 → 0.18.15

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 (25) hide show
  1. package/build/src/modeling/DomainValidation.d.ts +78 -0
  2. package/build/src/modeling/DomainValidation.d.ts.map +1 -1
  3. package/build/src/modeling/DomainValidation.js +78 -0
  4. package/build/src/modeling/DomainValidation.js.map +1 -1
  5. package/build/src/modeling/importers/FilteringJsonSchemaImporter.d.ts +84 -0
  6. package/build/src/modeling/importers/FilteringJsonSchemaImporter.d.ts.map +1 -0
  7. package/build/src/modeling/importers/FilteringJsonSchemaImporter.js +254 -0
  8. package/build/src/modeling/importers/FilteringJsonSchemaImporter.js.map +1 -0
  9. package/build/src/modeling/importers/SchemaFilteringStrategy.d.ts +72 -0
  10. package/build/src/modeling/importers/SchemaFilteringStrategy.d.ts.map +1 -0
  11. package/build/src/modeling/importers/SchemaFilteringStrategy.js +140 -0
  12. package/build/src/modeling/importers/SchemaFilteringStrategy.js.map +1 -0
  13. package/build/src/modeling/validation/entity_validation.d.ts +17 -3
  14. package/build/src/modeling/validation/entity_validation.d.ts.map +1 -1
  15. package/build/src/modeling/validation/entity_validation.js +51 -10
  16. package/build/src/modeling/validation/entity_validation.js.map +1 -1
  17. package/build/tsconfig.tsbuildinfo +1 -1
  18. package/data/models/example-generator-api.json +15 -15
  19. package/package.json +1 -1
  20. package/src/modeling/DomainValidation.ts +78 -0
  21. package/src/modeling/importers/FilteringJsonSchemaImporter.ts +324 -0
  22. package/src/modeling/importers/SchemaFilteringStrategy.ts +201 -0
  23. package/src/modeling/validation/entity_validation.ts +59 -10
  24. package/tests/unit/modeling/importers/schema_filtering.spec.ts +764 -0
  25. package/tests/unit/modeling/validation/entity_validation.spec.ts +95 -0
@@ -1,8 +1,86 @@
1
1
  import { DataDomain } from './DataDomain.js';
2
2
  import { DomainImpactReport } from './types.js';
3
+ /**
4
+ * DomainValidation performs comprehensive validation on a data domain and its components.
5
+ *
6
+ * This class orchestrates validation across all domain elements including entities, properties,
7
+ * associations, and semantics. It ensures that the data domain is well-formed and follows
8
+ * established conventions before it can be published or used in API generation.
9
+ *
10
+ * ## Validation Scope
11
+ *
12
+ * The validation process covers:
13
+ * - **Entities**: Structure, naming, primary keys, and inheritance
14
+ * - **Properties**: Naming conventions, data types, and constraints
15
+ * - **Associations**: Relationships, targets, and naming
16
+ * - **Semantics**: Recommended patterns and best practices
17
+ *
18
+ * ## Validation Severity Levels
19
+ *
20
+ * - **Error**: Blocking issues that prevent domain publication
21
+ * - **Warning**: Issues that may cause problems but don't block publication
22
+ * - **Info**: Recommendations for best practices
23
+ *
24
+ * ## Usage
25
+ *
26
+ * ```typescript
27
+ * const domain = new DataDomain()
28
+ * // ... add entities, properties, associations
29
+ *
30
+ * const validator = new DomainValidation(domain)
31
+ * const report = validator.validate()
32
+ *
33
+ * if (report.canProceed) {
34
+ * // Domain is valid and can be published
35
+ * } else {
36
+ * // Handle validation errors
37
+ * console.log(report.impact)
38
+ * }
39
+ * ```
40
+ *
41
+ * ## Validation Rules
42
+ *
43
+ * ### Entity Validation Rules
44
+ * - **Primary Key**: Each entity must have a primary key (can be inherited)
45
+ * - **Minimum Properties**: Entities should have properties or associations (can be inherited)
46
+ * - **Naming**: Entity names must follow PostgreSQL naming conventions
47
+ * - **Uniqueness**: Entity names must be unique within the domain
48
+ *
49
+ * ### Property Validation Rules
50
+ * - **Naming**: Properties must follow PostgreSQL column naming conventions
51
+ * - **Length**: Names must be 2-59 characters long
52
+ * - **Format**: Names must start with letter/underscore, contain only alphanumeric/underscore
53
+ * - **Reserved Words**: Names cannot be PostgreSQL reserved keywords
54
+ * - **Case**: Snake case is recommended (lowercase with underscores)
55
+ *
56
+ * ### Association Validation Rules
57
+ * - **Targets**: Associations must have at least one target entity
58
+ * - **Target Existence**: Target entities must exist in the domain
59
+ * - **Naming**: Same rules as properties
60
+ *
61
+ * ### Semantic Validation Rules
62
+ * - **User Entity**: Recommended to have at least one entity with User semantic
63
+ * - **Timestamps**: Recommended to have CreatedTimestamp and UpdatedTimestamp properties
64
+ * - **Soft Delete**: Recommended to have soft delete capability for entities
65
+ * - **Data Types**: Semantic-specific data type validation
66
+ *
67
+ * @see {@link EntityValidation} for detailed entity validation rules
68
+ * @see {@link PropertyValidation} for detailed property validation rules
69
+ * @see {@link AssociationValidation} for detailed association validation rules
70
+ * @see {@link SemanticValidation} for detailed semantic validation rules
71
+ */
3
72
  export declare class DomainValidation {
4
73
  private root;
5
74
  constructor(root: DataDomain);
75
+ /**
76
+ * Performs comprehensive validation on the entire data domain.
77
+ *
78
+ * This method validates all entities, properties, associations, and semantics
79
+ * in the domain. It returns a detailed report of all validation issues found,
80
+ * categorized by severity level.
81
+ *
82
+ * @returns A comprehensive validation report with all issues found
83
+ */
6
84
  validate(): DomainImpactReport;
7
85
  }
8
86
  //# sourceMappingURL=DomainValidation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DomainValidation.d.ts","sourceRoot":"","sources":["../../../src/modeling/DomainValidation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAM/C,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAAY;gBAEZ,IAAI,EAAE,UAAU;IAI5B,QAAQ,IAAI,kBAAkB;CAyF/B"}
1
+ {"version":3,"file":"DomainValidation.d.ts","sourceRoot":"","sources":["../../../src/modeling/DomainValidation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAM/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAAY;gBAEZ,IAAI,EAAE,UAAU;IAI5B;;;;;;;;OAQG;IACH,QAAQ,IAAI,kBAAkB;CAyF/B"}
@@ -3,11 +3,89 @@ import { AssociationValidation } from './validation/association_validation.js';
3
3
  import { EntityValidation } from './validation/entity_validation.js';
4
4
  import { PropertyValidation } from './validation/property_validation.js';
5
5
  import { SemanticValidation } from './validation/semantic_validation.js';
6
+ /**
7
+ * DomainValidation performs comprehensive validation on a data domain and its components.
8
+ *
9
+ * This class orchestrates validation across all domain elements including entities, properties,
10
+ * associations, and semantics. It ensures that the data domain is well-formed and follows
11
+ * established conventions before it can be published or used in API generation.
12
+ *
13
+ * ## Validation Scope
14
+ *
15
+ * The validation process covers:
16
+ * - **Entities**: Structure, naming, primary keys, and inheritance
17
+ * - **Properties**: Naming conventions, data types, and constraints
18
+ * - **Associations**: Relationships, targets, and naming
19
+ * - **Semantics**: Recommended patterns and best practices
20
+ *
21
+ * ## Validation Severity Levels
22
+ *
23
+ * - **Error**: Blocking issues that prevent domain publication
24
+ * - **Warning**: Issues that may cause problems but don't block publication
25
+ * - **Info**: Recommendations for best practices
26
+ *
27
+ * ## Usage
28
+ *
29
+ * ```typescript
30
+ * const domain = new DataDomain()
31
+ * // ... add entities, properties, associations
32
+ *
33
+ * const validator = new DomainValidation(domain)
34
+ * const report = validator.validate()
35
+ *
36
+ * if (report.canProceed) {
37
+ * // Domain is valid and can be published
38
+ * } else {
39
+ * // Handle validation errors
40
+ * console.log(report.impact)
41
+ * }
42
+ * ```
43
+ *
44
+ * ## Validation Rules
45
+ *
46
+ * ### Entity Validation Rules
47
+ * - **Primary Key**: Each entity must have a primary key (can be inherited)
48
+ * - **Minimum Properties**: Entities should have properties or associations (can be inherited)
49
+ * - **Naming**: Entity names must follow PostgreSQL naming conventions
50
+ * - **Uniqueness**: Entity names must be unique within the domain
51
+ *
52
+ * ### Property Validation Rules
53
+ * - **Naming**: Properties must follow PostgreSQL column naming conventions
54
+ * - **Length**: Names must be 2-59 characters long
55
+ * - **Format**: Names must start with letter/underscore, contain only alphanumeric/underscore
56
+ * - **Reserved Words**: Names cannot be PostgreSQL reserved keywords
57
+ * - **Case**: Snake case is recommended (lowercase with underscores)
58
+ *
59
+ * ### Association Validation Rules
60
+ * - **Targets**: Associations must have at least one target entity
61
+ * - **Target Existence**: Target entities must exist in the domain
62
+ * - **Naming**: Same rules as properties
63
+ *
64
+ * ### Semantic Validation Rules
65
+ * - **User Entity**: Recommended to have at least one entity with User semantic
66
+ * - **Timestamps**: Recommended to have CreatedTimestamp and UpdatedTimestamp properties
67
+ * - **Soft Delete**: Recommended to have soft delete capability for entities
68
+ * - **Data Types**: Semantic-specific data type validation
69
+ *
70
+ * @see {@link EntityValidation} for detailed entity validation rules
71
+ * @see {@link PropertyValidation} for detailed property validation rules
72
+ * @see {@link AssociationValidation} for detailed association validation rules
73
+ * @see {@link SemanticValidation} for detailed semantic validation rules
74
+ */
6
75
  export class DomainValidation {
7
76
  root;
8
77
  constructor(root) {
9
78
  this.root = root;
10
79
  }
80
+ /**
81
+ * Performs comprehensive validation on the entire data domain.
82
+ *
83
+ * This method validates all entities, properties, associations, and semantics
84
+ * in the domain. It returns a detailed report of all validation issues found,
85
+ * categorized by severity level.
86
+ *
87
+ * @returns A comprehensive validation report with all issues found
88
+ */
11
89
  validate() {
12
90
  const result = {
13
91
  key: '',
@@ -1 +1 @@
1
- {"version":3,"file":"DomainValidation.js","sourceRoot":"","sources":["../../../src/modeling/DomainValidation.ts"],"names":[],"mappings":"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","sourcesContent":["import { DataDomainKind } from '../models/kinds.js'\nimport { DataDomain } from './DataDomain.js'\nimport { DomainImpactReport } from './types.js'\nimport { AssociationValidation } from './validation/association_validation.js'\nimport { EntityValidation } from './validation/entity_validation.js'\nimport { PropertyValidation } from './validation/property_validation.js'\nimport { SemanticValidation } from './validation/semantic_validation.js'\n\nexport class DomainValidation {\n private root: DataDomain\n\n constructor(root: DataDomain) {\n this.root = root\n }\n\n validate(): DomainImpactReport {\n const result: DomainImpactReport = {\n key: '',\n kind: DataDomainKind,\n impact: [],\n canProceed: true,\n }\n const entityValidator = new EntityValidation(this.root)\n const propertyValidator = new PropertyValidation(this.root)\n const associationValidator = new AssociationValidation(this.root)\n const semanticValidator = new SemanticValidation(this.root)\n\n let hasEntities = false\n // Validate entities, properties, and associations\n for (const entity of this.root.listEntities()) {\n if (entity.domain.key !== this.root.key) {\n // we don't need to validate foreign entities\n continue\n }\n hasEntities = true\n const report = entityValidator.validate(entity)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n for (const property of entity.properties) {\n const report = propertyValidator.validate(property)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n }\n for (const association of entity.associations) {\n const report = associationValidator.validate(association)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n }\n }\n if (!hasEntities) {\n // no entities, no need to validate anything else\n return result\n }\n // Validate semantics\n const semanticReport = semanticValidator.validate()\n for (const item of semanticReport) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n\n return result\n }\n}\n"]}
1
+ {"version":3,"file":"DomainValidation.js","sourceRoot":"","sources":["../../../src/modeling/DomainValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAA;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAA;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,MAAM,OAAO,gBAAgB;IACnB,IAAI,CAAY;IAExB,YAAY,IAAgB;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAED;;;;;;;;OAQG;IACH,QAAQ;QACN,MAAM,MAAM,GAAuB;YACjC,GAAG,EAAE,EAAE;YACP,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,EAAE;YACV,UAAU,EAAE,IAAI;SACjB,CAAA;QACD,MAAM,eAAe,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACvD,MAAM,iBAAiB,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC3D,MAAM,oBAAoB,GAAG,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjE,MAAM,iBAAiB,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE3D,IAAI,WAAW,GAAG,KAAK,CAAA;QACvB,kDAAkD;QAClD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;YAC9C,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACxC,6CAA6C;gBAC7C,SAAQ;YACV,CAAC;YACD,WAAW,GAAG,IAAI,CAAA;YAClB,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;YAC/C,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;gBAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAA;gBAC1C,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAA;gBAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;oBACjB,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,IAAI,CAAC,OAAO;oBACpB,UAAU,EAAE,IAAI,CAAC,IAAI;oBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;iBACpB,CAAC,CAAA;YACJ,CAAC;YACD,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACzC,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;gBACnD,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;oBAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAA;oBAC1C,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAA;oBAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;wBACjB,GAAG,EAAE,IAAI,CAAC,GAAG;wBACb,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,IAAI,EAAE,SAAS;wBACf,MAAM,EAAE,IAAI,CAAC,OAAO;wBACpB,UAAU,EAAE,IAAI,CAAC,IAAI;wBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;qBACpB,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;YACD,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;gBAC9C,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;gBACzD,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;oBAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAA;oBAC1C,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAA;oBAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;wBACjB,GAAG,EAAE,IAAI,CAAC,GAAG;wBACb,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,IAAI,EAAE,SAAS;wBACf,MAAM,EAAE,IAAI,CAAC,OAAO;wBACpB,UAAU,EAAE,IAAI,CAAC,IAAI;wBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;qBACpB,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,iDAAiD;YACjD,OAAO,MAAM,CAAA;QACf,CAAC;QACD,qBAAqB;QACrB,MAAM,cAAc,GAAG,iBAAiB,CAAC,QAAQ,EAAE,CAAA;QACnD,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAA;YAC1C,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAA;YAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;gBACjB,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,IAAI,CAAC,OAAO;gBACpB,UAAU,EAAE,IAAI,CAAC,IAAI;gBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;CACF","sourcesContent":["import { DataDomainKind } from '../models/kinds.js'\nimport { DataDomain } from './DataDomain.js'\nimport { DomainImpactReport } from './types.js'\nimport { AssociationValidation } from './validation/association_validation.js'\nimport { EntityValidation } from './validation/entity_validation.js'\nimport { PropertyValidation } from './validation/property_validation.js'\nimport { SemanticValidation } from './validation/semantic_validation.js'\n\n/**\n * DomainValidation performs comprehensive validation on a data domain and its components.\n *\n * This class orchestrates validation across all domain elements including entities, properties,\n * associations, and semantics. It ensures that the data domain is well-formed and follows\n * established conventions before it can be published or used in API generation.\n *\n * ## Validation Scope\n *\n * The validation process covers:\n * - **Entities**: Structure, naming, primary keys, and inheritance\n * - **Properties**: Naming conventions, data types, and constraints\n * - **Associations**: Relationships, targets, and naming\n * - **Semantics**: Recommended patterns and best practices\n *\n * ## Validation Severity Levels\n *\n * - **Error**: Blocking issues that prevent domain publication\n * - **Warning**: Issues that may cause problems but don't block publication\n * - **Info**: Recommendations for best practices\n *\n * ## Usage\n *\n * ```typescript\n * const domain = new DataDomain()\n * // ... add entities, properties, associations\n *\n * const validator = new DomainValidation(domain)\n * const report = validator.validate()\n *\n * if (report.canProceed) {\n * // Domain is valid and can be published\n * } else {\n * // Handle validation errors\n * console.log(report.impact)\n * }\n * ```\n *\n * ## Validation Rules\n *\n * ### Entity Validation Rules\n * - **Primary Key**: Each entity must have a primary key (can be inherited)\n * - **Minimum Properties**: Entities should have properties or associations (can be inherited)\n * - **Naming**: Entity names must follow PostgreSQL naming conventions\n * - **Uniqueness**: Entity names must be unique within the domain\n *\n * ### Property Validation Rules\n * - **Naming**: Properties must follow PostgreSQL column naming conventions\n * - **Length**: Names must be 2-59 characters long\n * - **Format**: Names must start with letter/underscore, contain only alphanumeric/underscore\n * - **Reserved Words**: Names cannot be PostgreSQL reserved keywords\n * - **Case**: Snake case is recommended (lowercase with underscores)\n *\n * ### Association Validation Rules\n * - **Targets**: Associations must have at least one target entity\n * - **Target Existence**: Target entities must exist in the domain\n * - **Naming**: Same rules as properties\n *\n * ### Semantic Validation Rules\n * - **User Entity**: Recommended to have at least one entity with User semantic\n * - **Timestamps**: Recommended to have CreatedTimestamp and UpdatedTimestamp properties\n * - **Soft Delete**: Recommended to have soft delete capability for entities\n * - **Data Types**: Semantic-specific data type validation\n *\n * @see {@link EntityValidation} for detailed entity validation rules\n * @see {@link PropertyValidation} for detailed property validation rules\n * @see {@link AssociationValidation} for detailed association validation rules\n * @see {@link SemanticValidation} for detailed semantic validation rules\n */\nexport class DomainValidation {\n private root: DataDomain\n\n constructor(root: DataDomain) {\n this.root = root\n }\n\n /**\n * Performs comprehensive validation on the entire data domain.\n *\n * This method validates all entities, properties, associations, and semantics\n * in the domain. It returns a detailed report of all validation issues found,\n * categorized by severity level.\n *\n * @returns A comprehensive validation report with all issues found\n */\n validate(): DomainImpactReport {\n const result: DomainImpactReport = {\n key: '',\n kind: DataDomainKind,\n impact: [],\n canProceed: true,\n }\n const entityValidator = new EntityValidation(this.root)\n const propertyValidator = new PropertyValidation(this.root)\n const associationValidator = new AssociationValidation(this.root)\n const semanticValidator = new SemanticValidation(this.root)\n\n let hasEntities = false\n // Validate entities, properties, and associations\n for (const entity of this.root.listEntities()) {\n if (entity.domain.key !== this.root.key) {\n // we don't need to validate foreign entities\n continue\n }\n hasEntities = true\n const report = entityValidator.validate(entity)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n for (const property of entity.properties) {\n const report = propertyValidator.validate(property)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n }\n for (const association of entity.associations) {\n const report = associationValidator.validate(association)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n }\n }\n if (!hasEntities) {\n // no entities, no need to validate anything else\n return result\n }\n // Validate semantics\n const semanticReport = semanticValidator.validate()\n for (const item of semanticReport) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n\n return result\n }\n}\n"]}
@@ -0,0 +1,84 @@
1
+ import type { JSONSchema7 } from 'json-schema';
2
+ import type { JsonImportReport, InMemorySchema } from './JsonSchemaImporter.js';
3
+ import { JsonSchemaImporter } from './JsonSchemaImporter.js';
4
+ import { SchemaFilteringStrategy, type SchemaFilteringOptions, FILTERING_STRATEGIES } from './SchemaFilteringStrategy.js';
5
+ import type { DataDomain } from '../DataDomain.js';
6
+ /**
7
+ * Extended JsonSchemaImporter that supports filtering out non-structural schemas.
8
+ * This version can eliminate schemas that don't contribute meaningful structure
9
+ * to the data model, such as empty objects or conceptual marker types.
10
+ */
11
+ export declare class FilteringJsonSchemaImporter extends JsonSchemaImporter {
12
+ private filteringStrategy;
13
+ constructor(domain: DataDomain, filteringOptions?: SchemaFilteringOptions);
14
+ /**
15
+ * Creates an importer with a predefined filtering strategy.
16
+ */
17
+ static withStrategy(domain: DataDomain, strategyName: keyof typeof FILTERING_STRATEGIES): FilteringJsonSchemaImporter;
18
+ /**
19
+ * Creates an importer optimized for API development.
20
+ * Excludes schemas that don't contribute to API structure.
21
+ */
22
+ static forApiModeling(domain: DataDomain): FilteringJsonSchemaImporter;
23
+ /**
24
+ * Creates an importer optimized for database schema generation.
25
+ * Very strict filtering - only includes schemas with concrete properties.
26
+ */
27
+ static forDatabaseModeling(domain: DataDomain): FilteringJsonSchemaImporter;
28
+ /**
29
+ * Override the base import method to apply filtering.
30
+ */
31
+ import(schemas: InMemorySchema[], modelName: string): Promise<JsonImportReport>;
32
+ /**
33
+ * Pre-filters schemas before they are processed by the base importer.
34
+ */
35
+ private preFilterSchemas;
36
+ /**
37
+ * Post-processes the import report to clean up any remaining filtered entities.
38
+ */
39
+ private postFilterReport;
40
+ /**
41
+ * Determines if an entity should be filtered out based on its characteristics.
42
+ */
43
+ private shouldFilterEntity;
44
+ /**
45
+ * Gets a human-readable reason for filtering an entity.
46
+ */
47
+ private getEntityFilterReason;
48
+ /**
49
+ * Removes associations that reference entities that were filtered out.
50
+ */
51
+ private cleanupOrphanedAssociations;
52
+ }
53
+ /**
54
+ * Utility functions for working with filtered imports.
55
+ */
56
+ export declare const SchemaFilteringUtils: {
57
+ /**
58
+ * Analyzes a JSON schema to determine if it would be filtered by the given strategy.
59
+ */
60
+ wouldBeFiltered(schema: JSONSchema7, strategy: SchemaFilteringStrategy): boolean;
61
+ /**
62
+ * Gets statistics about what would be filtered from a set of schemas.
63
+ */
64
+ getFilteringStats(schemas: {
65
+ id: string;
66
+ schema: JSONSchema7;
67
+ }[], strategy: SchemaFilteringStrategy): {
68
+ total: number;
69
+ filtered: number;
70
+ remaining: number;
71
+ filteredSchemas: {
72
+ id: string;
73
+ reason: string;
74
+ }[];
75
+ };
76
+ /**
77
+ * Creates a report showing what would be filtered from a schema collection.
78
+ */
79
+ createFilteringReport(schemas: {
80
+ id: string;
81
+ schema: JSONSchema7;
82
+ }[], strategy: SchemaFilteringStrategy): string;
83
+ };
84
+ //# sourceMappingURL=FilteringJsonSchemaImporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilteringJsonSchemaImporter.d.ts","sourceRoot":"","sources":["../../../../src/modeling/importers/FilteringJsonSchemaImporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EACL,uBAAuB,EACvB,KAAK,sBAAsB,EAC3B,oBAAoB,EACrB,MAAM,8BAA8B,CAAA;AACrC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAIlD;;;;GAIG;AACH,qBAAa,2BAA4B,SAAQ,kBAAkB;IACjE,OAAO,CAAC,iBAAiB,CAAyB;gBAEtC,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAE,sBAA2B;IAK7E;;OAEG;IACH,MAAM,CAAC,YAAY,CACjB,MAAM,EAAE,UAAU,EAClB,YAAY,EAAE,MAAM,OAAO,oBAAoB,GAC9C,2BAA2B;IAO9B;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,2BAA2B;IAWtE;;;OAGG;IACH,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,2BAA2B;IAkB3E;;OAEG;IACY,MAAM,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAa9F;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgCxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAsDxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoB1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAW7B;;OAEG;IACH,OAAO,CAAC,2BAA2B;CAkCpC;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB;IAC/B;;OAEG;4BACqB,WAAW,YAAY,uBAAuB,GAAG,OAAO;IAIhF;;OAEG;+BAEQ;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,EAAE,YACpC,uBAAuB,GAChC;QACD,KAAK,EAAE,MAAM,CAAA;QACb,QAAQ,EAAE,MAAM,CAAA;QAChB,SAAS,EAAE,MAAM,CAAA;QACjB,eAAe,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAClD;IAoBD;;OAEG;mCAC4B;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,EAAE,YAAY,uBAAuB,GAAG,MAAM;CAmBjH,CAAA"}
@@ -0,0 +1,254 @@
1
+ import { JsonSchemaImporter } from './JsonSchemaImporter.js';
2
+ import { SchemaFilteringStrategy, FILTERING_STRATEGIES, } from './SchemaFilteringStrategy.js';
3
+ /**
4
+ * Extended JsonSchemaImporter that supports filtering out non-structural schemas.
5
+ * This version can eliminate schemas that don't contribute meaningful structure
6
+ * to the data model, such as empty objects or conceptual marker types.
7
+ */
8
+ export class FilteringJsonSchemaImporter extends JsonSchemaImporter {
9
+ filteringStrategy;
10
+ constructor(domain, filteringOptions = {}) {
11
+ super(domain);
12
+ this.filteringStrategy = new SchemaFilteringStrategy(filteringOptions);
13
+ }
14
+ /**
15
+ * Creates an importer with a predefined filtering strategy.
16
+ */
17
+ static withStrategy(domain, strategyName) {
18
+ const strategy = FILTERING_STRATEGIES[strategyName];
19
+ // Access private options through a public method (would need to be added to SchemaFilteringStrategy)
20
+ const options = strategy['options'];
21
+ return new FilteringJsonSchemaImporter(domain, options);
22
+ }
23
+ /**
24
+ * Creates an importer optimized for API development.
25
+ * Excludes schemas that don't contribute to API structure.
26
+ */
27
+ static forApiModeling(domain) {
28
+ return new FilteringJsonSchemaImporter(domain, {
29
+ excludeEmptyObjects: true,
30
+ excludeConceptualMarkers: true,
31
+ excludePatterns: [
32
+ /.*Enumeration$/, // Exclude *Enumeration schemas
33
+ /.*Type$/, // Exclude conceptual *Type schemas that are just markers
34
+ ],
35
+ });
36
+ }
37
+ /**
38
+ * Creates an importer optimized for database schema generation.
39
+ * Very strict filtering - only includes schemas with concrete properties.
40
+ */
41
+ static forDatabaseModeling(domain) {
42
+ return new FilteringJsonSchemaImporter(domain, {
43
+ excludeEmptyObjects: true,
44
+ excludeConceptualMarkers: true,
45
+ customExclusionFilter: (schema) => {
46
+ // For database modeling, exclude anything that doesn't have properties or clear structure
47
+ return (schema.type === 'object' &&
48
+ !schema.properties &&
49
+ !schema.additionalProperties &&
50
+ !schema.allOf?.some((subSchema) => typeof subSchema === 'object' && 'properties' in subSchema && subSchema.properties));
51
+ },
52
+ });
53
+ }
54
+ /**
55
+ * Override the base import method to apply filtering.
56
+ */
57
+ async import(schemas, modelName) {
58
+ // Apply pre-filtering to the schemas before import
59
+ const filteredSchemas = this.preFilterSchemas(schemas);
60
+ // Get the raw import result with filtered schemas
61
+ const rawResult = await super.import(filteredSchemas.schemas, modelName);
62
+ // Apply post-filtering to clean up any remaining issues
63
+ const finalReport = this.postFilterReport(rawResult, filteredSchemas.messages);
64
+ return finalReport;
65
+ }
66
+ /**
67
+ * Pre-filters schemas before they are processed by the base importer.
68
+ */
69
+ preFilterSchemas(schemas) {
70
+ const filteredSchemas = [];
71
+ const messages = [];
72
+ for (const schema of schemas) {
73
+ const schemaId = schema.contents.$id || schema.path;
74
+ if (this.filteringStrategy.shouldExcludeSchema(schema.contents, schemaId)) {
75
+ const reason = this.filteringStrategy.getExclusionReason(schema.contents, schemaId);
76
+ messages.push({
77
+ level: 'info',
78
+ message: `Pre-filtered schema '${schemaId}': ${reason}`,
79
+ location: schema.path,
80
+ });
81
+ }
82
+ else {
83
+ filteredSchemas.push(schema);
84
+ }
85
+ }
86
+ if (messages.length > 0) {
87
+ messages.push({
88
+ level: 'info',
89
+ message: `Pre-filtering removed ${messages.length} non-structural schema(s)`,
90
+ });
91
+ }
92
+ return { schemas: filteredSchemas, messages };
93
+ }
94
+ /**
95
+ * Post-processes the import report to clean up any remaining filtered entities.
96
+ */
97
+ postFilterReport(report, preFilterMessages) {
98
+ const { model, messages } = report;
99
+ const allMessages = [...preFilterMessages, ...messages];
100
+ let removedCount = 0;
101
+ // Get entities as array for easier processing
102
+ const entities = Array.from(model.listEntities());
103
+ const entitiesToRemove = [];
104
+ // Check each entity to see if it should be filtered out
105
+ for (const entity of entities) {
106
+ const shouldFilter = this.shouldFilterEntity(entity);
107
+ if (shouldFilter) {
108
+ entitiesToRemove.push(entity.key);
109
+ const reason = this.getEntityFilterReason(entity);
110
+ allMessages.push({
111
+ level: 'info',
112
+ message: `Post-filtered entity '${entity.info.name}': ${reason}`,
113
+ location: entity.key,
114
+ });
115
+ removedCount++;
116
+ }
117
+ }
118
+ // Remove the filtered entities
119
+ for (const entityKey of entitiesToRemove) {
120
+ try {
121
+ model.removeEntity(entityKey);
122
+ }
123
+ catch (error) {
124
+ allMessages.push({
125
+ level: 'warning',
126
+ message: `Failed to remove entity '${entityKey}': ${error}`,
127
+ location: entityKey,
128
+ });
129
+ }
130
+ }
131
+ // Clean up orphaned associations
132
+ this.cleanupOrphanedAssociations(entities, entitiesToRemove, allMessages);
133
+ if (removedCount > 0) {
134
+ allMessages.push({
135
+ level: 'info',
136
+ message: `Post-filtering removed ${removedCount} additional non-structural entity/entities`,
137
+ });
138
+ }
139
+ return {
140
+ model,
141
+ messages: allMessages,
142
+ };
143
+ }
144
+ /**
145
+ * Determines if an entity should be filtered out based on its characteristics.
146
+ */
147
+ shouldFilterEntity(entity) {
148
+ // Check if entity has no properties and no meaningful associations
149
+ const hasProperties = Array.from(entity.listProperties()).length > 0;
150
+ const hasAssociations = Array.from(entity.listAssociations()).length > 0;
151
+ // If it has neither properties nor associations, it's likely a conceptual marker
152
+ if (!hasProperties && !hasAssociations) {
153
+ return this.filteringStrategy.shouldExcludeSchema({
154
+ type: 'object',
155
+ title: entity.info.name,
156
+ description: entity.info.description,
157
+ }, entity.info.name);
158
+ }
159
+ return false;
160
+ }
161
+ /**
162
+ * Gets a human-readable reason for filtering an entity.
163
+ */
164
+ getEntityFilterReason(entity) {
165
+ const hasProperties = Array.from(entity.listProperties()).length > 0;
166
+ const hasAssociations = Array.from(entity.listAssociations()).length > 0;
167
+ if (!hasProperties && !hasAssociations) {
168
+ return 'Entity has no properties or associations (likely a conceptual marker)';
169
+ }
170
+ return 'Entity matches exclusion pattern';
171
+ }
172
+ /**
173
+ * Removes associations that reference entities that were filtered out.
174
+ */
175
+ cleanupOrphanedAssociations(entities, removedEntityKeys, messages) {
176
+ for (const entity of entities) {
177
+ const associationsToRemove = [];
178
+ for (const association of entity.listAssociations()) {
179
+ const hasOrphanedTargets = association.targets?.some((target) => removedEntityKeys.includes(target.key));
180
+ if (hasOrphanedTargets) {
181
+ associationsToRemove.push(association.key);
182
+ messages.push({
183
+ level: 'info',
184
+ message: `Removed association '${association.info.name}' from '${entity.info.name}' due to filtered target entity`,
185
+ location: `${entity.key}.${association.key}`,
186
+ });
187
+ }
188
+ }
189
+ for (const associationKey of associationsToRemove) {
190
+ try {
191
+ entity.removeAssociation(associationKey);
192
+ }
193
+ catch (error) {
194
+ messages.push({
195
+ level: 'warning',
196
+ message: `Failed to remove association '${associationKey}' from '${entity.info.name}': ${error}`,
197
+ location: `${entity.key}.${associationKey}`,
198
+ });
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ /**
205
+ * Utility functions for working with filtered imports.
206
+ */
207
+ export const SchemaFilteringUtils = {
208
+ /**
209
+ * Analyzes a JSON schema to determine if it would be filtered by the given strategy.
210
+ */
211
+ wouldBeFiltered(schema, strategy) {
212
+ return strategy.shouldExcludeSchema(schema);
213
+ },
214
+ /**
215
+ * Gets statistics about what would be filtered from a set of schemas.
216
+ */
217
+ getFilteringStats(schemas, strategy) {
218
+ const filteredSchemas = [];
219
+ for (const { id, schema } of schemas) {
220
+ if (strategy.shouldExcludeSchema(schema, id)) {
221
+ filteredSchemas.push({
222
+ id,
223
+ reason: strategy.getExclusionReason(schema, id),
224
+ });
225
+ }
226
+ }
227
+ return {
228
+ total: schemas.length,
229
+ filtered: filteredSchemas.length,
230
+ remaining: schemas.length - filteredSchemas.length,
231
+ filteredSchemas,
232
+ };
233
+ },
234
+ /**
235
+ * Creates a report showing what would be filtered from a schema collection.
236
+ */
237
+ createFilteringReport(schemas, strategy) {
238
+ const stats = this.getFilteringStats(schemas, strategy);
239
+ let report = `Schema Filtering Report\n`;
240
+ report += `======================\n\n`;
241
+ report += `Total schemas: ${stats.total}\n`;
242
+ report += `Would be filtered: ${stats.filtered}\n`;
243
+ report += `Would remain: ${stats.remaining}\n\n`;
244
+ if (stats.filteredSchemas.length > 0) {
245
+ report += `Schemas that would be filtered:\n`;
246
+ report += `-------------------------------\n`;
247
+ for (const { id, reason } of stats.filteredSchemas) {
248
+ report += `• ${id}: ${reason}\n`;
249
+ }
250
+ }
251
+ return report;
252
+ },
253
+ };
254
+ //# sourceMappingURL=FilteringJsonSchemaImporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilteringJsonSchemaImporter.js","sourceRoot":"","sources":["../../../../src/modeling/importers/FilteringJsonSchemaImporter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JSONSchema7 } from 'json-schema'\nimport type { JsonImportReport, InMemorySchema } from './JsonSchemaImporter.js'\nimport { JsonSchemaImporter } from './JsonSchemaImporter.js'\nimport {\n SchemaFilteringStrategy,\n type SchemaFilteringOptions,\n FILTERING_STRATEGIES,\n} from './SchemaFilteringStrategy.js'\nimport type { DataDomain } from '../DataDomain.js'\nimport type { DomainEntity } from '../DomainEntity.js'\nimport type { ImportMessage } from './JsonSchemaImporter.js'\n\n/**\n * Extended JsonSchemaImporter that supports filtering out non-structural schemas.\n * This version can eliminate schemas that don't contribute meaningful structure\n * to the data model, such as empty objects or conceptual marker types.\n */\nexport class FilteringJsonSchemaImporter extends JsonSchemaImporter {\n private filteringStrategy: SchemaFilteringStrategy\n\n constructor(domain: DataDomain, filteringOptions: SchemaFilteringOptions = {}) {\n super(domain)\n this.filteringStrategy = new SchemaFilteringStrategy(filteringOptions)\n }\n\n /**\n * Creates an importer with a predefined filtering strategy.\n */\n static withStrategy(\n domain: DataDomain,\n strategyName: keyof typeof FILTERING_STRATEGIES\n ): FilteringJsonSchemaImporter {\n const strategy = FILTERING_STRATEGIES[strategyName]\n // Access private options through a public method (would need to be added to SchemaFilteringStrategy)\n const options = strategy['options'] as SchemaFilteringOptions\n return new FilteringJsonSchemaImporter(domain, options)\n }\n\n /**\n * Creates an importer optimized for API development.\n * Excludes schemas that don't contribute to API structure.\n */\n static forApiModeling(domain: DataDomain): FilteringJsonSchemaImporter {\n return new FilteringJsonSchemaImporter(domain, {\n excludeEmptyObjects: true,\n excludeConceptualMarkers: true,\n excludePatterns: [\n /.*Enumeration$/, // Exclude *Enumeration schemas\n /.*Type$/, // Exclude conceptual *Type schemas that are just markers\n ],\n })\n }\n\n /**\n * Creates an importer optimized for database schema generation.\n * Very strict filtering - only includes schemas with concrete properties.\n */\n static forDatabaseModeling(domain: DataDomain): FilteringJsonSchemaImporter {\n return new FilteringJsonSchemaImporter(domain, {\n excludeEmptyObjects: true,\n excludeConceptualMarkers: true,\n customExclusionFilter: (schema: JSONSchema7) => {\n // For database modeling, exclude anything that doesn't have properties or clear structure\n return (\n schema.type === 'object' &&\n !schema.properties &&\n !schema.additionalProperties &&\n !schema.allOf?.some(\n (subSchema) => typeof subSchema === 'object' && 'properties' in subSchema && subSchema.properties\n )\n )\n },\n })\n }\n\n /**\n * Override the base import method to apply filtering.\n */\n override async import(schemas: InMemorySchema[], modelName: string): Promise<JsonImportReport> {\n // Apply pre-filtering to the schemas before import\n const filteredSchemas = this.preFilterSchemas(schemas)\n\n // Get the raw import result with filtered schemas\n const rawResult = await super.import(filteredSchemas.schemas, modelName)\n\n // Apply post-filtering to clean up any remaining issues\n const finalReport = this.postFilterReport(rawResult, filteredSchemas.messages)\n\n return finalReport\n }\n\n /**\n * Pre-filters schemas before they are processed by the base importer.\n */\n private preFilterSchemas(schemas: InMemorySchema[]): {\n schemas: InMemorySchema[]\n messages: ImportMessage[]\n } {\n const filteredSchemas: InMemorySchema[] = []\n const messages: ImportMessage[] = []\n\n for (const schema of schemas) {\n const schemaId = schema.contents.$id || schema.path\n\n if (this.filteringStrategy.shouldExcludeSchema(schema.contents, schemaId)) {\n const reason = this.filteringStrategy.getExclusionReason(schema.contents, schemaId)\n messages.push({\n level: 'info',\n message: `Pre-filtered schema '${schemaId}': ${reason}`,\n location: schema.path,\n })\n } else {\n filteredSchemas.push(schema)\n }\n }\n\n if (messages.length > 0) {\n messages.push({\n level: 'info',\n message: `Pre-filtering removed ${messages.length} non-structural schema(s)`,\n })\n }\n\n return { schemas: filteredSchemas, messages }\n }\n\n /**\n * Post-processes the import report to clean up any remaining filtered entities.\n */\n private postFilterReport(report: JsonImportReport, preFilterMessages: ImportMessage[]): JsonImportReport {\n const { model, messages } = report\n const allMessages = [...preFilterMessages, ...messages]\n let removedCount = 0\n\n // Get entities as array for easier processing\n const entities = Array.from(model.listEntities())\n const entitiesToRemove: string[] = []\n\n // Check each entity to see if it should be filtered out\n for (const entity of entities) {\n const shouldFilter = this.shouldFilterEntity(entity)\n\n if (shouldFilter) {\n entitiesToRemove.push(entity.key)\n const reason = this.getEntityFilterReason(entity)\n allMessages.push({\n level: 'info',\n message: `Post-filtered entity '${entity.info.name}': ${reason}`,\n location: entity.key,\n })\n removedCount++\n }\n }\n\n // Remove the filtered entities\n for (const entityKey of entitiesToRemove) {\n try {\n model.removeEntity(entityKey)\n } catch (error) {\n allMessages.push({\n level: 'warning',\n message: `Failed to remove entity '${entityKey}': ${error}`,\n location: entityKey,\n })\n }\n }\n\n // Clean up orphaned associations\n this.cleanupOrphanedAssociations(entities, entitiesToRemove, allMessages)\n\n if (removedCount > 0) {\n allMessages.push({\n level: 'info',\n message: `Post-filtering removed ${removedCount} additional non-structural entity/entities`,\n })\n }\n\n return {\n model,\n messages: allMessages,\n }\n }\n\n /**\n * Determines if an entity should be filtered out based on its characteristics.\n */\n private shouldFilterEntity(entity: DomainEntity): boolean {\n // Check if entity has no properties and no meaningful associations\n const hasProperties = Array.from(entity.listProperties()).length > 0\n const hasAssociations = Array.from(entity.listAssociations()).length > 0\n\n // If it has neither properties nor associations, it's likely a conceptual marker\n if (!hasProperties && !hasAssociations) {\n return this.filteringStrategy.shouldExcludeSchema(\n {\n type: 'object',\n title: entity.info.name,\n description: entity.info.description,\n } as JSONSchema7,\n entity.info.name\n )\n }\n\n return false\n }\n\n /**\n * Gets a human-readable reason for filtering an entity.\n */\n private getEntityFilterReason(entity: DomainEntity): string {\n const hasProperties = Array.from(entity.listProperties()).length > 0\n const hasAssociations = Array.from(entity.listAssociations()).length > 0\n\n if (!hasProperties && !hasAssociations) {\n return 'Entity has no properties or associations (likely a conceptual marker)'\n }\n\n return 'Entity matches exclusion pattern'\n }\n\n /**\n * Removes associations that reference entities that were filtered out.\n */\n private cleanupOrphanedAssociations(\n entities: DomainEntity[],\n removedEntityKeys: string[],\n messages: ImportMessage[]\n ): void {\n for (const entity of entities) {\n const associationsToRemove: string[] = []\n\n for (const association of entity.listAssociations()) {\n const hasOrphanedTargets = association.targets?.some((target) => removedEntityKeys.includes(target.key))\n\n if (hasOrphanedTargets) {\n associationsToRemove.push(association.key)\n messages.push({\n level: 'info',\n message: `Removed association '${association.info.name}' from '${entity.info.name}' due to filtered target entity`,\n location: `${entity.key}.${association.key}`,\n })\n }\n }\n\n for (const associationKey of associationsToRemove) {\n try {\n entity.removeAssociation(associationKey)\n } catch (error) {\n messages.push({\n level: 'warning',\n message: `Failed to remove association '${associationKey}' from '${entity.info.name}': ${error}`,\n location: `${entity.key}.${associationKey}`,\n })\n }\n }\n }\n }\n}\n\n/**\n * Utility functions for working with filtered imports.\n */\nexport const SchemaFilteringUtils = {\n /**\n * Analyzes a JSON schema to determine if it would be filtered by the given strategy.\n */\n wouldBeFiltered(schema: JSONSchema7, strategy: SchemaFilteringStrategy): boolean {\n return strategy.shouldExcludeSchema(schema)\n },\n\n /**\n * Gets statistics about what would be filtered from a set of schemas.\n */\n getFilteringStats(\n schemas: { id: string; schema: JSONSchema7 }[],\n strategy: SchemaFilteringStrategy\n ): {\n total: number\n filtered: number\n remaining: number\n filteredSchemas: { id: string; reason: string }[]\n } {\n const filteredSchemas: { id: string; reason: string }[] = []\n\n for (const { id, schema } of schemas) {\n if (strategy.shouldExcludeSchema(schema, id)) {\n filteredSchemas.push({\n id,\n reason: strategy.getExclusionReason(schema, id),\n })\n }\n }\n\n return {\n total: schemas.length,\n filtered: filteredSchemas.length,\n remaining: schemas.length - filteredSchemas.length,\n filteredSchemas,\n }\n },\n\n /**\n * Creates a report showing what would be filtered from a schema collection.\n */\n createFilteringReport(schemas: { id: string; schema: JSONSchema7 }[], strategy: SchemaFilteringStrategy): string {\n const stats = this.getFilteringStats(schemas, strategy)\n\n let report = `Schema Filtering Report\\n`\n report += `======================\\n\\n`\n report += `Total schemas: ${stats.total}\\n`\n report += `Would be filtered: ${stats.filtered}\\n`\n report += `Would remain: ${stats.remaining}\\n\\n`\n\n if (stats.filteredSchemas.length > 0) {\n report += `Schemas that would be filtered:\\n`\n report += `-------------------------------\\n`\n for (const { id, reason } of stats.filteredSchemas) {\n report += `• ${id}: ${reason}\\n`\n }\n }\n\n return report\n },\n}\n"]}