@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.
Files changed (35) hide show
  1. package/dist/index.cjs +777 -251
  2. package/dist/index.d.cts +301 -32
  3. package/dist/index.d.mts +301 -32
  4. package/dist/index.d.ts +301 -32
  5. package/dist/index.mjs +774 -252
  6. package/dist/shared/{core.BvFch1jG.mjs → core.BJDxVHFh.mjs} +19 -19
  7. package/dist/shared/{core.dY8efyk0.d.ts → core.C4jDMQ1i.d.cts} +242 -238
  8. package/dist/shared/{core.dY8efyk0.d.cts → core.C4jDMQ1i.d.mts} +242 -238
  9. package/dist/shared/{core.dY8efyk0.d.mts → core.C4jDMQ1i.d.ts} +242 -238
  10. package/dist/shared/{core.CTiE4teS.cjs → core.IR7XOEkQ.cjs} +19 -18
  11. package/dist/types/index.cjs +1 -1
  12. package/dist/types/index.d.cts +1 -1
  13. package/dist/types/index.d.mts +1 -1
  14. package/dist/types/index.d.ts +1 -1
  15. package/dist/types/index.mjs +1 -1
  16. package/package.json +4 -3
  17. package/src/builder/Builder.element.ts +221 -0
  18. package/src/builder/Builder.model.ts +346 -0
  19. package/src/builder/Builder.test-d.ts +69 -0
  20. package/src/builder/Builder.ts +574 -0
  21. package/src/builder/Builder.view.ts +358 -0
  22. package/src/builder/Builder.views.ts +318 -0
  23. package/src/builder/_types.ts +195 -0
  24. package/src/builder/index.ts +5 -0
  25. package/src/index.ts +4 -0
  26. package/src/model/LikeC4DiagramModel.ts +5 -5
  27. package/src/model/LikeC4Model.ts +63 -46
  28. package/src/model/LikeC4ViewModel.ts +7 -7
  29. package/src/model/__test__/fixture.ts +5 -0
  30. package/src/types/element.ts +19 -11
  31. package/src/types/model.ts +13 -8
  32. package/src/types/relation.ts +1 -1
  33. package/src/types/view-changes.ts +6 -2
  34. package/src/types/view.ts +53 -22
  35. 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
 
@@ -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 = new Map<Fqn, M>()
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
- static computed(source: C4ComputedLikeC4Model): LikeC4Model<LikeC4ViewModel> {
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',
@@ -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<string, 'Fqn'>
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[number]
14
+ export type BorderStyle = TupleToUnion<typeof BorderStyles>
15
15
 
16
- export type ElementKind = Tagged<string, 'ElementKind'>
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[number]
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<string, 'Tag'>
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 Element {
53
- readonly id: Fqn
54
- readonly kind: ElementKind
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> | null
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?: { [key: string]: string }
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 {
@@ -1,20 +1,25 @@
1
- import type { UnwrapTagged } from 'type-fest'
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<UnwrapTagged<ElementKind>, ElementKindSpecification>
13
- relationships: Record<UnwrapTagged<ElementKind>, RelationshipKindSpecification>
16
+ tags: Tag<Tags>[]
17
+ elements: Record<ElementKinds, ElementKindSpecification>
18
+ relationships: Record<RelationKinds, RelationshipKindSpecification>
14
19
  }
15
- elements: Record<Fqn, Element>
20
+ elements: Record<Fqns, TypedElement<Fqns, ElementKinds, Tags>>
16
21
  relations: Record<RelationID, Relation>
17
- views: Record<ViewID, LikeC4View>
22
+ views: Record<Views, LikeC4View<Views, Tags>>
18
23
  }
19
24
 
20
25
  /**
@@ -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<string, 'RelationshipKind'>
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 { ViewManualLayout, ViewRuleAutoLayout } from './view'
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: ViewRuleAutoLayout
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