@memberjunction/core 3.3.0 → 4.0.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/__tests__/mocks/TestMetadataProvider.d.ts +45 -0
- package/dist/__tests__/mocks/TestMetadataProvider.d.ts.map +1 -0
- package/dist/__tests__/mocks/TestMetadataProvider.js +217 -0
- package/dist/__tests__/mocks/TestMetadataProvider.js.map +1 -0
- package/dist/__tests__/providerBase.concurrency.test.d.ts +10 -0
- package/dist/__tests__/providerBase.concurrency.test.d.ts.map +1 -0
- package/dist/__tests__/providerBase.concurrency.test.js +253 -0
- package/dist/__tests__/providerBase.concurrency.test.js.map +1 -0
- package/dist/__tests__/providerBase.refresh.test.d.ts +10 -0
- package/dist/__tests__/providerBase.refresh.test.d.ts.map +1 -0
- package/dist/__tests__/providerBase.refresh.test.js +161 -0
- package/dist/__tests__/providerBase.refresh.test.js.map +1 -0
- package/dist/__tests__/setup.d.ts +5 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/setup.js +17 -0
- package/dist/__tests__/setup.js.map +1 -0
- package/dist/generic/InMemoryLocalStorageProvider.d.ts +1 -1
- package/dist/generic/InMemoryLocalStorageProvider.js +2 -6
- package/dist/generic/InMemoryLocalStorageProvider.js.map +1 -1
- package/dist/generic/QueryCache.d.ts +1 -1
- package/dist/generic/QueryCache.js +6 -10
- package/dist/generic/QueryCache.js.map +1 -1
- package/dist/generic/QueryCacheConfig.js +1 -2
- package/dist/generic/RegisterForStartup.d.ts +2 -2
- package/dist/generic/RegisterForStartup.js +7 -12
- package/dist/generic/RegisterForStartup.js.map +1 -1
- package/dist/generic/applicationInfo.d.ts +3 -3
- package/dist/generic/applicationInfo.js +4 -10
- package/dist/generic/applicationInfo.js.map +1 -1
- package/dist/generic/authEvaluator.d.ts +1 -1
- package/dist/generic/authEvaluator.js +4 -8
- package/dist/generic/authEvaluator.js.map +1 -1
- package/dist/generic/authTypes.js +1 -4
- package/dist/generic/authTypes.js.map +1 -1
- package/dist/generic/baseEngine.d.ts +5 -5
- package/dist/generic/baseEngine.js +51 -56
- package/dist/generic/baseEngine.js.map +1 -1
- package/dist/generic/baseEngineRegistry.js +13 -17
- package/dist/generic/baseEngineRegistry.js.map +1 -1
- package/dist/generic/baseEntity.d.ts +5 -5
- package/dist/generic/baseEntity.js +103 -113
- package/dist/generic/baseEntity.js.map +1 -1
- package/dist/generic/baseInfo.js +3 -7
- package/dist/generic/baseInfo.js.map +1 -1
- package/dist/generic/compositeKey.d.ts +2 -2
- package/dist/generic/compositeKey.js +5 -11
- package/dist/generic/compositeKey.js.map +1 -1
- package/dist/generic/databaseProviderBase.d.ts +2 -2
- package/dist/generic/databaseProviderBase.js +2 -6
- package/dist/generic/databaseProviderBase.js.map +1 -1
- package/dist/generic/entityInfo.d.ts +74 -5
- package/dist/generic/entityInfo.d.ts.map +1 -1
- package/dist/generic/entityInfo.js +138 -105
- package/dist/generic/entityInfo.js.map +1 -1
- package/dist/generic/explorerNavigationItem.d.ts +1 -1
- package/dist/generic/explorerNavigationItem.js +2 -6
- package/dist/generic/explorerNavigationItem.js.map +1 -1
- package/dist/generic/graphqlTypeNames.d.ts +1 -1
- package/dist/generic/graphqlTypeNames.js +4 -9
- package/dist/generic/graphqlTypeNames.js.map +1 -1
- package/dist/generic/interfaces.d.ts +51 -12
- package/dist/generic/interfaces.d.ts.map +1 -1
- package/dist/generic/interfaces.js +15 -30
- package/dist/generic/interfaces.js.map +1 -1
- package/dist/generic/libraryInfo.d.ts +1 -1
- package/dist/generic/libraryInfo.js +2 -6
- package/dist/generic/libraryInfo.js.map +1 -1
- package/dist/generic/localCacheManager.d.ts +2 -2
- package/dist/generic/localCacheManager.js +44 -48
- package/dist/generic/localCacheManager.js.map +1 -1
- package/dist/generic/logging.d.ts.map +1 -1
- package/dist/generic/logging.js +54 -67
- package/dist/generic/logging.js.map +1 -1
- package/dist/generic/metadata.d.ts +10 -10
- package/dist/generic/metadata.js +17 -21
- package/dist/generic/metadata.js.map +1 -1
- package/dist/generic/metadataUtil.d.ts +1 -1
- package/dist/generic/metadataUtil.js +3 -7
- package/dist/generic/metadataUtil.js.map +1 -1
- package/dist/generic/providerBase.d.ts +18 -12
- package/dist/generic/providerBase.d.ts.map +1 -1
- package/dist/generic/providerBase.js +130 -130
- package/dist/generic/providerBase.js.map +1 -1
- package/dist/generic/queryInfo.d.ts +5 -5
- package/dist/generic/queryInfo.js +21 -30
- package/dist/generic/queryInfo.js.map +1 -1
- package/dist/generic/queryInfoInterfaces.js +1 -2
- package/dist/generic/queryInfoInterfaces.js.map +1 -1
- package/dist/generic/querySQLFilters.js +5 -10
- package/dist/generic/querySQLFilters.js.map +1 -1
- package/dist/generic/runQuery.d.ts +2 -2
- package/dist/generic/runQuery.js +5 -9
- package/dist/generic/runQuery.js.map +1 -1
- package/dist/generic/runQuerySQLFilterImplementations.d.ts +1 -1
- package/dist/generic/runQuerySQLFilterImplementations.js +4 -8
- package/dist/generic/runQuerySQLFilterImplementations.js.map +1 -1
- package/dist/generic/runReport.d.ts +2 -2
- package/dist/generic/runReport.js +5 -9
- package/dist/generic/runReport.js.map +1 -1
- package/dist/generic/securityInfo.d.ts +2 -2
- package/dist/generic/securityInfo.js +10 -20
- package/dist/generic/securityInfo.js.map +1 -1
- package/dist/generic/telemetryManager.js +20 -32
- package/dist/generic/telemetryManager.js.map +1 -1
- package/dist/generic/transactionGroup.d.ts +1 -1
- package/dist/generic/transactionGroup.d.ts.map +1 -1
- package/dist/generic/transactionGroup.js +11 -19
- package/dist/generic/transactionGroup.js.map +1 -1
- package/dist/generic/util.js +15 -31
- package/dist/generic/util.js.map +1 -1
- package/dist/index.d.ts +34 -34
- package/dist/index.js +45 -63
- package/dist/index.js.map +1 -1
- package/dist/views/runView.d.ts +3 -3
- package/dist/views/runView.js +6 -11
- package/dist/views/runView.js.map +1 -1
- package/dist/views/viewInfo.d.ts +3 -3
- package/dist/views/viewInfo.js +10 -17
- package/dist/views/viewInfo.js.map +1 -1
- package/package.json +11 -10
|
@@ -1,19 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const logging_1 = require("./logging");
|
|
10
|
-
const compositeKey_1 = require("./compositeKey");
|
|
11
|
-
const rxjs_1 = require("rxjs");
|
|
1
|
+
import { MJEventType, MJGlobal, uuidv4, WarningManager } from '@memberjunction/global';
|
|
2
|
+
import { EntityFieldInfo, EntityInfo, EntityFieldTSType, EntityPermissionType, RecordChange, ValidationErrorInfo, ValidationResult } from './entityInfo.js';
|
|
3
|
+
import { EntitySaveOptions } from './interfaces.js';
|
|
4
|
+
import { Metadata } from './metadata.js';
|
|
5
|
+
import { RunView } from '../views/runView.js';
|
|
6
|
+
import { LogDebug, LogError } from './logging.js';
|
|
7
|
+
import { CompositeKey } from './compositeKey.js';
|
|
8
|
+
import { finalize, firstValueFrom, from, of, shareReplay, Subject, switchMap } from 'rxjs';
|
|
12
9
|
/**
|
|
13
10
|
* Represents a field in an instance of the BaseEntity class. This class is used to store the value of the field, dirty state, as well as other run-time information about the field. The class encapsulates the underlying field metadata and exposes some of the more commonly
|
|
14
11
|
* used properties from the entity field metadata.
|
|
15
12
|
*/
|
|
16
|
-
class EntityField {
|
|
13
|
+
export class EntityField {
|
|
14
|
+
/**
|
|
15
|
+
* Static object containing the value ranges for various SQL number types.
|
|
16
|
+
* This is used to validate the value of the field when it is set or validated.
|
|
17
|
+
*/
|
|
18
|
+
static { this.SQLTypeValueRanges = {
|
|
19
|
+
"int": { min: -2147483648, max: 2147483647 },
|
|
20
|
+
"bigint": { min: -9223372036854775808, max: 9223372036854775807 },
|
|
21
|
+
"smallint": { min: -32768, max: 32767 },
|
|
22
|
+
"tinyint": { min: 0, max: 255 },
|
|
23
|
+
"decimal": { min: -7922816251426433759354395033, max: 79228162514264337593543950335 },
|
|
24
|
+
"numeric": { min: -7922816251426433759354395033, max: 79228162514264337593543950335 },
|
|
25
|
+
"float": { min: -1.7976931348623157e+308, max: 1.7976931348623157e+308 },
|
|
26
|
+
"real": { min: -3.402823466e+38, max: 3.402823466e+38 },
|
|
27
|
+
"money": { min: -922337203685477.5808, max: 922337203685477.5807 },
|
|
28
|
+
}; }
|
|
17
29
|
get Name() {
|
|
18
30
|
return this._entityFieldInfo.Name;
|
|
19
31
|
}
|
|
@@ -45,7 +57,7 @@ class EntityField {
|
|
|
45
57
|
// Asserting status here for deprecated or disabled fields, not in constructor because
|
|
46
58
|
// we legacy fields will exist
|
|
47
59
|
if (this._assertActiveStatusRequired) {
|
|
48
|
-
|
|
60
|
+
EntityFieldInfo.AssertEntityFieldActiveStatus(this._entityFieldInfo, 'EntityField.Value setter');
|
|
49
61
|
}
|
|
50
62
|
return this._Value;
|
|
51
63
|
}
|
|
@@ -75,7 +87,7 @@ class EntityField {
|
|
|
75
87
|
// asserting status here becuase the flag is on AND the values
|
|
76
88
|
// are different - this avoid assertions during sysops like SetMany that often aren't changing
|
|
77
89
|
// the value of the field
|
|
78
|
-
|
|
90
|
+
EntityFieldInfo.AssertEntityFieldActiveStatus(this._entityFieldInfo, 'EntityField.Value setter');
|
|
79
91
|
}
|
|
80
92
|
if (!this.ReadOnly ||
|
|
81
93
|
this._NeverSet /* Allow one time set of any field because BaseEntity Object passes in ReadOnly fields when we load,
|
|
@@ -121,7 +133,7 @@ class EntityField {
|
|
|
121
133
|
newCompare = this.Value.getTime();
|
|
122
134
|
}
|
|
123
135
|
// Special handling for bit/Boolean types - treat truthy values as equivalent
|
|
124
|
-
if (this._entityFieldInfo.TSType ===
|
|
136
|
+
if (this._entityFieldInfo.TSType === EntityFieldTSType.Boolean ||
|
|
125
137
|
this._entityFieldInfo.Type.toLowerCase() === 'bit') {
|
|
126
138
|
// Convert both values to boolean for comparison
|
|
127
139
|
const oldBool = this.convertToBoolean(oldCompare);
|
|
@@ -129,7 +141,7 @@ class EntityField {
|
|
|
129
141
|
return oldBool !== newBool;
|
|
130
142
|
}
|
|
131
143
|
// Special handling for numeric types - treat numeric strings that convert to same value as equivalent
|
|
132
|
-
if (this._entityFieldInfo.TSType ===
|
|
144
|
+
if (this._entityFieldInfo.TSType === EntityFieldTSType.Number || this.isNumericType(this._entityFieldInfo.Type)) {
|
|
133
145
|
const oldNum = this.convertToNumber(oldCompare);
|
|
134
146
|
const newNum = this.convertToNumber(newCompare);
|
|
135
147
|
// Handle NaN cases - if both are NaN, they're equivalent
|
|
@@ -143,7 +155,7 @@ class EntityField {
|
|
|
143
155
|
return oldNum !== newNum;
|
|
144
156
|
}
|
|
145
157
|
// for string types where the comparisons are not both strings
|
|
146
|
-
if (this._entityFieldInfo.TSType ===
|
|
158
|
+
if (this._entityFieldInfo.TSType === EntityFieldTSType.String) {
|
|
147
159
|
if (typeof oldCompare === 'object') {
|
|
148
160
|
// need to convert the object to a string for comparison
|
|
149
161
|
oldCompare = JSON.stringify(oldCompare);
|
|
@@ -228,7 +240,7 @@ class EntityField {
|
|
|
228
240
|
*/
|
|
229
241
|
Validate() {
|
|
230
242
|
const ef = this._entityFieldInfo;
|
|
231
|
-
const result = new
|
|
243
|
+
const result = new ValidationResult();
|
|
232
244
|
result.Success = true; // assume success
|
|
233
245
|
if (!ef.ReadOnly && !ef.SkipValidation) {
|
|
234
246
|
// only do validation on updatable fields and skip the special case fields defined inside the SkipValidation property (like ID/CreatedAt/UpdatedAt)
|
|
@@ -237,24 +249,24 @@ class EntityField {
|
|
|
237
249
|
if (ef.DefaultValue === null || ef.DefaultValue === undefined || ef.DefaultValue.trim().length === 0) {
|
|
238
250
|
// we have no default value, so this is an error
|
|
239
251
|
result.Success = false;
|
|
240
|
-
result.Errors.push(new
|
|
252
|
+
result.Errors.push(new ValidationErrorInfo(ef.Name, `${ef.DisplayNameOrName} cannot be null`, null));
|
|
241
253
|
}
|
|
242
254
|
else {
|
|
243
255
|
// we do have a default value, but our current value is null. If we are in an EXISTING record, this is an error, check the OldValue to determine this
|
|
244
256
|
if (this._OldValue !== null && this._OldValue !== undefined) {
|
|
245
257
|
result.Success = false;
|
|
246
|
-
result.Errors.push(new
|
|
258
|
+
result.Errors.push(new ValidationErrorInfo(ef.Name, `${ef.DisplayNameOrName} cannot be null`, null));
|
|
247
259
|
}
|
|
248
260
|
}
|
|
249
261
|
}
|
|
250
|
-
if (ef.TSType ==
|
|
262
|
+
if (ef.TSType == EntityFieldTSType.String && ef.MaxLength > 0 && this.Value && this.Value.length > ef.MaxLength) {
|
|
251
263
|
result.Success = false;
|
|
252
|
-
result.Errors.push(new
|
|
264
|
+
result.Errors.push(new ValidationErrorInfo(ef.Name, `${ef.DisplayNameOrName} cannot be longer than ${ef.MaxLength} characters. Current value is ${this.Value.length} characters`, this.Value));
|
|
253
265
|
}
|
|
254
|
-
if (ef.TSType ==
|
|
266
|
+
if (ef.TSType == EntityFieldTSType.Date && (this.Value !== null && this.Value !== undefined && !(this.Value instanceof Date))) {
|
|
255
267
|
// invalid non-null date, but that is okay if we are a new record and we have a default value
|
|
256
268
|
result.Success = false;
|
|
257
|
-
result.Errors.push(new
|
|
269
|
+
result.Errors.push(new ValidationErrorInfo(ef.Name, `${this.Value} is not a valid date for ${ef.DisplayNameOrName}`, this.Value));
|
|
258
270
|
}
|
|
259
271
|
// add validation to ensure a number value is within range based on the
|
|
260
272
|
// underlying SQL type
|
|
@@ -263,7 +275,7 @@ class EntityField {
|
|
|
263
275
|
if (typeLookup) {
|
|
264
276
|
if (this.Value < typeLookup.min || this.Value > typeLookup.max) {
|
|
265
277
|
result.Success = false;
|
|
266
|
-
result.Errors.push(new
|
|
278
|
+
result.Errors.push(new ValidationErrorInfo(ef.Name, `${ef.DisplayNameOrName} is ${ef.SQLFullType} in the database and must be a valid number between ${-typeLookup.min} and ${typeLookup.max}. Current value is ${this.Value}`, this.Value));
|
|
267
279
|
}
|
|
268
280
|
}
|
|
269
281
|
}
|
|
@@ -288,14 +300,14 @@ class EntityField {
|
|
|
288
300
|
this.Value = Value;
|
|
289
301
|
}
|
|
290
302
|
else if (fieldInfo.DefaultValue) {
|
|
291
|
-
if (fieldInfo.TSType ===
|
|
303
|
+
if (fieldInfo.TSType === EntityFieldTSType.Boolean) {
|
|
292
304
|
// special handling for booleans as we don't want a string passed into a boolean field, we want a true boolean
|
|
293
305
|
if (typeof fieldInfo.DefaultValue === "string" && fieldInfo.DefaultValue.trim() === "1" || fieldInfo.DefaultValue.trim().toLowerCase() === "true")
|
|
294
306
|
this.Value = true;
|
|
295
307
|
else
|
|
296
308
|
this.Value = false;
|
|
297
309
|
}
|
|
298
|
-
else if (fieldInfo.TSType ===
|
|
310
|
+
else if (fieldInfo.TSType === EntityFieldTSType.Number) {
|
|
299
311
|
// special handling for numbers as we don't want a string passed into a value for a numeric field
|
|
300
312
|
if (!isNaN(Number(fieldInfo.DefaultValue))) {
|
|
301
313
|
this.Value = Number(fieldInfo.DefaultValue);
|
|
@@ -308,7 +320,7 @@ class EntityField {
|
|
|
308
320
|
// special handling for GUIDs, we don't want to populate anything here because the server always sets the value, leave blank
|
|
309
321
|
this.Value = null;
|
|
310
322
|
}
|
|
311
|
-
else if (fieldInfo.TSType ===
|
|
323
|
+
else if (fieldInfo.TSType === EntityFieldTSType.Date) {
|
|
312
324
|
if (fieldInfo.DefaultValue.trim().length > 0) {
|
|
313
325
|
// special handling for dates as we don't want to use getdate() type defaults as is, we want to convert them to a JS Date object
|
|
314
326
|
try {
|
|
@@ -324,7 +336,7 @@ class EntityField {
|
|
|
324
336
|
}
|
|
325
337
|
catch (e) {
|
|
326
338
|
// if we get here, that means the default value is not a valid date, so we need to check to see if it's a SQL current date function
|
|
327
|
-
if (
|
|
339
|
+
if (EntityFieldInfo.IsDefaultValueSQLCurrentDateFunction(fieldInfo.DefaultValue)) {
|
|
328
340
|
// we have a SQL current date function default, leave the field alone if its a special date field as the server (i.e. database) will handle
|
|
329
341
|
//setting the value, otherwise set the value to the current date
|
|
330
342
|
if (fieldInfo.IsSpecialDateField) {
|
|
@@ -379,26 +391,9 @@ class EntityField {
|
|
|
379
391
|
return this._OldValue;
|
|
380
392
|
}
|
|
381
393
|
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Static object containing the value ranges for various SQL number types.
|
|
385
|
-
* This is used to validate the value of the field when it is set or validated.
|
|
386
|
-
*/
|
|
387
|
-
EntityField.SQLTypeValueRanges = {
|
|
388
|
-
"int": { min: -2147483648, max: 2147483647 },
|
|
389
|
-
"bigint": { min: -9223372036854775808, max: 9223372036854775807 },
|
|
390
|
-
"smallint": { min: -32768, max: 32767 },
|
|
391
|
-
"tinyint": { min: 0, max: 255 },
|
|
392
|
-
"decimal": { min: -7922816251426433759354395033, max: 79228162514264337593543950335 },
|
|
393
|
-
"numeric": { min: -7922816251426433759354395033, max: 79228162514264337593543950335 },
|
|
394
|
-
"float": { min: -1.7976931348623157e+308, max: 1.7976931348623157e+308 },
|
|
395
|
-
"real": { min: -3.402823466e+38, max: 3.402823466e+38 },
|
|
396
|
-
"money": { min: -922337203685477.5808, max: 922337203685477.5807 },
|
|
397
|
-
};
|
|
398
|
-
class DataObjectRelatedEntityParam {
|
|
394
|
+
export class DataObjectRelatedEntityParam {
|
|
399
395
|
}
|
|
400
|
-
|
|
401
|
-
class DataObjectParams {
|
|
396
|
+
export class DataObjectParams {
|
|
402
397
|
constructor(oldValues = false, omitNullValues = false, omitEmptyStrings = false, excludeFields = [], includeRelatedEntityData = false, relatedEntityList = []) {
|
|
403
398
|
this.oldValues = oldValues;
|
|
404
399
|
this.omitNullValues = omitNullValues;
|
|
@@ -408,14 +403,12 @@ class DataObjectParams {
|
|
|
408
403
|
this.relatedEntityList = relatedEntityList;
|
|
409
404
|
}
|
|
410
405
|
}
|
|
411
|
-
|
|
412
|
-
class BaseEntityAIActionParams {
|
|
406
|
+
export class BaseEntityAIActionParams {
|
|
413
407
|
}
|
|
414
|
-
exports.BaseEntityAIActionParams = BaseEntityAIActionParams;
|
|
415
408
|
/**
|
|
416
409
|
* Used for storing the result of a Save or Delete or other transactional operation within a BaseEntity
|
|
417
410
|
*/
|
|
418
|
-
class BaseEntityResult {
|
|
411
|
+
export class BaseEntityResult {
|
|
419
412
|
constructor(success, message, type) {
|
|
420
413
|
/**
|
|
421
414
|
* A copy of the values of the entity object BEFORE the operation was performed
|
|
@@ -463,18 +456,16 @@ class BaseEntityResult {
|
|
|
463
456
|
return msg;
|
|
464
457
|
}
|
|
465
458
|
}
|
|
466
|
-
exports.BaseEntityResult = BaseEntityResult;
|
|
467
459
|
/**
|
|
468
460
|
* Event type that is used to raise events and provided structured callbacks for any caller that is interested in registering for events.
|
|
469
461
|
* This type is also used for whenever a BaseEntity instance raises an event with MJGlobal.
|
|
470
462
|
*/
|
|
471
|
-
class BaseEntityEvent {
|
|
463
|
+
export class BaseEntityEvent {
|
|
472
464
|
}
|
|
473
|
-
exports.BaseEntityEvent = BaseEntityEvent;
|
|
474
465
|
/**
|
|
475
466
|
* Base class used for all entity objects. This class is abstract and is sub-classes for each particular entity using the CodeGen tool. This class provides the basic functionality for loading, saving, and validating entity objects.
|
|
476
467
|
*/
|
|
477
|
-
class BaseEntity {
|
|
468
|
+
export class BaseEntity {
|
|
478
469
|
constructor(Entity, Provider = null) {
|
|
479
470
|
this._Fields = [];
|
|
480
471
|
this._recordLoaded = false;
|
|
@@ -494,9 +485,9 @@ class BaseEntity {
|
|
|
494
485
|
* that embed multiple fields if desired.
|
|
495
486
|
*/
|
|
496
487
|
this._vectors = new Map();
|
|
497
|
-
this._eventSubject = new
|
|
488
|
+
this._eventSubject = new Subject();
|
|
498
489
|
this._EntityInfo = Entity;
|
|
499
|
-
|
|
490
|
+
EntityInfo.AssertEntityActiveStatus(Entity, 'BaseEntity::constructor');
|
|
500
491
|
this._provider = Provider;
|
|
501
492
|
this.init();
|
|
502
493
|
}
|
|
@@ -552,6 +543,7 @@ class BaseEntity {
|
|
|
552
543
|
RaiseReadyForTransaction() {
|
|
553
544
|
this.RaiseEvent('transaction_ready', null);
|
|
554
545
|
}
|
|
546
|
+
static { this._baseEventCode = 'BaseEntityEvent'; }
|
|
555
547
|
/**
|
|
556
548
|
* When a BaseEntity class raises an event with MJGlobal, the eventCode property is set to this value. This is used to identify events that are raised by BaseEntity objects.
|
|
557
549
|
* Any MJGlobal event that is raised by a BaseEntity class will use a BaseEntityEvent type as the args parameter
|
|
@@ -564,7 +556,7 @@ class BaseEntity {
|
|
|
564
556
|
*/
|
|
565
557
|
RaiseEvent(type, payload, saveSubType = undefined) {
|
|
566
558
|
// this is the local event handler that is specific to THIS instance of the entity object
|
|
567
|
-
|
|
559
|
+
LogDebug(`BaseEntity.RaiseEvent() - ${type === 'save' ? 'save:' + saveSubType : type} event raised for ${this.EntityInfo.Name}, about to call this._eventSubject.next()`);
|
|
568
560
|
this._eventSubject.next({ type: type, payload: payload, saveSubType: saveSubType, baseEntity: this });
|
|
569
561
|
// this next call is to MJGlobal to let everyone who cares knows that we had an event on an entity object
|
|
570
562
|
// we broadcast save/delete/load events and their _started counterparts
|
|
@@ -575,10 +567,10 @@ class BaseEntity {
|
|
|
575
567
|
event.payload = payload;
|
|
576
568
|
event.type = type;
|
|
577
569
|
event.saveSubType = saveSubType;
|
|
578
|
-
|
|
579
|
-
|
|
570
|
+
LogDebug(`BaseEntity.RaiseEvent() - ${type === 'save' ? 'save:' + saveSubType : type} event raised for ${this.EntityInfo.Name}, about to call MJGlobal.RaiseEvent()`);
|
|
571
|
+
MJGlobal.Instance.RaiseEvent({
|
|
580
572
|
component: this,
|
|
581
|
-
event:
|
|
573
|
+
event: MJEventType.ComponentEvent,
|
|
582
574
|
eventCode: BaseEntity.BaseEventCode,
|
|
583
575
|
args: event
|
|
584
576
|
});
|
|
@@ -773,7 +765,7 @@ class BaseEntity {
|
|
|
773
765
|
*/
|
|
774
766
|
get PrimaryKey() {
|
|
775
767
|
if (this._compositeKey === null) {
|
|
776
|
-
this._compositeKey = new
|
|
768
|
+
this._compositeKey = new CompositeKey();
|
|
777
769
|
this._compositeKey.LoadFromEntityFields(this.PrimaryKeys);
|
|
778
770
|
}
|
|
779
771
|
return this._compositeKey;
|
|
@@ -799,10 +791,10 @@ class BaseEntity {
|
|
|
799
791
|
Set(FieldName, Value) {
|
|
800
792
|
const field = this.GetFieldByName(FieldName);
|
|
801
793
|
if (field != null) {
|
|
802
|
-
if (field.EntityFieldInfo.TSType ===
|
|
794
|
+
if (field.EntityFieldInfo.TSType === EntityFieldTSType.Date && (typeof Value === 'string' || typeof Value === 'number')) {
|
|
803
795
|
field.Value = new Date(Value);
|
|
804
796
|
}
|
|
805
|
-
else if (field.EntityFieldInfo.TSType ===
|
|
797
|
+
else if (field.EntityFieldInfo.TSType === EntityFieldTSType.Number && typeof Value === 'string' && Value !== null && Value !== undefined) {
|
|
806
798
|
const numericValue = Number(Value);
|
|
807
799
|
if (!isNaN(numericValue)) {
|
|
808
800
|
field.Value = numericValue;
|
|
@@ -811,7 +803,7 @@ class BaseEntity {
|
|
|
811
803
|
field.Value = Value;
|
|
812
804
|
}
|
|
813
805
|
}
|
|
814
|
-
else if (field.EntityFieldInfo.TSType ===
|
|
806
|
+
else if (field.EntityFieldInfo.TSType === EntityFieldTSType.Boolean && Value !== null && Value !== undefined) {
|
|
815
807
|
if (typeof Value === 'string') {
|
|
816
808
|
if (Value.trim() === '1' || Value.trim().toLowerCase() === 'true') {
|
|
817
809
|
field.Value = true;
|
|
@@ -844,7 +836,7 @@ class BaseEntity {
|
|
|
844
836
|
const field = this.GetFieldByName(FieldName);
|
|
845
837
|
if (field != null) {
|
|
846
838
|
// if the field is a date and the value is a string, convert it to a date
|
|
847
|
-
if (field.EntityFieldInfo.TSType ===
|
|
839
|
+
if (field.EntityFieldInfo.TSType === EntityFieldTSType.Date && (typeof field.Value === 'string' || typeof field.Value === 'number')) {
|
|
848
840
|
field.Value = new Date(field.Value);
|
|
849
841
|
}
|
|
850
842
|
return field.Value;
|
|
@@ -902,7 +894,7 @@ class BaseEntity {
|
|
|
902
894
|
throw new Error(`Field ${key} does not exist on ${this.EntityInfo.Name}`);
|
|
903
895
|
else {
|
|
904
896
|
// Record field-not-found warning - will be batched and displayed after debounce period
|
|
905
|
-
|
|
897
|
+
WarningManager.Instance.RecordFieldNotFoundWarning(this.EntityInfo.Name, key, 'BaseEntity::SetMany');
|
|
906
898
|
}
|
|
907
899
|
}
|
|
908
900
|
}
|
|
@@ -924,7 +916,7 @@ class BaseEntity {
|
|
|
924
916
|
const tempStatus = field.ActiveStatusAssertions; // save the current active status assertions
|
|
925
917
|
field.ActiveStatusAssertions = false; // disable active status assertions for this field
|
|
926
918
|
obj[field.Name] = oldValues ? field.OldValue : field.Value;
|
|
927
|
-
if (field.EntityFieldInfo.TSType ==
|
|
919
|
+
if (field.EntityFieldInfo.TSType == EntityFieldTSType.Date && obj[field.Name] && !(obj[field.Name] instanceof Date)) {
|
|
928
920
|
obj[field.Name] = new Date(obj[field.Name]); // a timestamp, convert to JS Date Object
|
|
929
921
|
}
|
|
930
922
|
field.ActiveStatusAssertions = tempStatus; // restore the prior status for assertions
|
|
@@ -1009,8 +1001,8 @@ class BaseEntity {
|
|
|
1009
1001
|
}
|
|
1010
1002
|
async GetRelatedEntityDataExt(re, filter = null, maxRecords = null) {
|
|
1011
1003
|
// we need to query the database to get related entity info
|
|
1012
|
-
const params =
|
|
1013
|
-
const rv = new
|
|
1004
|
+
const params = EntityInfo.BuildRelationshipViewParams(this, re, filter, maxRecords);
|
|
1005
|
+
const rv = new RunView();
|
|
1014
1006
|
const result = await rv.RunView(params, this._contextCurrentUser);
|
|
1015
1007
|
if (result && result.Success) {
|
|
1016
1008
|
return {
|
|
@@ -1030,7 +1022,7 @@ class BaseEntity {
|
|
|
1030
1022
|
for (const rawField of this.EntityInfo.Fields) {
|
|
1031
1023
|
const key = this.EntityInfo.Name + '.' + rawField.Name;
|
|
1032
1024
|
// support for sub-classes of the EntityField class
|
|
1033
|
-
const newField =
|
|
1025
|
+
const newField = MJGlobal.Instance.ClassFactory.CreateInstance(EntityField, key, rawField);
|
|
1034
1026
|
this.Fields.push(newField);
|
|
1035
1027
|
}
|
|
1036
1028
|
}
|
|
@@ -1059,7 +1051,7 @@ class BaseEntity {
|
|
|
1059
1051
|
return true;
|
|
1060
1052
|
}
|
|
1061
1053
|
catch (e) {
|
|
1062
|
-
|
|
1054
|
+
LogError(`Error in BaseEntity.CopyFrom: ${e}`);
|
|
1063
1055
|
return false;
|
|
1064
1056
|
}
|
|
1065
1057
|
}
|
|
@@ -1090,7 +1082,7 @@ class BaseEntity {
|
|
|
1090
1082
|
pk.Type.toLowerCase().trim() === 'uniqueidentifier' &&
|
|
1091
1083
|
!this.Get(pk.Name)) {
|
|
1092
1084
|
// Generate and set UUID for this primary key
|
|
1093
|
-
const uuid =
|
|
1085
|
+
const uuid = uuidv4();
|
|
1094
1086
|
this.Set(pk.Name, uuid);
|
|
1095
1087
|
const field = this.GetFieldByName(pk.Name);
|
|
1096
1088
|
if (field) {
|
|
@@ -1120,17 +1112,17 @@ class BaseEntity {
|
|
|
1120
1112
|
async Save(options) {
|
|
1121
1113
|
// If a save is already in progress, return its promise.
|
|
1122
1114
|
if (this._pendingSave$) {
|
|
1123
|
-
return
|
|
1115
|
+
return firstValueFrom(this._pendingSave$);
|
|
1124
1116
|
}
|
|
1125
1117
|
// Create a new observable that debounces duplicative calls, and executes the save.
|
|
1126
|
-
this._pendingSave$ =
|
|
1118
|
+
this._pendingSave$ = of(options).pipe(
|
|
1127
1119
|
// Execute the actual save logic.
|
|
1128
|
-
|
|
1120
|
+
switchMap(opts => from(this._InnerSave(opts))),
|
|
1129
1121
|
// When the save completes (whether successfully or not), clear the pending save observable.
|
|
1130
|
-
|
|
1122
|
+
finalize(() => { this._pendingSave$ = null; }),
|
|
1131
1123
|
// Ensure that all subscribers get the same result.
|
|
1132
|
-
|
|
1133
|
-
return
|
|
1124
|
+
shareReplay(1));
|
|
1125
|
+
return firstValueFrom(this._pendingSave$);
|
|
1134
1126
|
}
|
|
1135
1127
|
/**
|
|
1136
1128
|
* Private, internal method to handle saving the current state of the object to the database. This method is called by the public facing Save() method
|
|
@@ -1143,8 +1135,8 @@ class BaseEntity {
|
|
|
1143
1135
|
const newResult = new BaseEntityResult();
|
|
1144
1136
|
newResult.StartedAt = new Date();
|
|
1145
1137
|
try {
|
|
1146
|
-
const _options = options ? options : new
|
|
1147
|
-
const type = this.IsSaved ?
|
|
1138
|
+
const _options = options ? options : new EntitySaveOptions();
|
|
1139
|
+
const type = this.IsSaved ? EntityPermissionType.Update : EntityPermissionType.Create;
|
|
1148
1140
|
const saveSubType = this.IsSaved ? 'update' : 'create';
|
|
1149
1141
|
this.CheckPermissions(type, true); // this will throw an error and exit out if we don't have permission
|
|
1150
1142
|
if (_options.IgnoreDirtyState || this.Dirty || _options.ReplayOnly) {
|
|
@@ -1154,7 +1146,7 @@ class BaseEntity {
|
|
|
1154
1146
|
throw new Error('No provider set');
|
|
1155
1147
|
}
|
|
1156
1148
|
else {
|
|
1157
|
-
let valResult = new
|
|
1149
|
+
let valResult = new ValidationResult();
|
|
1158
1150
|
if (_options.ReplayOnly) {
|
|
1159
1151
|
valResult.Success = true; // bypassing validation since we are in replay only....
|
|
1160
1152
|
}
|
|
@@ -1247,7 +1239,7 @@ class BaseEntity {
|
|
|
1247
1239
|
* Internal helper method for the class and sub-classes - used to easily get the Active User which is either the ContextCurrentUser, if defined, or the Metadata.Provider.CurrentUser if not.
|
|
1248
1240
|
*/
|
|
1249
1241
|
get ActiveUser() {
|
|
1250
|
-
return this.ContextCurrentUser ||
|
|
1242
|
+
return this.ContextCurrentUser || Metadata.Provider.CurrentUser; // use the context user ahead of the Provider.Current User - this is for SERVER side ops where the user changes per request
|
|
1251
1243
|
}
|
|
1252
1244
|
/**
|
|
1253
1245
|
* Utility method that returns true if the given permission being checked is enabled for the current user, and false if not.
|
|
@@ -1261,7 +1253,7 @@ class BaseEntity {
|
|
|
1261
1253
|
throw new Error('No user set - either the context user for the entity object must be set, or the Metadata.Provider.CurrentUser must be set');
|
|
1262
1254
|
// first check if the AllowCreateAPI/AllowUpdateAPI/AllowDeleteAPI settings are flipped on for the entity in question
|
|
1263
1255
|
switch (type) {
|
|
1264
|
-
case
|
|
1256
|
+
case EntityPermissionType.Create:
|
|
1265
1257
|
if (!this.EntityInfo.AllowCreateAPI) {
|
|
1266
1258
|
if (throwError)
|
|
1267
1259
|
throw new Error(`Create API is disabled for ${this.EntityInfo.Name}`);
|
|
@@ -1269,7 +1261,7 @@ class BaseEntity {
|
|
|
1269
1261
|
return false;
|
|
1270
1262
|
}
|
|
1271
1263
|
break;
|
|
1272
|
-
case
|
|
1264
|
+
case EntityPermissionType.Update:
|
|
1273
1265
|
if (!this.EntityInfo.AllowUpdateAPI) {
|
|
1274
1266
|
if (throwError)
|
|
1275
1267
|
throw new Error(`Update API is disabled for ${this.EntityInfo.Name}`);
|
|
@@ -1277,7 +1269,7 @@ class BaseEntity {
|
|
|
1277
1269
|
return false;
|
|
1278
1270
|
}
|
|
1279
1271
|
break;
|
|
1280
|
-
case
|
|
1272
|
+
case EntityPermissionType.Delete:
|
|
1281
1273
|
if (!this.EntityInfo.AllowDeleteAPI) {
|
|
1282
1274
|
if (throwError)
|
|
1283
1275
|
throw new Error(`Delete API is disabled for ${this.EntityInfo.Name}`);
|
|
@@ -1285,7 +1277,7 @@ class BaseEntity {
|
|
|
1285
1277
|
return false;
|
|
1286
1278
|
}
|
|
1287
1279
|
break;
|
|
1288
|
-
case
|
|
1280
|
+
case EntityPermissionType.Read:
|
|
1289
1281
|
if (!this.EntityInfo.IncludeInAPI) {
|
|
1290
1282
|
if (throwError)
|
|
1291
1283
|
throw new Error(`API is disabled for ${this.EntityInfo.Name}`);
|
|
@@ -1297,16 +1289,16 @@ class BaseEntity {
|
|
|
1297
1289
|
const permissions = this.EntityInfo.GetUserPermisions(u);
|
|
1298
1290
|
let bAllowed = false;
|
|
1299
1291
|
switch (type) {
|
|
1300
|
-
case
|
|
1292
|
+
case EntityPermissionType.Create:
|
|
1301
1293
|
bAllowed = permissions.CanCreate;
|
|
1302
1294
|
break;
|
|
1303
|
-
case
|
|
1295
|
+
case EntityPermissionType.Read:
|
|
1304
1296
|
bAllowed = permissions.CanRead;
|
|
1305
1297
|
break;
|
|
1306
|
-
case
|
|
1298
|
+
case EntityPermissionType.Update:
|
|
1307
1299
|
bAllowed = permissions.CanUpdate;
|
|
1308
1300
|
break;
|
|
1309
|
-
case
|
|
1301
|
+
case EntityPermissionType.Delete:
|
|
1310
1302
|
bAllowed = permissions.CanDelete;
|
|
1311
1303
|
break;
|
|
1312
1304
|
}
|
|
@@ -1318,7 +1310,7 @@ class BaseEntity {
|
|
|
1318
1310
|
}
|
|
1319
1311
|
ThrowPermissionError(u, type, additionalInfoMessage) {
|
|
1320
1312
|
throw new Error(`User: ${u.Name} (ID: ${u.ID}, Email: ${u.Email})
|
|
1321
|
-
Does NOT have permission to ${
|
|
1313
|
+
Does NOT have permission to ${EntityPermissionType[type]} ${this.EntityInfo.Name} records.
|
|
1322
1314
|
If you believe this is an error, please contact your system administrator.${additionalInfoMessage ? '\nAdditional Information: ' + additionalInfoMessage : ''}}`);
|
|
1323
1315
|
}
|
|
1324
1316
|
/**
|
|
@@ -1349,7 +1341,7 @@ class BaseEntity {
|
|
|
1349
1341
|
const valResult = CompositeKey.Validate();
|
|
1350
1342
|
if (!valResult || !valResult.IsValid)
|
|
1351
1343
|
throw new Error(`Invalid CompositeKey passed to BaseEntity.Load(${this.EntityInfo.Name}): ${valResult.ErrorMessage}`);
|
|
1352
|
-
this.CheckPermissions(
|
|
1344
|
+
this.CheckPermissions(EntityPermissionType.Read, true); // this will throw an error and exit out if we don't have permission
|
|
1353
1345
|
// Set loading state and raise load_started event
|
|
1354
1346
|
this._isLoading = true;
|
|
1355
1347
|
this.RaiseEvent('load_started', { CompositeKey });
|
|
@@ -1359,7 +1351,7 @@ class BaseEntity {
|
|
|
1359
1351
|
}
|
|
1360
1352
|
const data = await this.ProviderToUse.Load(this, CompositeKey, EntityRelationshipsToLoad, this.ActiveUser);
|
|
1361
1353
|
if (!data) {
|
|
1362
|
-
|
|
1354
|
+
LogError(`Error in BaseEntity.Load(${this.EntityInfo.Name}, Key: ${CompositeKey.ToString()}`);
|
|
1363
1355
|
return false; // no data loaded, return false
|
|
1364
1356
|
}
|
|
1365
1357
|
this.SetMany(data, false, true, true); // don't ignore non-existent fields, but DO replace old values
|
|
@@ -1453,7 +1445,7 @@ class BaseEntity {
|
|
|
1453
1445
|
}
|
|
1454
1446
|
else {
|
|
1455
1447
|
// this is an error state as every entity must have > 0 primary keys defined
|
|
1456
|
-
|
|
1448
|
+
LogError(`BaseEntity.LoadFromData() called on ${this.EntityInfo.Name} with no primary keys defined. This is an error state and should not happen.`);
|
|
1457
1449
|
this._recordLoaded = false;
|
|
1458
1450
|
this._everSaved = false; // Mark as NOT saved since we loaded from data without primary keys
|
|
1459
1451
|
}
|
|
@@ -1466,7 +1458,7 @@ class BaseEntity {
|
|
|
1466
1458
|
* @returns ValidationResult The validation result
|
|
1467
1459
|
*/
|
|
1468
1460
|
Validate() {
|
|
1469
|
-
const result = new
|
|
1461
|
+
const result = new ValidationResult();
|
|
1470
1462
|
result.Success = true; // start off with assumption of success, if any field fails, we'll set this to false
|
|
1471
1463
|
for (let field of this.Fields) {
|
|
1472
1464
|
const err = field.Validate();
|
|
@@ -1507,7 +1499,7 @@ class BaseEntity {
|
|
|
1507
1499
|
async ValidateAsync() {
|
|
1508
1500
|
// Default implementation just returns success
|
|
1509
1501
|
// Subclasses should override this to perform actual async validation
|
|
1510
|
-
const result = new
|
|
1502
|
+
const result = new ValidationResult();
|
|
1511
1503
|
result.Success = true;
|
|
1512
1504
|
return result;
|
|
1513
1505
|
}
|
|
@@ -1522,17 +1514,17 @@ class BaseEntity {
|
|
|
1522
1514
|
async Delete(options) {
|
|
1523
1515
|
// If a delete is already in progress, return its promise.
|
|
1524
1516
|
if (this._pendingDelete$) {
|
|
1525
|
-
return
|
|
1517
|
+
return firstValueFrom(this._pendingDelete$);
|
|
1526
1518
|
}
|
|
1527
1519
|
// Create a new observable that debounces duplicative calls, and executes the delete.
|
|
1528
|
-
this._pendingDelete$ =
|
|
1520
|
+
this._pendingDelete$ = of(options).pipe(
|
|
1529
1521
|
// Execute the actual delete logic.
|
|
1530
|
-
|
|
1522
|
+
switchMap(opts => from(this._InnerDelete(opts))),
|
|
1531
1523
|
// When the delete completes (whether successfully or not), clear the pending delete observable.
|
|
1532
|
-
|
|
1524
|
+
finalize(() => { this._pendingDelete$ = null; }),
|
|
1533
1525
|
// Ensure that all subscribers get the same result.
|
|
1534
|
-
|
|
1535
|
-
return
|
|
1526
|
+
shareReplay(1));
|
|
1527
|
+
return firstValueFrom(this._pendingDelete$);
|
|
1536
1528
|
}
|
|
1537
1529
|
/**
|
|
1538
1530
|
* Private, internal method to handle deleting a record from the database. This method is called by the public facing Delete() method
|
|
@@ -1549,7 +1541,7 @@ class BaseEntity {
|
|
|
1549
1541
|
throw new Error('No provider set');
|
|
1550
1542
|
}
|
|
1551
1543
|
else {
|
|
1552
|
-
this.CheckPermissions(
|
|
1544
|
+
this.CheckPermissions(EntityPermissionType.Delete, true); // this will throw an error and exit out if we don't have permission
|
|
1553
1545
|
// Raise delete_started event before the actual delete operation begins
|
|
1554
1546
|
this.RaiseEvent('delete_started', null);
|
|
1555
1547
|
// stash the old values for the event
|
|
@@ -1624,20 +1616,21 @@ class BaseEntity {
|
|
|
1624
1616
|
async AfterEntityAIAction(params) {
|
|
1625
1617
|
return true; // default implementation does nothing
|
|
1626
1618
|
}
|
|
1619
|
+
static { this._globalProviderKey = 'MJ_BaseEntityProvider'; }
|
|
1627
1620
|
/**
|
|
1628
1621
|
* Static property to get/set the IEntityDataProvider that is used by all BaseEntity objects. This is a global setting that is used by all BaseEntity objects. It can be overriden for a given BaseEntity object instance by passing in a provider to the
|
|
1629
1622
|
* constructor of the BaseEntity object. Typically, a provider will pass itself into BaseEntity objects it creates to create a tight coupling between the provider and the BaseEntity objects it creates. This allows multiple concurrent
|
|
1630
1623
|
* connections to exist in the same process space without interfering with each other.
|
|
1631
1624
|
*/
|
|
1632
1625
|
static get Provider() {
|
|
1633
|
-
const g =
|
|
1626
|
+
const g = MJGlobal.Instance.GetGlobalObjectStore();
|
|
1634
1627
|
if (g)
|
|
1635
1628
|
return g[BaseEntity._globalProviderKey];
|
|
1636
1629
|
else
|
|
1637
1630
|
throw new Error('No global object store, so we cant get the static provider');
|
|
1638
1631
|
}
|
|
1639
1632
|
static set Provider(value) {
|
|
1640
|
-
const g =
|
|
1633
|
+
const g = MJGlobal.Instance.GetGlobalObjectStore();
|
|
1641
1634
|
if (g)
|
|
1642
1635
|
g[BaseEntity._globalProviderKey] = value;
|
|
1643
1636
|
else
|
|
@@ -1683,7 +1676,7 @@ class BaseEntity {
|
|
|
1683
1676
|
if (results) {
|
|
1684
1677
|
const changes = [];
|
|
1685
1678
|
for (let result of results)
|
|
1686
|
-
changes.push(new
|
|
1679
|
+
changes.push(new RecordChange(result));
|
|
1687
1680
|
return changes;
|
|
1688
1681
|
}
|
|
1689
1682
|
else
|
|
@@ -1704,7 +1697,7 @@ class BaseEntity {
|
|
|
1704
1697
|
return true;
|
|
1705
1698
|
}
|
|
1706
1699
|
else {
|
|
1707
|
-
|
|
1700
|
+
LogError(parseResult.error.flatten());
|
|
1708
1701
|
return false;
|
|
1709
1702
|
}
|
|
1710
1703
|
}
|
|
@@ -1725,7 +1718,7 @@ class BaseEntity {
|
|
|
1725
1718
|
parseResult.data;
|
|
1726
1719
|
}
|
|
1727
1720
|
else {
|
|
1728
|
-
|
|
1721
|
+
LogError(parseResult.error.flatten());
|
|
1729
1722
|
return null;
|
|
1730
1723
|
}
|
|
1731
1724
|
}
|
|
@@ -1846,7 +1839,4 @@ class BaseEntity {
|
|
|
1846
1839
|
this._vectors.clear();
|
|
1847
1840
|
}
|
|
1848
1841
|
}
|
|
1849
|
-
exports.BaseEntity = BaseEntity;
|
|
1850
|
-
BaseEntity._baseEventCode = 'BaseEntityEvent';
|
|
1851
|
-
BaseEntity._globalProviderKey = 'MJ_BaseEntityProvider';
|
|
1852
1842
|
//# sourceMappingURL=baseEntity.js.map
|