@likec4/core 1.17.1 → 1.19.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/LICENSE +1 -1
- package/dist/builder/index.d.mts +502 -0
- package/dist/builder/index.d.ts +502 -0
- package/dist/builder/index.js +871 -0
- package/dist/compute-view/index.d.mts +22 -0
- package/dist/compute-view/index.d.ts +22 -0
- package/dist/compute-view/index.js +78 -0
- package/dist/index.d.mts +10 -919
- package/dist/index.d.ts +10 -919
- package/dist/{index.mjs → index.js} +7 -1688
- package/dist/model/index.d.mts +254 -0
- package/dist/model/index.d.ts +254 -0
- package/dist/model/index.js +1 -0
- package/dist/shared/chunk-RAAYCPUM.M-JWF7SS.js +21 -0
- package/dist/shared/core.BIfgabEw.d.ts +719 -0
- package/dist/shared/core.BZ9Msq7w.d.mts +719 -0
- package/dist/shared/{core.CoLQkcYL.d.cts → core.C05RDLhi.d.ts} +460 -362
- package/dist/shared/{core.CoLQkcYL.d.mts → core.CmYMqgha.d.mts} +460 -362
- package/dist/shared/core.D6-fWbM3.d.ts +127 -0
- package/dist/shared/core.D74xkKkG.d.mts +148 -0
- package/dist/shared/core.D74xkKkG.d.ts +148 -0
- package/dist/shared/core.sLaWHBjR.d.mts +127 -0
- package/dist/shared/index.-BdzdI2C.js +7443 -0
- package/dist/shared/object_hash.CE_oF3I3.js +1222 -0
- package/dist/shared/{core.BXTs-8n7.mjs → view.CyZrG8BJ.js} +108 -47
- package/dist/types/index.d.mts +71 -1
- package/dist/types/index.d.ts +71 -1
- package/dist/types/index.js +20 -0
- package/dist/utils/index.d.mts +404 -0
- package/dist/utils/index.d.ts +404 -0
- package/dist/utils/index.js +330 -0
- package/package.json +59 -47
- package/src/builder/Builder-style1.test-d.ts +190 -0
- package/src/builder/Builder-style2.test-d.ts +175 -0
- package/src/builder/Builder.deployment.ts +224 -0
- package/src/builder/Builder.deploymentModel.ts +385 -0
- package/src/builder/Builder.element.ts +19 -19
- package/src/builder/Builder.model.ts +67 -42
- package/src/builder/Builder.ts +533 -367
- package/src/builder/Builder.view-common.ts +234 -0
- package/src/builder/Builder.view-deployment.ts +135 -0
- package/src/builder/Builder.view-element.ts +189 -0
- package/src/builder/Builder.views.ts +109 -33
- package/src/builder/Builder.with.ts +475 -0
- package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +374 -0
- package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +183 -0
- package/src/builder/__snapshots__/Builder.spec.ts.snap +374 -0
- package/src/builder/_types.ts +113 -18
- package/src/builder/index.ts +10 -4
- package/src/colors/index.ts +2 -2
- package/src/compute-view/compute-view.ts +83 -0
- package/src/compute-view/deployment-view/__test__/TestHelper.ts +236 -0
- package/src/compute-view/deployment-view/__test__/fixture.ts +198 -0
- package/src/compute-view/deployment-view/_types.ts +37 -0
- package/src/compute-view/deployment-view/clean-connections.ts +238 -0
- package/src/compute-view/deployment-view/compute.ts +125 -0
- package/src/compute-view/deployment-view/memory/index.ts +9 -0
- package/src/compute-view/deployment-view/memory/memory.ts +97 -0
- package/src/compute-view/deployment-view/predicates/elements.ts +144 -0
- package/src/compute-view/deployment-view/predicates/relation-direct.ts +237 -0
- package/src/compute-view/deployment-view/predicates/relation-in-out.ts +61 -0
- package/src/compute-view/deployment-view/predicates/relation-incoming.ts +95 -0
- package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +88 -0
- package/src/compute-view/deployment-view/predicates/wildcard.ts +29 -0
- package/src/compute-view/deployment-view/stages/stage-exclude.ts +5 -0
- package/src/compute-view/deployment-view/stages/stage-final.ts +245 -0
- package/src/compute-view/deployment-view/stages/stage-include.ts +95 -0
- package/src/compute-view/deployment-view/utils.ts +276 -0
- package/src/compute-view/dynamic-view/__test__/fixture.ts +58 -0
- package/src/compute-view/dynamic-view/compute.ts +324 -0
- package/src/compute-view/element-view/__test__/TestHelper.ts +258 -0
- package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +458 -0
- package/src/compute-view/element-view/__test__/fixture.ts +675 -0
- package/src/compute-view/element-view/_types.ts +48 -0
- package/src/compute-view/element-view/clean-connections.ts +113 -0
- package/src/compute-view/element-view/compute.ts +315 -0
- package/src/compute-view/element-view/memory/NodeGroup.ts +31 -0
- package/src/compute-view/element-view/memory/index.ts +10 -0
- package/src/compute-view/element-view/memory/memory.ts +177 -0
- package/src/compute-view/element-view/memory/stage-exclude.ts +59 -0
- package/src/compute-view/element-view/memory/stage-final.ts +129 -0
- package/src/compute-view/element-view/memory/stage-include.ts +128 -0
- package/src/compute-view/element-view/predicates/_utils.ts +86 -0
- package/src/compute-view/element-view/predicates/element-expand.ts +33 -0
- package/src/compute-view/element-view/predicates/element-kind-tag.ts +26 -0
- package/src/compute-view/element-view/predicates/element-ref.ts +37 -0
- package/src/compute-view/element-view/predicates/relation-direct.ts +309 -0
- package/src/compute-view/element-view/predicates/relation-in-out.ts +66 -0
- package/src/compute-view/element-view/predicates/relation-in.ts +118 -0
- package/src/compute-view/element-view/predicates/relation-out.ts +116 -0
- package/src/compute-view/element-view/predicates/wildcard.ts +88 -0
- package/src/compute-view/element-view/utils.ts +59 -0
- package/src/compute-view/index.ts +4 -0
- package/src/compute-view/memory/AbstractMemory.ts +45 -0
- package/src/compute-view/memory/AbstractStageExclude.ts +216 -0
- package/src/compute-view/memory/AbstractStageInclude.ts +200 -0
- package/src/compute-view/memory/_types.ts +147 -0
- package/src/compute-view/memory/index.ts +5 -0
- package/src/compute-view/memory/ops.ts +43 -0
- package/src/compute-view/utils/__snapshots__/sortNodes.spec.ts.snap +113 -0
- package/src/compute-view/utils/ancestorsOfNode.ts +19 -0
- package/src/compute-view/utils/applyCustomElementProperties.ts +66 -0
- package/src/compute-view/utils/applyCustomRelationProperties.ts +41 -0
- package/src/compute-view/utils/applyViewRuleStyles.ts +53 -0
- package/src/compute-view/utils/buildComputedNodes.ts +148 -0
- package/src/compute-view/utils/buildElementNotations.ts +63 -0
- package/src/compute-view/utils/elementExpressionToPredicate.ts +50 -0
- package/src/compute-view/utils/link-nodes-with-edges.ts +54 -0
- package/src/compute-view/utils/merge-props-from-relationships.ts +128 -0
- package/src/compute-view/utils/relationExpressionToPredicates.ts +55 -0
- package/src/compute-view/utils/resolve-extended-views.ts +69 -0
- package/src/compute-view/utils/resolve-global-rules.ts +83 -0
- package/src/compute-view/utils/topological-sort.ts +256 -0
- package/src/compute-view/utils/uniqueTags.test.ts +42 -0
- package/src/compute-view/utils/uniqueTags.ts +21 -0
- package/src/compute-view/utils/view-hash.ts +27 -0
- package/src/compute-view/utils/with-readable-edges.ts +36 -0
- package/src/index.ts +22 -12
- package/src/model/DeploymentElementModel.ts +599 -0
- package/src/model/DeploymentModel.ts +370 -0
- package/src/model/ElementModel.ts +221 -0
- package/src/model/LikeC4Model.test-d.ts +152 -0
- package/src/model/LikeC4Model.ts +336 -457
- package/src/model/RelationModel.ts +85 -0
- package/src/model/__test__/fixture.ts +149 -338
- package/src/model/connection/Connection.ts +72 -0
- package/src/model/connection/deployment/DeploymentConnectionModel.ts +178 -0
- package/src/model/connection/deployment/find.ts +120 -0
- package/src/model/connection/deployment/index.ts +8 -0
- package/src/model/connection/index.ts +22 -0
- package/src/model/connection/model/ConnectionModel.ts +157 -0
- package/src/model/connection/model/find.ts +117 -0
- package/src/model/connection/model/index.ts +8 -0
- package/src/model/connection/ops.ts +274 -0
- package/src/model/guards.ts +9 -0
- package/src/model/index.ts +46 -2
- package/src/model/types.ts +112 -15
- package/src/model/view/EdgeModel.ts +103 -0
- package/src/model/view/LikeC4ViewModel.ts +226 -0
- package/src/model/view/NodeModel.ts +229 -0
- package/src/types/_common.ts +55 -1
- package/src/types/deployments.ts +169 -0
- package/src/types/element.ts +9 -5
- package/src/types/expression-v2.ts +172 -0
- package/src/types/expression.ts +17 -11
- package/src/types/global.ts +3 -3
- package/src/types/index.ts +220 -12
- package/src/types/model.ts +75 -12
- package/src/types/operators.ts +1 -1
- package/src/types/overview-graph.ts +2 -2
- package/src/types/relation.ts +18 -14
- package/src/types/theme.ts +0 -1
- package/src/types/view-notation.ts +2 -2
- package/src/types/view.ts +117 -41
- package/src/utils/{commonHead.ts → common-head.ts} +2 -0
- package/src/utils/const.ts +1 -0
- package/src/utils/fqn.ts +203 -56
- package/src/utils/getOrCreate.ts +42 -0
- package/src/utils/graphlib.ts +10 -0
- package/src/utils/index.ts +74 -6
- package/src/utils/iterable/_types.ts +5 -0
- package/src/utils/iterable/filter.ts +55 -0
- package/src/utils/iterable/flat.ts +17 -0
- package/src/utils/iterable/index.ts +12 -0
- package/src/utils/iterable/map.ts +40 -0
- package/src/utils/iterable/reduce.ts +24 -0
- package/src/utils/iterable/some.ts +36 -0
- package/src/utils/iterable/to.ts +19 -0
- package/src/utils/iterable/unique.ts +29 -0
- package/src/utils/object-hash.ts +3 -0
- package/src/utils/relations.ts +3 -64
- package/src/utils/set.ts +78 -0
- package/src/utils/string-hash.ts +16 -0
- package/dist/index.cjs +0 -5452
- package/dist/index.d.cts +0 -1053
- package/dist/shared/core.CoLQkcYL.d.ts +0 -734
- package/dist/shared/core.DIYZvpVg.cjs +0 -353
- package/dist/types/index.cjs +0 -70
- package/dist/types/index.d.cts +0 -2
- package/dist/types/index.mjs +0 -1
- package/src/builder/Builder.test-d.ts +0 -69
- package/src/builder/Builder.view.ts +0 -358
- package/src/model/LikeC4DiagramModel.ts +0 -338
- package/src/model/LikeC4ViewModel.ts +0 -320
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { allPass, anyPass, pipe, prop, reduce } from 'remeda'
|
|
2
|
+
import { Connection, ConnectionModel } from '../../model/connection'
|
|
3
|
+
import {
|
|
4
|
+
differenceConnections,
|
|
5
|
+
findDeepestNestedConnection,
|
|
6
|
+
findDescendantConnections,
|
|
7
|
+
isAnyInOut,
|
|
8
|
+
isIncoming,
|
|
9
|
+
isOutgoing,
|
|
10
|
+
mergeConnections,
|
|
11
|
+
} from '../../model/connection/ops'
|
|
12
|
+
import type { RelationshipModel } from '../../model/RelationModel'
|
|
13
|
+
import { intersection, union } from '../../utils'
|
|
14
|
+
import { ifilter, toSet } from '../../utils/iterable'
|
|
15
|
+
|
|
16
|
+
export function allRelationshipsFrom(connections: Iterable<ConnectionModel>): Set<RelationshipModel> {
|
|
17
|
+
const relations = new Set<RelationshipModel>()
|
|
18
|
+
for (const c of connections) {
|
|
19
|
+
for (const rel of c.relations) {
|
|
20
|
+
relations.add(rel)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return relations
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isInOutToDescendant(id: string): (c: ConnectionModel) => boolean {
|
|
27
|
+
return anyPass([
|
|
28
|
+
allPass([
|
|
29
|
+
Connection.isOutgoing(id),
|
|
30
|
+
c => c.source.id !== id,
|
|
31
|
+
]),
|
|
32
|
+
allPass([
|
|
33
|
+
Connection.isIncoming(id),
|
|
34
|
+
c => c.target.id !== id,
|
|
35
|
+
]),
|
|
36
|
+
])
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function findRedundantConnections(connections: Iterable<ConnectionModel>): Array<ConnectionModel> {
|
|
40
|
+
return pipe(
|
|
41
|
+
[...connections],
|
|
42
|
+
mergeConnections,
|
|
43
|
+
reduce((reducedConnections, connection, _, all) => {
|
|
44
|
+
const descendants = findDescendantConnections(all, connection)
|
|
45
|
+
const nestedRelations = union(
|
|
46
|
+
...descendants.map(prop('relations')),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
let accum = intersection(connection.relations, nestedRelations)
|
|
50
|
+
|
|
51
|
+
if (descendants.length > 0) {
|
|
52
|
+
accum = union(accum, connection.directRelations)
|
|
53
|
+
}
|
|
54
|
+
// Check if there is any reversed connection to descendant
|
|
55
|
+
if (findDeepestNestedConnection(all, connection.reversed(false))) {
|
|
56
|
+
accum = union(accum, connection.directRelations)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (accum.size < connection.relations.size) {
|
|
60
|
+
const isSourceExpanded = all.some(isAnyInOut(connection.source))
|
|
61
|
+
const isTargetExpanded = all.some(isAnyInOut(connection.target))
|
|
62
|
+
|
|
63
|
+
// Check if source expandaded
|
|
64
|
+
if (isSourceExpanded) {
|
|
65
|
+
accum = union(
|
|
66
|
+
accum,
|
|
67
|
+
toSet(
|
|
68
|
+
ifilter(
|
|
69
|
+
connection.relations,
|
|
70
|
+
isOutgoing(connection.source),
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check if target expandaded
|
|
77
|
+
if (isTargetExpanded) {
|
|
78
|
+
accum = union(
|
|
79
|
+
accum,
|
|
80
|
+
toSet(
|
|
81
|
+
ifilter(
|
|
82
|
+
connection.relations,
|
|
83
|
+
isIncoming(connection.target),
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (accum.size > 0) {
|
|
91
|
+
reducedConnections.push(connection.update(accum))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return reducedConnections
|
|
95
|
+
}, [] as ConnectionModel[]),
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Remove relationships from connection model, that are already included in the connections between descendants.
|
|
101
|
+
* In other words - if there is same connection down the hierarchy.
|
|
102
|
+
*
|
|
103
|
+
* @returns New connection without redundant relationships
|
|
104
|
+
* Connection may be empty if all relationships are redundant, in this case it should be removed
|
|
105
|
+
*/
|
|
106
|
+
export function cleanRedundantRelationships(
|
|
107
|
+
connections: Iterable<ConnectionModel>,
|
|
108
|
+
): Array<ConnectionModel> {
|
|
109
|
+
return differenceConnections(
|
|
110
|
+
connections,
|
|
111
|
+
findRedundantConnections(connections),
|
|
112
|
+
)
|
|
113
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import DefaultMap from 'mnemonist/default-map'
|
|
2
|
+
import { filter, findLast, forEach, map, pipe } from 'remeda'
|
|
3
|
+
import { invariant, nonexhaustive, nonNullable } from '../../errors'
|
|
4
|
+
import { type ElementModel, LikeC4Model } from '../../model'
|
|
5
|
+
import { ConnectionModel } from '../../model/connection/model/ConnectionModel'
|
|
6
|
+
import type { RelationshipModel } from '../../model/RelationModel'
|
|
7
|
+
import type { AnyAux } from '../../model/types'
|
|
8
|
+
import type {
|
|
9
|
+
ComputedElementView,
|
|
10
|
+
ElementPredicateExpression,
|
|
11
|
+
ElementView,
|
|
12
|
+
NodeId,
|
|
13
|
+
RelationPredicateExpression,
|
|
14
|
+
ViewRule,
|
|
15
|
+
} from '../../types'
|
|
16
|
+
import { isViewRuleAutoLayout, isViewRuleGroup, isViewRulePredicate, whereOperatorAsPredicate } from '../../types'
|
|
17
|
+
import * as Expr from '../../types/expression'
|
|
18
|
+
import { sortNaturalByFqn, sortParentsFirst } from '../../utils'
|
|
19
|
+
import { applyCustomElementProperties } from '../utils/applyCustomElementProperties'
|
|
20
|
+
import { applyCustomRelationProperties } from '../utils/applyCustomRelationProperties'
|
|
21
|
+
import { applyViewRuleStyles } from '../utils/applyViewRuleStyles'
|
|
22
|
+
import { buildElementNotations } from '../utils/buildElementNotations'
|
|
23
|
+
import { linkNodesWithEdges } from '../utils/link-nodes-with-edges'
|
|
24
|
+
import { resolveGlobalRulesInElementView } from '../utils/resolve-global-rules'
|
|
25
|
+
import { topologicalSort } from '../utils/topological-sort'
|
|
26
|
+
import { calcViewLayoutHash } from '../utils/view-hash'
|
|
27
|
+
import { type Connection, type Elem, type PredicateCtx } from './_types'
|
|
28
|
+
import { type NodesGroup, type Stage, Memory } from './memory'
|
|
29
|
+
import { ActiveGroupMemory } from './memory/memory'
|
|
30
|
+
import { StageFinal } from './memory/stage-final'
|
|
31
|
+
import { ExpandedElementPredicate } from './predicates/element-expand'
|
|
32
|
+
import { ElementKindOrTagPredicate } from './predicates/element-kind-tag'
|
|
33
|
+
import { ElementRefPredicate } from './predicates/element-ref'
|
|
34
|
+
import { DirectRelationExprPredicate } from './predicates/relation-direct'
|
|
35
|
+
import { IncomingExprPredicate } from './predicates/relation-in'
|
|
36
|
+
import { InOutRelationPredicate } from './predicates/relation-in-out'
|
|
37
|
+
import { OutgoingExprPredicate } from './predicates/relation-out'
|
|
38
|
+
import { WildcardPredicate } from './predicates/wildcard'
|
|
39
|
+
import { buildNodes, NoFilter, NoWhere, toComputedEdges } from './utils'
|
|
40
|
+
|
|
41
|
+
function processElementPredicate(
|
|
42
|
+
// ...args:
|
|
43
|
+
// | [expr: ElementPredicateExpression, op: 'include', IncludePredicateCtx<ElementPredicateExpression>]
|
|
44
|
+
// | [expr: ElementPredicateExpression, op: 'exclude', ExcludePredicateCtx<ElementPredicateExpression>]
|
|
45
|
+
expr: ElementPredicateExpression,
|
|
46
|
+
op: 'include' | 'exclude',
|
|
47
|
+
ctx: Omit<PredicateCtx<RelationPredicateExpression>, 'expr'>,
|
|
48
|
+
): Stage {
|
|
49
|
+
switch (true) {
|
|
50
|
+
case Expr.isCustomElement(expr): {
|
|
51
|
+
if (op === 'include') {
|
|
52
|
+
return processElementPredicate(expr.custom.expr, op, ctx)
|
|
53
|
+
}
|
|
54
|
+
return ctx.stage
|
|
55
|
+
}
|
|
56
|
+
case Expr.isElementWhere(expr): {
|
|
57
|
+
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
58
|
+
const filterWhere = filter<Elem>(where)
|
|
59
|
+
return processElementPredicate(expr.where.expr, op, { ...ctx, where, filterWhere } as any)
|
|
60
|
+
}
|
|
61
|
+
case Expr.isExpandedElementExpr(expr): {
|
|
62
|
+
return ExpandedElementPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
63
|
+
}
|
|
64
|
+
case Expr.isElementRef(expr): {
|
|
65
|
+
return ElementRefPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
66
|
+
}
|
|
67
|
+
case Expr.isWildcard(expr):
|
|
68
|
+
return WildcardPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
69
|
+
case Expr.isElementKindExpr(expr):
|
|
70
|
+
case Expr.isElementTagExpr(expr):
|
|
71
|
+
return ElementKindOrTagPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
72
|
+
default:
|
|
73
|
+
nonexhaustive(expr)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function processRelationtPredicate(
|
|
77
|
+
expr: RelationPredicateExpression,
|
|
78
|
+
op: 'include' | 'exclude',
|
|
79
|
+
ctx: Omit<PredicateCtx<RelationPredicateExpression>, 'expr'>,
|
|
80
|
+
): Stage {
|
|
81
|
+
switch (true) {
|
|
82
|
+
case Expr.isCustomRelationExpr(expr): {
|
|
83
|
+
if (op === 'include') {
|
|
84
|
+
return processRelationtPredicate(expr.customRelation.relation, op, ctx)
|
|
85
|
+
}
|
|
86
|
+
return ctx.stage
|
|
87
|
+
}
|
|
88
|
+
case Expr.isRelationWhere(expr): {
|
|
89
|
+
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
90
|
+
const filterRelations = (relations: ReadonlySet<RelationshipModel>) => {
|
|
91
|
+
return new Set(filter([...relations], where))
|
|
92
|
+
}
|
|
93
|
+
const filterWhere = (connections: ReadonlyArray<Connection>) => {
|
|
94
|
+
return pipe(
|
|
95
|
+
connections,
|
|
96
|
+
map(c => new ConnectionModel(c.source, c.target, filterRelations(c.relations))),
|
|
97
|
+
filter(c => c.nonEmpty()),
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
return processRelationtPredicate(expr.where.expr, op, {
|
|
101
|
+
...ctx,
|
|
102
|
+
where,
|
|
103
|
+
filterWhere,
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
case Expr.isInOut(expr): {
|
|
107
|
+
return InOutRelationPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
108
|
+
}
|
|
109
|
+
case Expr.isRelation(expr): {
|
|
110
|
+
return DirectRelationExprPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
111
|
+
}
|
|
112
|
+
case Expr.isOutgoing(expr): {
|
|
113
|
+
return OutgoingExprPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
114
|
+
}
|
|
115
|
+
case Expr.isIncoming(expr): {
|
|
116
|
+
return IncomingExprPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
117
|
+
}
|
|
118
|
+
default:
|
|
119
|
+
nonexhaustive(expr)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function processPredicates<M extends AnyAux>(
|
|
124
|
+
model: LikeC4Model<M>,
|
|
125
|
+
memory: Memory,
|
|
126
|
+
rules: ViewRule[],
|
|
127
|
+
): Memory {
|
|
128
|
+
const ctx = {
|
|
129
|
+
model,
|
|
130
|
+
scope: memory.scope,
|
|
131
|
+
where: NoWhere,
|
|
132
|
+
filterWhere: NoFilter,
|
|
133
|
+
}
|
|
134
|
+
for (const rule of rules) {
|
|
135
|
+
if (isViewRuleGroup(rule)) {
|
|
136
|
+
const groupMemory = ActiveGroupMemory.enter(memory, rule)
|
|
137
|
+
memory = processPredicates(model, groupMemory, rule.groupRules)
|
|
138
|
+
invariant(memory instanceof ActiveGroupMemory, 'processPredicates must return ActiveGroupMemory')
|
|
139
|
+
memory = memory.leave()
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
if (isViewRulePredicate(rule)) {
|
|
143
|
+
const op = 'include' in rule ? 'include' : 'exclude'
|
|
144
|
+
const exprs = rule.include ?? rule.exclude
|
|
145
|
+
for (const expr of exprs) {
|
|
146
|
+
let stage = op === 'include' ? memory.stageInclude(expr) : memory.stageExclude(expr)
|
|
147
|
+
switch (true) {
|
|
148
|
+
case Expr.isElementPredicateExpr(expr):
|
|
149
|
+
stage = processElementPredicate(expr, op, {
|
|
150
|
+
...ctx,
|
|
151
|
+
stage,
|
|
152
|
+
memory,
|
|
153
|
+
} as any) ?? stage
|
|
154
|
+
break
|
|
155
|
+
case Expr.isRelationPredicateExpr(expr):
|
|
156
|
+
stage = processRelationtPredicate(expr, op, {
|
|
157
|
+
...ctx,
|
|
158
|
+
stage,
|
|
159
|
+
memory,
|
|
160
|
+
}) ?? stage
|
|
161
|
+
break
|
|
162
|
+
default:
|
|
163
|
+
nonexhaustive(expr)
|
|
164
|
+
}
|
|
165
|
+
memory = stage.commit()
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return StageFinal.for(memory).commit()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function computeElementView<M extends AnyAux>(
|
|
173
|
+
likec4model: LikeC4Model<M>,
|
|
174
|
+
{
|
|
175
|
+
docUri: _docUri, // exclude docUri
|
|
176
|
+
rules, // exclude rules
|
|
177
|
+
...view
|
|
178
|
+
}: ElementView,
|
|
179
|
+
): ComputedElementView<M['ViewId']> {
|
|
180
|
+
rules = resolveGlobalRulesInElementView(rules, likec4model.globals())
|
|
181
|
+
const scope = view.viewOf ? likec4model.element(view.viewOf) : null
|
|
182
|
+
let memory = processPredicates<M>(
|
|
183
|
+
likec4model,
|
|
184
|
+
Memory.empty(scope),
|
|
185
|
+
rules,
|
|
186
|
+
)
|
|
187
|
+
if (memory.isEmpty() && scope) {
|
|
188
|
+
memory = memory.update({
|
|
189
|
+
final: new Set([scope]),
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
memory = assignElementsToGroups(memory)
|
|
193
|
+
|
|
194
|
+
const nodesMap = buildNodes(memory)
|
|
195
|
+
|
|
196
|
+
const computedEdges = toComputedEdges(memory.connections)
|
|
197
|
+
|
|
198
|
+
linkNodesWithEdges(nodesMap, computedEdges)
|
|
199
|
+
|
|
200
|
+
const sorted = topologicalSort({
|
|
201
|
+
nodes: nodesMap,
|
|
202
|
+
edges: computedEdges,
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
const nodes = applyCustomElementProperties(
|
|
206
|
+
rules,
|
|
207
|
+
applyViewRuleStyles(
|
|
208
|
+
rules,
|
|
209
|
+
sorted.nodes,
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
const autoLayoutRule = findLast(rules, isViewRuleAutoLayout)
|
|
214
|
+
|
|
215
|
+
const elementNotations = buildElementNotations(nodes)
|
|
216
|
+
|
|
217
|
+
return calcViewLayoutHash({
|
|
218
|
+
...view,
|
|
219
|
+
autoLayout: {
|
|
220
|
+
direction: autoLayoutRule?.direction ?? 'TB',
|
|
221
|
+
...(autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep }),
|
|
222
|
+
...(autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep }),
|
|
223
|
+
},
|
|
224
|
+
edges: applyCustomRelationProperties(rules, nodes, sorted.edges),
|
|
225
|
+
nodes: map(nodes, n => {
|
|
226
|
+
// omit notation
|
|
227
|
+
delete n.notation
|
|
228
|
+
if (n.icon === 'none') {
|
|
229
|
+
delete n.icon
|
|
230
|
+
}
|
|
231
|
+
return n
|
|
232
|
+
}),
|
|
233
|
+
...(elementNotations.length > 0 && {
|
|
234
|
+
notation: {
|
|
235
|
+
elements: elementNotations,
|
|
236
|
+
},
|
|
237
|
+
}),
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Clean up group.explicits and group.implicits
|
|
243
|
+
*/
|
|
244
|
+
function assignElementsToGroups(memory: Memory) {
|
|
245
|
+
if (memory.groups.length === 0) {
|
|
246
|
+
return memory
|
|
247
|
+
}
|
|
248
|
+
const groupAssignments = new DefaultMap<NodeId, Set<Elem>>(() => new Set())
|
|
249
|
+
|
|
250
|
+
const assignedTo = new Map<ElementModel, NodesGroup['id']>()
|
|
251
|
+
|
|
252
|
+
const isAncestorAssigned = (el: Elem) => {
|
|
253
|
+
for (const parent of el.ancestors()) {
|
|
254
|
+
const groupId = assignedTo.get(parent)
|
|
255
|
+
if (groupId) {
|
|
256
|
+
assignedTo.set(el, groupId)
|
|
257
|
+
groupAssignments.get(groupId).add(el)
|
|
258
|
+
return true
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return false
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const isDescendantAssigned = (el: Elem) => {
|
|
265
|
+
for (const descendant of el.descendants('asc')) {
|
|
266
|
+
const groupId = assignedTo.get(descendant)
|
|
267
|
+
if (groupId) {
|
|
268
|
+
assignedTo.set(el, groupId)
|
|
269
|
+
groupAssignments.get(groupId).add(el)
|
|
270
|
+
return true
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return false
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
pipe(
|
|
277
|
+
sortParentsFirst([...memory.explicitFirstSeenIn.keys()]),
|
|
278
|
+
forEach((el) => {
|
|
279
|
+
if (!isAncestorAssigned(el)) {
|
|
280
|
+
const groupId = nonNullable(memory.explicitFirstSeenIn.get(el))
|
|
281
|
+
assignedTo.set(el, groupId)
|
|
282
|
+
groupAssignments.get(groupId).add(el)
|
|
283
|
+
}
|
|
284
|
+
}),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
pipe(
|
|
288
|
+
sortParentsFirst([...memory.lastSeenIn.keys()]),
|
|
289
|
+
filter(el => !assignedTo.has(el)),
|
|
290
|
+
forEach((el) => {
|
|
291
|
+
if (isAncestorAssigned(el)) {
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
if (isDescendantAssigned(el)) {
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
const groupId = nonNullable(memory.lastSeenIn.get(el))
|
|
298
|
+
assignedTo.set(el, groupId)
|
|
299
|
+
groupAssignments.get(groupId).add(el)
|
|
300
|
+
}),
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
if (groupAssignments.size === 0) {
|
|
304
|
+
return memory
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let groups = memory.groups.map(group => {
|
|
308
|
+
const explicits = groupAssignments.get(group.id)
|
|
309
|
+
if (!explicits) {
|
|
310
|
+
return group
|
|
311
|
+
}
|
|
312
|
+
return group.update(explicits)
|
|
313
|
+
})
|
|
314
|
+
return memory.update({ groups })
|
|
315
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ElementKind, type NodeId, type ViewRuleGroup } from '../../../types'
|
|
2
|
+
import type { Elem } from '../_types'
|
|
3
|
+
|
|
4
|
+
export class NodesGroup {
|
|
5
|
+
static readonly kind = ElementKind.Group
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
public readonly id: NodeId,
|
|
9
|
+
public readonly viewRule: ViewRuleGroup,
|
|
10
|
+
public readonly parent: NodeId | null = null,
|
|
11
|
+
public readonly elements: ReadonlySet<Elem> = new Set<Elem>()
|
|
12
|
+
) {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
isEmpty() {
|
|
16
|
+
return this.elements.size === 0
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
update(elements: ReadonlySet<Elem>): NodesGroup {
|
|
20
|
+
return new NodesGroup(
|
|
21
|
+
this.id,
|
|
22
|
+
this.viewRule,
|
|
23
|
+
this.parent,
|
|
24
|
+
elements
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
clone() {
|
|
29
|
+
return new NodesGroup(this.id, this.viewRule, this.parent, new Set(this.elements))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StageExclude } from './stage-exclude'
|
|
2
|
+
import type { StageInclude } from './stage-include'
|
|
3
|
+
|
|
4
|
+
export type { Ctx } from './memory'
|
|
5
|
+
export { Memory } from './memory'
|
|
6
|
+
export { NodesGroup } from './NodeGroup'
|
|
7
|
+
export { StageExclude } from './stage-exclude'
|
|
8
|
+
export { StageInclude } from './stage-include'
|
|
9
|
+
|
|
10
|
+
export type Stage = StageInclude | StageExclude
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import Stack from 'mnemonist/stack'
|
|
2
|
+
import { nonNullable } from '../../../errors'
|
|
3
|
+
import type { ConnectionModel } from '../../../model/connection'
|
|
4
|
+
import type { ElementModel } from '../../../model/ElementModel'
|
|
5
|
+
import type { Expression, NodeId, ViewRuleGroup } from '../../../types'
|
|
6
|
+
import { AbstractMemory, type ComputeCtx, type CtxElement, type MutableState, type StageExpression } from '../../memory'
|
|
7
|
+
import { NodesGroup } from './NodeGroup'
|
|
8
|
+
import { ActiveGroupStageExclude, StageExclude } from './stage-exclude'
|
|
9
|
+
import { ActiveGroupStageInclude, StageInclude } from './stage-include'
|
|
10
|
+
|
|
11
|
+
export interface Ctx extends
|
|
12
|
+
ComputeCtx<
|
|
13
|
+
ElementModel,
|
|
14
|
+
ConnectionModel,
|
|
15
|
+
Memory<Ctx>,
|
|
16
|
+
StageInclude<Ctx>,
|
|
17
|
+
StageExclude,
|
|
18
|
+
Expression
|
|
19
|
+
>
|
|
20
|
+
{
|
|
21
|
+
MutableState: {
|
|
22
|
+
elements: Set<ElementModel>
|
|
23
|
+
explicits: Set<ElementModel>
|
|
24
|
+
final: Set<ElementModel>
|
|
25
|
+
connections: ConnectionModel[]
|
|
26
|
+
groups: NodesGroup[]
|
|
27
|
+
/*
|
|
28
|
+
* The group where explict element was added first time
|
|
29
|
+
* May be a root group
|
|
30
|
+
*/
|
|
31
|
+
explicitFirstSeenIn: Map<ElementModel, NodesGroup['id']>
|
|
32
|
+
/**
|
|
33
|
+
* The group where element (implicit or explicit) was last seen (added or updated)
|
|
34
|
+
* Exclude root group
|
|
35
|
+
*/
|
|
36
|
+
lastSeenIn: Map<ElementModel, NodesGroup['id']>
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class Memory<C extends Ctx = Ctx> extends AbstractMemory<C> {
|
|
41
|
+
static empty(scope: CtxElement<Ctx> | null): Memory {
|
|
42
|
+
return new Memory({
|
|
43
|
+
elements: new Set(),
|
|
44
|
+
explicits: new Set(),
|
|
45
|
+
final: new Set(),
|
|
46
|
+
connections: [],
|
|
47
|
+
groups: [],
|
|
48
|
+
explicitFirstSeenIn: new Map(),
|
|
49
|
+
lastSeenIn: new Map(),
|
|
50
|
+
}, scope)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected constructor(
|
|
54
|
+
protected override state: MutableState<C>,
|
|
55
|
+
public readonly scope: CtxElement<C> | null,
|
|
56
|
+
) {
|
|
57
|
+
super(state)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public get groups(): ReadonlyArray<NodesGroup> {
|
|
61
|
+
return this.state.groups
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public get explicitFirstSeenIn(): ReadonlyMap<ElementModel, NodesGroup['id']> {
|
|
65
|
+
return this.state.explicitFirstSeenIn
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public get lastSeenIn(): ReadonlyMap<ElementModel, NodesGroup['id']> {
|
|
69
|
+
return this.state.lastSeenIn
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override stageInclude(expr: StageExpression<Ctx>): StageInclude {
|
|
73
|
+
return new StageInclude(this, expr)
|
|
74
|
+
}
|
|
75
|
+
override stageExclude(expr: StageExpression<Ctx>): StageExclude {
|
|
76
|
+
return new StageExclude(this, expr)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
override mutableState(): MutableState<C> {
|
|
80
|
+
return ({
|
|
81
|
+
elements: new Set(this.state.elements),
|
|
82
|
+
explicits: new Set(this.state.explicits),
|
|
83
|
+
final: new Set(this.state.final),
|
|
84
|
+
connections: [...this.state.connections],
|
|
85
|
+
groups: this.state.groups.map((g) => g.clone()),
|
|
86
|
+
explicitFirstSeenIn: new Map(this.state.explicitFirstSeenIn),
|
|
87
|
+
lastSeenIn: new Map(this.state.lastSeenIn),
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
override update(newstate: Partial<C['MutableState']>): Memory {
|
|
92
|
+
return new Memory({
|
|
93
|
+
...this.state,
|
|
94
|
+
...newstate,
|
|
95
|
+
}, this.scope)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
type ActiveGroupState = Ctx['MutableState'] & {
|
|
100
|
+
// activeGroup: NodesGroup
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ActiveGroupCtx extends Ctx {
|
|
104
|
+
MutableState: ActiveGroupState
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export class ActiveGroupMemory extends Memory<ActiveGroupCtx> {
|
|
108
|
+
static enter(
|
|
109
|
+
memory: Memory,
|
|
110
|
+
rule: ViewRuleGroup,
|
|
111
|
+
): ActiveGroupMemory {
|
|
112
|
+
const groupId = `@gr${memory.groups.length + 1}` as NodeId
|
|
113
|
+
if (memory instanceof ActiveGroupMemory) {
|
|
114
|
+
const stack = Stack.from(memory.stack)
|
|
115
|
+
const state = memory.mutableState()
|
|
116
|
+
state.groups.push(new NodesGroup(groupId, rule, memory.activeGroupId))
|
|
117
|
+
stack.push(groupId)
|
|
118
|
+
return new ActiveGroupMemory(state, memory.scope, stack)
|
|
119
|
+
}
|
|
120
|
+
const state = memory.mutableState() as ActiveGroupState
|
|
121
|
+
state.groups.push(new NodesGroup(groupId, rule, null))
|
|
122
|
+
const stack = Stack.of(groupId)
|
|
123
|
+
return new ActiveGroupMemory(state, memory.scope, stack)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
protected constructor(
|
|
127
|
+
protected override state: ActiveGroupState,
|
|
128
|
+
public override readonly scope: CtxElement<ActiveGroupCtx> | null,
|
|
129
|
+
// Stack of group ids
|
|
130
|
+
protected stack: Stack<NodeId>,
|
|
131
|
+
) {
|
|
132
|
+
super(state, scope)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
get activeGroupId(): NodeId {
|
|
136
|
+
return nonNullable(this.stack.peek(), 'Stack must not be empty')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
override mutableState(): ActiveGroupState {
|
|
140
|
+
const state = super.mutableState()
|
|
141
|
+
return ({
|
|
142
|
+
...state,
|
|
143
|
+
// activeGroup: this.findActiveGroup(state.groups)
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// private findActiveGroup(groups: NodesGroup[]): NodesGroup {
|
|
148
|
+
// return nonNullable(groups.find(g => g.id === this.activeGroup.id), 'Active group not found in groups')
|
|
149
|
+
// }
|
|
150
|
+
|
|
151
|
+
override update(newstate: Partial<ActiveGroupState>): ActiveGroupMemory {
|
|
152
|
+
const nextstate = {
|
|
153
|
+
...this.state,
|
|
154
|
+
...newstate,
|
|
155
|
+
}
|
|
156
|
+
// const activeGroup = this.findActiveGroup(nextstate.groups)
|
|
157
|
+
// invariant(Object.is(activeGroup, nextstate.activeGroup), 'Active group must strictly equal to the group in groups')
|
|
158
|
+
return new ActiveGroupMemory(nextstate, this.scope, this.stack)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
override stageInclude(expr: StageExpression<Ctx>): ActiveGroupStageInclude {
|
|
162
|
+
return new ActiveGroupStageInclude(this, expr)
|
|
163
|
+
}
|
|
164
|
+
override stageExclude(expr: StageExpression<Ctx>): ActiveGroupStageExclude {
|
|
165
|
+
return new ActiveGroupStageExclude(this, expr)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public leave(): Memory {
|
|
169
|
+
const state = this.mutableState()
|
|
170
|
+
this.stack.pop()
|
|
171
|
+
const prevgroup = this.stack.peek()
|
|
172
|
+
if (prevgroup) {
|
|
173
|
+
return new ActiveGroupMemory(state, this.scope, this.stack)
|
|
174
|
+
}
|
|
175
|
+
return new Memory(state, this.scope)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { filter, forEach, pipe } from 'remeda'
|
|
2
|
+
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
3
|
+
import { difference, hasIntersection, intersection } from '../../../utils/set'
|
|
4
|
+
import { AbstractStageExclude, type MutableState, type StageExpression } from '../../memory'
|
|
5
|
+
import { type ActiveGroupCtx, type ActiveGroupMemory, type Ctx } from './memory'
|
|
6
|
+
|
|
7
|
+
export class StageExclude<C extends Ctx = Ctx> extends AbstractStageExclude<C> {
|
|
8
|
+
public excludeRelations(excluded: ReadonlySet<RelationshipModel<any>>): this {
|
|
9
|
+
pipe(
|
|
10
|
+
this.memory.connections,
|
|
11
|
+
filter(c => hasIntersection(c.relations, excluded)),
|
|
12
|
+
forEach(c => {
|
|
13
|
+
this.excludeConnections(
|
|
14
|
+
c.update(intersection(c.relations, excluded)),
|
|
15
|
+
)
|
|
16
|
+
}),
|
|
17
|
+
)
|
|
18
|
+
return this
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Precommit hook
|
|
23
|
+
*/
|
|
24
|
+
protected override precommit(state: MutableState<C>): MutableState<C> {
|
|
25
|
+
if (this.excluded.elements.size > 0) {
|
|
26
|
+
const excludeRelationships = new Set(
|
|
27
|
+
[...this.excluded.elements].flatMap(el => [
|
|
28
|
+
...el.incoming('direct'),
|
|
29
|
+
...el.outgoing('direct'),
|
|
30
|
+
]),
|
|
31
|
+
)
|
|
32
|
+
this.excludeRelations(excludeRelationships)
|
|
33
|
+
}
|
|
34
|
+
return state
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected override postcommit(state: C['MutableState']) {
|
|
38
|
+
const leftExplicits = difference(this.memory.explicits, state.explicits)
|
|
39
|
+
for (const explicit of leftExplicits) {
|
|
40
|
+
state.explicitFirstSeenIn.delete(explicit)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Left elements
|
|
44
|
+
const left = difference(this.memory.elements, state.elements)
|
|
45
|
+
for (const el of left) {
|
|
46
|
+
state.lastSeenIn.delete(el)
|
|
47
|
+
}
|
|
48
|
+
return state
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class ActiveGroupStageExclude extends StageExclude {
|
|
53
|
+
constructor(
|
|
54
|
+
public override readonly memory: ActiveGroupMemory,
|
|
55
|
+
public override readonly expression: StageExpression<ActiveGroupCtx>,
|
|
56
|
+
) {
|
|
57
|
+
super(memory, expression)
|
|
58
|
+
}
|
|
59
|
+
}
|