@likec4/core 1.38.1 → 1.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/builder/index.d.mts +6 -3
  2. package/dist/builder/index.mjs +55 -111
  3. package/dist/compute-view/index.d.mts +6 -3
  4. package/dist/compute-view/index.mjs +33 -34
  5. package/dist/compute-view/relationships-view/index.d.mts +6 -3
  6. package/dist/compute-view/relationships-view/index.mjs +6 -6
  7. package/dist/index.d.mts +8 -5
  8. package/dist/index.mjs +15 -24
  9. package/dist/model/connection/deployment/index.d.mts +8 -5
  10. package/dist/model/connection/deployment/index.mjs +7 -9
  11. package/dist/model/connection/index.d.mts +10 -7
  12. package/dist/model/connection/index.mjs +10 -14
  13. package/dist/model/connection/model/index.d.mts +8 -5
  14. package/dist/model/connection/model/index.mjs +7 -10
  15. package/dist/model/index.d.mts +13 -10
  16. package/dist/model/index.mjs +17 -20
  17. package/dist/shared/core.20m8Um0g.d.mts +253 -0
  18. package/dist/shared/{core.CpwrZi3D.mjs → core.4u1dVmfX.mjs} +3 -5
  19. package/dist/shared/{core.Bp-IQmJR.mjs → core.BB6zA-UU.mjs} +9 -214
  20. package/dist/shared/{core.BH0VqY-7.mjs → core.BVNaR17u.mjs} +1 -1
  21. package/dist/shared/{core.BL_xjl90.mjs → core.Be1n9a0i.mjs} +4 -5
  22. package/dist/shared/{core.BoyJPEen.mjs → core.Be4Jy9Qx.mjs} +2 -2
  23. package/dist/shared/{core.H8NbNp2X.mjs → core.BqVo8LrH.mjs} +1 -1
  24. package/dist/shared/{core.DDoAFHHp.mjs → core.BuoXfSMj.mjs} +1 -1
  25. package/dist/shared/{core.DhDo8LAz.d.mts → core.C7pZp1nR.d.mts} +3 -3
  26. package/dist/shared/{core.BH9nN-_I.mjs → core.CCD1ID81.mjs} +3 -4
  27. package/dist/shared/{core.8KM0D6va.mjs → core.CNGyqkJh.mjs} +1 -9
  28. package/dist/shared/{core.B0oEIXqd.mjs → core.CUw_2J9j.mjs} +1 -1
  29. package/dist/shared/{core.C4Cn4Jy_.mjs → core.CWv15GLo.mjs} +11 -15
  30. package/dist/shared/{core.Bf5zKDK1.d.mts → core.CXCmvCws.d.mts} +2 -2
  31. package/dist/shared/core.CaT0SUUK.mjs +201 -0
  32. package/dist/shared/{core.Dtw1SBlz.d.mts → core.ClpXZ4Ib.d.mts} +3 -3
  33. package/dist/shared/{core.DC6zzxS0.mjs → core.CsbOakly.mjs} +13 -22
  34. package/dist/shared/{core.Btl47GOn.mjs → core.DLpidoWc.mjs} +2 -2
  35. package/dist/shared/{core.wwaSalox.d.mts → core.DM3vdkO_.d.mts} +1 -1
  36. package/dist/shared/core.DXhDWKFZ.d.mts +3 -0
  37. package/dist/shared/{core.O6FG0HF9.d.mts → core.DdzRtJ9d.d.mts} +1 -1
  38. package/dist/shared/{core.OUoo2ohq.mjs → core.DgttqsJc.mjs} +5 -9
  39. package/dist/shared/{core.Dhc0yIwT.mjs → core.DlG1xWt2.mjs} +3 -43
  40. package/dist/shared/core.DnRszv-l.mjs +11982 -0
  41. package/dist/shared/core.DoM6aM9N.mjs +7 -0
  42. package/dist/shared/{core.CzHQRs5x.mjs → core.DozSPE83.mjs} +43 -60
  43. package/dist/shared/{core.DsMrjY2q.mjs → core.GTgsmXS_.mjs} +2 -2
  44. package/dist/shared/core.ILwny7BV.mjs +38 -0
  45. package/dist/shared/core.PHwHXzkG.d.mts +213 -0
  46. package/dist/shared/core.T-eLZTW_.mjs +59 -0
  47. package/dist/shared/{core.CE_hG7EJ.d.mts → core.aWJgARyY.d.mts} +10 -486
  48. package/dist/shared/{core.BvlHvikl.d.mts → core.v1iEaXaR.d.mts} +5 -3
  49. package/dist/shared/core.yHe79zEl.mjs +98 -0
  50. package/dist/theme/index.d.mts +3 -126
  51. package/dist/theme/index.mjs +2 -3
  52. package/dist/types/_aux.d.mts +1 -1
  53. package/dist/types/expression-model.d.mts +191 -0
  54. package/dist/types/expression-model.mjs +112 -0
  55. package/dist/types/expression.d.mts +7 -0
  56. package/dist/types/expression.mjs +116 -0
  57. package/dist/types/fqnRef.d.mts +61 -0
  58. package/dist/types/fqnRef.mjs +46 -0
  59. package/dist/types/index.d.mts +6 -3
  60. package/dist/types/index.mjs +9 -9
  61. package/dist/types/scalar.mjs +2 -3
  62. package/dist/utils/index.d.mts +6 -428
  63. package/dist/utils/index.mjs +56 -15
  64. package/dist/utils/iterable/index.d.mts +2 -3
  65. package/dist/utils/iterable/index.mjs +9 -44
  66. package/package.json +8 -7
  67. package/src/index.ts +24 -1
  68. package/src/model/RelationModel.ts +1 -1
  69. package/src/model/index.ts +1 -1
  70. package/src/theme/element.ts +2 -2
  71. package/src/theme/relationships.ts +2 -2
  72. package/src/types/RichText.ts +1 -1
  73. package/src/types/expression-model.ts +3 -1
  74. package/src/types/expression.ts +3 -1
  75. package/src/types/fqnRef.ts +4 -1
  76. package/src/utils/guards.ts +0 -1
  77. package/src/utils/index.ts +0 -1
  78. package/dist/shared/core.BW8K1y6R.mjs +0 -278
  79. package/dist/shared/core.BgvIuGGU.mjs +0 -3
  80. package/dist/shared/core.BpJu1RrF.mjs +0 -5
  81. package/dist/shared/core.ByrmCl9I.mjs +0 -5
  82. package/dist/shared/core.CnIyafWn.mjs +0 -3
  83. package/dist/shared/core.DK6I-Nr7.mjs +0 -149
  84. package/dist/shared/core.DbRvwARP.mjs +0 -5
  85. package/dist/shared/core.Dw47t7nG.mjs +0 -5236
  86. package/dist/shared/core.zLlww-07.mjs +0 -3
  87. package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +0 -425
  88. package/src/builder/__snapshots__/Builder-style2.compute-model.json5 +0 -318
  89. package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +0 -221
  90. package/src/compute-view/deployment-view/__test__/TestHelper.ts +0 -249
  91. package/src/compute-view/deployment-view/__test__/fixture.ts +0 -238
  92. package/src/compute-view/dynamic-view/__test__/fixture.ts +0 -67
  93. package/src/compute-view/element-view/__test__/TestHelper.ts +0 -282
  94. package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +0 -458
  95. package/src/compute-view/element-view/__test__/fixture.ts +0 -749
  96. package/src/compute-view/utils/__snapshots__/sortNodes.spec.ts.snap +0 -113
  97. package/src/model/__test__/fixture.ts +0 -168
  98. package/src/theme/__test__/theme-index.ts +0 -29
@@ -1,749 +0,0 @@
1
- import { indexBy, isString, map, prop } from 'remeda'
2
- import { LikeC4Model } from '../../../model'
3
- import {
4
- type Aux,
5
- type aux,
6
- type BorderStyle,
7
- type Color,
8
- type ComputedElementView,
9
- type Element,
10
- type ElementShape,
11
- type ElementViewPredicate,
12
- type ElementViewPredicate as ViewRulePredicate,
13
- type ElementViewRule,
14
- type ElementViewRule as ViewRule,
15
- type ElementViewRuleGroup as ViewRuleGroup,
16
- type ElementViewRuleStyle as ViewRuleStyle,
17
- type Fqn,
18
- type GlobalPredicateId,
19
- type GlobalStyleID,
20
- type IconUrl,
21
- type KindEqual,
22
- type ModelExpression,
23
- type ModelRelation,
24
- type NonEmptyArray,
25
- type ParsedElementView,
26
- type RelationId,
27
- type RelationshipArrowType,
28
- type RelationshipLineType,
29
- type scalar,
30
- type SpecAux,
31
- type TagEqual,
32
- type ViewRuleGlobalPredicateRef,
33
- type ViewRuleGlobalStyle,
34
- type WhereOperator,
35
- ModelFqnExpr,
36
- ModelRelationExpr,
37
- } from '../../../types'
38
- import type { Participant } from '../../../types/operators'
39
- import { withReadableEdges } from '../../utils/with-readable-edges'
40
- import { computeElementView } from '../compute'
41
-
42
- /**
43
- ┌──────────────────────────────────────────────────┐
44
- │ cloud │
45
- │ ┌───────────────────────────────────────────┐ │
46
- │ │ frontend │ │
47
- ┏━━━━━━━━━━┓ │ │ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━┓ │ │ ┏━━━━━━━━━━━┓
48
- ┃ ┃ │ │ ┃ ┃ ┃ ┃ │ │ ┃ ┃
49
- ┃ customer ┃──┼──┼──▶┃ dashboard ┃ ┃ adminpanel ┃◀───┼───┼───┃ support ┃
50
- ┃ ┃ │ │ ┃ ┃ ┃ ┃ │ │ ┃ ┃
51
- ┗━━━━━━━━━━┛ │ │ ┗━━━━━━┳━━━━━━┛ ┗━━━━━━━━┳━━━━━━━┛ │ │ ┗━━━━━━━━━━━┛
52
- │ └──────────┼───────────────────┼────────────┘ │
53
- │ ├───────────────────┘ │
54
- │ │ │
55
- │ ┌──────────┼────────────────────────────────┐ │
56
- │ │ ▼ backend │ │
57
- │ │ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━┓ │ │
58
- │ │ ┃ ┃ ┃ ┃ │ │
59
- │ │ ┃ graphlql ┃──────▶┃ storage ┃ │ │
60
- │ │ ┃ ┃ ┃ ┃ │ │
61
- │ │ ┗━━━━━━━━━━━━━┛ ┗━━━━━━┳━━━━━━┛ │ │
62
- │ └────────────────────────────────┼──────────┘ │
63
- └───────────────────────────────────┼──────────────┘
64
-
65
- ┌─────────┼─────────┐
66
- │ amazon │ │
67
- │ ▼ │
68
- │ ┏━━━━━━━━━━━━━━┓ │
69
- │ ┃ ┃ │
70
- │ ┃ s3 ┃ │
71
- │ ┃ ┃ │
72
- │ ┗━━━━━━━━━━━━━━┛ │
73
- └───────────────────┘
74
-
75
- specification {
76
- element actor
77
- element system
78
- element container
79
- element component
80
-
81
- tag old
82
- }
83
-
84
- model {
85
-
86
- actor customer
87
- actor support
88
-
89
- system cloud {
90
- container backend {
91
- component graphql
92
- component storage {
93
- #old
94
- }
95
-
96
- graphql -> storage
97
- }
98
-
99
- container frontend {
100
- component dashboard {
101
- -> graphql
102
- }
103
- component adminPanel {
104
- #old
105
- -> graphql
106
- }
107
- }
108
- }
109
-
110
- customer -> dashboard
111
- support -> adminPanel
112
-
113
- system amazon {
114
- component s3
115
-
116
- cloud.backend.storage -> s3
117
- }
118
-
119
- }
120
-
121
- */
122
- type TestTag = 'old' | 'next' | 'aws' | 'storage' | 'communication' | 'legacy'
123
-
124
- const el = ({
125
- id,
126
- kind,
127
- title,
128
- style,
129
- tags,
130
- ...props
131
- }: Partial<Omit<Element, 'id' | 'kind' | 'tags'>> & {
132
- id: string
133
- kind: string
134
- tags?: NonEmptyArray<TestTag>
135
- }): Element => ({
136
- id: id as scalar.Fqn,
137
- kind: kind as scalar.ElementKind,
138
- title: title ?? id,
139
- description: null,
140
- technology: null,
141
- tags: tags as NonEmptyArray<scalar.Tag> ?? null,
142
- links: null,
143
- style: {
144
- ...style,
145
- },
146
- ...props,
147
- })
148
-
149
- export const fakeElements = {
150
- 'customer': el({
151
- id: 'customer',
152
- kind: 'actor',
153
- title: 'customer',
154
- shape: 'person',
155
- }),
156
- 'support': el({
157
- id: 'support',
158
- kind: 'actor',
159
- title: 'support',
160
- shape: 'person',
161
- }),
162
- 'cloud': el({
163
- id: 'cloud',
164
- kind: 'system',
165
- title: 'cloud',
166
- icon: 'none',
167
- tags: ['next', 'old'],
168
- }),
169
- 'cloud.backend': el({
170
- id: 'cloud.backend',
171
- kind: 'container',
172
- title: 'backend',
173
- }),
174
- 'cloud.frontend': el({
175
- id: 'cloud.frontend',
176
- kind: 'container',
177
- title: 'frontend',
178
- shape: 'browser',
179
- }),
180
- 'cloud.backend.graphql': el({
181
- id: 'cloud.backend.graphql',
182
- kind: 'component',
183
- icon: 'tech:graphql' as IconUrl,
184
- title: 'graphql',
185
- }),
186
- 'email': el({
187
- id: 'email',
188
- kind: 'system',
189
- title: 'email',
190
- }),
191
- 'cloud.backend.storage': el({
192
- id: 'cloud.backend.storage',
193
- kind: 'component',
194
- title: 'storage',
195
- tags: ['storage', 'old'],
196
- }),
197
- 'cloud.frontend.dashboard': el({
198
- id: 'cloud.frontend.dashboard',
199
- kind: 'component',
200
- title: 'dashboard',
201
- icon: 'tech:react' as IconUrl,
202
- tags: ['next'],
203
- }),
204
- 'cloud.frontend.supportPanel': el({
205
- id: 'cloud.frontend.supportPanel',
206
- kind: 'component',
207
- title: 'adminPanel',
208
- tags: ['old'],
209
- }),
210
- 'amazon': el({
211
- id: 'amazon',
212
- kind: 'system',
213
- title: 'amazon',
214
- icon: 'tech:aws' as IconUrl,
215
- tags: ['aws'],
216
- }),
217
- 'amazon.s3': el({
218
- id: 'amazon.s3',
219
- kind: 'component',
220
- title: 's3',
221
- shape: 'storage',
222
- icon: 'aws:s3' as IconUrl,
223
- tags: ['aws', 'storage'],
224
- }),
225
- } satisfies Record<string, Element>
226
-
227
- export type FakeElementIds = keyof typeof fakeElements
228
-
229
- const rel = <Source extends FakeElementIds, Target extends FakeElementIds>({
230
- source,
231
- target,
232
- title,
233
- ...props
234
- }: {
235
- source: Source
236
- target: Target
237
- title?: string
238
- kind?: string
239
- color?: Color
240
- line?: RelationshipLineType
241
- head?: RelationshipArrowType
242
- tail?: RelationshipArrowType
243
- tags?: NonEmptyArray<TestTag>
244
- }) =>
245
- ({
246
- id: `${source}:${target}` as RelationId,
247
- title: title ?? '',
248
- source: {
249
- model: source as Fqn,
250
- },
251
- target: {
252
- model: target as Fqn,
253
- },
254
- ...(props as any),
255
- }) as Omit<ModelRelation, 'id'> & { id: `${Source}:${Target}` }
256
-
257
- export const fakeRelations = [
258
- rel({
259
- source: 'customer',
260
- target: 'cloud.frontend.dashboard',
261
- title: 'opens in browser',
262
- }),
263
- rel({
264
- source: 'support',
265
- target: 'cloud.frontend.supportPanel',
266
- title: 'manages',
267
- }),
268
- rel({
269
- source: 'cloud.backend.storage',
270
- target: 'amazon.s3',
271
- title: 'uploads',
272
- tags: ['aws', 'storage', 'legacy'],
273
- }),
274
- rel({
275
- source: 'customer',
276
- target: 'cloud',
277
- title: 'uses',
278
- }),
279
- rel({
280
- source: 'cloud.backend.graphql',
281
- target: 'cloud.backend.storage',
282
- title: 'stores',
283
- tags: ['old', 'storage'],
284
- }),
285
- rel({
286
- source: 'cloud.frontend',
287
- target: 'cloud.backend',
288
- title: 'requests',
289
- }),
290
- rel({
291
- source: 'cloud.frontend.dashboard',
292
- target: 'cloud.backend.graphql',
293
- kind: 'graphlql',
294
- title: 'requests',
295
- line: 'solid',
296
- tags: ['next'],
297
- }),
298
- rel({
299
- source: 'cloud.frontend.supportPanel',
300
- target: 'cloud.backend.graphql',
301
- kind: 'graphlql',
302
- title: 'fetches',
303
- line: 'dashed',
304
- tail: 'odiamond',
305
- tags: ['old'],
306
- }),
307
- rel({
308
- source: 'cloud',
309
- target: 'amazon',
310
- title: 'uses',
311
- head: 'diamond',
312
- tail: 'odiamond',
313
- tags: ['aws'],
314
- }),
315
- rel({
316
- source: 'cloud.backend',
317
- target: 'email',
318
- title: 'schedule',
319
- tags: ['communication'],
320
- }),
321
- rel({
322
- source: 'cloud',
323
- target: 'email',
324
- title: 'uses',
325
- tags: ['communication'],
326
- }),
327
- rel({
328
- source: 'email',
329
- target: 'cloud',
330
- title: 'notifies',
331
- tags: ['communication'],
332
- }),
333
- ]
334
-
335
- export const globalStyles = {
336
- 'mute_old': [{
337
- targets: [$expr({
338
- elementTag: 'old',
339
- isEqual: true,
340
- })],
341
- style: {
342
- color: 'muted',
343
- },
344
- }],
345
- 'red_next': [{
346
- targets: [$expr({
347
- elementTag: 'next',
348
- isEqual: true,
349
- })],
350
- style: {
351
- color: 'red',
352
- },
353
- }],
354
- } as const
355
-
356
- export type FakeRelationIds = (typeof fakeRelations)[number]['id']
357
- const fakeParsedModel = {
358
- _stage: 'computed' as const,
359
- project: { id: 'test-project', config: { name: 'test-project' } },
360
- specification: {
361
- elements: {
362
- actor: {},
363
- system: {},
364
- container: {},
365
- component: {},
366
- },
367
- relationships: {
368
- graphlql: {},
369
- },
370
- deployments: {},
371
- tags: {
372
- old: {},
373
- next: {},
374
- aws: {},
375
- storage: {},
376
- communication: {},
377
- legacy: {},
378
- },
379
- },
380
- elements: fakeElements,
381
- relations: indexBy(fakeRelations, r => r.id),
382
- deployments: {
383
- elements: {},
384
- relations: {},
385
- },
386
- views: {},
387
- imports: {},
388
- globals: {
389
- predicates: {
390
- 'remove_tag_old': [
391
- $exclude('*', {
392
- tag: { eq: 'old' },
393
- }),
394
- ],
395
- 'remove_not_tag_old': [
396
- $exclude('*', {
397
- tag: { neq: 'old' },
398
- }),
399
- ],
400
- 'include_next': [
401
- $include('* -> *', {
402
- where: {
403
- and: [
404
- {
405
- or: [
406
- { tag: { eq: 'communication' } },
407
- { tag: { eq: 'next' } },
408
- { tag: { eq: 'old' } },
409
- ],
410
- },
411
- { tag: { neq: 'storage' } },
412
- ],
413
- },
414
- }),
415
- ],
416
- } as Record<string, NonEmptyArray<ElementViewPredicate<$Aux>>>,
417
- dynamicPredicates: {},
418
- styles: globalStyles,
419
- },
420
- } as const
421
- export const fakeModel = LikeC4Model.fromDump(fakeParsedModel)
422
-
423
- // export type $Aux = typeof fakeModel.Aux
424
- export type $Aux = Aux<
425
- 'computed',
426
- FakeElementIds,
427
- never,
428
- 'index',
429
- never,
430
- SpecAux<
431
- 'actor' | 'system' | 'container' | 'component',
432
- never,
433
- 'graphlql',
434
- TestTag,
435
- never
436
- >
437
- >
438
-
439
- const emptyView = {
440
- _stage: 'parsed' as const,
441
- _type: 'element' as const,
442
- id: 'index' as scalar.ViewId<'index'>,
443
- title: null,
444
- description: null,
445
- tags: null,
446
- links: null,
447
- rules: [],
448
- } satisfies ParsedElementView<$Aux>
449
-
450
- export const includeWildcard = {
451
- include: [
452
- {
453
- wildcard: true,
454
- },
455
- ],
456
- } satisfies ViewRule
457
-
458
- export type ElementRefExpr =
459
- | '*'
460
- | FakeElementIds
461
- | `${FakeElementIds}.*`
462
- | `${FakeElementIds}.**`
463
- | `${FakeElementIds}._`
464
-
465
- type InOutExpr = `-> ${ElementRefExpr} ->`
466
- type IncomingExpr = `-> ${ElementRefExpr}`
467
- type OutgoingExpr = `${ElementRefExpr} ->`
468
- type RelationKeyword = '->' | '<->'
469
- type RelationExpr = `${ElementRefExpr} ${RelationKeyword} ${ElementRefExpr}`
470
-
471
- export type Expression =
472
- | ElementRefExpr
473
- | InOutExpr
474
- | IncomingExpr
475
- | OutgoingExpr
476
- | RelationExpr
477
-
478
- export function $custom(
479
- expr: ElementRefExpr,
480
- props: Omit<ModelFqnExpr.Custom<$Aux>['custom'], 'expr'>,
481
- ): ModelFqnExpr.Custom<$Aux> {
482
- return {
483
- custom: {
484
- expr: $expr(expr) as any,
485
- ...props as any,
486
- },
487
- }
488
- }
489
-
490
- export function $customRelation(
491
- relation: ModelRelationExpr.OrWhere<$Aux>,
492
- props: Omit<ModelRelationExpr.Custom<$Aux>['customRelation'], 'expr'>,
493
- ): ModelRelationExpr.Custom<$Aux> {
494
- return {
495
- customRelation: {
496
- expr: $expr(relation) as any,
497
- ...props,
498
- },
499
- }
500
- }
501
-
502
- export function $where(
503
- expr: Expression,
504
- operator: WhereOperator<$Aux>,
505
- ): ModelExpression.Where<$Aux>
506
- export function $where(
507
- expr: ModelRelationExpr<$Aux>,
508
- operator: WhereOperator<$Aux>,
509
- ): ModelRelationExpr.Where<$Aux>
510
- export function $where(
511
- expr: ModelFqnExpr<$Aux>,
512
- operator: WhereOperator<$Aux>,
513
- ): ModelFqnExpr.Where<$Aux>
514
- export function $where(
515
- expr: Expression | ModelExpression<$Aux>,
516
- operator: WhereOperator<$Aux>,
517
- ): ModelExpression.Where<$Aux> {
518
- return {
519
- where: {
520
- expr: $expr(expr) as any,
521
- condition: operator,
522
- },
523
- }
524
- }
525
-
526
- export function $participant(
527
- participant: Participant,
528
- operator: TagEqual<$Aux> | KindEqual<$Aux>,
529
- ): WhereOperator<$Aux> {
530
- return {
531
- participant,
532
- operator,
533
- }
534
- }
535
-
536
- export function $inout(
537
- expr: InOutExpr | ModelFqnExpr<$Aux>,
538
- ): ModelRelationExpr.InOut<$Aux> {
539
- const innerExpression = !isString(expr)
540
- ? expr
541
- : $expr(expr.replace(/->/g, '').trim() as any)
542
-
543
- return { inout: innerExpression as any }
544
- }
545
-
546
- export function $incoming(
547
- expr: IncomingExpr | ModelFqnExpr<$Aux>,
548
- ): ModelRelationExpr.Incoming<$Aux> {
549
- const innerExpression = !isString(expr)
550
- ? expr
551
- : $expr(expr.replace('-> ', '') as any)
552
-
553
- return { incoming: innerExpression as any }
554
- }
555
-
556
- export function $outgoing(
557
- expr: OutgoingExpr | ModelFqnExpr<$Aux>,
558
- ): ModelRelationExpr.Outgoing<$Aux> {
559
- const innerExpression = !isString(expr)
560
- ? expr as ModelFqnExpr
561
- : $expr(expr.replace(' ->', '') as any)
562
-
563
- return { outgoing: innerExpression as any }
564
- }
565
-
566
- export function $relation(
567
- expr: RelationExpr,
568
- ): ModelRelationExpr.Direct<$Aux> {
569
- const [source, target] = expr.split(/ -> | <-> /)
570
- const isBidirectional = expr.includes(' <-> ')
571
-
572
- return {
573
- source: $expr(source as ElementRefExpr) as any,
574
- target: $expr(target as ElementRefExpr) as any,
575
- ...(isBidirectional && { isBidirectional }),
576
- }
577
- }
578
-
579
- export function $expr(expr: Expression | ModelExpression<$Aux>): ModelExpression<$Aux> {
580
- if (!isString(expr)) {
581
- return expr as ModelExpression<$Aux>
582
- }
583
- if (expr === '*') {
584
- return { wildcard: true }
585
- }
586
- if (expr.startsWith('->')) {
587
- return expr.endsWith('->') ? $inout(expr as InOutExpr) : $incoming(expr as IncomingExpr)
588
- }
589
- if (expr.endsWith(' ->')) {
590
- return $outgoing(expr as OutgoingExpr)
591
- }
592
- if (expr.includes(' -> ') || expr.includes(' <-> ')) {
593
- return $relation(expr as RelationExpr)
594
- }
595
- if (expr.endsWith('._')) {
596
- return {
597
- ref: {
598
- model: expr.replace('._', '') as aux.Fqn<$Aux>,
599
- },
600
- selector: 'expanded',
601
- }
602
- }
603
- if (expr.endsWith('.*')) {
604
- return {
605
- ref: {
606
- model: expr.replace('.*', '') as aux.Fqn<$Aux>,
607
- },
608
- selector: 'children',
609
- }
610
- }
611
- if (expr.endsWith('.**')) {
612
- return {
613
- ref: {
614
- model: expr.replace('.**', '') as aux.Fqn<$Aux>,
615
- },
616
- selector: 'descendants',
617
- }
618
- }
619
- return {
620
- ref: {
621
- model: expr as aux.Fqn<$Aux>,
622
- },
623
- }
624
- }
625
-
626
- type CustomProps = {
627
- where?: WhereOperator<$Aux>
628
- with?: {
629
- title?: string
630
- description?: string
631
- technology?: string
632
- shape?: ElementShape
633
- color?: Color
634
- border?: BorderStyle
635
- icon?: string
636
- opacity?: number
637
- navigateTo?: string
638
- multiple?: boolean
639
- } & Omit<ModelRelationExpr.Custom<$Aux>['customRelation'], 'expr' | 'navigateTo'>
640
- }
641
- export function $include(
642
- expr: Expression | ModelExpression<$Aux>,
643
- props?: CustomProps,
644
- ): ElementViewPredicate<$Aux> {
645
- let _expr = props?.where ? $where(expr as any, props.where as any) : $expr(expr)
646
- _expr = props?.with ? $with(_expr, props.with) : _expr
647
- return {
648
- include: [_expr],
649
- }
650
- }
651
- export function $with(
652
- expr: ModelExpression<$Aux>,
653
- props?: CustomProps['with'],
654
- ): ModelRelationExpr.Custom<$Aux> | ModelFqnExpr.Custom<$Aux> {
655
- if (ModelRelationExpr.is(expr) || ModelRelationExpr.isWhere(expr)) {
656
- return {
657
- customRelation: {
658
- expr,
659
- ...props as any,
660
- },
661
- }
662
- } else if (ModelFqnExpr.is(expr) || ModelFqnExpr.isWhere(expr)) {
663
- return {
664
- custom: {
665
- expr,
666
- ...props as any,
667
- },
668
- }
669
- }
670
-
671
- throw 'Unsupported type of internal expression'
672
- }
673
- export function $exclude(
674
- expr: Expression | ModelExpression<$Aux>,
675
- where?: WhereOperator<$Aux>,
676
- ): ViewRulePredicate<$Aux> {
677
- let _expr = where ? $where(expr as any, where) : $expr(expr)
678
- return {
679
- exclude: [_expr],
680
- }
681
- }
682
- export function $group(groupRules: ViewRuleGroup<$Aux>['groupRules']): ViewRuleGroup<$Aux> {
683
- return {
684
- title: null,
685
- groupRules,
686
- }
687
- }
688
-
689
- export function $style(element: ElementRefExpr, style: ViewRuleStyle<$Aux>['style']): ViewRuleStyle<$Aux> {
690
- return {
691
- targets: [$expr(element) as ModelFqnExpr<$Aux>],
692
- style: Object.assign({}, style),
693
- }
694
- }
695
-
696
- type GlobalStyles = `style ${keyof typeof globalStyles}`
697
- type GlobalPredicate = `predicate ${keyof typeof fakeParsedModel.globals.predicates}`
698
- type GlobalExpr = GlobalStyles | GlobalPredicate
699
- export function $global(expr: GlobalExpr): ViewRuleGlobalStyle | ViewRuleGlobalPredicateRef {
700
- const [_t, id] = expr.split(' ') as [string, string]
701
- switch (_t) {
702
- case 'predicate':
703
- return {
704
- predicateId: id as GlobalPredicateId,
705
- }
706
- case 'style':
707
- return {
708
- styleId: id as GlobalStyleID,
709
- }
710
- default:
711
- throw new Error(`Invalid global expression: ${expr}`)
712
- }
713
- }
714
-
715
- export function computeView(
716
- ...args: [FakeElementIds, ElementViewRule<$Aux> | ElementViewRule<$Aux>[]] | [
717
- ElementViewRule<$Aux> | ElementViewRule<$Aux>[],
718
- ]
719
- ): ComputedElementView<$Aux> & {
720
- nodeIds: string[]
721
- edgeIds: string[]
722
- } {
723
- let result: ComputedElementView<$Aux>
724
- if (args.length === 1) {
725
- result = computeElementView<$Aux>(
726
- fakeModel as unknown as LikeC4Model<$Aux>,
727
- {
728
- ...emptyView,
729
- rules: [args[0]].flat() as ElementViewRule<$Aux>[],
730
- },
731
- )
732
- } else {
733
- result = computeElementView(
734
- fakeModel as unknown as LikeC4Model<$Aux>,
735
- {
736
- ...emptyView,
737
- viewOf: args[0] as aux.Fqn<$Aux>,
738
- rules: [args[1]].flat() as ElementViewRule<$Aux>[],
739
- },
740
- )
741
- }
742
-
743
- result = withReadableEdges(result)
744
-
745
- return Object.assign(result, {
746
- nodeIds: map(result.nodes, prop('id')) as string[],
747
- edgeIds: map(result.edges, prop('id')) as string[],
748
- })
749
- }