@likec4/core 1.27.3 → 1.28.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.
- package/dist/builder/index.d.mts +6 -6
- package/dist/builder/index.mjs +23 -13
- package/dist/compute-view/index.d.mts +3 -3
- package/dist/compute-view/index.mjs +3 -3
- package/dist/compute-view/relationships-view/index.d.mts +3 -3
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +2 -2
- package/dist/model/index.d.mts +6 -6
- package/dist/model/index.mjs +1 -1
- package/dist/shared/{core.BpuZeqlX.d.mts → core.CZR9J1Qx.d.mts} +2 -2
- package/dist/shared/{core.Djro-kuQ.d.mts → core.CiFKryGW.d.mts} +7 -2
- package/dist/shared/{core.HKn0jlo_.d.mts → core.D2830qgg.d.mts} +1 -1
- package/dist/shared/{core.CFDwncxs.d.mts → core.DJvmfnEi.d.mts} +481 -186
- package/dist/shared/{core.BMep80lJ.mjs → core.DgfwjBtu.mjs} +198 -87
- package/dist/shared/core.SjLhMA7a.d.mts +62 -0
- package/dist/shared/{core.RjpTh3vE.mjs → core.w6BcP9z5.mjs} +263 -168
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/utils/index.d.mts +2 -2
- package/package.json +7 -7
- package/src/builder/Builder.ts +1 -0
- package/src/builder/Builder.view-common.ts +10 -10
- package/src/builder/Builder.view-element.ts +26 -14
- package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +23 -8
- package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +9 -4
- package/src/compute-view/deployment-view/predicates/deploymentRefs-where.ts +18 -0
- package/src/compute-view/deployment-view/predicates/relation-direct.ts +6 -0
- package/src/compute-view/deployment-view/predicates/relation-in-out.ts +3 -0
- package/src/compute-view/deployment-view/predicates/relation-incoming.ts +3 -0
- package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +3 -0
- package/src/compute-view/deployment-view/predicates/utils.ts +10 -3
- package/src/compute-view/deployment-view/utils.ts +7 -4
- package/src/compute-view/element-view/__test__/fixture.ts +72 -65
- package/src/compute-view/element-view/_types.ts +13 -9
- package/src/compute-view/element-view/compute.ts +27 -27
- package/src/compute-view/element-view/memory/NodeGroup.ts +4 -3
- package/src/compute-view/element-view/memory/memory.ts +3 -2
- package/src/compute-view/element-view/memory/stage-include.ts +2 -2
- package/src/compute-view/element-view/predicates/_utils.ts +25 -24
- package/src/compute-view/element-view/predicates/element-expand.ts +7 -10
- package/src/compute-view/element-view/predicates/element-kind-tag.ts +5 -3
- package/src/compute-view/element-view/predicates/element-ref.ts +7 -19
- package/src/compute-view/element-view/predicates/relation-direct.ts +15 -13
- package/src/compute-view/element-view/predicates/relation-in-out.ts +5 -6
- package/src/compute-view/element-view/predicates/relation-in.ts +36 -32
- package/src/compute-view/element-view/predicates/relation-out.ts +37 -33
- package/src/compute-view/element-view/predicates/wildcard.ts +2 -2
- package/src/compute-view/utils/applyCustomElementProperties.ts +9 -4
- package/src/compute-view/utils/applyCustomRelationProperties.ts +10 -7
- package/src/compute-view/utils/elementExpressionToPredicate.ts +27 -26
- package/src/compute-view/utils/link-nodes-with-edges.ts +1 -1
- package/src/compute-view/utils/relationExpressionToPredicates.ts +10 -17
- package/src/compute-view/utils/uniqueTags.ts +3 -3
- package/src/index.ts +0 -2
- package/src/model/ElementModel.ts +25 -6
- package/src/model/LikeC4Model.ts +47 -6
- package/src/model/RelationModel.ts +4 -2
- package/src/model/view/LikeC4ViewModel.ts +3 -2
- package/src/types/_common.ts +0 -4
- package/src/types/deployments.ts +3 -2
- package/src/types/element.ts +2 -10
- package/src/types/expression-v2-model.ts +314 -0
- package/src/types/expression-v2.ts +238 -30
- package/src/types/index.ts +15 -46
- package/src/types/model-data.ts +4 -1
- package/src/types/relation.ts +2 -1
- package/src/types/scalars.ts +39 -0
- package/src/types/view-changes.ts +2 -1
- package/src/types/view.ts +7 -8
- package/dist/shared/core.30yYKXcZ.d.mts +0 -186
- package/src/types/expression.ts +0 -207
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import { pick } from 'remeda'
|
|
2
1
|
import { nonexhaustive } from '../../errors'
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
type RelationExpression,
|
|
6
|
-
type RelationWhereExpr,
|
|
7
|
-
isIncoming,
|
|
8
|
-
isInOut,
|
|
9
|
-
isOutgoing,
|
|
10
|
-
isRelation,
|
|
11
|
-
isRelationWhere,
|
|
12
|
-
} from '../../types/expression'
|
|
2
|
+
import { ModelLayer } from '../../types'
|
|
13
3
|
import { whereOperatorAsPredicate } from '../../types/operators'
|
|
14
4
|
import type { ModelRelation } from '../../types/relation'
|
|
15
5
|
import type { ComputedNode } from '../../types/view'
|
|
@@ -22,10 +12,13 @@ export type FilterableEdge = Pick<ModelRelation, 'kind' | 'tags'> & {
|
|
|
22
12
|
}
|
|
23
13
|
|
|
24
14
|
export function relationExpressionToPredicates<T extends FilterableEdge>(
|
|
25
|
-
expr:
|
|
15
|
+
expr: ModelLayer.AnyRelationExpr,
|
|
26
16
|
): Predicate<T> {
|
|
27
17
|
switch (true) {
|
|
28
|
-
case
|
|
18
|
+
case ModelLayer.RelationExpr.isCustom(expr): {
|
|
19
|
+
return relationExpressionToPredicates(expr.customRelation.expr)
|
|
20
|
+
}
|
|
21
|
+
case ModelLayer.RelationExpr.isWhere(expr): {
|
|
29
22
|
const predicate = relationExpressionToPredicates(expr.where.expr)
|
|
30
23
|
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
31
24
|
return e =>
|
|
@@ -36,7 +29,7 @@ export function relationExpressionToPredicates<T extends FilterableEdge>(
|
|
|
36
29
|
...(e.kind && { kind: e.kind }),
|
|
37
30
|
})
|
|
38
31
|
}
|
|
39
|
-
case
|
|
32
|
+
case ModelLayer.RelationExpr.isDirect(expr): {
|
|
40
33
|
const isSource = elementExprToPredicate(expr.source)
|
|
41
34
|
const isTarget = elementExprToPredicate(expr.target)
|
|
42
35
|
return edge => {
|
|
@@ -44,15 +37,15 @@ export function relationExpressionToPredicates<T extends FilterableEdge>(
|
|
|
44
37
|
|| (!!expr.isBidirectional && isSource(edge.target) && isTarget(edge.source))
|
|
45
38
|
}
|
|
46
39
|
}
|
|
47
|
-
case isInOut(expr): {
|
|
40
|
+
case ModelLayer.RelationExpr.isInOut(expr): {
|
|
48
41
|
const isInOut = elementExprToPredicate(expr.inout)
|
|
49
42
|
return edge => isInOut(edge.source) || isInOut(edge.target)
|
|
50
43
|
}
|
|
51
|
-
case isIncoming(expr): {
|
|
44
|
+
case ModelLayer.RelationExpr.isIncoming(expr): {
|
|
52
45
|
const isTarget = elementExprToPredicate(expr.incoming)
|
|
53
46
|
return edge => isTarget(edge.target)
|
|
54
47
|
}
|
|
55
|
-
case isOutgoing(expr): {
|
|
48
|
+
case ModelLayer.RelationExpr.isOutgoing(expr): {
|
|
56
49
|
const isSource = elementExprToPredicate(expr.outgoing)
|
|
57
50
|
return edge => isSource(edge.source)
|
|
58
51
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { flatMap, hasAtLeast, pipe, sort, unique } from 'remeda'
|
|
2
2
|
import type { LiteralUnion } from 'type-fest'
|
|
3
3
|
import type { NonEmptyReadonlyArray } from '../../types/_common'
|
|
4
|
-
import type { Tag } from '../../types/
|
|
4
|
+
import type { Tag } from '../../types/scalars'
|
|
5
5
|
import { compareNatural } from '../../utils/compare-natural'
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -9,13 +9,13 @@ import { compareNatural } from '../../utils/compare-natural'
|
|
|
9
9
|
* and sort in natural order; returns null if no tags are present.
|
|
10
10
|
*/
|
|
11
11
|
export function uniqueTags<T extends { tags?: NonEmptyReadonlyArray<LiteralUnion<Tag, string>> | null }>(
|
|
12
|
-
elements: ReadonlyArray<T
|
|
12
|
+
elements: ReadonlyArray<T>,
|
|
13
13
|
) {
|
|
14
14
|
const tags = pipe(
|
|
15
15
|
elements,
|
|
16
16
|
flatMap(e => e.tags ?? []),
|
|
17
17
|
unique(),
|
|
18
|
-
sort(compareNatural)
|
|
18
|
+
sort(compareNatural),
|
|
19
19
|
)
|
|
20
20
|
return hasAtLeast(tags, 1) ? tags as NonEmptyReadonlyArray<Tag> : null
|
|
21
21
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { isTruthy } from 'remeda'
|
|
2
2
|
import type { SetRequired } from 'type-fest'
|
|
3
|
+
import type { IteratorLike } from '../types/_common'
|
|
3
4
|
import {
|
|
4
5
|
type Element as C4Element,
|
|
5
6
|
type ElementKind as C4ElementKind,
|
|
6
7
|
type ElementShape as C4ElementShape,
|
|
7
8
|
type ElementStyle,
|
|
8
|
-
type IconUrl,
|
|
9
|
-
type IteratorLike,
|
|
10
9
|
type Link,
|
|
11
|
-
type Tag as C4Tag,
|
|
12
|
-
type ThemeColor,
|
|
13
10
|
DefaultElementShape,
|
|
14
11
|
DefaultThemeColor,
|
|
15
|
-
} from '../types'
|
|
12
|
+
} from '../types/element'
|
|
16
13
|
import { DefaultShapeSize } from '../types/element'
|
|
14
|
+
import {
|
|
15
|
+
type IconUrl,
|
|
16
|
+
type ProjectId,
|
|
17
|
+
type Tag as C4Tag,
|
|
18
|
+
splitGlobalFqn,
|
|
19
|
+
} from '../types/scalars'
|
|
20
|
+
import type { ThemeColor } from '../types/theme'
|
|
17
21
|
import { commonAncestor, hierarchyLevel, isAncestor, sortNaturalByFqn } from '../utils'
|
|
18
22
|
import { type DeployedInstancesIterator } from './DeploymentElementModel'
|
|
19
23
|
import type { LikeC4Model } from './LikeC4Model'
|
|
@@ -28,13 +32,28 @@ export class ElementModel<M extends AnyAux = AnyAux> {
|
|
|
28
32
|
readonly _literalId: M['Element']
|
|
29
33
|
readonly hierarchyLevel: number
|
|
30
34
|
|
|
35
|
+
readonly imported: null | {
|
|
36
|
+
from: ProjectId
|
|
37
|
+
fqn: string
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
constructor(
|
|
32
41
|
public readonly $model: LikeC4Model<M>,
|
|
33
42
|
public readonly $element: C4Element,
|
|
34
43
|
) {
|
|
35
44
|
this.id = this.$element.id
|
|
36
45
|
this._literalId = this.$element.id
|
|
37
|
-
|
|
46
|
+
const [projectId, fqn] = splitGlobalFqn(this.id)
|
|
47
|
+
if (projectId) {
|
|
48
|
+
this.imported = {
|
|
49
|
+
from: projectId,
|
|
50
|
+
fqn,
|
|
51
|
+
}
|
|
52
|
+
this.hierarchyLevel = hierarchyLevel(fqn)
|
|
53
|
+
} else {
|
|
54
|
+
this.imported = null
|
|
55
|
+
this.hierarchyLevel = hierarchyLevel(this.id)
|
|
56
|
+
}
|
|
38
57
|
}
|
|
39
58
|
|
|
40
59
|
get parent(): ElementModel<M> | null {
|
package/src/model/LikeC4Model.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { mapValues, pipe, sort, values } from 'remeda'
|
|
1
|
+
import { entries, mapValues, pipe, sort, values } from 'remeda'
|
|
2
2
|
import type { LiteralUnion } from 'type-fest'
|
|
3
3
|
import { computeView, unsafeComputeView } from '../compute-view'
|
|
4
4
|
import type { ComputeViewResult } from '../compute-view/compute-view'
|
|
5
5
|
import { invariant, nonNullable } from '../errors'
|
|
6
|
-
import type {
|
|
6
|
+
import type { IteratorLike } from '../types/_common'
|
|
7
7
|
import type { Element } from '../types/element'
|
|
8
|
-
import
|
|
8
|
+
import type { ModelGlobals } from '../types/global'
|
|
9
9
|
import type { AnyParsedLikeC4ModelData, GenericLikeC4ModelData, LikeC4ModelDump } from '../types/model-data'
|
|
10
10
|
import type { ModelRelation } from '../types/relation'
|
|
11
|
+
import { type ProjectId, type Tag as C4Tag, GlobalFqn, isGlobalFqn } from '../types/scalars'
|
|
12
|
+
import type { ComputedView, DiagramView, LikeC4View } from '../types/view'
|
|
11
13
|
import { compareNatural } from '../utils'
|
|
12
|
-
import { ancestorsFqn, commonAncestor, parentFqn } from '../utils/fqn'
|
|
14
|
+
import { ancestorsFqn, commonAncestor, parentFqn, sortParentsFirst } from '../utils/fqn'
|
|
13
15
|
import { DefaultMap } from '../utils/mnemonist'
|
|
14
16
|
import type {
|
|
15
17
|
DeployedInstanceModel,
|
|
@@ -121,6 +123,14 @@ export class LikeC4Model<M extends AnyAux = LikeC4Model.Any> {
|
|
|
121
123
|
this.#allTags.get(tag).add(el)
|
|
122
124
|
}
|
|
123
125
|
}
|
|
126
|
+
for (const [projectId, elements] of entries($model.imports)) {
|
|
127
|
+
for (const element of sortParentsFirst(elements)) {
|
|
128
|
+
const el = this.addImportedElement(projectId as ProjectId, element)
|
|
129
|
+
for (const tag of el.tags) {
|
|
130
|
+
this.#allTags.get(tag).add(el)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
124
134
|
for (const relation of values($model.relations)) {
|
|
125
135
|
const el = this.addRelation(relation)
|
|
126
136
|
for (const tag of el.tags) {
|
|
@@ -365,7 +375,7 @@ export class LikeC4Model<M extends AnyAux = LikeC4Model.Any> {
|
|
|
365
375
|
if (this.#elements.has(element.id)) {
|
|
366
376
|
throw new Error(`Element ${element.id} already exists`)
|
|
367
377
|
}
|
|
368
|
-
const el = new ElementModel(this, Object.freeze(
|
|
378
|
+
const el = new ElementModel(this, Object.freeze(element))
|
|
369
379
|
this.#elements.set(el.id, el)
|
|
370
380
|
const parentId = parentFqn(el.id)
|
|
371
381
|
if (parentId) {
|
|
@@ -378,13 +388,42 @@ export class LikeC4Model<M extends AnyAux = LikeC4Model.Any> {
|
|
|
378
388
|
return el
|
|
379
389
|
}
|
|
380
390
|
|
|
391
|
+
private addImportedElement(projectId: ProjectId, element: Element) {
|
|
392
|
+
invariant(!isGlobalFqn(element.id), `Imported element already has global FQN`)
|
|
393
|
+
const id = GlobalFqn(projectId, element.id)
|
|
394
|
+
if (this.#elements.has(id)) {
|
|
395
|
+
throw new Error(`Element ${id} already exists`)
|
|
396
|
+
}
|
|
397
|
+
const el = new ElementModel(
|
|
398
|
+
this,
|
|
399
|
+
Object.freeze({
|
|
400
|
+
...element,
|
|
401
|
+
id,
|
|
402
|
+
}),
|
|
403
|
+
)
|
|
404
|
+
this.#elements.set(el.id, el)
|
|
405
|
+
let parentId = parentFqn(el.id)
|
|
406
|
+
while (parentId) {
|
|
407
|
+
// For imported elements - id has format `@projectId.fqn`
|
|
408
|
+
// We need to exclude `@projectId` from the parentId
|
|
409
|
+
if (parentId.includes('.') && this.#elements.has(parentId)) {
|
|
410
|
+
this.#parents.set(el.id, this.element(parentId))
|
|
411
|
+
this.#children.get(parentId).add(el)
|
|
412
|
+
return el
|
|
413
|
+
}
|
|
414
|
+
parentId = parentFqn(parentId)
|
|
415
|
+
}
|
|
416
|
+
this.#rootElements.add(el)
|
|
417
|
+
return el
|
|
418
|
+
}
|
|
419
|
+
|
|
381
420
|
private addRelation(relation: ModelRelation) {
|
|
382
421
|
if (this.#relations.has(relation.id)) {
|
|
383
422
|
throw new Error(`Relation ${relation.id} already exists`)
|
|
384
423
|
}
|
|
385
424
|
const rel = new RelationshipModel(
|
|
386
425
|
this,
|
|
387
|
-
Object.freeze(
|
|
426
|
+
Object.freeze(relation),
|
|
388
427
|
)
|
|
389
428
|
const { source, target } = rel
|
|
390
429
|
this.#relations.set(rel.id, rel)
|
|
@@ -418,6 +457,7 @@ export class LikeC4Model<M extends AnyAux = LikeC4Model.Any> {
|
|
|
418
457
|
|
|
419
458
|
export namespace LikeC4Model {
|
|
420
459
|
export const EMPTY = LikeC4Model.create({
|
|
460
|
+
projectId: 'default' as ProjectId,
|
|
421
461
|
specification: {
|
|
422
462
|
elements: {},
|
|
423
463
|
relationships: {},
|
|
@@ -436,6 +476,7 @@ export namespace LikeC4Model {
|
|
|
436
476
|
elements: {},
|
|
437
477
|
relations: {},
|
|
438
478
|
views: {},
|
|
479
|
+
imports: {},
|
|
439
480
|
}) as LikeC4Model<AnyAux>
|
|
440
481
|
|
|
441
482
|
export type Any = Aux<
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { isEmpty } from 'remeda'
|
|
2
|
-
import type {
|
|
3
|
-
import type { Link
|
|
2
|
+
import type { IteratorLike } from '../types/_common'
|
|
3
|
+
import type { Link } from '../types/element'
|
|
4
4
|
import {
|
|
5
5
|
type ModelRelation,
|
|
6
6
|
type RelationshipLineType,
|
|
7
7
|
DefaultLineStyle,
|
|
8
8
|
DefaultRelationshipColor,
|
|
9
9
|
} from '../types/relation'
|
|
10
|
+
import type { Tag } from '../types/scalars'
|
|
11
|
+
import type { Color } from '../types/theme'
|
|
10
12
|
import { commonAncestor } from '../utils/fqn'
|
|
11
13
|
import type { DeploymentRelationModel } from './DeploymentElementModel'
|
|
12
14
|
import type { ElementModel } from './ElementModel'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { nonNullable } from '../../errors'
|
|
2
|
-
import type { IteratorLike } from '../../types'
|
|
3
|
-
import type { Link
|
|
2
|
+
import type { IteratorLike } from '../../types/_common'
|
|
3
|
+
import type { Link } from '../../types/element'
|
|
4
|
+
import type { Tag } from '../../types/scalars'
|
|
4
5
|
import {
|
|
5
6
|
type ComputedDeploymentView,
|
|
6
7
|
type ComputedDynamicView,
|
package/src/types/_common.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import type { Simplify, Tagged, UnionToIntersection } from 'type-fest'
|
|
2
2
|
|
|
3
|
-
export type ProjectId = Tagged<string, 'ProjectID'>
|
|
4
|
-
|
|
5
3
|
export type NonEmptyArray<T> = [T, ...T[]]
|
|
6
4
|
|
|
7
5
|
export type NonEmptyReadonlyArray<T> = readonly [T, ...T[]]
|
|
8
6
|
|
|
9
|
-
export type IconUrl = Tagged<string, 'IconUrl'> | 'none'
|
|
10
|
-
|
|
11
7
|
export type CustomColor = string
|
|
12
8
|
|
|
13
9
|
export type Point = readonly [x: number, y: number]
|
package/src/types/deployments.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { MergeExclusive, Simplify, Tagged } from 'type-fest'
|
|
2
|
-
import type {
|
|
3
|
-
import type { ElementShape, ElementStyle,
|
|
2
|
+
import type { NonEmptyArray } from './_common'
|
|
3
|
+
import type { ElementShape, ElementStyle, Link } from './element'
|
|
4
4
|
import type { AbstractRelation, RelationId } from './relation'
|
|
5
|
+
import type { Fqn, IconUrl, Tag } from './scalars'
|
|
5
6
|
import type { Color } from './theme'
|
|
6
7
|
|
|
7
8
|
export type DeploymentNodeKind<Kinds extends string = string> = Tagged<Kinds, 'DeploymentNodeKind'>
|
package/src/types/element.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import type { Tagged, TupleToUnion } from 'type-fest'
|
|
2
|
-
import type {
|
|
2
|
+
import type { NonEmptyArray } from './_common'
|
|
3
|
+
import type { Fqn, IconUrl, Tag } from './scalars'
|
|
3
4
|
import type { Color, ShapeSize, SpacingSize, TextSize, ThemeColor } from './theme'
|
|
4
5
|
|
|
5
|
-
// Full-qualified-name
|
|
6
|
-
export type Fqn<Id extends string = string> = Tagged<Id, 'Fqn'>
|
|
7
|
-
|
|
8
|
-
export function AsFqn(name: string, parent?: Fqn | null) {
|
|
9
|
-
return (parent ? parent + '.' + name : name) as Fqn
|
|
10
|
-
}
|
|
11
|
-
|
|
12
6
|
export const BorderStyles = ['solid', 'dashed', 'dotted', 'none'] as const
|
|
13
7
|
|
|
14
8
|
export type BorderStyle = TupleToUnion<typeof BorderStyles>
|
|
@@ -60,8 +54,6 @@ export interface ElementStyle {
|
|
|
60
54
|
readonly textSize?: TextSize
|
|
61
55
|
}
|
|
62
56
|
|
|
63
|
-
export type Tag<Tags extends string = string> = Tagged<Tags, 'Tag'>
|
|
64
|
-
|
|
65
57
|
export interface TagSpec {
|
|
66
58
|
readonly id: Tag
|
|
67
59
|
readonly style: ElementStyle
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import type { ExclusiveUnion } from './_common'
|
|
2
|
+
import type { PredicateSelector } from './deployments'
|
|
3
|
+
import type { BorderStyle, ElementKind, ElementShape } from './element'
|
|
4
|
+
import type { WhereOperator } from './operators'
|
|
5
|
+
import type { RelationshipArrowType, RelationshipLineType } from './relation'
|
|
6
|
+
import { type Fqn, type IconUrl, type ProjectId, type Tag, GlobalFqn } from './scalars'
|
|
7
|
+
import type { Color, ShapeSize } from './theme'
|
|
8
|
+
import type { ViewId } from './view'
|
|
9
|
+
|
|
10
|
+
export namespace ModelLayer {
|
|
11
|
+
export namespace FqnRef {
|
|
12
|
+
/**
|
|
13
|
+
* Reference to logical model element
|
|
14
|
+
*/
|
|
15
|
+
export type ModelRef<F = Fqn> = {
|
|
16
|
+
model: F
|
|
17
|
+
}
|
|
18
|
+
export const isModelRef = (ref: FqnRef): ref is ModelRef => {
|
|
19
|
+
return 'model' in ref && !('project' in ref)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Reference to imported logical model element
|
|
24
|
+
*/
|
|
25
|
+
export type ImportRef<F = Fqn> = {
|
|
26
|
+
project: ProjectId
|
|
27
|
+
model: F
|
|
28
|
+
}
|
|
29
|
+
export const isImportRef = (ref: FqnRef): ref is ImportRef => {
|
|
30
|
+
return 'project' in ref && 'model' in ref
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const toFqn = (ref: FqnRef): Fqn => {
|
|
34
|
+
if (isImportRef(ref)) {
|
|
35
|
+
return GlobalFqn(ref.project, ref.model)
|
|
36
|
+
}
|
|
37
|
+
if (isModelRef(ref)) {
|
|
38
|
+
return ref.model
|
|
39
|
+
}
|
|
40
|
+
throw new Error('Expected FqnRef.ModelRef or FqnRef.ImportRef')
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type FqnRef<M = Fqn> = ExclusiveUnion<{
|
|
45
|
+
ModelRef: FqnRef.ModelRef<M>
|
|
46
|
+
ImportRef: FqnRef.ImportRef<M>
|
|
47
|
+
}>
|
|
48
|
+
|
|
49
|
+
export namespace FqnExpr {
|
|
50
|
+
export type Wildcard = {
|
|
51
|
+
wildcard: true
|
|
52
|
+
}
|
|
53
|
+
export const isWildcard = (expr: Expression): expr is FqnExpr.Wildcard => {
|
|
54
|
+
return 'wildcard' in expr && expr.wildcard === true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type ModelRef<M = Fqn> = {
|
|
58
|
+
ref: FqnRef.ModelRef<M> | FqnRef.ImportRef<M>
|
|
59
|
+
selector?: PredicateSelector
|
|
60
|
+
}
|
|
61
|
+
export const isModelRef = (ref: Expression): ref is FqnExpr.ModelRef => {
|
|
62
|
+
return 'ref' in ref
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type ElementKindExpr = {
|
|
66
|
+
elementKind: ElementKind
|
|
67
|
+
isEqual: boolean
|
|
68
|
+
}
|
|
69
|
+
export function isElementKindExpr(expr: Expression): expr is ElementKindExpr {
|
|
70
|
+
return 'elementKind' in expr && 'isEqual' in expr
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type ElementTagExpr = {
|
|
74
|
+
elementTag: Tag
|
|
75
|
+
isEqual: boolean
|
|
76
|
+
}
|
|
77
|
+
export function isElementTagExpr(expr: Expression): expr is ElementTagExpr {
|
|
78
|
+
return 'elementTag' in expr && 'isEqual' in expr
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type NonWildcard<M = Fqn> = ExclusiveUnion<{
|
|
82
|
+
ModelRef: ModelRef<M>
|
|
83
|
+
ElementKind: ElementKindExpr
|
|
84
|
+
ElementTag: ElementTagExpr
|
|
85
|
+
}>
|
|
86
|
+
|
|
87
|
+
export type Where<M = Fqn> = {
|
|
88
|
+
where: {
|
|
89
|
+
expr: ExclusiveUnion<{
|
|
90
|
+
Wildcard: Wildcard
|
|
91
|
+
ModelRef: ModelRef<M>
|
|
92
|
+
ElementKind: ElementKindExpr
|
|
93
|
+
ElementTag: ElementTagExpr
|
|
94
|
+
}>
|
|
95
|
+
condition: WhereOperator<string, string>
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const isWhere = (expr: Expression): expr is FqnExpr.Where => {
|
|
100
|
+
return 'where' in expr && is(expr.where.expr)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type Custom<M = Fqn> = {
|
|
104
|
+
custom: {
|
|
105
|
+
expr: FqnExprOrWhere<M>
|
|
106
|
+
title?: string
|
|
107
|
+
description?: string
|
|
108
|
+
technology?: string
|
|
109
|
+
notation?: string
|
|
110
|
+
shape?: ElementShape
|
|
111
|
+
color?: Color
|
|
112
|
+
icon?: IconUrl
|
|
113
|
+
border?: BorderStyle
|
|
114
|
+
opacity?: number
|
|
115
|
+
navigateTo?: ViewId
|
|
116
|
+
multiple?: boolean
|
|
117
|
+
size?: ShapeSize
|
|
118
|
+
padding?: ShapeSize
|
|
119
|
+
textSize?: ShapeSize
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const isCustom = (expr: Expression): expr is FqnExpr.Custom => {
|
|
124
|
+
return 'custom' in expr && (is(expr.custom.expr) || isWhere(expr.custom.expr))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const is = (expr: Expression): expr is FqnExpr => {
|
|
128
|
+
return isWildcard(expr)
|
|
129
|
+
|| isModelRef(expr)
|
|
130
|
+
|| isElementKindExpr(expr)
|
|
131
|
+
|| isElementTagExpr(expr)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export type FqnExpr<M = Fqn> = ExclusiveUnion<{
|
|
136
|
+
Wildcard: FqnExpr.Wildcard
|
|
137
|
+
ModelRef: FqnExpr.ModelRef<M>
|
|
138
|
+
ElementKind: FqnExpr.ElementKindExpr
|
|
139
|
+
ElementTag: FqnExpr.ElementTagExpr
|
|
140
|
+
}>
|
|
141
|
+
|
|
142
|
+
export type FqnExprOrWhere<M = Fqn> = ExclusiveUnion<{
|
|
143
|
+
Wildcard: FqnExpr.Wildcard
|
|
144
|
+
ModelRef: FqnExpr.ModelRef<M>
|
|
145
|
+
ElementKind: FqnExpr.ElementKindExpr
|
|
146
|
+
ElementTag: FqnExpr.ElementTagExpr
|
|
147
|
+
Where: FqnExpr.Where<M>
|
|
148
|
+
}>
|
|
149
|
+
|
|
150
|
+
export type AnyFqnExpr<M = Fqn> = ExclusiveUnion<{
|
|
151
|
+
Wildcard: FqnExpr.Wildcard
|
|
152
|
+
ModelRef: FqnExpr.ModelRef<M>
|
|
153
|
+
ElementKind: FqnExpr.ElementKindExpr
|
|
154
|
+
ElementTag: FqnExpr.ElementTagExpr
|
|
155
|
+
Where: FqnExpr.Where<M>
|
|
156
|
+
Custom: FqnExpr.Custom<M>
|
|
157
|
+
}>
|
|
158
|
+
|
|
159
|
+
export function isAnyFqnExpr(expr: Expression): expr is AnyFqnExpr {
|
|
160
|
+
return FqnExpr.is(expr)
|
|
161
|
+
|| FqnExpr.isWhere(expr)
|
|
162
|
+
|| FqnExpr.isCustom(expr)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export namespace RelationExpr {
|
|
166
|
+
export type Direct<M = Fqn> = {
|
|
167
|
+
source: FqnExpr<M>
|
|
168
|
+
target: FqnExpr<M>
|
|
169
|
+
isBidirectional?: boolean
|
|
170
|
+
}
|
|
171
|
+
export const isDirect = (expr: Expression): expr is RelationExpr.Direct => {
|
|
172
|
+
return 'source' in expr && 'target' in expr
|
|
173
|
+
}
|
|
174
|
+
export type Incoming<M = Fqn> = {
|
|
175
|
+
incoming: FqnExpr<M>
|
|
176
|
+
}
|
|
177
|
+
export const isIncoming = (expr: Expression): expr is RelationExpr.Incoming => {
|
|
178
|
+
return 'incoming' in expr
|
|
179
|
+
}
|
|
180
|
+
export type Outgoing<M = Fqn> = {
|
|
181
|
+
outgoing: FqnExpr<M>
|
|
182
|
+
}
|
|
183
|
+
export const isOutgoing = (expr: Expression): expr is RelationExpr.Outgoing => {
|
|
184
|
+
return 'outgoing' in expr
|
|
185
|
+
}
|
|
186
|
+
export type InOut<M = Fqn> = {
|
|
187
|
+
inout: FqnExpr<M>
|
|
188
|
+
}
|
|
189
|
+
export const isInOut = (expr: Expression): expr is RelationExpr.InOut => {
|
|
190
|
+
return 'inout' in expr
|
|
191
|
+
}
|
|
192
|
+
export type Where<M = Fqn> = {
|
|
193
|
+
where: {
|
|
194
|
+
expr: ExclusiveUnion<{
|
|
195
|
+
Direct: RelationExpr.Direct<M>
|
|
196
|
+
Incoming: RelationExpr.Incoming<M>
|
|
197
|
+
Outgoing: RelationExpr.Outgoing<M>
|
|
198
|
+
InOut: RelationExpr.InOut<M>
|
|
199
|
+
}>
|
|
200
|
+
condition: WhereOperator<string, string>
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export const isWhere = (expr: Expression): expr is RelationExpr.Where => {
|
|
204
|
+
return 'where' in expr && is(expr.where.expr)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export type Custom<M = Fqn> = {
|
|
208
|
+
customRelation: {
|
|
209
|
+
expr: RelationExprOrWhere<M>
|
|
210
|
+
title?: string
|
|
211
|
+
description?: string
|
|
212
|
+
technology?: string
|
|
213
|
+
notation?: string
|
|
214
|
+
// Link to dynamic view
|
|
215
|
+
navigateTo?: ViewId
|
|
216
|
+
// Notes for walkthrough
|
|
217
|
+
notes?: string
|
|
218
|
+
color?: Color
|
|
219
|
+
line?: RelationshipLineType
|
|
220
|
+
head?: RelationshipArrowType
|
|
221
|
+
tail?: RelationshipArrowType
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export const isCustom = (expr: Expression): expr is Custom => {
|
|
226
|
+
return 'customRelation' in expr
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export const is = (expr: Expression): expr is RelationExpr => {
|
|
230
|
+
return isDirect(expr)
|
|
231
|
+
|| isIncoming(expr)
|
|
232
|
+
|| isOutgoing(expr)
|
|
233
|
+
|| isInOut(expr)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export type RelationExpr<M = Fqn> = ExclusiveUnion<{
|
|
238
|
+
Direct: RelationExpr.Direct<M>
|
|
239
|
+
Incoming: RelationExpr.Incoming<M>
|
|
240
|
+
Outgoing: RelationExpr.Outgoing<M>
|
|
241
|
+
InOut: RelationExpr.InOut<M>
|
|
242
|
+
}>
|
|
243
|
+
|
|
244
|
+
export type RelationExprOrWhere<M = Fqn> = ExclusiveUnion<{
|
|
245
|
+
Direct: RelationExpr.Direct<M>
|
|
246
|
+
Incoming: RelationExpr.Incoming<M>
|
|
247
|
+
Outgoing: RelationExpr.Outgoing<M>
|
|
248
|
+
InOut: RelationExpr.InOut<M>
|
|
249
|
+
Where: RelationExpr.Where<M>
|
|
250
|
+
}>
|
|
251
|
+
|
|
252
|
+
export type AnyRelationExpr<M = Fqn> = ExclusiveUnion<{
|
|
253
|
+
Direct: RelationExpr.Direct<M>
|
|
254
|
+
Incoming: RelationExpr.Incoming<M>
|
|
255
|
+
Outgoing: RelationExpr.Outgoing<M>
|
|
256
|
+
InOut: RelationExpr.InOut<M>
|
|
257
|
+
Where: RelationExpr.Where<M>
|
|
258
|
+
CustomRelation: RelationExpr.Custom<M>
|
|
259
|
+
}>
|
|
260
|
+
|
|
261
|
+
export function isAnyRelationExpr(expr: Expression): expr is AnyRelationExpr {
|
|
262
|
+
return RelationExpr.is(expr)
|
|
263
|
+
|| RelationExpr.isWhere(expr)
|
|
264
|
+
|| RelationExpr.isCustom(expr)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Represents a version 2 expression which can be one of several types.
|
|
269
|
+
*
|
|
270
|
+
* @template M - The type for the model FQN, defaults to `Fqn`.
|
|
271
|
+
*/
|
|
272
|
+
export type Expression<M = Fqn> = ExclusiveUnion<{
|
|
273
|
+
Wildcard: FqnExpr.Wildcard
|
|
274
|
+
ModelRef: FqnExpr.ModelRef<M>
|
|
275
|
+
ElementKind: FqnExpr.ElementKindExpr
|
|
276
|
+
ElementTag: FqnExpr.ElementTagExpr
|
|
277
|
+
Custom: FqnExpr.Custom<M>
|
|
278
|
+
Direct: RelationExpr.Direct<M>
|
|
279
|
+
Incoming: RelationExpr.Incoming<M>
|
|
280
|
+
Outgoing: RelationExpr.Outgoing<M>
|
|
281
|
+
InOut: RelationExpr.InOut<M>
|
|
282
|
+
CustomRelation: RelationExpr.Custom<M>
|
|
283
|
+
Where: Expression.Where<M>
|
|
284
|
+
}>
|
|
285
|
+
|
|
286
|
+
export function isExpression(expr: any): expr is Expression {
|
|
287
|
+
return isAnyFqnExpr(expr)
|
|
288
|
+
|| isAnyRelationExpr(expr)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export namespace Expression {
|
|
292
|
+
export type Where<M = Fqn> = FqnExpr.Where<M> | RelationExpr.Where<M>
|
|
293
|
+
|
|
294
|
+
export const isWhere = (expr: Expression): expr is Expression.Where => {
|
|
295
|
+
return 'where' in expr
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export const isCustomFqnExpr = (expr: Expression): expr is FqnExpr.Custom => {
|
|
299
|
+
return FqnExpr.isCustom(expr)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export const isCustomRelationExpr = (expr: Expression): expr is RelationExpr.Custom => {
|
|
303
|
+
return RelationExpr.isCustom(expr)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export const isFqnExpr = (expr: Expression): expr is FqnExpr => {
|
|
307
|
+
return FqnExpr.is(expr)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export const isRelation = (expr: Expression): expr is RelationExpr => {
|
|
311
|
+
return RelationExpr.is(expr)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|