@likec4/core 1.12.2 → 1.13.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/index.cjs +777 -251
- package/dist/index.d.cts +301 -32
- package/dist/index.d.mts +301 -32
- package/dist/index.d.ts +301 -32
- package/dist/index.mjs +774 -252
- package/dist/shared/{core.BvFch1jG.mjs → core.BJDxVHFh.mjs} +19 -19
- package/dist/shared/{core.dY8efyk0.d.ts → core.C4jDMQ1i.d.cts} +242 -238
- package/dist/shared/{core.dY8efyk0.d.cts → core.C4jDMQ1i.d.mts} +242 -238
- package/dist/shared/{core.dY8efyk0.d.mts → core.C4jDMQ1i.d.ts} +242 -238
- package/dist/shared/{core.CTiE4teS.cjs → core.IR7XOEkQ.cjs} +19 -18
- package/dist/types/index.cjs +1 -1
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.mts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.mjs +1 -1
- package/package.json +4 -3
- package/src/builder/Builder.element.ts +221 -0
- package/src/builder/Builder.model.ts +346 -0
- package/src/builder/Builder.test-d.ts +69 -0
- package/src/builder/Builder.ts +574 -0
- package/src/builder/Builder.view.ts +358 -0
- package/src/builder/Builder.views.ts +318 -0
- package/src/builder/_types.ts +195 -0
- package/src/builder/index.ts +5 -0
- package/src/index.ts +4 -0
- package/src/model/LikeC4DiagramModel.ts +5 -5
- package/src/model/LikeC4Model.ts +63 -46
- package/src/model/LikeC4ViewModel.ts +7 -7
- package/src/model/__test__/fixture.ts +5 -0
- package/src/types/element.ts +19 -11
- package/src/types/model.ts +13 -8
- package/src/types/relation.ts +1 -1
- package/src/types/view-changes.ts +6 -2
- package/src/types/view.ts +53 -22
- package/src/utils/fqn.ts +12 -11
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { IfNever, IsLiteral, Tagged, TupleToUnion } from 'type-fest'
|
|
2
|
+
import type {
|
|
3
|
+
BorderStyle,
|
|
4
|
+
Color,
|
|
5
|
+
ElementKindSpecification,
|
|
6
|
+
ElementShape,
|
|
7
|
+
ParsedLikeC4Model,
|
|
8
|
+
RelationshipArrowType,
|
|
9
|
+
RelationshipKindSpecification,
|
|
10
|
+
RelationshipLineType
|
|
11
|
+
} from '../types'
|
|
12
|
+
|
|
13
|
+
type KeysOf<T> = keyof T extends infer K extends string ? K : never
|
|
14
|
+
|
|
15
|
+
export type BuilderSpecification = {
|
|
16
|
+
elements: {
|
|
17
|
+
[kind: string]: Partial<ElementKindSpecification>
|
|
18
|
+
}
|
|
19
|
+
relationships?: Record<string, Partial<RelationshipKindSpecification>>
|
|
20
|
+
tags?: [string, ...string[]]
|
|
21
|
+
metadataKeys?: [string, ...string[]]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Metadata<MetadataKey extends string> = IfNever<MetadataKey, never, Record<MetadataKey, string>>
|
|
25
|
+
|
|
26
|
+
export type NewElementProps<Tag, Metadata> = {
|
|
27
|
+
title?: string
|
|
28
|
+
description?: string
|
|
29
|
+
technology?: string
|
|
30
|
+
tags?: IfNever<Tag, never, [Tag, ...Tag[]]>
|
|
31
|
+
metadata?: Metadata
|
|
32
|
+
icon?: string
|
|
33
|
+
shape?: ElementShape
|
|
34
|
+
color?: Color
|
|
35
|
+
links?: Array<string | { title?: string; url: string }>
|
|
36
|
+
style?: {
|
|
37
|
+
border?: BorderStyle
|
|
38
|
+
// 0-100
|
|
39
|
+
opacity?: number
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type NewViewProps<Tag> = {
|
|
44
|
+
title?: string
|
|
45
|
+
description?: string
|
|
46
|
+
tags?: IfNever<Tag, never, [Tag, ...Tag[]]>
|
|
47
|
+
links?: Array<string | { title?: string; url: string }>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type NewRelationProps<Kind, Tag, Metadata> = {
|
|
51
|
+
kind?: Kind
|
|
52
|
+
title?: string
|
|
53
|
+
description?: string
|
|
54
|
+
technology?: string
|
|
55
|
+
tags?: IfNever<Tag, never, [Tag, ...Tag[]]>
|
|
56
|
+
metadata?: Metadata
|
|
57
|
+
head?: RelationshipArrowType
|
|
58
|
+
tail?: RelationshipArrowType
|
|
59
|
+
line?: RelationshipLineType
|
|
60
|
+
color?: Color
|
|
61
|
+
links?: Array<string | { title?: string; url: string }>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type Invalid<Message extends string> = Tagged<Message, 'Error'>
|
|
65
|
+
export type Warn<Id, Existing> = IsLiteral<Existing> extends true ? Id extends Existing ? Invalid<'Already exists'> : Id
|
|
66
|
+
: Id
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Auxilary type to keep track of the types in builder
|
|
70
|
+
*/
|
|
71
|
+
export interface Types<
|
|
72
|
+
ElementKind extends string,
|
|
73
|
+
Fqn extends string,
|
|
74
|
+
ViewId extends string,
|
|
75
|
+
RelationshipKind extends string,
|
|
76
|
+
Tag extends string,
|
|
77
|
+
MetadataKey extends string
|
|
78
|
+
> {
|
|
79
|
+
ElementKind: ElementKind
|
|
80
|
+
Fqn: Fqn
|
|
81
|
+
ViewId: ViewId
|
|
82
|
+
RelationshipKind: RelationshipKind
|
|
83
|
+
Tag: Tag
|
|
84
|
+
MetadataKey: MetadataKey
|
|
85
|
+
|
|
86
|
+
Tags: IfNever<Tag, never, [Tag, ...Tag[]]>
|
|
87
|
+
// Metadata: Metadata<MetadataKey>
|
|
88
|
+
|
|
89
|
+
NewElementProps: NewElementProps<Tag, Metadata<MetadataKey>>
|
|
90
|
+
NewRelationshipProps: NewRelationProps<RelationshipKind, Tag, Metadata<MetadataKey>>
|
|
91
|
+
NewViewProps: NewViewProps<Tag>
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* When building nested objects
|
|
96
|
+
*/
|
|
97
|
+
export interface TypesNested<
|
|
98
|
+
Parent extends string,
|
|
99
|
+
ElementKind extends string,
|
|
100
|
+
Fqn extends string,
|
|
101
|
+
ViewId extends string,
|
|
102
|
+
RelationshipKind extends string,
|
|
103
|
+
Tag extends string,
|
|
104
|
+
MetadataKey extends string
|
|
105
|
+
> extends
|
|
106
|
+
Types<
|
|
107
|
+
ElementKind,
|
|
108
|
+
Fqn,
|
|
109
|
+
ViewId,
|
|
110
|
+
RelationshipKind,
|
|
111
|
+
Tag,
|
|
112
|
+
MetadataKey
|
|
113
|
+
>
|
|
114
|
+
{
|
|
115
|
+
Parent: Parent
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export type AnyTypes = Types<
|
|
119
|
+
any,
|
|
120
|
+
any,
|
|
121
|
+
any,
|
|
122
|
+
any,
|
|
123
|
+
any,
|
|
124
|
+
any
|
|
125
|
+
>
|
|
126
|
+
|
|
127
|
+
export type AnyTypesNested = TypesNested<
|
|
128
|
+
any,
|
|
129
|
+
any,
|
|
130
|
+
any,
|
|
131
|
+
any,
|
|
132
|
+
any,
|
|
133
|
+
any,
|
|
134
|
+
any
|
|
135
|
+
>
|
|
136
|
+
|
|
137
|
+
export namespace Types {
|
|
138
|
+
export type FromSpecification<Spec> = Spec extends BuilderSpecification ? Types<
|
|
139
|
+
KeysOf<Spec['elements']>,
|
|
140
|
+
never,
|
|
141
|
+
never,
|
|
142
|
+
KeysOf<Spec['relationships']>,
|
|
143
|
+
TupleToUnion<Spec['tags']>,
|
|
144
|
+
TupleToUnion<Spec['metadataKeys']>
|
|
145
|
+
>
|
|
146
|
+
: never
|
|
147
|
+
|
|
148
|
+
export type AddFqn<T, Id extends string> = T extends TypesNested<infer P, any, any, any, any, any, any> ? TypesNested<
|
|
149
|
+
P,
|
|
150
|
+
T['ElementKind'],
|
|
151
|
+
`${P}.${Id}` | T['Fqn'],
|
|
152
|
+
T['ViewId'],
|
|
153
|
+
T['RelationshipKind'],
|
|
154
|
+
T['Tag'],
|
|
155
|
+
T['MetadataKey']
|
|
156
|
+
>
|
|
157
|
+
: T extends AnyTypes ? Types<
|
|
158
|
+
T['ElementKind'],
|
|
159
|
+
Id | T['Fqn'],
|
|
160
|
+
T['ViewId'],
|
|
161
|
+
T['RelationshipKind'],
|
|
162
|
+
T['Tag'],
|
|
163
|
+
T['MetadataKey']
|
|
164
|
+
>
|
|
165
|
+
: never
|
|
166
|
+
|
|
167
|
+
export type AddView<T, Id extends string> = T extends TypesNested<infer P, any, any, any, any, any, any>
|
|
168
|
+
? TypesNested<
|
|
169
|
+
P,
|
|
170
|
+
T['ElementKind'],
|
|
171
|
+
T['Fqn'],
|
|
172
|
+
Id | T['ViewId'],
|
|
173
|
+
T['RelationshipKind'],
|
|
174
|
+
T['Tag'],
|
|
175
|
+
T['MetadataKey']
|
|
176
|
+
>
|
|
177
|
+
: T extends AnyTypes ? Types<
|
|
178
|
+
T['ElementKind'],
|
|
179
|
+
T['Fqn'],
|
|
180
|
+
Id | T['ViewId'],
|
|
181
|
+
T['RelationshipKind'],
|
|
182
|
+
T['Tag'],
|
|
183
|
+
T['MetadataKey']
|
|
184
|
+
>
|
|
185
|
+
: never
|
|
186
|
+
|
|
187
|
+
export type ToParsedLikeC4Model<T> = T extends AnyTypes ? ParsedLikeC4Model<
|
|
188
|
+
T['ElementKind'],
|
|
189
|
+
T['RelationshipKind'],
|
|
190
|
+
T['Tag'],
|
|
191
|
+
T['Fqn'],
|
|
192
|
+
T['ViewId']
|
|
193
|
+
>
|
|
194
|
+
: never
|
|
195
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { AnyTypes, AnyTypesNested, BuilderSpecification, Types, TypesNested } from './_types'
|
|
2
|
+
export { Builder } from './Builder'
|
|
3
|
+
export type { ModelBuilder, ModelHelpers } from './Builder.model'
|
|
4
|
+
export type { ViewBuilder, ViewHelpers } from './Builder.view'
|
|
5
|
+
export type { ViewsBuilder } from './Builder.views'
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
export * from './builder'
|
|
2
|
+
export { Builder } from './builder'
|
|
3
|
+
export type * from './builder'
|
|
1
4
|
export * from './colors'
|
|
2
5
|
export * from './errors'
|
|
3
6
|
export * from './model'
|
|
7
|
+
|
|
4
8
|
export * from './types'
|
|
5
9
|
export * from './utils/compare-natural'
|
|
6
10
|
export * from './utils/fqn'
|
|
@@ -15,11 +15,11 @@ import { type EdgeId, type ElementOrFqn, type Fqn, getId, type IncomingFilter, t
|
|
|
15
15
|
* All methods are view-scoped, i.e. only return elements and connections in the view.
|
|
16
16
|
*/
|
|
17
17
|
export class LikeC4DiagramModel {
|
|
18
|
-
private _rootElements = new Set<LikeC4DiagramModel.Element>()
|
|
18
|
+
private readonly _rootElements = new Set<LikeC4DiagramModel.Element>()
|
|
19
19
|
|
|
20
|
-
private _elements = new Map<Fqn, LikeC4DiagramModel.Element>()
|
|
20
|
+
private readonly _elements = new Map<Fqn, LikeC4DiagramModel.Element>()
|
|
21
21
|
|
|
22
|
-
private _connections: Map<EdgeId, LikeC4DiagramModel.Connection>
|
|
22
|
+
private readonly _connections: Map<EdgeId, LikeC4DiagramModel.Connection>
|
|
23
23
|
|
|
24
24
|
constructor(
|
|
25
25
|
public readonly view: DiagramView,
|
|
@@ -198,7 +198,7 @@ export namespace LikeC4DiagramModel {
|
|
|
198
198
|
export class Element {
|
|
199
199
|
constructor(
|
|
200
200
|
public readonly node: DiagramNode,
|
|
201
|
-
private view: LikeC4DiagramModel
|
|
201
|
+
private readonly view: LikeC4DiagramModel
|
|
202
202
|
) {
|
|
203
203
|
}
|
|
204
204
|
|
|
@@ -304,7 +304,7 @@ export namespace LikeC4DiagramModel {
|
|
|
304
304
|
export class Connection {
|
|
305
305
|
constructor(
|
|
306
306
|
public readonly edge: DiagramEdge,
|
|
307
|
-
private view: LikeC4DiagramModel
|
|
307
|
+
private readonly view: LikeC4DiagramModel
|
|
308
308
|
) {
|
|
309
309
|
}
|
|
310
310
|
|
package/src/model/LikeC4Model.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { isString, isTruthy, values } from 'remeda'
|
|
2
|
+
import type { Class } from 'type-fest'
|
|
2
3
|
import { nonNullable } from '../errors'
|
|
3
|
-
import { DefaultElementShape, DefaultThemeColor } from '../types/element'
|
|
4
4
|
import type {
|
|
5
5
|
Element as C4Element,
|
|
6
6
|
ElementKind as C4ElementKind,
|
|
7
7
|
ElementShape as C4ElementShape,
|
|
8
|
-
Fqn as C4Fqn,
|
|
9
8
|
Tag as C4Tag
|
|
10
9
|
} from '../types/element'
|
|
10
|
+
import { DefaultElementShape, DefaultThemeColor } from '../types/element'
|
|
11
11
|
import type {
|
|
12
12
|
ComputedLikeC4Model as C4ComputedLikeC4Model,
|
|
13
13
|
LayoutedLikeC4Model as C4LayoutedLikeC4Model
|
|
@@ -17,8 +17,8 @@ import type { Color as C4Color } from '../types/theme'
|
|
|
17
17
|
import { ancestorsFqn, commonAncestor, parentFqn } from '../utils/fqn'
|
|
18
18
|
import { LikeC4DiagramModel } from './LikeC4DiagramModel'
|
|
19
19
|
import { LikeC4ViewModel } from './LikeC4ViewModel'
|
|
20
|
-
import { getId } from './types'
|
|
21
20
|
import type { ElementOrFqn, Fqn, IncomingFilter, OutgoingFilter, RelationID, ViewID } from './types'
|
|
21
|
+
import { getId } from './types'
|
|
22
22
|
|
|
23
23
|
type PickBySource<Source> = Source extends LikeC4Model.Layouted.SourceModel ? LikeC4Model.Layouted
|
|
24
24
|
: Source extends LikeC4Model.Computed.SourceModel ? LikeC4Model.Computed
|
|
@@ -48,47 +48,15 @@ export class LikeC4Model<M extends LikeC4Model.ViewModel = LikeC4Model.ViewModel
|
|
|
48
48
|
|
|
49
49
|
private _cacheAscendingSiblings = new Map<Fqn, LikeC4Model.ElementModel<M>[]>()
|
|
50
50
|
|
|
51
|
-
private _views
|
|
52
|
-
|
|
53
|
-
static create(source: LikeC4Model.SourceModel): PickBySource<typeof source> {
|
|
54
|
-
// static create<MM extends LikeC4Model>(source: MM['s']): PickBySource<typeof source> {
|
|
55
|
-
if (source.__ === 'layouted') {
|
|
56
|
-
return LikeC4Model.layouted(source)
|
|
57
|
-
}
|
|
58
|
-
return LikeC4Model.computed(source)
|
|
59
|
-
}
|
|
51
|
+
private _views: Map<Fqn, M>
|
|
60
52
|
|
|
61
|
-
|
|
62
|
-
const instance = new LikeC4Model<LikeC4ViewModel>(
|
|
63
|
-
'computed',
|
|
64
|
-
source,
|
|
65
|
-
values(source.elements),
|
|
66
|
-
values(source.relations)
|
|
67
|
-
)
|
|
68
|
-
for (const view of values(source.views)) {
|
|
69
|
-
instance._views.set(view.id, new LikeC4ViewModel(view, instance))
|
|
70
|
-
}
|
|
71
|
-
return instance
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
static layouted(source: C4LayoutedLikeC4Model): LikeC4Model<LikeC4DiagramModel> {
|
|
75
|
-
const instance = new LikeC4Model<LikeC4DiagramModel>(
|
|
76
|
-
'layouted',
|
|
77
|
-
source,
|
|
78
|
-
values(source.elements),
|
|
79
|
-
values(source.relations)
|
|
80
|
-
)
|
|
81
|
-
for (const view of values(source.views)) {
|
|
82
|
-
instance._views.set(view.id, new LikeC4DiagramModel(view, instance))
|
|
83
|
-
}
|
|
84
|
-
return instance
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
protected constructor(
|
|
53
|
+
constructor(
|
|
88
54
|
public readonly type: LikeC4Model.ModelType<M>,
|
|
89
55
|
public readonly sourcemodel: LikeC4Model.SourceModel<M>,
|
|
90
56
|
elements: C4Element[],
|
|
91
|
-
relations: C4Relation[]
|
|
57
|
+
relations: C4Relation[],
|
|
58
|
+
views: LikeC4Model.SourceModel<M>['views'],
|
|
59
|
+
ViewModelClass: Class<M>
|
|
92
60
|
) {
|
|
93
61
|
for (const el of elements) {
|
|
94
62
|
this.addElement(el)
|
|
@@ -96,6 +64,9 @@ export class LikeC4Model<M extends LikeC4Model.ViewModel = LikeC4Model.ViewModel
|
|
|
96
64
|
for (const rel of relations) {
|
|
97
65
|
this.addRelation(rel)
|
|
98
66
|
}
|
|
67
|
+
this._views = new Map(
|
|
68
|
+
values(views).map(view => [view.id, new ViewModelClass(view, this)] as [Fqn, M])
|
|
69
|
+
)
|
|
99
70
|
}
|
|
100
71
|
|
|
101
72
|
/**
|
|
@@ -228,7 +199,7 @@ export class LikeC4Model<M extends LikeC4Model.ViewModel = LikeC4Model.ViewModel
|
|
|
228
199
|
element: ElementOrFqn,
|
|
229
200
|
filter: IncomingFilter = 'all'
|
|
230
201
|
): ReadonlyArray<LikeC4Model.ElementModel<M>> {
|
|
231
|
-
return this.incoming(element, filter).map(r => r.source)
|
|
202
|
+
return [...new Set(this.incoming(element, filter).map(r => r.source))]
|
|
232
203
|
}
|
|
233
204
|
|
|
234
205
|
/**
|
|
@@ -259,7 +230,7 @@ export class LikeC4Model<M extends LikeC4Model.ViewModel = LikeC4Model.ViewModel
|
|
|
259
230
|
element: ElementOrFqn,
|
|
260
231
|
filter: OutgoingFilter = 'all'
|
|
261
232
|
): ReadonlyArray<LikeC4Model.ElementModel<M>> {
|
|
262
|
-
return this.outgoing(element, filter).map(r => r.target)
|
|
233
|
+
return [...new Set(this.outgoing(element, filter).map(r => r.target))]
|
|
263
234
|
}
|
|
264
235
|
|
|
265
236
|
/**
|
|
@@ -380,6 +351,36 @@ export class LikeC4Model<M extends LikeC4Model.ViewModel = LikeC4Model.ViewModel
|
|
|
380
351
|
}
|
|
381
352
|
|
|
382
353
|
export namespace LikeC4Model {
|
|
354
|
+
export function create(source: LikeC4Model.SourceModel): PickBySource<typeof source> {
|
|
355
|
+
// static create<MM extends LikeC4Model>(source: MM['s']): PickBySource<typeof source> {
|
|
356
|
+
if (source.__ === 'layouted') {
|
|
357
|
+
return LikeC4Model.layouted(source)
|
|
358
|
+
}
|
|
359
|
+
return LikeC4Model.computed(source)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function computed(source: C4ComputedLikeC4Model): LikeC4Model<LikeC4ViewModel> {
|
|
363
|
+
return new LikeC4Model(
|
|
364
|
+
'computed',
|
|
365
|
+
source,
|
|
366
|
+
values(source.elements),
|
|
367
|
+
values(source.relations),
|
|
368
|
+
source.views,
|
|
369
|
+
LikeC4ViewModel
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function layouted(source: C4LayoutedLikeC4Model): LikeC4Model<LikeC4DiagramModel> {
|
|
374
|
+
return new LikeC4Model(
|
|
375
|
+
'layouted',
|
|
376
|
+
source,
|
|
377
|
+
values(source.elements),
|
|
378
|
+
values(source.relations),
|
|
379
|
+
source.views,
|
|
380
|
+
LikeC4DiagramModel
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
383
384
|
export function isModel(model: any): model is LikeC4Model {
|
|
384
385
|
return model instanceof LikeC4Model
|
|
385
386
|
}
|
|
@@ -389,6 +390,7 @@ export namespace LikeC4Model {
|
|
|
389
390
|
|
|
390
391
|
export type ViewModel = LikeC4ViewModel | LikeC4DiagramModel
|
|
391
392
|
export namespace ViewModel {
|
|
393
|
+
export type ElementModel = LikeC4ViewModel.Element | LikeC4DiagramModel.Element
|
|
392
394
|
export type Relationship = LikeC4Model.Relationship<ViewModel>
|
|
393
395
|
|
|
394
396
|
export function isLayouted(model: ViewModel): model is LikeC4DiagramModel {
|
|
@@ -416,10 +418,10 @@ export namespace LikeC4Model {
|
|
|
416
418
|
return model.type === 'layouted'
|
|
417
419
|
}
|
|
418
420
|
|
|
419
|
-
export class Relationship<M extends ViewModel> {
|
|
421
|
+
export class Relationship<M extends ViewModel = ViewModel> {
|
|
420
422
|
constructor(
|
|
421
423
|
public readonly relationship: C4Relation,
|
|
422
|
-
private model: LikeC4Model<M>
|
|
424
|
+
private readonly model: LikeC4Model<M>
|
|
423
425
|
) {
|
|
424
426
|
}
|
|
425
427
|
|
|
@@ -459,10 +461,10 @@ export namespace LikeC4Model {
|
|
|
459
461
|
}
|
|
460
462
|
|
|
461
463
|
// Class renamed to ElementModel, otherwise generated DTS will be incorrect
|
|
462
|
-
export class ElementModel<M extends ViewModel> {
|
|
464
|
+
export class ElementModel<M extends ViewModel = ViewModel> {
|
|
463
465
|
constructor(
|
|
464
466
|
public readonly element: C4Element,
|
|
465
|
-
private model: LikeC4Model<M>
|
|
467
|
+
private readonly model: LikeC4Model<M>
|
|
466
468
|
) {
|
|
467
469
|
}
|
|
468
470
|
|
|
@@ -528,6 +530,13 @@ export namespace LikeC4Model {
|
|
|
528
530
|
return this.model.children(this)
|
|
529
531
|
}
|
|
530
532
|
|
|
533
|
+
/**
|
|
534
|
+
* All views 'view of' current element
|
|
535
|
+
*/
|
|
536
|
+
public viewsOf() {
|
|
537
|
+
return this.model.views().filter(v => v.viewOf?.id === this.id)
|
|
538
|
+
}
|
|
539
|
+
|
|
531
540
|
/**
|
|
532
541
|
* Views that contain this element
|
|
533
542
|
*/
|
|
@@ -555,6 +564,14 @@ export namespace LikeC4Model {
|
|
|
555
564
|
return this.model.internal(this)
|
|
556
565
|
}
|
|
557
566
|
|
|
567
|
+
/**
|
|
568
|
+
* Resolve siblings of the element and siblings of ancestors
|
|
569
|
+
* (from closest to root)
|
|
570
|
+
*/
|
|
571
|
+
public ascendingSiblings() {
|
|
572
|
+
return this.model.ascendingSiblings(this)
|
|
573
|
+
}
|
|
574
|
+
|
|
558
575
|
// public *descendants(): IterableIterator<LikeC4Element> {
|
|
559
576
|
// return
|
|
560
577
|
// }
|
|
@@ -14,11 +14,11 @@ import { type EdgeId, type ElementOrFqn, type Fqn, getId, type IncomingFilter, t
|
|
|
14
14
|
* All methods are view-scoped, i.e. only return elements and connections in the view.
|
|
15
15
|
*/
|
|
16
16
|
export class LikeC4ViewModel {
|
|
17
|
-
private _rootElements = new Set<LikeC4ViewModel.Element>()
|
|
17
|
+
private readonly _rootElements = new Set<LikeC4ViewModel.Element>()
|
|
18
18
|
|
|
19
|
-
private _elements = new Map<Fqn, LikeC4ViewModel.Element>()
|
|
19
|
+
private readonly _elements = new Map<Fqn, LikeC4ViewModel.Element>()
|
|
20
20
|
|
|
21
|
-
private _connections: Map<EdgeId, LikeC4ViewModel.Connection>
|
|
21
|
+
private readonly _connections: Map<EdgeId, LikeC4ViewModel.Connection>
|
|
22
22
|
|
|
23
23
|
constructor(
|
|
24
24
|
public readonly view: ComputedView,
|
|
@@ -151,7 +151,7 @@ export class LikeC4ViewModel {
|
|
|
151
151
|
element: ElementOrFqn,
|
|
152
152
|
filter: IncomingFilter = 'all'
|
|
153
153
|
): ReadonlyArray<LikeC4ViewModel.Element> {
|
|
154
|
-
return this.incoming(element, filter).map(r => r.source)
|
|
154
|
+
return [...new Set(this.incoming(element, filter).map(r => r.source))]
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/**
|
|
@@ -180,7 +180,7 @@ export class LikeC4ViewModel {
|
|
|
180
180
|
element: ElementOrFqn,
|
|
181
181
|
filter: OutgoingFilter = 'all'
|
|
182
182
|
): ReadonlyArray<LikeC4ViewModel.Element> {
|
|
183
|
-
return this.outgoing(element, filter).map(r => r.target)
|
|
183
|
+
return [...new Set(this.outgoing(element, filter).map(r => r.target))]
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -192,7 +192,7 @@ export namespace LikeC4ViewModel {
|
|
|
192
192
|
export class Element {
|
|
193
193
|
constructor(
|
|
194
194
|
public readonly node: ComputedNode,
|
|
195
|
-
private viewmodel: LikeC4ViewModel
|
|
195
|
+
private readonly viewmodel: LikeC4ViewModel
|
|
196
196
|
) {
|
|
197
197
|
}
|
|
198
198
|
|
|
@@ -290,7 +290,7 @@ export namespace LikeC4ViewModel {
|
|
|
290
290
|
export class Connection {
|
|
291
291
|
constructor(
|
|
292
292
|
public readonly edge: ComputedEdge,
|
|
293
|
-
private viewmodel: LikeC4ViewModel
|
|
293
|
+
private readonly viewmodel: LikeC4ViewModel
|
|
294
294
|
) {
|
|
295
295
|
}
|
|
296
296
|
|
|
@@ -248,6 +248,11 @@ const fakeRelationsArr = [
|
|
|
248
248
|
target: 'cloud',
|
|
249
249
|
title: 'uses'
|
|
250
250
|
}),
|
|
251
|
+
rel({
|
|
252
|
+
source: 'customer',
|
|
253
|
+
target: 'cloud.frontend',
|
|
254
|
+
title: 'uses frontend'
|
|
255
|
+
}),
|
|
251
256
|
rel({
|
|
252
257
|
source: 'cloud.backend.graphql',
|
|
253
258
|
target: 'cloud.backend.storage',
|
package/src/types/element.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { Tagged } from 'type-fest'
|
|
1
|
+
import type { IfNever, Tagged, TupleToUnion } from 'type-fest'
|
|
2
2
|
import type { IconUrl, NonEmptyArray } from './_common'
|
|
3
3
|
import type { Color, ThemeColor } from './theme'
|
|
4
4
|
|
|
5
5
|
// Full-qualified-name
|
|
6
|
-
export type Fqn = Tagged<
|
|
6
|
+
export type Fqn<Id extends string = string> = Tagged<Id, 'Fqn'>
|
|
7
7
|
|
|
8
8
|
export function AsFqn(name: string, parent?: Fqn | null) {
|
|
9
9
|
return (parent ? parent + '.' + name : name) as Fqn
|
|
@@ -11,9 +11,9 @@ export function AsFqn(name: string, parent?: Fqn | null) {
|
|
|
11
11
|
|
|
12
12
|
export const BorderStyles = ['solid', 'dashed', 'dotted', 'none'] as const
|
|
13
13
|
|
|
14
|
-
export type BorderStyle = typeof BorderStyles
|
|
14
|
+
export type BorderStyle = TupleToUnion<typeof BorderStyles>
|
|
15
15
|
|
|
16
|
-
export type ElementKind = Tagged<
|
|
16
|
+
export type ElementKind<Kinds extends string = string> = Tagged<Kinds, 'ElementKind'>
|
|
17
17
|
export const ElementShapes = [
|
|
18
18
|
'rectangle',
|
|
19
19
|
'person',
|
|
@@ -24,7 +24,7 @@ export const ElementShapes = [
|
|
|
24
24
|
'queue'
|
|
25
25
|
] as const
|
|
26
26
|
|
|
27
|
-
export type ElementShape = typeof ElementShapes
|
|
27
|
+
export type ElementShape = TupleToUnion<typeof ElementShapes>
|
|
28
28
|
export const DefaultThemeColor = 'primary' satisfies ThemeColor
|
|
29
29
|
export const DefaultElementShape = 'rectangle' satisfies ElementShape
|
|
30
30
|
|
|
@@ -34,7 +34,7 @@ export interface ElementStyle {
|
|
|
34
34
|
opacity?: number
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export type Tag = Tagged<
|
|
37
|
+
export type Tag<Tags extends string = string> = Tagged<Tags, 'Tag'>
|
|
38
38
|
|
|
39
39
|
export interface TagSpec {
|
|
40
40
|
readonly id: Tag
|
|
@@ -49,20 +49,28 @@ export interface Link {
|
|
|
49
49
|
readonly relative?: string
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export interface
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
export interface TypedElement<
|
|
53
|
+
Ids extends string,
|
|
54
|
+
Kinds extends string,
|
|
55
|
+
Tags extends string,
|
|
56
|
+
MetadataKeys extends string = never
|
|
57
|
+
> {
|
|
58
|
+
readonly id: Fqn<Ids>
|
|
59
|
+
readonly kind: ElementKind<Kinds>
|
|
55
60
|
readonly title: string
|
|
56
61
|
readonly description: string | null
|
|
57
62
|
readonly technology: string | null
|
|
58
|
-
readonly tags: NonEmptyArray<Tag
|
|
63
|
+
readonly tags: NonEmptyArray<Tag<Tags>> | null
|
|
59
64
|
readonly links: NonEmptyArray<Link> | null
|
|
60
65
|
readonly icon?: IconUrl
|
|
61
66
|
readonly shape?: ElementShape
|
|
62
67
|
readonly color?: Color
|
|
63
68
|
readonly style?: ElementStyle
|
|
64
69
|
readonly notation?: string
|
|
65
|
-
readonly metadata?:
|
|
70
|
+
readonly metadata?: Record<MetadataKeys, string>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface Element extends TypedElement<string, string, string, string> {
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
export interface ElementKindSpecificationStyle {
|
package/src/types/model.ts
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Element, ElementKind, ElementKindSpecification, Fqn, Tag } from './element'
|
|
1
|
+
import type { ElementKindSpecification, Tag, TypedElement } from './element'
|
|
3
2
|
import type { Relation, RelationID, RelationshipKindSpecification } from './relation'
|
|
4
3
|
import type { ComputedView, DiagramView, LikeC4View, ViewID } from './view'
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Parsed elements, relations, and views.
|
|
8
7
|
*/
|
|
9
|
-
export interface ParsedLikeC4Model
|
|
8
|
+
export interface ParsedLikeC4Model<
|
|
9
|
+
ElementKinds extends string = string,
|
|
10
|
+
RelationKinds extends string = string,
|
|
11
|
+
Tags extends string = string,
|
|
12
|
+
Fqns extends string = string,
|
|
13
|
+
Views extends string = string
|
|
14
|
+
> {
|
|
10
15
|
specification: {
|
|
11
|
-
tags: Tag[]
|
|
12
|
-
elements: Record<
|
|
13
|
-
relationships: Record<
|
|
16
|
+
tags: Tag<Tags>[]
|
|
17
|
+
elements: Record<ElementKinds, ElementKindSpecification>
|
|
18
|
+
relationships: Record<RelationKinds, RelationshipKindSpecification>
|
|
14
19
|
}
|
|
15
|
-
elements: Record<
|
|
20
|
+
elements: Record<Fqns, TypedElement<Fqns, ElementKinds, Tags>>
|
|
16
21
|
relations: Record<RelationID, Relation>
|
|
17
|
-
views: Record<
|
|
22
|
+
views: Record<Views, LikeC4View<Views, Tags>>
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
/**
|
package/src/types/relation.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { Color, ThemeColor } from './theme'
|
|
|
5
5
|
import type { ViewID } from './view'
|
|
6
6
|
|
|
7
7
|
export type RelationID = Tagged<string, 'RelationID'>
|
|
8
|
-
export type RelationshipKind = Tagged<
|
|
8
|
+
export type RelationshipKind<Kinds extends string = string> = Tagged<Kinds, 'RelationshipKind'>
|
|
9
9
|
|
|
10
10
|
export type RelationshipLineType = 'dashed' | 'solid' | 'dotted'
|
|
11
11
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NonEmptyArray } from './_common'
|
|
2
2
|
import type { BorderStyle, ElementShape, Fqn } from './element'
|
|
3
3
|
import type { ThemeColor } from './theme'
|
|
4
|
-
import type {
|
|
4
|
+
import type { AutoLayoutDirection, ViewManualLayout } from './view'
|
|
5
5
|
|
|
6
6
|
export namespace ViewChange {
|
|
7
7
|
export interface ChangeElementStyle {
|
|
@@ -22,7 +22,11 @@ export namespace ViewChange {
|
|
|
22
22
|
|
|
23
23
|
export interface ChangeAutoLayout {
|
|
24
24
|
op: 'change-autolayout'
|
|
25
|
-
layout:
|
|
25
|
+
layout: {
|
|
26
|
+
direction: AutoLayoutDirection
|
|
27
|
+
nodeSep?: number | null
|
|
28
|
+
rankSep?: number | null
|
|
29
|
+
}
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
export type ViewChange = ViewChange.ChangeElementStyle | ViewChange.SaveManualLayout | ViewChange.ChangeAutoLayout
|