@likec4/core 1.8.1 → 1.10.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 +4467 -0
- package/dist/index.d.cts +793 -0
- package/dist/index.d.mts +793 -0
- package/dist/index.d.ts +793 -7
- package/dist/index.mjs +4390 -0
- package/dist/shared/core.CpR2fq5B.d.cts +833 -0
- package/dist/shared/core.CpR2fq5B.d.mts +833 -0
- package/dist/shared/core.CpR2fq5B.d.ts +833 -0
- package/dist/shared/core.Db3ntVII.cjs +318 -0
- package/dist/shared/core.DeifT5lC.mjs +263 -0
- package/dist/types/index.cjs +55 -0
- package/dist/types/index.d.cts +1 -0
- package/dist/types/index.d.mts +1 -0
- package/dist/types/index.d.ts +1 -14
- package/dist/types/index.mjs +1 -0
- package/package.json +43 -33
- package/src/colors/index.ts +27 -1
- package/src/errors/index.ts +3 -95
- package/src/index.ts +6 -6
- package/src/model/LikeC4Model.ts +480 -0
- package/src/model/LikeC4ViewModel.ts +318 -0
- package/src/model/__test__/LikeC4Model.spec.ts +79 -0
- package/src/model/__test__/fixture.ts +333 -0
- package/src/model/index.ts +2 -0
- package/src/model/types.ts +10 -0
- package/src/types/_common.ts +4 -2
- package/src/types/element.ts +11 -8
- package/src/types/expression.ts +7 -3
- package/src/types/index.ts +0 -1
- package/src/types/model.ts +16 -6
- package/src/types/relation.ts +18 -5
- package/src/types/theme.ts +28 -12
- package/src/types/view-notation.ts +3 -4
- package/src/types/view.ts +51 -16
- package/src/utils/compare-natural.spec.ts +37 -0
- package/src/utils/compare-natural.ts +14 -0
- package/src/utils/fqn.spec.ts +99 -2
- package/src/utils/fqn.ts +45 -5
- package/src/utils/index.ts +1 -0
- package/src/utils/relations.spec.ts +2 -4
- package/src/utils/relations.ts +14 -24
- package/dist/colors/element.d.ts +0 -69
- package/dist/colors/element.js +0 -75
- package/dist/colors/index.d.ts +0 -133
- package/dist/colors/index.js +0 -9
- package/dist/colors/relationships.d.ts +0 -58
- package/dist/colors/relationships.js +0 -49
- package/dist/errors/errors.spec.d.ts +0 -2
- package/dist/errors/errors.spec.js +0 -69
- package/dist/errors/index.d.ts +0 -39
- package/dist/errors/index.js +0 -104
- package/dist/index.js +0 -4
- package/dist/types/_common.d.ts +0 -9
- package/dist/types/_common.js +0 -1
- package/dist/types/element.d.ts +0 -55
- package/dist/types/element.js +0 -15
- package/dist/types/expression.d.ts +0 -119
- package/dist/types/expression.js +0 -67
- package/dist/types/index.js +0 -14
- package/dist/types/model.d.ts +0 -14
- package/dist/types/model.js +0 -1
- package/dist/types/opaque.d.ts +0 -103
- package/dist/types/opaque.js +0 -1
- package/dist/types/operators.d.ts +0 -44
- package/dist/types/operators.js +0 -59
- package/dist/types/overview-graph.d.ts +0 -41
- package/dist/types/overview-graph.js +0 -1
- package/dist/types/relation.d.ts +0 -30
- package/dist/types/relation.js +0 -3
- package/dist/types/theme.d.ts +0 -27
- package/dist/types/theme.js +0 -1
- package/dist/types/view-changes.d.ts +0 -26
- package/dist/types/view-changes.js +0 -1
- package/dist/types/view-notation.d.ts +0 -9
- package/dist/types/view-notation.js +0 -1
- package/dist/types/view.d.ts +0 -243
- package/dist/types/view.js +0 -50
- package/dist/utils/fqn.d.ts +0 -39
- package/dist/utils/fqn.js +0 -102
- package/dist/utils/fqn.spec.d.ts +0 -2
- package/dist/utils/fqn.spec.js +0 -82
- package/dist/utils/guards.d.ts +0 -5
- package/dist/utils/guards.js +0 -7
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.js +0 -4
- package/dist/utils/promises.d.ts +0 -2
- package/dist/utils/promises.js +0 -8
- package/dist/utils/relations.d.ts +0 -30
- package/dist/utils/relations.js +0 -79
- package/dist/utils/relations.spec.d.ts +0 -2
- package/dist/utils/relations.spec.js +0 -210
- package/src/errors/errors.spec.ts +0 -88
- package/src/reset.d.ts +0 -2
- package/src/types/opaque.ts +0 -108
package/src/types/view.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { isNullish } from 'remeda'
|
|
1
|
+
import { isArray, isNullish } from 'remeda'
|
|
2
|
+
import type { Tagged } from 'type-fest'
|
|
2
3
|
import type { IconUrl, NonEmptyArray, Point, XYPoint } from './_common'
|
|
3
4
|
import type { ElementKind, ElementShape, ElementStyle, Fqn, Link, Tag } from './element'
|
|
4
5
|
import type { ElementExpression, ElementPredicateExpression, Expression } from './expression'
|
|
5
|
-
import type { Opaque } from './opaque'
|
|
6
6
|
import type { RelationID, RelationshipArrowType, RelationshipKind, RelationshipLineType } from './relation'
|
|
7
|
-
import type {
|
|
7
|
+
import type { Color, ThemeColorValues } from './theme'
|
|
8
8
|
import type { ElementNotation } from './view-notation'
|
|
9
9
|
|
|
10
10
|
// Full-qualified-name
|
|
11
|
-
export type ViewID =
|
|
11
|
+
export type ViewID = Tagged<string, 'ViewID'>
|
|
12
12
|
|
|
13
13
|
export type ViewRulePredicate =
|
|
14
14
|
| {
|
|
@@ -30,7 +30,7 @@ export interface ViewRuleStyle {
|
|
|
30
30
|
targets: ElementExpression[]
|
|
31
31
|
notation?: string
|
|
32
32
|
style: ElementStyle & {
|
|
33
|
-
color?:
|
|
33
|
+
color?: Color
|
|
34
34
|
shape?: ElementShape
|
|
35
35
|
icon?: IconUrl
|
|
36
36
|
}
|
|
@@ -76,6 +76,8 @@ export interface BasicView<ViewType extends 'element' | 'dynamic'> {
|
|
|
76
76
|
* If the view is changed manually this field contains the layout data.
|
|
77
77
|
*/
|
|
78
78
|
readonly manualLayout?: ViewManualLayout | undefined
|
|
79
|
+
|
|
80
|
+
readonly customColorDefinitions: CustomColorDefinitions
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
export interface BasicElementView extends BasicView<'element'> {
|
|
@@ -98,13 +100,24 @@ export interface DynamicViewStep {
|
|
|
98
100
|
readonly description?: string
|
|
99
101
|
readonly technology?: string
|
|
100
102
|
readonly notation?: string
|
|
101
|
-
|
|
103
|
+
// Notes for walkthrough
|
|
104
|
+
readonly notes?: string
|
|
105
|
+
readonly color?: Color
|
|
102
106
|
readonly line?: RelationshipLineType
|
|
103
107
|
readonly head?: RelationshipArrowType
|
|
104
108
|
readonly tail?: RelationshipArrowType
|
|
105
109
|
readonly isBackward?: boolean
|
|
110
|
+
// Link to dynamic view
|
|
111
|
+
readonly navigateTo?: ViewID
|
|
112
|
+
__parallel?: never
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface DynamicViewParallelSteps {
|
|
116
|
+
readonly __parallel: DynamicViewStep[]
|
|
106
117
|
}
|
|
107
118
|
|
|
119
|
+
export type DynamicViewStepOrParallel = DynamicViewStep | DynamicViewParallelSteps
|
|
120
|
+
|
|
108
121
|
export type DynamicViewIncludeRule = {
|
|
109
122
|
include: ElementPredicateExpression[]
|
|
110
123
|
}
|
|
@@ -117,11 +130,17 @@ export type DynamicViewRule = DynamicViewIncludeRule | ViewRuleStyle | ViewRuleA
|
|
|
117
130
|
export interface DynamicView extends BasicView<'dynamic'> {
|
|
118
131
|
readonly __: 'dynamic'
|
|
119
132
|
|
|
120
|
-
readonly steps:
|
|
133
|
+
readonly steps: DynamicViewStepOrParallel[]
|
|
121
134
|
|
|
122
135
|
readonly rules: DynamicViewRule[]
|
|
123
136
|
}
|
|
124
137
|
|
|
138
|
+
export function isDynamicViewParallelSteps(step: DynamicViewStepOrParallel): step is DynamicViewParallelSteps {
|
|
139
|
+
return '__parallel' in step && isArray(step.__parallel)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export type CustomColorDefinitions = { [key: string]: ThemeColorValues }
|
|
143
|
+
|
|
125
144
|
export type LikeC4View = ElementView | DynamicView
|
|
126
145
|
|
|
127
146
|
export function isDynamicView(view: LikeC4View): view is DynamicView {
|
|
@@ -139,23 +158,34 @@ export function isScopedElementView(view: LikeC4View): view is ScopedElementView
|
|
|
139
158
|
return isElementView(view) && 'viewOf' in view
|
|
140
159
|
}
|
|
141
160
|
|
|
142
|
-
export type NodeId = Fqn
|
|
161
|
+
export type NodeId = Tagged<string, 'Fqn'>
|
|
143
162
|
|
|
144
|
-
export type EdgeId =
|
|
145
|
-
export type StepEdgeIdLiteral = `step-${number}`
|
|
146
|
-
export type StepEdgeId =
|
|
147
|
-
export function StepEdgeId(step: number): StepEdgeId {
|
|
148
|
-
|
|
163
|
+
export type EdgeId = Tagged<string, 'EdgeId'>
|
|
164
|
+
export type StepEdgeIdLiteral = `step-${number}` | `step-${number}.${number}`
|
|
165
|
+
export type StepEdgeId = Tagged<StepEdgeIdLiteral, 'EdgeId'>
|
|
166
|
+
export function StepEdgeId(step: number, parallelStep?: number): StepEdgeId {
|
|
167
|
+
const id = `step-${String(step).padStart(2, '0')}` as StepEdgeId
|
|
168
|
+
return parallelStep ? `${id}.${parallelStep}` as StepEdgeId : id
|
|
149
169
|
}
|
|
150
170
|
|
|
151
171
|
export function isStepEdgeId(id: string): id is StepEdgeId {
|
|
152
172
|
return id.startsWith('step-')
|
|
153
173
|
}
|
|
174
|
+
|
|
154
175
|
export function extractStep(id: EdgeId): number {
|
|
155
176
|
if (!isStepEdgeId(id)) {
|
|
156
177
|
throw new Error(`Invalid step edge id: ${id}`)
|
|
157
178
|
}
|
|
158
|
-
return
|
|
179
|
+
return parseFloat(id.slice('step-'.length))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Get the prefix of the parallel steps
|
|
183
|
+
// i.e. step-01.1 -> step-01.
|
|
184
|
+
export function getParallelStepsPrefix(id: string): string | null {
|
|
185
|
+
if (isStepEdgeId(id) && id.includes('.')) {
|
|
186
|
+
return id.slice(0, id.indexOf('.') + 1)
|
|
187
|
+
}
|
|
188
|
+
return null
|
|
159
189
|
}
|
|
160
190
|
|
|
161
191
|
export interface ComputedNode {
|
|
@@ -175,7 +205,7 @@ export interface ComputedNode {
|
|
|
175
205
|
/**
|
|
176
206
|
* @deprecated Use `style` instead
|
|
177
207
|
*/
|
|
178
|
-
color:
|
|
208
|
+
color: Color
|
|
179
209
|
/**
|
|
180
210
|
* @deprecated Use `style` instead
|
|
181
211
|
*/
|
|
@@ -201,11 +231,16 @@ export interface ComputedEdge {
|
|
|
201
231
|
technology?: string
|
|
202
232
|
relations: RelationID[]
|
|
203
233
|
kind?: RelationshipKind
|
|
204
|
-
|
|
234
|
+
notation?: string
|
|
235
|
+
// Notes for walkthrough
|
|
236
|
+
notes?: string
|
|
237
|
+
color?: Color
|
|
205
238
|
line?: RelationshipLineType
|
|
206
239
|
head?: RelationshipArrowType
|
|
207
240
|
tail?: RelationshipArrowType
|
|
208
241
|
tags?: NonEmptyArray<Tag>
|
|
242
|
+
// Link to dynamic view
|
|
243
|
+
navigateTo?: ViewID
|
|
209
244
|
/**
|
|
210
245
|
* If this edge is derived from custom relationship predicate
|
|
211
246
|
*/
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sort as remedaSort } from 'remeda'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { compareNatural } from './compare-natural'
|
|
4
|
+
|
|
5
|
+
describe('compareNatural', () => {
|
|
6
|
+
const sort = (array: Array<string | undefined>) => remedaSort(array, compareNatural)
|
|
7
|
+
|
|
8
|
+
it('should move undefined to the end', () => {
|
|
9
|
+
expect(
|
|
10
|
+
sort([
|
|
11
|
+
undefined,
|
|
12
|
+
'apple',
|
|
13
|
+
undefined,
|
|
14
|
+
'banana'
|
|
15
|
+
])
|
|
16
|
+
).toEqual([
|
|
17
|
+
'apple',
|
|
18
|
+
'banana',
|
|
19
|
+
undefined,
|
|
20
|
+
undefined
|
|
21
|
+
])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should sort array of strings', () => {
|
|
25
|
+
expect(sort([
|
|
26
|
+
'apple1',
|
|
27
|
+
'apple11',
|
|
28
|
+
'apple2',
|
|
29
|
+
'apple10'
|
|
30
|
+
])).toEqual([
|
|
31
|
+
'apple1',
|
|
32
|
+
'apple2',
|
|
33
|
+
'apple10',
|
|
34
|
+
'apple11'
|
|
35
|
+
])
|
|
36
|
+
})
|
|
37
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import compare from 'natural-compare-lite'
|
|
2
|
+
import { isString } from 'remeda'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function compareNatural(a: string | undefined, b: string | undefined): -1 | 0 | 1 {
|
|
6
|
+
if (a === b) return 0
|
|
7
|
+
if (isString(a)) {
|
|
8
|
+
if (isString(b)) {
|
|
9
|
+
return compare(a, b)
|
|
10
|
+
}
|
|
11
|
+
return 1
|
|
12
|
+
}
|
|
13
|
+
return isString(b) ? -1 : 0
|
|
14
|
+
}
|
package/src/utils/fqn.spec.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { prop } from 'remeda'
|
|
1
2
|
import { describe, expect, it } from 'vitest'
|
|
2
3
|
import type { Element, Fqn } from '../types'
|
|
3
4
|
import {
|
|
@@ -7,7 +8,9 @@ import {
|
|
|
7
8
|
isAncestor,
|
|
8
9
|
isDescendantOf,
|
|
9
10
|
notDescendantOf,
|
|
10
|
-
parentFqn
|
|
11
|
+
parentFqn,
|
|
12
|
+
sortByFqnHierarchically,
|
|
13
|
+
sortNaturalByFqn
|
|
11
14
|
} from './fqn'
|
|
12
15
|
|
|
13
16
|
const el = (id: string): Element => ({ id }) as unknown as Element
|
|
@@ -88,7 +91,13 @@ describe('notDescendantOf', () => {
|
|
|
88
91
|
|
|
89
92
|
describe('compareFqnHierarchically', () => {
|
|
90
93
|
it('should compare hierarchically', () => {
|
|
91
|
-
expect([
|
|
94
|
+
expect([
|
|
95
|
+
'a',
|
|
96
|
+
'b',
|
|
97
|
+
'a.b',
|
|
98
|
+
'a.b.c',
|
|
99
|
+
'a.c.c'
|
|
100
|
+
].sort(compareFqnHierarchically)).toEqual([
|
|
92
101
|
'a',
|
|
93
102
|
'b',
|
|
94
103
|
'a.b',
|
|
@@ -103,3 +112,91 @@ describe('compareFqnHierarchically', () => {
|
|
|
103
112
|
).toEqual(['aaa', 'aa', 'a', 'aaa.c', 'aa.b', 'a.c', 'a.b'])
|
|
104
113
|
})
|
|
105
114
|
})
|
|
115
|
+
|
|
116
|
+
describe('sortNaturalByFqn', () => {
|
|
117
|
+
it('should sort hierarchically', () => {
|
|
118
|
+
expect(
|
|
119
|
+
sortNaturalByFqn([
|
|
120
|
+
el('a.b'),
|
|
121
|
+
el('a'),
|
|
122
|
+
el('a.c.a.b'),
|
|
123
|
+
el('a.c.c'),
|
|
124
|
+
el('b'),
|
|
125
|
+
el('a.b.c')
|
|
126
|
+
]).map(prop('id'))
|
|
127
|
+
).toEqual([
|
|
128
|
+
'a',
|
|
129
|
+
'b',
|
|
130
|
+
'a.b',
|
|
131
|
+
'a.b.c',
|
|
132
|
+
'a.c.c',
|
|
133
|
+
'a.c.a.b'
|
|
134
|
+
])
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('should sort natural', () => {
|
|
138
|
+
expect(
|
|
139
|
+
sortNaturalByFqn([
|
|
140
|
+
el('b'),
|
|
141
|
+
el('a.c.c'),
|
|
142
|
+
el('a'),
|
|
143
|
+
el('a.b1'),
|
|
144
|
+
el('a.b2'),
|
|
145
|
+
el('a.b10'),
|
|
146
|
+
el('a.b2.c')
|
|
147
|
+
]).map(prop('id'))
|
|
148
|
+
).toEqual([
|
|
149
|
+
'a',
|
|
150
|
+
'b',
|
|
151
|
+
'a.b1',
|
|
152
|
+
'a.b2',
|
|
153
|
+
'a.b10',
|
|
154
|
+
'a.b2.c',
|
|
155
|
+
'a.c.c'
|
|
156
|
+
])
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
describe('sortByFqnHierarchically', () => {
|
|
161
|
+
it('should sort hierarchically', () => {
|
|
162
|
+
expect(
|
|
163
|
+
sortByFqnHierarchically([
|
|
164
|
+
el('a.b'),
|
|
165
|
+
el('a'),
|
|
166
|
+
el('a.b.c'),
|
|
167
|
+
el('a.c.a.b'),
|
|
168
|
+
el('a.c.c'),
|
|
169
|
+
el('b')
|
|
170
|
+
]).map(prop('id'))
|
|
171
|
+
).toEqual([
|
|
172
|
+
'a',
|
|
173
|
+
'b',
|
|
174
|
+
'a.b',
|
|
175
|
+
'a.b.c',
|
|
176
|
+
'a.c.c',
|
|
177
|
+
'a.c.a.b'
|
|
178
|
+
])
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should preserve initial order', () => {
|
|
182
|
+
expect(
|
|
183
|
+
sortByFqnHierarchically([
|
|
184
|
+
el('b'),
|
|
185
|
+
el('a.c.c'),
|
|
186
|
+
el('a'),
|
|
187
|
+
el('a.b10'),
|
|
188
|
+
el('a.b2.c'),
|
|
189
|
+
el('a.b1'),
|
|
190
|
+
el('a.b2')
|
|
191
|
+
]).map(prop('id'))
|
|
192
|
+
).toEqual([
|
|
193
|
+
'b',
|
|
194
|
+
'a',
|
|
195
|
+
'a.b10',
|
|
196
|
+
'a.b1',
|
|
197
|
+
'a.b2',
|
|
198
|
+
'a.c.c',
|
|
199
|
+
'a.b2.c'
|
|
200
|
+
])
|
|
201
|
+
})
|
|
202
|
+
})
|
package/src/utils/fqn.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Element, Fqn } from '../types'
|
|
2
|
+
import { compareNatural } from './compare-natural'
|
|
2
3
|
import { isString } from './guards'
|
|
3
4
|
|
|
4
5
|
type Predicate<T> = (x: T) => boolean
|
|
@@ -61,7 +62,7 @@ export function commonAncestor(first: Fqn, second: Fqn) {
|
|
|
61
62
|
export function parentFqn(fqn: Fqn): Fqn | null {
|
|
62
63
|
const lastDot = fqn.lastIndexOf('.')
|
|
63
64
|
if (lastDot > 0) {
|
|
64
|
-
return fqn.
|
|
65
|
+
return fqn.slice(0, lastDot) as Fqn
|
|
65
66
|
}
|
|
66
67
|
return null
|
|
67
68
|
}
|
|
@@ -81,9 +82,12 @@ export function ancestorsFqn(fqn: Fqn): Fqn[] {
|
|
|
81
82
|
if (path.length === 0) {
|
|
82
83
|
return []
|
|
83
84
|
}
|
|
84
|
-
return path.reduce((acc,
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
return path.reduce((acc, part, idx) => {
|
|
86
|
+
if (idx === 0) {
|
|
87
|
+
acc.push(part as Fqn)
|
|
88
|
+
return acc
|
|
89
|
+
}
|
|
90
|
+
acc.unshift(`${acc[0]}.${part}` as Fqn)
|
|
87
91
|
return acc
|
|
88
92
|
}, [] as Fqn[])
|
|
89
93
|
}
|
|
@@ -98,7 +102,7 @@ export function ancestorsFqn(fqn: Fqn): Fqn[] {
|
|
|
98
102
|
* - Positive number if a is deeper than b.
|
|
99
103
|
* - Negative number if b is deeper than a.
|
|
100
104
|
*/
|
|
101
|
-
export function compareFqnHierarchically<T extends string = string>(a: T, b: T):
|
|
105
|
+
export function compareFqnHierarchically<T extends string = string>(a: T, b: T): -1 | 0 | 1 {
|
|
102
106
|
const depthA = a.split('.').length
|
|
103
107
|
const depthB = b.split('.').length
|
|
104
108
|
switch (true) {
|
|
@@ -117,3 +121,39 @@ export function compareFqnHierarchically<T extends string = string>(a: T, b: T):
|
|
|
117
121
|
export function compareByFqnHierarchically<T extends { id: string }>(a: T, b: T) {
|
|
118
122
|
return compareFqnHierarchically(a.id, b.id)
|
|
119
123
|
}
|
|
124
|
+
|
|
125
|
+
type IterableContainer<T = unknown> = ReadonlyArray<T> | readonly []
|
|
126
|
+
type ReorderedArray<T extends IterableContainer> = {
|
|
127
|
+
-readonly [P in keyof T]: T[number]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function sortByFqnHierarchically<T extends { id: string }, A extends IterableContainer<T>>(
|
|
131
|
+
array: A
|
|
132
|
+
): ReorderedArray<A> {
|
|
133
|
+
return array
|
|
134
|
+
.map(item => ({ item, fqn: item.id.split('.') }))
|
|
135
|
+
.sort((a, b) => {
|
|
136
|
+
return a.fqn.length - b.fqn.length
|
|
137
|
+
})
|
|
138
|
+
.map(({ item }) => item) as ReorderedArray<A>
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function sortNaturalByFqn<T extends { id: string }, A extends IterableContainer<T>>(
|
|
142
|
+
array: A
|
|
143
|
+
): ReorderedArray<A> {
|
|
144
|
+
return array
|
|
145
|
+
.map(item => ({ item, fqn: item.id.split('.') }))
|
|
146
|
+
.sort((a, b) => {
|
|
147
|
+
if (a.fqn.length !== b.fqn.length) {
|
|
148
|
+
return a.fqn.length - b.fqn.length
|
|
149
|
+
}
|
|
150
|
+
for (let i = 0; i < a.fqn.length; i++) {
|
|
151
|
+
const compare = compareNatural(a.fqn[i], b.fqn[i])
|
|
152
|
+
if (compare !== 0) {
|
|
153
|
+
return compare
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return 0
|
|
157
|
+
})
|
|
158
|
+
.map(({ item }) => item) as ReorderedArray<A>
|
|
159
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import type { Fqn, Relation, RelationID } from '../types'
|
|
3
|
-
import { compareRelations,
|
|
4
|
-
|
|
5
|
-
const { isAnyBetween, isAnyInOut, isBetween, isIncoming, isInside, isOutgoing } = Relations
|
|
3
|
+
import { compareRelations, isAnyBetween, isAnyInOut, isBetween, isIncoming, isInside, isOutgoing } from './relations'
|
|
6
4
|
|
|
7
5
|
const relations = [
|
|
8
6
|
{
|
|
@@ -172,7 +170,7 @@ describe('compareRelations', () => {
|
|
|
172
170
|
}
|
|
173
171
|
}
|
|
174
172
|
function sorted(...relations: Array<{ source: string; target: string }>) {
|
|
175
|
-
return relations.
|
|
173
|
+
return relations.toSorted(compareRelations).map(r => r.source + ' -> ' + r.target)
|
|
176
174
|
}
|
|
177
175
|
|
|
178
176
|
it('should sort by source and target', () => {
|
package/src/utils/relations.ts
CHANGED
|
@@ -22,24 +22,24 @@ export const compareRelations = <T extends { source: string; target: string }>(a
|
|
|
22
22
|
return -1
|
|
23
23
|
}
|
|
24
24
|
const compareParents = parentA && parentB ? compareFqnHierarchically(parentA, parentB) : 0
|
|
25
|
-
if (compareParents
|
|
26
|
-
|
|
27
|
-
if (compareSource !== 0) {
|
|
28
|
-
return compareSource
|
|
29
|
-
}
|
|
30
|
-
return compareFqnHierarchically(a.target, b.target)
|
|
25
|
+
if (compareParents !== 0) {
|
|
26
|
+
return compareParents
|
|
31
27
|
}
|
|
32
|
-
|
|
28
|
+
const compareSource = compareFqnHierarchically(a.source, b.source)
|
|
29
|
+
if (compareSource !== 0) {
|
|
30
|
+
return compareSource
|
|
31
|
+
}
|
|
32
|
+
return compareFqnHierarchically(a.target, b.target)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const isInside = (parent: Fqn): RelationPredicate => {
|
|
35
|
+
export const isInside = (parent: Fqn): RelationPredicate => {
|
|
36
36
|
const prefix = parent + '.'
|
|
37
37
|
return (rel: Relation) => {
|
|
38
38
|
return rel.source.startsWith(prefix) && rel.target.startsWith(prefix)
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const isBetween = (source: Fqn, target: Fqn): RelationPredicate => {
|
|
42
|
+
export const isBetween = (source: Fqn, target: Fqn): RelationPredicate => {
|
|
43
43
|
const sourcePrefix = source + '.'
|
|
44
44
|
const targetPrefix = target + '.'
|
|
45
45
|
return (rel: Relation) => {
|
|
@@ -50,12 +50,12 @@ const isBetween = (source: Fqn, target: Fqn): RelationPredicate => {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const isAnyBetween = (source: Fqn, target: Fqn): RelationPredicate => {
|
|
53
|
+
export const isAnyBetween = (source: Fqn, target: Fqn): RelationPredicate => {
|
|
54
54
|
const predicates = [isBetween(source, target), isBetween(target, source)]
|
|
55
55
|
return (rel) => predicates.some(p => p(rel))
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const isIncoming = (target: Fqn): RelationPredicate => {
|
|
58
|
+
export const isIncoming = (target: Fqn): RelationPredicate => {
|
|
59
59
|
const targetPrefix = target + '.'
|
|
60
60
|
return (rel: Relation) => {
|
|
61
61
|
return (
|
|
@@ -65,7 +65,7 @@ const isIncoming = (target: Fqn): RelationPredicate => {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const isOutgoing = (source: Fqn): RelationPredicate => {
|
|
68
|
+
export const isOutgoing = (source: Fqn): RelationPredicate => {
|
|
69
69
|
const sourcePrefix = source + '.'
|
|
70
70
|
return (rel: Relation) => {
|
|
71
71
|
return (
|
|
@@ -75,14 +75,14 @@ const isOutgoing = (source: Fqn): RelationPredicate => {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
const isAnyInOut = (source: Fqn): RelationPredicate => {
|
|
78
|
+
export const isAnyInOut = (source: Fqn): RelationPredicate => {
|
|
79
79
|
const predicates = [isIncoming(source), isOutgoing(source)]
|
|
80
80
|
return (rel: Relation) => {
|
|
81
81
|
return predicates.some(p => p(rel))
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
const hasRelation = <R extends { source: Fqn; target: Fqn }>(rel: R) => {
|
|
85
|
+
export const hasRelation = <R extends { source: Fqn; target: Fqn }>(rel: R) => {
|
|
86
86
|
return <E extends { id: Fqn }>(element: E) => {
|
|
87
87
|
return (
|
|
88
88
|
rel.source === element.id
|
|
@@ -92,13 +92,3 @@ const hasRelation = <R extends { source: Fqn; target: Fqn }>(rel: R) => {
|
|
|
92
92
|
)
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
export const Relations = {
|
|
97
|
-
isInside,
|
|
98
|
-
isBetween,
|
|
99
|
-
isAnyBetween,
|
|
100
|
-
isIncoming,
|
|
101
|
-
isOutgoing,
|
|
102
|
-
isAnyInOut,
|
|
103
|
-
hasRelation
|
|
104
|
-
} as const
|
package/dist/colors/element.d.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
export declare const ElementColors: {
|
|
2
|
-
readonly primary: {
|
|
3
|
-
fill: "#3b82f6";
|
|
4
|
-
stroke: "#2563eb";
|
|
5
|
-
hiContrast: "#eff6ff";
|
|
6
|
-
loContrast: "#bfdbfe";
|
|
7
|
-
};
|
|
8
|
-
readonly blue: {
|
|
9
|
-
fill: "#3b82f6";
|
|
10
|
-
stroke: "#2563eb";
|
|
11
|
-
hiContrast: "#eff6ff";
|
|
12
|
-
loContrast: "#bfdbfe";
|
|
13
|
-
};
|
|
14
|
-
readonly secondary: {
|
|
15
|
-
fill: "#0284c7";
|
|
16
|
-
stroke: "#0369a1";
|
|
17
|
-
hiContrast: "#f0f9ff";
|
|
18
|
-
loContrast: "#B6ECF7";
|
|
19
|
-
};
|
|
20
|
-
readonly sky: {
|
|
21
|
-
fill: "#0284c7";
|
|
22
|
-
stroke: "#0369a1";
|
|
23
|
-
hiContrast: "#f0f9ff";
|
|
24
|
-
loContrast: "#B6ECF7";
|
|
25
|
-
};
|
|
26
|
-
readonly muted: {
|
|
27
|
-
fill: "#64748b";
|
|
28
|
-
stroke: "#475569";
|
|
29
|
-
hiContrast: "#f8fafc";
|
|
30
|
-
loContrast: "#cbd5e1";
|
|
31
|
-
};
|
|
32
|
-
readonly slate: {
|
|
33
|
-
fill: "#64748b";
|
|
34
|
-
stroke: "#475569";
|
|
35
|
-
hiContrast: "#f8fafc";
|
|
36
|
-
loContrast: "#cbd5e1";
|
|
37
|
-
};
|
|
38
|
-
readonly gray: {
|
|
39
|
-
readonly fill: "#737373";
|
|
40
|
-
readonly stroke: "#525252";
|
|
41
|
-
readonly hiContrast: "#fafafa";
|
|
42
|
-
readonly loContrast: "#d4d4d4";
|
|
43
|
-
};
|
|
44
|
-
readonly red: {
|
|
45
|
-
readonly fill: "#AC4D39";
|
|
46
|
-
readonly stroke: "#853A2D";
|
|
47
|
-
readonly hiContrast: "#FBD3CB";
|
|
48
|
-
readonly loContrast: "#f5b2a3";
|
|
49
|
-
};
|
|
50
|
-
readonly green: {
|
|
51
|
-
readonly fill: "#428a4f";
|
|
52
|
-
readonly stroke: "#2d5d39";
|
|
53
|
-
readonly hiContrast: "#f8fafc";
|
|
54
|
-
readonly loContrast: "#c2f0c2";
|
|
55
|
-
};
|
|
56
|
-
readonly amber: {
|
|
57
|
-
readonly fill: "#A35829";
|
|
58
|
-
readonly stroke: "#7E451D";
|
|
59
|
-
readonly hiContrast: "#FFE0C2";
|
|
60
|
-
readonly loContrast: "#f9b27c";
|
|
61
|
-
};
|
|
62
|
-
readonly indigo: {
|
|
63
|
-
readonly fill: "#6366f1";
|
|
64
|
-
readonly stroke: "#4f46e5";
|
|
65
|
-
readonly hiContrast: "#eef2ff";
|
|
66
|
-
readonly loContrast: "#c7d2fe";
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
//# sourceMappingURL=element.d.ts.map
|
package/dist/colors/element.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
const blue = {
|
|
2
|
-
fill: '#3b82f6',
|
|
3
|
-
stroke: '#2563eb',
|
|
4
|
-
hiContrast: '#eff6ff',
|
|
5
|
-
loContrast: '#bfdbfe'
|
|
6
|
-
};
|
|
7
|
-
const sky = {
|
|
8
|
-
fill: '#0284c7',
|
|
9
|
-
stroke: '#0369a1',
|
|
10
|
-
hiContrast: '#f0f9ff',
|
|
11
|
-
loContrast: '#B6ECF7'
|
|
12
|
-
};
|
|
13
|
-
const slate = {
|
|
14
|
-
fill: '#64748b',
|
|
15
|
-
stroke: '#475569',
|
|
16
|
-
hiContrast: '#f8fafc',
|
|
17
|
-
loContrast: '#cbd5e1'
|
|
18
|
-
};
|
|
19
|
-
export const ElementColors = {
|
|
20
|
-
primary: blue,
|
|
21
|
-
blue,
|
|
22
|
-
secondary: sky,
|
|
23
|
-
sky,
|
|
24
|
-
muted: slate,
|
|
25
|
-
slate,
|
|
26
|
-
gray: {
|
|
27
|
-
// fill: colors.neutral[500],
|
|
28
|
-
// stroke: colors.neutral[600],
|
|
29
|
-
// hiContrast: colors.neutral[50],
|
|
30
|
-
// loContrast: colors.neutral[200],
|
|
31
|
-
fill: '#737373',
|
|
32
|
-
stroke: '#525252',
|
|
33
|
-
hiContrast: '#fafafa',
|
|
34
|
-
loContrast: '#d4d4d4'
|
|
35
|
-
},
|
|
36
|
-
red: {
|
|
37
|
-
// fill: colors.red[500],
|
|
38
|
-
// stroke: colors.red[600],
|
|
39
|
-
// hiContrast: colors.red[50],
|
|
40
|
-
// loContrast: colors.red[200],
|
|
41
|
-
fill: '#AC4D39',
|
|
42
|
-
// fill: '#b54548',
|
|
43
|
-
stroke: '#853A2D',
|
|
44
|
-
// hiContrast: '#fef2f2',
|
|
45
|
-
// loContrast: '#fecaca',
|
|
46
|
-
// hiContrast: '#191111', // colors.gray[900],
|
|
47
|
-
// loContrast: '#3b1219' // colors.gray[800],
|
|
48
|
-
hiContrast: '#FBD3CB',
|
|
49
|
-
// hiContrast: '#f8fafc',
|
|
50
|
-
// loContrast: '#fdd8d8' // radix black red 12
|
|
51
|
-
loContrast: '#f5b2a3'
|
|
52
|
-
},
|
|
53
|
-
green: {
|
|
54
|
-
fill: '#428a4f',
|
|
55
|
-
stroke: '#2d5d39',
|
|
56
|
-
hiContrast: '#f8fafc',
|
|
57
|
-
loContrast: '#c2f0c2'
|
|
58
|
-
},
|
|
59
|
-
amber: {
|
|
60
|
-
fill: '#A35829',
|
|
61
|
-
stroke: '#7E451D',
|
|
62
|
-
hiContrast: '#FFE0C2',
|
|
63
|
-
loContrast: '#f9b27c'
|
|
64
|
-
},
|
|
65
|
-
indigo: {
|
|
66
|
-
// fill: colors.indigo[500],
|
|
67
|
-
// stroke: colors.indigo[600],
|
|
68
|
-
// hiContrast: colors.indigo[50],
|
|
69
|
-
// loContrast: colors.indigo[200],
|
|
70
|
-
fill: '#6366f1',
|
|
71
|
-
stroke: '#4f46e5',
|
|
72
|
-
hiContrast: '#eef2ff',
|
|
73
|
-
loContrast: '#c7d2fe'
|
|
74
|
-
}
|
|
75
|
-
};
|