@likec4/core 1.12.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +777 -251
- package/dist/index.d.cts +301 -32
- package/dist/index.d.mts +301 -32
- package/dist/index.d.ts +301 -32
- package/dist/index.mjs +774 -252
- package/dist/shared/{core.BvFch1jG.mjs → core.BJDxVHFh.mjs} +19 -19
- package/dist/shared/{core.dY8efyk0.d.ts → core.C4jDMQ1i.d.cts} +242 -238
- package/dist/shared/{core.dY8efyk0.d.cts → core.C4jDMQ1i.d.mts} +242 -238
- package/dist/shared/{core.dY8efyk0.d.mts → core.C4jDMQ1i.d.ts} +242 -238
- package/dist/shared/{core.CTiE4teS.cjs → core.IR7XOEkQ.cjs} +19 -18
- package/dist/types/index.cjs +1 -1
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.mts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.mjs +1 -1
- package/package.json +4 -3
- package/src/builder/Builder.element.ts +221 -0
- package/src/builder/Builder.model.ts +346 -0
- package/src/builder/Builder.test-d.ts +69 -0
- package/src/builder/Builder.ts +574 -0
- package/src/builder/Builder.view.ts +358 -0
- package/src/builder/Builder.views.ts +318 -0
- package/src/builder/_types.ts +195 -0
- package/src/builder/index.ts +5 -0
- package/src/index.ts +4 -0
- package/src/model/LikeC4DiagramModel.ts +5 -5
- package/src/model/LikeC4Model.ts +63 -46
- package/src/model/LikeC4ViewModel.ts +7 -7
- package/src/model/__test__/fixture.ts +5 -0
- package/src/types/element.ts +19 -11
- package/src/types/model.ts +13 -8
- package/src/types/relation.ts +1 -1
- package/src/types/view-changes.ts +6 -2
- package/src/types/view.ts +53 -22
- package/src/utils/fqn.ts +12 -11
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
import defu from 'defu'
|
|
2
|
+
import { fromEntries, hasAtLeast, isFunction, isNonNullish, map, mapToObj, mapValues, pickBy } from 'remeda'
|
|
3
|
+
import type { Writable } from 'type-fest'
|
|
4
|
+
import { invariant } from '../errors'
|
|
5
|
+
import {
|
|
6
|
+
type Color,
|
|
7
|
+
DefaultElementShape,
|
|
8
|
+
DefaultThemeColor,
|
|
9
|
+
type Element,
|
|
10
|
+
type ElementShape,
|
|
11
|
+
type ElementView,
|
|
12
|
+
type Fqn,
|
|
13
|
+
type IconUrl,
|
|
14
|
+
isScopedElementView,
|
|
15
|
+
type LikeC4View,
|
|
16
|
+
type Link,
|
|
17
|
+
type NonEmptyArray,
|
|
18
|
+
type Relation,
|
|
19
|
+
type RelationID,
|
|
20
|
+
type Tag
|
|
21
|
+
} from '../types'
|
|
22
|
+
import type { ParsedLikeC4Model } from '../types/model'
|
|
23
|
+
import { isSameHierarchy, nameFromFqn, parentFqn } from '../utils/fqn'
|
|
24
|
+
import type { AnyTypes, BuilderSpecification, Types } from './_types'
|
|
25
|
+
import type { AddElement, AddElementHelpers } from './Builder.element'
|
|
26
|
+
import { type ModelBuilder, type ModelHelpers } from './Builder.model'
|
|
27
|
+
import {
|
|
28
|
+
$autoLayout,
|
|
29
|
+
$exclude,
|
|
30
|
+
$expr,
|
|
31
|
+
$include,
|
|
32
|
+
$rules,
|
|
33
|
+
$style,
|
|
34
|
+
type ViewBuilder,
|
|
35
|
+
type ViewHelpers,
|
|
36
|
+
type ViewRuleBuilderOp
|
|
37
|
+
} from './Builder.view'
|
|
38
|
+
import { type ViewsBuilder } from './Builder.views'
|
|
39
|
+
|
|
40
|
+
export interface Builder<T extends AnyTypes> {
|
|
41
|
+
/**
|
|
42
|
+
* Only available in compile time
|
|
43
|
+
*/
|
|
44
|
+
readonly Types: T
|
|
45
|
+
|
|
46
|
+
clone(): Builder<T>
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Builders for each element kind
|
|
50
|
+
*/
|
|
51
|
+
helpers(): {
|
|
52
|
+
model: ModelHelpers<T>
|
|
53
|
+
views: ViewHelpers<T['NewViewProps']>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
__model(): ModelBuilder<T>
|
|
57
|
+
__views(): ViewsBuilder<T>
|
|
58
|
+
|
|
59
|
+
build(): ParsedLikeC4Model<
|
|
60
|
+
T['ElementKind'],
|
|
61
|
+
T['RelationshipKind'],
|
|
62
|
+
T['Tag'],
|
|
63
|
+
T['Fqn'],
|
|
64
|
+
T['ViewId']
|
|
65
|
+
>
|
|
66
|
+
|
|
67
|
+
with<
|
|
68
|
+
A extends AnyTypes
|
|
69
|
+
>(
|
|
70
|
+
op1: (input: Builder<T>) => Builder<A>
|
|
71
|
+
): Builder<A>
|
|
72
|
+
|
|
73
|
+
with<
|
|
74
|
+
A extends AnyTypes,
|
|
75
|
+
B extends AnyTypes
|
|
76
|
+
>(
|
|
77
|
+
op1: (input: Builder<T>) => Builder<A>,
|
|
78
|
+
op2: (input: Builder<A>) => Builder<B>
|
|
79
|
+
): Builder<B>
|
|
80
|
+
|
|
81
|
+
with<
|
|
82
|
+
A extends AnyTypes,
|
|
83
|
+
B extends AnyTypes,
|
|
84
|
+
C extends AnyTypes
|
|
85
|
+
>(
|
|
86
|
+
op1: (input: Builder<T>) => Builder<A>,
|
|
87
|
+
op2: (input: Builder<A>) => Builder<B>,
|
|
88
|
+
op3: (input: Builder<B>) => Builder<C>
|
|
89
|
+
): Builder<C>
|
|
90
|
+
|
|
91
|
+
with<
|
|
92
|
+
A extends AnyTypes,
|
|
93
|
+
B extends AnyTypes,
|
|
94
|
+
C extends AnyTypes,
|
|
95
|
+
D extends AnyTypes
|
|
96
|
+
>(
|
|
97
|
+
op1: (input: Builder<T>) => Builder<A>,
|
|
98
|
+
op2: (input: Builder<A>) => Builder<B>,
|
|
99
|
+
op3: (input: Builder<B>) => Builder<C>,
|
|
100
|
+
op4: (input: Builder<C>) => Builder<D>
|
|
101
|
+
): Builder<D>
|
|
102
|
+
|
|
103
|
+
with<
|
|
104
|
+
A extends AnyTypes,
|
|
105
|
+
B extends AnyTypes,
|
|
106
|
+
C extends AnyTypes,
|
|
107
|
+
D extends AnyTypes,
|
|
108
|
+
E extends AnyTypes
|
|
109
|
+
>(
|
|
110
|
+
op1: (input: Builder<T>) => Builder<A>,
|
|
111
|
+
op2: (input: Builder<A>) => Builder<B>,
|
|
112
|
+
op3: (input: Builder<B>) => Builder<C>,
|
|
113
|
+
op4: (input: Builder<C>) => Builder<D>,
|
|
114
|
+
op5: (input: Builder<D>) => Builder<E>
|
|
115
|
+
): Builder<E>
|
|
116
|
+
|
|
117
|
+
with<
|
|
118
|
+
A extends AnyTypes,
|
|
119
|
+
B extends AnyTypes,
|
|
120
|
+
C extends AnyTypes,
|
|
121
|
+
D extends AnyTypes,
|
|
122
|
+
E extends AnyTypes,
|
|
123
|
+
F extends AnyTypes
|
|
124
|
+
>(
|
|
125
|
+
op1: (input: Builder<T>) => Builder<A>,
|
|
126
|
+
op2: (input: Builder<A>) => Builder<B>,
|
|
127
|
+
op3: (input: Builder<B>) => Builder<C>,
|
|
128
|
+
op4: (input: Builder<C>) => Builder<D>,
|
|
129
|
+
op5: (input: Builder<D>) => Builder<E>,
|
|
130
|
+
op6: (input: Builder<E>) => Builder<F>
|
|
131
|
+
): Builder<F>
|
|
132
|
+
|
|
133
|
+
with<
|
|
134
|
+
A extends AnyTypes,
|
|
135
|
+
B extends AnyTypes,
|
|
136
|
+
C extends AnyTypes,
|
|
137
|
+
D extends AnyTypes,
|
|
138
|
+
E extends AnyTypes,
|
|
139
|
+
F extends AnyTypes,
|
|
140
|
+
G extends AnyTypes
|
|
141
|
+
>(
|
|
142
|
+
op1: (input: Builder<T>) => Builder<A>,
|
|
143
|
+
op2: (input: Builder<A>) => Builder<B>,
|
|
144
|
+
op3: (input: Builder<B>) => Builder<C>,
|
|
145
|
+
op4: (input: Builder<C>) => Builder<D>,
|
|
146
|
+
op5: (input: Builder<D>) => Builder<E>,
|
|
147
|
+
op6: (input: Builder<E>) => Builder<F>,
|
|
148
|
+
op7: (input: Builder<F>) => Builder<G>
|
|
149
|
+
): Builder<G>
|
|
150
|
+
|
|
151
|
+
with<
|
|
152
|
+
A extends AnyTypes,
|
|
153
|
+
B extends AnyTypes,
|
|
154
|
+
C extends AnyTypes,
|
|
155
|
+
D extends AnyTypes,
|
|
156
|
+
E extends AnyTypes,
|
|
157
|
+
F extends AnyTypes,
|
|
158
|
+
G extends AnyTypes,
|
|
159
|
+
H extends AnyTypes
|
|
160
|
+
>(
|
|
161
|
+
op1: (input: Builder<T>) => Builder<A>,
|
|
162
|
+
op2: (input: Builder<A>) => Builder<B>,
|
|
163
|
+
op3: (input: Builder<B>) => Builder<C>,
|
|
164
|
+
op4: (input: Builder<C>) => Builder<D>,
|
|
165
|
+
op5: (input: Builder<D>) => Builder<E>,
|
|
166
|
+
op6: (input: Builder<E>) => Builder<F>,
|
|
167
|
+
op7: (input: Builder<F>) => Builder<G>,
|
|
168
|
+
op8: (input: Builder<G>) => Builder<H>
|
|
169
|
+
): Builder<H>
|
|
170
|
+
|
|
171
|
+
with<
|
|
172
|
+
A extends AnyTypes,
|
|
173
|
+
B extends AnyTypes,
|
|
174
|
+
C extends AnyTypes,
|
|
175
|
+
D extends AnyTypes,
|
|
176
|
+
E extends AnyTypes,
|
|
177
|
+
F extends AnyTypes,
|
|
178
|
+
G extends AnyTypes,
|
|
179
|
+
H extends AnyTypes,
|
|
180
|
+
I extends AnyTypes
|
|
181
|
+
>(
|
|
182
|
+
op1: (input: Builder<T>) => Builder<A>,
|
|
183
|
+
op2: (input: Builder<A>) => Builder<B>,
|
|
184
|
+
op3: (input: Builder<B>) => Builder<C>,
|
|
185
|
+
op4: (input: Builder<C>) => Builder<D>,
|
|
186
|
+
op5: (input: Builder<D>) => Builder<E>,
|
|
187
|
+
op6: (input: Builder<E>) => Builder<F>,
|
|
188
|
+
op7: (input: Builder<F>) => Builder<G>,
|
|
189
|
+
op8: (input: Builder<G>) => Builder<H>,
|
|
190
|
+
op9: (input: Builder<H>) => Builder<I>
|
|
191
|
+
): Builder<I>
|
|
192
|
+
|
|
193
|
+
with<
|
|
194
|
+
A extends AnyTypes,
|
|
195
|
+
B extends AnyTypes,
|
|
196
|
+
C extends AnyTypes,
|
|
197
|
+
D extends AnyTypes,
|
|
198
|
+
E extends AnyTypes,
|
|
199
|
+
F extends AnyTypes,
|
|
200
|
+
G extends AnyTypes,
|
|
201
|
+
H extends AnyTypes,
|
|
202
|
+
I extends AnyTypes,
|
|
203
|
+
J extends AnyTypes
|
|
204
|
+
>(
|
|
205
|
+
op1: (input: Builder<T>) => Builder<A>,
|
|
206
|
+
op2: (input: Builder<A>) => Builder<B>,
|
|
207
|
+
op3: (input: Builder<B>) => Builder<C>,
|
|
208
|
+
op4: (input: Builder<C>) => Builder<D>,
|
|
209
|
+
op5: (input: Builder<D>) => Builder<E>,
|
|
210
|
+
op6: (input: Builder<E>) => Builder<F>,
|
|
211
|
+
op7: (input: Builder<F>) => Builder<G>,
|
|
212
|
+
op8: (input: Builder<G>) => Builder<H>,
|
|
213
|
+
op9: (input: Builder<H>) => Builder<I>,
|
|
214
|
+
op10: (input: Builder<I>) => Builder<J>
|
|
215
|
+
): Builder<J>
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function builder<Spec extends BuilderSpecification, T extends AnyTypes>(
|
|
219
|
+
spec: Spec,
|
|
220
|
+
_elements = new Map<string, Element>(),
|
|
221
|
+
_relations = [] as Relation[],
|
|
222
|
+
_views = new Map<string, LikeC4View>()
|
|
223
|
+
): Builder<T> {
|
|
224
|
+
const toLikeC4Specification = (): Types.ToParsedLikeC4Model<T>['specification'] => ({
|
|
225
|
+
tags: (spec.tags ?? []) as Tag<T['Tag']>[],
|
|
226
|
+
elements: {
|
|
227
|
+
...spec.elements as any
|
|
228
|
+
},
|
|
229
|
+
relationships: {
|
|
230
|
+
...spec.relationships
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const mapLinks = (links?: Array<string | { title?: string; url: string }>): NonEmptyArray<Link> | null => {
|
|
235
|
+
if (!links || !hasAtLeast(links, 1)) {
|
|
236
|
+
return null
|
|
237
|
+
}
|
|
238
|
+
return map(links, l => (typeof l === 'string' ? { url: l } : l))
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const mkViewBuilder = (view: Writable<ElementView>): ViewBuilder<T> => {
|
|
242
|
+
const viewBuilder: ViewBuilder<T> = {
|
|
243
|
+
autoLayout(autoLayout) {
|
|
244
|
+
view.rules.push({
|
|
245
|
+
direction: autoLayout
|
|
246
|
+
})
|
|
247
|
+
return viewBuilder
|
|
248
|
+
},
|
|
249
|
+
exclude(expr) {
|
|
250
|
+
view.rules.push({
|
|
251
|
+
exclude: [expr]
|
|
252
|
+
})
|
|
253
|
+
return viewBuilder
|
|
254
|
+
},
|
|
255
|
+
include(expr) {
|
|
256
|
+
view.rules.push({
|
|
257
|
+
include: [expr]
|
|
258
|
+
})
|
|
259
|
+
return viewBuilder
|
|
260
|
+
},
|
|
261
|
+
style(rule) {
|
|
262
|
+
view.rules.push(rule)
|
|
263
|
+
return viewBuilder
|
|
264
|
+
}
|
|
265
|
+
// title(title: string) {
|
|
266
|
+
// view.title = title
|
|
267
|
+
// return viewBuilder
|
|
268
|
+
// },
|
|
269
|
+
// description(description: string) {
|
|
270
|
+
// view.description = description
|
|
271
|
+
// return viewBuilder
|
|
272
|
+
// }
|
|
273
|
+
}
|
|
274
|
+
return viewBuilder
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const self: Builder<T> = {
|
|
278
|
+
get Types(): T {
|
|
279
|
+
throw new Error('Types are not available in runtime')
|
|
280
|
+
},
|
|
281
|
+
clone: () =>
|
|
282
|
+
builder(
|
|
283
|
+
structuredClone(spec),
|
|
284
|
+
structuredClone(_elements),
|
|
285
|
+
structuredClone(_relations),
|
|
286
|
+
structuredClone(_views)
|
|
287
|
+
),
|
|
288
|
+
__model: () => ({
|
|
289
|
+
addElement: (element) => {
|
|
290
|
+
const parent = parentFqn(element.id)
|
|
291
|
+
if (parent) {
|
|
292
|
+
invariant(
|
|
293
|
+
_elements.get(parent),
|
|
294
|
+
`Parent element with id "${parent}" not found for element with id "${element.id}"`
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
if (_elements.has(element.id)) {
|
|
298
|
+
throw new Error(`Element with id "${element.id}" already exists`)
|
|
299
|
+
}
|
|
300
|
+
_elements.set(element.id, element)
|
|
301
|
+
return self
|
|
302
|
+
},
|
|
303
|
+
addRelation(relation) {
|
|
304
|
+
const sourceEl = _elements.get(relation.source)
|
|
305
|
+
invariant(sourceEl, `Element with id "${relation.source}" not found`)
|
|
306
|
+
const targetEl = _elements.get(relation.target)
|
|
307
|
+
invariant(targetEl, `Element with id "${relation.target}" not found`)
|
|
308
|
+
invariant(
|
|
309
|
+
!isSameHierarchy(sourceEl, targetEl),
|
|
310
|
+
'Cannot create relationship between elements in the same hierarchy'
|
|
311
|
+
)
|
|
312
|
+
_relations.push({
|
|
313
|
+
id: `rel${_relations.length + 1}` as RelationID,
|
|
314
|
+
...relation
|
|
315
|
+
})
|
|
316
|
+
return self
|
|
317
|
+
},
|
|
318
|
+
/**
|
|
319
|
+
* Fully qualified name for nested elements
|
|
320
|
+
*/
|
|
321
|
+
fqn(id) {
|
|
322
|
+
return id as Fqn
|
|
323
|
+
},
|
|
324
|
+
addSourcelessRelation() {
|
|
325
|
+
throw new Error('Can be called only in nested model')
|
|
326
|
+
}
|
|
327
|
+
}),
|
|
328
|
+
__views: () => ({
|
|
329
|
+
addView: (view) => {
|
|
330
|
+
if (isScopedElementView(view)) {
|
|
331
|
+
invariant(_elements.get(view.viewOf), `Invalid view ${view.id}, wlement with id "${view.viewOf}" not found`)
|
|
332
|
+
}
|
|
333
|
+
_views.set(view.id, view)
|
|
334
|
+
return self
|
|
335
|
+
}
|
|
336
|
+
}),
|
|
337
|
+
build: () => ({
|
|
338
|
+
specification: toLikeC4Specification(),
|
|
339
|
+
elements: fromEntries(Array.from(_elements.entries())) as any,
|
|
340
|
+
relations: mapToObj(_relations, r => [r.id, r]),
|
|
341
|
+
views: fromEntries(Array.from(_views.entries())) as any
|
|
342
|
+
}),
|
|
343
|
+
helpers: () => ({
|
|
344
|
+
model: {
|
|
345
|
+
model: (...ops: ((b: ModelBuilder<T>) => ModelBuilder<T>)[]) => {
|
|
346
|
+
return (b: Builder<T>) => {
|
|
347
|
+
const v = b.__model()
|
|
348
|
+
for (const op of ops) {
|
|
349
|
+
op(v)
|
|
350
|
+
}
|
|
351
|
+
return b
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
rel: (source: string, target: string, _props?: T['NewRelationshipProps'] | string) => {
|
|
355
|
+
return <T extends AnyTypes>(b: ModelBuilder<T>) => {
|
|
356
|
+
const {
|
|
357
|
+
title = '',
|
|
358
|
+
links: _links = [],
|
|
359
|
+
...props
|
|
360
|
+
} = defu(
|
|
361
|
+
typeof _props === 'string' ? { title: _props } : { ..._props },
|
|
362
|
+
{ title: null, links: null }
|
|
363
|
+
)
|
|
364
|
+
const links = mapLinks(_links)
|
|
365
|
+
b.addRelation({
|
|
366
|
+
source: source as any,
|
|
367
|
+
target: target as any,
|
|
368
|
+
title,
|
|
369
|
+
...(links && { links }),
|
|
370
|
+
...props
|
|
371
|
+
})
|
|
372
|
+
return b
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
relTo: (target, _props?) => {
|
|
376
|
+
return <T extends AnyTypes>(b: ModelBuilder<T>) => {
|
|
377
|
+
const {
|
|
378
|
+
title = '',
|
|
379
|
+
links: _links = [],
|
|
380
|
+
...props
|
|
381
|
+
} = defu(
|
|
382
|
+
typeof _props === 'string' ? { title: _props } : { ..._props },
|
|
383
|
+
{ title: null, links: null }
|
|
384
|
+
)
|
|
385
|
+
const links = mapLinks(_links)
|
|
386
|
+
b.addSourcelessRelation({
|
|
387
|
+
target,
|
|
388
|
+
title,
|
|
389
|
+
...(links && { links }),
|
|
390
|
+
...props
|
|
391
|
+
})
|
|
392
|
+
return b
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
...mapValues(
|
|
396
|
+
spec.elements,
|
|
397
|
+
({ style: specStyle, ...spec }, kind) =>
|
|
398
|
+
<Id extends string>(
|
|
399
|
+
id: Id,
|
|
400
|
+
_props?: T['NewElementProps'] | string
|
|
401
|
+
): AddElement<Id> => {
|
|
402
|
+
const add = (<T extends AnyTypes>(b: ModelBuilder<T>) => {
|
|
403
|
+
const {
|
|
404
|
+
links: _links,
|
|
405
|
+
icon: _icon,
|
|
406
|
+
style,
|
|
407
|
+
title,
|
|
408
|
+
...props
|
|
409
|
+
} = typeof _props === 'string' ? { title: _props } : { ..._props }
|
|
410
|
+
|
|
411
|
+
const links = mapLinks(_links)
|
|
412
|
+
|
|
413
|
+
const icon = _icon ?? specStyle?.icon
|
|
414
|
+
|
|
415
|
+
const _id = b.fqn(id)
|
|
416
|
+
|
|
417
|
+
b.addElement({
|
|
418
|
+
id: _id,
|
|
419
|
+
kind: kind as any,
|
|
420
|
+
title: title ?? nameFromFqn(_id),
|
|
421
|
+
description: null,
|
|
422
|
+
technology: null,
|
|
423
|
+
tags: null,
|
|
424
|
+
color: specStyle?.color ?? DefaultThemeColor as Color,
|
|
425
|
+
shape: specStyle?.shape ?? DefaultElementShape as ElementShape,
|
|
426
|
+
style: pickBy({
|
|
427
|
+
border: specStyle?.border,
|
|
428
|
+
opacity: specStyle?.opacity,
|
|
429
|
+
...style
|
|
430
|
+
}, isNonNullish),
|
|
431
|
+
links,
|
|
432
|
+
...icon && { icon: icon as IconUrl },
|
|
433
|
+
...spec,
|
|
434
|
+
...props
|
|
435
|
+
})
|
|
436
|
+
return b
|
|
437
|
+
}) as AddElement<Id>
|
|
438
|
+
|
|
439
|
+
add.with = (...ops: Array<(input: ModelBuilder<any>) => ModelBuilder<any>>) => (b: ModelBuilder<any>) => {
|
|
440
|
+
add(b)
|
|
441
|
+
const nestedBuilder: ModelBuilder<any> = {
|
|
442
|
+
...b,
|
|
443
|
+
fqn: (child) => `${b.fqn(id)}.${child}` as Fqn,
|
|
444
|
+
addSourcelessRelation: (relation) => {
|
|
445
|
+
return b.addRelation({
|
|
446
|
+
...relation,
|
|
447
|
+
source: b.fqn(id)
|
|
448
|
+
})
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
for (const op of ops) {
|
|
452
|
+
op(nestedBuilder)
|
|
453
|
+
}
|
|
454
|
+
return b
|
|
455
|
+
}
|
|
456
|
+
return add
|
|
457
|
+
}
|
|
458
|
+
) as AddElementHelpers<T>
|
|
459
|
+
},
|
|
460
|
+
views: {
|
|
461
|
+
views: (...ops: ((b: ViewsBuilder<T>) => ViewsBuilder<T>)[]) => {
|
|
462
|
+
return (b: Builder<T>) => {
|
|
463
|
+
const v = b.__views()
|
|
464
|
+
for (const op of ops) {
|
|
465
|
+
op(v)
|
|
466
|
+
}
|
|
467
|
+
return b
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
view: (id, _props, builder?: ViewRuleBuilderOp<T>) => {
|
|
471
|
+
if (isFunction(_props)) {
|
|
472
|
+
builder = _props
|
|
473
|
+
_props = {}
|
|
474
|
+
}
|
|
475
|
+
return <T extends AnyTypes>(b: ViewsBuilder<T>): ViewsBuilder<T> => {
|
|
476
|
+
const {
|
|
477
|
+
links: _links = [],
|
|
478
|
+
title = null,
|
|
479
|
+
...props
|
|
480
|
+
} = typeof _props === 'string' ? { title: _props } : { ..._props }
|
|
481
|
+
|
|
482
|
+
const links = mapLinks(_links)
|
|
483
|
+
|
|
484
|
+
const view: Writable<ElementView> = {
|
|
485
|
+
id: id as any,
|
|
486
|
+
__: 'element',
|
|
487
|
+
title,
|
|
488
|
+
description: null,
|
|
489
|
+
tags: null,
|
|
490
|
+
rules: [],
|
|
491
|
+
links,
|
|
492
|
+
customColorDefinitions: {},
|
|
493
|
+
...props
|
|
494
|
+
}
|
|
495
|
+
b.addView(view)
|
|
496
|
+
|
|
497
|
+
if (builder) {
|
|
498
|
+
builder(mkViewBuilder(view))
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return b
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
viewOf: (
|
|
505
|
+
id,
|
|
506
|
+
viewOf,
|
|
507
|
+
_props?: T['NewViewProps'] | string | ViewRuleBuilderOp<T>,
|
|
508
|
+
builder?: ViewRuleBuilderOp<T>
|
|
509
|
+
) => {
|
|
510
|
+
if (isFunction(_props)) {
|
|
511
|
+
builder = _props
|
|
512
|
+
_props = {}
|
|
513
|
+
}
|
|
514
|
+
return <T extends AnyTypes>(b: ViewsBuilder<T>): ViewsBuilder<T> => {
|
|
515
|
+
const {
|
|
516
|
+
links: _links = [],
|
|
517
|
+
title = null,
|
|
518
|
+
...props
|
|
519
|
+
} = typeof _props === 'string' ? { title: _props } : { ..._props }
|
|
520
|
+
|
|
521
|
+
const links = mapLinks(_links)
|
|
522
|
+
|
|
523
|
+
const view: Writable<ElementView> = {
|
|
524
|
+
id: id as any,
|
|
525
|
+
__: 'element',
|
|
526
|
+
viewOf,
|
|
527
|
+
title,
|
|
528
|
+
description: null,
|
|
529
|
+
tags: null,
|
|
530
|
+
rules: [],
|
|
531
|
+
links,
|
|
532
|
+
customColorDefinitions: {},
|
|
533
|
+
...props
|
|
534
|
+
}
|
|
535
|
+
b.addView(view)
|
|
536
|
+
|
|
537
|
+
if (builder) {
|
|
538
|
+
builder(mkViewBuilder(view))
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return b
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
$autoLayout,
|
|
545
|
+
$exclude,
|
|
546
|
+
$expr,
|
|
547
|
+
$include,
|
|
548
|
+
$rules,
|
|
549
|
+
$style
|
|
550
|
+
}
|
|
551
|
+
}),
|
|
552
|
+
with: (...ops: ((b: Builder<T>) => Builder<T>)[]) => {
|
|
553
|
+
return ops.reduce((b, op) => op(b), self).clone()
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return self
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
export namespace Builder {
|
|
561
|
+
export function forSpecification<const Spec extends BuilderSpecification>(
|
|
562
|
+
spec: Spec
|
|
563
|
+
): {
|
|
564
|
+
builder: Builder<Types.FromSpecification<Spec>>
|
|
565
|
+
model: ModelHelpers<Types.FromSpecification<Spec>>
|
|
566
|
+
views: ViewHelpers<Types.FromSpecification<Spec>['NewViewProps']>
|
|
567
|
+
} {
|
|
568
|
+
const b = builder<Spec, Types.FromSpecification<Spec>>(spec)
|
|
569
|
+
return {
|
|
570
|
+
...b.helpers(),
|
|
571
|
+
builder: b
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|