@api-client/core 0.6.14 → 0.6.17

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 (151) hide show
  1. package/build/browser.d.ts +9 -2
  2. package/build/browser.js +12 -2
  3. package/build/browser.js.map +1 -1
  4. package/build/index.d.ts +9 -2
  5. package/build/index.js +12 -2
  6. package/build/index.js.map +1 -1
  7. package/build/src/amf/AmfMixin.d.ts +395 -0
  8. package/build/src/amf/AmfMixin.js +1146 -0
  9. package/build/src/amf/AmfMixin.js.map +1 -0
  10. package/build/src/amf/AmfSerializer.d.ts +136 -0
  11. package/build/src/amf/AmfSerializer.js +1913 -0
  12. package/build/src/amf/AmfSerializer.js.map +1 -0
  13. package/build/src/amf/AmfShapeGenerator.d.ts +85 -0
  14. package/build/src/amf/AmfShapeGenerator.js +385 -0
  15. package/build/src/amf/AmfShapeGenerator.js.map +1 -0
  16. package/build/src/amf/AmfTypes.d.ts +24 -0
  17. package/build/src/amf/AmfTypes.js +122 -0
  18. package/build/src/amf/AmfTypes.js.map +1 -0
  19. package/build/src/amf/ApiExampleGenerator.d.ts +36 -0
  20. package/build/src/amf/ApiExampleGenerator.js +109 -0
  21. package/build/src/amf/ApiExampleGenerator.js.map +1 -0
  22. package/build/src/amf/ApiMonacoSchemaGenerator.d.ts +67 -0
  23. package/build/src/amf/ApiMonacoSchemaGenerator.js +243 -0
  24. package/build/src/amf/ApiMonacoSchemaGenerator.js.map +1 -0
  25. package/build/src/amf/ApiSchemaGenerator.d.ts +55 -0
  26. package/build/src/amf/ApiSchemaGenerator.js +94 -0
  27. package/build/src/amf/ApiSchemaGenerator.js.map +1 -0
  28. package/build/src/amf/ApiSchemaValues.d.ts +98 -0
  29. package/build/src/amf/ApiSchemaValues.js +382 -0
  30. package/build/src/amf/ApiSchemaValues.js.map +1 -0
  31. package/build/src/amf/Utils.d.ts +41 -0
  32. package/build/src/amf/Utils.js +176 -0
  33. package/build/src/amf/Utils.js.map +1 -0
  34. package/build/src/amf/data-node/DataNodeBase.d.ts +31 -0
  35. package/build/src/amf/data-node/DataNodeBase.js +77 -0
  36. package/build/src/amf/data-node/DataNodeBase.js.map +1 -0
  37. package/build/src/amf/data-node/JsonDataNodeGenerator.d.ts +15 -0
  38. package/build/src/amf/data-node/JsonDataNodeGenerator.js +24 -0
  39. package/build/src/amf/data-node/JsonDataNodeGenerator.js.map +1 -0
  40. package/build/src/amf/data-node/UrlEncodedDataNodeGenerator.d.ts +13 -0
  41. package/build/src/amf/data-node/UrlEncodedDataNodeGenerator.js +42 -0
  42. package/build/src/amf/data-node/UrlEncodedDataNodeGenerator.js.map +1 -0
  43. package/build/src/amf/data-node/XmlDataNodeGenerator.d.ts +21 -0
  44. package/build/src/amf/data-node/XmlDataNodeGenerator.js +30 -0
  45. package/build/src/amf/data-node/XmlDataNodeGenerator.js.map +1 -0
  46. package/build/src/amf/definitions/Amf.d.ts +362 -0
  47. package/build/src/amf/definitions/Amf.js +2 -0
  48. package/build/src/amf/definitions/Amf.js.map +1 -0
  49. package/build/src/amf/definitions/Api.d.ts +381 -0
  50. package/build/src/amf/definitions/Api.js +2 -0
  51. package/build/src/amf/definitions/Api.js.map +1 -0
  52. package/build/src/amf/definitions/Base.d.ts +11 -0
  53. package/build/src/amf/definitions/Base.js +2 -0
  54. package/build/src/amf/definitions/Base.js.map +1 -0
  55. package/build/src/amf/definitions/Namespace.d.ts +311 -0
  56. package/build/src/amf/definitions/Namespace.js +330 -0
  57. package/build/src/amf/definitions/Namespace.js.map +1 -0
  58. package/build/src/amf/definitions/Shapes.d.ts +309 -0
  59. package/build/src/amf/definitions/Shapes.js +87 -0
  60. package/build/src/amf/definitions/Shapes.js.map +1 -0
  61. package/build/src/amf/models/AmfDataNode.d.ts +68 -0
  62. package/build/src/amf/models/AmfDataNode.js +188 -0
  63. package/build/src/amf/models/AmfDataNode.js.map +1 -0
  64. package/build/src/amf/shape/ShapeBase.d.ts +75 -0
  65. package/build/src/amf/shape/ShapeBase.js +90 -0
  66. package/build/src/amf/shape/ShapeBase.js.map +1 -0
  67. package/build/src/amf/shape/ShapeJsonSchemaGenerator.d.ts +46 -0
  68. package/build/src/amf/shape/ShapeJsonSchemaGenerator.js +406 -0
  69. package/build/src/amf/shape/ShapeJsonSchemaGenerator.js.map +1 -0
  70. package/build/src/amf/shape/ShapeXmlSchemaGenerator.d.ts +84 -0
  71. package/build/src/amf/shape/ShapeXmlSchemaGenerator.js +820 -0
  72. package/build/src/amf/shape/ShapeXmlSchemaGenerator.js.map +1 -0
  73. package/build/src/models/data/Bindings.d.ts +161 -0
  74. package/build/src/models/data/Bindings.js +2 -0
  75. package/build/src/models/data/Bindings.js.map +1 -0
  76. package/build/src/models/data/DataAssociation.d.ts +135 -14
  77. package/build/src/models/data/DataAssociation.js +154 -21
  78. package/build/src/models/data/DataAssociation.js.map +1 -1
  79. package/build/src/models/data/DataAssociationSchema.d.ts +36 -0
  80. package/build/src/models/data/DataEntity.d.ts +71 -15
  81. package/build/src/models/data/DataEntity.js +133 -53
  82. package/build/src/models/data/DataEntity.js.map +1 -1
  83. package/build/src/models/data/DataFile.d.ts +3 -0
  84. package/build/src/models/data/DataFile.js +3 -0
  85. package/build/src/models/data/DataFile.js.map +1 -1
  86. package/build/src/models/data/DataModel.d.ts +1 -1
  87. package/build/src/models/data/DataModel.js.map +1 -1
  88. package/build/src/models/data/DataNamespace.d.ts +14 -0
  89. package/build/src/models/data/DataNamespace.js +50 -0
  90. package/build/src/models/data/DataNamespace.js.map +1 -1
  91. package/build/src/models/data/DataProperty.d.ts +107 -36
  92. package/build/src/models/data/DataProperty.js +147 -17
  93. package/build/src/models/data/DataProperty.js.map +1 -1
  94. package/data/apis/oas-date/oas-date.yaml +28 -0
  95. package/data/apis/oas-types/oas-types.yaml +159 -0
  96. package/data/apis/oas-unions/oas-unions.yaml +75 -0
  97. package/data/apis/raml-date/raml-date.raml +28 -0
  98. package/data/apis/recursive/recursive.raml +14 -0
  99. package/data/apis/schema-api/examples/person.json +14 -0
  100. package/data/apis/schema-api/examples/person.raml +14 -0
  101. package/data/apis/schema-api/examples/person.url.encoded +1 -0
  102. package/data/apis/schema-api/examples/person.xml +14 -0
  103. package/data/apis/schema-api/library/demo-types.raml +43 -0
  104. package/data/apis/schema-api/schema-api.raml +644 -0
  105. package/data/apis/schema-api/schemas/person.json +104 -0
  106. package/data/apis/schema-api/schemas/person.xsd +26 -0
  107. package/data/apis/schema-api/types/DemoPerson.raml +67 -0
  108. package/data/model.js +106 -0
  109. package/data/models/oas-date.json +637 -0
  110. package/data/models/oas-types.json +5352 -0
  111. package/data/models/oas-unions.json +1881 -0
  112. package/data/models/raml-date.json +1096 -0
  113. package/data/models/recursive.json +610 -0
  114. package/data/models/schema-api.json +37319 -0
  115. package/package.json +9 -6
  116. package/src/amf/AmfMixin.ts +1623 -0
  117. package/src/amf/AmfSerializer.ts +2028 -0
  118. package/src/amf/AmfShapeGenerator.ts +400 -0
  119. package/src/amf/AmfTypes.ts +126 -0
  120. package/src/amf/ApiExampleGenerator.ts +112 -0
  121. package/src/amf/ApiMonacoSchemaGenerator.ts +296 -0
  122. package/src/amf/ApiSchemaGenerator.ts +108 -0
  123. package/src/amf/ApiSchemaValues.ts +411 -0
  124. package/src/amf/Utils.ts +182 -0
  125. package/src/amf/data-node/DataNodeBase.ts +81 -0
  126. package/src/amf/data-node/JsonDataNodeGenerator.ts +26 -0
  127. package/src/amf/data-node/README.md +3 -0
  128. package/src/amf/data-node/UrlEncodedDataNodeGenerator.ts +43 -0
  129. package/src/amf/data-node/XmlDataNodeGenerator.ts +38 -0
  130. package/src/amf/definitions/Amf.ts +443 -0
  131. package/src/amf/definitions/Api.ts +427 -0
  132. package/src/amf/definitions/Base.ts +13 -0
  133. package/src/amf/definitions/Namespace.ts +341 -0
  134. package/src/amf/definitions/Shapes.ts +414 -0
  135. package/src/amf/models/AmfDataNode.ts +200 -0
  136. package/src/amf/shape/README.md +4 -0
  137. package/src/amf/shape/ShapeBase.ts +160 -0
  138. package/src/amf/shape/ShapeJsonSchemaGenerator.ts +422 -0
  139. package/src/amf/shape/ShapeXmlSchemaGenerator.ts +876 -0
  140. package/src/models/data/Bindings.ts +186 -0
  141. package/src/models/data/DataAssociation.ts +226 -29
  142. package/src/models/data/DataAssociationSchema.ts +38 -0
  143. package/src/models/data/DataEntity.ts +162 -60
  144. package/src/models/data/DataFile.ts +3 -0
  145. package/src/models/data/DataModel.ts +1 -1
  146. package/src/models/data/DataNamespace.ts +54 -0
  147. package/src/models/data/DataProperty.ts +191 -47
  148. package/build/src/models/data/DataPropertySchema.d.ts +0 -125
  149. package/build/src/models/data/DataPropertySchema.js +0 -33
  150. package/build/src/models/data/DataPropertySchema.js.map +0 -1
  151. package/src/models/data/DataPropertySchema.ts +0 -156
@@ -5,6 +5,9 @@ import { DataProperty, DataPropertyType } from "./DataProperty.js";
5
5
  import { DataAssociation } from "./DataAssociation.js";
6
6
  import { IBreadcrumb } from "../store/Breadcrumb.js";
7
7
  import { DataModel } from "./DataModel.js";
8
+ import { INodeShape, IShapeUnion } from "../../amf/definitions/Shapes.js";
9
+ import { AmfShapeGenerator } from "../../amf/AmfShapeGenerator.js";
10
+ import { ApiSchemaGenerator } from "../../amf/ApiSchemaGenerator.js";
8
11
 
9
12
  export const Kind = 'Core#DataEntity';
10
13
 
@@ -60,6 +63,24 @@ export interface IDataEntity {
60
63
  * Whether this entity is deprecated.
61
64
  */
62
65
  deprecated?: boolean;
66
+
67
+ /**
68
+ * The schema allowing to translate the model into a specific format (like JSON, RAML, XML, etc.)
69
+ *
70
+ * Schema limitations:
71
+ *
72
+ * - can only occur on an adapted property. Has no effect on the "main" property.
73
+ */
74
+ schema?: INodeShape;
75
+
76
+ /**
77
+ * The key of the entity that is adapted by this entity.
78
+ * Adapted entities can manipulate the shape of the schema for the entity.
79
+ *
80
+ * Each value defined on the adapted entity changes the original value defined on
81
+ * the entity.
82
+ */
83
+ adapts?: string;
63
84
  }
64
85
 
65
86
  /**
@@ -114,6 +135,15 @@ export class DataEntity {
114
135
  */
115
136
  deprecated?: boolean;
116
137
 
138
+ /**
139
+ * The key of the entity that is adapted by this entity.
140
+ * Adapted entities can manipulate the shape of the schema for the entity.
141
+ *
142
+ * Each value defined on the adapted entity changes the original value defined on
143
+ * the entity.
144
+ */
145
+ adapts?: string;
146
+
117
147
  static fromName(root: DataNamespace, name: string): DataEntity {
118
148
  const entity = new DataEntity(root);
119
149
  entity.info = Thing.fromName(name);
@@ -123,7 +153,7 @@ export class DataEntity {
123
153
  /**
124
154
  * @param input The data entity definition to restore.
125
155
  */
126
- constructor(protected root: DataNamespace, input?: string | IDataEntity) {
156
+ constructor(public root: DataNamespace, input?: string | IDataEntity) {
127
157
  let init: IDataEntity;
128
158
  if (typeof input === 'string') {
129
159
  init = JSON.parse(input);
@@ -143,7 +173,10 @@ export class DataEntity {
143
173
  if (!DataEntity.isDataEntity(init)) {
144
174
  throw new Error(`Not a data entity.`);
145
175
  }
146
- const { info, key = v4(), kind = Kind, tags, taxonomy, parents, properties, associations, deprecated } = init;
176
+ const {
177
+ info, key = v4(), kind = Kind, tags, taxonomy, parents, properties, associations,
178
+ deprecated, adapts,
179
+ } = init;
147
180
  this.kind = kind;
148
181
  this.key = key;
149
182
  if (info) {
@@ -151,7 +184,6 @@ export class DataEntity {
151
184
  } else {
152
185
  this.info = Thing.fromName('');
153
186
  }
154
-
155
187
  if (Array.isArray(tags)) {
156
188
  this.tags = [...tags];
157
189
  } else {
@@ -167,7 +199,6 @@ export class DataEntity {
167
199
  } else {
168
200
  this.parents = [];
169
201
  }
170
-
171
202
  this.properties = [];
172
203
  if (Array.isArray(properties)) {
173
204
  properties.forEach(key => {
@@ -177,7 +208,6 @@ export class DataEntity {
177
208
  }
178
209
  });
179
210
  }
180
-
181
211
  this.associations = [];
182
212
  if (Array.isArray(associations)) {
183
213
  associations.forEach(key => {
@@ -187,12 +217,16 @@ export class DataEntity {
187
217
  }
188
218
  });
189
219
  }
190
-
191
220
  if (typeof deprecated === 'boolean') {
192
221
  this.deprecated = deprecated;
193
222
  } else {
194
223
  this.deprecated = undefined;
195
224
  }
225
+ if (typeof adapts === 'string') {
226
+ this.adapts = adapts;
227
+ } else {
228
+ this.adapts = undefined;
229
+ }
196
230
  }
197
231
 
198
232
  static isDataEntity(input: unknown): boolean {
@@ -227,6 +261,9 @@ export class DataEntity {
227
261
  if (typeof this.deprecated === 'boolean') {
228
262
  result.deprecated = this.deprecated;
229
263
  }
264
+ if (this.adapts) {
265
+ result.adapts = this.adapts;
266
+ }
230
267
  return result;
231
268
  }
232
269
 
@@ -243,8 +280,11 @@ export class DataEntity {
243
280
  * @param type The type of the property
244
281
  * @returns The created property
245
282
  */
246
- addTypedProperty(type: DataPropertyType): DataProperty {
283
+ addTypedProperty(type: DataPropertyType, name?: string): DataProperty {
247
284
  const property = DataProperty.fromType(this.root, type);
285
+ if (name) {
286
+ property.info.name = name;
287
+ }
248
288
  this.root.definitions.properties.push(property);
249
289
  this.properties.push(property);
250
290
  return property;
@@ -295,8 +335,11 @@ export class DataEntity {
295
335
  * @param target The target entity key of the association
296
336
  * @returns The created association
297
337
  */
298
- addTargetAssociation(target: string): DataAssociation {
338
+ addTargetAssociation(target: string, name?: string): DataAssociation {
299
339
  const result = DataAssociation.fromTarget(this.root, target);
340
+ if (name) {
341
+ result.info.name = name;
342
+ }
300
343
  this.root.definitions.associations.push(result);
301
344
  this.associations.push(result);
302
345
  return result;
@@ -321,15 +364,18 @@ export class DataEntity {
321
364
  /**
322
365
  * Reads the list of parents for the entity, inside the root namespace. The computed list contains the list of all
323
366
  * parents in the inheritance chain in no particular order.
367
+ * @param recursive Whether to include parent parents as well.
324
368
  */
325
- getComputedParents(): DataEntity[] {
369
+ getComputedParents(recursive?: boolean): DataEntity[] {
326
370
  const { entities } = this.root.definitions;
327
371
  let result: DataEntity[] = [];
328
372
  this.parents.forEach((key) => {
329
373
  const parent = entities.find(i => i.key === key);
330
374
  if (parent) {
331
375
  result.push(parent);
332
- result = result.concat(parent.getComputedParents());
376
+ if (recursive) {
377
+ result = result.concat(parent.getComputedParents());
378
+ }
333
379
  }
334
380
  });
335
381
  return result;
@@ -352,10 +398,10 @@ export class DataEntity {
352
398
  const { entities } = root.definitions;
353
399
  const result: DataEntity[] = [];
354
400
  associations.forEach((assoc) => {
355
- if (!assoc.target) {
401
+ if (!assoc.targets.length) {
356
402
  return;
357
403
  }
358
- const entity = entities.find(i => i.key === assoc.target);
404
+ const entity = entities.find(i => assoc.targets.includes(i.key));
359
405
  if (entity) {
360
406
  result.push(entity);
361
407
  }
@@ -419,63 +465,39 @@ export class DataEntity {
419
465
  * @yields The path containing keys of entities from this entity to the `toEntity` (inclusive) and all entities in between.
420
466
  */
421
467
  * associationPath(toEntity: string): Generator<string[]> {
422
- const graph = this._associationGraph();
423
- for (const path of this._associationPath(this.key, toEntity, graph)) {
468
+ const graph = this.root.associationGraph();
469
+ for (const path of this.root.associationPath(this.key, toEntity, graph)) {
424
470
  yield path;
425
471
  }
426
472
  }
427
473
 
428
474
  /**
429
- * The actual implementation of the graph search.
475
+ * Returns a list of entities that are association with this entity through an association
476
+ * that the target property points to this entity.
430
477
  *
431
- * @param from The current from node
432
- * @param to The target node
433
- * @param g The graph
434
- * @param path The current list of entity ids.
435
- * @param visited The list of visited paths to avoid cycles
436
- */
437
- protected * _associationPath(from: string, to: string, g: Record<string, string[]>, path: string[] = [], visited: Set<string> = new Set()): Generator<string[]> {
438
- if (from === to) {
439
- yield path.concat(to);
440
- return;
441
- }
442
- if (visited.has(from)) {
443
- // it's a cycle
444
- return;
445
- }
446
- if (g[from]) {
447
- visited.add(from);
448
- path.push(from);
478
+ * In other words, if entity `A` has association target with `B`, then asking `B` for related entities will
479
+ * result with `[A]`.
480
+ *
481
+ * ```plain
482
+ * A -> B -> C
483
+ * D -> C
484
+ *
485
+ * C => [B, D]
486
+ * ```
487
+ */
488
+ getRelatedEntities(): DataEntity[] {
489
+ const { key, root } = this;
490
+ const result: DataEntity[] = [];
491
+ const inverse = root.definitions.associations.filter(i => i.targets.includes(key));
449
492
 
450
- for (const neighbor of g[from]) {
451
- yield *this._associationPath(neighbor, to, g, path, visited);
493
+ inverse.forEach((assoc) => {
494
+ const entity = root.definitions.entities.find(e => e.associations.includes(assoc));
495
+ if (entity) {
496
+ result.push(entity);
452
497
  }
453
-
454
- visited.delete(from);
455
- path.pop();
456
- }
457
- }
498
+ });
458
499
 
459
- /**
460
- * @returns The graph of associations where keys are the source entities and the value is the list of all target entities.
461
- */
462
- protected _associationGraph(): Record<string, string[]> {
463
- const graph: Record<string, string[]> = {};
464
- const { associations, entities } = this.root.definitions;
465
- for (const assoc of associations) {
466
- if (!assoc.target) {
467
- continue;
468
- }
469
- const srcEntity = entities.find(i => i.associations.some(a => a === assoc));
470
- if (!srcEntity) {
471
- continue;
472
- }
473
- if (!graph[srcEntity.key]) {
474
- graph[srcEntity.key] = [];
475
- }
476
- graph[srcEntity.key].push(assoc.target);
477
- }
478
- return graph;
500
+ return result;
479
501
  }
480
502
 
481
503
  /**
@@ -552,4 +574,84 @@ export class DataEntity {
552
574
  tags.splice(index, 1);
553
575
  }
554
576
  }
577
+
578
+ /**
579
+ * Creates a Shape of AMF.
580
+ * The property itself is auto-generated. If the `schema` is defined then it is used
581
+ * as the `range` of the property. Otherwise basic shape is generated for the range.
582
+ *
583
+ * This is a preferred way of reading the AMF shape as this synchronizes changed
584
+ * data properties with the shape definition.
585
+ *
586
+ * @returns AMF property shape definition.
587
+ */
588
+ toApiShape(): IShapeUnion {
589
+ const serializer = new AmfShapeGenerator();
590
+ return serializer.entity(this);
591
+ }
592
+
593
+ /**
594
+ * Reads the schema of the Entity and generates an example for it.
595
+ */
596
+ toExample(mime: string): string | number | boolean | null | undefined {
597
+ const shape = this.toApiShape();
598
+ const generator = new ApiSchemaGenerator(mime, {
599
+ renderExamples: true,
600
+ renderMocked: true,
601
+ renderOptional: true,
602
+ });
603
+ return generator.generate(shape);
604
+ }
605
+
606
+ /**
607
+ * @returns The adapted entity, if any
608
+ */
609
+ readAdapted(): DataEntity | undefined {
610
+ const { adapts } = this;
611
+ if (!adapts) {
612
+ return undefined;
613
+ }
614
+ return this.root.definitions.entities.find(i => i.key === adapts);
615
+ }
616
+
617
+ /**
618
+ * Creates new adapted entity and associates it with this entity.
619
+ * @returns The instance of the created entity.
620
+ */
621
+ createAdapted(): DataEntity {
622
+ const entity = new DataEntity(this.root);
623
+ // disallow defaults as this would influence the schema generation
624
+ entity.info.name = undefined;
625
+ this.root.definitions.entities.push(entity);
626
+ this.adapts = entity.key;
627
+ return entity;
628
+ }
629
+
630
+ /**
631
+ * Checks whether the `target` creates a cycle in the graph (whether this creates a recursive association).
632
+ * @param target
633
+ */
634
+ hasForwardCycle(target: string, g = this.root.associationGraph(), key = this.key): boolean {
635
+ if (target === key) {
636
+ return true;
637
+ }
638
+ const neighbors: string[] = [];
639
+ Object.keys(g).forEach(prop => {
640
+ if (g[prop].includes(key)) {
641
+ neighbors.push(prop);
642
+ }
643
+ });
644
+ if (neighbors.includes(target)) {
645
+ return true;
646
+ }
647
+ for (const neighbor of neighbors) {
648
+ if (neighbor === key) {
649
+ return true;
650
+ }
651
+ if (this.hasForwardCycle(target, g, neighbor)) {
652
+ return true;
653
+ }
654
+ }
655
+ return false;
656
+ }
555
657
  }
@@ -9,6 +9,9 @@ export interface IDataFile extends IFile {
9
9
  kind: typeof Kind;
10
10
  }
11
11
 
12
+ /**
13
+ * Used by the store. A file definition for the DataNamespace
14
+ */
12
15
  export class DataFile extends File {
13
16
  kind = Kind;
14
17
 
@@ -59,7 +59,7 @@ export class DataModel {
59
59
  * @param root the root namespace.
60
60
  * @param input The data model definition to restore.
61
61
  */
62
- constructor(protected root: DataNamespace, input?: string | IDataModel) {
62
+ constructor(public root: DataNamespace, input?: string | IDataModel) {
63
63
  let init: IDataModel;
64
64
  if (typeof input === 'string') {
65
65
  init = JSON.parse(input);
@@ -534,4 +534,58 @@ export class DataNamespace extends DataNamespaceParent {
534
534
  model.remove();
535
535
  }
536
536
  }
537
+
538
+ /**
539
+ * @returns The graph of associations where keys are the source entities and the value is the list of all target entities.
540
+ */
541
+ associationGraph(): Record<string, string[]> {
542
+ const graph: Record<string, string[]> = {};
543
+ const { definitions } = this.root || this;
544
+ const { associations, entities } = definitions;
545
+ for (const assoc of associations) {
546
+ if (!assoc.targets.length) {
547
+ continue;
548
+ }
549
+ const srcEntity = entities.find(i => i.associations.some(a => a === assoc));
550
+ if (!srcEntity) {
551
+ continue;
552
+ }
553
+ if (!graph[srcEntity.key]) {
554
+ graph[srcEntity.key] = [];
555
+ }
556
+ graph[srcEntity.key].splice(0, 0, ...assoc.targets);
557
+ }
558
+ return graph;
559
+ }
560
+
561
+ /**
562
+ * Prints out all associations from one entity to another through all entities that may be in between.
563
+ *
564
+ * @param from The key of the from entity
565
+ * @param to The key of the target entity
566
+ * @param g The graph generated with `associationGraph()`
567
+ * @param path The current list of entity ids. Do not set this, it is for the recursive processing of the graph.
568
+ * @param visited The list of visited paths to avoid cycles. Do not set this, it is for the recursive processing of the graph.
569
+ */
570
+ * associationPath(from: string, to: string, g: Record<string, string[]>, path: string[] = [], visited: Set<string> = new Set()): Generator<string[]> {
571
+ if (from === to) {
572
+ yield path.concat(to);
573
+ return;
574
+ }
575
+ if (visited.has(from)) {
576
+ // it's a cycle
577
+ return;
578
+ }
579
+ if (g[from]) {
580
+ visited.add(from);
581
+ path.push(from);
582
+
583
+ for (const neighbor of g[from]) {
584
+ yield *this.associationPath(neighbor, to, g, path, visited);
585
+ }
586
+
587
+ visited.delete(from);
588
+ path.pop();
589
+ }
590
+ }
537
591
  }