@likec4/core 1.1.1 → 1.2.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.
Files changed (56) hide show
  1. package/dist/colors/element.d.ts +1 -0
  2. package/dist/colors/index.d.ts +1 -0
  3. package/dist/colors/relationships.d.ts +1 -0
  4. package/dist/errors/errors.spec.d.ts +2 -0
  5. package/dist/errors/errors.spec.js +69 -0
  6. package/dist/errors/index.d.ts +1 -0
  7. package/dist/errors/index.js +7 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/types/_common.d.ts +1 -0
  10. package/dist/types/element.d.ts +1 -0
  11. package/dist/types/expression.d.ts +1 -0
  12. package/dist/types/index.d.ts +1 -2
  13. package/dist/types/index.js +2 -3
  14. package/dist/types/model.d.ts +3 -3
  15. package/dist/types/opaque.d.ts +1 -0
  16. package/dist/types/relation.d.ts +1 -0
  17. package/dist/types/theme.d.ts +1 -0
  18. package/dist/types/view.d.ts +133 -8
  19. package/dist/types/view.js +31 -3
  20. package/dist/utils/fqn.d.ts +1 -0
  21. package/dist/utils/fqn.spec.d.ts +2 -0
  22. package/dist/utils/fqn.spec.js +82 -0
  23. package/dist/utils/guards.d.ts +1 -0
  24. package/dist/utils/index.d.ts +1 -0
  25. package/dist/utils/promises.d.ts +1 -0
  26. package/dist/utils/relations.d.ts +1 -0
  27. package/dist/utils/relations.spec.d.ts +2 -0
  28. package/dist/utils/relations.spec.js +210 -0
  29. package/package.json +6 -6
  30. package/src/colors/element.ts +80 -0
  31. package/src/colors/index.ts +12 -0
  32. package/src/colors/relationships.ts +54 -0
  33. package/src/errors/errors.spec.ts +88 -0
  34. package/src/errors/index.ts +120 -0
  35. package/src/index.ts +9 -0
  36. package/src/reset.d.ts +2 -0
  37. package/src/types/_common.ts +5 -0
  38. package/src/types/element.ts +56 -0
  39. package/src/types/expression.ts +140 -0
  40. package/src/types/index.ts +10 -0
  41. package/src/types/model.ts +15 -0
  42. package/src/types/opaque.ts +108 -0
  43. package/src/types/relation.ts +38 -0
  44. package/src/types/theme.ts +46 -0
  45. package/src/types/view.ts +263 -0
  46. package/src/utils/fqn.spec.ts +105 -0
  47. package/src/utils/fqn.ts +112 -0
  48. package/src/utils/guards.ts +10 -0
  49. package/src/utils/index.ts +4 -0
  50. package/src/utils/promises.ts +9 -0
  51. package/src/utils/relations.spec.ts +268 -0
  52. package/src/utils/relations.ts +104 -0
  53. package/dist/types/computed-view.d.ts +0 -52
  54. package/dist/types/computed-view.js +0 -1
  55. package/dist/types/diagram.d.ts +0 -40
  56. package/dist/types/diagram.js +0 -1
@@ -0,0 +1,140 @@
1
+ import type { IconUrl } from './_common'
2
+ import type { BorderStyle, ElementKind, ElementShape, Fqn, Tag } from './element'
3
+ import type { ThemeColor } from './theme'
4
+ import type { ViewID } from './view'
5
+
6
+ interface BaseExpr {
7
+ element?: never
8
+ custom?: never
9
+ expanded?: never
10
+ elementKind?: never
11
+ elementTag?: never
12
+ isEqual?: never
13
+ isDescedants?: never
14
+ wildcard?: never
15
+ source?: never
16
+ target?: never
17
+ inout?: never
18
+ incoming?: never
19
+ outgoing?: never
20
+ }
21
+
22
+ export interface ElementRefExpr extends Omit<BaseExpr, 'element' | 'isDescedants'> {
23
+ element: Fqn
24
+ isDescedants?: boolean
25
+ }
26
+ export function isElementRef(expr: Expression): expr is ElementRefExpr {
27
+ return 'element' in expr
28
+ }
29
+
30
+ export interface ExpandedElementExpr extends Omit<BaseExpr, 'expanded'> {
31
+ expanded: Fqn
32
+ }
33
+ export function isExpandedElementExpr(expr: Expression): expr is ExpandedElementExpr {
34
+ return 'expanded' in expr
35
+ }
36
+
37
+ export interface CustomElementExpr extends Omit<BaseExpr, 'custom'> {
38
+ custom: {
39
+ element: Fqn
40
+ title?: string
41
+ description?: string
42
+ technology?: string
43
+ shape?: ElementShape
44
+ color?: ThemeColor
45
+ icon?: IconUrl
46
+ border?: BorderStyle
47
+ opacity?: number
48
+ navigateTo?: ViewID
49
+ }
50
+ }
51
+
52
+ export function isCustomElement(expr: Expression): expr is CustomElementExpr {
53
+ return 'custom' in expr
54
+ }
55
+
56
+ export interface WildcardExpr extends Omit<BaseExpr, 'wildcard'> {
57
+ wildcard: true
58
+ }
59
+ export function isWildcard(expr: Expression): expr is WildcardExpr {
60
+ return 'wildcard' in expr
61
+ }
62
+ export interface ElementKindExpr extends Omit<BaseExpr, 'elementKind' | 'isEqual'> {
63
+ elementKind: ElementKind
64
+ isEqual: boolean
65
+ }
66
+ export function isElementKindExpr(expr: Expression): expr is ElementKindExpr {
67
+ return 'elementKind' in expr && 'isEqual' in expr
68
+ }
69
+
70
+ export interface ElementTagExpr extends Omit<BaseExpr, 'elementTag' | 'isEqual'> {
71
+ elementTag: Tag
72
+ isEqual: boolean
73
+ }
74
+ export function isElementTagExpr(expr: Expression): expr is ElementTagExpr {
75
+ return 'elementTag' in expr && 'isEqual' in expr
76
+ }
77
+
78
+ export type ElementExpression =
79
+ | ElementRefExpr
80
+ | WildcardExpr
81
+ | ElementKindExpr
82
+ | ElementTagExpr
83
+ | ExpandedElementExpr
84
+ export function isElement(expr: Expression): expr is ElementExpression {
85
+ return isElementRef(expr)
86
+ || isWildcard(expr)
87
+ || isElementKindExpr(expr)
88
+ || isElementTagExpr(expr)
89
+ || isExpandedElementExpr(expr)
90
+ }
91
+
92
+ export interface RelationExpr extends Omit<BaseExpr, 'source' | 'target'> {
93
+ source: ElementExpression
94
+ target: ElementExpression
95
+ isBidirectional?: boolean
96
+ }
97
+ export function isRelation(expr: Expression): expr is RelationExpr {
98
+ return 'source' in expr && 'target' in expr
99
+ }
100
+
101
+ export interface InOutExpr extends Omit<BaseExpr, 'inout'> {
102
+ inout: ElementExpression
103
+ }
104
+ export function isInOut(expr: Expression): expr is InOutExpr {
105
+ return 'inout' in expr
106
+ }
107
+
108
+ export interface IncomingExpr extends Omit<BaseExpr, 'incoming'> {
109
+ incoming: ElementExpression
110
+ }
111
+ export function isIncoming(expr: Expression): expr is IncomingExpr {
112
+ return 'incoming' in expr
113
+ }
114
+ export interface OutgoingExpr extends Omit<BaseExpr, 'outgoing'> {
115
+ outgoing: ElementExpression
116
+ }
117
+ export function isOutgoing(expr: Expression): expr is OutgoingExpr {
118
+ return 'outgoing' in expr
119
+ }
120
+
121
+ export type AnyRelationExpression = RelationExpr | InOutExpr | IncomingExpr | OutgoingExpr
122
+
123
+ export function isAnyRelation(expr: Expression): expr is AnyRelationExpression {
124
+ return isRelation(expr) || isInOut(expr) || isIncoming(expr) || isOutgoing(expr)
125
+ }
126
+
127
+ export type Expression = ElementExpression | CustomElementExpr | AnyRelationExpression
128
+
129
+ // export const Expr = {
130
+ // isElementRef,
131
+ // isWildcard,
132
+ // isElementKindExpr,
133
+ // isElementTagExpr,
134
+ // isElement,
135
+ // isRelation,
136
+ // isInOut,
137
+ // isIncoming,
138
+ // isOutgoing,
139
+ // isAnyRelation,
140
+ // }
@@ -0,0 +1,10 @@
1
+ export * from './_common'
2
+ export * from './element'
3
+ export * from './expression'
4
+ export * from './model'
5
+ export * from './opaque'
6
+ export * from './relation'
7
+ export * from './theme'
8
+ export * from './view'
9
+
10
+ export * as Expr from './expression'
@@ -0,0 +1,15 @@
1
+ import type { Element, Fqn } from './element'
2
+ import type { Relation, RelationID } from './relation'
3
+ import type { ComputedView, View, ViewID } from './view'
4
+
5
+ export interface LikeC4Model {
6
+ elements: Record<Fqn, Element>
7
+ relations: Record<RelationID, Relation>
8
+ views: Record<ViewID, View>
9
+ }
10
+
11
+ export interface LikeC4ComputedModel {
12
+ elements: Record<Fqn, Element>
13
+ relations: Record<RelationID, Relation>
14
+ views: Record<ViewID, ComputedView>
15
+ }
@@ -0,0 +1,108 @@
1
+ declare const tag: unique symbol
2
+
3
+ type Tagged<Token> = {
4
+ readonly [tag]: Token
5
+ }
6
+
7
+ /**
8
+ Create an opaque type, which hides its internal details from the public, and can only be created by being used explicitly.
9
+
10
+ The generic type parameter can be anything. It doesn't have to be an object.
11
+
12
+ [Read more about opaque types.](https://codemix.com/opaque-types-in-javascript/)
13
+
14
+ There have been several discussions about adding this feature to TypeScript via the `opaque type` operator, similar to how Flow does it. Unfortunately, nothing has (yet) moved forward:
15
+ - [Microsoft/TypeScript#202](https://github.com/microsoft/TypeScript/issues/202)
16
+ - [Microsoft/TypeScript#15408](https://github.com/Microsoft/TypeScript/issues/15408)
17
+ - [Microsoft/TypeScript#15807](https://github.com/Microsoft/TypeScript/issues/15807)
18
+
19
+ @example
20
+ ```
21
+ import type {Opaque} from 'type-fest';
22
+
23
+ type AccountNumber = Opaque<number, 'AccountNumber'>;
24
+ type AccountBalance = Opaque<number, 'AccountBalance'>;
25
+
26
+ // The `Token` parameter allows the compiler to differentiate between types, whereas "unknown" will not. For example, consider the following structures:
27
+ type ThingOne = Opaque<string>;
28
+ type ThingTwo = Opaque<string>;
29
+
30
+ // To the compiler, these types are allowed to be cast to each other as they have the same underlying type. They are both `string & { __opaque__: unknown }`.
31
+ // To avoid this behaviour, you would instead pass the "Token" parameter, like so.
32
+ type NewThingOne = Opaque<string, 'ThingOne'>;
33
+ type NewThingTwo = Opaque<string, 'ThingTwo'>;
34
+
35
+ // Now they're completely separate types, so the following will fail to compile.
36
+ function createNewThingOne (): NewThingOne {
37
+ // As you can see, casting from a string is still allowed. However, you may not cast NewThingOne to NewThingTwo, and vice versa.
38
+ return 'new thing one' as NewThingOne;
39
+ }
40
+
41
+ // This will fail to compile, as they are fundamentally different types.
42
+ const thingTwo = createNewThingOne() as NewThingTwo;
43
+
44
+ // Here's another example of opaque typing.
45
+ function createAccountNumber(): AccountNumber {
46
+ return 2 as AccountNumber;
47
+ }
48
+
49
+ function getMoneyForAccount(accountNumber: AccountNumber): AccountBalance {
50
+ return 4 as AccountBalance;
51
+ }
52
+
53
+ // This will compile successfully.
54
+ getMoneyForAccount(createAccountNumber());
55
+
56
+ // But this won't, because it has to be explicitly passed as an `AccountNumber` type.
57
+ getMoneyForAccount(2);
58
+
59
+ // You can use opaque values like they aren't opaque too.
60
+ const accountNumber = createAccountNumber();
61
+
62
+ // This will not compile successfully.
63
+ const newAccountNumber = accountNumber + 2;
64
+
65
+ // As a side note, you can (and should) use recursive types for your opaque types to make them stronger and hopefully easier to type.
66
+ type Person = {
67
+ id: Opaque<number, Person>;
68
+ name: string;
69
+ };
70
+ ```
71
+
72
+ @category Type
73
+ */
74
+ export type Opaque<Type, Token = unknown> = Type & Tagged<Token>
75
+
76
+ /**
77
+ Revert an opaque type back to its original type by removing the readonly `[tag]`.
78
+
79
+ Why is this necessary?
80
+
81
+ 1. Use an `Opaque` type as object keys
82
+ 2. Prevent TS4058 error: "Return type of exported function has or is using name X from external module Y but cannot be named"
83
+
84
+ @example
85
+ ```
86
+ import type {Opaque, UnwrapOpaque} from 'type-fest';
87
+
88
+ type AccountType = Opaque<'SAVINGS' | 'CHECKING', 'AccountType'>;
89
+
90
+ const moneyByAccountType: Record<UnwrapOpaque<AccountType>, number> = {
91
+ SAVINGS: 99,
92
+ CHECKING: 0.1
93
+ };
94
+
95
+ // Without UnwrapOpaque, the following expression would throw a type error.
96
+ const money = moneyByAccountType.SAVINGS; // TS error: Property 'SAVINGS' does not exist
97
+
98
+ // Attempting to pass an non-Opaque type to UnwrapOpaque will raise a type error.
99
+ type WontWork = UnwrapOpaque<string>;
100
+ ```
101
+
102
+ @category Type
103
+ */
104
+ export type UnwrapOpaque<OpaqueType extends Tagged<unknown>> = OpaqueType extends Opaque<
105
+ infer Type,
106
+ OpaqueType[typeof tag]
107
+ > ? Type
108
+ : OpaqueType
@@ -0,0 +1,38 @@
1
+ import type { NonEmptyArray } from './_common'
2
+ import type { Fqn, Tag } from './element'
3
+ import type { Opaque } from './opaque'
4
+ import type { ThemeColor } from './theme'
5
+
6
+ export type RelationID = Opaque<string, 'RelationID'>
7
+ export type RelationshipKind = Opaque<string, 'RelationshipKind'>
8
+
9
+ export type RelationshipLineType = 'dashed' | 'solid' | 'dotted'
10
+
11
+ // reference: https://graphviz.org/docs/attr-types/arrowType/
12
+ export type RelationshipArrowType =
13
+ | 'none'
14
+ | 'normal'
15
+ | 'onormal'
16
+ | 'diamond'
17
+ | 'odiamond'
18
+ | 'crow'
19
+ | 'open'
20
+ | 'vee'
21
+
22
+ export const DefaultLineStyle = 'dashed' satisfies RelationshipLineType
23
+ export const DefaultArrowType = 'normal' satisfies RelationshipArrowType
24
+ export const DefaultRelationshipColor = 'gray' satisfies ThemeColor
25
+
26
+ export interface Relation {
27
+ readonly id: RelationID
28
+ readonly source: Fqn
29
+ readonly target: Fqn
30
+ readonly title: string
31
+ readonly tags?: NonEmptyArray<Tag>
32
+ readonly kind?: RelationshipKind
33
+ readonly color?: ThemeColor
34
+ readonly line?: RelationshipLineType
35
+ readonly head?: RelationshipArrowType
36
+ readonly tail?: RelationshipArrowType
37
+ readonly links?: NonEmptyArray<string>
38
+ }
@@ -0,0 +1,46 @@
1
+ export type ThemeColor =
2
+ | 'amber'
3
+ | 'blue'
4
+ | 'gray'
5
+ | 'slate'
6
+ | 'green'
7
+ | 'indigo'
8
+ | 'muted'
9
+ | 'primary'
10
+ | 'red'
11
+ | 'secondary'
12
+ | 'sky'
13
+
14
+ export type HexColorLiteral = `#${string}`
15
+
16
+ export type ColorLiteral = HexColorLiteral
17
+
18
+ export interface ElementThemeColorValues {
19
+ fill: ColorLiteral
20
+ stroke: ColorLiteral
21
+ // Main text (title, etc.)
22
+ hiContrast: ColorLiteral
23
+ // Secondary text (description, etc.)
24
+ loContrast: ColorLiteral
25
+ }
26
+
27
+ export type ElementThemeColors = {
28
+ [key in ThemeColor]: ElementThemeColorValues
29
+ }
30
+
31
+ export interface RelationshipThemeColorValues {
32
+ lineColor: ColorLiteral
33
+ labelBgColor: ColorLiteral
34
+ labelColor: ColorLiteral
35
+ }
36
+
37
+ export type RelationshipThemeColors = {
38
+ [key in ThemeColor]: RelationshipThemeColorValues
39
+ }
40
+
41
+ export interface LikeC4Theme {
42
+ font: 'Helvetica' // for now only support Helvetica
43
+ shadow: ColorLiteral
44
+ relationships: RelationshipThemeColors
45
+ elements: ElementThemeColors
46
+ }
@@ -0,0 +1,263 @@
1
+ import { isNullish } from 'remeda'
2
+ import type { IconUrl, NonEmptyArray } from './_common'
3
+ import type { ElementKind, ElementShape, ElementStyle, Fqn, Tag } from './element'
4
+ import type { CustomElementExpr, ElementExpression, Expression } from './expression'
5
+ import type { Opaque } from './opaque'
6
+ import type { RelationID, RelationshipArrowType, RelationshipLineType } from './relation'
7
+ import type { ColorLiteral, ThemeColor } from './theme'
8
+
9
+ // Full-qualified-name
10
+ export type ViewID = Opaque<string, 'ViewID'>
11
+
12
+ export type ViewRuleExpression =
13
+ | {
14
+ include: Expression[]
15
+ exclude?: never
16
+ }
17
+ | {
18
+ include?: never
19
+ exclude: Expression[]
20
+ }
21
+ export function isViewRuleExpression(rule: ViewRule): rule is ViewRuleExpression {
22
+ return (
23
+ ('include' in rule && Array.isArray(rule.include))
24
+ || ('exclude' in rule && Array.isArray(rule.exclude))
25
+ )
26
+ }
27
+
28
+ export interface ViewRuleStyle {
29
+ targets: ElementExpression[]
30
+ style: ElementStyle & {
31
+ color?: ThemeColor
32
+ shape?: ElementShape
33
+ icon?: IconUrl
34
+ }
35
+ }
36
+ export function isViewRuleStyle(rule: ViewRule): rule is ViewRuleStyle {
37
+ return 'style' in rule && 'targets' in rule
38
+ }
39
+
40
+ export type AutoLayoutDirection = 'TB' | 'BT' | 'LR' | 'RL'
41
+ export interface ViewRuleAutoLayout {
42
+ autoLayout: AutoLayoutDirection
43
+ }
44
+ export function isViewRuleAutoLayout(rule: ViewRule): rule is ViewRuleAutoLayout {
45
+ return 'autoLayout' in rule
46
+ }
47
+
48
+ export type ViewRule = ViewRuleExpression | ViewRuleStyle | ViewRuleAutoLayout
49
+
50
+ export interface BasicView<ViewType extends 'element' | 'dynamic' = 'element' | 'dynamic'> {
51
+ readonly __?: ViewType
52
+ readonly id: ViewID
53
+ readonly title: string | null
54
+ readonly description: string | null
55
+ readonly tags: NonEmptyArray<Tag> | null
56
+ readonly links: NonEmptyArray<string> | null
57
+
58
+ /**
59
+ * URI to the source file of this view.
60
+ * Undefined if the view is auto-generated.
61
+ */
62
+ readonly docUri?: string
63
+ /**
64
+ * For all views we find common ancestor path.
65
+ * This is used to generate relative paths, i.e.:
66
+ * - "" for views in the common ancestor directory (or root)
67
+ * - "subdir" for views in "<root>/subdir"
68
+ * - "subdir/subdir1" for views in "<root>/subdir/subdir1"
69
+ *
70
+ * Undefined if the view is auto-generated.
71
+ */
72
+ readonly relativePath?: string
73
+ }
74
+
75
+ export interface BasicElementView extends BasicView<'element'> {
76
+ readonly viewOf?: Fqn
77
+ readonly rules: ViewRule[]
78
+ }
79
+ export interface StrictElementView extends BasicElementView {
80
+ readonly viewOf: Fqn
81
+ }
82
+
83
+ export interface ExtendsElementView extends BasicElementView {
84
+ readonly extends: ViewID
85
+ }
86
+ export type ElementView = StrictElementView | ExtendsElementView | BasicElementView
87
+
88
+ export interface DynamicViewStep {
89
+ readonly source: Fqn
90
+ readonly target: Fqn
91
+ readonly title: string | null
92
+ readonly isBackward?: boolean
93
+ }
94
+
95
+ export type DynamicViewIncludeRule = {
96
+ include: (ElementExpression | CustomElementExpr)[]
97
+ }
98
+
99
+ export function isDynamicViewIncludeRule(rule: DynamicViewRule): rule is DynamicViewIncludeRule {
100
+ return 'include' in rule && Array.isArray(rule.include)
101
+ }
102
+
103
+ export type DynamicViewRule = DynamicViewIncludeRule | ViewRuleStyle | ViewRuleAutoLayout
104
+ export interface DynamicView extends BasicView<'dynamic'> {
105
+ readonly __: 'dynamic'
106
+
107
+ readonly steps: DynamicViewStep[]
108
+
109
+ readonly rules: DynamicViewRule[]
110
+ }
111
+
112
+ export type View = ElementView | DynamicView
113
+
114
+ export function isDynamicView(view: View): view is DynamicView {
115
+ return view.__ === 'dynamic'
116
+ }
117
+ export function isElementView(view: View): view is ElementView {
118
+ return isNullish(view.__) || view.__ === 'element'
119
+ }
120
+
121
+ export function isExtendsElementView(view: View): view is ExtendsElementView {
122
+ return isElementView(view) && 'extends' in view
123
+ }
124
+
125
+ export function isStrictElementView(view: View): view is StrictElementView {
126
+ return isElementView(view) && 'viewOf' in view
127
+ }
128
+
129
+ export type NodeId = Fqn
130
+
131
+ export type EdgeId = Opaque<string, 'EdgeId'>
132
+
133
+ export type StepEdgeIdLiteral = `step-${number}`
134
+ export type StepEdgeId = Opaque<StepEdgeIdLiteral, 'EdgeId'>
135
+ export function StepEdgeId(step: number): StepEdgeId {
136
+ return `step-${String(step).padStart(3, '0')}` as StepEdgeId
137
+ }
138
+
139
+ export function isStepEdgeId(id: string): id is StepEdgeId {
140
+ return id.startsWith('step-')
141
+ }
142
+ export function extractStep(id: EdgeId): number {
143
+ if (!isStepEdgeId(id)) {
144
+ throw new Error(`Invalid step edge id: ${id}`)
145
+ }
146
+ return Number(id.slice('step-'.length))
147
+ }
148
+
149
+ export interface ComputedNode {
150
+ id: NodeId
151
+ kind: ElementKind
152
+ parent: NodeId | null
153
+ title: string
154
+ description: string | null
155
+ technology: string | null
156
+ tags: NonEmptyArray<Tag> | null
157
+ links: NonEmptyArray<string> | null
158
+ children: NodeId[]
159
+ inEdges: EdgeId[]
160
+ outEdges: EdgeId[]
161
+ shape: ElementShape
162
+ /**
163
+ * @deprecated Use `style` instead
164
+ */
165
+ color: ThemeColor
166
+ /**
167
+ * @deprecated Use `style` instead
168
+ */
169
+ icon?: IconUrl
170
+ style: ElementStyle
171
+ navigateTo?: ViewID
172
+ level: number
173
+ // For compound nodes, the max depth of nested nodes
174
+ depth?: number
175
+ }
176
+
177
+ export interface ComputedEdge {
178
+ id: EdgeId
179
+ parent: NodeId | null
180
+ source: NodeId
181
+ target: NodeId
182
+ label: string | null
183
+ relations: RelationID[]
184
+ color?: ThemeColor
185
+ line?: RelationshipLineType
186
+ head?: RelationshipArrowType
187
+ tail?: RelationshipArrowType
188
+
189
+ /**
190
+ * For layouting purposes
191
+ * @default 'forward'
192
+ */
193
+ dir?: 'forward' | 'back'
194
+ }
195
+
196
+ export interface ComputedElementView extends Omit<ElementView, 'rules'> {
197
+ readonly extends?: ViewID
198
+ readonly autoLayout: ViewRuleAutoLayout['autoLayout']
199
+ readonly nodes: ComputedNode[]
200
+ readonly edges: ComputedEdge[]
201
+ }
202
+ export interface ComputedDynamicView extends Omit<DynamicView, 'rules' | 'steps'> {
203
+ readonly autoLayout: ViewRuleAutoLayout['autoLayout']
204
+ readonly nodes: ComputedNode[]
205
+ readonly edges: ComputedEdge[]
206
+ }
207
+ export function isComputedDynamicView(view: ComputedView): view is ComputedDynamicView {
208
+ return view.__ === 'dynamic'
209
+ }
210
+
211
+ export type ComputedView = ComputedElementView | ComputedDynamicView
212
+
213
+ export function isComputedElementView(view: ComputedView): view is ComputedElementView {
214
+ return isNullish(view.__) || view.__ === 'element'
215
+ }
216
+
217
+ export type Point = readonly [x: number, y: number]
218
+
219
+ // Bounding box
220
+ export type BBox = {
221
+ x: number
222
+ y: number
223
+ width: number
224
+ height: number
225
+ }
226
+
227
+ export interface DiagramLabel {
228
+ align: 'left' | 'right' | 'center'
229
+ fontStyle?: 'bold' | 'normal'
230
+ color?: ColorLiteral
231
+ fontSize: number
232
+ pt: Point
233
+ width: number
234
+ text: string
235
+ }
236
+
237
+ export interface DiagramNode extends ComputedNode {
238
+ width: number
239
+ height: number
240
+ position: Point // Absolute position, top left
241
+ // relative: Point // Top left, relative to parent
242
+ labels: DiagramLabel[]
243
+ }
244
+
245
+ export interface DiagramEdge extends ComputedEdge {
246
+ points: NonEmptyArray<Point>
247
+ // Polygons are used to draw arrows
248
+ headArrow?: NonEmptyArray<Point>
249
+ // Draw arrow from the last point of the edge to this point
250
+ headArrowPoint?: Point
251
+ tailArrow?: NonEmptyArray<Point>
252
+ // Draw arrow from the first point of the edge to this point
253
+ tailArrowPoint?: Point
254
+ labels?: NonEmptyArray<DiagramLabel>
255
+ labelBBox?: BBox
256
+ }
257
+
258
+ export interface DiagramView extends Omit<ComputedView, 'nodes' | 'edges'> {
259
+ readonly nodes: DiagramNode[]
260
+ readonly edges: DiagramEdge[]
261
+ readonly width: number
262
+ readonly height: number
263
+ }