@likec4/core 1.40.0 → 1.41.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.
- package/dist/builder/index.d.mts +75 -14
- package/dist/builder/index.mjs +111 -47
- package/dist/compute-view/index.d.mts +5 -5
- package/dist/compute-view/index.mjs +3 -3
- package/dist/compute-view/relationships-view/index.d.mts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +4 -4
- package/dist/model/connection/deployment/index.d.mts +7 -7
- package/dist/model/connection/deployment/index.mjs +1 -1
- package/dist/model/connection/index.d.mts +8 -8
- package/dist/model/connection/index.mjs +1 -1
- package/dist/model/connection/model/index.d.mts +7 -7
- package/dist/model/index.d.mts +10 -10
- package/dist/model/index.mjs +3 -3
- package/dist/shared/{core.DHQ32OHa.mjs → core.Ae86Y3tB.mjs} +2 -3
- package/dist/shared/{core.FPITT8Vx.mjs → core.Asc11Mjy.mjs} +28 -9
- package/dist/shared/{core.CySf-2gP.d.mts → core.BXsxn-hf.d.mts} +29 -4
- package/dist/shared/core.BazD6cot.mjs +14 -0
- package/dist/shared/{core.D_fI5Lf6.d.mts → core.BhpgpOep.d.mts} +24 -4
- package/dist/shared/{core.DuR3R-xJ.mjs → core.BvMRMo7Z.mjs} +68 -46
- package/dist/shared/{core.BbThmf6M.d.mts → core.CUG6vpyL.d.mts} +2 -2
- package/dist/shared/{core.yHe79zEl.mjs → core.CXy1NLUG.mjs} +2 -2
- package/dist/shared/{core.Bpou6c2f.d.mts → core.D-ZGWDDt.d.mts} +19 -38
- package/dist/shared/{core.BIAlzwKc.mjs → core.DA5vzwZ9.mjs} +30 -4
- package/dist/shared/{core.T-eLZTW_.mjs → core.DJp8fgok.mjs} +6 -2
- package/dist/shared/{core.DrvfVXkh.d.mts → core.DgesAwC8.d.mts} +11 -2
- package/dist/shared/{core.vWJTxpLV.d.mts → core.FUPplpae.d.mts} +1 -1
- package/dist/shared/{core.DrVOJuxX.d.mts → core.hfOE3RfK.d.mts} +4 -4
- package/dist/shared/{core.D704WZES.d.mts → core.xSQO0XYD.d.mts} +2 -2
- package/dist/shared/{core.kxeJe0OY.d.mts → core.zxTBF_PD.d.mts} +3 -3
- package/dist/types/_aux.d.mts +2 -2
- package/dist/types/_aux.mjs +1 -5
- package/dist/types/expression-model.d.mts +3 -3
- package/dist/types/expression.d.mts +3 -3
- package/dist/types/fqnRef.d.mts +2 -2
- package/dist/types/index.d.mts +6 -6
- package/dist/types/index.mjs +4 -4
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/iterable/index.d.mts +1 -1
- package/package.json +2 -2
- package/src/builder/Builder.ts +119 -43
- package/src/builder/_types.ts +12 -8
- package/src/compute-view/deployment-view/utils.ts +47 -36
- package/src/compute-view/relationships-view/layout.ts +7 -6
- package/src/compute-view/utils/buildComputedNodes.ts +23 -10
- package/src/compute-view/utils/buildElementNotations.ts +2 -1
- package/src/model/DeploymentElementModel.ts +33 -3
- package/src/model/ElementModel.ts +25 -2
- package/src/model/RelationModel.ts +1 -1
- package/src/model/view/EdgeModel.ts +3 -2
- package/src/model/view/LikeC4ViewModel.ts +1 -1
- package/src/model/view/NodeModel.ts +1 -1
- package/src/types/RichText.ts +2 -3
- package/src/types/_aux.ts +26 -0
- package/src/types/_common.ts +23 -1
- package/src/types/index.ts +2 -0
- package/src/types/model-deployment.ts +8 -18
- package/src/types/model-logical.ts +4 -13
- package/src/types/model-spec.ts +3 -0
- package/src/types/view-computed.ts +21 -13
- package/src/types/view-layouted.ts +0 -4
package/src/builder/Builder.ts
CHANGED
|
@@ -2,7 +2,9 @@ import defu from 'defu'
|
|
|
2
2
|
import {
|
|
3
3
|
entries,
|
|
4
4
|
fromEntries,
|
|
5
|
+
fromKeys,
|
|
5
6
|
hasAtLeast,
|
|
7
|
+
isArray,
|
|
6
8
|
isFunction,
|
|
7
9
|
isNonNullish,
|
|
8
10
|
isNullish,
|
|
@@ -23,6 +25,7 @@ import type {
|
|
|
23
25
|
ParsedElementView,
|
|
24
26
|
ParsedLikeC4ModelData,
|
|
25
27
|
Specification,
|
|
28
|
+
TagSpecification,
|
|
26
29
|
} from '../types'
|
|
27
30
|
import {
|
|
28
31
|
type Color,
|
|
@@ -200,30 +203,51 @@ interface Internals<T extends AnyTypes> extends ViewsBuilder<T>, ModelBuilder<T>
|
|
|
200
203
|
|
|
201
204
|
type Op<T> = (b: T) => T
|
|
202
205
|
|
|
203
|
-
function
|
|
204
|
-
|
|
206
|
+
function ensureObj<T>(value: string[] | Record<string, Partial<T>>): Record<string, Partial<T>> {
|
|
207
|
+
return isArray(value) ? fromKeys(value, v => ({})) : value
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function validateSpec({ tags, elements, deployments, relationships, ...specification }: BuilderSpecification) {
|
|
211
|
+
const spectags = {} as Record<string, Partial<TagSpecification>>
|
|
212
|
+
if (tags) {
|
|
213
|
+
Object.assign(
|
|
214
|
+
spectags,
|
|
215
|
+
ensureObj(tags),
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
const _elements = ensureObj(elements)
|
|
219
|
+
|
|
220
|
+
for (const [kind, spec] of entries(_elements)) {
|
|
205
221
|
if (spec.tags) {
|
|
206
222
|
for (const tag of spec.tags) {
|
|
207
|
-
invariant(
|
|
223
|
+
invariant(tag in spectags, `Invalid specification for element kind "${kind}": tag "${tag}" not found`)
|
|
208
224
|
}
|
|
209
225
|
}
|
|
210
226
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
227
|
+
const _deployments = ensureObj(deployments ?? {})
|
|
228
|
+
|
|
229
|
+
for (const [kind, spec] of entries(_deployments)) {
|
|
230
|
+
if (spec.tags) {
|
|
231
|
+
for (const tag of spec.tags) {
|
|
232
|
+
invariant(
|
|
233
|
+
tag in spectags,
|
|
234
|
+
`Invalid specification for deployment kind "${kind}": tag "${tag}" not found`,
|
|
235
|
+
)
|
|
220
236
|
}
|
|
221
237
|
}
|
|
222
238
|
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
...specification,
|
|
242
|
+
tags: spectags,
|
|
243
|
+
elements: _elements,
|
|
244
|
+
deployments: _deployments,
|
|
245
|
+
relationships: ensureObj(relationships ?? {}),
|
|
246
|
+
}
|
|
223
247
|
}
|
|
224
248
|
|
|
225
249
|
function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
226
|
-
|
|
250
|
+
_spec: Spec,
|
|
227
251
|
_elements = new Map<string, Element<Any>>(),
|
|
228
252
|
_relations = [] as ModelRelation[],
|
|
229
253
|
_views = new Map<string, LikeC4View>(),
|
|
@@ -235,22 +259,17 @@ function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
|
235
259
|
_deployments = new Map<string, DeploymentElement>(),
|
|
236
260
|
_deploymentRelations = [] as DeploymentRelation[],
|
|
237
261
|
): Builder<T> {
|
|
262
|
+
const spec = validateSpec(_spec)
|
|
263
|
+
|
|
238
264
|
const toLikeC4Specification = (): Specification<Types.ToAux<T>> => {
|
|
239
|
-
validateSpec(spec)
|
|
240
265
|
return ({
|
|
241
|
-
elements:
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
...structuredClone(spec.deployments) as any,
|
|
246
|
-
},
|
|
247
|
-
relationships: {
|
|
248
|
-
...structuredClone(spec.relationships) as any,
|
|
249
|
-
},
|
|
250
|
-
tags: mapValues(spec.tags ?? {}, (tagSpec) => ({
|
|
266
|
+
elements: structuredClone(spec.elements),
|
|
267
|
+
deployments: structuredClone(spec.deployments),
|
|
268
|
+
relationships: structuredClone(spec.relationships),
|
|
269
|
+
tags: mapValues(spec.tags, (tagSpec) => ({
|
|
251
270
|
color: DefaultThemeColor,
|
|
252
271
|
...tagSpec,
|
|
253
|
-
}))
|
|
272
|
+
})),
|
|
254
273
|
...(!!spec.metadataKeys ? { metadataKeys: spec.metadataKeys as any } : {}),
|
|
255
274
|
customColors: {},
|
|
256
275
|
} as Specification<Types.ToAux<T>>)
|
|
@@ -510,7 +529,8 @@ function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
|
510
529
|
icon: _icon,
|
|
511
530
|
style,
|
|
512
531
|
title,
|
|
513
|
-
description
|
|
532
|
+
description,
|
|
533
|
+
summary,
|
|
514
534
|
...props
|
|
515
535
|
} = typeof _props === 'string' ? { title: _props } : { ..._props }
|
|
516
536
|
|
|
@@ -525,8 +545,7 @@ function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
|
525
545
|
kind: kind as any,
|
|
526
546
|
title: title ?? nameFromFqn(_id),
|
|
527
547
|
...(description && { description: { txt: description } }),
|
|
528
|
-
|
|
529
|
-
tags: [],
|
|
548
|
+
...(summary && { summary: { txt: summary } }),
|
|
530
549
|
color: specStyle?.color ?? DefaultThemeColor as Color,
|
|
531
550
|
shape: specStyle?.shape ?? DefaultElementShape as ElementShape,
|
|
532
551
|
style: pickBy({
|
|
@@ -537,7 +556,7 @@ function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
|
537
556
|
textSize: specStyle?.textSize,
|
|
538
557
|
...style,
|
|
539
558
|
}, isNonNullish),
|
|
540
|
-
links,
|
|
559
|
+
...(links && { links }),
|
|
541
560
|
...icon && { icon: icon as IconUrl },
|
|
542
561
|
...spec,
|
|
543
562
|
...props,
|
|
@@ -704,7 +723,8 @@ function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
|
704
723
|
const {
|
|
705
724
|
links,
|
|
706
725
|
title,
|
|
707
|
-
description
|
|
726
|
+
description,
|
|
727
|
+
summary,
|
|
708
728
|
...props
|
|
709
729
|
} = typeof _props === 'string' ? { title: _props } : { ..._props }
|
|
710
730
|
const _id = b.__deploymentFqn(id)
|
|
@@ -713,6 +733,7 @@ function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
|
713
733
|
id: _id,
|
|
714
734
|
element: target as Fqn,
|
|
715
735
|
...title && { title },
|
|
736
|
+
...(summary && { summary: { txt: summary } }),
|
|
716
737
|
...(description && { description: { txt: description } }),
|
|
717
738
|
...links && { links: mapLinks(links) },
|
|
718
739
|
...props,
|
|
@@ -757,7 +778,8 @@ function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
|
757
778
|
icon: _icon,
|
|
758
779
|
style,
|
|
759
780
|
title,
|
|
760
|
-
description
|
|
781
|
+
description,
|
|
782
|
+
summary,
|
|
761
783
|
...props
|
|
762
784
|
} = typeof _props === 'string' ? { title: _props } : { ..._props }
|
|
763
785
|
|
|
@@ -770,8 +792,7 @@ function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
|
770
792
|
kind: kind as any,
|
|
771
793
|
title: title ?? nameFromFqn(_id),
|
|
772
794
|
...(description && { description: { txt: description } }),
|
|
773
|
-
|
|
774
|
-
tags: null,
|
|
795
|
+
...(summary && { summary: { txt: summary } }),
|
|
775
796
|
style: {
|
|
776
797
|
...icon && { icon: icon as IconUrl },
|
|
777
798
|
color: specStyle?.color ?? DefaultThemeColor as Color,
|
|
@@ -841,11 +862,40 @@ function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
|
841
862
|
|
|
842
863
|
return self
|
|
843
864
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
865
|
+
export const Builder = {
|
|
866
|
+
/**
|
|
867
|
+
* Creates a builder with compositional methods
|
|
868
|
+
*
|
|
869
|
+
* @example
|
|
870
|
+
* ```ts
|
|
871
|
+
* const {
|
|
872
|
+
* model: { model, system, component, relTo },
|
|
873
|
+
* deployment: { env, vm},
|
|
874
|
+
* views: { view, $include },
|
|
875
|
+
* builder,
|
|
876
|
+
* } = Builder.forSpecification({
|
|
877
|
+
* elements: {
|
|
878
|
+
* system: {},
|
|
879
|
+
* component: {},
|
|
880
|
+
* },
|
|
881
|
+
* deployments: ['env', 'vm'],
|
|
882
|
+
* })
|
|
883
|
+
*
|
|
884
|
+
* const b = builder
|
|
885
|
+
* .with(
|
|
886
|
+
* model(
|
|
887
|
+
* system('cloud').with(
|
|
888
|
+
* component('backend'),
|
|
889
|
+
* component('backend.api'),
|
|
890
|
+
* component('frontend').with(
|
|
891
|
+
* relTo('cloud.backend.api'),
|
|
892
|
+
* ),
|
|
893
|
+
* ),
|
|
894
|
+
* ),
|
|
895
|
+
* )
|
|
896
|
+
* ```
|
|
897
|
+
*/
|
|
898
|
+
forSpecification<const Spec extends BuilderSpecification>(
|
|
849
899
|
spec: Spec,
|
|
850
900
|
): {
|
|
851
901
|
builder: Builder<Types.FromSpecification<Spec>>
|
|
@@ -853,18 +903,44 @@ export namespace Builder {
|
|
|
853
903
|
deployment: DeloymentModelHelpers<Types.FromSpecification<Spec>>
|
|
854
904
|
views: ViewsHelpers
|
|
855
905
|
} {
|
|
856
|
-
validateSpec(spec)
|
|
857
906
|
const b = builder<Spec, Types.FromSpecification<Spec>>(spec)
|
|
858
907
|
return {
|
|
859
908
|
...b.helpers(),
|
|
860
909
|
builder: b,
|
|
861
910
|
}
|
|
862
|
-
}
|
|
911
|
+
},
|
|
863
912
|
|
|
864
|
-
|
|
913
|
+
/**
|
|
914
|
+
* Creates a builder with chainable methods
|
|
915
|
+
*
|
|
916
|
+
* @example
|
|
917
|
+
* ```ts
|
|
918
|
+
* const b = Builder
|
|
919
|
+
* .specification({
|
|
920
|
+
* elements: ['system', 'component'],
|
|
921
|
+
* deployments: ['env', 'vm'],
|
|
922
|
+
* })
|
|
923
|
+
* .model(({ system, component, relTo }, _) =>
|
|
924
|
+
* _(
|
|
925
|
+
* system('cloud').with(
|
|
926
|
+
* component('backend').with(
|
|
927
|
+
* component('api'),
|
|
928
|
+
* ),
|
|
929
|
+
* component('frontend').with(
|
|
930
|
+
* relTo('cloud.backend.api'),
|
|
931
|
+
* )
|
|
932
|
+
* )
|
|
933
|
+
* )
|
|
934
|
+
* )
|
|
935
|
+
* ```
|
|
936
|
+
*/
|
|
937
|
+
specification<const Spec extends BuilderSpecification>(
|
|
865
938
|
spec: Spec,
|
|
866
939
|
): Builder<Types.FromSpecification<Spec>> {
|
|
867
|
-
validateSpec(spec)
|
|
868
940
|
return builder<Spec, Types.FromSpecification<Spec>>(spec)
|
|
869
|
-
}
|
|
941
|
+
},
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
export namespace Builder {
|
|
945
|
+
export type Any = Builder<AnyTypes>
|
|
870
946
|
}
|
package/src/builder/_types.ts
CHANGED
|
@@ -26,16 +26,16 @@ type ElementSpecification = Omit<ElementKindSpecification, 'tags'> & {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export type BuilderSpecification = {
|
|
29
|
-
elements: {
|
|
29
|
+
elements: string[] | {
|
|
30
30
|
[kind: string]: Partial<ElementSpecification>
|
|
31
31
|
}
|
|
32
|
-
relationships?: {
|
|
32
|
+
relationships?: string[] | {
|
|
33
33
|
[kind: string]: Partial<RelationshipKindSpecification>
|
|
34
34
|
}
|
|
35
|
-
deployments?: {
|
|
35
|
+
deployments?: string[] | {
|
|
36
36
|
[kind: string]: Partial<ElementSpecification>
|
|
37
37
|
}
|
|
38
|
-
tags?: {
|
|
38
|
+
tags?: string[] | {
|
|
39
39
|
[kind: string]: Partial<TagSpecification>
|
|
40
40
|
}
|
|
41
41
|
metadataKeys?: string[]
|
|
@@ -59,6 +59,7 @@ export type Metadata<MetadataKey extends string> = IsNever<MetadataKey> extends
|
|
|
59
59
|
|
|
60
60
|
export type NewElementProps<Tag, Metadata> = {
|
|
61
61
|
title?: string
|
|
62
|
+
summary?: string
|
|
62
63
|
description?: string
|
|
63
64
|
technology?: string
|
|
64
65
|
tags?: IfNever<Tag, never, [Tag, ...Tag[]]>
|
|
@@ -79,6 +80,7 @@ export type NewElementProps<Tag, Metadata> = {
|
|
|
79
80
|
|
|
80
81
|
export type NewDeploymentNodeProps<Tag, Metadata> = {
|
|
81
82
|
title?: string
|
|
83
|
+
summary?: string
|
|
82
84
|
description?: string
|
|
83
85
|
technology?: string
|
|
84
86
|
tags?: IfNever<Tag, never, [Tag, ...Tag[]]>
|
|
@@ -211,15 +213,17 @@ export type AnyTypesNested = TypesNested<
|
|
|
211
213
|
any
|
|
212
214
|
>
|
|
213
215
|
|
|
216
|
+
type KeysOrTuple<T> = T extends readonly string[] ? TupleToUnion<T> : KeysOf<T>
|
|
217
|
+
|
|
214
218
|
export namespace Types {
|
|
215
219
|
export type FromSpecification<Spec> = Spec extends BuilderSpecification ? Types<
|
|
216
|
-
|
|
220
|
+
KeysOrTuple<Spec['elements']>,
|
|
217
221
|
never,
|
|
218
222
|
never,
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
KeysOrTuple<Spec['relationships']>,
|
|
224
|
+
KeysOrTuple<Spec['tags']>,
|
|
221
225
|
TupleToUnion<Spec['metadataKeys']>,
|
|
222
|
-
|
|
226
|
+
KeysOrTuple<Spec['deployments']>,
|
|
223
227
|
never
|
|
224
228
|
>
|
|
225
229
|
: never
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasAtLeast,
|
|
1
|
+
import { hasAtLeast, unique } from 'remeda'
|
|
2
2
|
import type {
|
|
3
3
|
DeployedInstanceModel,
|
|
4
4
|
DeploymentConnectionModel,
|
|
@@ -17,8 +17,12 @@ import {
|
|
|
17
17
|
type scalar,
|
|
18
18
|
type Unknown,
|
|
19
19
|
DefaultArrowType,
|
|
20
|
+
DefaultElementShape,
|
|
21
|
+
DefaultThemeColor,
|
|
20
22
|
FqnExpr,
|
|
21
23
|
isViewRuleStyle,
|
|
24
|
+
omitUndefined,
|
|
25
|
+
preferSummary,
|
|
22
26
|
} from '../../types'
|
|
23
27
|
import { invariant, nameFromFqn, nonexhaustive, parentFqn } from '../../utils'
|
|
24
28
|
import { applyViewRuleStyle } from '../utils/applyViewRuleStyles'
|
|
@@ -135,44 +139,56 @@ export function deploymentExpressionToPredicate<
|
|
|
135
139
|
nonexhaustive(target)
|
|
136
140
|
}
|
|
137
141
|
|
|
142
|
+
function instanceSummary(model: DeployedInstanceModel<any>) {
|
|
143
|
+
return preferSummary(model.$instance) ?? preferSummary(model.element.$element)
|
|
144
|
+
}
|
|
145
|
+
|
|
138
146
|
export function toNodeSource<A extends AnyAux>(
|
|
139
147
|
el: DeploymentNodeModel<A> | DeployedInstanceModel<A>,
|
|
140
148
|
): ComputedNodeSource<A> {
|
|
141
149
|
if (el.isDeploymentNode()) {
|
|
142
150
|
const onlyOneInstance = el.onlyOneInstance()
|
|
143
|
-
let {
|
|
144
|
-
|
|
151
|
+
let {
|
|
152
|
+
title,
|
|
153
|
+
id,
|
|
154
|
+
description,
|
|
155
|
+
summary,
|
|
156
|
+
metadata,
|
|
157
|
+
style,
|
|
158
|
+
element,
|
|
159
|
+
tags: _tags, // omit
|
|
160
|
+
...$node
|
|
161
|
+
} = el.$node
|
|
162
|
+
let icon = style.icon
|
|
145
163
|
let tags = [...el.tags]
|
|
146
164
|
// let description
|
|
147
165
|
// If there is only one instance
|
|
166
|
+
summary ??= description
|
|
148
167
|
if (onlyOneInstance) {
|
|
149
168
|
tags = unique([...tags, ...onlyOneInstance.tags])
|
|
150
169
|
// If title was not overriden, take title from the instance
|
|
151
170
|
if (title === nameFromFqn(el.id)) {
|
|
152
171
|
title = onlyOneInstance.title
|
|
153
172
|
}
|
|
154
|
-
|
|
173
|
+
icon ??= onlyOneInstance.style.icon
|
|
174
|
+
summary ??= instanceSummary(onlyOneInstance)
|
|
155
175
|
}
|
|
156
176
|
|
|
157
|
-
return {
|
|
177
|
+
return omitUndefined({
|
|
178
|
+
id: id as scalar.NodeId,
|
|
179
|
+
deploymentRef: id,
|
|
180
|
+
title,
|
|
181
|
+
...$node,
|
|
182
|
+
color: style.color ?? onlyOneInstance?.color ?? DefaultThemeColor,
|
|
183
|
+
shape: style.shape ?? DefaultElementShape,
|
|
158
184
|
...(onlyOneInstance && {
|
|
159
|
-
...toNodeSource(onlyOneInstance),
|
|
160
185
|
modelRef: onlyOneInstance.element.id,
|
|
161
186
|
}),
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
...(icon && { icon }),
|
|
165
|
-
...(color && { color }),
|
|
166
|
-
...(shape && { shape }),
|
|
167
|
-
...(description && { description }),
|
|
187
|
+
icon,
|
|
188
|
+
description: summary,
|
|
168
189
|
tags,
|
|
169
|
-
style
|
|
170
|
-
|
|
171
|
-
},
|
|
172
|
-
deploymentRef: id,
|
|
173
|
-
kind,
|
|
174
|
-
id: id as scalar.NodeId,
|
|
175
|
-
}
|
|
190
|
+
style,
|
|
191
|
+
})
|
|
176
192
|
}
|
|
177
193
|
invariant(el.isInstance(), 'Expected Instance')
|
|
178
194
|
const instance = el.$instance
|
|
@@ -184,33 +200,28 @@ export function toNodeSource<A extends AnyAux>(
|
|
|
184
200
|
...(instance.links ?? []),
|
|
185
201
|
]
|
|
186
202
|
|
|
187
|
-
const metadata = {
|
|
188
|
-
...element.getMetadata(),
|
|
189
|
-
...instance.metadata,
|
|
190
|
-
}
|
|
191
|
-
|
|
192
203
|
const notation = instance.notation ?? element.$element.notation
|
|
193
204
|
|
|
194
|
-
|
|
195
|
-
let technology = instance.technology ?? element.technology
|
|
205
|
+
const description = instanceSummary(el)
|
|
196
206
|
|
|
197
|
-
|
|
207
|
+
const technology = el.technology ?? undefined
|
|
208
|
+
|
|
209
|
+
return omitUndefined({
|
|
198
210
|
id: el.id as scalar.NodeId,
|
|
199
211
|
kind: 'instance' as unknown as aux.DeploymentKind<A>,
|
|
200
212
|
title: el.title,
|
|
201
|
-
|
|
202
|
-
|
|
213
|
+
description,
|
|
214
|
+
technology,
|
|
203
215
|
tags: [...el.tags],
|
|
204
|
-
links: hasAtLeast(links, 1) ? links :
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
216
|
+
links: hasAtLeast(links, 1) ? links : undefined,
|
|
217
|
+
icon,
|
|
218
|
+
color,
|
|
219
|
+
shape,
|
|
208
220
|
style,
|
|
209
221
|
deploymentRef: instance.id,
|
|
210
222
|
modelRef: element.id,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
223
|
+
notation,
|
|
224
|
+
})
|
|
214
225
|
}
|
|
215
226
|
|
|
216
227
|
export function toComputedEdges<A extends AnyAux>(
|
|
@@ -2,7 +2,7 @@ import dagre, { type EdgeConfig, type GraphLabel } from '@dagrejs/dagre'
|
|
|
2
2
|
import { concat, filter, forEachObj, groupBy, map, mapToObj, pipe, prop, reduce, tap } from 'remeda'
|
|
3
3
|
import type { ElementModel } from '../../model/ElementModel'
|
|
4
4
|
import type { RelationshipModel } from '../../model/RelationModel'
|
|
5
|
-
import type
|
|
5
|
+
import { type DiagramNode, type DiagramView, type Fqn, type NodeId, omitUndefined } from '../../types'
|
|
6
6
|
import { invariant, sortParentsFirst } from '../../utils'
|
|
7
7
|
import { toArray } from '../../utils/iterable'
|
|
8
8
|
import { DefaultMap } from '../../utils/mnemonist'
|
|
@@ -311,13 +311,14 @@ export function layoutRelationshipsView(data: RelationshipsViewData): Pick<Diagr
|
|
|
311
311
|
// }
|
|
312
312
|
const children = (g.children(id) as NodeId[] | undefined ?? []).filter(c => !c.endsWith(PortSuffix))
|
|
313
313
|
|
|
314
|
-
return {
|
|
314
|
+
return omitUndefined({
|
|
315
315
|
id: id as NodeId,
|
|
316
316
|
parent: parentId as NodeId ?? null,
|
|
317
317
|
title: element.title,
|
|
318
|
-
|
|
318
|
+
x: position.x,
|
|
319
|
+
y: position.y,
|
|
319
320
|
position: [position.x, position.y],
|
|
320
|
-
description: element.description
|
|
321
|
+
description: element.$element.summary ?? element.$element.description ?? null,
|
|
321
322
|
technology: element.technology,
|
|
322
323
|
tags: [],
|
|
323
324
|
links: null,
|
|
@@ -337,11 +338,11 @@ export function layoutRelationshipsView(data: RelationshipsViewData): Pick<Diagr
|
|
|
337
338
|
},
|
|
338
339
|
inEdges: [],
|
|
339
340
|
outEdges: [],
|
|
340
|
-
|
|
341
|
+
depth: children.length > 0 ? nodeDepth(id) : 0,
|
|
341
342
|
children,
|
|
342
343
|
width,
|
|
343
344
|
height,
|
|
344
|
-
}
|
|
345
|
+
})
|
|
345
346
|
})
|
|
346
347
|
|
|
347
348
|
return {
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Except } from 'type-fest'
|
|
2
2
|
import type { ElementModel } from '../../model'
|
|
3
3
|
import {
|
|
4
4
|
type AnyAux,
|
|
5
5
|
type aux,
|
|
6
6
|
type ComputedNode,
|
|
7
|
-
type Element,
|
|
8
7
|
type scalar,
|
|
9
8
|
type Unknown,
|
|
10
9
|
DefaultElementShape,
|
|
11
10
|
DefaultThemeColor,
|
|
12
11
|
GroupElementKind,
|
|
12
|
+
omitUndefined,
|
|
13
|
+
preferSummary,
|
|
13
14
|
} from '../../types'
|
|
14
15
|
import { nonNullable } from '../../utils'
|
|
15
16
|
import { compareByFqnHierarchically, parentFqn } from '../../utils/fqn'
|
|
@@ -28,18 +29,30 @@ function updateDepthOfAncestors(node: ComputedNode, nodes: ReadonlyMap<string, C
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
export type ComputedNodeSource<A extends AnyAux = Unknown> =
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
export type ComputedNodeSource<A extends AnyAux = Unknown> = Except<
|
|
33
|
+
ComputedNode<A>,
|
|
34
|
+
'parent' | 'children' | 'inEdges' | 'outEdges' | 'level' | 'depth',
|
|
35
|
+
{ requireExactProps: true }
|
|
34
36
|
>
|
|
35
37
|
|
|
36
38
|
export function elementModelToNodeSource<A extends AnyAux>(el: ElementModel<A>): ComputedNodeSource<A> {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
const {
|
|
40
|
+
id,
|
|
41
|
+
summary, // omit
|
|
42
|
+
style,
|
|
43
|
+
metadata, // omit
|
|
44
|
+
...rest
|
|
45
|
+
} = el.$element
|
|
46
|
+
return omitUndefined({
|
|
47
|
+
id: id as scalar.NodeId,
|
|
48
|
+
modelRef: id,
|
|
49
|
+
...rest,
|
|
50
|
+
style: { ...style },
|
|
51
|
+
color: el.color,
|
|
52
|
+
shape: el.shape,
|
|
53
|
+
description: preferSummary(el.$element),
|
|
39
54
|
tags: [...el.tags],
|
|
40
|
-
|
|
41
|
-
modelRef: el.id,
|
|
42
|
-
}
|
|
55
|
+
})
|
|
43
56
|
}
|
|
44
57
|
|
|
45
58
|
export function buildComputedNodes<A extends AnyAux>(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { entries, flatMap, groupBy, map, mapValues, pipe, piped, prop, sortBy, unique } from 'remeda'
|
|
1
|
+
import { entries, filter, flatMap, groupBy, map, mapValues, pipe, piped, prop, sortBy, unique } from 'remeda'
|
|
2
2
|
import type { ComputedNode, NodeNotation } from '../../types'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -12,6 +12,7 @@ import type { ComputedNode, NodeNotation } from '../../types'
|
|
|
12
12
|
export function buildElementNotations(nodes: ComputedNode<any>[]): NodeNotation[] {
|
|
13
13
|
return pipe(
|
|
14
14
|
nodes,
|
|
15
|
+
filter((n): n is ComputedNode<any> & { notation: string } => !!n.notation),
|
|
15
16
|
groupBy(prop('notation')),
|
|
16
17
|
mapValues(
|
|
17
18
|
piped(
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
DefaultLineStyle,
|
|
19
19
|
DefaultShapeSize,
|
|
20
20
|
DefaultThemeColor,
|
|
21
|
+
preferDescription,
|
|
22
|
+
preferSummary,
|
|
21
23
|
RichText,
|
|
22
24
|
} from '../types'
|
|
23
25
|
import * as aux from '../types/_aux'
|
|
@@ -67,8 +69,20 @@ abstract class AbstractDeploymentElementModel<A extends Any> implements WithTags
|
|
|
67
69
|
return this.$node.style?.color as Color ?? DefaultThemeColor
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Short description of the element.
|
|
74
|
+
* Falls back to description if summary is not provided.
|
|
75
|
+
*/
|
|
76
|
+
get summary(): RichTextOrEmpty {
|
|
77
|
+
return RichText.memoize(this, 'summary', preferSummary(this.$node))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Long description of the element.
|
|
82
|
+
* Falls back to summary if description is not provided.
|
|
83
|
+
*/
|
|
70
84
|
get description(): RichTextOrEmpty {
|
|
71
|
-
return RichText.memoize(this, this.$node
|
|
85
|
+
return RichText.memoize(this, 'description', preferDescription(this.$node))
|
|
72
86
|
}
|
|
73
87
|
|
|
74
88
|
get technology(): string | null {
|
|
@@ -412,8 +426,20 @@ export class DeployedInstanceModel<A extends Any = Any> extends AbstractDeployme
|
|
|
412
426
|
return this.element.kind
|
|
413
427
|
}
|
|
414
428
|
|
|
429
|
+
override get summary(): RichTextOrEmpty {
|
|
430
|
+
return RichText.memoize(
|
|
431
|
+
this,
|
|
432
|
+
'summary',
|
|
433
|
+
preferSummary(this.$instance) ?? preferSummary(this.element.$element),
|
|
434
|
+
)
|
|
435
|
+
}
|
|
436
|
+
|
|
415
437
|
override get description(): RichTextOrEmpty {
|
|
416
|
-
return RichText.memoize(
|
|
438
|
+
return RichText.memoize(
|
|
439
|
+
this,
|
|
440
|
+
'description',
|
|
441
|
+
preferDescription(this.$instance) ?? preferDescription(this.element.$element),
|
|
442
|
+
)
|
|
417
443
|
}
|
|
418
444
|
|
|
419
445
|
override get technology(): string | null {
|
|
@@ -496,6 +522,10 @@ export class NestedElementOfDeployedInstanceModel<A extends Any = Any> {
|
|
|
496
522
|
return this.element.title
|
|
497
523
|
}
|
|
498
524
|
|
|
525
|
+
get summary(): RichTextOrEmpty {
|
|
526
|
+
return this.element.summary
|
|
527
|
+
}
|
|
528
|
+
|
|
499
529
|
get description(): RichTextOrEmpty {
|
|
500
530
|
return this.element.description
|
|
501
531
|
}
|
|
@@ -550,7 +580,7 @@ export class DeploymentRelationModel<A extends Any = Any> implements AnyRelation
|
|
|
550
580
|
}
|
|
551
581
|
|
|
552
582
|
get description(): RichTextOrEmpty {
|
|
553
|
-
return RichText.memoize(this, this.$relationship.description)
|
|
583
|
+
return RichText.memoize(this, 'description', this.$relationship.description)
|
|
554
584
|
}
|
|
555
585
|
|
|
556
586
|
get tags(): aux.Tags<A> {
|