@quadnix/octo 0.0.21 → 0.0.22

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 (131) hide show
  1. package/dist/app.type.d.ts +3 -1
  2. package/dist/app.type.js +7 -7
  3. package/dist/app.type.js.map +1 -1
  4. package/dist/decorators/action.decorator.d.ts +6 -6
  5. package/dist/decorators/action.decorator.js +9 -9
  6. package/dist/decorators/action.decorator.js.map +1 -1
  7. package/dist/decorators/anchor.decorator.js +1 -1
  8. package/dist/decorators/anchor.decorator.js.map +1 -1
  9. package/dist/decorators/factory.decorator.d.ts +34 -15
  10. package/dist/decorators/factory.decorator.js +36 -13
  11. package/dist/decorators/factory.decorator.js.map +1 -1
  12. package/dist/decorators/model.decorator.js +1 -1
  13. package/dist/decorators/model.decorator.js.map +1 -1
  14. package/dist/decorators/module.decorator.d.ts +36 -32
  15. package/dist/decorators/module.decorator.js +2 -2
  16. package/dist/decorators/module.decorator.js.map +1 -1
  17. package/dist/decorators/overlay.decorator.js +1 -1
  18. package/dist/decorators/overlay.decorator.js.map +1 -1
  19. package/dist/decorators/resource.decorator.js +1 -1
  20. package/dist/decorators/resource.decorator.js.map +1 -1
  21. package/dist/{decorators → functions/container}/container.d.ts +2 -33
  22. package/dist/{decorators → functions/container}/container.js +8 -63
  23. package/dist/functions/container/container.js.map +1 -0
  24. package/dist/{decorators → functions/container}/test-container.d.ts +2 -6
  25. package/dist/{decorators → functions/container}/test-container.js +7 -14
  26. package/dist/functions/container/test-container.js.map +1 -0
  27. package/dist/functions/dependency/dependency.d.ts +6 -6
  28. package/dist/functions/dependency/dependency.js.map +1 -1
  29. package/dist/functions/diff/diff-metadata.d.ts +2 -2
  30. package/dist/functions/diff/diff-metadata.js +1 -1
  31. package/dist/functions/diff/diff-metadata.js.map +1 -1
  32. package/dist/functions/diff/diff.d.ts +4 -4
  33. package/dist/functions/diff/diff.js +6 -10
  34. package/dist/functions/diff/diff.js.map +1 -1
  35. package/dist/functions/diff/diff.utility.d.ts +23 -23
  36. package/dist/functions/diff/diff.utility.js +23 -23
  37. package/dist/functions/diff/diff.utility.js.map +1 -1
  38. package/dist/functions/hook/commit.hook.js.map +1 -1
  39. package/dist/functions/hook/model-action.hook.js +1 -1
  40. package/dist/functions/hook/model-action.hook.js.map +1 -1
  41. package/dist/functions/node/node.abstract.d.ts +84 -0
  42. package/dist/functions/node/node.abstract.js +275 -0
  43. package/dist/functions/node/node.abstract.js.map +1 -0
  44. package/dist/functions/node/node.interface.d.ts +176 -0
  45. package/dist/functions/node/node.interface.js +2 -0
  46. package/dist/functions/node/node.interface.js.map +1 -0
  47. package/dist/index.d.ts +11 -12
  48. package/dist/index.js +10 -11
  49. package/dist/index.js.map +1 -1
  50. package/dist/main.d.ts +7 -9
  51. package/dist/main.js +51 -24
  52. package/dist/main.js.map +1 -1
  53. package/dist/models/app/app.model.d.ts +1 -3
  54. package/dist/models/app/app.model.js +5 -8
  55. package/dist/models/app/app.model.js.map +1 -1
  56. package/dist/models/deployment/deployment.model.d.ts +1 -3
  57. package/dist/models/deployment/deployment.model.js +5 -8
  58. package/dist/models/deployment/deployment.model.js.map +1 -1
  59. package/dist/models/environment/environment.model.d.ts +1 -3
  60. package/dist/models/environment/environment.model.js +5 -9
  61. package/dist/models/environment/environment.model.js.map +1 -1
  62. package/dist/models/execution/execution.model.d.ts +1 -3
  63. package/dist/models/execution/execution.model.js +2 -6
  64. package/dist/models/execution/execution.model.js.map +1 -1
  65. package/dist/models/image/image.model.d.ts +1 -3
  66. package/dist/models/image/image.model.js +5 -8
  67. package/dist/models/image/image.model.js.map +1 -1
  68. package/dist/models/model-action.interface.d.ts +2 -6
  69. package/dist/models/model.abstract.d.ts +7 -307
  70. package/dist/models/model.abstract.js +20 -560
  71. package/dist/models/model.abstract.js.map +1 -1
  72. package/dist/models/model.interface.d.ts +36 -21
  73. package/dist/models/pipeline/pipeline.model.d.ts +1 -3
  74. package/dist/models/pipeline/pipeline.model.js +5 -9
  75. package/dist/models/pipeline/pipeline.model.js.map +1 -1
  76. package/dist/models/region/region.model.d.ts +1 -3
  77. package/dist/models/region/region.model.js +5 -8
  78. package/dist/models/region/region.model.js.map +1 -1
  79. package/dist/models/server/server.model.d.ts +1 -3
  80. package/dist/models/server/server.model.js +5 -8
  81. package/dist/models/server/server.model.js.map +1 -1
  82. package/dist/models/service/service.model.d.ts +1 -3
  83. package/dist/models/service/service.model.js +3 -7
  84. package/dist/models/service/service.model.js.map +1 -1
  85. package/dist/models/subnet/subnet.model.d.ts +3 -4
  86. package/dist/models/subnet/subnet.model.js +9 -18
  87. package/dist/models/subnet/subnet.model.js.map +1 -1
  88. package/dist/modules/module.container.d.ts +2 -2
  89. package/dist/modules/module.container.js.map +1 -1
  90. package/dist/modules/test-module.container.d.ts +2 -2
  91. package/dist/modules/test-module.container.js +8 -10
  92. package/dist/modules/test-module.container.js.map +1 -1
  93. package/dist/overlays/overlay-data.repository.d.ts +2 -4
  94. package/dist/overlays/overlay-data.repository.js +8 -33
  95. package/dist/overlays/overlay-data.repository.js.map +1 -1
  96. package/dist/overlays/overlay.abstract.d.ts +6 -7
  97. package/dist/overlays/overlay.abstract.js +20 -42
  98. package/dist/overlays/overlay.abstract.js.map +1 -1
  99. package/dist/overlays/overlay.service.d.ts +0 -1
  100. package/dist/overlays/overlay.service.js +1 -5
  101. package/dist/overlays/overlay.service.js.map +1 -1
  102. package/dist/resources/resource-data.repository.d.ts +16 -6
  103. package/dist/resources/resource-data.repository.js +115 -15
  104. package/dist/resources/resource-data.repository.js.map +1 -1
  105. package/dist/resources/resource.abstract.d.ts +23 -6
  106. package/dist/resources/resource.abstract.js +144 -10
  107. package/dist/resources/resource.abstract.js.map +1 -1
  108. package/dist/resources/shared-resource.abstract.d.ts +2 -2
  109. package/dist/resources/shared-resource.abstract.js +2 -2
  110. package/dist/resources/shared-resource.abstract.js.map +1 -1
  111. package/dist/services/capture/capture.service.d.ts +1 -2
  112. package/dist/services/capture/capture.service.js +2 -2
  113. package/dist/services/capture/capture.service.js.map +1 -1
  114. package/dist/services/event/event.service.js +1 -1
  115. package/dist/services/event/event.service.js.map +1 -1
  116. package/dist/services/input/input.service.js +2 -2
  117. package/dist/services/input/input.service.js.map +1 -1
  118. package/dist/services/serialization/model/model-serialization.service.js +4 -11
  119. package/dist/services/serialization/model/model-serialization.service.js.map +1 -1
  120. package/dist/services/serialization/resource/resource-serialization.service.d.ts +4 -4
  121. package/dist/services/serialization/resource/resource-serialization.service.js +15 -18
  122. package/dist/services/serialization/resource/resource-serialization.service.js.map +1 -1
  123. package/dist/services/state-management/state-management.service.d.ts +1 -1
  124. package/dist/services/state-management/state-management.service.js +6 -7
  125. package/dist/services/state-management/state-management.service.js.map +1 -1
  126. package/dist/services/transaction/transaction.service.d.ts +2 -3
  127. package/dist/services/transaction/transaction.service.js +88 -90
  128. package/dist/services/transaction/transaction.service.js.map +1 -1
  129. package/package.json +1 -1
  130. package/dist/decorators/container.js.map +0 -1
  131. package/dist/decorators/test-container.js.map +0 -1
@@ -1,619 +1,79 @@
1
- import { ModelType } from '../app.type.js';
2
- import { Dependency } from '../functions/dependency/dependency.js';
1
+ import { NodeType } from '../app.type.js';
3
2
  import { Diff, DiffAction } from '../functions/diff/diff.js';
4
3
  import { DiffUtility } from '../functions/diff/diff.utility.js';
5
- /**
6
- * This is Octo's base class, and all other classes extend from it.
7
- * It helps provide a common set of methods and properties that is core to Octo.
8
- *
9
- * Octo is built as a graph, and this base class can be considered as a generic node.
10
- * Nodes can have children, and siblings, but only if connected via an edge, called {@link Dependency} here.
11
- *
12
- * From this base class, three more base classes are derived,
13
- * - [Models](/docs/fundamentals/models)
14
- * - [Resources](/docs/fundamentals/resources)
15
- * - [Overlays](/docs/fundamentals/overlay-and-anchor)
16
- *
17
- * Each of these derived base classes, using the base methods of this class,
18
- * define their own specific rules on how their graphs should be constructed.
19
- */
20
- export class AModel {
4
+ import { ANode } from '../functions/node/node.abstract.js';
5
+ export class AModel extends ANode {
21
6
  constructor() {
22
- this.MODEL_TYPE = ModelType.MODEL;
23
- this._deleteMarker = false;
7
+ super(...arguments);
8
+ this.NODE_TYPE = NodeType.MODEL;
24
9
  this.anchors = [];
25
- this.dependencies = [];
26
10
  }
27
- /**
28
- * To add an {@link Anchor}.
29
- *
30
- * Each node can store multiple anchors for reference.
31
- * These anchors don't necessarily need to be parented by self, but must be unique,
32
- * i.e. an anchor, identified by it's parent, cannot be added twice to self's list of anchors.
33
- */
34
11
  addAnchor(anchor) {
35
12
  const existingAnchor = this.getAnchor(anchor.anchorId, anchor.getParent());
36
13
  if (!existingAnchor) {
37
14
  this.anchors.push(anchor);
38
15
  }
39
16
  }
40
- /**
41
- * To add another node as a child of self.
42
- *
43
- * Every node must have a unique ID against other nodes of the same {@link MODEL_NAME}.
44
- * This ID is how Octo differentiates similar nodes, and keeps track of them.
45
- * But because each node is free to define their own fields,
46
- * the fields containing the ID are a necessary input to this method.
47
- *
48
- * @example
49
- * ```ts
50
- * const region = new Region('region');
51
- * const environment = new Environment('qa');
52
- * region.addChild('regionId', environment, 'environmentName');
53
- * ```
54
- * @param onField The field in self which contains the unique ID.
55
- * @param child The child node.
56
- * @param toField The field in child which contains the unique ID.
57
- * @returns
58
- * - childToParentDependency: The dependency edge from child to self.
59
- * - parentToChildDependency: The dependency edge from self to child.
60
- */
61
- addChild(onField, child, toField) {
62
- const cIndex = child.dependencies.findIndex((d) => Object.is(d.to, this));
63
- const pIndex = this.dependencies.findIndex((d) => Object.is(d.to, child));
64
- if (cIndex !== -1 || pIndex !== -1) {
65
- throw new Error('Dependency relationship already exists!');
66
- }
67
- const childToParentDependency = new Dependency(child, this);
68
- childToParentDependency.addBehavior(toField, DiffAction.ADD, onField, DiffAction.ADD);
69
- childToParentDependency.addBehavior(toField, DiffAction.ADD, onField, DiffAction.UPDATE);
70
- childToParentDependency.addChildRelationship(toField, onField);
71
- child.dependencies.push(childToParentDependency);
72
- const parentToChildDependency = new Dependency(this, child);
73
- parentToChildDependency.addBehavior(onField, DiffAction.DELETE, toField, DiffAction.DELETE);
74
- parentToChildDependency.addParentRelationship(onField, toField);
75
- this.dependencies.push(parentToChildDependency);
76
- return { childToParentDependency, parentToChildDependency };
77
- }
78
- /**
79
- * To add another node as a sibling of self.
80
- *
81
- * @example
82
- * ```ts
83
- * const region1 = new Region('region-1');
84
- * const region2 = new Region('region-2');
85
- * region1.addSibling(region2);
86
- * ```
87
- * @param to The sibling node.
88
- * @returns
89
- * - thatToThisDependency: The dependency edge from sibling to self.
90
- * - thisToThatDependency: The dependency edge from self to sibling.
91
- */
92
- addRelationship(to) {
93
- const thisToThatDependency = new Dependency(this, to);
94
- this.dependencies.push(thisToThatDependency);
95
- const thatToThisDependency = new Dependency(to, this);
96
- to.dependencies.push(thatToThisDependency);
97
- return { thatToThisDependency, thisToThatDependency };
98
- }
99
- /**
100
- * To derive the name of the field in self which contains the ID.
101
- *
102
- * @remarks This method does a best attempt search of the field name by traversing all dependencies of self.
103
- * But if the dependencies lack, or have not been added yet, this method might return `undefined`.
104
- * That does not mean that such a field does not exist,
105
- * but rather that it wasn't possible to derive the name at that point.
106
- * @returns The name of the field in self which contains the ID, or `undefined` if not found.
107
- */
108
17
  deriveDependencyField() {
109
- return this.dependencies.find((d) => d.getRelationship() !== undefined)?.getRelationship().onField;
18
+ return this.getDependencies()
19
+ .find((d) => d.getRelationship() !== undefined)
20
+ ?.getRelationship().onField;
110
21
  }
111
- /**
112
- * Compares and calculates the difference between the current version of self node, and the previous version.
113
- * - Recursively compares self children too.
114
- *
115
- * @param previous The previous version of self.
116
- */
117
- async diff(previous) {
22
+ async diff() {
118
23
  const diffs = [];
119
- const previousChildrenByModels = previous?.getChildren() ?? {};
120
24
  const currentChildrenByModels = this.getChildren();
121
- // Compare previous children with current children.
122
- for (const modelName of Object.keys(previousChildrenByModels)) {
123
- const previousChildren = previousChildrenByModels[modelName].map((d) => d.to);
124
- const currentChildren = currentChildrenByModels[modelName]?.map((d) => d.to) || [];
125
- const field = previousChildrenByModels[modelName][0].getRelationship().toField;
126
- const childrenDiffs = await DiffUtility.diffModels(previousChildren, currentChildren, field);
127
- diffs.push(...childrenDiffs);
128
- }
129
- // Add new children not in previous.
130
25
  for (const modelName of Object.keys(currentChildrenByModels)) {
131
- if (previousChildrenByModels.hasOwnProperty(modelName)) {
132
- continue;
133
- }
134
26
  const previousChildren = [];
135
27
  const currentChildren = currentChildrenByModels[modelName].map((d) => d.to);
136
28
  const field = currentChildrenByModels[modelName][0].getRelationship().toField;
137
- const childrenDiffs = await DiffUtility.diffModels(previousChildren, currentChildren, field);
29
+ const childrenDiffs = await DiffUtility.diffNodes(previousChildren, currentChildren, field);
138
30
  diffs.push(...childrenDiffs);
139
31
  }
140
- const previousSiblingsByModels = previous?.getSiblings() ?? {};
141
32
  const currentSiblingsByModels = this.getSiblings();
142
- // Compare previous siblings with current siblings.
143
- for (const modelName of Object.keys(previousSiblingsByModels)) {
144
- const previousSiblings = previousSiblingsByModels[modelName];
145
- const currentSiblings = currentSiblingsByModels[modelName] || [];
146
- for (const pd of previousSiblings) {
147
- // Skip OVERLAY sibling, since overlays are diffed separately.
148
- if (pd.to.MODEL_TYPE === ModelType.OVERLAY) {
149
- continue;
150
- }
151
- if (!currentSiblings.find((cd) => cd.to.getContext() === pd.to.getContext())) {
152
- diffs.push(new Diff(this, DiffAction.DELETE, 'sibling', pd.to));
153
- }
154
- }
155
- for (const cd of currentSiblings) {
156
- // Skip OVERLAY sibling, since overlays are diffed separately.
157
- if (cd.to.MODEL_TYPE === ModelType.OVERLAY) {
158
- continue;
159
- }
160
- if (!previousSiblings.find((pd) => pd.to.getContext() === cd.to.getContext())) {
161
- diffs.push(new Diff(this, DiffAction.ADD, 'sibling', cd.to));
162
- }
163
- }
164
- }
165
- // Add new siblings not in previous.
166
33
  for (const modelName of Object.keys(currentSiblingsByModels)) {
167
- if (previousSiblingsByModels.hasOwnProperty(modelName)) {
168
- continue;
169
- }
170
34
  const currentSiblings = currentSiblingsByModels[modelName];
171
35
  for (const cd of currentSiblings) {
172
36
  // Skip OVERLAY sibling, since overlays are diffed separately.
173
- if (cd.to.MODEL_TYPE === ModelType.OVERLAY) {
37
+ if (cd.to.NODE_TYPE === NodeType.OVERLAY) {
174
38
  continue;
175
39
  }
176
40
  diffs.push(new Diff(this, DiffAction.ADD, 'sibling', cd.to));
177
41
  }
178
42
  }
43
+ // Add model.
179
44
  const field = this.deriveDependencyField() || '';
180
45
  const fieldValue = field ? this[field] : '';
181
- if (this.isMarkedDeleted()) {
182
- diffs.push(new Diff(this, DiffAction.DELETE, field, fieldValue));
183
- }
184
- else if (!previous) {
185
- diffs.push(new Diff(this, DiffAction.ADD, field, fieldValue));
186
- }
187
- else {
188
- const pDiffs = await this.diffProperties(previous);
189
- diffs.push(...pDiffs);
190
- }
46
+ diffs.push(new Diff(this, DiffAction.ADD, field, fieldValue));
47
+ // Diff model properties.
48
+ const propertyDiffs = await this.diffProperties();
49
+ diffs.push(...propertyDiffs);
191
50
  return diffs;
192
51
  }
193
- /**
194
- * To get all ancestors of self.
195
- * - Includes parent nodes.
196
- * - Might include sibling nodes, if they are necessary for self node to exist.
197
- */
198
- getAncestors() {
199
- const members = [this];
200
- const membersProcessed = [];
201
- while (members.length > 0) {
202
- const member = members.pop();
203
- // Skip processing an already processed member.
204
- if (membersProcessed.some((m) => m.getContext() === member.getContext())) {
205
- continue;
206
- }
207
- for (const d of member.dependencies) {
208
- // If in dependency I am not declared a parent, then d.to is either my parent or has a relationship to me.
209
- // The behavior of that relationship should be that it must exist for me to exist.
210
- if (!d.isParentRelationship() &&
211
- d.hasMatchingBehavior(undefined, DiffAction.ADD, undefined, DiffAction.ADD) &&
212
- !membersProcessed.some((m) => m.getContext() === d.to.getContext())) {
213
- members.push(d.to);
214
- }
215
- }
216
- membersProcessed.push(member);
217
- }
218
- return membersProcessed;
52
+ async diffProperties() {
53
+ return [];
219
54
  }
220
- /**
221
- * To get an anchor with a given ID and parent.
222
- *
223
- * @param anchorId The ID of the anchor.
224
- * @param parent The parent of the anchor.
225
- * - If parent is not given, then self is considered the parent of this anchor.
226
- */
227
55
  getAnchor(anchorId, parent) {
228
56
  const index = this.getAnchorIndex(anchorId, parent);
229
57
  return index > -1 ? this.anchors[index] : undefined;
230
58
  }
231
- /**
232
- * To get the index of an anchor with a given ID and parent.
233
- *
234
- * @param anchorId The ID of the anchor.
235
- * @param parent The parent of the anchor.
236
- * - If parent is not given, then self is considered the parent of this anchor.
237
- */
238
59
  getAnchorIndex(anchorId, parent) {
239
60
  return this.anchors.findIndex((a) => a.anchorId === anchorId && a.getParent().getContext() === (parent || this).getContext());
240
61
  }
241
- /**
242
- * To get all anchors, filtered by anchor properties.
243
- *
244
- * @param filters A set of filters, where `key` is the property name and `value` is the value to filter by.
245
- */
246
62
  getAnchors(filters = []) {
247
63
  return this.anchors.filter((a) => filters.every((c) => a[c.key] === c.value));
248
64
  }
249
- /**
250
- * To get a boundary (sub graph) of self.
251
- * A boundary is a group of nodes that must belong together.
252
- * The boundary consists of all nodes associated with self,
253
- * which includes parents, grand parents, children, grand children, and siblings.
254
- */
255
- getBoundaryMembers() {
256
- const extenders = [this];
257
- const members = [];
258
- const parentOf = {};
259
- const pushToExtenders = (models) => {
260
- models.forEach((model) => {
261
- if (!members.some((m) => m.getContext() === model.getContext())) {
262
- extenders.push(model);
263
- }
264
- });
265
- };
266
- while (extenders.length > 0) {
267
- const model = extenders.pop();
268
- const ancestors = model.getAncestors();
269
- for (const ancestor of ancestors) {
270
- // Skip processing an already processed ancestor.
271
- if (members.some((m) => m.getContext() === ancestor.getContext())) {
272
- continue;
273
- }
274
- members.push(ancestor);
275
- // Check for circular dependencies on ancestor.
276
- for (const d of ancestor.dependencies) {
277
- let childContext;
278
- let parentContext;
279
- if (d.isParentRelationship()) {
280
- childContext = d.to.getContext();
281
- parentContext = d.from.getContext();
282
- }
283
- else if (d.isChildRelationship()) {
284
- childContext = d.from.getContext();
285
- parentContext = d.to.getContext();
286
- }
287
- else {
288
- // A relationship that is neither parent nor child, will not be reported in circular dependency!
289
- continue;
290
- }
291
- const seen = {};
292
- const toProcess = [parentContext];
293
- while (toProcess.length > 0) {
294
- const context = toProcess.pop();
295
- if (seen[context]) {
296
- throw new Error('Found circular dependencies!');
297
- }
298
- else {
299
- seen[context] = true;
300
- }
301
- toProcess.push(...(parentOf[context] || []));
302
- }
303
- if (!parentOf[childContext]) {
304
- parentOf[childContext] = [];
305
- parentOf[childContext].push(parentContext);
306
- }
307
- else if (!parentOf[childContext].some((p) => p === parentContext)) {
308
- parentOf[childContext].push(parentContext);
309
- }
310
- }
311
- }
312
- const children = model.getChildren();
313
- for (const modelName in children) {
314
- pushToExtenders(children[modelName].map((d) => d.to));
315
- }
316
- const dependents = model.dependencies
317
- .filter((d) => !d.isParentRelationship() && !d.isChildRelationship())
318
- .map((d) => d.to);
319
- pushToExtenders(dependents);
320
- }
321
- return members;
322
- }
323
- /**
324
- * To get a child of self that matches the given filters.
325
- *
326
- * @param modelName The {@link MODEL_NAME} of the child.
327
- * @param filters A set of filters, to be applied on children,
328
- * where `key` is the property name and `value` is the value to filter by.
329
- * @throws {@link Error} If more than one child is found.
330
- */
331
- getChild(modelName, filters = []) {
332
- const dependencies = this.getChildren(modelName)[modelName]?.filter((d) => filters.every((c) => d.to[c.key] === c.value));
333
- if (!dependencies) {
334
- return undefined;
335
- }
336
- if (dependencies.length > 1) {
337
- throw new Error('More than one children found! Use getChildren() instead.');
338
- }
339
- return dependencies[0].to;
340
- }
341
- /**
342
- * To get all children of self.
343
- *
344
- * @example
345
- * ```ts
346
- * const children = region.getChildren('environment');
347
- * const environments = children['environment'].map((d) => d.to);
348
- * ```
349
- * @param modelName The {@link MODEL_NAME} of the children.
350
- * @returns All children as an object with the {@link MODEL_NAME} as the key,
351
- * and self's dependency to the child as the value.
352
- */
353
- getChildren(modelName) {
354
- return this.dependencies
355
- .filter((d) => d.isParentRelationship() && (modelName ? d.to.MODEL_NAME === modelName : true))
356
- .reduce((accumulator, currentValue) => {
357
- if (!(currentValue.to.MODEL_NAME in accumulator)) {
358
- accumulator[currentValue.to.MODEL_NAME] = [];
359
- }
360
- accumulator[currentValue.to.MODEL_NAME].push(currentValue);
361
- return accumulator;
362
- }, {});
363
- }
364
- /**
365
- * To get the context of self.
366
- * A context is a string representation of self that uniquely identifies self in the graph.
367
- *
368
- * @example
369
- * ```ts
370
- * const app = new App('my-app');
371
- * const region = new Region('my-region');
372
- * app.addRegion(region);
373
- * const context = region.getContext(); // "region=my-region,app=my-app"
374
- * ```
375
- */
376
- getContext() {
377
- if (!this.context) {
378
- this.context = this.setContext();
379
- }
380
- return this.context;
381
- }
382
- /**
383
- * To get all dependencies of self.
384
- * - Can be filtered to just dependencies with a single node.
385
- *
386
- * @param to The other node. If present, will only return dependencies of self with this node.
387
- */
388
- getDependencies(to) {
389
- const filters = [];
390
- if (to) {
391
- filters.push({ key: 'to', value: to.getContext() });
392
- }
393
- return this.dependencies.filter((d) => {
394
- return filters.every((c) => {
395
- if (c.key === 'to') {
396
- return d.to.getContext() === c.value;
397
- }
398
- else {
399
- return true;
400
- }
401
- });
402
- });
403
- }
404
- /**
405
- * To get a dependency of self with another node.
406
- *
407
- * @param to The other node.
408
- * @param relationship The relationship between self and the other node.
409
- * @returns The dependency with the other node having the given relationship, or `undefined` if not found.
410
- */
411
- getDependency(to, relationship) {
412
- const index = this.getDependencyIndex(to, relationship);
413
- return index > -1 ? this.dependencies[index] : undefined;
414
- }
415
- /**
416
- * To get the index of a dependency of self with another node.
417
- *
418
- * @param to The other node.
419
- * @param relationship The relationship between self and the other node.
420
- * @returns The index of the dependency with the other node having the given relationship, or `-1` if not found.
421
- */
422
- getDependencyIndex(to, relationship) {
423
- return this.dependencies.findIndex((d) => d.from.getContext() === this.getContext() &&
424
- d.to.getContext() === to.getContext() &&
425
- d.getRelationship() !== undefined &&
426
- d.getRelationship().type === relationship);
427
- }
428
- /**
429
- * To get all parent of self.
430
- *
431
- * @example
432
- * ```ts
433
- * const parents = environment.getParents('region');
434
- * const regions = parents['region'].map((d) => d.to);
435
- * ```
436
- * @param modelName The {@link MODEL_NAME} of the parent.
437
- * @returns All parents as an object with the {@link MODEL_NAME} as the key,
438
- * and self's dependency to the parent as the value.
439
- */
440
- getParents(modelName) {
441
- return this.dependencies
442
- .filter((d) => d.isChildRelationship() && (modelName ? d.to.MODEL_NAME === modelName : true))
443
- .reduce((accumulator, currentValue) => {
444
- if (!(currentValue.to.MODEL_NAME in accumulator)) {
445
- accumulator[currentValue.to.MODEL_NAME] = [];
446
- }
447
- accumulator[currentValue.to.MODEL_NAME].push(currentValue);
448
- return accumulator;
449
- }, {});
450
- }
451
- /**
452
- * To get all siblings of self.
453
- *
454
- * :::note Note
455
- * Two environments, or two regions, etc. are not automatically considered siblings,
456
- * unless they have a dependency between them.
457
- *
458
- * ```ts
459
- * const environment1 = new Environment('qa');
460
- * const environment2 = new Environment('prod');
461
- * const region = new Region('region');
462
- * region.addEnvironment(environment1);
463
- * region.addEnvironment(environment2);
464
- * environment1.getSiblings('environment'); // This won't return a dependency to environment2.
465
- * ```
466
- * :::
467
- *
468
- * @example
469
- * ```ts
470
- * const siblings = environment.getSiblings('image');
471
- * const images = siblings['image'].map((d) => d.to);
472
- * ```
473
- * @param modelName The {@link MODEL_NAME} of the sibling.
474
- * @returns All siblings as an object with the {@link MODEL_NAME} as the key,
475
- * and self's dependency to the sibling as the value.
476
- */
477
- getSiblings(modelName) {
478
- return this.dependencies
479
- .filter((d) => !d.isChildRelationship() && !d.isParentRelationship() && (modelName ? d.to.MODEL_NAME === modelName : true))
480
- .reduce((accumulator, currentValue) => {
481
- if (!(currentValue.to.MODEL_NAME in accumulator)) {
482
- accumulator[currentValue.to.MODEL_NAME] = [];
483
- }
484
- accumulator[currentValue.to.MODEL_NAME].push(currentValue);
485
- return accumulator;
486
- }, {});
487
- }
488
- /**
489
- * To check if given node is an ancestor of self.
490
- *
491
- * @param model The other node.
492
- */
493
- hasAncestor(model) {
494
- const modelParts = model
495
- .getContext()
496
- .split(',')
497
- .map((p) => p.split('='));
498
- const ancestors = this.getAncestors();
499
- return ancestors.some((a) => {
500
- const ancestorParts = a
501
- .getContext()
502
- .split(',')
503
- .reduce((map, p) => {
504
- const parts = p.split('=');
505
- map[parts[0]] = parts[1];
506
- return map;
507
- }, {});
508
- return modelParts.every((p) => ancestorParts[p[0]] === p[1]);
509
- });
510
- }
511
- /**
512
- * To check if self is marked as deleted.
513
- * A deleted node will be removed from the graph after the transaction.
514
- */
515
- isMarkedDeleted() {
516
- return this._deleteMarker;
517
- }
518
- /**
519
- * To mark self as deleted.
520
- * A deleted node will be removed from the graph after the transaction.
521
- * - A node cannot be deleted if it has dependencies.
522
- * - Unless the dependency is a sibling dependency and `ignoreDirectRelationships` is `true`.
523
- *
524
- * @example
525
- * ```ts
526
- * const region1 = new Region('region-1');
527
- * const environment = new Environment('environment');
528
- * region1.addEnvironment(environment);
529
- * const region2 = new Region('region-2');
530
- * region1.addSibling(region2);
531
- *
532
- * // Cannot remove region1 because it has dependencies.
533
- * region1.remove(); // throws.
534
- *
535
- * // Still can't remove region1 because it has a sibling dependency.
536
- * environment.remove(); // ok.
537
- * region1.remove(); // throws.
538
- *
539
- * // But can remove region1 when sibling relationships are ignored.
540
- * region1.remove(true); // ok.
541
- * ```
542
- * @param ignoreDirectRelationships Whether to ignore sibling relationships.
543
- * @throws {@link Error} If node contains dependencies to other nodes.
544
- */
545
- remove(ignoreDirectRelationships = false) {
546
- // Verify model can be removed.
547
- for (const dependency of this.dependencies) {
548
- // When direct relationship is not ignored, this model can't be a parent or have direct relationships.
549
- // When direct relationship is ignored, this model can't be a parent.
550
- if ((!ignoreDirectRelationships && !dependency.isChildRelationship()) ||
551
- (ignoreDirectRelationships && dependency.isParentRelationship())) {
552
- throw new Error('Cannot remove model until dependent models exist!');
553
- }
554
- }
555
- // Removing all dependencies that points to this.
556
- for (const dependency of this.dependencies) {
557
- const index = dependency.to.dependencies.findIndex((d) => d.to.getContext() === this.getContext());
558
- dependency.to.dependencies.splice(index, 1);
559
- }
560
- this._deleteMarker = true;
561
- }
562
- /**
563
- * To remove all anchors from self.
564
- */
565
65
  removeAllAnchors() {
566
- let anchors = this.getAnchors();
66
+ const anchors = this.getAnchors();
567
67
  while (anchors.length > 0) {
568
- this.removeAnchor(anchors[0]);
569
- anchors = this.getAnchors();
68
+ this.removeAnchor(anchors.shift());
570
69
  }
571
70
  }
572
- /**
573
- * To remove an anchor from self.
574
- *
575
- * @param anchor The anchor to remove.
576
- */
577
71
  removeAnchor(anchor) {
578
72
  const existingAnchorIndex = this.getAnchorIndex(anchor.anchorId, anchor.getParent());
579
73
  if (existingAnchorIndex !== -1) {
580
74
  this.anchors.splice(existingAnchorIndex, 1);
581
75
  }
582
76
  }
583
- /**
584
- * To remove a dependency from self.
585
- *
586
- * @param dependencyIndex The index of the dependency to remove.
587
- */
588
- removeDependency(dependencyIndex) {
589
- if (dependencyIndex > -1 && dependencyIndex < this.dependencies.length) {
590
- this.dependencies.splice(dependencyIndex, 1);
591
- }
592
- }
593
- /**
594
- * To remove ALL dependencies from self for the given node.
595
- *
596
- * @param model The other node.
597
- */
598
- removeRelationship(model) {
599
- for (let i = this.dependencies.length - 1; i >= 0; i--) {
600
- const dependency = this.dependencies[i];
601
- if (dependency.to.getContext() === model.getContext()) {
602
- this.dependencies.splice(i, 1);
603
- }
604
- }
605
- for (let i = model.dependencies.length - 1; i >= 0; i--) {
606
- const dependency = model.dependencies[i];
607
- if (dependency.to.getContext() === this.getContext()) {
608
- model.dependencies.splice(i, 1);
609
- }
610
- }
611
- }
612
- /**
613
- * To create self given its serialized representation.
614
- * - First argument is the serialized representation.
615
- * - Second argument is the `deReferenceContext()` function to resolve other nodes self depends on.
616
- */
617
77
  static async unSynth(...args) {
618
78
  if (args.length > 4) {
619
79
  throw new Error('Too many args in unSynth()');