@lionweb/class-core 0.6.13-beta.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 (143) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +157 -0
  3. package/dist/base-types.d.ts +167 -0
  4. package/dist/base-types.d.ts.map +1 -0
  5. package/dist/base-types.js +147 -0
  6. package/dist/base-types.js.map +1 -0
  7. package/dist/convenience.d.ts +49 -0
  8. package/dist/convenience.d.ts.map +1 -0
  9. package/dist/convenience.js +75 -0
  10. package/dist/convenience.js.map +1 -0
  11. package/dist/deltas/appliers.d.ts +42 -0
  12. package/dist/deltas/appliers.d.ts.map +1 -0
  13. package/dist/deltas/appliers.js +274 -0
  14. package/dist/deltas/appliers.js.map +1 -0
  15. package/dist/deltas/base.d.ts +6 -0
  16. package/dist/deltas/base.d.ts.map +1 -0
  17. package/dist/deltas/base.js +18 -0
  18. package/dist/deltas/base.js.map +1 -0
  19. package/dist/deltas/handlers.d.ts +17 -0
  20. package/dist/deltas/handlers.d.ts.map +1 -0
  21. package/dist/deltas/handlers.js +43 -0
  22. package/dist/deltas/handlers.js.map +1 -0
  23. package/dist/deltas/index.d.ts +7 -0
  24. package/dist/deltas/index.d.ts.map +1 -0
  25. package/dist/deltas/index.js +23 -0
  26. package/dist/deltas/index.js.map +1 -0
  27. package/dist/deltas/inverters.d.ts +7 -0
  28. package/dist/deltas/inverters.d.ts.map +1 -0
  29. package/dist/deltas/inverters.js +82 -0
  30. package/dist/deltas/inverters.js.map +1 -0
  31. package/dist/deltas/serialization/base.d.ts +7 -0
  32. package/dist/deltas/serialization/base.d.ts.map +1 -0
  33. package/dist/deltas/serialization/base.js +18 -0
  34. package/dist/deltas/serialization/base.js.map +1 -0
  35. package/dist/deltas/serialization/deserializer.g.d.ts +5 -0
  36. package/dist/deltas/serialization/deserializer.g.d.ts.map +1 -0
  37. package/dist/deltas/serialization/deserializer.g.js +164 -0
  38. package/dist/deltas/serialization/deserializer.g.js.map +1 -0
  39. package/dist/deltas/serialization/index.d.ts +5 -0
  40. package/dist/deltas/serialization/index.d.ts.map +1 -0
  41. package/dist/deltas/serialization/index.js +21 -0
  42. package/dist/deltas/serialization/index.js.map +1 -0
  43. package/dist/deltas/serialization/serializer-helpers.d.ts +12 -0
  44. package/dist/deltas/serialization/serializer-helpers.d.ts.map +1 -0
  45. package/dist/deltas/serialization/serializer-helpers.js +28 -0
  46. package/dist/deltas/serialization/serializer-helpers.js.map +1 -0
  47. package/dist/deltas/serialization/serializer.g.d.ts +4 -0
  48. package/dist/deltas/serialization/serializer.g.d.ts.map +1 -0
  49. package/dist/deltas/serialization/serializer.g.js +208 -0
  50. package/dist/deltas/serialization/serializer.g.js.map +1 -0
  51. package/dist/deltas/serialization/types.g.d.ts +147 -0
  52. package/dist/deltas/serialization/types.g.d.ts.map +1 -0
  53. package/dist/deltas/serialization/types.g.js +18 -0
  54. package/dist/deltas/serialization/types.g.js.map +1 -0
  55. package/dist/deltas/types.g.d.ts +149 -0
  56. package/dist/deltas/types.g.d.ts.map +1 -0
  57. package/dist/deltas/types.g.js +180 -0
  58. package/dist/deltas/types.g.js.map +1 -0
  59. package/dist/deserializer.d.ts +27 -0
  60. package/dist/deserializer.d.ts.map +1 -0
  61. package/dist/deserializer.js +168 -0
  62. package/dist/deserializer.js.map +1 -0
  63. package/dist/duplicator.d.ts +19 -0
  64. package/dist/duplicator.d.ts.map +1 -0
  65. package/dist/duplicator.js +87 -0
  66. package/dist/duplicator.js.map +1 -0
  67. package/dist/id-mapping.d.ts +19 -0
  68. package/dist/id-mapping.d.ts.map +1 -0
  69. package/dist/id-mapping.js +44 -0
  70. package/dist/id-mapping.js.map +1 -0
  71. package/dist/index.d.ts +11 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +28 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/linking.d.ts +20 -0
  76. package/dist/linking.d.ts.map +1 -0
  77. package/dist/linking.js +18 -0
  78. package/dist/linking.js.map +1 -0
  79. package/dist/lionCore_builtins.g.d.ts +35 -0
  80. package/dist/lionCore_builtins.g.d.ts.map +1 -0
  81. package/dist/lionCore_builtins.g.js +96 -0
  82. package/dist/lionCore_builtins.g.js.map +1 -0
  83. package/dist/serializer.d.ts +17 -0
  84. package/dist/serializer.d.ts.map +1 -0
  85. package/dist/serializer.js +70 -0
  86. package/dist/serializer.js.map +1 -0
  87. package/dist/textualizer.d.ts +7 -0
  88. package/dist/textualizer.d.ts.map +1 -0
  89. package/dist/textualizer.js +82 -0
  90. package/dist/textualizer.js.map +1 -0
  91. package/dist/value-managers/annotations.d.ts +33 -0
  92. package/dist/value-managers/annotations.d.ts.map +1 -0
  93. package/dist/value-managers/annotations.js +192 -0
  94. package/dist/value-managers/annotations.js.map +1 -0
  95. package/dist/value-managers/base.d.ts +53 -0
  96. package/dist/value-managers/base.d.ts.map +1 -0
  97. package/dist/value-managers/base.js +102 -0
  98. package/dist/value-managers/base.js.map +1 -0
  99. package/dist/value-managers/containments.d.ts +58 -0
  100. package/dist/value-managers/containments.d.ts.map +1 -0
  101. package/dist/value-managers/containments.js +408 -0
  102. package/dist/value-managers/containments.js.map +1 -0
  103. package/dist/value-managers/index.d.ts +6 -0
  104. package/dist/value-managers/index.d.ts.map +1 -0
  105. package/dist/value-managers/index.js +22 -0
  106. package/dist/value-managers/index.js.map +1 -0
  107. package/dist/value-managers/properties.d.ts +24 -0
  108. package/dist/value-managers/properties.d.ts.map +1 -0
  109. package/dist/value-managers/properties.js +191 -0
  110. package/dist/value-managers/properties.js.map +1 -0
  111. package/dist/value-managers/references.d.ts +58 -0
  112. package/dist/value-managers/references.d.ts.map +1 -0
  113. package/dist/value-managers/references.js +346 -0
  114. package/dist/value-managers/references.js.map +1 -0
  115. package/package.json +35 -0
  116. package/src/base-types.ts +353 -0
  117. package/src/convenience.ts +101 -0
  118. package/src/deltas/appliers.ts +317 -0
  119. package/src/deltas/base.ts +23 -0
  120. package/src/deltas/handlers.ts +64 -0
  121. package/src/deltas/index.ts +23 -0
  122. package/src/deltas/inverters.ts +111 -0
  123. package/src/deltas/serialization/base.ts +26 -0
  124. package/src/deltas/serialization/deserializer.g.ts +193 -0
  125. package/src/deltas/serialization/index.ts +22 -0
  126. package/src/deltas/serialization/serializer-helpers.ts +36 -0
  127. package/src/deltas/serialization/serializer.g.ts +272 -0
  128. package/src/deltas/serialization/types.g.ts +209 -0
  129. package/src/deltas/types.g.ts +231 -0
  130. package/src/deserializer.ts +234 -0
  131. package/src/duplicator.ts +111 -0
  132. package/src/id-mapping.ts +58 -0
  133. package/src/index.ts +29 -0
  134. package/src/linking.ts +39 -0
  135. package/src/lionCore_builtins.g.ts +141 -0
  136. package/src/serializer.ts +86 -0
  137. package/src/textualizer.ts +104 -0
  138. package/src/value-managers/annotations.ts +139 -0
  139. package/src/value-managers/base.ts +123 -0
  140. package/src/value-managers/containments.ts +323 -0
  141. package/src/value-managers/index.ts +22 -0
  142. package/src/value-managers/properties.ts +127 -0
  143. package/src/value-managers/references.ts +268 -0
@@ -0,0 +1,353 @@
1
+ // Copyright 2025 TRUMPF Laser SE and other contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License")
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // SPDX-FileCopyrightText: 2025 TRUMPF Laser SE and other contributors
16
+ // SPDX-License-Identifier: Apache-2.0
17
+
18
+ import {
19
+ allFeaturesOf,
20
+ Classifier,
21
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
22
+ Concept,
23
+ Containment,
24
+ EnumerationLiteral,
25
+ Feature,
26
+ Id,
27
+ Language,
28
+ Node,
29
+ Property,
30
+ Reference
31
+ } from "@lionweb/core";
32
+ import {makeObservable} from "mobx";
33
+
34
+ import {
35
+ AnnotationsValueManager,
36
+ ContainmentValueManager,
37
+ DeltaHandler,
38
+ FeatureValueManager,
39
+ MultiContainmentValueManager,
40
+ PropertyValueManager,
41
+ ReferenceValueManager,
42
+ SingleContainmentValueManager
43
+ } from "./index.js";
44
+
45
+
46
+ /**
47
+ * Encodes how a {@link INodeBase} is contained by a parent.
48
+ * If the {@code containingFeature} is {@code null}, the {@link INodeBase} is an annotation
49
+ * and effectively contained by the {@link INodeBase.annotations} property.
50
+ */
51
+ export type Parentage = [ parent: INodeBase, containingFeature: Containment | null ];
52
+
53
+
54
+ /**
55
+ * The base interface for node objects that know about how they're contained,
56
+ * know what their {@link Classifier meta-type} is,
57
+ * and manage the values of their features through {@link ValueManager value managers}
58
+ * that are wired up for observability (according to the MobX library) and emission of deltas.
59
+ */
60
+ export interface INodeBase extends Node {
61
+
62
+ classifier: Classifier;
63
+
64
+ /**
65
+ * The parent of this node object,
66
+ * or {@code undefined} if it's either a root (in which case it {@code this.classifier} should be a {@link Concept} which is a partition)
67
+ * or (currently/temporarily) an orphan.
68
+ */
69
+ parent: INodeBase | undefined;
70
+
71
+ /**
72
+ * The {@link Containment containment feature} through which this node is contained by its parent,
73
+ * or `null` if it's an annotation,
74
+ * or `undefined` exactly when `parent` is also `undefined`.
75
+ */
76
+ containment: Containment | null | undefined;
77
+
78
+ /**
79
+ * Attach this node to the given `parent` {@link INodeBase}, through the given `containment` {@link Containment}.
80
+ *Note* that this is **for internal use only**!
81
+ */
82
+ attachTo(parent: INodeBase, containment: Containment | null): void;
83
+
84
+ /**
85
+ * Detach this node from its stated parent(age).
86
+ * *Note* that this is **for internal use only**!
87
+ */
88
+ detach(): void;
89
+
90
+
91
+ /**
92
+ * @return the value manager for the given {@link Property property} feature.
93
+ * @throws if this node('s {@link Classifier classifier}) doesn't have that property.
94
+ */
95
+ getPropertyValueManager(property: Property): PropertyValueManager<unknown>;
96
+
97
+ /**
98
+ * @return the value manager for the given {@link Containment containment} feature.
99
+ * @throws if this node('s {@link Classifier classifier}) doesn't have that containment.
100
+ */
101
+ getContainmentValueManager(containment: Containment): ContainmentValueManager<INodeBase>;
102
+
103
+ /**
104
+ * @return the value manager for the given {@link Reference reference} feature.
105
+ * @throws if this node('s {@link Classifier classifier}) doesn't have that reference.
106
+ */
107
+ getReferenceValueManager(reference: Reference): ReferenceValueManager<INodeBase>;
108
+
109
+ /**
110
+ * @return the value manager for the given {@link Feature feature}.
111
+ * @throws if this node('s {@link Classifier classifier}) doesn't have that feature.
112
+ */
113
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
+ getFeatureValueManager(feature: Feature): FeatureValueManager<Feature>;
115
+
116
+ /**
117
+ * @return the value manager for the annotations.
118
+ */
119
+ annotationsValueManager: AnnotationsValueManager;
120
+
121
+ /**
122
+ * The annotations annotating this node.
123
+ */
124
+ annotations: INodeBase[];
125
+
126
+ /**
127
+ * Adds the given annotation (as an instance of {@link INodeBase}) to the annotations of this node.
128
+ */
129
+ addAnnotation(annotation: INodeBase): void;
130
+
131
+ /**
132
+ * Inserts the given annotation (as an instance of {@link INodeBase}) among the annotations of this node,
133
+ * displacing all existing annotations with index &geq; the given one “to the right”.
134
+ */
135
+ insertAnnotationAtIndex(annotation: INodeBase, index: number): void;
136
+
137
+ /**
138
+ * Moves the annotation at the given `oldIndex` to the `newIndex` position.
139
+ */
140
+ moveAnnotation(oldIndex: number, newIndex: number): void;
141
+
142
+ /**
143
+ * Replaces the existing annotation at the given index, with the given annotation (as an instance of {@link INodeBase}).
144
+ */
145
+ replaceAnnotationAtIndex(annotation: INodeBase, index: number): void;
146
+
147
+ /**
148
+ * Removes the given annotation (as an instance of {@link INodeBase}) from the annotations of this node.
149
+ * (In the case of duplicates, only the first occurrence is removed.)
150
+ */
151
+ removeAnnotation(annotation: INodeBase): void;
152
+
153
+
154
+ /**
155
+ * @return the {@link Feature features} of the {@link Classifier} that is this node's meta-type,
156
+ * that are set.
157
+ * A feature has a set value if it's not `undefined` for single-valued features,
158
+ * or an empty array `[]` for multi-valued features.
159
+ *
160
+ * Note* that this is (in principle) **for internal use only**!
161
+ */
162
+ get setFeatures(): Feature[];
163
+
164
+ /**
165
+ * @return all children – not all descendants! – of this node, including directly-contained annotations.
166
+ *Note* that this is (in principle) **for internal use only**!
167
+ */
168
+ get children(): INodeBase[];
169
+
170
+
171
+ /**
172
+ * An optionally-installed {@link DeltaHandler}.
173
+ */
174
+ handleDelta?: DeltaHandler;
175
+
176
+
177
+ /**
178
+ * A message to indicate this node's meta-type (including originating language) and its ID.
179
+ */
180
+ locationMessage: string;
181
+
182
+ }
183
+
184
+
185
+ /**
186
+ * The base type for any node that's managed by a, potentially delta-emitting, API.
187
+ *
188
+ * It receives a {@link DeltaHandler} to pass {@link IDelta deltas} to
189
+ * that occur due to changes to values of its features,
190
+ * as well as moving among parents and deletion.
191
+ *
192
+ * It keeps explicit track of how it's contained by its parent (when it has one),
193
+ * to make moving children among parents easier.
194
+ */
195
+ export abstract class NodeBase implements INodeBase {
196
+
197
+ private _parentage?: Parentage;
198
+ get parent(): INodeBase | undefined {
199
+ return this._parentage?.[0];
200
+ }
201
+
202
+ get containment(): Containment | null | undefined {
203
+ return this._parentage?.[1];
204
+ }
205
+
206
+ /*
207
+ * Note: the tuple [_parent, _containingFeature] can only be set simultaneously, through the attachTo method.
208
+ */
209
+
210
+ attachTo(parent: INodeBase, containingFeature: Containment | null) {
211
+ this._parentage = [parent, containingFeature];
212
+ }
213
+
214
+ detach() {
215
+ this._parentage = undefined;
216
+ }
217
+
218
+
219
+ protected constructor(readonly classifier: Classifier, readonly id: Id, readonly handleDelta?: DeltaHandler, parentage?: Parentage) {
220
+ this.classifier = classifier;
221
+ this.id = id;
222
+ if (parentage) {
223
+ this.attachTo(parentage[0], parentage[1]);
224
+ }
225
+ makeObservable(this);
226
+ }
227
+
228
+ get locationMessage(): string {
229
+ return `instance of ${this.classifier.language.name}.${this.classifier.name} with id=${this.id}`;
230
+ }
231
+
232
+
233
+ getPropertyValueManager(property: Property): PropertyValueManager<unknown> {
234
+ throw new Error(`property (feature) "${property.name}" (with key=${property.key}) doesn't exist on ${this.locationMessage}`);
235
+ }
236
+
237
+ getContainmentValueManager(containment: Containment): ContainmentValueManager<INodeBase> {
238
+ throw new Error(`containment (feature) "${containment.name}" (with key=${containment.key}) doesn't exist on ${this.locationMessage}`);
239
+ }
240
+
241
+ getReferenceValueManager(reference: Reference): ReferenceValueManager<INodeBase> {
242
+ throw new Error(`reference (feature) "${reference.name}" (with key=${reference.key}) doesn't exist on ${this.locationMessage}`);
243
+ }
244
+
245
+ getFeatureValueManager(feature: Feature): FeatureValueManager<Feature> {
246
+ if (feature instanceof Property) {
247
+ return this.getPropertyValueManager(feature);
248
+ }
249
+ if (feature instanceof Containment) {
250
+ return this.getContainmentValueManager(feature);
251
+ }
252
+ if (feature instanceof Reference) {
253
+ return this.getReferenceValueManager(feature);
254
+ }
255
+ throw new Error(`unhandled Feature sub type ${feature.constructor.name}`)
256
+ }
257
+
258
+
259
+ get setFeatures(): Feature[] {
260
+ return allFeaturesOf(this.classifier)
261
+ .filter((feature) => this.getFeatureValueManager(feature).isSet());
262
+ }
263
+
264
+ get children(): INodeBase[] {
265
+ return [
266
+ ...(this.setFeatures
267
+ .filter((feature) => feature instanceof Containment)
268
+ .flatMap((feature) => {
269
+ const valueManager = this.getContainmentValueManager(feature as Containment);
270
+ if (valueManager instanceof SingleContainmentValueManager) {
271
+ return valueManager.isSet() ? [valueManager.getDirectly()] : [];
272
+ }
273
+ if (valueManager instanceof MultiContainmentValueManager) {
274
+ return valueManager.get();
275
+ }
276
+ throw new Error(`don't know how to obtain value of feature ${feature.name} of classifier ${this.classifier.name} in language ${this.classifier.language.name}`);
277
+ })),
278
+ ...this.annotations
279
+ ];
280
+ }
281
+
282
+
283
+ readonly annotationsValueManager: AnnotationsValueManager = new AnnotationsValueManager(this);
284
+ get annotations(): INodeBase[] {
285
+ return this.annotationsValueManager.get();
286
+ }
287
+
288
+ addAnnotation(annotation: INodeBase) {
289
+ this.annotationsValueManager.add(annotation);
290
+ }
291
+
292
+ insertAnnotationAtIndex(annotation: INodeBase, index: number) {
293
+ this.annotationsValueManager.insertAtIndex(annotation, index);
294
+ }
295
+
296
+ moveAnnotation(oldIndex: number, newIndex: number) {
297
+ this.annotationsValueManager.move(oldIndex, newIndex);
298
+ }
299
+
300
+ replaceAnnotationAtIndex(annotation: INodeBase, index: number) {
301
+ this.annotationsValueManager.replaceAtIndex(annotation, index);
302
+ }
303
+
304
+ removeAnnotation(annotation: INodeBase) {
305
+ this.annotationsValueManager.remove(annotation);
306
+ }
307
+
308
+ }
309
+
310
+
311
+ /**
312
+ * A type for functions that acts as factories, creating an instance of {@link INodeBase}
313
+ * matching the given {@link Classifier classifier} and the given ID (of type {@link Id}).
314
+ */
315
+ export type NodeBaseFactory = (classifier: Classifier, id: Id) => INodeBase;
316
+
317
+ /**
318
+ * A type that captures three base aspects of a language:
319
+ * its {@link Language definition},
320
+ * a method to produce {@link NodeBaseFactory factories},
321
+ * and a method to produce a runtime representation of a given {@link EnumerationLiteral}.
322
+ */
323
+ export interface ILanguageBase {
324
+ language: Language;
325
+ factory(handleDelta?: DeltaHandler): NodeBaseFactory;
326
+ enumLiteralFrom<T>(enumerationLiteral: EnumerationLiteral): T;
327
+ }
328
+
329
+
330
+ /**
331
+ * Removes the given child node from its parent, and returns its containment index.
332
+ */
333
+ export const removeFromParent = (parent: INodeBase | undefined, child: INodeBase): number => {
334
+ if (parent === undefined) {
335
+ throw new Error(`can't remove an orphan from its parent`);
336
+ }
337
+ if (child.containment instanceof Containment) {
338
+ const valueManager = parent.getContainmentValueManager(child.containment);
339
+ if (valueManager instanceof SingleContainmentValueManager) {
340
+ valueManager.setDirectly(undefined);
341
+ return 0;
342
+ } else if (valueManager instanceof MultiContainmentValueManager) {
343
+ return valueManager.removeDirectly(child);
344
+ } else {
345
+ throw new Error(`don't know how to remove a child that's contained through a value manager of type ${valueManager.constructor.name}`);
346
+ }
347
+ }
348
+ if (child.containment === null) {
349
+ return parent.annotationsValueManager.removeDirectly(child);
350
+ }
351
+ throw new Error(`not going to remove a child from its parent without knowing how it's contained`);
352
+ }
353
+
@@ -0,0 +1,101 @@
1
+ // Copyright 2025 TRUMPF Laser SE and other contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License")
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // SPDX-FileCopyrightText: 2025 TRUMPF Laser SE and other contributors
16
+ // SPDX-License-Identifier: Apache-2.0
17
+
18
+ import {
19
+ Id,
20
+ incomingReferences as lwIncomingReferences,
21
+ ReferenceValue
22
+ } from "@lionweb/core";
23
+ import {deepDuplicateWith, DeltaHandler, IdMapping, ILanguageBase, INodeBase} from "./index.js";
24
+ import {nodeBaseReader} from "./serializer.js";
25
+ import {NodeDuplicator} from "./duplicator.js";
26
+
27
+
28
+ /**
29
+ * @return all descendant nodes from the given node, including that node itself.
30
+ */
31
+ export const allNodesFrom = (rootNode: INodeBase): INodeBase[] => {
32
+ const nodes: INodeBase[] = [rootNode];
33
+ const visit = (node: INodeBase) => {
34
+ const children = node.children;
35
+ nodes.push(...children);
36
+ children.forEach(visit);
37
+ }
38
+ visit(rootNode);
39
+ return nodes;
40
+ }
41
+
42
+
43
+ /**
44
+ * Finds all references coming into the given target node or any of the given target nodes,
45
+ * within the search scope (given as _root_ nodes),
46
+ * returning these as {@link ReferenceValue reference values}.
47
+ * Note that any reference is found uniquely,
48
+ * i.e. the returned {@link ReferenceValue reference values} are pairwise distinct,
49
+ * even if the given target nodes or scope contain duplicate nodes.
50
+ *
51
+ * @param targetNodeOrNodes one or more target {@link Node nodes} for which the incoming references are searched
52
+ * @param rootNodesInScope the {@link Node _root_ nodes} of the model that's searched for references
53
+ */
54
+ export const incomingReferences = (targetNodeOrNodes: INodeBase | INodeBase[], rootNodesInScope: INodeBase[]): ReferenceValue<INodeBase>[] =>
55
+ lwIncomingReferences(targetNodeOrNodes, rootNodesInScope.flatMap(allNodesFrom), nodeBaseReader);
56
+
57
+
58
+ /**
59
+ * @return a function that deep-clones the given nodes (of type {@link INodeBase}),
60
+ * *without* generating new IDs.
61
+ * @param languageBases the {@link ILanguageBase language bases} of the languages that the nodes might come from
62
+ * — these are necessary for their factories
63
+ * @param handleDelta an optional {@link DeltaHandler} that gets installed on the deep-cloned nodes
64
+ */
65
+ export const deepClonerFor = (languageBases: ILanguageBase[], handleDelta?: DeltaHandler) =>
66
+ deepDuplicatorFor(languageBases, (originalNode) => originalNode.id, handleDelta);
67
+
68
+
69
+ /**
70
+ * Type definition for functions that compute a – not necessarily new – ID from a given original {@link INodeBase node}.
71
+ */
72
+ export type NewIdFunction = (originalNode: INodeBase) => Id;
73
+
74
+
75
+ /**
76
+ * @return a {@link NodeDuplicator} function that does the “obvious” thing, given the parameters:
77
+ * @param languageBases an array of languages in the form of {@link ILanguageBase}s
78
+ * @param newIdFunc a function that computes a – not necessarily new – ID from a given original {@link INodeBase node}
79
+ * @param handleDelta an optional {@link DeltaHandler}
80
+ * @param idMapping an optional {@link IdMapping}
81
+ */
82
+ export const defaultNodeDuplicatorFor = (languageBases: ILanguageBase[], newIdFunc: NewIdFunction, handleDelta?: DeltaHandler, idMapping?: IdMapping): NodeDuplicator =>
83
+ (originalNode: INodeBase) => {
84
+ const languageBase = languageBases.find((languageBase) => languageBase.language === originalNode.classifier.language)!;
85
+ const duplicatedNode = languageBase.factory(handleDelta)(originalNode.classifier, newIdFunc(originalNode));
86
+ idMapping?.updateWith(duplicatedNode);
87
+ return [duplicatedNode, originalNode.setFeatures];
88
+ };
89
+
90
+
91
+ /**
92
+ * @return a function that deep-duplicates the given nodes (of type {@link INodeBase}),
93
+ * computing IDs using the `newIdFunc` function passed as the 2nd argument.
94
+ * @param languageBases the {@link ILanguageBase language bases} of the languages that the nodes might come from
95
+ * — these are necessary for their factories
96
+ * @param newIdFunc a function that computes a – not necessarily new – ID from a given original {@link INodeBase node}
97
+ * @param handleDelta an optional {@link DeltaHandler} that gets installed on the deep-cloned nodes
98
+ */
99
+ export const deepDuplicatorFor = (languageBases: ILanguageBase[], newIdFunc: NewIdFunction, handleDelta?: DeltaHandler, idMapping?: IdMapping) =>
100
+ deepDuplicateWith(defaultNodeDuplicatorFor(languageBases, newIdFunc, handleDelta, idMapping));
101
+