@api-client/core 0.11.7 → 0.11.9
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/build/src/browser.d.ts +2 -0
- package/build/src/browser.d.ts.map +1 -1
- package/build/src/browser.js +2 -0
- package/build/src/browser.js.map +1 -1
- package/build/src/index.d.ts +2 -0
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +2 -0
- package/build/src/index.js.map +1 -1
- package/build/src/modeling/DataAssociation.d.ts +8 -0
- package/build/src/modeling/DataAssociation.d.ts.map +1 -1
- package/build/src/modeling/DataAssociation.js +20 -0
- package/build/src/modeling/DataAssociation.js.map +1 -1
- package/build/src/modeling/DataEntity.d.ts +17 -1
- package/build/src/modeling/DataEntity.d.ts.map +1 -1
- package/build/src/modeling/DataEntity.js +48 -6
- package/build/src/modeling/DataEntity.js.map +1 -1
- package/build/src/modeling/DataModel.d.ts +10 -1
- package/build/src/modeling/DataModel.d.ts.map +1 -1
- package/build/src/modeling/DataModel.js +22 -2
- package/build/src/modeling/DataModel.js.map +1 -1
- package/build/src/modeling/DataNamespace.d.ts +60 -55
- package/build/src/modeling/DataNamespace.d.ts.map +1 -1
- package/build/src/modeling/DataNamespace.js +133 -116
- package/build/src/modeling/DataNamespace.js.map +1 -1
- package/build/src/modeling/DataProperty.d.ts +16 -3
- package/build/src/modeling/DataProperty.d.ts.map +1 -1
- package/build/src/modeling/DataProperty.js +28 -2
- package/build/src/modeling/DataProperty.js.map +1 -1
- package/build/src/modeling/DomainImpactAnalysis.d.ts +289 -0
- package/build/src/modeling/DomainImpactAnalysis.d.ts.map +1 -0
- package/build/src/modeling/DomainImpactAnalysis.js +437 -0
- package/build/src/modeling/DomainImpactAnalysis.js.map +1 -0
- package/build/src/modeling/types.d.ts +14 -0
- package/build/src/modeling/types.d.ts.map +1 -0
- package/build/src/modeling/types.js +2 -0
- package/build/src/modeling/types.js.map +1 -0
- package/data/models/example-generator-api.json +14 -14
- package/package.json +6 -6
- package/src/modeling/DataAssociation.ts +21 -0
- package/src/modeling/DataEntity.ts +59 -10
- package/src/modeling/DataModel.ts +24 -2
- package/src/modeling/DataNamespace.ts +150 -137
- package/src/modeling/DataProperty.ts +32 -3
- package/src/modeling/DomainImpactAnalysis.ts +519 -0
- package/src/modeling/types.ts +13 -0
- package/tests/servers/ExpressServer.ts +1 -0
- package/tests/servers/express-routes/BaseApi.ts +1 -1
- package/tests/servers/express-routes/TestsApi.ts +1 -1
- package/tests/unit/modeling/data_association.spec.ts +73 -0
- package/tests/unit/modeling/data_entity.spec.ts +111 -1
- package/tests/unit/modeling/data_model.spec.ts +54 -0
- package/tests/unit/modeling/data_namespace.spec.ts +46 -1
- package/tests/unit/modeling/data_property.spec.ts +73 -0
- package/tests/unit/modeling/impact_analysis.spec.ts +373 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { DataNamespaceKind, DataEntityKind, DataModelKind, DataPropertyKind, DataAssociationKind, } from '../models/kinds.js';
|
|
2
|
+
/**
|
|
3
|
+
* # DomainImpactAnalysis
|
|
4
|
+
*
|
|
5
|
+
* The `DomainImpactAnalysis` class is a powerful tool for analyzing the consequences of deleting data domain objects
|
|
6
|
+
* within a `DataNamespace`.
|
|
7
|
+
* It helps developers understand the ripple effects of removing a namespace, data model, entity, property,
|
|
8
|
+
* or association, ensuring data integrity and preventing unintended side effects.
|
|
9
|
+
*
|
|
10
|
+
* ## Core Concepts
|
|
11
|
+
*
|
|
12
|
+
* - **Impact Report:** The central output of the `DomainImpactAnalysis` class is an `DomainImpactReport`.
|
|
13
|
+
* This report details the potential consequences of a deletion operation, including:
|
|
14
|
+
* - The object being deleted (`key`, `kind`).
|
|
15
|
+
* - A list of `ImpactItem` objects, each describing a specific consequence.
|
|
16
|
+
* - Whether the deletion can proceed safely (`canProceed`).
|
|
17
|
+
*
|
|
18
|
+
* - **ImpactItem:** Each `ImpactItem` describes a specific consequence of the deletion. Key properties include:
|
|
19
|
+
* - `key`: The key of the impacted object.
|
|
20
|
+
* - `kind`: The kind of the impacted object.
|
|
21
|
+
* - `type`: The type of impact (currently only `delete`).
|
|
22
|
+
* - `impact`: A human-readable description of the impact.
|
|
23
|
+
* - `blocking`: Whether this impact prevents the deletion from proceeding.
|
|
24
|
+
* - `relationship`: The type of relationship between the deleted object and the impacted object (e.g., `child`).
|
|
25
|
+
* - `resolution`: A description of how the impact will be resolved if the deletion is forced.
|
|
26
|
+
*
|
|
27
|
+
* - **Blocking Impacts:** Some impacts are considered "blocking," meaning they prevent the deletion from proceeding
|
|
28
|
+
* without manual intervention. For example, deleting an entity that is a parent to other entities is
|
|
29
|
+
* a blocking impact.
|
|
30
|
+
*
|
|
31
|
+
* - **Non-Blocking Impacts:** Some impacts are informational and do not prevent the deletion. For example, deleting a
|
|
32
|
+
* property is not a blocking impact.
|
|
33
|
+
*
|
|
34
|
+
* ## Usage
|
|
35
|
+
*
|
|
36
|
+
* 1. **Instantiation:** Create an instance of `DomainImpactAnalysis`, passing the root `DataNamespace` as an argument.
|
|
37
|
+
*
|
|
38
|
+
* ```typescript
|
|
39
|
+
* import { DataNamespace } from './DataNamespace';
|
|
40
|
+
* import { DomainImpactAnalysis } from './DomainImpactAnalysis';
|
|
41
|
+
*
|
|
42
|
+
* const rootNamespace = new DataNamespace();
|
|
43
|
+
* // ... add some data to the namespace
|
|
44
|
+
* const analyzer = new DomainImpactAnalysis(rootNamespace);
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* 2. **Performing Analysis:** Use the `deleteAnalysis()` method to generate an `ImpactReport` for a specific deletion.
|
|
48
|
+
* Provide the `key` and `kind` of the object to be deleted.
|
|
49
|
+
*
|
|
50
|
+
* ```typescript
|
|
51
|
+
* import { DataEntityKind } from '../models/kinds.js';
|
|
52
|
+
* // ...
|
|
53
|
+
* const entityKey = 'some-entity-key';
|
|
54
|
+
* const report = analyzer.deleteAnalysis(entityKey, DataEntityKind);
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* 3. **Interpreting the Report:** Examine the `ImpactReport` to understand the consequences of the deletion.
|
|
58
|
+
* - Check `report.canProceed` to see if the deletion is safe.
|
|
59
|
+
* - Iterate through `report.impact` to understand each consequence.
|
|
60
|
+
* - Pay special attention to `impact.blocking` to identify impacts that require manual resolution.
|
|
61
|
+
*
|
|
62
|
+
* ```typescript
|
|
63
|
+
* if (report.canProceed) {
|
|
64
|
+
* // Proceed with deletion
|
|
65
|
+
* } else {
|
|
66
|
+
* console.warn('Deletion cannot proceed due to the following impacts:');
|
|
67
|
+
* report.impact.forEach((item) => {
|
|
68
|
+
* console.warn(`- ${item.impact}`);
|
|
69
|
+
* if (item.blocking) {
|
|
70
|
+
* console.warn(` - This impact is blocking.`);
|
|
71
|
+
* if (item.resolution) {
|
|
72
|
+
* console.warn(` - Resolution: ${item.resolution}`);
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* });
|
|
76
|
+
* // Handle blocking impacts (e.g., prompt the user, modify the data, etc.)
|
|
77
|
+
* }
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* ## Supported Deletion Scenarios
|
|
81
|
+
*
|
|
82
|
+
* The `DomainImpactAnalysis` class supports analyzing the deletion of the following data domain object types:
|
|
83
|
+
*
|
|
84
|
+
* - **Namespaces (`DataNamespaceKind`):** Deleting a namespace also impacts all its child namespaces,
|
|
85
|
+
* data models, entities, properties, and associations.
|
|
86
|
+
* - **Data Models (`DataModelKind`):** Deleting a data model impacts all its entities, properties, and associations.
|
|
87
|
+
* - **Entities (`DataEntityKind`):** Deleting an entity impacts its properties, associations, and any other
|
|
88
|
+
* entities that have it as a parent or target in an association.
|
|
89
|
+
* - **Properties (`DataPropertyKind`):** Deleting a property impacts the entity it belongs to.
|
|
90
|
+
* - **Associations (`DataAssociationKind`):** Deleting an association impacts the entity it belongs to.
|
|
91
|
+
*
|
|
92
|
+
* ## Example: Deleting an Entity
|
|
93
|
+
*
|
|
94
|
+
* Consider the following scenario:
|
|
95
|
+
*
|
|
96
|
+
* - Namespace: `MyNamespace`
|
|
97
|
+
* - Data Model: `ProductModel`
|
|
98
|
+
* - Entity: `Product`
|
|
99
|
+
* - Property: `name`
|
|
100
|
+
* - Association: `category` (targets `Category` entity)
|
|
101
|
+
* - Entity: `Category`
|
|
102
|
+
* - Property: `name`
|
|
103
|
+
* - Entity: `SpecialProduct` (parent: `Product`)
|
|
104
|
+
*
|
|
105
|
+
* If you attempt to delete the `Product` entity, the `DomainImpactAnalysis` will generate a report similar to this:
|
|
106
|
+
*
|
|
107
|
+
* ```json
|
|
108
|
+
* {
|
|
109
|
+
* "key": "Product",
|
|
110
|
+
* "kind": "DataEntityKind",
|
|
111
|
+
* "impact": [
|
|
112
|
+
* {
|
|
113
|
+
* "key": "Product",
|
|
114
|
+
* "kind": "DataEntityKind",
|
|
115
|
+
* "type": "delete",
|
|
116
|
+
* "impact": "The entity with key Product will be deleted.",
|
|
117
|
+
* "blocking": false
|
|
118
|
+
* },
|
|
119
|
+
* {
|
|
120
|
+
* "key": "SpecialProduct",
|
|
121
|
+
* "kind": "DataEntityKind",
|
|
122
|
+
* "type": "delete",
|
|
123
|
+
* "impact": "The SpecialProduct entity will become an orphan because it is a child of Product.",
|
|
124
|
+
* "resolution": "The Product will be removed as the parent parent of SpecialProduct.",
|
|
125
|
+
* "blocking": true,
|
|
126
|
+
* "relationship": "child"
|
|
127
|
+
* },
|
|
128
|
+
* {
|
|
129
|
+
* "key": "category",
|
|
130
|
+
* "kind": "DataAssociationKind",
|
|
131
|
+
* "type": "delete",
|
|
132
|
+
* "impact": "The association with key category will be broken because it has a target to Product.",
|
|
133
|
+
* "resolution": "The association with key category will be removed from Product.",
|
|
134
|
+
* "blocking": true
|
|
135
|
+
* },
|
|
136
|
+
* {
|
|
137
|
+
* "key": "name",
|
|
138
|
+
* "kind": "DataPropertyKind",
|
|
139
|
+
* "type": "delete",
|
|
140
|
+
* "impact": "The property with key name will be deleted.",
|
|
141
|
+
* "blocking": false
|
|
142
|
+
* }
|
|
143
|
+
* ],
|
|
144
|
+
* "canProceed": false
|
|
145
|
+
* }
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* This report indicates that:
|
|
149
|
+
*
|
|
150
|
+
* - The `Product` entity will be deleted.
|
|
151
|
+
* - The `SpecialProduct` entity will become an orphan (blocking).
|
|
152
|
+
* - The `category` association will be broken (blocking).
|
|
153
|
+
* - The `name` property will be deleted.
|
|
154
|
+
* - The deletion cannot proceed without addressing the blocking impacts.
|
|
155
|
+
*
|
|
156
|
+
* ## Types
|
|
157
|
+
*
|
|
158
|
+
* ### `ImpactKinds`
|
|
159
|
+
*
|
|
160
|
+
* - **Description:** A type alias for the kinds of data domain objects that can be analyzed.
|
|
161
|
+
* - **Values:**
|
|
162
|
+
* - `DataNamespaceKind`
|
|
163
|
+
* - `DataEntityKind`
|
|
164
|
+
* - `DataModelKind`
|
|
165
|
+
* - `DataPropertyKind`
|
|
166
|
+
* - `DataAssociationKind`
|
|
167
|
+
*
|
|
168
|
+
* ### `ImpactReport`
|
|
169
|
+
*
|
|
170
|
+
* - **Description:** The structure of the impact analysis report.
|
|
171
|
+
* - **Properties:**
|
|
172
|
+
* - `key` (`string`): The key of the object being deleted.
|
|
173
|
+
* - `kind` (`ImpactKinds`): The kind of the object being deleted.
|
|
174
|
+
* - `impact` (`ImpactItem[]`): The list of impacts.
|
|
175
|
+
* - `canProceed` (`boolean`): Whether the deletion can proceed.
|
|
176
|
+
*
|
|
177
|
+
* ### `ImpactItem`
|
|
178
|
+
*
|
|
179
|
+
* - **Description:** The structure of an individual impact item.
|
|
180
|
+
* - **Properties:**
|
|
181
|
+
* - `key` (`string`): The key of the impacted object.
|
|
182
|
+
* - `kind` (`string`): The kind of the impacted object.
|
|
183
|
+
* - `type` (`'delete'`): The type of impact.
|
|
184
|
+
* - `impact` (`string`): The impact description.
|
|
185
|
+
* - `blocking` (`boolean`): Whether the impact is blocking.
|
|
186
|
+
* - `relationship` (`'child'`, optional): The relationship type.
|
|
187
|
+
* - `resolution` (`string`, optional): The resolution description.
|
|
188
|
+
*
|
|
189
|
+
* ## Error Handling
|
|
190
|
+
*
|
|
191
|
+
* The `DomainImpactAnalysis` class does not throw errors. Instead, it uses the `ImpactReport` to communicate
|
|
192
|
+
* the results of the analysis, including any blocking impacts.
|
|
193
|
+
*
|
|
194
|
+
* ## Best Practices
|
|
195
|
+
*
|
|
196
|
+
* - **Always Analyze Before Deleting:** Before deleting any data domain object, always use `DomainImpactAnalysis`
|
|
197
|
+
* to understand the consequences.
|
|
198
|
+
* - **Handle Blocking Impacts:** Pay close attention to `blocking` impacts and implement appropriate
|
|
199
|
+
* logic to handle them.
|
|
200
|
+
* - **Inform the User:** If a deletion cannot proceed, inform the user about the blocking impacts and provide
|
|
201
|
+
* guidance on how to resolve them.
|
|
202
|
+
* - **Consider Forced Deletion:** In some cases, you may want to allow users to force a deletion even if there are
|
|
203
|
+
* blocking impacts. In such cases, you should clearly communicate the risks to the user.
|
|
204
|
+
*
|
|
205
|
+
* ## Conclusion
|
|
206
|
+
*
|
|
207
|
+
* The `DomainImpactAnalysis` class is an essential tool for maintaining data integrity when working with complex
|
|
208
|
+
* data domain models. By providing detailed impact reports, it empowers developers to make informed decisions
|
|
209
|
+
* about data deletion and prevent unintended consequences.
|
|
210
|
+
*/
|
|
211
|
+
export class DomainImpactAnalysis {
|
|
212
|
+
report;
|
|
213
|
+
root;
|
|
214
|
+
constructor(root) {
|
|
215
|
+
this.root = root;
|
|
216
|
+
this.report = {
|
|
217
|
+
key: '',
|
|
218
|
+
kind: DataNamespaceKind,
|
|
219
|
+
impact: [],
|
|
220
|
+
canProceed: false,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Generates a report of how the data domain will be impacted by the deletion of a data domain object.
|
|
225
|
+
* @param key The key of the impacted data domain object.
|
|
226
|
+
* @param kind The kind of the impacted data object.
|
|
227
|
+
* @returns The delete impact analysis report.
|
|
228
|
+
*/
|
|
229
|
+
deleteAnalysis(key, kind) {
|
|
230
|
+
this.report = {
|
|
231
|
+
key,
|
|
232
|
+
kind,
|
|
233
|
+
impact: [],
|
|
234
|
+
canProceed: true,
|
|
235
|
+
};
|
|
236
|
+
this.report.impact = this.createDeleteImpact(key, kind, key);
|
|
237
|
+
// this.report.impact.unshift({
|
|
238
|
+
// key,
|
|
239
|
+
// kind,
|
|
240
|
+
// type: 'delete',
|
|
241
|
+
// impact: `The ${this.kindToLabel(kind)} with key ${key} will be deleted.`,
|
|
242
|
+
// blocking: false,
|
|
243
|
+
// })
|
|
244
|
+
return this.report;
|
|
245
|
+
}
|
|
246
|
+
createDeleteImpact(key, kind, rootKey) {
|
|
247
|
+
switch (kind) {
|
|
248
|
+
case DataNamespaceKind:
|
|
249
|
+
return this.deleteNamespaceAnalysis(key, rootKey);
|
|
250
|
+
case DataModelKind:
|
|
251
|
+
return this.deleteDataModelAnalysis(key, rootKey);
|
|
252
|
+
case DataEntityKind:
|
|
253
|
+
return this.deleteEntityAnalysis(key, rootKey);
|
|
254
|
+
case DataPropertyKind:
|
|
255
|
+
return this.deletePropertyAnalysis(key);
|
|
256
|
+
case DataAssociationKind:
|
|
257
|
+
return this.deleteAssociationAnalysis(key);
|
|
258
|
+
default:
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
deleteNamespaceAnalysis(key, rootKey) {
|
|
263
|
+
const result = [];
|
|
264
|
+
const ns = this.root.findNamespace(key);
|
|
265
|
+
if (!ns) {
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
result.push({
|
|
269
|
+
key: ns.key,
|
|
270
|
+
kind: ns.kind,
|
|
271
|
+
type: 'delete',
|
|
272
|
+
impact: `The ${ns.info.renderLabel} ${this.kindToLabel(DataNamespaceKind)} will be deleted.`,
|
|
273
|
+
blocking: false,
|
|
274
|
+
});
|
|
275
|
+
const namespaces = ns.listNamespaces();
|
|
276
|
+
namespaces.forEach((child) => {
|
|
277
|
+
const items = this.deleteNamespaceAnalysis(child.key, rootKey);
|
|
278
|
+
result.push(...items);
|
|
279
|
+
});
|
|
280
|
+
const models = ns.listDataModels();
|
|
281
|
+
models.forEach((child) => {
|
|
282
|
+
const items = this.deleteDataModelAnalysis(child.key, rootKey);
|
|
283
|
+
result.push(...items);
|
|
284
|
+
});
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
deleteDataModelAnalysis(key, rootKey) {
|
|
288
|
+
const result = [];
|
|
289
|
+
const model = this.root.findDataModel(key);
|
|
290
|
+
if (!model) {
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
result.push({
|
|
294
|
+
key: model.key,
|
|
295
|
+
kind: model.kind,
|
|
296
|
+
type: 'delete',
|
|
297
|
+
impact: `The ${model.info.renderLabel} ${this.kindToLabel(DataModelKind)} will be deleted.`,
|
|
298
|
+
blocking: false,
|
|
299
|
+
});
|
|
300
|
+
model.entities.forEach((child) => {
|
|
301
|
+
const items = this.deleteEntityAnalysis(child.key, rootKey);
|
|
302
|
+
result.push(...items);
|
|
303
|
+
});
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
deleteEntityAnalysis(key, rootKey) {
|
|
307
|
+
const result = [];
|
|
308
|
+
const entity = this.root.findEntity(key);
|
|
309
|
+
if (!entity) {
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
result.push({
|
|
313
|
+
key: entity.key,
|
|
314
|
+
kind: entity.kind,
|
|
315
|
+
type: 'delete',
|
|
316
|
+
impact: `The ${entity.info.renderLabel} ${this.kindToLabel(DataEntityKind)} will be deleted.`,
|
|
317
|
+
blocking: false,
|
|
318
|
+
});
|
|
319
|
+
// We need to know whether the entity is a parent of another entity
|
|
320
|
+
const children = this.root.definitions.entities.filter((domainEntity) => {
|
|
321
|
+
if (domainEntity.key === entity.key) {
|
|
322
|
+
// ignore self
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
if (!domainEntity.parents.includes(entity.key)) {
|
|
326
|
+
// no relationship
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
if (domainEntity.isChildOf(rootKey)) {
|
|
330
|
+
// No need to include this parent as it itself is being deleted
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
// the entity has a parent-child relationship to the deleted entity.
|
|
334
|
+
return true;
|
|
335
|
+
});
|
|
336
|
+
if (children.length) {
|
|
337
|
+
children.forEach((child) => {
|
|
338
|
+
result.push({
|
|
339
|
+
key: child.key,
|
|
340
|
+
kind: child.kind,
|
|
341
|
+
type: 'delete',
|
|
342
|
+
impact: `The ${child.info.renderLabel} ${this.kindToLabel(DataEntityKind)} will become an orphan because it is a child of ${entity.info.renderLabel}.`,
|
|
343
|
+
resolution: `The ${entity.info.renderLabel} will be removed as the parent parent of ${child.info.renderLabel}.`,
|
|
344
|
+
blocking: true,
|
|
345
|
+
relationship: 'child',
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
this.report.canProceed = false;
|
|
349
|
+
}
|
|
350
|
+
// We need to know whether there's another entity that has an association to this entity.
|
|
351
|
+
const inAssociations = this.root.definitions.associations.filter((association) => {
|
|
352
|
+
return association.targets.some((item) => {
|
|
353
|
+
if (item.key === entity.key) {
|
|
354
|
+
const related = association.getParentInstance();
|
|
355
|
+
if (!related) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
if (related.isChildOf(rootKey)) {
|
|
359
|
+
// No need to include this association as the related entity itself is being deleted
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
if (inAssociations.length) {
|
|
368
|
+
inAssociations.forEach((association) => {
|
|
369
|
+
result.push({
|
|
370
|
+
key: association.key,
|
|
371
|
+
kind: association.kind,
|
|
372
|
+
type: 'delete',
|
|
373
|
+
impact: `The ${association.info.renderLabel} ${this.kindToLabel(DataAssociationKind)} will be broken because it has a target to ${entity.info.renderLabel}.`,
|
|
374
|
+
resolution: `The ${association.info.renderLabel} ${this.kindToLabel(DataAssociationKind)} will be removed from ${entity.info.renderLabel}.`,
|
|
375
|
+
blocking: true,
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
this.report.canProceed = false;
|
|
379
|
+
}
|
|
380
|
+
entity.properties.forEach((child) => {
|
|
381
|
+
const items = this.deletePropertyAnalysis(child.key);
|
|
382
|
+
result.push(...items);
|
|
383
|
+
});
|
|
384
|
+
entity.associations.forEach((child) => {
|
|
385
|
+
const items = this.deleteAssociationAnalysis(child.key);
|
|
386
|
+
result.push(...items);
|
|
387
|
+
});
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
deletePropertyAnalysis(key) {
|
|
391
|
+
const result = [];
|
|
392
|
+
const property = this.root.findProperty(key);
|
|
393
|
+
if (!property) {
|
|
394
|
+
return result;
|
|
395
|
+
}
|
|
396
|
+
result.push({
|
|
397
|
+
key: property.key,
|
|
398
|
+
kind: property.kind,
|
|
399
|
+
type: 'delete',
|
|
400
|
+
impact: `The ${property.info.renderLabel} ${this.kindToLabel(DataPropertyKind)} will be deleted.`,
|
|
401
|
+
blocking: false,
|
|
402
|
+
});
|
|
403
|
+
return result;
|
|
404
|
+
}
|
|
405
|
+
deleteAssociationAnalysis(key) {
|
|
406
|
+
const result = [];
|
|
407
|
+
const association = this.root.findAssociation(key);
|
|
408
|
+
if (!association) {
|
|
409
|
+
return result;
|
|
410
|
+
}
|
|
411
|
+
result.push({
|
|
412
|
+
key: association.key,
|
|
413
|
+
kind: association.kind,
|
|
414
|
+
type: 'delete',
|
|
415
|
+
impact: `The ${association.info.renderLabel} ${this.kindToLabel(DataAssociationKind)} will be deleted.`,
|
|
416
|
+
blocking: false,
|
|
417
|
+
});
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
kindToLabel(kind) {
|
|
421
|
+
switch (kind) {
|
|
422
|
+
case DataNamespaceKind:
|
|
423
|
+
return 'namespace';
|
|
424
|
+
case DataEntityKind:
|
|
425
|
+
return 'entity';
|
|
426
|
+
case DataModelKind:
|
|
427
|
+
return 'data model';
|
|
428
|
+
case DataPropertyKind:
|
|
429
|
+
return 'property';
|
|
430
|
+
case DataAssociationKind:
|
|
431
|
+
return 'association';
|
|
432
|
+
default:
|
|
433
|
+
return 'unknown';
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
//# sourceMappingURL=DomainImpactAnalysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DomainImpactAnalysis.js","sourceRoot":"","sources":["../../../src/modeling/DomainImpactAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DataNamespaceKind,\n DataEntityKind,\n DataModelKind,\n DataPropertyKind,\n DataAssociationKind,\n} from '../models/kinds.js'\nimport type { DataNamespace } from './DataNamespace.js'\n\nexport type DomainImpactKinds =\n | typeof DataNamespaceKind\n | typeof DataEntityKind\n | typeof DataModelKind\n | typeof DataPropertyKind\n | typeof DataAssociationKind\n\n/**\n * The impact analysis report\n */\nexport interface DomainImpactReport {\n /**\n * The key of the impacted data object.\n * This is the key of the object that is being changed.\n */\n key: string\n /**\n * The kind of the impacted data object.\n * This is the kind of the object that is being changed.\n */\n kind: DomainImpactKinds\n /**\n * The list of impacted data objects.\n */\n impact: DomainImpactItem[]\n /**\n * Whether it is possible to proceed with the change.\n * If the change is not possible, the reason will be in the impact list.\n */\n canProceed: boolean\n}\n\nexport interface DomainImpactItem {\n /**\n * The key of the impacted data object.\n */\n key: string\n /**\n * The kind of the impacted data object.\n */\n kind: string\n /**\n * The type of the impact.\n *\n * - `delete` - The data object would be deleted.\n */\n type: 'delete'\n /**\n * The impact description.\n */\n impact: string\n /**\n * Whether the impact is blocking the operation.\n * If true, the operation cannot proceed.\n */\n blocking: boolean\n /**\n * The type of the relationship between two impacted objects.\n */\n relationship?: 'child'\n /**\n * The resolution of the conflict if the change will be forced.\n */\n resolution?: string\n}\n\n/**\n * # DomainImpactAnalysis\n *\n * The `DomainImpactAnalysis` class is a powerful tool for analyzing the consequences of deleting data domain objects\n * within a `DataNamespace`.\n * It helps developers understand the ripple effects of removing a namespace, data model, entity, property,\n * or association, ensuring data integrity and preventing unintended side effects.\n *\n * ## Core Concepts\n *\n * - **Impact Report:** The central output of the `DomainImpactAnalysis` class is an `DomainImpactReport`.\n * This report details the potential consequences of a deletion operation, including:\n * - The object being deleted (`key`, `kind`).\n * - A list of `ImpactItem` objects, each describing a specific consequence.\n * - Whether the deletion can proceed safely (`canProceed`).\n *\n * - **ImpactItem:** Each `ImpactItem` describes a specific consequence of the deletion. Key properties include:\n * - `key`: The key of the impacted object.\n * - `kind`: The kind of the impacted object.\n * - `type`: The type of impact (currently only `delete`).\n * - `impact`: A human-readable description of the impact.\n * - `blocking`: Whether this impact prevents the deletion from proceeding.\n * - `relationship`: The type of relationship between the deleted object and the impacted object (e.g., `child`).\n * - `resolution`: A description of how the impact will be resolved if the deletion is forced.\n *\n * - **Blocking Impacts:** Some impacts are considered \"blocking,\" meaning they prevent the deletion from proceeding\n * without manual intervention. For example, deleting an entity that is a parent to other entities is\n * a blocking impact.\n *\n * - **Non-Blocking Impacts:** Some impacts are informational and do not prevent the deletion. For example, deleting a\n * property is not a blocking impact.\n *\n * ## Usage\n *\n * 1. **Instantiation:** Create an instance of `DomainImpactAnalysis`, passing the root `DataNamespace` as an argument.\n *\n * ```typescript\n * import { DataNamespace } from './DataNamespace';\n * import { DomainImpactAnalysis } from './DomainImpactAnalysis';\n *\n * const rootNamespace = new DataNamespace();\n * // ... add some data to the namespace\n * const analyzer = new DomainImpactAnalysis(rootNamespace);\n * ```\n *\n * 2. **Performing Analysis:** Use the `deleteAnalysis()` method to generate an `ImpactReport` for a specific deletion.\n * Provide the `key` and `kind` of the object to be deleted.\n *\n * ```typescript\n * import { DataEntityKind } from '../models/kinds.js';\n * // ...\n * const entityKey = 'some-entity-key';\n * const report = analyzer.deleteAnalysis(entityKey, DataEntityKind);\n * ```\n *\n * 3. **Interpreting the Report:** Examine the `ImpactReport` to understand the consequences of the deletion.\n * - Check `report.canProceed` to see if the deletion is safe.\n * - Iterate through `report.impact` to understand each consequence.\n * - Pay special attention to `impact.blocking` to identify impacts that require manual resolution.\n *\n * ```typescript\n * if (report.canProceed) {\n * // Proceed with deletion\n * } else {\n * console.warn('Deletion cannot proceed due to the following impacts:');\n * report.impact.forEach((item) => {\n * console.warn(`- ${item.impact}`);\n * if (item.blocking) {\n * console.warn(` - This impact is blocking.`);\n * if (item.resolution) {\n * console.warn(` - Resolution: ${item.resolution}`);\n * }\n * }\n * });\n * // Handle blocking impacts (e.g., prompt the user, modify the data, etc.)\n * }\n * ```\n *\n * ## Supported Deletion Scenarios\n *\n * The `DomainImpactAnalysis` class supports analyzing the deletion of the following data domain object types:\n *\n * - **Namespaces (`DataNamespaceKind`):** Deleting a namespace also impacts all its child namespaces,\n * data models, entities, properties, and associations.\n * - **Data Models (`DataModelKind`):** Deleting a data model impacts all its entities, properties, and associations.\n * - **Entities (`DataEntityKind`):** Deleting an entity impacts its properties, associations, and any other\n * entities that have it as a parent or target in an association.\n * - **Properties (`DataPropertyKind`):** Deleting a property impacts the entity it belongs to.\n * - **Associations (`DataAssociationKind`):** Deleting an association impacts the entity it belongs to.\n *\n * ## Example: Deleting an Entity\n *\n * Consider the following scenario:\n *\n * - Namespace: `MyNamespace`\n * - Data Model: `ProductModel`\n * - Entity: `Product`\n * - Property: `name`\n * - Association: `category` (targets `Category` entity)\n * - Entity: `Category`\n * - Property: `name`\n * - Entity: `SpecialProduct` (parent: `Product`)\n *\n * If you attempt to delete the `Product` entity, the `DomainImpactAnalysis` will generate a report similar to this:\n *\n * ```json\n * {\n * \"key\": \"Product\",\n * \"kind\": \"DataEntityKind\",\n * \"impact\": [\n * {\n * \"key\": \"Product\",\n * \"kind\": \"DataEntityKind\",\n * \"type\": \"delete\",\n * \"impact\": \"The entity with key Product will be deleted.\",\n * \"blocking\": false\n * },\n * {\n * \"key\": \"SpecialProduct\",\n * \"kind\": \"DataEntityKind\",\n * \"type\": \"delete\",\n * \"impact\": \"The SpecialProduct entity will become an orphan because it is a child of Product.\",\n * \"resolution\": \"The Product will be removed as the parent parent of SpecialProduct.\",\n * \"blocking\": true,\n * \"relationship\": \"child\"\n * },\n * {\n * \"key\": \"category\",\n * \"kind\": \"DataAssociationKind\",\n * \"type\": \"delete\",\n * \"impact\": \"The association with key category will be broken because it has a target to Product.\",\n * \"resolution\": \"The association with key category will be removed from Product.\",\n * \"blocking\": true\n * },\n * {\n * \"key\": \"name\",\n * \"kind\": \"DataPropertyKind\",\n * \"type\": \"delete\",\n * \"impact\": \"The property with key name will be deleted.\",\n * \"blocking\": false\n * }\n * ],\n * \"canProceed\": false\n * }\n * ```\n *\n * This report indicates that:\n *\n * - The `Product` entity will be deleted.\n * - The `SpecialProduct` entity will become an orphan (blocking).\n * - The `category` association will be broken (blocking).\n * - The `name` property will be deleted.\n * - The deletion cannot proceed without addressing the blocking impacts.\n *\n * ## Types\n *\n * ### `ImpactKinds`\n *\n * - **Description:** A type alias for the kinds of data domain objects that can be analyzed.\n * - **Values:**\n * - `DataNamespaceKind`\n * - `DataEntityKind`\n * - `DataModelKind`\n * - `DataPropertyKind`\n * - `DataAssociationKind`\n *\n * ### `ImpactReport`\n *\n * - **Description:** The structure of the impact analysis report.\n * - **Properties:**\n * - `key` (`string`): The key of the object being deleted.\n * - `kind` (`ImpactKinds`): The kind of the object being deleted.\n * - `impact` (`ImpactItem[]`): The list of impacts.\n * - `canProceed` (`boolean`): Whether the deletion can proceed.\n *\n * ### `ImpactItem`\n *\n * - **Description:** The structure of an individual impact item.\n * - **Properties:**\n * - `key` (`string`): The key of the impacted object.\n * - `kind` (`string`): The kind of the impacted object.\n * - `type` (`'delete'`): The type of impact.\n * - `impact` (`string`): The impact description.\n * - `blocking` (`boolean`): Whether the impact is blocking.\n * - `relationship` (`'child'`, optional): The relationship type.\n * - `resolution` (`string`, optional): The resolution description.\n *\n * ## Error Handling\n *\n * The `DomainImpactAnalysis` class does not throw errors. Instead, it uses the `ImpactReport` to communicate\n * the results of the analysis, including any blocking impacts.\n *\n * ## Best Practices\n *\n * - **Always Analyze Before Deleting:** Before deleting any data domain object, always use `DomainImpactAnalysis`\n * to understand the consequences.\n * - **Handle Blocking Impacts:** Pay close attention to `blocking` impacts and implement appropriate\n * logic to handle them.\n * - **Inform the User:** If a deletion cannot proceed, inform the user about the blocking impacts and provide\n * guidance on how to resolve them.\n * - **Consider Forced Deletion:** In some cases, you may want to allow users to force a deletion even if there are\n * blocking impacts. In such cases, you should clearly communicate the risks to the user.\n *\n * ## Conclusion\n *\n * The `DomainImpactAnalysis` class is an essential tool for maintaining data integrity when working with complex\n * data domain models. By providing detailed impact reports, it empowers developers to make informed decisions\n * about data deletion and prevent unintended consequences.\n */\nexport class DomainImpactAnalysis {\n private report: DomainImpactReport\n private root: DataNamespace\n\n constructor(root: DataNamespace) {\n this.root = root\n this.report = {\n key: '',\n kind: DataNamespaceKind,\n impact: [],\n canProceed: false,\n }\n }\n\n /**\n * Generates a report of how the data domain will be impacted by the deletion of a data domain object.\n * @param key The key of the impacted data domain object.\n * @param kind The kind of the impacted data object.\n * @returns The delete impact analysis report.\n */\n deleteAnalysis(key: string, kind: DomainImpactKinds): DomainImpactReport {\n this.report = {\n key,\n kind,\n impact: [],\n canProceed: true,\n }\n this.report.impact = this.createDeleteImpact(key, kind, key)\n // this.report.impact.unshift({\n // key,\n // kind,\n // type: 'delete',\n // impact: `The ${this.kindToLabel(kind)} with key ${key} will be deleted.`,\n // blocking: false,\n // })\n return this.report\n }\n\n protected createDeleteImpact(key: string, kind: DomainImpactKinds, rootKey: string): DomainImpactItem[] {\n switch (kind) {\n case DataNamespaceKind:\n return this.deleteNamespaceAnalysis(key, rootKey)\n case DataModelKind:\n return this.deleteDataModelAnalysis(key, rootKey)\n case DataEntityKind:\n return this.deleteEntityAnalysis(key, rootKey)\n case DataPropertyKind:\n return this.deletePropertyAnalysis(key)\n case DataAssociationKind:\n return this.deleteAssociationAnalysis(key)\n default:\n return []\n }\n }\n\n protected deleteNamespaceAnalysis(key: string, rootKey: string): DomainImpactItem[] {\n const result: DomainImpactItem[] = []\n const ns = this.root.findNamespace(key)\n if (!ns) {\n return result\n }\n result.push({\n key: ns.key,\n kind: ns.kind,\n type: 'delete',\n impact: `The ${ns.info.renderLabel} ${this.kindToLabel(DataNamespaceKind)} will be deleted.`,\n blocking: false,\n })\n const namespaces = ns.listNamespaces()\n namespaces.forEach((child) => {\n const items = this.deleteNamespaceAnalysis(child.key, rootKey)\n result.push(...items)\n })\n const models = ns.listDataModels()\n models.forEach((child) => {\n const items = this.deleteDataModelAnalysis(child.key, rootKey)\n result.push(...items)\n })\n return result\n }\n\n protected deleteDataModelAnalysis(key: string, rootKey: string): DomainImpactItem[] {\n const result: DomainImpactItem[] = []\n const model = this.root.findDataModel(key)\n if (!model) {\n return result\n }\n result.push({\n key: model.key,\n kind: model.kind,\n type: 'delete',\n impact: `The ${model.info.renderLabel} ${this.kindToLabel(DataModelKind)} will be deleted.`,\n blocking: false,\n })\n model.entities.forEach((child) => {\n const items = this.deleteEntityAnalysis(child.key, rootKey)\n result.push(...items)\n })\n return result\n }\n\n protected deleteEntityAnalysis(key: string, rootKey: string): DomainImpactItem[] {\n const result: DomainImpactItem[] = []\n const entity = this.root.findEntity(key)\n if (!entity) {\n return result\n }\n result.push({\n key: entity.key,\n kind: entity.kind,\n type: 'delete',\n impact: `The ${entity.info.renderLabel} ${this.kindToLabel(DataEntityKind)} will be deleted.`,\n blocking: false,\n })\n // We need to know whether the entity is a parent of another entity\n const children = this.root.definitions.entities.filter((domainEntity) => {\n if (domainEntity.key === entity.key) {\n // ignore self\n return false\n }\n if (!domainEntity.parents.includes(entity.key)) {\n // no relationship\n return false\n }\n if (domainEntity.isChildOf(rootKey)) {\n // No need to include this parent as it itself is being deleted\n return false\n }\n // the entity has a parent-child relationship to the deleted entity.\n return true\n })\n if (children.length) {\n children.forEach((child) => {\n result.push({\n key: child.key,\n kind: child.kind,\n type: 'delete',\n impact: `The ${child.info.renderLabel} ${this.kindToLabel(DataEntityKind)} will become an orphan because it is a child of ${entity.info.renderLabel}.`,\n resolution: `The ${entity.info.renderLabel} will be removed as the parent parent of ${child.info.renderLabel}.`,\n blocking: true,\n relationship: 'child',\n })\n })\n this.report.canProceed = false\n }\n // We need to know whether there's another entity that has an association to this entity.\n const inAssociations = this.root.definitions.associations.filter((association) => {\n return association.targets.some((item) => {\n if (item.key === entity.key) {\n const related = association.getParentInstance()\n if (!related) {\n return false\n }\n if (related.isChildOf(rootKey)) {\n // No need to include this association as the related entity itself is being deleted\n return false\n }\n return true\n }\n return false\n })\n })\n if (inAssociations.length) {\n inAssociations.forEach((association) => {\n result.push({\n key: association.key,\n kind: association.kind,\n type: 'delete',\n impact: `The ${association.info.renderLabel} ${this.kindToLabel(DataAssociationKind)} will be broken because it has a target to ${entity.info.renderLabel}.`,\n resolution: `The ${association.info.renderLabel} ${this.kindToLabel(DataAssociationKind)} will be removed from ${entity.info.renderLabel}.`,\n blocking: true,\n })\n })\n this.report.canProceed = false\n }\n entity.properties.forEach((child) => {\n const items = this.deletePropertyAnalysis(child.key)\n result.push(...items)\n })\n entity.associations.forEach((child) => {\n const items = this.deleteAssociationAnalysis(child.key)\n result.push(...items)\n })\n return result\n }\n\n protected deletePropertyAnalysis(key: string): DomainImpactItem[] {\n const result: DomainImpactItem[] = []\n const property = this.root.findProperty(key)\n if (!property) {\n return result\n }\n result.push({\n key: property.key,\n kind: property.kind,\n type: 'delete',\n impact: `The ${property.info.renderLabel} ${this.kindToLabel(DataPropertyKind)} will be deleted.`,\n blocking: false,\n })\n return result\n }\n\n protected deleteAssociationAnalysis(key: string): DomainImpactItem[] {\n const result: DomainImpactItem[] = []\n const association = this.root.findAssociation(key)\n if (!association) {\n return result\n }\n result.push({\n key: association.key,\n kind: association.kind,\n type: 'delete',\n impact: `The ${association.info.renderLabel} ${this.kindToLabel(DataAssociationKind)} will be deleted.`,\n blocking: false,\n })\n return result\n }\n\n protected kindToLabel(kind: DomainImpactKinds): string {\n switch (kind) {\n case DataNamespaceKind:\n return 'namespace'\n case DataEntityKind:\n return 'entity'\n case DataModelKind:\n return 'data model'\n case DataPropertyKind:\n return 'property'\n case DataAssociationKind:\n return 'association'\n default:\n return 'unknown'\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface DataDomainRemoveOptions {
|
|
2
|
+
/**
|
|
3
|
+
* When true, the object will be forcebly removed.
|
|
4
|
+
* The resolution defined in the `ImpactResolution` class will be applied.
|
|
5
|
+
*
|
|
6
|
+
* For example, when removing an entity that is a parent to another entity, it will
|
|
7
|
+
* removed itself as a parent from the child entity.
|
|
8
|
+
*
|
|
9
|
+
* Note, this option should only be used when the DomainImpactAnalysis has been performed
|
|
10
|
+
* and the user was informed of the impact.
|
|
11
|
+
*/
|
|
12
|
+
force?: boolean;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/modeling/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IACtC;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/modeling/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface DataDomainRemoveOptions {\n /**\n * When true, the object will be forcebly removed.\n * The resolution defined in the `ImpactResolution` class will be applied.\n *\n * For example, when removing an entity that is a parent to another entity, it will\n * removed itself as a parent from the child entity.\n *\n * Note, this option should only be used when the DomainImpactAnalysis has been performed\n * and the user was informed of the impact.\n */\n force?: boolean\n}\n"]}
|
|
@@ -41725,21 +41725,21 @@
|
|
|
41725
41725
|
"@id": "#191"
|
|
41726
41726
|
},
|
|
41727
41727
|
{
|
|
41728
|
+
"@id": "#200"
|
|
41729
|
+
},
|
|
41730
|
+
{
|
|
41728
41731
|
"@id": "#194"
|
|
41729
41732
|
},
|
|
41730
41733
|
{
|
|
41731
41734
|
"@id": "#197"
|
|
41732
41735
|
},
|
|
41733
41736
|
{
|
|
41734
|
-
"@id": "#
|
|
41737
|
+
"@id": "#206"
|
|
41735
41738
|
},
|
|
41736
41739
|
{
|
|
41737
41740
|
"@id": "#203"
|
|
41738
41741
|
},
|
|
41739
41742
|
{
|
|
41740
|
-
"@id": "#206"
|
|
41741
|
-
},
|
|
41742
|
-
{
|
|
41743
41743
|
"@id": "#209"
|
|
41744
41744
|
}
|
|
41745
41745
|
],
|
|
@@ -43117,7 +43117,7 @@
|
|
|
43117
43117
|
"doc:ExternalDomainElement",
|
|
43118
43118
|
"doc:DomainElement"
|
|
43119
43119
|
],
|
|
43120
|
-
"doc:raw": "
|
|
43120
|
+
"doc:raw": "code: '5'\ndescription: 'Limited company'\n",
|
|
43121
43121
|
"core:mediaType": "application/yaml",
|
|
43122
43122
|
"sourcemaps:sources": [
|
|
43123
43123
|
{
|
|
@@ -43138,7 +43138,7 @@
|
|
|
43138
43138
|
"doc:ExternalDomainElement",
|
|
43139
43139
|
"doc:DomainElement"
|
|
43140
43140
|
],
|
|
43141
|
-
"doc:raw": "
|
|
43141
|
+
"doc:raw": "class: '3'\ndescription: '150 - 300'\nnumberOfFte: 5500\nnumberOfEmployees: 5232\n",
|
|
43142
43142
|
"core:mediaType": "application/yaml",
|
|
43143
43143
|
"sourcemaps:sources": [
|
|
43144
43144
|
{
|
|
@@ -43159,7 +43159,7 @@
|
|
|
43159
43159
|
"doc:ExternalDomainElement",
|
|
43160
43160
|
"doc:DomainElement"
|
|
43161
43161
|
],
|
|
43162
|
-
"doc:raw": "
|
|
43162
|
+
"doc:raw": "addressType: 'REGISTERED-OFFICE-ADDRESS'\nstreetName: 'UITBREIDINGSTRAAT'\nhouseNumber: '84'\nhouseNumberAddition: '/1'\npostalCode: '2600'\ncity: 'BERCHEM (ANTWERPEN)'\ncountry: 'Belgium'\ncountryCode: 'BE'\nfullFormatedAddress: \"UITBREIDINGSTRAAT 84 /1, 2600 BERCHEM (ANTWERPEN), BELIUM\"\n",
|
|
43163
43163
|
"core:mediaType": "application/yaml",
|
|
43164
43164
|
"sourcemaps:sources": [
|
|
43165
43165
|
{
|
|
@@ -43180,7 +43180,7 @@
|
|
|
43180
43180
|
"doc:ExternalDomainElement",
|
|
43181
43181
|
"doc:DomainElement"
|
|
43182
43182
|
],
|
|
43183
|
-
"doc:raw": "code: '
|
|
43183
|
+
"doc:raw": "code: '7487'\ndescription: 'Financial and insurance activities'\ntype: \"PRIMARY\"\nclassificationCode: 'BE_NACEBEL2008'\nactivityGroupCode: 'ABCDE'\n",
|
|
43184
43184
|
"core:mediaType": "application/yaml",
|
|
43185
43185
|
"sourcemaps:sources": [
|
|
43186
43186
|
{
|
|
@@ -43201,7 +43201,7 @@
|
|
|
43201
43201
|
"doc:ExternalDomainElement",
|
|
43202
43202
|
"doc:DomainElement"
|
|
43203
43203
|
],
|
|
43204
|
-
"doc:raw": "code: '
|
|
43204
|
+
"doc:raw": "code: 'J'\ndescription: 'Information and communication'\n",
|
|
43205
43205
|
"core:mediaType": "application/yaml",
|
|
43206
43206
|
"sourcemaps:sources": [
|
|
43207
43207
|
{
|
|
@@ -44421,27 +44421,27 @@
|
|
|
44421
44421
|
{
|
|
44422
44422
|
"@id": "#196/source-map/lexical/element_0",
|
|
44423
44423
|
"sourcemaps:element": "amf://id#196",
|
|
44424
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44424
|
+
"sourcemaps:value": "[(1,0)-(3,0)]"
|
|
44425
44425
|
},
|
|
44426
44426
|
{
|
|
44427
44427
|
"@id": "#199/source-map/lexical/element_0",
|
|
44428
44428
|
"sourcemaps:element": "amf://id#199",
|
|
44429
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44429
|
+
"sourcemaps:value": "[(1,0)-(5,0)]"
|
|
44430
44430
|
},
|
|
44431
44431
|
{
|
|
44432
44432
|
"@id": "#202/source-map/lexical/element_0",
|
|
44433
44433
|
"sourcemaps:element": "amf://id#202",
|
|
44434
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44434
|
+
"sourcemaps:value": "[(1,0)-(10,0)]"
|
|
44435
44435
|
},
|
|
44436
44436
|
{
|
|
44437
44437
|
"@id": "#205/source-map/lexical/element_0",
|
|
44438
44438
|
"sourcemaps:element": "amf://id#205",
|
|
44439
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44439
|
+
"sourcemaps:value": "[(1,0)-(6,0)]"
|
|
44440
44440
|
},
|
|
44441
44441
|
{
|
|
44442
44442
|
"@id": "#208/source-map/lexical/element_0",
|
|
44443
44443
|
"sourcemaps:element": "amf://id#208",
|
|
44444
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44444
|
+
"sourcemaps:value": "[(1,0)-(3,0)]"
|
|
44445
44445
|
},
|
|
44446
44446
|
{
|
|
44447
44447
|
"@id": "#223/source-map/lexical/element_0",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@api-client/core",
|
|
3
3
|
"description": "The API Client's core client library. Works in NodeJS and in a ES enabled browser.",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.9",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./browser.js": {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"#proxy/*": "./src/proxy/*",
|
|
32
32
|
"#ui/*": "./src/ui/*"
|
|
33
33
|
},
|
|
34
|
-
"main": "./build/index.js",
|
|
35
|
-
"browser": "./build/browser.js",
|
|
36
|
-
"module": "./build/index.js",
|
|
34
|
+
"main": "./build/src/index.js",
|
|
35
|
+
"browser": "./build/src/browser.js",
|
|
36
|
+
"module": "./build/src/index.js",
|
|
37
37
|
"type": "module",
|
|
38
38
|
"author": {
|
|
39
39
|
"name": "Pawel Uchida-Psztyc"
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"eslint-config-prettier": "^10.0.1",
|
|
89
89
|
"eslint-plugin-no-only-tests": "^3.3.0",
|
|
90
90
|
"eslint-plugin-prettier": "^5.2.3",
|
|
91
|
-
"express": "^
|
|
91
|
+
"express": "^5.1.0",
|
|
92
92
|
"express-ntlm": "^2.6.1",
|
|
93
93
|
"get-port": "^7.0.0",
|
|
94
94
|
"globals": "^16.0.0",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"test:browser:watch": "node --import ts-node-maintained/register/esm --enable-source-maps bin/test-web.ts --watch --playwright --browsers chromium",
|
|
121
121
|
"test": "wireit",
|
|
122
122
|
"test:coverage": "wireit",
|
|
123
|
-
"test:node": "node --import ts-node-maintained/register/esm bin/test.ts",
|
|
123
|
+
"test:node": "node --import ts-node-maintained/register/esm --enable-source-maps bin/test.ts",
|
|
124
124
|
"test:node:coverage": "c8 --reporter lcov --reporter text node --import ts-node-maintained/register/esm --enable-source-maps bin/test.ts",
|
|
125
125
|
"test:node:watch": "node --import ts-node-maintained/register/esm --enable-source-maps bin/test.ts --watch",
|
|
126
126
|
"build:api-models": "node data/model.js",
|