@likec4/core 1.12.1 → 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
package/src/types/view.ts CHANGED
@@ -7,8 +7,7 @@ import type { RelationID, RelationshipArrowType, RelationshipKind, RelationshipL
7
7
  import type { Color, ThemeColorValues } from './theme'
8
8
  import type { ElementNotation } from './view-notation'
9
9
 
10
- // Full-qualified-name
11
- export type ViewID = Tagged<string, 'ViewID'>
10
+ export type ViewID<Id extends string = string> = Tagged<Id, 'ViewID'>
12
11
 
13
12
  export type ViewRulePredicate =
14
13
  | {
@@ -55,12 +54,16 @@ export function isViewRuleAutoLayout(rule: ViewRule): rule is ViewRuleAutoLayout
55
54
 
56
55
  export type ViewRule = ViewRulePredicate | ViewRuleStyle | ViewRuleAutoLayout
57
56
 
58
- export interface BasicView<ViewType extends 'element' | 'dynamic'> {
57
+ export interface BasicView<
58
+ ViewType extends 'element' | 'dynamic',
59
+ ViewIDs extends string,
60
+ Tags extends string
61
+ > {
59
62
  readonly __?: ViewType
60
- readonly id: ViewID
63
+ readonly id: ViewID<ViewIDs>
61
64
  readonly title: string | null
62
65
  readonly description: string | null
63
- readonly tags: NonEmptyArray<Tag> | null
66
+ readonly tags: NonEmptyArray<Tag<Tags>> | null
64
67
  readonly links: NonEmptyArray<Link> | null
65
68
 
66
69
  /**
@@ -86,18 +89,30 @@ export interface BasicView<ViewType extends 'element' | 'dynamic'> {
86
89
  readonly customColorDefinitions: CustomColorDefinitions
87
90
  }
88
91
 
89
- export interface BasicElementView extends BasicView<'element'> {
92
+ export interface BasicElementView<ViewIDs extends string, Tags extends string>
93
+ extends BasicView<'element', ViewIDs, Tags>
94
+ {
90
95
  readonly viewOf?: Fqn
91
96
  readonly rules: ViewRule[]
92
97
  }
93
- export interface ScopedElementView extends BasicElementView {
98
+ export interface ScopedElementView<ViewIDs extends string, Tags extends string>
99
+ extends BasicElementView<ViewIDs, Tags>
100
+ {
94
101
  readonly viewOf: Fqn
95
102
  }
96
103
 
97
- export interface ExtendsElementView extends BasicElementView {
98
- readonly extends: ViewID
104
+ export interface ExtendsElementView<ViewIDs extends string, Tags extends string>
105
+ extends BasicElementView<ViewIDs, Tags>
106
+ {
107
+ readonly extends: ViewID<ViewIDs>
99
108
  }
100
- export type ElementView = ScopedElementView | ExtendsElementView | BasicElementView
109
+ export type ElementView<
110
+ ViewIDs extends string = string,
111
+ Tags extends string = string
112
+ > =
113
+ | ScopedElementView<ViewIDs, Tags>
114
+ | ExtendsElementView<ViewIDs, Tags>
115
+ | BasicElementView<ViewIDs, Tags>
101
116
 
102
117
  export interface DynamicViewStep {
103
118
  readonly source: Fqn
@@ -133,7 +148,10 @@ export function isDynamicViewIncludeRule(rule: DynamicViewRule): rule is Dynamic
133
148
  }
134
149
 
135
150
  export type DynamicViewRule = DynamicViewIncludeRule | ViewRuleStyle | ViewRuleAutoLayout
136
- export interface DynamicView extends BasicView<'dynamic'> {
151
+ export interface DynamicView<
152
+ ViewIDs extends string = string,
153
+ Tags extends string = string
154
+ > extends BasicView<'dynamic', ViewIDs, Tags> {
137
155
  readonly __: 'dynamic'
138
156
 
139
157
  readonly steps: DynamicViewStepOrParallel[]
@@ -147,7 +165,10 @@ export function isDynamicViewParallelSteps(step: DynamicViewStepOrParallel): ste
147
165
 
148
166
  export type CustomColorDefinitions = { [key: string]: ThemeColorValues }
149
167
 
150
- export type LikeC4View = ElementView | DynamicView
168
+ export type LikeC4View<
169
+ ViewIDs extends string = string,
170
+ Tags extends string = string
171
+ > = ElementView<ViewIDs, Tags> | DynamicView<ViewIDs, Tags>
151
172
 
152
173
  export function isDynamicView(view: LikeC4View): view is DynamicView {
153
174
  return view.__ === 'dynamic'
@@ -156,15 +177,15 @@ export function isElementView(view: LikeC4View): view is ElementView {
156
177
  return isNullish(view.__) || view.__ === 'element'
157
178
  }
158
179
 
159
- export function isExtendsElementView(view: LikeC4View): view is ExtendsElementView {
180
+ export function isExtendsElementView(view: LikeC4View): view is ExtendsElementView<string, string> {
160
181
  return isElementView(view) && 'extends' in view
161
182
  }
162
183
 
163
- export function isScopedElementView(view: LikeC4View): view is ScopedElementView {
184
+ export function isScopedElementView(view: LikeC4View): view is ScopedElementView<string, string> {
164
185
  return isElementView(view) && 'viewOf' in view
165
186
  }
166
187
 
167
- export type NodeId = Tagged<string, 'Fqn'>
188
+ export type NodeId<IDs extends string = string> = Tagged<IDs, 'Fqn'>
168
189
 
169
190
  export type EdgeId = Tagged<string, 'EdgeId'>
170
191
  export type StepEdgeIdLiteral = `step-${number}` | `step-${number}.${number}`
@@ -276,17 +297,21 @@ export interface ViewAutoLayout {
276
297
  rankSep?: number
277
298
  nodeSep?: number
278
299
  }
279
- export interface ComputedElementView extends Omit<ElementView, 'rules' | 'docUri'>, ViewWithHash, ViewWithNotation {
280
- readonly extends?: ViewID
300
+ export interface ComputedElementView<
301
+ ViewIDs extends string = string,
302
+ Tags extends string = string
303
+ > extends Omit<ElementView<ViewIDs, Tags>, 'rules' | 'docUri'>, ViewWithHash, ViewWithNotation {
304
+ readonly extends?: ViewID<ViewIDs>
281
305
  readonly autoLayout: ViewAutoLayout
282
306
  readonly nodes: ComputedNode[]
283
307
  readonly edges: ComputedEdge[]
284
308
  rules?: never
285
309
  docUri?: never
286
310
  }
287
- export interface ComputedDynamicView
288
- extends Omit<DynamicView, 'rules' | 'steps' | 'docUri'>, ViewWithHash, ViewWithNotation
289
- {
311
+ export interface ComputedDynamicView<
312
+ ViewIDs extends string = string,
313
+ Tags extends string = string
314
+ > extends Omit<DynamicView<ViewIDs, Tags>, 'rules' | 'steps' | 'docUri'>, ViewWithHash, ViewWithNotation {
290
315
  readonly autoLayout: ViewAutoLayout
291
316
  readonly nodes: ComputedNode[]
292
317
  readonly edges: ComputedEdge[]
@@ -298,7 +323,10 @@ export function isComputedDynamicView(view: ComputedView): view is ComputedDynam
298
323
  return view.__ === 'dynamic'
299
324
  }
300
325
 
301
- export type ComputedView = ComputedElementView | ComputedDynamicView
326
+ export type ComputedView<
327
+ ViewIDs extends string = string,
328
+ Tags extends string = string
329
+ > = ComputedElementView<ViewIDs, Tags> | ComputedDynamicView<ViewIDs, Tags>
302
330
 
303
331
  export function isComputedElementView(view: ComputedView): view is ComputedElementView {
304
332
  return isNullish(view.__) || view.__ === 'element'
@@ -343,7 +371,10 @@ export interface DiagramEdge extends ComputedEdge {
343
371
  dotpos?: string
344
372
  }
345
373
 
346
- export interface DiagramView extends Omit<ComputedView, 'nodes' | 'edges' | 'manualLayout'> {
374
+ export interface DiagramView<
375
+ ViewIDs extends string = string,
376
+ Tags extends string = string
377
+ > extends Omit<ComputedView<ViewIDs, Tags>, 'nodes' | 'edges' | 'manualLayout'> {
347
378
  readonly nodes: DiagramNode[]
348
379
  readonly edges: DiagramEdge[]
349
380
  readonly bounds: BBox
package/src/utils/fqn.ts CHANGED
@@ -1,10 +1,11 @@
1
+ import type { LiteralUnion } from 'type-fest'
1
2
  import type { Element, Fqn } from '../types'
2
3
  import { compareNatural } from './compare-natural'
3
4
  import { isString } from './guards'
4
5
 
5
6
  type Predicate<T> = (x: T) => boolean
6
7
 
7
- export function nameFromFqn(fqn: Fqn) {
8
+ export function nameFromFqn(fqn: LiteralUnion<Fqn, string>) {
8
9
  const lastDot = fqn.lastIndexOf('.')
9
10
  if (lastDot > 0) {
10
11
  return fqn.slice(lastDot + 1)
@@ -37,7 +38,7 @@ export function notDescendantOf(ancestors: Element[]): (e: Element) => boolean {
37
38
  return (e: Element) => !isDescendant(e)
38
39
  }
39
40
 
40
- export function commonAncestor(first: Fqn, second: Fqn) {
41
+ export function commonAncestor<IDs extends string>(first: Fqn<IDs>, second: Fqn<IDs>) {
41
42
  const parentA = parentFqn(first)
42
43
  const parentB = parentFqn(second)
43
44
  if (parentA === parentB) {
@@ -49,20 +50,20 @@ export function commonAncestor(first: Fqn, second: Fqn) {
49
50
 
50
51
  const a = first.split('.')
51
52
  const b = second.split('.')
52
- let ancestor: Fqn | null = null
53
+ let ancestor: Fqn<IDs> | null = null
53
54
 
54
55
  while (a.length > 1 && b.length > 1 && !!a[0] && a[0] === b[0]) {
55
- ancestor = (ancestor ? `${ancestor}.${a[0]}` : a[0]) as Fqn
56
+ ancestor = (ancestor ? `${ancestor}.${a[0]}` : a[0]) as Fqn<IDs>
56
57
  a.shift()
57
58
  b.shift()
58
59
  }
59
60
  return ancestor
60
61
  }
61
62
 
62
- export function parentFqn(fqn: Fqn): Fqn | null {
63
+ export function parentFqn<IDs extends string>(fqn: Fqn<IDs>): Fqn<IDs> | null {
63
64
  const lastDot = fqn.lastIndexOf('.')
64
65
  if (lastDot > 0) {
65
- return fqn.slice(0, lastDot) as Fqn
66
+ return fqn.slice(0, lastDot) as Fqn<IDs>
66
67
  }
67
68
  return null
68
69
  }
@@ -76,20 +77,20 @@ export function parentFqnPredicate<T extends { parent: Fqn | null }>(parent: Fqn
76
77
  * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
77
78
  * (from closest to root)
78
79
  */
79
- export function ancestorsFqn(fqn: Fqn): Fqn[] {
80
- const path = fqn.split('.')
80
+ export function ancestorsFqn<IDs extends string>(fqn: Fqn<IDs>): Fqn<IDs>[] {
81
+ const path = fqn.split('.') as Fqn<IDs>[]
81
82
  path.pop()
82
83
  if (path.length === 0) {
83
84
  return []
84
85
  }
85
86
  return path.reduce((acc, part, idx) => {
86
87
  if (idx === 0) {
87
- acc.push(part as Fqn)
88
+ acc.push(part)
88
89
  return acc
89
90
  }
90
- acc.unshift(`${acc[0]}.${part}` as Fqn)
91
+ acc.unshift(`${acc[0]}.${part}` as Fqn<IDs>)
91
92
  return acc
92
- }, [] as Fqn[])
93
+ }, [] as Fqn<IDs>[])
93
94
  }
94
95
 
95
96
  /**