@memberjunction/core 3.4.0 → 4.1.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 (107) hide show
  1. package/dist/generic/InMemoryLocalStorageProvider.d.ts +1 -1
  2. package/dist/generic/InMemoryLocalStorageProvider.js +2 -6
  3. package/dist/generic/InMemoryLocalStorageProvider.js.map +1 -1
  4. package/dist/generic/QueryCache.d.ts +1 -1
  5. package/dist/generic/QueryCache.js +6 -10
  6. package/dist/generic/QueryCache.js.map +1 -1
  7. package/dist/generic/QueryCacheConfig.js +1 -2
  8. package/dist/generic/RegisterForStartup.d.ts +2 -2
  9. package/dist/generic/RegisterForStartup.js +7 -12
  10. package/dist/generic/RegisterForStartup.js.map +1 -1
  11. package/dist/generic/applicationInfo.d.ts +3 -3
  12. package/dist/generic/applicationInfo.js +4 -10
  13. package/dist/generic/applicationInfo.js.map +1 -1
  14. package/dist/generic/authEvaluator.d.ts +1 -1
  15. package/dist/generic/authEvaluator.js +4 -8
  16. package/dist/generic/authEvaluator.js.map +1 -1
  17. package/dist/generic/authTypes.js +1 -4
  18. package/dist/generic/authTypes.js.map +1 -1
  19. package/dist/generic/baseEngine.d.ts +5 -5
  20. package/dist/generic/baseEngine.js +51 -56
  21. package/dist/generic/baseEngine.js.map +1 -1
  22. package/dist/generic/baseEngineRegistry.js +13 -17
  23. package/dist/generic/baseEngineRegistry.js.map +1 -1
  24. package/dist/generic/baseEntity.d.ts +171 -5
  25. package/dist/generic/baseEntity.d.ts.map +1 -1
  26. package/dist/generic/baseEntity.js +651 -121
  27. package/dist/generic/baseEntity.js.map +1 -1
  28. package/dist/generic/baseInfo.js +3 -7
  29. package/dist/generic/baseInfo.js.map +1 -1
  30. package/dist/generic/compositeKey.d.ts +2 -2
  31. package/dist/generic/compositeKey.js +5 -11
  32. package/dist/generic/compositeKey.js.map +1 -1
  33. package/dist/generic/databaseProviderBase.d.ts +2 -2
  34. package/dist/generic/databaseProviderBase.js +2 -6
  35. package/dist/generic/databaseProviderBase.js.map +1 -1
  36. package/dist/generic/entityInfo.d.ts +84 -5
  37. package/dist/generic/entityInfo.d.ts.map +1 -1
  38. package/dist/generic/entityInfo.js +235 -108
  39. package/dist/generic/entityInfo.js.map +1 -1
  40. package/dist/generic/explorerNavigationItem.d.ts +1 -1
  41. package/dist/generic/explorerNavigationItem.js +2 -6
  42. package/dist/generic/explorerNavigationItem.js.map +1 -1
  43. package/dist/generic/graphqlTypeNames.d.ts +1 -1
  44. package/dist/generic/graphqlTypeNames.js +4 -9
  45. package/dist/generic/graphqlTypeNames.js.map +1 -1
  46. package/dist/generic/interfaces.d.ts +104 -14
  47. package/dist/generic/interfaces.d.ts.map +1 -1
  48. package/dist/generic/interfaces.js +28 -30
  49. package/dist/generic/interfaces.js.map +1 -1
  50. package/dist/generic/libraryInfo.d.ts +1 -1
  51. package/dist/generic/libraryInfo.js +2 -6
  52. package/dist/generic/libraryInfo.js.map +1 -1
  53. package/dist/generic/localCacheManager.d.ts +2 -2
  54. package/dist/generic/localCacheManager.js +44 -48
  55. package/dist/generic/localCacheManager.js.map +1 -1
  56. package/dist/generic/logging.d.ts.map +1 -1
  57. package/dist/generic/logging.js +54 -67
  58. package/dist/generic/logging.js.map +1 -1
  59. package/dist/generic/metadata.d.ts +12 -12
  60. package/dist/generic/metadata.d.ts.map +1 -1
  61. package/dist/generic/metadata.js +21 -25
  62. package/dist/generic/metadata.js.map +1 -1
  63. package/dist/generic/metadataUtil.d.ts +1 -1
  64. package/dist/generic/metadataUtil.js +3 -7
  65. package/dist/generic/metadataUtil.js.map +1 -1
  66. package/dist/generic/providerBase.d.ts +63 -16
  67. package/dist/generic/providerBase.d.ts.map +1 -1
  68. package/dist/generic/providerBase.js +253 -130
  69. package/dist/generic/providerBase.js.map +1 -1
  70. package/dist/generic/queryInfo.d.ts +5 -5
  71. package/dist/generic/queryInfo.js +21 -30
  72. package/dist/generic/queryInfo.js.map +1 -1
  73. package/dist/generic/queryInfoInterfaces.js +1 -2
  74. package/dist/generic/queryInfoInterfaces.js.map +1 -1
  75. package/dist/generic/querySQLFilters.js +5 -10
  76. package/dist/generic/querySQLFilters.js.map +1 -1
  77. package/dist/generic/runQuery.d.ts +2 -2
  78. package/dist/generic/runQuery.js +5 -9
  79. package/dist/generic/runQuery.js.map +1 -1
  80. package/dist/generic/runQuerySQLFilterImplementations.d.ts +1 -1
  81. package/dist/generic/runQuerySQLFilterImplementations.js +4 -8
  82. package/dist/generic/runQuerySQLFilterImplementations.js.map +1 -1
  83. package/dist/generic/runReport.d.ts +2 -2
  84. package/dist/generic/runReport.js +5 -9
  85. package/dist/generic/runReport.js.map +1 -1
  86. package/dist/generic/securityInfo.d.ts +2 -2
  87. package/dist/generic/securityInfo.js +10 -20
  88. package/dist/generic/securityInfo.js.map +1 -1
  89. package/dist/generic/telemetryManager.js +20 -32
  90. package/dist/generic/telemetryManager.js.map +1 -1
  91. package/dist/generic/transactionGroup.d.ts +1 -1
  92. package/dist/generic/transactionGroup.d.ts.map +1 -1
  93. package/dist/generic/transactionGroup.js +11 -19
  94. package/dist/generic/transactionGroup.js.map +1 -1
  95. package/dist/generic/util.js +15 -31
  96. package/dist/generic/util.js.map +1 -1
  97. package/dist/index.d.ts +34 -34
  98. package/dist/index.js +45 -63
  99. package/dist/index.js.map +1 -1
  100. package/dist/views/runView.d.ts +3 -3
  101. package/dist/views/runView.js +6 -11
  102. package/dist/views/runView.js.map +1 -1
  103. package/dist/views/viewInfo.d.ts +3 -3
  104. package/dist/views/viewInfo.js +10 -17
  105. package/dist/views/viewInfo.js.map +1 -1
  106. package/package.json +11 -10
  107. package/readme.md +871 -1271
@@ -1,15 +1,12 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RecordMergeResult = exports.RecordMergeDetailResult = exports.RecordMergeRequest = exports.RecordDependency = exports.EntityDependency = exports.ValidationResult = exports.ValidationErrorInfo = exports.ValidationErrorType = exports.EntityInfo = exports.EntitySettingInfo = exports.EntityDocumentTypeInfo = exports.EntityFieldInfo = exports.GeneratedFormSectionType = exports.EntityFieldValueInfo = exports.EntityFieldValueListType = exports.EntityFieldGraphQLType = exports.EntityFieldTSType = exports.EntityPermissionInfo = exports.EntityUserPermissionInfo = exports.EntityPermissionType = exports.EntityRelationshipInfo = exports.RecordChange = exports.RecordChangeStatus = void 0;
4
- const baseInfo_1 = require("./baseInfo");
5
- const metadata_1 = require("./metadata");
6
- const util_1 = require("./util");
7
- const logging_1 = require("./logging");
8
- const global_1 = require("@memberjunction/global");
1
+ import { BaseInfo } from "./baseInfo.js";
2
+ import { Metadata } from "./metadata.js";
3
+ import { TypeScriptTypeFromSQLType, SQLFullType, SQLMaxLength, FormatValue, CodeNameFromString } from "./util.js";
4
+ import { LogError } from "./logging.js";
5
+ import { WarningManager, SafeJSONParse } from "@memberjunction/global";
9
6
  /**
10
7
  * The possible status values for a record change
11
8
  */
12
- exports.RecordChangeStatus = {
9
+ export const RecordChangeStatus = {
13
10
  Pending: 'Pending',
14
11
  Complete: 'Complete',
15
12
  Error: 'Error',
@@ -17,9 +14,9 @@ exports.RecordChangeStatus = {
17
14
  /**
18
15
  * Record Change object has information on a change to a record in the Record Changes entity
19
16
  */
20
- class RecordChange extends baseInfo_1.BaseInfo {
17
+ export class RecordChange extends BaseInfo {
21
18
  get StatusValue() {
22
- return exports.RecordChangeStatus[this.Status?.trim()];
19
+ return RecordChangeStatus[this.Status?.trim()];
23
20
  }
24
21
  get Changes() {
25
22
  return JSON.parse(this.ChangesJSON);
@@ -40,7 +37,6 @@ class RecordChange extends baseInfo_1.BaseInfo {
40
37
  this.copyInitData(initData);
41
38
  }
42
39
  }
43
- exports.RecordChange = RecordChange;
44
40
  /**
45
41
  * Information about the Entity Relationship between the Entity and the Related Entity - this class
46
42
  * maps to information in the Entity Relationships metadata entity.
@@ -49,7 +45,7 @@ exports.RecordChange = RecordChange;
49
45
  * Metadata about relationships between entities including display preferences for the UI.
50
46
  * Defines foreign key relationships and how they should be represented in the user interface.
51
47
  */
52
- class EntityRelationshipInfo extends baseInfo_1.BaseInfo {
48
+ export class EntityRelationshipInfo extends BaseInfo {
53
49
  constructor(initData) {
54
50
  super();
55
51
  this.ID = null;
@@ -97,54 +93,52 @@ class EntityRelationshipInfo extends baseInfo_1.BaseInfo {
97
93
  this.copyInitData(initData);
98
94
  }
99
95
  }
100
- exports.EntityRelationshipInfo = EntityRelationshipInfo;
101
- exports.EntityPermissionType = {
96
+ export const EntityPermissionType = {
102
97
  Read: 'Read',
103
98
  Create: 'Create',
104
99
  Update: 'Update',
105
100
  Delete: 'Delete',
106
101
  };
107
- class EntityUserPermissionInfo {
102
+ export class EntityUserPermissionInfo {
108
103
  constructor() {
109
104
  this.ID = null;
110
105
  }
111
106
  }
112
- exports.EntityUserPermissionInfo = EntityUserPermissionInfo;
113
107
  /**
114
108
  * Security settings for each entity.
115
109
  * Controls which roles can perform create, read, update, and delete operations.
116
110
  */
117
- class EntityPermissionInfo extends baseInfo_1.BaseInfo {
111
+ export class EntityPermissionInfo extends BaseInfo {
118
112
  get CreateRLSFilterObject() {
119
- return this.RLSFilter(exports.EntityPermissionType.Create);
113
+ return this.RLSFilter(EntityPermissionType.Create);
120
114
  }
121
115
  get ReadRLSFilterObject() {
122
- return this.RLSFilter(exports.EntityPermissionType.Read);
116
+ return this.RLSFilter(EntityPermissionType.Read);
123
117
  }
124
118
  get UpdateRLSFilterObject() {
125
- return this.RLSFilter(exports.EntityPermissionType.Update);
119
+ return this.RLSFilter(EntityPermissionType.Update);
126
120
  }
127
121
  get DeleteRLSFilterObject() {
128
- return this.RLSFilter(exports.EntityPermissionType.Delete);
122
+ return this.RLSFilter(EntityPermissionType.Delete);
129
123
  }
130
124
  RLSFilter(type) {
131
125
  let fID = "";
132
126
  switch (type) {
133
- case exports.EntityPermissionType.Read:
127
+ case EntityPermissionType.Read:
134
128
  fID = this.ReadRLSFilterID;
135
129
  break;
136
- case exports.EntityPermissionType.Create:
130
+ case EntityPermissionType.Create:
137
131
  fID = this.CreateRLSFilterID;
138
132
  break;
139
- case exports.EntityPermissionType.Update:
133
+ case EntityPermissionType.Update:
140
134
  fID = this.UpdateRLSFilterID;
141
135
  break;
142
- case exports.EntityPermissionType.Delete:
136
+ case EntityPermissionType.Delete:
143
137
  fID = this.DeleteRLSFilterID;
144
138
  break;
145
139
  }
146
140
  if (fID && fID.length > 0)
147
- return metadata_1.Metadata.Provider.RowLevelSecurityFilters.find(f => f.ID === fID);
141
+ return Metadata.Provider.RowLevelSecurityFilters.find(f => f.ID === fID);
148
142
  }
149
143
  constructor(initData) {
150
144
  super();
@@ -172,21 +166,20 @@ class EntityPermissionInfo extends baseInfo_1.BaseInfo {
172
166
  this.copyInitData(initData);
173
167
  }
174
168
  }
175
- exports.EntityPermissionInfo = EntityPermissionInfo;
176
- exports.EntityFieldTSType = {
169
+ export const EntityFieldTSType = {
177
170
  String: 'string',
178
171
  Number: 'number',
179
172
  Date: 'Date',
180
173
  Boolean: 'boolean',
181
174
  };
182
- exports.EntityFieldGraphQLType = {
175
+ export const EntityFieldGraphQLType = {
183
176
  Int: 'Int',
184
177
  Float: 'Float',
185
178
  String: 'String',
186
179
  Boolean: 'Boolean',
187
180
  Timestamp: 'Timestamp',
188
181
  };
189
- exports.EntityFieldValueListType = {
182
+ export const EntityFieldValueListType = {
190
183
  None: 'None',
191
184
  List: 'List',
192
185
  ListOrUserEntry: 'ListOrUserEntry',
@@ -195,7 +188,7 @@ exports.EntityFieldValueListType = {
195
188
  * Defines allowed values for entity fields with value lists.
196
189
  * Supports dropdowns, validations, and data integrity constraints.
197
190
  */
198
- class EntityFieldValueInfo extends baseInfo_1.BaseInfo {
191
+ export class EntityFieldValueInfo extends BaseInfo {
199
192
  constructor(initData) {
200
193
  super();
201
194
  this.ID = null;
@@ -209,8 +202,7 @@ class EntityFieldValueInfo extends baseInfo_1.BaseInfo {
209
202
  this.copyInitData(initData);
210
203
  }
211
204
  }
212
- exports.EntityFieldValueInfo = EntityFieldValueInfo;
213
- exports.GeneratedFormSectionType = {
205
+ export const GeneratedFormSectionType = {
214
206
  Top: 'Top',
215
207
  Details: 'Details',
216
208
  Category: 'Category',
@@ -222,7 +214,7 @@ exports.GeneratedFormSectionType = {
222
214
  * List of all fields within each entity with metadata about each field.
223
215
  * Includes data types, relationships, defaults, and UI display preferences.
224
216
  */
225
- class EntityFieldInfo extends baseInfo_1.BaseInfo {
217
+ export class EntityFieldInfo extends BaseInfo {
226
218
  /**
227
219
  * JSON configuration for additional fields to join from the related entity.
228
220
  * Parsed from the RelatedEntityJoinFields column. Uses lazy initialization and caching
@@ -242,7 +234,7 @@ class EntityFieldInfo extends baseInfo_1.BaseInfo {
242
234
  return this._relatedEntityJoinFieldsParsed;
243
235
  }
244
236
  // Parse and cache the configuration
245
- const parsed = (0, global_1.SafeJSONParse)(this.RelatedEntityJoinFields, false);
237
+ const parsed = SafeJSONParse(this.RelatedEntityJoinFields, false);
246
238
  if (parsed === null) {
247
239
  // Parsing failed - mark as failed so we don't try again
248
240
  this._relatedEntityJoinFieldsFailedParsing = true;
@@ -260,34 +252,34 @@ class EntityFieldInfo extends baseInfo_1.BaseInfo {
260
252
  */
261
253
  get ValueListTypeEnum() {
262
254
  if (this.ValueListType == null)
263
- return exports.EntityFieldValueListType.None;
255
+ return EntityFieldValueListType.None;
264
256
  else {
265
257
  // iterate through list of possibilities from enum and compare lcase
266
- for (let enumMember in exports.EntityFieldValueListType) {
267
- if (typeof exports.EntityFieldValueListType[enumMember] === 'string' &&
258
+ for (let enumMember in EntityFieldValueListType) {
259
+ if (typeof EntityFieldValueListType[enumMember] === 'string' &&
268
260
  enumMember.toLowerCase().trim() === this.ValueListType.toLowerCase().trim()) {
269
- return exports.EntityFieldValueListType[enumMember];
261
+ return EntityFieldValueListType[enumMember];
270
262
  }
271
263
  }
272
264
  }
273
265
  }
274
266
  get GeneratedFormSectionType() {
275
- return exports.GeneratedFormSectionType[this.GeneratedFormSection];
267
+ return GeneratedFormSectionType[this.GeneratedFormSection];
276
268
  }
277
269
  /**
278
270
  * Provides the TypeScript type for a given Entity Field. This is useful to map
279
271
  * a wide array of database types to a narrower set of TypeScript types.
280
272
  */
281
273
  get TSType() {
282
- switch ((0, util_1.TypeScriptTypeFromSQLType)(this.Type).toLowerCase()) {
274
+ switch (TypeScriptTypeFromSQLType(this.Type).toLowerCase()) {
283
275
  case "number":
284
- return exports.EntityFieldTSType.Number;
276
+ return EntityFieldTSType.Number;
285
277
  case "boolean":
286
- return exports.EntityFieldTSType.Boolean;
278
+ return EntityFieldTSType.Boolean;
287
279
  case "date":
288
- return exports.EntityFieldTSType.Date;
280
+ return EntityFieldTSType.Date;
289
281
  default:
290
- return exports.EntityFieldTSType.String;
282
+ return EntityFieldTSType.String;
291
283
  }
292
284
  }
293
285
  /**
@@ -332,8 +324,8 @@ class EntityFieldInfo extends baseInfo_1.BaseInfo {
332
324
  */
333
325
  get NeedsQuotes() {
334
326
  switch (this.TSType) {
335
- case exports.EntityFieldTSType.Number:
336
- case exports.EntityFieldTSType.Boolean:
327
+ case EntityFieldTSType.Number:
328
+ case EntityFieldTSType.Boolean:
337
329
  return false;
338
330
  default:
339
331
  return true;
@@ -341,12 +333,12 @@ class EntityFieldInfo extends baseInfo_1.BaseInfo {
341
333
  }
342
334
  get CodeName() {
343
335
  if (this._codeName === null) {
344
- this._codeName = (0, util_1.CodeNameFromString)(this.Name);
336
+ this._codeName = CodeNameFromString(this.Name);
345
337
  }
346
338
  return this._codeName;
347
339
  }
348
340
  get GraphQLType() {
349
- switch ((0, util_1.TypeScriptTypeFromSQLType)(this.Type).toLowerCase()) {
341
+ switch (TypeScriptTypeFromSQLType(this.Type).toLowerCase()) {
350
342
  case "number":
351
343
  // either an int or float if not an int
352
344
  switch (this.Type.toLowerCase().trim()) {
@@ -354,30 +346,34 @@ class EntityFieldInfo extends baseInfo_1.BaseInfo {
354
346
  case "smallint":
355
347
  case "tinyint":
356
348
  case "bigint":
357
- return exports.EntityFieldGraphQLType.Int;
349
+ return EntityFieldGraphQLType.Int;
358
350
  default:
359
- return exports.EntityFieldGraphQLType.Float;
351
+ return EntityFieldGraphQLType.Float;
360
352
  }
361
353
  case "boolean":
362
- return exports.EntityFieldGraphQLType.Boolean;
354
+ return EntityFieldGraphQLType.Boolean;
363
355
  case "date":
364
- return exports.EntityFieldGraphQLType.Timestamp;
356
+ return EntityFieldGraphQLType.Timestamp;
365
357
  default:
366
- return exports.EntityFieldGraphQLType.String;
358
+ return EntityFieldGraphQLType.String;
367
359
  }
368
360
  }
369
361
  /**
370
362
  * Returns a string with the full SQL data type that combines, as appropriate, Type, Length, Precision and Scale where these attributes are relevant to the Type
371
363
  */
372
364
  get SQLFullType() {
373
- return (0, util_1.SQLFullType)(this.Type, this.Length, this.Precision, this.Scale);
365
+ return SQLFullType(this.Type, this.Length, this.Precision, this.Scale);
374
366
  }
375
367
  get MaxLength() {
376
- return (0, util_1.SQLMaxLength)(this.Type, this.Length);
368
+ return SQLMaxLength(this.Type, this.Length);
377
369
  }
378
370
  get ReadOnly() {
379
- return this.IsVirtual ||
380
- !this.AllowUpdateAPI ||
371
+ // Note: IsVirtual is intentionally NOT checked here. For IS-A (table-per-type) inheritance,
372
+ // parent entity fields on child entities are marked IsVirtual=1 (they don't exist in the
373
+ // child's base table) but ARE editable via the parent save chain. The AllowUpdateAPI flag
374
+ // already correctly distinguishes editable IS-A parent fields (AllowUpdateAPI=1) from
375
+ // truly read-only virtual fields like joined display names (AllowUpdateAPI=0).
376
+ return !this.AllowUpdateAPI ||
381
377
  this.IsPrimaryKey ||
382
378
  this.IsSpecialDateField;
383
379
  }
@@ -451,7 +447,7 @@ class EntityFieldInfo extends baseInfo_1.BaseInfo {
451
447
  * @returns either the original string value or a formatted version. If the format cannot be applied an an exception occurs it is captured and the error is put to the log, and the original value is returned
452
448
  */
453
449
  FormatValue(value, decimals = 2, currency = 'USD', maxLength = 0, trailingChars = "...") {
454
- return (0, util_1.FormatValue)(this.Type, value, decimals, currency, maxLength, trailingChars);
450
+ return FormatValue(this.Type, value, decimals, currency, maxLength, trailingChars);
455
451
  }
456
452
  constructor(initData = null) {
457
453
  super();
@@ -752,15 +748,26 @@ class EntityFieldInfo extends baseInfo_1.BaseInfo {
752
748
  }
753
749
  if (entityField.Status?.trim().toLowerCase() === 'deprecated') {
754
750
  // Record deprecation warning - will be batched and displayed after debounce period
755
- global_1.WarningManager.Instance.RecordFieldDeprecationWarning(entityField.Entity, entityField.Name, callerName);
751
+ WarningManager.Instance.RecordFieldDeprecationWarning(entityField.Entity, entityField.Name, callerName);
756
752
  }
757
753
  else if (entityField.Status?.trim().toLowerCase() === 'disabled') {
758
754
  // console.error and throw the exception
759
755
  const exceptionString = `${callerName}: Entity Field ${entityField.Entity}.${entityField.Name} is disabled and cannot be used.`;
760
- (0, logging_1.LogError)(exceptionString);
756
+ LogError(exceptionString);
761
757
  throw new Error(exceptionString);
762
758
  }
763
759
  }
760
+ /**
761
+ * Readonly array of SQL Server date/time functions that return the current date/time
762
+ */
763
+ static { this.SQL_CURRENT_DATE_FUNCTIONS = [
764
+ 'getdate()',
765
+ 'getutcdate()',
766
+ 'sysdatetimeoffset()',
767
+ 'current_timestamp',
768
+ 'sysdatetime()',
769
+ 'sysutcdatetime()'
770
+ ]; }
764
771
  /**
765
772
  * Checks if a default value is a SQL Server function that returns the current date/time
766
773
  * @param defaultValue - The default value to check
@@ -777,18 +784,6 @@ class EntityFieldInfo extends baseInfo_1.BaseInfo {
777
784
  return EntityFieldInfo.SQL_CURRENT_DATE_FUNCTIONS.some(func => normalizedValue.includes(func));
778
785
  }
779
786
  }
780
- exports.EntityFieldInfo = EntityFieldInfo;
781
- /**
782
- * Readonly array of SQL Server date/time functions that return the current date/time
783
- */
784
- EntityFieldInfo.SQL_CURRENT_DATE_FUNCTIONS = [
785
- 'getdate()',
786
- 'getutcdate()',
787
- 'sysdatetimeoffset()',
788
- 'current_timestamp',
789
- 'sysdatetime()',
790
- 'sysutcdatetime()'
791
- ];
792
787
  /**
793
788
  * Entity Document Type Info object has information about the document types that exist across all entities. When Entity Documents are created they are associated with a document type.
794
789
  */
@@ -796,7 +791,7 @@ EntityFieldInfo.SQL_CURRENT_DATE_FUNCTIONS = [
796
791
  * Defines types of documents that can be generated from entity data.
797
792
  * Supports various output formats for entity records.
798
793
  */
799
- class EntityDocumentTypeInfo extends baseInfo_1.BaseInfo {
794
+ export class EntityDocumentTypeInfo extends BaseInfo {
800
795
  constructor(initData = null) {
801
796
  super();
802
797
  this.ID = null;
@@ -807,7 +802,6 @@ class EntityDocumentTypeInfo extends baseInfo_1.BaseInfo {
807
802
  this.copyInitData(initData);
808
803
  }
809
804
  }
810
- exports.EntityDocumentTypeInfo = EntityDocumentTypeInfo;
811
805
  /**
812
806
  * Settings allow you to store key/value pairs of information that can be used to configure the behavior of the entity.
813
807
  */
@@ -815,7 +809,7 @@ exports.EntityDocumentTypeInfo = EntityDocumentTypeInfo;
815
809
  * Stores entity-specific configuration settings.
816
810
  * Allows customization of how entities function within the system.
817
811
  */
818
- class EntitySettingInfo extends baseInfo_1.BaseInfo {
812
+ export class EntitySettingInfo extends BaseInfo {
819
813
  constructor(initData = null) {
820
814
  super();
821
815
  this.ID = null;
@@ -828,12 +822,11 @@ class EntitySettingInfo extends baseInfo_1.BaseInfo {
828
822
  this.copyInitData(initData);
829
823
  }
830
824
  }
831
- exports.EntitySettingInfo = EntitySettingInfo;
832
825
  /**
833
826
  * Catalog of all entities across all schemas.
834
827
  * Contains comprehensive metadata about each entity including its database mappings, security settings, and UI preferences.
835
828
  */
836
- class EntityInfo extends baseInfo_1.BaseInfo {
829
+ export class EntityInfo extends BaseInfo {
837
830
  /**
838
831
  * Returns the primary key field for the entity. For entities with a composite primary key, use the PrimaryKeys property which returns all.
839
832
  * In the case of a composite primary key, the PrimaryKey property will return the first field in the sequence of the primary key fields.
@@ -897,6 +890,17 @@ class EntityInfo extends baseInfo_1.BaseInfo {
897
890
  get Settings() {
898
891
  return this._Settings;
899
892
  }
893
+ /**
894
+ * Gets the parsed FieldCategoryInfo map for this entity, keyed by category name.
895
+ * Auto-populated from the 'FieldCategoryInfo' EntitySetting (with legacy 'FieldCategoryIcons' fallback)
896
+ * during EntityInfo construction. Returns null if no category info is configured.
897
+ */
898
+ get FieldCategories() {
899
+ return this._FieldCategories;
900
+ }
901
+ static { this.__createdAtFieldName = '__mj_CreatedAt'; }
902
+ static { this.__updatedAtFieldName = '__mj_UpdatedAt'; }
903
+ static { this.__deletedAtFieldName = '__mj_DeletedAt'; }
900
904
  /**
901
905
  * Returns the name of the special reserved field that is used to store the CreatedAt timestamp across all of MJ. This is only used when an entity has TrackRecordChanges turned on
902
906
  */
@@ -928,12 +932,12 @@ class EntityInfo extends baseInfo_1.BaseInfo {
928
932
  }
929
933
  if (entity.Status?.trim().toLowerCase() === 'deprecated') {
930
934
  // Record deprecation warning - will be batched and displayed after debounce period
931
- global_1.WarningManager.Instance.RecordEntityDeprecationWarning(entity.Name, callerName);
935
+ WarningManager.Instance.RecordEntityDeprecationWarning(entity.Name, callerName);
932
936
  }
933
937
  else if (entity.Status?.trim().toLowerCase() === 'disabled') {
934
938
  // console.error and throw the exception
935
939
  const exceptionString = `${callerName}: Entity ${entity.Name} is disabled and cannot be used.`;
936
- (0, logging_1.LogError)(exceptionString);
940
+ LogError(exceptionString);
937
941
  throw new Error(exceptionString);
938
942
  }
939
943
  }
@@ -960,6 +964,104 @@ class EntityInfo extends baseInfo_1.BaseInfo {
960
964
  else
961
965
  return f;
962
966
  }
967
+ /**************************************************************************
968
+ * IS-A Type Relationship Computed Properties
969
+ *
970
+ * These properties support the IS-A (parent/child type) inheritance model
971
+ * where child entities share their parent's primary key and inherit all
972
+ * parent fields. Example: Meeting IS-A Product, Webinar IS-A Meeting.
973
+ **************************************************************************/
974
+ /**
975
+ * Returns the parent EntityInfo for IS-A type inheritance, or null if this entity
976
+ * has no parent type. Uses the existing ParentID column on the Entity table.
977
+ * Example: For "Meetings" entity with ParentID pointing to "Products", returns the Products EntityInfo.
978
+ */
979
+ get ParentEntityInfo() {
980
+ if (!this.ParentID)
981
+ return null;
982
+ return Metadata.Provider?.Entities?.find(e => e.ID === this.ParentID) ?? null;
983
+ }
984
+ /**
985
+ * Returns all child entities that have their ParentID set to this entity's ID.
986
+ * These represent IS-A type specializations of this entity.
987
+ * Example: For "Products" entity, might return [Meetings, Publications].
988
+ */
989
+ get ChildEntities() {
990
+ return Metadata.Provider?.Entities?.filter(e => e.ParentID === this.ID) ?? [];
991
+ }
992
+ /**
993
+ * Walks the IS-A chain upward from this entity to the root, returning all parent entities.
994
+ * Does NOT include this entity itself.
995
+ * Example: For Webinars (IS-A Meetings IS-A Products), returns [Meetings, Products].
996
+ * Results are cached after first computation for performance.
997
+ */
998
+ get ParentChain() {
999
+ if (this._parentChainCache !== null)
1000
+ return this._parentChainCache;
1001
+ const chain = [];
1002
+ let current = this.ParentEntityInfo;
1003
+ const visited = new Set(); // circular reference protection
1004
+ while (current) {
1005
+ if (visited.has(current.ID))
1006
+ break; // prevent infinite loop
1007
+ visited.add(current.ID);
1008
+ chain.push(current);
1009
+ current = current.ParentEntityInfo;
1010
+ }
1011
+ this._parentChainCache = chain;
1012
+ return chain;
1013
+ }
1014
+ /**
1015
+ * Returns true if this entity is a child type in an IS-A relationship (has a parent entity).
1016
+ */
1017
+ get IsChildType() {
1018
+ return this.ParentID != null;
1019
+ }
1020
+ /**
1021
+ * Returns true if this entity is a parent type in an IS-A relationship (has child entities).
1022
+ */
1023
+ get IsParentType() {
1024
+ return this.ChildEntities.length > 0;
1025
+ }
1026
+ /**
1027
+ * Returns all fields from all parent entities in the IS-A chain, excluding primary keys,
1028
+ * virtual fields, and timestamp fields (__mj_ prefixed). These represent the inherited
1029
+ * fields that should be available on child entities.
1030
+ */
1031
+ get AllParentFields() {
1032
+ const fields = [];
1033
+ for (const parent of this.ParentChain) {
1034
+ fields.push(...parent.Fields.filter(f => !f.IsPrimaryKey && !f.Name.startsWith('__mj_') && !f.IsVirtual));
1035
+ }
1036
+ return fields;
1037
+ }
1038
+ /**
1039
+ * Returns a cached Set of field names that belong to parent entities in the IS-A chain,
1040
+ * including the shared primary key(s). Used for efficient field routing in
1041
+ * BaseEntity.Set/Get/SetMany/Hydrate operations.
1042
+ * The Set enables O(1) lookup to determine if a field should be routed to the parent entity.
1043
+ *
1044
+ * Note: AllParentFields excludes PKs (they aren't "inherited data" fields), but the
1045
+ * routing set must include them so that SetMany and Hydrate can forward the shared
1046
+ * IS-A primary key to parent entities.
1047
+ */
1048
+ get ParentEntityFieldNames() {
1049
+ if (this._parentEntityFieldNamesCache !== null)
1050
+ return this._parentEntityFieldNamesCache;
1051
+ const names = this.AllParentFields.map(f => f.Name);
1052
+ // Add shared PK names from the immediate parent — these are excluded from
1053
+ // AllParentFields but must be routed so parent entities receive their identity.
1054
+ const parentEntity = this.ParentEntityInfo;
1055
+ if (parentEntity) {
1056
+ for (const pk of parentEntity.PrimaryKeys) {
1057
+ if (!names.includes(pk.Name)) {
1058
+ names.push(pk.Name);
1059
+ }
1060
+ }
1061
+ }
1062
+ this._parentEntityFieldNamesCache = new Set(names);
1063
+ return this._parentEntityFieldNamesCache;
1064
+ }
963
1065
  /**
964
1066
  * Returns the Permissions for this entity for a given user, based on the roles the user is part of
965
1067
  * @param user
@@ -1008,19 +1110,19 @@ class EntityInfo extends baseInfo_1.BaseInfo {
1008
1110
  const roleMatch = user.UserRoles?.find((r) => r.RoleID === ep.RoleID);
1009
1111
  if (roleMatch) { // user has this role
1010
1112
  switch (type) {
1011
- case exports.EntityPermissionType.Create:
1113
+ case EntityPermissionType.Create:
1012
1114
  if (!ep.CreateRLSFilterID)
1013
1115
  return true;
1014
1116
  break;
1015
- case exports.EntityPermissionType.Read:
1117
+ case EntityPermissionType.Read:
1016
1118
  if (!ep.ReadRLSFilterID)
1017
1119
  return true;
1018
1120
  break;
1019
- case exports.EntityPermissionType.Update:
1121
+ case EntityPermissionType.Update:
1020
1122
  if (!ep.UpdateRLSFilterID)
1021
1123
  return true;
1022
1124
  break;
1023
- case exports.EntityPermissionType.Delete:
1125
+ case EntityPermissionType.Delete:
1024
1126
  if (!ep.DeleteRLSFilterID)
1025
1127
  return true;
1026
1128
  break;
@@ -1043,19 +1145,19 @@ class EntityInfo extends baseInfo_1.BaseInfo {
1043
1145
  if (roleMatch) { // user has this role
1044
1146
  let matchObject = null;
1045
1147
  switch (type) {
1046
- case exports.EntityPermissionType.Create:
1148
+ case EntityPermissionType.Create:
1047
1149
  if (ep.CreateRLSFilterID)
1048
1150
  matchObject = ep.CreateRLSFilterObject;
1049
1151
  break;
1050
- case exports.EntityPermissionType.Read:
1152
+ case EntityPermissionType.Read:
1051
1153
  if (ep.ReadRLSFilterID)
1052
1154
  matchObject = ep.ReadRLSFilterObject;
1053
1155
  break;
1054
- case exports.EntityPermissionType.Update:
1156
+ case EntityPermissionType.Update:
1055
1157
  if (ep.UpdateRLSFilterID)
1056
1158
  matchObject = ep.UpdateRLSFilterObject;
1057
1159
  break;
1058
- case exports.EntityPermissionType.Delete:
1160
+ case EntityPermissionType.Delete:
1059
1161
  if (ep.DeleteRLSFilterID)
1060
1162
  matchObject = ep.DeleteRLSFilterObject;
1061
1163
  break;
@@ -1436,11 +1538,16 @@ class EntityInfo extends baseInfo_1.BaseInfo {
1436
1538
  this.ParentEntity = null;
1437
1539
  this.ParentBaseTable = null;
1438
1540
  this.ParentBaseView = null;
1541
+ this._FieldCategories = null;
1439
1542
  this._hasIdField = false;
1440
1543
  this._virtualCount = 0;
1441
1544
  this._manyToManyCount = 0;
1442
1545
  this._oneToManyCount = 0;
1443
1546
  this._floatCount = 0;
1547
+ // Cache for ParentChain to avoid repeated walks
1548
+ this._parentChainCache = null;
1549
+ // Cache for ParentEntityFieldNames
1550
+ this._parentEntityFieldNamesCache = null;
1444
1551
  if (initData) {
1445
1552
  this.copyInitData(initData);
1446
1553
  // do some special handling to create class instances instead of just data objects
@@ -1466,6 +1573,8 @@ class EntityInfo extends baseInfo_1.BaseInfo {
1466
1573
  if (es) {
1467
1574
  es.map((s) => this._Settings.push(new EntitySettingInfo(s)));
1468
1575
  }
1576
+ // auto-populate FieldCategories from the FieldCategoryInfo setting
1577
+ this._FieldCategories = this.parseFieldCategoriesFromSettings();
1469
1578
  // copy the Related Entities
1470
1579
  this._RelatedEntities = [];
1471
1580
  const er = initData.EntityRelationships || initData._RelatedEntities;
@@ -1525,47 +1634,65 @@ class EntityInfo extends baseInfo_1.BaseInfo {
1525
1634
  this._oneToManyCount = oneToManyCount;
1526
1635
  }
1527
1636
  catch (e) {
1528
- (0, logging_1.LogError)(e);
1637
+ LogError(e);
1638
+ }
1639
+ }
1640
+ /**
1641
+ * Parses FieldCategoryInfo from EntitySettings, with legacy FieldCategoryIcons fallback.
1642
+ * Called once during construction so the result is cached on _FieldCategories.
1643
+ */
1644
+ parseFieldCategoriesFromSettings() {
1645
+ if (!this._Settings || this._Settings.length === 0) {
1646
+ return null;
1647
+ }
1648
+ // Try new format first
1649
+ const infoSetting = this._Settings.find(s => s.Name === 'FieldCategoryInfo');
1650
+ if (infoSetting?.Value) {
1651
+ const parsed = SafeJSONParse(infoSetting.Value, false);
1652
+ if (parsed) {
1653
+ return parsed;
1654
+ }
1655
+ }
1656
+ // Fallback to legacy FieldCategoryIcons format (icon-only map)
1657
+ const iconSetting = this._Settings.find(s => s.Name === 'FieldCategoryIcons');
1658
+ if (iconSetting?.Value) {
1659
+ const icons = SafeJSONParse(iconSetting.Value, false);
1660
+ if (icons) {
1661
+ const result = {};
1662
+ for (const [category, icon] of Object.entries(icons)) {
1663
+ result[category] = { icon, description: '' };
1664
+ }
1665
+ return result;
1666
+ }
1529
1667
  }
1668
+ return null;
1530
1669
  }
1531
1670
  }
1532
- exports.EntityInfo = EntityInfo;
1533
- EntityInfo.__createdAtFieldName = '__mj_CreatedAt';
1534
- EntityInfo.__updatedAtFieldName = '__mj_UpdatedAt';
1535
- EntityInfo.__deletedAtFieldName = '__mj_DeletedAt';
1536
1671
  // Re-export validation types from @memberjunction/global for backward compatibility
1537
- var global_2 = require("@memberjunction/global");
1538
- Object.defineProperty(exports, "ValidationErrorType", { enumerable: true, get: function () { return global_2.ValidationErrorType; } });
1539
- Object.defineProperty(exports, "ValidationErrorInfo", { enumerable: true, get: function () { return global_2.ValidationErrorInfo; } });
1540
- Object.defineProperty(exports, "ValidationResult", { enumerable: true, get: function () { return global_2.ValidationResult; } });
1672
+ export { ValidationErrorType, ValidationErrorInfo, ValidationResult } from '@memberjunction/global';
1541
1673
  /**
1542
1674
  * Information about the link between two entities
1543
1675
  */
1544
- class EntityDependency {
1676
+ export class EntityDependency {
1545
1677
  }
1546
- exports.EntityDependency = EntityDependency;
1547
1678
  /**
1548
1679
  * Information about the link between two records
1549
1680
  */
1550
- class RecordDependency {
1681
+ export class RecordDependency {
1551
1682
  }
1552
- exports.RecordDependency = RecordDependency;
1553
1683
  /**
1554
1684
  * Information about a merge request including the entity, the surviving record and the records to merge into the surviving record. Additionally, there is an optional field map that can be used to override field values in the surviving record to values specified.
1555
1685
  */
1556
- class RecordMergeRequest {
1686
+ export class RecordMergeRequest {
1557
1687
  }
1558
- exports.RecordMergeRequest = RecordMergeRequest;
1559
1688
  /**
1560
1689
  * The result of a merge request for a single record
1561
1690
  */
1562
- class RecordMergeDetailResult {
1691
+ export class RecordMergeDetailResult {
1563
1692
  }
1564
- exports.RecordMergeDetailResult = RecordMergeDetailResult;
1565
1693
  /**
1566
1694
  * The result of a merge request
1567
1695
  */
1568
- class RecordMergeResult {
1696
+ export class RecordMergeResult {
1569
1697
  }
1570
- exports.RecordMergeResult = RecordMergeResult;
1571
1698
  //# sourceMappingURL=entityInfo.js.map