@api-client/core 0.11.10 → 0.12.0

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 (252) hide show
  1. package/Testing.md +1 -1
  2. package/bin/plugins/events/EventPlugin.ts +61 -0
  3. package/bin/plugins/events/assert.ts +193 -0
  4. package/bin/plugins/events/types.ts +6 -0
  5. package/bin/test.ts +8 -1
  6. package/build/src/amf/AmfShapeGenerator.d.ts +6 -3
  7. package/build/src/amf/AmfShapeGenerator.d.ts.map +1 -1
  8. package/build/src/amf/AmfShapeGenerator.js +4 -1
  9. package/build/src/amf/AmfShapeGenerator.js.map +1 -1
  10. package/build/src/amf/AmfTypes.d.ts +2 -2
  11. package/build/src/amf/AmfTypes.d.ts.map +1 -1
  12. package/build/src/amf/AmfTypes.js.map +1 -1
  13. package/build/src/amf/DataValueGenerator.d.ts +15 -15
  14. package/build/src/amf/DataValueGenerator.d.ts.map +1 -1
  15. package/build/src/amf/DataValueGenerator.js +14 -14
  16. package/build/src/amf/DataValueGenerator.js.map +1 -1
  17. package/build/src/browser.d.ts +12 -9
  18. package/build/src/browser.d.ts.map +1 -1
  19. package/build/src/browser.js +11 -8
  20. package/build/src/browser.js.map +1 -1
  21. package/build/src/exceptions/attach_exception.d.ts +11 -0
  22. package/build/src/exceptions/attach_exception.d.ts.map +1 -0
  23. package/build/src/exceptions/attach_exception.js +11 -0
  24. package/build/src/exceptions/attach_exception.js.map +1 -0
  25. package/build/src/exceptions/detach_exception.d.ts +11 -0
  26. package/build/src/exceptions/detach_exception.d.ts.map +1 -0
  27. package/build/src/exceptions/detach_exception.js +11 -0
  28. package/build/src/exceptions/detach_exception.js.map +1 -0
  29. package/build/src/exceptions/remove_model_exception.d.ts +8 -0
  30. package/build/src/exceptions/remove_model_exception.d.ts.map +1 -0
  31. package/build/src/exceptions/remove_model_exception.js +8 -0
  32. package/build/src/exceptions/remove_model_exception.js.map +1 -0
  33. package/build/src/exceptions/remove_namespace_exception.d.ts +8 -0
  34. package/build/src/exceptions/remove_namespace_exception.d.ts.map +1 -0
  35. package/build/src/exceptions/remove_namespace_exception.js +8 -0
  36. package/build/src/exceptions/remove_namespace_exception.js.map +1 -0
  37. package/build/src/index.d.ts +12 -9
  38. package/build/src/index.d.ts.map +1 -1
  39. package/build/src/index.js +11 -8
  40. package/build/src/index.js.map +1 -1
  41. package/build/src/legacy.d.ts +8 -0
  42. package/build/src/legacy.d.ts.map +1 -1
  43. package/build/src/legacy.js +9 -0
  44. package/build/src/legacy.js.map +1 -1
  45. package/build/src/modeling/Bindings.d.ts +2 -2
  46. package/build/src/modeling/Bindings.d.ts.map +1 -1
  47. package/build/src/modeling/Bindings.js.map +1 -1
  48. package/build/src/modeling/DataDomain.d.ts +601 -0
  49. package/build/src/modeling/DataDomain.d.ts.map +1 -0
  50. package/build/src/modeling/DataDomain.js +1142 -0
  51. package/build/src/modeling/DataDomain.js.map +1 -0
  52. package/build/src/modeling/DataFormat.d.ts +42 -41
  53. package/build/src/modeling/DataFormat.d.ts.map +1 -1
  54. package/build/src/modeling/DataFormat.js +30 -131
  55. package/build/src/modeling/DataFormat.js.map +1 -1
  56. package/build/src/modeling/DomainAssociation.d.ts +281 -0
  57. package/build/src/modeling/DomainAssociation.d.ts.map +1 -0
  58. package/build/src/modeling/DomainAssociation.js +440 -0
  59. package/build/src/modeling/DomainAssociation.js.map +1 -0
  60. package/build/src/modeling/DomainElement.d.ts +33 -0
  61. package/build/src/modeling/DomainElement.d.ts.map +1 -0
  62. package/build/src/modeling/DomainElement.js +32 -0
  63. package/build/src/modeling/DomainElement.js.map +1 -0
  64. package/build/src/modeling/DomainEntity.d.ts +383 -0
  65. package/build/src/modeling/DomainEntity.d.ts.map +1 -0
  66. package/build/src/modeling/DomainEntity.js +718 -0
  67. package/build/src/modeling/DomainEntity.js.map +1 -0
  68. package/build/src/modeling/DomainFile.d.ts +25 -0
  69. package/build/src/modeling/DomainFile.d.ts.map +1 -0
  70. package/build/src/modeling/DomainFile.js +86 -0
  71. package/build/src/modeling/DomainFile.js.map +1 -0
  72. package/build/src/modeling/DomainImpactAnalysis.d.ts +60 -47
  73. package/build/src/modeling/DomainImpactAnalysis.d.ts.map +1 -1
  74. package/build/src/modeling/DomainImpactAnalysis.js +201 -132
  75. package/build/src/modeling/DomainImpactAnalysis.js.map +1 -1
  76. package/build/src/modeling/DomainModel.d.ts +226 -0
  77. package/build/src/modeling/DomainModel.d.ts.map +1 -0
  78. package/build/src/modeling/DomainModel.js +401 -0
  79. package/build/src/modeling/DomainModel.js.map +1 -0
  80. package/build/src/modeling/DomainNamespace.d.ts +268 -0
  81. package/build/src/modeling/DomainNamespace.d.ts.map +1 -0
  82. package/build/src/modeling/DomainNamespace.js +512 -0
  83. package/build/src/modeling/DomainNamespace.js.map +1 -0
  84. package/build/src/modeling/DomainProperty.d.ts +281 -0
  85. package/build/src/modeling/DomainProperty.d.ts.map +1 -0
  86. package/build/src/modeling/DomainProperty.js +560 -0
  87. package/build/src/modeling/DomainProperty.js.map +1 -0
  88. package/build/src/modeling/DomainSerialization.d.ts +40 -0
  89. package/build/src/modeling/DomainSerialization.d.ts.map +1 -0
  90. package/build/src/modeling/DomainSerialization.js +288 -0
  91. package/build/src/modeling/DomainSerialization.js.map +1 -0
  92. package/build/src/modeling/DomainVersioning.d.ts +17 -0
  93. package/build/src/modeling/DomainVersioning.d.ts.map +1 -0
  94. package/build/src/modeling/DomainVersioning.js +124 -0
  95. package/build/src/modeling/DomainVersioning.js.map +1 -0
  96. package/build/src/modeling/GraphUtils.d.ts +8 -0
  97. package/build/src/modeling/GraphUtils.d.ts.map +1 -0
  98. package/build/src/modeling/GraphUtils.js +26 -0
  99. package/build/src/modeling/GraphUtils.js.map +1 -0
  100. package/build/src/modeling/amf/ShapeGenerator.d.ts +164 -0
  101. package/build/src/modeling/amf/ShapeGenerator.d.ts.map +1 -0
  102. package/build/src/modeling/amf/ShapeGenerator.js +492 -0
  103. package/build/src/modeling/amf/ShapeGenerator.js.map +1 -0
  104. package/build/src/modeling/{DataAssociation.d.ts → legacy/DataAssociation.d.ts} +10 -5
  105. package/build/src/modeling/legacy/DataAssociation.d.ts.map +1 -0
  106. package/build/src/modeling/{DataAssociation.js → legacy/DataAssociation.js} +9 -6
  107. package/build/src/modeling/legacy/DataAssociation.js.map +1 -0
  108. package/build/src/modeling/{DataEntity.d.ts → legacy/DataEntity.d.ts} +12 -7
  109. package/build/src/modeling/legacy/DataEntity.d.ts.map +1 -0
  110. package/build/src/modeling/{DataEntity.js → legacy/DataEntity.js} +19 -18
  111. package/build/src/modeling/legacy/DataEntity.js.map +1 -0
  112. package/build/src/modeling/{DataEntityBuilder.d.ts → legacy/DataEntityBuilder.d.ts} +3 -2
  113. package/build/src/modeling/legacy/DataEntityBuilder.d.ts.map +1 -0
  114. package/build/src/modeling/{DataEntityBuilder.js → legacy/DataEntityBuilder.js} +3 -2
  115. package/build/src/modeling/legacy/DataEntityBuilder.js.map +1 -0
  116. package/build/src/modeling/legacy/DataImpactAnalysis.d.ts +298 -0
  117. package/build/src/modeling/legacy/DataImpactAnalysis.d.ts.map +1 -0
  118. package/build/src/modeling/legacy/DataImpactAnalysis.js +441 -0
  119. package/build/src/modeling/legacy/DataImpactAnalysis.js.map +1 -0
  120. package/build/src/modeling/{DataModel.d.ts → legacy/DataModel.d.ts} +6 -4
  121. package/build/src/modeling/legacy/DataModel.d.ts.map +1 -0
  122. package/build/src/modeling/{DataModel.js → legacy/DataModel.js} +7 -6
  123. package/build/src/modeling/legacy/DataModel.js.map +1 -0
  124. package/build/src/modeling/{DataNamespace.d.ts → legacy/DataNamespace.d.ts} +22 -3
  125. package/build/src/modeling/legacy/DataNamespace.d.ts.map +1 -0
  126. package/build/src/modeling/{DataNamespace.js → legacy/DataNamespace.js} +7 -3
  127. package/build/src/modeling/legacy/DataNamespace.js.map +1 -0
  128. package/build/src/modeling/{DataProperty.d.ts → legacy/DataProperty.d.ts} +13 -5
  129. package/build/src/modeling/legacy/DataProperty.d.ts.map +1 -0
  130. package/build/src/modeling/{DataProperty.js → legacy/DataProperty.js} +8 -5
  131. package/build/src/modeling/legacy/DataProperty.js.map +1 -0
  132. package/build/src/modeling/observed.d.ts +67 -0
  133. package/build/src/modeling/observed.d.ts.map +1 -0
  134. package/build/src/modeling/observed.js +230 -0
  135. package/build/src/modeling/observed.js.map +1 -0
  136. package/build/src/modeling/types.d.ts +165 -1
  137. package/build/src/modeling/types.d.ts.map +1 -1
  138. package/build/src/modeling/types.js.map +1 -1
  139. package/build/src/models/Thing.d.ts +26 -5
  140. package/build/src/models/Thing.d.ts.map +1 -1
  141. package/build/src/models/Thing.js +193 -91
  142. package/build/src/models/Thing.js.map +1 -1
  143. package/build/src/models/kinds.d.ts +31 -6
  144. package/build/src/models/kinds.d.ts.map +1 -1
  145. package/build/src/models/kinds.js +31 -6
  146. package/build/src/models/kinds.js.map +1 -1
  147. package/build/src/models/store/DataFile.d.ts +3 -1
  148. package/build/src/models/store/DataFile.d.ts.map +1 -1
  149. package/build/src/models/store/DataFile.js +2 -0
  150. package/build/src/models/store/DataFile.js.map +1 -1
  151. package/build/src/models/types.d.ts +12 -0
  152. package/build/src/models/types.d.ts.map +1 -0
  153. package/build/src/models/types.js +2 -0
  154. package/build/src/models/types.js.map +1 -0
  155. package/build/src/runtime/store/FilesSdk.d.ts +2 -2
  156. package/build/src/runtime/store/FilesSdk.d.ts.map +1 -1
  157. package/build/src/runtime/store/FilesSdk.js.map +1 -1
  158. package/data/models/example-generator-api.json +26 -26
  159. package/package.json +20 -6
  160. package/readme.md +1 -1
  161. package/src/amf/AmfShapeGenerator.ts +7 -4
  162. package/src/amf/AmfTypes.ts +2 -2
  163. package/src/amf/DataValueGenerator.ts +21 -21
  164. package/src/exceptions/attach_exception.ts +11 -0
  165. package/src/exceptions/detach_exception.ts +11 -0
  166. package/src/exceptions/remove_model_exception.ts +8 -0
  167. package/src/exceptions/remove_namespace_exception.ts +8 -0
  168. package/src/modeling/Bindings.ts +2 -2
  169. package/src/modeling/DataDomain.ts +1221 -0
  170. package/src/modeling/DataFormat.ts +54 -163
  171. package/src/modeling/DomainAssociation.ts +476 -0
  172. package/src/modeling/DomainElement.ts +50 -0
  173. package/src/modeling/DomainEntity.ts +769 -0
  174. package/src/modeling/DomainFile.ts +94 -0
  175. package/src/modeling/DomainImpactAnalysis.ts +218 -144
  176. package/src/modeling/DomainModel.ts +421 -0
  177. package/src/modeling/DomainNamespace.ts +537 -0
  178. package/src/modeling/DomainProperty.ts +548 -0
  179. package/src/modeling/DomainSerialization.ts +312 -0
  180. package/src/modeling/DomainVersioning.ts +144 -0
  181. package/src/modeling/GraphUtils.ts +28 -0
  182. package/src/modeling/amf/ShapeGenerator.ts +552 -0
  183. package/src/modeling/graph.md +115 -0
  184. package/src/modeling/{DataAssociation.ts → legacy/DataAssociation.ts} +13 -8
  185. package/src/modeling/{DataEntity.ts → legacy/DataEntity.ts} +28 -23
  186. package/src/modeling/{DataEntityBuilder.ts → legacy/DataEntityBuilder.ts} +4 -3
  187. package/src/modeling/legacy/DataImpactAnalysis.ts +530 -0
  188. package/src/modeling/{DataModel.ts → legacy/DataModel.ts} +10 -8
  189. package/src/modeling/{DataNamespace.ts → legacy/DataNamespace.ts} +23 -5
  190. package/src/modeling/{DataProperty.ts → legacy/DataProperty.ts} +15 -7
  191. package/src/modeling/observed.ts +267 -0
  192. package/src/modeling/types.ts +174 -1
  193. package/src/models/Thing.ts +70 -5
  194. package/src/models/kinds.ts +32 -6
  195. package/src/models/store/DataFile.ts +3 -1
  196. package/src/models/types.ts +11 -0
  197. package/src/runtime/store/FilesSdk.ts +2 -6
  198. package/tests/unit/amf/data_value_generator.spec.ts +15 -15
  199. package/tests/unit/legacy-transformers/ARC-legacy-import.spec.ts +1 -1
  200. package/tests/unit/modeling/amf/shape_generator.spec.ts +1174 -0
  201. package/tests/unit/modeling/data_domain.spec.ts +444 -0
  202. package/tests/unit/modeling/data_domain_associations.spec.ts +279 -0
  203. package/tests/unit/modeling/data_domain_change_observers.spec.ts +681 -0
  204. package/tests/unit/modeling/data_domain_entities.spec.ts +449 -0
  205. package/tests/unit/modeling/data_domain_foreign.spec.ts +355 -0
  206. package/tests/unit/modeling/data_domain_models.spec.ts +658 -0
  207. package/tests/unit/modeling/data_domain_namespaces.spec.ts +668 -0
  208. package/tests/unit/modeling/data_domain_property.spec.ts +264 -0
  209. package/tests/unit/modeling/data_domain_serialization.spec.ts +294 -0
  210. package/tests/unit/modeling/domain.property.spec.ts +822 -0
  211. package/tests/unit/modeling/domain_asociation.spec.ts +643 -0
  212. package/tests/unit/modeling/domain_asociation_targets.spec.ts +350 -0
  213. package/tests/unit/modeling/domain_entity.spec.ts +730 -0
  214. package/tests/unit/modeling/domain_entity_associations.spec.ts +330 -0
  215. package/tests/unit/modeling/domain_entity_example_generator_json.spec.ts +988 -0
  216. package/tests/unit/modeling/domain_entity_example_generator_xml.spec.ts +1451 -0
  217. package/tests/unit/modeling/domain_entity_fields.spec.ts +113 -0
  218. package/tests/unit/modeling/domain_entity_generators.spec.ts +20 -0
  219. package/tests/unit/modeling/domain_entity_parents.spec.ts +291 -0
  220. package/tests/unit/modeling/domain_entity_properties.spec.ts +305 -0
  221. package/tests/unit/modeling/{data_file.spec.ts → domain_file.spec.ts} +29 -85
  222. package/tests/unit/modeling/domain_impact_analysis.spec.ts +452 -0
  223. package/tests/unit/modeling/domain_model.spec.ts +568 -0
  224. package/tests/unit/modeling/domain_model_entities.spec.ts +408 -0
  225. package/tests/unit/modeling/domain_namespace.spec.ts +514 -0
  226. package/tests/unit/modeling/domain_namespace_models.spec.ts +324 -0
  227. package/tests/unit/modeling/domain_namespace_namespaces.spec.ts +404 -0
  228. package/tests/unit/modeling/domain_versioning.spec.ts +140 -0
  229. package/tests/unit/{amf → modeling/legacy}/amf_shape_generator.spec.ts +11 -11
  230. package/tests/unit/modeling/{data_association.spec.ts → legacy/data_association.spec.ts} +3 -11
  231. package/tests/unit/modeling/{data_entity.spec.ts → legacy/data_entity.spec.ts} +10 -8
  232. package/tests/unit/modeling/{data_entity_generator_json.spec.ts → legacy/data_entity_generator_json.spec.ts} +1 -1
  233. package/tests/unit/modeling/{data_entity_generator_xml.spec.ts → legacy/data_entity_generator_xml.spec.ts} +1 -1
  234. package/tests/unit/modeling/{data_model.spec.ts → legacy/data_model.spec.ts} +3 -3
  235. package/tests/unit/modeling/{data_namespace.spec.ts → legacy/data_namespace.spec.ts} +3 -10
  236. package/tests/unit/modeling/{data_property.spec.ts → legacy/data_property.spec.ts} +3 -6
  237. package/tests/unit/modeling/{impact_analysis.spec.ts → legacy/impact_analysis.spec.ts} +9 -9
  238. package/tests/unit/models/File/new.spec.ts +1 -1
  239. package/tests/unit/models/HttpProject.spec.ts +3 -3
  240. package/tsconfig.node.json +35 -0
  241. package/build/src/modeling/DataAssociation.d.ts.map +0 -1
  242. package/build/src/modeling/DataAssociation.js.map +0 -1
  243. package/build/src/modeling/DataEntity.d.ts.map +0 -1
  244. package/build/src/modeling/DataEntity.js.map +0 -1
  245. package/build/src/modeling/DataEntityBuilder.d.ts.map +0 -1
  246. package/build/src/modeling/DataEntityBuilder.js.map +0 -1
  247. package/build/src/modeling/DataModel.d.ts.map +0 -1
  248. package/build/src/modeling/DataModel.js.map +0 -1
  249. package/build/src/modeling/DataNamespace.d.ts.map +0 -1
  250. package/build/src/modeling/DataNamespace.js.map +0 -1
  251. package/build/src/modeling/DataProperty.d.ts.map +0 -1
  252. package/build/src/modeling/DataProperty.js.map +0 -1
@@ -0,0 +1,769 @@
1
+ import type { DataDomain } from './DataDomain.js'
2
+ import { DomainAssociationKind, DomainEntityKind, DomainPropertyKind } from '../models/kinds.js'
3
+ import { DomainElement, type DomainElementSchema } from './DomainElement.js'
4
+ import { nanoid } from 'nanoid'
5
+ import { type IThing, Thing } from '../models/Thing.js'
6
+ import { observed, retargetChange, toRaw } from './observed.js'
7
+ import type { IShapeUnion } from '../amf/definitions/Shapes.js'
8
+ import { DomainProperty, type DomainPropertySchema } from './DomainProperty.js'
9
+ import { RemovePropertyException } from '../exceptions/remove_property_exception.js'
10
+ import { DomainAssociation, DomainAssociationSchema } from './DomainAssociation.js'
11
+ import type { AssociationAddOptions } from './types.js'
12
+ import type { FileBreadcrumb } from '../models/store/File.js'
13
+ import type { DomainModel } from './DomainModel.js'
14
+ import type { DomainNamespace } from './DomainNamespace.js'
15
+ import { ValidationError } from '../exceptions/validation_error.js'
16
+ import { ShapeGenerator } from './amf/ShapeGenerator.js'
17
+ import type { IShapeRenderOptions } from '../amf/shape/ShapeBase.js'
18
+ import { ApiSchemaGenerator } from '../amf/ApiSchemaGenerator.js'
19
+ import { removeGraphNode } from './GraphUtils.js'
20
+
21
+ export interface EntityOrderedItem {
22
+ /**
23
+ * The type of the ordered item.
24
+ */
25
+ type: 'property' | 'association'
26
+
27
+ /**
28
+ * The key of the ordered item.
29
+ */
30
+ key: string
31
+ }
32
+
33
+ export interface DomainEntitySchema extends DomainElementSchema {
34
+ kind: typeof DomainEntityKind
35
+ /**
36
+ * The domain entity description.
37
+ */
38
+ info: IThing
39
+
40
+ /**
41
+ * Optional general purpose tags for the UI.
42
+ */
43
+ tags?: string[]
44
+
45
+ /**
46
+ * For future use.
47
+ *
48
+ * The keys of the taxonomy items associated with the entity.
49
+ */
50
+ taxonomy?: string[]
51
+
52
+ /**
53
+ * The ordered list of fields (properties and associations) in the schema.
54
+ * These only keep references to the properties and associations.
55
+ * The order of the fields is important as it defines the order of the
56
+ * properties in the schema.
57
+ */
58
+ fields?: EntityOrderedItem[]
59
+
60
+ /**
61
+ * Whether this entity is deprecated.
62
+ */
63
+ deprecated?: boolean
64
+ }
65
+
66
+ /**
67
+ * Represents an entity within a data domain model.
68
+ *
69
+ * A `DomainEntity` defines a structured data type,
70
+ * containing properties and associations. It's a
71
+ * fundamental building block for modeling complex data
72
+ * structures.
73
+ *
74
+ * **Key Features:**
75
+ *
76
+ * - **Properties:** Defines the data elements (fields) within the entity.
77
+ * - **Associations:** Defines relationships to other entities.
78
+ * - **Inheritance:** Supports inheriting properties and associations from parent entities.
79
+ * - **Metadata:** Supports tags, taxonomy, and deprecation status.
80
+ * - **AMF Shape Generation:** Can be serialized to an AMF (API Modeling Framework) shape.
81
+ * - **Example Generation:** Can generate example databased on its schema.
82
+ *
83
+ * **Usage:**
84
+ *
85
+ * The preferred way to create a `DomainEntity` is through
86
+ * the `DomainModel.addEntity()` method.
87
+ *
88
+ * **Example:**
89
+ *
90
+ * ```typescript
91
+ * const dataDomain = new DataDomain();
92
+ * const userModel = dataDomain.addModel({ key: 'userModel' });
93
+ * const userEntity = userModel.addEntity({ key: 'user' });
94
+ * ```
95
+ */
96
+ export class DomainEntity extends DomainElement {
97
+ /**
98
+ * The kind of the domain element.
99
+ */
100
+ override kind: typeof DomainEntityKind
101
+
102
+ /**
103
+ * The description of the domain entity.
104
+ */
105
+ @retargetChange() accessor info: Thing
106
+
107
+ /**
108
+ * Optional general purpose tags for the UI.
109
+ */
110
+ @observed({ deep: true }) accessor tags: string[]
111
+
112
+ /**
113
+ * Reserved for future use.
114
+ *
115
+ * The keys of the taxonomy items associated with the entity.
116
+ */
117
+ @observed({ deep: true }) accessor taxonomy: string[]
118
+
119
+ /**
120
+ * The ordered list of fields (properties and associations) in the schema.
121
+ * These only keep references to the properties and associations.
122
+ * The order of the fields is important as it defines the order of the
123
+ * properties in the schema.
124
+ */
125
+ accessor fields: EntityOrderedItem[]
126
+
127
+ /**
128
+ * Whether this entity is deprecated.
129
+ */
130
+ @observed() accessor deprecated: boolean | undefined
131
+
132
+ /**
133
+ * Creates a full data entity schema with defaults.
134
+ *
135
+ * @param input The partial data entity schema.
136
+ * @returns The data entity schema.
137
+ */
138
+ static createSchema(input: Partial<DomainEntitySchema> = {}): DomainEntitySchema {
139
+ const { key = nanoid(), tags, taxonomy, fields, deprecated } = input
140
+ const info = Thing.fromJSON(input.info, { name: 'New entity' }).toJSON()
141
+ const result: DomainEntitySchema = {
142
+ kind: DomainEntityKind,
143
+ key,
144
+ info,
145
+ }
146
+ if (Array.isArray(tags)) {
147
+ result.tags = [...tags]
148
+ }
149
+ if (Array.isArray(taxonomy)) {
150
+ result.taxonomy = [...taxonomy]
151
+ }
152
+ if (Array.isArray(fields)) {
153
+ result.fields = [...fields]
154
+ }
155
+ if (typeof deprecated === 'boolean') {
156
+ result.deprecated = deprecated
157
+ }
158
+ return result
159
+ }
160
+
161
+ /**
162
+ * Creates a new domain entity instance.
163
+ *
164
+ * You probably want to use the `DomainModel.addEntity()`
165
+ * method instead.
166
+ *
167
+ * @param root A reference to the root DataDomain instance.
168
+ * @param input The partial domain entity schema.
169
+ */
170
+ constructor(root: DataDomain, input?: Partial<DomainEntitySchema>) {
171
+ const init = DomainEntity.createSchema(input)
172
+ super(root, init.key)
173
+ this.kind = DomainEntityKind
174
+ this.info = new Thing(init.info)
175
+ if (Array.isArray(init.tags)) {
176
+ this.tags = [...init.tags]
177
+ } else {
178
+ this.tags = []
179
+ }
180
+ if (Array.isArray(init.taxonomy)) {
181
+ this.taxonomy = [...init.taxonomy]
182
+ } else {
183
+ this.taxonomy = []
184
+ }
185
+ if (Array.isArray(init.fields)) {
186
+ this.fields = [...init.fields]
187
+ } else {
188
+ this.fields = []
189
+ }
190
+ if (typeof init.deprecated === 'boolean') {
191
+ this.deprecated = init.deprecated
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Generates a JSON representation of the entity.
197
+ *
198
+ * @returns A plain object representation of the `DomainEntity`.
199
+ */
200
+ toJSON(): DomainEntitySchema {
201
+ const result: DomainEntitySchema = {
202
+ kind: this.kind,
203
+ key: this.key,
204
+ info: this.info.toJSON(),
205
+ }
206
+ if (Array.isArray(this.fields) && this.fields.length) {
207
+ result.fields = [...this.fields]
208
+ }
209
+ if (Array.isArray(this.taxonomy) && this.taxonomy.length) {
210
+ result.taxonomy = [...(toRaw(this, this.taxonomy) as string[])]
211
+ }
212
+ if (Array.isArray(this.tags) && this.tags.length) {
213
+ result.tags = [...(toRaw(this, this.tags) as string[])]
214
+ }
215
+ if (typeof this.deprecated === 'boolean') {
216
+ result.deprecated = this.deprecated
217
+ }
218
+ return result
219
+ }
220
+
221
+ /**
222
+ * Returns a parent data entity where this data property
223
+ * exist.
224
+ *
225
+ * @returns The parent `DomainModel` or undefined if not found.
226
+ * @example
227
+ * ```typescript
228
+ * const parent = entity.getParentInstance();
229
+ * if (parent) {
230
+ * console.log(parent.key);
231
+ * }
232
+ * ```
233
+ */
234
+ getParentInstance(): DomainModel | undefined {
235
+ const parentKey = this.root.graph.parent(this.key)
236
+ if (!parentKey) {
237
+ return undefined
238
+ }
239
+ return this.root.graph.node(parentKey) as DomainModel | undefined
240
+ }
241
+
242
+ /**
243
+ * Adds a property to this entity.
244
+ *
245
+ * @param property The partial property schema.
246
+ * @returns The created `DomainProperty` instance.
247
+ * @example
248
+ * ```typescript
249
+ * const property = entity.addProperty({
250
+ * key: 'name',
251
+ * type: 'string',
252
+ * required: true,
253
+ * })
254
+ * ```
255
+ */
256
+ addProperty(property?: Partial<DomainPropertySchema>): DomainProperty {
257
+ if (property && property.key && this.root.graph.hasNode(property.key)) {
258
+ throw new Error(`Trying to add a property ${property.key}, but it already exists`)
259
+ }
260
+ const instance = new DomainProperty(this.root, this.key, property)
261
+ this.root.graph.setNode(instance.key, instance)
262
+ this.root.graph.setEdge(this.key, instance.key, { type: 'property' })
263
+ this.fields.push({
264
+ type: 'property',
265
+ key: instance.key,
266
+ })
267
+ this.root.notifyChange()
268
+ return instance
269
+ }
270
+
271
+ /**
272
+ * Removes a property from this entity.
273
+ *
274
+ * @param key The key of the property to remove.
275
+ * @throws {RemovePropertyException} When the property does not exist.
276
+ * @example
277
+ * ```typescript
278
+ * entity.removeProperty('name')
279
+ * ```
280
+ */
281
+ removeProperty(key: string): void {
282
+ if (!this.root.graph.hasNode(key)) {
283
+ throw new RemovePropertyException(`Trying to remove the ${key} property, but it doesn't exist`)
284
+ }
285
+ const prop = this.root.graph.node(key) as DomainProperty
286
+ if (prop.getParentInstance() !== this) {
287
+ throw new RemovePropertyException(`Trying to remove the ${key} property, but it doesn't belong to this entity`)
288
+ }
289
+ removeGraphNode(this.root.graph, key)
290
+ this.removeField(key)
291
+ this.root.notifyChange()
292
+ }
293
+
294
+ /**
295
+ * Lists all properties of this entity.
296
+ *
297
+ * Note, it accounts for the order of the properties as
298
+ * defined in the `fields` array.
299
+ *
300
+ * @returns A generator that yields each `DomainProperty`.
301
+ * @example
302
+ * ```typescript
303
+ * for (const property of entity.listProperties()) {
304
+ * console.log(property.key);
305
+ * }
306
+ * ```
307
+ */
308
+ *listProperties(): Generator<DomainProperty> {
309
+ for (const { key, type } of this.fields) {
310
+ if (type !== 'property') {
311
+ continue
312
+ }
313
+ const node = this.root.graph.node(key)
314
+ if (!node || node.kind !== DomainPropertyKind) {
315
+ continue
316
+ }
317
+ yield node
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Checks if this entity has any properties.
323
+ *
324
+ * @returns True if the entity has properties.
325
+ * @example
326
+ * ```typescript
327
+ * if (entity.hasProperties()) {
328
+ * // ...
329
+ * }
330
+ * ```
331
+ */
332
+ hasProperties(): boolean {
333
+ return this.fields.some((item) => item.type === 'property')
334
+ }
335
+
336
+ /**
337
+ * Lists all parent entities of this entity.
338
+ *
339
+ * @returns A generator that yields each parent `DomainEntity`.
340
+ * @example
341
+ * ```typescript
342
+ * for (const parent of entity.listParents()) {
343
+ * console.log(parent.key);
344
+ * }
345
+ * ```
346
+ */
347
+ *listParents(): Generator<DomainEntity> {
348
+ for (const edge of this.root.graph.outEdges(this.key)) {
349
+ const value = this.root.graph.edge(edge)
350
+ if (!value || value.type !== 'parent') {
351
+ continue
352
+ }
353
+ const parent = this.root.graph.node(edge.w) as DomainEntity
354
+ if (parent) {
355
+ yield parent
356
+ }
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Adds a parent to this entity.
362
+ *
363
+ * @param key The key of the parent entity.
364
+ * @param domain The key of the foreign domain the parent belongs to.
365
+ * @returns `this` for chaining.
366
+ * @throws ValidationError When the parent does not exist or a circular dependency is detected.
367
+ * @example
368
+ * ```typescript
369
+ * entity.addParent('baseEntity');
370
+ * ```
371
+ */
372
+ addParent(key: string, domain?: string): this {
373
+ const effectiveKey = domain ? `${domain}:${key}` : key
374
+ // Prevent adding self as parent
375
+ if (effectiveKey === this.key) {
376
+ const message = 'Entity cannot be a parent of itself'
377
+ throw new ValidationError(
378
+ [
379
+ {
380
+ field: 'parents',
381
+ message,
382
+ rule: 'circular',
383
+ },
384
+ ],
385
+ { message }
386
+ )
387
+ }
388
+ const { graph } = this.root
389
+ if (!graph.hasNode(effectiveKey)) {
390
+ let message = `Entity with key "${key}" not found`
391
+ if (domain) {
392
+ message += ` in domain "${domain}"`
393
+ }
394
+ throw new ValidationError(
395
+ [
396
+ {
397
+ field: 'parents',
398
+ message,
399
+ rule: 'required',
400
+ },
401
+ ],
402
+ { message }
403
+ )
404
+ }
405
+ // Check for circular inheritance
406
+ if (this.hasCircularParent(effectiveKey)) {
407
+ const message = `Circular inheritance detected. Cannot add ${key} as a parent`
408
+ throw new ValidationError(
409
+ [
410
+ {
411
+ field: 'parents',
412
+ message,
413
+ rule: 'circular',
414
+ },
415
+ ],
416
+ { message }
417
+ )
418
+ }
419
+ if (graph.outEdges(this.key, effectiveKey).next().value) {
420
+ const message = `Parent ${key} already exists`
421
+ throw new ValidationError(
422
+ [
423
+ {
424
+ field: 'parents',
425
+ message,
426
+ rule: 'unique',
427
+ },
428
+ ],
429
+ { message }
430
+ )
431
+ }
432
+ // Parents are associated through graph edges.
433
+ // Otherwise it would collide with the parent model.
434
+ if (domain) {
435
+ graph.setEdge(this.key, effectiveKey, { type: 'parent', domain, foreign: true })
436
+ } else {
437
+ graph.setEdge(this.key, effectiveKey, { type: 'parent' })
438
+ }
439
+ this.root.notifyChange()
440
+ return this
441
+ }
442
+
443
+ /**
444
+ * Removes a parent from this entity.
445
+ *
446
+ * @param key The key of the parent entity to remove.
447
+ * @returns `this` for chaining.
448
+ * @throws Error When the parent does not exist.
449
+ * @example
450
+ * ```typescript
451
+ * entity.removeParent('baseEntity');
452
+ * ```
453
+ */
454
+ removeParent(key: string): this {
455
+ const { graph } = this.root
456
+ if (!graph.hasEdge(this.key, key)) {
457
+ throw new Error(`Trying to remove a parent ${key} from ${this.key}, but it doesn't exist`)
458
+ }
459
+ graph.removeEdge(this.key, key)
460
+ this.root.notifyChange()
461
+ return this
462
+ }
463
+
464
+ /**
465
+ * Checks if this entity has any parents.
466
+ *
467
+ * @returns True if the entity has parents.
468
+ * @example
469
+ * ```typescript
470
+ * if (entity.hasParents()) {
471
+ * // ...
472
+ * }
473
+ * ```
474
+ */
475
+ hasParents(): boolean {
476
+ for (const edge of this.root.graph.outEdges(this.key)) {
477
+ if (this.root.graph.edge(edge)?.type === 'parent') {
478
+ return true
479
+ }
480
+ }
481
+ return false
482
+ }
483
+
484
+ /**
485
+ * Checks if the passed entity has a circular parent
486
+ * relationship when attempting to add a new parent.
487
+ *
488
+ * @param key The key of the parent being added.
489
+ * @returns True if adding this parent would create a
490
+ * circular relationship.
491
+ */
492
+ hasCircularParent(key: string): boolean {
493
+ const { graph } = this.root
494
+
495
+ // Check if new parent is one of the children.
496
+ for (const edge of graph.outEdges(key, this.key)) {
497
+ if (graph.edge(edge)?.type === 'parent') {
498
+ return true
499
+ }
500
+ }
501
+
502
+ // check if this entity is in parents chain.
503
+ let current: DomainEntity | undefined = graph.node(key) as DomainEntity | undefined
504
+ while (current) {
505
+ if (current.key === this.key) {
506
+ return true
507
+ }
508
+ const parents = [...current.listParents()]
509
+ if (parents.length === 0) {
510
+ break
511
+ }
512
+ // Assuming single inheritance
513
+ // TODO: Update this to check for multiple parents.
514
+ current = parents[0]
515
+ }
516
+
517
+ return false
518
+ }
519
+
520
+ /**
521
+ * Creates breadcrumbs from this entity to the root
522
+ * namespace.
523
+ *
524
+ * @returns An array of `FileBreadcrumb` instances.
525
+ * @example
526
+ * ```typescript
527
+ * const breadcrumbs = entity.breadcrumbs();
528
+ * console.log(breadcrumbs);
529
+ * ```
530
+ */
531
+ breadcrumbs(): FileBreadcrumb[] {
532
+ const result: FileBreadcrumb[] = []
533
+ result.push({
534
+ key: this.key,
535
+ name: this.info.getLabel('Unnamed entity'),
536
+ kind: DomainEntityKind,
537
+ })
538
+ const model = this.getParentInstance()
539
+ if (model) {
540
+ result.push({
541
+ key: model.key,
542
+ kind: model.kind,
543
+ name: model.info.getLabel('Unnamed model'),
544
+ })
545
+ let parent: DataDomain | DomainNamespace = model.getParentInstance()
546
+ while (parent && parent !== this.root) {
547
+ result.push({
548
+ key: parent.key,
549
+ kind: parent.kind,
550
+ name: parent.info.getLabel('Unnamed namespace'),
551
+ })
552
+ const typed = parent as DomainNamespace
553
+ parent = typed.getParentInstance()
554
+ }
555
+ }
556
+ result.push({
557
+ key: this.root.key,
558
+ name: this.root.info.getLabel('Unnamed data domain'),
559
+ kind: this.root.kind,
560
+ })
561
+ return result.reverse()
562
+ }
563
+
564
+ /**
565
+ * Adds an association to this entity.
566
+ *
567
+ * @param init The association options.
568
+ * @param input The partial association schema.
569
+ * @returns The created `DomainAssociation` instance.
570
+ * @throws Error When the target entity does not exist.
571
+ * @example
572
+ * ```typescript
573
+ * const association = entity.addAssociation({
574
+ * key: 'address',
575
+ * });
576
+ * ```
577
+ */
578
+ addAssociation(init: AssociationAddOptions = {}, input?: Partial<DomainAssociationSchema>): DomainAssociation {
579
+ const { graph } = this.root
580
+ if (init.domain && init.key) {
581
+ // target is foreign
582
+ const foreignKey = `${init.domain}:${init.key}`
583
+ if (!graph.hasNode(foreignKey)) {
584
+ throw new Error(`Foreign entity ${foreignKey} not found`)
585
+ }
586
+ } else if (init.key) {
587
+ if (!graph.hasNode(init.key)) {
588
+ throw new Error(`Target entity ${init.key} not found`)
589
+ }
590
+ }
591
+ const item = new DomainAssociation(this.root, this.key, input)
592
+ graph.setNode(item.key, item)
593
+ graph.setEdge(this.key, item.key, { type: 'association' })
594
+ if (init.key) {
595
+ item.addTarget(init.key, init.domain)
596
+ }
597
+ this.fields.push({ type: 'association', key: item.key })
598
+ this.root.notifyChange()
599
+ return item
600
+ }
601
+
602
+ /**
603
+ * Removes an association from this entity.
604
+ *
605
+ * @param key The key of the association to remove.
606
+ * @throws Error When the association does not exist.
607
+ * @example
608
+ * ```typescript
609
+ * entity.removeAssociation('address');
610
+ * ```
611
+ */
612
+ removeAssociation(key: string): void {
613
+ if (!this.root.graph.hasEdge(this.key, key)) {
614
+ throw new Error(`Unable to find a connection between this entity and the ${key} association`)
615
+ }
616
+ const edge = this.root.graph.edge(this.key, key)
617
+ if (!edge || edge.type !== 'association') {
618
+ throw new Error(`Association ${key} does not exist`)
619
+ }
620
+ removeGraphNode(this.root.graph, key)
621
+ this.removeField(key)
622
+ this.root.notifyChange()
623
+ }
624
+
625
+ /**
626
+ * Lists all associations of this entity.
627
+ *
628
+ * The order of the associations is defined in the `fields` array.
629
+ *
630
+ * @returns A generator that yields each
631
+ * `DomainAssociation`.
632
+ * @example
633
+ * ```typescript
634
+ * for (const association of entity.listAssociations()) {
635
+ * console.log(association.key);
636
+ * }
637
+ * ```
638
+ */
639
+ *listAssociations(): Generator<DomainAssociation> {
640
+ for (const { key, type } of this.fields) {
641
+ if (type !== 'association') {
642
+ continue
643
+ }
644
+ const node = this.root.graph.node(key)
645
+ if (!node || node.kind !== DomainAssociationKind) {
646
+ continue
647
+ }
648
+ yield node
649
+ }
650
+ }
651
+
652
+ /**
653
+ * Checks if this entity has any associations.
654
+ *
655
+ * @returns True if the entity has associations.
656
+ * @example
657
+ * ```typescript
658
+ * if (entity.hasAssociations()) {
659
+ * // ...
660
+ * }
661
+ * ```
662
+ */
663
+ hasAssociations(): boolean {
664
+ return this.fields.some((item) => item.type === 'association')
665
+ }
666
+
667
+ /**
668
+ * Lists all fields (properties and associations) of this entity.
669
+ *
670
+ * @returns A generator that yields each `DomainAssociation` or `DomainProperty`.
671
+ * @example
672
+ * ```typescript
673
+ * for (const field of entity.listFields()) {
674
+ * console.log(field.key);
675
+ * }
676
+ * ```
677
+ */
678
+ *listFields(): Generator<DomainAssociation | DomainProperty> {
679
+ for (const { key } of this.fields) {
680
+ const node = this.root.graph.node(key)
681
+ if (!node) {
682
+ continue
683
+ }
684
+ if (node.kind === DomainAssociationKind || node.kind === DomainPropertyKind) {
685
+ yield node
686
+ }
687
+ }
688
+ }
689
+
690
+ /**
691
+ * Checks if this entity has any fields (properties or associations).
692
+ *
693
+ * @returns True if the entity has fields.
694
+ * @example
695
+ * ```typescript
696
+ * if (entity.hasFields()) {
697
+ * // ...
698
+ * }
699
+ * ```
700
+ */
701
+ hasFields(): boolean {
702
+ return this.fields.length > 0
703
+ }
704
+
705
+ private removeField(key: string): void {
706
+ this.fields = this.fields.filter((item) => item.key !== key)
707
+ }
708
+
709
+ /**
710
+ * Creates an AMF's data Shape.
711
+ *
712
+ * The property itself is auto-generated. If the `schema` is defined then it is used
713
+ * as the `range` of the property. Otherwise basic shape is generated for the range.
714
+ *
715
+ * This is a preferred way of reading the AMF shape as this synchronizes changed
716
+ * data properties with the shape definition.
717
+ *
718
+ * @returns AMF property shape definition.
719
+ */
720
+ toApiShape(): IShapeUnion {
721
+ const serializer = new ShapeGenerator()
722
+ return serializer.entity(this)
723
+ }
724
+
725
+ /**
726
+ * Reads the schema of the Entity and generates an example for it.
727
+ *
728
+ * @param mime The mime type of the example.
729
+ * @param opts The example generation options.
730
+ * @returns The generated example.
731
+ * @example
732
+ * ```typescript
733
+ * const example = entity.toExample('application/json');
734
+ * console.log(example);
735
+ * ```
736
+ */
737
+ toExample(mime: string, opts: IShapeRenderOptions = {}): string | number | boolean | null | undefined {
738
+ const shape = this.toApiShape()
739
+ const generator = new ApiSchemaGenerator(mime, {
740
+ renderExamples: typeof opts.renderExamples === 'boolean' ? opts.renderExamples : true,
741
+ renderMocked: typeof opts.renderMocked === 'boolean' ? opts.renderMocked : true,
742
+ renderOptional: typeof opts.renderOptional === 'boolean' ? opts.renderOptional : true,
743
+ selectedUnions: opts.selectedUnions,
744
+ })
745
+ return generator.generate(shape)
746
+ }
747
+
748
+ /**
749
+ * Checks whether the entity is a child of the given namespace or data model.
750
+ * The relationship doesn't have to be direct, as long as the entity
751
+ * is in the hierarchy it will return true.
752
+ *
753
+ * @param key The key of the parent to check.
754
+ * @returns True if this entity is a child of the given namespace or data model.
755
+ */
756
+ isChildOf(key: string): boolean {
757
+ if (this.key === key) {
758
+ return false
759
+ }
760
+ const parent = this.getParentInstance()
761
+ if (!parent) {
762
+ return false
763
+ }
764
+ if (parent.key === key) {
765
+ return true
766
+ }
767
+ return parent.isChildOf(key)
768
+ }
769
+ }