@likec4/core 1.26.2 → 1.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder/index.d.mts +3 -1
- package/dist/builder/index.mjs +6 -4
- package/dist/compute-view/index.d.mts +1 -2
- package/dist/compute-view/index.mjs +6 -6
- package/dist/compute-view/relationships-view/index.d.mts +42 -0
- package/dist/compute-view/relationships-view/index.mjs +181 -0
- package/dist/index.d.mts +3 -5
- package/dist/index.mjs +5 -5
- package/dist/model/index.d.mts +3 -3
- package/dist/model/index.mjs +1 -1
- package/dist/shared/{core.yND74qFI.mjs → core.BMep80lJ.mjs} +2 -1
- package/dist/shared/{core.DRxv3HIl.mjs → core.C-YXI-43.mjs} +1 -5
- package/dist/shared/core.CQXU9DF7.mjs +477 -0
- package/dist/shared/{core.Brmu7VBL.d.mts → core.CtloVwbo.d.mts} +3 -1
- package/dist/shared/{core.DvRnF4Sc.mjs → core.Cu-UdkSl.mjs} +16 -477
- package/dist/shared/core.DbRvwARP.mjs +5 -0
- package/dist/shared/{core.oI7caxVx.d.mts → core.Uz54YXx0.d.mts} +1 -1
- package/dist/shared/{core.NNojPYtn.mjs → core.zj2huD5x.mjs} +12 -8
- package/dist/types/index.mjs +1 -1
- package/dist/utils/index.d.mts +3 -2
- package/dist/utils/index.mjs +5 -3
- package/package.json +19 -9
- package/src/builder/Builder.model.ts +74 -0
- package/src/compute-view/deployment-view/_types.ts +1 -1
- package/src/compute-view/deployment-view/predicates/relation-direct.ts +6 -7
- package/src/compute-view/deployment-view/predicates/relation-incoming.ts +2 -1
- package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +1 -1
- package/src/compute-view/index.ts +0 -1
- package/src/compute-view/relationships-view/_types.ts +7 -6
- package/src/compute-view/relationships-view/compute.ts +140 -52
- package/src/compute-view/relationships-view/index.ts +3 -0
- package/src/compute-view/relationships-view/utils.ts +44 -11
- package/src/compute-view/utils/view-hash.ts +1 -4
- package/src/index.ts +1 -1
- package/src/model/ElementModel.ts +2 -0
- package/src/model/view/NodeModel.ts +5 -0
- package/src/utils/object-hash.ts +13 -2
- package/dist/builder/index.d.ts +0 -509
- package/dist/compute-view/index.d.ts +0 -23
- package/dist/index.d.ts +0 -147
- package/dist/model/index.d.ts +0 -118
- package/dist/shared/core.-qjOvsEL.d.ts +0 -146
- package/dist/shared/core.30yYKXcZ.d.ts +0 -186
- package/dist/shared/core.BH59D8Ji.d.ts +0 -858
- package/dist/shared/core.C8xqv5sq.mjs +0 -37
- package/dist/shared/core.CIbWBWW3.d.ts +0 -127
- package/dist/shared/core.Cpd5mZgF.d.ts +0 -732
- package/dist/shared/core.Dd1nCiNx.d.mts +0 -12
- package/dist/shared/core.mD0TIdz_.d.ts +0 -12
- package/dist/types/index.d.ts +0 -72
- package/dist/utils/index.d.ts +0 -458
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://likec4.dev",
|
|
6
6
|
"author": "Denis Davydkov <denis@davydkov.com>",
|
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
"default": "./dist/builder/index.mjs"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
+
"./compute-view/relationships": {
|
|
41
|
+
"sources": "./src/compute-view/relationships-view/index.ts",
|
|
42
|
+
"default": {
|
|
43
|
+
"types": "./dist/compute-view/relationships-view/index.d.mts",
|
|
44
|
+
"import": "./dist/compute-view/relationships-view/index.mjs",
|
|
45
|
+
"default": "./dist/compute-view/relationships-view/index.mjs"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
40
48
|
"./compute-view": {
|
|
41
49
|
"sources": "./src/compute-view/index.ts",
|
|
42
50
|
"default": {
|
|
@@ -68,22 +76,24 @@
|
|
|
68
76
|
"import": "./dist/types/index.mjs",
|
|
69
77
|
"default": "./dist/types/index.mjs"
|
|
70
78
|
}
|
|
71
|
-
}
|
|
79
|
+
},
|
|
80
|
+
"./src": "./src/index.ts"
|
|
72
81
|
},
|
|
73
82
|
"publishConfig": {
|
|
74
83
|
"registry": "https://registry.npmjs.org",
|
|
75
84
|
"access": "public"
|
|
76
85
|
},
|
|
77
86
|
"dependencies": {
|
|
78
|
-
"type-fest": "4.
|
|
87
|
+
"type-fest": "^4.38.0"
|
|
79
88
|
},
|
|
80
89
|
"devDependencies": {
|
|
81
90
|
"@dagrejs/dagre": "1.1.4",
|
|
82
|
-
"@mantine/colors-generator": "7.17.
|
|
91
|
+
"@mantine/colors-generator": "7.17.2",
|
|
83
92
|
"@types/natural-compare-lite": "^1.4.2",
|
|
84
93
|
"@types/object-hash": "^3.0.6",
|
|
85
94
|
"chroma-js": "^3.1.2",
|
|
86
95
|
"defu": "^6.1.4",
|
|
96
|
+
"fdir": "^6.4.3",
|
|
87
97
|
"graphology": "^0.25.4",
|
|
88
98
|
"graphology-dag": "^0.4.1",
|
|
89
99
|
"graphology-types": "^0.24.8",
|
|
@@ -91,12 +101,12 @@
|
|
|
91
101
|
"natural-compare-lite": "^1.4.0",
|
|
92
102
|
"object-hash": "^3.0.0",
|
|
93
103
|
"obliterator": "^2.0.5",
|
|
94
|
-
"remeda": "^2.21.
|
|
104
|
+
"remeda": "^2.21.2",
|
|
95
105
|
"turbo": "^2.4.4",
|
|
96
|
-
"typescript": "5.8.2",
|
|
97
|
-
"unbuild": "^3.
|
|
98
|
-
"vitest": "^3.0.
|
|
99
|
-
"@likec4/tsconfig": "1.
|
|
106
|
+
"typescript": "^5.8.2",
|
|
107
|
+
"unbuild": "^3.5.0",
|
|
108
|
+
"vitest": "^3.0.9",
|
|
109
|
+
"@likec4/tsconfig": "1.27.1"
|
|
100
110
|
},
|
|
101
111
|
"scripts": {
|
|
102
112
|
"typecheck": "tsc --noEmit",
|
|
@@ -318,6 +318,80 @@ export function model<
|
|
|
318
318
|
op14: (input: ModelBuilder<N>) => ModelBuilder<O>,
|
|
319
319
|
op15: (input: ModelBuilder<O>) => ModelBuilder<P>,
|
|
320
320
|
): (input: Builder<A>) => Builder<P>
|
|
321
|
+
export function model<
|
|
322
|
+
A extends AnyTypes,
|
|
323
|
+
B extends AnyTypes,
|
|
324
|
+
C extends AnyTypes,
|
|
325
|
+
D extends AnyTypes,
|
|
326
|
+
E extends AnyTypes,
|
|
327
|
+
F extends AnyTypes,
|
|
328
|
+
G extends AnyTypes,
|
|
329
|
+
H extends AnyTypes,
|
|
330
|
+
I extends AnyTypes,
|
|
331
|
+
J extends AnyTypes,
|
|
332
|
+
K extends AnyTypes,
|
|
333
|
+
L extends AnyTypes,
|
|
334
|
+
M extends AnyTypes,
|
|
335
|
+
N extends AnyTypes,
|
|
336
|
+
O extends AnyTypes,
|
|
337
|
+
P extends AnyTypes,
|
|
338
|
+
Q extends AnyTypes,
|
|
339
|
+
>(
|
|
340
|
+
op1: (input: ModelBuilder<A>) => ModelBuilder<B>,
|
|
341
|
+
op2: (input: ModelBuilder<B>) => ModelBuilder<C>,
|
|
342
|
+
op3: (input: ModelBuilder<C>) => ModelBuilder<D>,
|
|
343
|
+
op4: (input: ModelBuilder<D>) => ModelBuilder<E>,
|
|
344
|
+
op5: (input: ModelBuilder<E>) => ModelBuilder<F>,
|
|
345
|
+
op6: (input: ModelBuilder<F>) => ModelBuilder<G>,
|
|
346
|
+
op7: (input: ModelBuilder<G>) => ModelBuilder<H>,
|
|
347
|
+
op8: (input: ModelBuilder<H>) => ModelBuilder<I>,
|
|
348
|
+
op9: (input: ModelBuilder<I>) => ModelBuilder<J>,
|
|
349
|
+
op10: (input: ModelBuilder<J>) => ModelBuilder<K>,
|
|
350
|
+
op11: (input: ModelBuilder<K>) => ModelBuilder<L>,
|
|
351
|
+
op12: (input: ModelBuilder<L>) => ModelBuilder<M>,
|
|
352
|
+
op13: (input: ModelBuilder<M>) => ModelBuilder<N>,
|
|
353
|
+
op14: (input: ModelBuilder<N>) => ModelBuilder<O>,
|
|
354
|
+
op15: (input: ModelBuilder<O>) => ModelBuilder<P>,
|
|
355
|
+
op16: (input: ModelBuilder<P>) => ModelBuilder<Q>,
|
|
356
|
+
): (input: Builder<A>) => Builder<Q>
|
|
357
|
+
export function model<
|
|
358
|
+
A extends AnyTypes,
|
|
359
|
+
B extends AnyTypes,
|
|
360
|
+
C extends AnyTypes,
|
|
361
|
+
D extends AnyTypes,
|
|
362
|
+
E extends AnyTypes,
|
|
363
|
+
F extends AnyTypes,
|
|
364
|
+
G extends AnyTypes,
|
|
365
|
+
H extends AnyTypes,
|
|
366
|
+
I extends AnyTypes,
|
|
367
|
+
J extends AnyTypes,
|
|
368
|
+
K extends AnyTypes,
|
|
369
|
+
L extends AnyTypes,
|
|
370
|
+
M extends AnyTypes,
|
|
371
|
+
N extends AnyTypes,
|
|
372
|
+
O extends AnyTypes,
|
|
373
|
+
P extends AnyTypes,
|
|
374
|
+
Q extends AnyTypes,
|
|
375
|
+
R extends AnyTypes,
|
|
376
|
+
>(
|
|
377
|
+
op1: (input: ModelBuilder<A>) => ModelBuilder<B>,
|
|
378
|
+
op2: (input: ModelBuilder<B>) => ModelBuilder<C>,
|
|
379
|
+
op3: (input: ModelBuilder<C>) => ModelBuilder<D>,
|
|
380
|
+
op4: (input: ModelBuilder<D>) => ModelBuilder<E>,
|
|
381
|
+
op5: (input: ModelBuilder<E>) => ModelBuilder<F>,
|
|
382
|
+
op6: (input: ModelBuilder<F>) => ModelBuilder<G>,
|
|
383
|
+
op7: (input: ModelBuilder<G>) => ModelBuilder<H>,
|
|
384
|
+
op8: (input: ModelBuilder<H>) => ModelBuilder<I>,
|
|
385
|
+
op9: (input: ModelBuilder<I>) => ModelBuilder<J>,
|
|
386
|
+
op10: (input: ModelBuilder<J>) => ModelBuilder<K>,
|
|
387
|
+
op11: (input: ModelBuilder<K>) => ModelBuilder<L>,
|
|
388
|
+
op12: (input: ModelBuilder<L>) => ModelBuilder<M>,
|
|
389
|
+
op13: (input: ModelBuilder<M>) => ModelBuilder<N>,
|
|
390
|
+
op14: (input: ModelBuilder<N>) => ModelBuilder<O>,
|
|
391
|
+
op15: (input: ModelBuilder<O>) => ModelBuilder<P>,
|
|
392
|
+
op16: (input: ModelBuilder<P>) => ModelBuilder<Q>,
|
|
393
|
+
op17: (input: ModelBuilder<Q>) => ModelBuilder<R>,
|
|
394
|
+
): (input: Builder<A>) => Builder<R>
|
|
321
395
|
|
|
322
396
|
export function model(...ops: any[]) {
|
|
323
397
|
return (input: Builder<any>) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LikeC4DeploymentModel } from '../../model'
|
|
1
|
+
import { LikeC4DeploymentModel } from '../../model/DeploymentModel'
|
|
2
2
|
import type { RelationshipModel } from '../../model/RelationModel'
|
|
3
3
|
import type { ExpressionV2, Filterable, OperatorPredicate } from '../../types'
|
|
4
4
|
import type { Ctx, Memory, Stage, StageExclude, StageInclude } from './memory'
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { filter, flatMap, isNonNullish, map, pick, pipe } from 'remeda'
|
|
2
2
|
import { invariant } from '../../../errors'
|
|
3
|
-
import type {
|
|
4
|
-
ConnectionModel,
|
|
5
|
-
DeploymentElementModel,
|
|
6
|
-
DeploymentRelationModel,
|
|
7
|
-
LikeC4DeploymentModel,
|
|
8
|
-
} from '../../../model'
|
|
9
3
|
import type { DeploymentConnectionModel } from '../../../model/connection/deployment'
|
|
10
4
|
import { findConnection, findConnectionsBetween, findConnectionsWithin } from '../../../model/connection/deployment'
|
|
11
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type ConnectionModel,
|
|
7
|
+
findConnectionsBetween as findModelConnectionsBetween,
|
|
8
|
+
} from '../../../model/connection/model'
|
|
9
|
+
import type { DeploymentElementModel, DeploymentRelationModel } from '../../../model/DeploymentElementModel'
|
|
10
|
+
import type { LikeC4DeploymentModel } from '../../../model/DeploymentModel'
|
|
12
11
|
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
13
12
|
import type { AnyAux } from '../../../model/types'
|
|
14
13
|
import { type Filterable, type OperatorPredicate, type RelationExpr, FqnExpr } from '../../../types'
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { anyPass, filter, pipe } from 'remeda'
|
|
2
2
|
import { invariant } from '../../../errors'
|
|
3
|
-
import type { AnyAux, RelationshipModel } from '../../../model'
|
|
4
3
|
import { findConnectionsBetween } from '../../../model/connection/deployment'
|
|
5
4
|
import type { LikeC4DeploymentModel } from '../../../model/DeploymentModel'
|
|
5
|
+
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
6
|
+
import type { AnyAux } from '../../../model/types'
|
|
6
7
|
import { type RelationExpr, FqnExpr } from '../../../types'
|
|
7
8
|
import { isAncestor } from '../../../utils'
|
|
8
9
|
import type { Connection, Elem, PredicateExecutor } from '../_types'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { anyPass, filter, pipe } from 'remeda'
|
|
2
2
|
import { invariant } from '../../../errors'
|
|
3
|
-
import type { LikeC4DeploymentModel } from '../../../model'
|
|
4
3
|
import { findConnection, findConnectionsBetween } from '../../../model/connection/deployment'
|
|
4
|
+
import type { LikeC4DeploymentModel } from '../../../model/DeploymentModel'
|
|
5
5
|
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
6
6
|
import { type RelationExpr, FqnExpr } from '../../../types'
|
|
7
7
|
import { isAncestor } from '../../../utils'
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { computeView, computeViews, unsafeComputeView } from './compute-view'
|
|
2
2
|
export type { ComputeViewResult } from './compute-view'
|
|
3
|
-
export { computeRelationshipsView } from './relationships-view/compute'
|
|
4
3
|
export { resolveRulesExtendedViews } from './utils/resolve-extended-views'
|
|
5
4
|
export { viewsWithReadableEdges, withReadableEdges } from './utils/with-readable-edges'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ElementModel } from '../../model/ElementModel'
|
|
2
2
|
import type { RelationshipModel } from '../../model/RelationModel'
|
|
3
|
+
import type { AnyAux } from '../../model/types'
|
|
3
4
|
|
|
4
5
|
// type Pair = {
|
|
5
6
|
// left: ReadonlySet<ElementModel>
|
|
@@ -7,10 +8,10 @@ import type { RelationshipModel } from '../../model/RelationModel'
|
|
|
7
8
|
// right: ReadonlySet<ElementModel>
|
|
8
9
|
// }
|
|
9
10
|
|
|
10
|
-
export type RelationshipsViewData = {
|
|
11
|
-
incomers: ReadonlySet<ElementModel
|
|
12
|
-
incoming: ReadonlySet<RelationshipModel
|
|
13
|
-
subjects: ReadonlySet<ElementModel
|
|
14
|
-
outgoing: ReadonlySet<RelationshipModel
|
|
15
|
-
outgoers: ReadonlySet<ElementModel
|
|
11
|
+
export type RelationshipsViewData<M extends AnyAux = AnyAux> = {
|
|
12
|
+
incomers: ReadonlySet<ElementModel<M>>
|
|
13
|
+
incoming: ReadonlySet<RelationshipModel<M>>
|
|
14
|
+
subjects: ReadonlySet<ElementModel<M>>
|
|
15
|
+
outgoing: ReadonlySet<RelationshipModel<M>>
|
|
16
|
+
outgoers: ReadonlySet<ElementModel<M>>
|
|
16
17
|
}
|
|
@@ -1,74 +1,162 @@
|
|
|
1
|
+
import { pipe } from 'remeda'
|
|
2
|
+
import { invariant } from '../../errors'
|
|
1
3
|
import type { ElementModel } from '../../model/ElementModel'
|
|
2
4
|
import type { LikeC4Model } from '../../model/LikeC4Model'
|
|
3
5
|
import type { RelationshipModel } from '../../model/RelationModel'
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
6
|
+
import type { AnyAux } from '../../model/types'
|
|
7
|
+
import type { LikeC4ViewModel } from '../../model/view/LikeC4ViewModel'
|
|
8
|
+
import { isDescendantOf, sortParentsFirst } from '../../utils/fqn'
|
|
9
|
+
import { ifilter, imap, toArray, toSet } from '../../utils/iterable'
|
|
10
|
+
import type { RelationshipsViewData } from './_types'
|
|
11
|
+
import { treeFromElements } from './utils'
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
): {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} {
|
|
17
|
-
const subject = likec4model.element(subjectId)
|
|
18
|
-
let relationships = {
|
|
19
|
-
incoming: [...subject.incoming()],
|
|
20
|
-
outgoing: [...subject.outgoing()],
|
|
13
|
+
const finalize = <M extends AnyAux>(
|
|
14
|
+
elements: Set<ElementModel<M>>,
|
|
15
|
+
explicits: Set<ElementModel<M>>,
|
|
16
|
+
): Set<ElementModel<M>> => {
|
|
17
|
+
if (elements.size > 2 && explicits.size !== elements.size) {
|
|
18
|
+
return new Set(sortParentsFirst([
|
|
19
|
+
...treeFromElements(elements).flatten(),
|
|
20
|
+
...explicits,
|
|
21
|
+
]))
|
|
21
22
|
}
|
|
23
|
+
if (elements.size > 1) {
|
|
24
|
+
return new Set(sortParentsFirst([...elements]))
|
|
25
|
+
}
|
|
26
|
+
return elements
|
|
27
|
+
}
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
function computeRelationships<const M extends AnyAux>(
|
|
30
|
+
subject: ElementModel<M>,
|
|
31
|
+
others: Set<ElementModel<M>>,
|
|
32
|
+
relationships: {
|
|
33
|
+
incoming: ReadonlyArray<RelationshipModel<M>>
|
|
34
|
+
outgoing: ReadonlyArray<RelationshipModel<M>>
|
|
35
|
+
},
|
|
36
|
+
): Omit<RelationshipsViewData<M>, 'subjectExistsInScope'> {
|
|
37
|
+
const isOther = (e: ElementModel<M>) => others.has(e)
|
|
38
|
+
let subjects = new Set([subject])
|
|
39
|
+
const explicits = {
|
|
40
|
+
incomers: new Set<ElementModel<M>>(),
|
|
41
|
+
subjects: new Set([subject]),
|
|
42
|
+
outgoers: new Set<ElementModel<M>>(),
|
|
43
|
+
}
|
|
26
44
|
|
|
27
45
|
// Iterate over incoming and outgoing relationships
|
|
28
46
|
// and add the source and target elements to appropriate sets.
|
|
29
|
-
// If element is deeper in the hierarchy -
|
|
30
|
-
|
|
47
|
+
// If element is deeper in the hierarchy - add all ancestors until that is a sibling of the subject
|
|
48
|
+
let incomers = new Set(relationships.incoming.flatMap((r) => {
|
|
49
|
+
explicits.subjects.add(r.target)
|
|
50
|
+
explicits.incomers.add(r.source)
|
|
51
|
+
|
|
31
52
|
subjects.add(r.target)
|
|
32
|
-
if (
|
|
33
|
-
|
|
53
|
+
if (r.target !== subject) {
|
|
54
|
+
let target = r.target.parent
|
|
55
|
+
while (target && target !== subject) {
|
|
56
|
+
subjects.add(target)
|
|
57
|
+
target = target.parent
|
|
58
|
+
}
|
|
34
59
|
}
|
|
35
|
-
|
|
36
|
-
|
|
60
|
+
|
|
61
|
+
let source = r.source
|
|
62
|
+
const incomerBranch = [] as ElementModel<M>[]
|
|
63
|
+
while (true) {
|
|
64
|
+
incomerBranch.push(source)
|
|
65
|
+
if (isOther(source) || !source.parent) {
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
source = source.parent
|
|
69
|
+
}
|
|
70
|
+
return incomerBranch
|
|
37
71
|
}))
|
|
38
|
-
|
|
72
|
+
|
|
73
|
+
let outgoers = new Set(relationships.outgoing.flatMap((r) => {
|
|
74
|
+
explicits.subjects.add(r.source)
|
|
75
|
+
explicits.outgoers.add(r.target)
|
|
76
|
+
|
|
39
77
|
subjects.add(r.source)
|
|
40
|
-
if (
|
|
41
|
-
|
|
78
|
+
if (r.source !== subject) {
|
|
79
|
+
let source = r.source.parent
|
|
80
|
+
while (source && source !== subject) {
|
|
81
|
+
subjects.add(source)
|
|
82
|
+
source = source.parent
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let target = r.target
|
|
87
|
+
const outgoerBranch = [] as ElementModel<M>[]
|
|
88
|
+
while (true) {
|
|
89
|
+
outgoerBranch.push(target)
|
|
90
|
+
if (isOther(target) || !target.parent) {
|
|
91
|
+
break
|
|
92
|
+
}
|
|
93
|
+
target = target.parent
|
|
42
94
|
}
|
|
43
|
-
|
|
44
|
-
return parent ? [r.target, parent] : [r.target]
|
|
95
|
+
return outgoerBranch
|
|
45
96
|
}))
|
|
46
97
|
|
|
47
98
|
return {
|
|
48
|
-
incomers,
|
|
99
|
+
incomers: finalize(incomers, explicits.incomers),
|
|
49
100
|
incoming: new Set(relationships.incoming),
|
|
50
|
-
subjects,
|
|
101
|
+
subjects: finalize(subjects, explicits.subjects),
|
|
51
102
|
outgoing: new Set(relationships.outgoing),
|
|
52
|
-
outgoers,
|
|
103
|
+
outgoers: finalize(outgoers, explicits.outgoers),
|
|
53
104
|
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function computeRelationshipsView<const M extends AnyAux>(
|
|
108
|
+
subjectId: NoInfer<M['Element']>,
|
|
109
|
+
likec4model: LikeC4Model<M>,
|
|
110
|
+
scopeViewId: NoInfer<M['View']> | null,
|
|
111
|
+
scope: 'global' | 'view' = 'global',
|
|
112
|
+
): RelationshipsViewData<M> {
|
|
113
|
+
let subjectExistsInScope = true
|
|
114
|
+
const view = scopeViewId ? likec4model.findView(scopeViewId) : null
|
|
115
|
+
if (scope === 'view') {
|
|
116
|
+
invariant(
|
|
117
|
+
view,
|
|
118
|
+
'Scope view id is required when scope is "view"',
|
|
119
|
+
)
|
|
120
|
+
return computeScopedRelationshipsView(subjectId, view, likec4model)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const subject = likec4model.element(subjectId)
|
|
124
|
+
const subjectSiblings = toSet(subject.ascendingSiblings())
|
|
125
|
+
return computeRelationships(
|
|
126
|
+
subject,
|
|
127
|
+
subjectSiblings,
|
|
128
|
+
{
|
|
129
|
+
incoming: [...subject.incoming()],
|
|
130
|
+
outgoing: [...subject.outgoing()],
|
|
131
|
+
},
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function computeScopedRelationshipsView<const M extends AnyAux>(
|
|
136
|
+
subjectId: NoInfer<M['Element']>,
|
|
137
|
+
view: LikeC4ViewModel<M>,
|
|
138
|
+
likec4model: LikeC4Model<M>,
|
|
139
|
+
): RelationshipsViewData<M> {
|
|
140
|
+
const subject = likec4model.element(subjectId)
|
|
141
|
+
let relationships = {
|
|
142
|
+
incoming: toArray(ifilter(subject.incoming(), (r) => view.includesRelation(r.id))),
|
|
143
|
+
outgoing: toArray(ifilter(subject.outgoing(), (r) => view.includesRelation(r.id))),
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const isDescendant = isDescendantOf(subject)
|
|
147
|
+
|
|
148
|
+
const others = new Set([
|
|
149
|
+
...subject.ascendingSiblings(),
|
|
150
|
+
...pipe(
|
|
151
|
+
view.elements(),
|
|
152
|
+
imap((e) => e.element),
|
|
153
|
+
ifilter((e): e is ElementModel<M> => e !== subject && isDescendant(e)),
|
|
154
|
+
),
|
|
155
|
+
])
|
|
54
156
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// direction: 'LR',
|
|
61
|
-
// },
|
|
62
|
-
// tags: null,
|
|
63
|
-
// links: null,
|
|
64
|
-
// hash: 'empty',
|
|
65
|
-
// customColorDefinitions: {},
|
|
66
|
-
// ...layoutRelationshipsView({
|
|
67
|
-
// incomers,
|
|
68
|
-
// incoming: new Set(relationships.incoming),
|
|
69
|
-
// subjects,
|
|
70
|
-
// outgoing: new Set(relationships.outgoing),
|
|
71
|
-
// outgoers,
|
|
72
|
-
// }),
|
|
73
|
-
// }
|
|
157
|
+
return computeRelationships(
|
|
158
|
+
subject,
|
|
159
|
+
others,
|
|
160
|
+
relationships,
|
|
161
|
+
)
|
|
74
162
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { nonNullable } from '../../errors'
|
|
2
2
|
import type { ElementModel } from '../../model/ElementModel'
|
|
3
|
-
import type {
|
|
3
|
+
import type { AnyAux } from '../../model/types'
|
|
4
4
|
import { isAncestor, isDescendantOf, sortParentsFirst } from '../../utils/fqn'
|
|
5
5
|
import { DefaultMap } from '../../utils/mnemonist'
|
|
6
6
|
|
|
7
|
-
export function treeFromElements(elements: Iterable<ElementModel
|
|
8
|
-
const sorted = sortParentsFirst([...elements]) as ReadonlyArray<ElementModel
|
|
7
|
+
export function treeFromElements<const M extends AnyAux>(elements: Iterable<ElementModel<M>>) {
|
|
8
|
+
const sorted = sortParentsFirst([...elements]) as ReadonlyArray<ElementModel<M>>
|
|
9
9
|
const root = new Set(sorted)
|
|
10
|
-
const map = new Map(sorted.map(e => [e.
|
|
11
|
-
const parents = new DefaultMap<ElementModel
|
|
10
|
+
const map = new Map(sorted.map(e => [e._literalId, e]))
|
|
11
|
+
const parents = new DefaultMap<ElementModel<M>, ElementModel<M> | null>(() => null)
|
|
12
12
|
const children = sorted.reduce((acc, parent, index, all) => {
|
|
13
13
|
acc.set(
|
|
14
14
|
parent,
|
|
@@ -25,16 +25,49 @@ export function treeFromElements(elements: Iterable<ElementModel>) {
|
|
|
25
25
|
parents.set(el, parent)
|
|
26
26
|
}
|
|
27
27
|
return acc
|
|
28
|
-
}, [] as ElementModel[]),
|
|
28
|
+
}, [] as ElementModel<M>[]),
|
|
29
29
|
)
|
|
30
30
|
return acc
|
|
31
|
-
}, new DefaultMap<ElementModel
|
|
31
|
+
}, new DefaultMap<ElementModel<M>, ElementModel<M>[]>(() => []))
|
|
32
32
|
|
|
33
33
|
return {
|
|
34
34
|
sorted,
|
|
35
|
-
byId: (id:
|
|
36
|
-
root: root as ReadonlySet<ElementModel
|
|
37
|
-
parent: (el: ElementModel) => parents.get(el),
|
|
38
|
-
children: (el: ElementModel): ReadonlyArray<ElementModel
|
|
35
|
+
byId: (id: M['Element']): ElementModel<M> => nonNullable(map.get(id), `Element not found by id: ${id}`),
|
|
36
|
+
root: root as ReadonlySet<ElementModel<M>>,
|
|
37
|
+
parent: (el: ElementModel<M>): ElementModel<M> | null => parents.get(el),
|
|
38
|
+
children: (el: ElementModel<M>): ReadonlyArray<ElementModel<M>> => children.get(el),
|
|
39
|
+
/**
|
|
40
|
+
* Flattens the tree structure by removing redundant hierarchy levels.
|
|
41
|
+
* @example
|
|
42
|
+
* A
|
|
43
|
+
* └── B
|
|
44
|
+
* ├── C
|
|
45
|
+
* │ └── D
|
|
46
|
+
* │ └── E
|
|
47
|
+
* └── F
|
|
48
|
+
* └── G
|
|
49
|
+
* becomes
|
|
50
|
+
* A
|
|
51
|
+
* ├── C
|
|
52
|
+
* │ └── E
|
|
53
|
+
* └── F
|
|
54
|
+
* └── G
|
|
55
|
+
*/
|
|
56
|
+
flatten: (): Set<ElementModel<M>> => {
|
|
57
|
+
return new Set([
|
|
58
|
+
...root,
|
|
59
|
+
...sorted.reduce((acc, el) => {
|
|
60
|
+
const _children = children.get(el)
|
|
61
|
+
if (_children.length === 0) {
|
|
62
|
+
acc.push(el)
|
|
63
|
+
return acc
|
|
64
|
+
}
|
|
65
|
+
if (_children.length > 1) {
|
|
66
|
+
acc.push(..._children)
|
|
67
|
+
}
|
|
68
|
+
return acc
|
|
69
|
+
}, [] as ElementModel<M>[]),
|
|
70
|
+
])
|
|
71
|
+
},
|
|
39
72
|
}
|
|
40
73
|
}
|
|
@@ -19,9 +19,6 @@ export function calcViewLayoutHash<V extends ComputedView>(view: SetOptional<V,
|
|
|
19
19
|
mapToObj(({ source, target, ...edge }) => [`${source}:${target}`, edge]),
|
|
20
20
|
),
|
|
21
21
|
}
|
|
22
|
-
view.hash = objectHash(tohash
|
|
23
|
-
ignoreUnknown: true,
|
|
24
|
-
respectType: false,
|
|
25
|
-
})
|
|
22
|
+
view.hash = objectHash(tohash)
|
|
26
23
|
return view as unknown as V
|
|
27
24
|
}
|
package/src/index.ts
CHANGED
|
@@ -36,9 +36,9 @@ export * from './utils'
|
|
|
36
36
|
export * as Expr from './types/expression'
|
|
37
37
|
|
|
38
38
|
export {
|
|
39
|
-
computeRelationshipsView,
|
|
40
39
|
computeView,
|
|
41
40
|
computeViews,
|
|
42
41
|
unsafeComputeView,
|
|
43
42
|
} from './compute-view'
|
|
43
|
+
|
|
44
44
|
export type { ComputeViewResult } from './compute-view'
|
|
@@ -25,6 +25,7 @@ export type ElementsIterator<M extends AnyAux> = IteratorLike<ElementModel<M>>
|
|
|
25
25
|
|
|
26
26
|
export class ElementModel<M extends AnyAux = AnyAux> {
|
|
27
27
|
readonly id: M['Fqn']
|
|
28
|
+
readonly _literalId: M['Element']
|
|
28
29
|
readonly hierarchyLevel: number
|
|
29
30
|
|
|
30
31
|
constructor(
|
|
@@ -32,6 +33,7 @@ export class ElementModel<M extends AnyAux = AnyAux> {
|
|
|
32
33
|
public readonly $element: C4Element,
|
|
33
34
|
) {
|
|
34
35
|
this.id = this.$element.id
|
|
36
|
+
this._literalId = this.$element.id
|
|
35
37
|
this.hierarchyLevel = hierarchyLevel(this.id)
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type DeploymentNodeKind,
|
|
5
5
|
type DiagramView,
|
|
6
6
|
type ElementShape as C4ElementShape,
|
|
7
|
+
type ElementStyle,
|
|
7
8
|
type IconUrl,
|
|
8
9
|
type IteratorLike,
|
|
9
10
|
type Link,
|
|
@@ -114,6 +115,10 @@ export class NodeModel<M extends AnyAux, V extends ComputedView | DiagramView =
|
|
|
114
115
|
return this.$node.navigateTo ? this.$view.$model.view(this.$node.navigateTo) : null
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
get style(): ElementStyle {
|
|
119
|
+
return this.$node.style
|
|
120
|
+
}
|
|
121
|
+
|
|
117
122
|
/**
|
|
118
123
|
* Get all ancestor elements (i.e. parent, parent’s parent, etc.)
|
|
119
124
|
* (from closest to root)
|
package/src/utils/object-hash.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import objecthash from 'object-hash'
|
|
2
|
+
import { isNonNullish } from 'remeda'
|
|
3
|
+
import { invariant } from '../errors'
|
|
2
4
|
|
|
3
|
-
export
|
|
5
|
+
export function objectHash(value: any): string {
|
|
6
|
+
invariant(
|
|
7
|
+
typeof value === 'object' && isNonNullish(value),
|
|
8
|
+
'objectHash: value must be an object',
|
|
9
|
+
)
|
|
10
|
+
return objecthash(value, {
|
|
11
|
+
ignoreUnknown: true,
|
|
12
|
+
respectType: false,
|
|
13
|
+
})
|
|
14
|
+
}
|