@memberjunction/core-entities-server 6.2.0-edge.5 → 6.2.0-edge.6
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/custom/MJAIAgentSessionEntityServer.server.d.ts +9 -26
- package/dist/custom/MJAIAgentSessionEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJAIAgentSessionEntityServer.server.js +21 -44
- package/dist/custom/MJAIAgentSessionEntityServer.server.js.map +1 -1
- package/dist/custom/MJComponentEntityServer.server.d.ts +9 -0
- package/dist/custom/MJComponentEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJComponentEntityServer.server.js +25 -1
- package/dist/custom/MJComponentEntityServer.server.js.map +1 -1
- package/dist/custom/MJConversationEntityServer.server.d.ts +13 -0
- package/dist/custom/MJConversationEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJConversationEntityServer.server.js +33 -0
- package/dist/custom/MJConversationEntityServer.server.js.map +1 -0
- package/dist/custom/MJDashboardEntityServer.server.d.ts +14 -0
- package/dist/custom/MJDashboardEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJDashboardEntityServer.server.js +38 -0
- package/dist/custom/MJDashboardEntityServer.server.js.map +1 -0
- package/dist/custom/MJDuplicateRunEntityServer.server.d.ts +34 -2
- package/dist/custom/MJDuplicateRunEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJDuplicateRunEntityServer.server.js +103 -23
- package/dist/custom/MJDuplicateRunEntityServer.server.js.map +1 -1
- package/dist/custom/MJEntityAlternateBaseViewEntityServer.server.d.ts +73 -0
- package/dist/custom/MJEntityAlternateBaseViewEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJEntityAlternateBaseViewEntityServer.server.js +391 -0
- package/dist/custom/MJEntityAlternateBaseViewEntityServer.server.js.map +1 -0
- package/dist/custom/MJEntityFieldPermissionEntityServer.server.d.ts +96 -22
- package/dist/custom/MJEntityFieldPermissionEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJEntityFieldPermissionEntityServer.server.js +239 -55
- package/dist/custom/MJEntityFieldPermissionEntityServer.server.js.map +1 -1
- package/dist/custom/MJQueryEntityServer.server.d.ts +8 -0
- package/dist/custom/MJQueryEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJQueryEntityServer.server.js +45 -2
- package/dist/custom/MJQueryEntityServer.server.js.map +1 -1
- package/dist/custom/MJUserRoleEntityServer.server.d.ts +74 -7
- package/dist/custom/MJUserRoleEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJUserRoleEntityServer.server.js +206 -20
- package/dist/custom/MJUserRoleEntityServer.server.js.map +1 -1
- package/dist/custom/MJUserViewEntityServer.server.d.ts +17 -1
- package/dist/custom/MJUserViewEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJUserViewEntityServer.server.js +95 -7
- package/dist/custom/MJUserViewEntityServer.server.js.map +1 -1
- package/dist/custom/MJVectorIndexEntityServer.server.d.ts +2 -0
- package/dist/custom/MJVectorIndexEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJVectorIndexEntityServer.server.js +33 -0
- package/dist/custom/MJVectorIndexEntityServer.server.js.map +1 -1
- package/dist/custom/anonymousOwnershipKeyGuard.d.ts +42 -0
- package/dist/custom/anonymousOwnershipKeyGuard.d.ts.map +1 -0
- package/dist/custom/anonymousOwnershipKeyGuard.js +36 -0
- package/dist/custom/anonymousOwnershipKeyGuard.js.map +1 -0
- package/dist/custom/approvalStamp.d.ts +37 -0
- package/dist/custom/approvalStamp.d.ts.map +1 -0
- package/dist/custom/approvalStamp.js +67 -0
- package/dist/custom/approvalStamp.js.map +1 -0
- package/dist/custom/fieldPermissionAccess.d.ts +31 -0
- package/dist/custom/fieldPermissionAccess.d.ts.map +1 -0
- package/dist/custom/fieldPermissionAccess.js +38 -0
- package/dist/custom/fieldPermissionAccess.js.map +1 -0
- package/dist/custom/fieldPermissionReconciler.d.ts.map +1 -1
- package/dist/custom/fieldPermissionReconciler.js +12 -2
- package/dist/custom/fieldPermissionReconciler.js.map +1 -1
- package/dist/custom/query-extraction/resolve.d.ts.map +1 -1
- package/dist/custom/query-extraction/resolve.js +25 -7
- package/dist/custom/query-extraction/resolve.js.map +1 -1
- package/dist/custom/query-extraction/sync.d.ts +1 -1
- package/dist/custom/query-extraction/sync.d.ts.map +1 -1
- package/dist/custom/query-extraction/sync.js +84 -21
- package/dist/custom/query-extraction/sync.js.map +1 -1
- package/dist/custom/query-extraction/types.d.ts +4 -0
- package/dist/custom/query-extraction/types.d.ts.map +1 -1
- package/dist/custom/systemUserGuardState.d.ts +73 -0
- package/dist/custom/systemUserGuardState.d.ts.map +1 -0
- package/dist/custom/systemUserGuardState.js +161 -0
- package/dist/custom/systemUserGuardState.js.map +1 -0
- package/dist/custom/template-extraction/parser.js +5 -2
- package/dist/custom/template-extraction/parser.js.map +1 -1
- package/dist/custom/template-extraction/types.d.ts +1 -1
- package/dist/custom/template-extraction/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/package.json +30 -30
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { BaseEntity, IsValidAlternateBaseViewName, IsValidSQLIdentifier, LogWarning, ValidationErrorInfo, ValidationErrorType, } from "@memberjunction/core";
|
|
8
|
+
import { MJEntityAlternateBaseViewEntity } from "@memberjunction/core-entities";
|
|
9
|
+
import { IndexByUUID, RegisterClass, UUIDsEqual } from "@memberjunction/global";
|
|
10
|
+
import { SQLParser } from "@memberjunction/sql-parser";
|
|
11
|
+
import { GetDialect } from "@memberjunction/sql-dialect";
|
|
12
|
+
/**
|
|
13
|
+
* Server-side entity subclass for `MJ: Entity Alternate Base Views`.
|
|
14
|
+
*
|
|
15
|
+
* Enforces:
|
|
16
|
+
* - Immutability of `EntityID` on existing saved records
|
|
17
|
+
* - Logical `Name` format (`IsValidAlternateBaseViewName` — non-empty, no `|`, `:`, or leading `//`)
|
|
18
|
+
* - Physical `ViewName` and optional `SchemaName` identifier format (`IsValidSQLIdentifier`)
|
|
19
|
+
* - Same-entity logical `Name` uniqueness
|
|
20
|
+
* - Cross-entity `(EffectiveSchemaName, ViewName)` uniqueness against every entity's
|
|
21
|
+
* `BaseView`, `BaseTable`, and registered `AlternateBaseViews` (D13)
|
|
22
|
+
* - Same-entity `ParentID` hierarchy validation and cycle detection (D15)
|
|
23
|
+
* - Live database row and physical SQL view existence verification in `ValidateAsync`
|
|
24
|
+
* - Automatic AST inference of `ParentID` on save when `ParentID` is unset and
|
|
25
|
+
* the view's SQL definition references a single sibling alternate base view on the same entity (D15)
|
|
26
|
+
*/
|
|
27
|
+
let MJEntityAlternateBaseViewEntityServer = class MJEntityAlternateBaseViewEntityServer extends MJEntityAlternateBaseViewEntity {
|
|
28
|
+
Validate() {
|
|
29
|
+
const result = super.Validate();
|
|
30
|
+
this.validateEntityIDImmutability(result);
|
|
31
|
+
this.validateNameAndIdentifiers(result);
|
|
32
|
+
const md = this.ProviderToUse;
|
|
33
|
+
const entities = md?.Entities ?? [];
|
|
34
|
+
const owningEntity = this.EntityID
|
|
35
|
+
? entities.find(e => UUIDsEqual(e.ID, this.EntityID))
|
|
36
|
+
: undefined;
|
|
37
|
+
if (this.EntityID && entities.length > 0 && !owningEntity) {
|
|
38
|
+
result.Errors.push(new ValidationErrorInfo('EntityID', `EntityID '${this.EntityID}' does not match any registered entity in metadata.`, this.EntityID, ValidationErrorType.Failure));
|
|
39
|
+
result.Success = false;
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
if (owningEntity) {
|
|
43
|
+
this.validateUniquenessAndCollisions(result, owningEntity, entities);
|
|
44
|
+
this.validateParentHierarchy(result, owningEntity, entities);
|
|
45
|
+
}
|
|
46
|
+
result.Success = result.Success && result.Errors.length === 0;
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
async ValidateAsync() {
|
|
50
|
+
const result = await super.ValidateAsync();
|
|
51
|
+
const md = this.ProviderToUse;
|
|
52
|
+
const entities = md?.Entities ?? [];
|
|
53
|
+
const owningEntity = this.EntityID
|
|
54
|
+
? entities.find(e => UUIDsEqual(e.ID, this.EntityID))
|
|
55
|
+
: undefined;
|
|
56
|
+
if (!owningEntity) {
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
await this.validateAgainstLiveDatabaseRows(result, owningEntity, entities);
|
|
60
|
+
await this.validatePhysicalViewExistsIfUsable(result, owningEntity);
|
|
61
|
+
result.Success = result.Success && result.Errors.length === 0;
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
async Save(options) {
|
|
65
|
+
if (typeof this.Name === 'string') {
|
|
66
|
+
this.Name = this.Name.trim();
|
|
67
|
+
}
|
|
68
|
+
if (typeof this.ViewName === 'string') {
|
|
69
|
+
this.ViewName = this.ViewName.trim();
|
|
70
|
+
}
|
|
71
|
+
if (typeof this.SchemaName === 'string') {
|
|
72
|
+
const trimmedSchema = this.SchemaName.trim();
|
|
73
|
+
this.SchemaName = trimmedSchema.length > 0 ? trimmedSchema : null;
|
|
74
|
+
}
|
|
75
|
+
// Auto-populate ParentID from the SQL view definition AST when ParentID is not explicitly set
|
|
76
|
+
if (!this.ParentID) {
|
|
77
|
+
await this.resolveAndPopulateParentIDFromDatabase();
|
|
78
|
+
}
|
|
79
|
+
return super.Save(options);
|
|
80
|
+
}
|
|
81
|
+
validateEntityIDImmutability(result) {
|
|
82
|
+
if (this.IsSaved && this.GetFieldByName('EntityID')?.Dirty === true) {
|
|
83
|
+
result.Errors.push(new ValidationErrorInfo('EntityID', `EntityID cannot be changed on an existing Alternate Base View record.`, this.EntityID, ValidationErrorType.Failure));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Validates `Name`, `ViewName`, and optional `SchemaName` using zero-dependency
|
|
88
|
+
* validators from `@memberjunction/core`.
|
|
89
|
+
*/
|
|
90
|
+
validateNameAndIdentifiers(result) {
|
|
91
|
+
if (!IsValidAlternateBaseViewName(this.Name)) {
|
|
92
|
+
result.Errors.push(new ValidationErrorInfo('Name', `Alternate Base View Name '${this.Name ?? ''}' is invalid. Name must be non-empty after trimming and must not contain '|', ':', or start with '//'.`, this.Name, ValidationErrorType.Failure));
|
|
93
|
+
}
|
|
94
|
+
if (!IsValidSQLIdentifier(this.ViewName)) {
|
|
95
|
+
result.Errors.push(new ValidationErrorInfo('ViewName', `ViewName '${this.ViewName ?? ''}' is not a valid bare SQL identifier. Provide only the view name without schema prefix, brackets, quotes, or whitespace.`, this.ViewName, ValidationErrorType.Failure));
|
|
96
|
+
}
|
|
97
|
+
if (this.SchemaName != null && this.SchemaName.trim().length > 0 && !IsValidSQLIdentifier(this.SchemaName.trim())) {
|
|
98
|
+
result.Errors.push(new ValidationErrorInfo('SchemaName', `SchemaName '${this.SchemaName}' is not a valid bare SQL identifier.`, this.SchemaName, ValidationErrorType.Failure));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validates:
|
|
103
|
+
* 1. Same-entity logical `Name` uniqueness
|
|
104
|
+
* 2. Cross-entity `(EffectiveSchemaName, ViewName)` uniqueness against every entity's
|
|
105
|
+
* `BaseView`, `BaseTable`, and registered `AlternateBaseViews` (D13)
|
|
106
|
+
*/
|
|
107
|
+
validateUniquenessAndCollisions(result, owningEntity, allEntities) {
|
|
108
|
+
const trimmedName = (this.Name ?? '').trim().toLowerCase();
|
|
109
|
+
const trimmedViewName = (this.ViewName ?? '').trim().toLowerCase();
|
|
110
|
+
const effectiveSchema = ((this.SchemaName ?? '').trim() || owningEntity.SchemaName || '').toLowerCase();
|
|
111
|
+
if (!trimmedViewName) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (trimmedName) {
|
|
115
|
+
this.checkSameEntityLogicalNameDuplicate(result, owningEntity, trimmedName);
|
|
116
|
+
}
|
|
117
|
+
for (const entity of allEntities) {
|
|
118
|
+
this.checkCrossEntityPhysicalCollision(result, entity, effectiveSchema, trimmedViewName);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
checkSameEntityLogicalNameDuplicate(result, owningEntity, trimmedName) {
|
|
122
|
+
const duplicateLogicalName = (owningEntity.AlternateBaseViews ?? []).find(abv => !UUIDsEqual(abv.ID, this.ID) && abv.Name.trim().toLowerCase() === trimmedName);
|
|
123
|
+
if (duplicateLogicalName) {
|
|
124
|
+
result.Errors.push(new ValidationErrorInfo('Name', `Entity '${owningEntity.Name}' already has an alternate base view named '${duplicateLogicalName.Name}'.`, this.Name, ValidationErrorType.Failure));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
checkCrossEntityPhysicalCollision(result, entity, effectiveSchema, trimmedViewName) {
|
|
128
|
+
const entitySchema = (entity.SchemaName ?? '').toLowerCase();
|
|
129
|
+
if (entitySchema === effectiveSchema) {
|
|
130
|
+
if ((entity.BaseView ?? '').toLowerCase() === trimmedViewName) {
|
|
131
|
+
result.Errors.push(new ValidationErrorInfo('ViewName', `Physical view '${effectiveSchema}.${this.ViewName}' collides with the canonical BaseView of entity '${entity.Name}'.`, this.ViewName, ValidationErrorType.Failure));
|
|
132
|
+
}
|
|
133
|
+
else if ((entity.BaseTable ?? '').toLowerCase() === trimmedViewName) {
|
|
134
|
+
result.Errors.push(new ValidationErrorInfo('ViewName', `Physical view '${effectiveSchema}.${this.ViewName}' collides with the BaseTable of entity '${entity.Name}'.`, this.ViewName, ValidationErrorType.Failure));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
this.checkExistingAlternateViewCollisionsOnEntity(result, entity, effectiveSchema, trimmedViewName);
|
|
138
|
+
}
|
|
139
|
+
checkExistingAlternateViewCollisionsOnEntity(result, entity, effectiveSchema, trimmedViewName) {
|
|
140
|
+
for (const existingAbv of entity.AlternateBaseViews ?? []) {
|
|
141
|
+
if (UUIDsEqual(existingAbv.ID, this.ID)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const existingSchema = (existingAbv.EffectiveSchemaName || entity.SchemaName || '').toLowerCase();
|
|
145
|
+
if (existingSchema === effectiveSchema && existingAbv.ViewName.trim().toLowerCase() === trimmedViewName) {
|
|
146
|
+
result.Errors.push(new ValidationErrorInfo('ViewName', `Physical view '${effectiveSchema}.${this.ViewName}' is already registered as alternate base view '${existingAbv.Name}' on entity '${entity.Name}'.`, this.ViewName, ValidationErrorType.Failure));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Validates `ParentID` (when set):
|
|
152
|
+
* - Cannot self-reference (`ParentID === ID`)
|
|
153
|
+
* - Parent must exist and belong to the same `EntityID`
|
|
154
|
+
* - Parent chain must be acyclic
|
|
155
|
+
*/
|
|
156
|
+
validateParentHierarchy(result, owningEntity, allEntities) {
|
|
157
|
+
if (!this.ParentID) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (this.ID && UUIDsEqual(this.ParentID, this.ID)) {
|
|
161
|
+
result.Errors.push(new ValidationErrorInfo('ParentID', `An alternate base view cannot reference itself as ParentID.`, this.ParentID, ValidationErrorType.Failure));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const siblingsById = IndexByUUID(owningEntity.AlternateBaseViews ?? []);
|
|
165
|
+
const sameEntityParent = siblingsById.Get(this.ParentID);
|
|
166
|
+
if (!sameEntityParent) {
|
|
167
|
+
this.checkCrossEntityParentOwner(result, owningEntity, allEntities);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this.checkParentChainForCycle(result, owningEntity, sameEntityParent, siblingsById);
|
|
171
|
+
}
|
|
172
|
+
checkCrossEntityParentOwner(result, owningEntity, allEntities) {
|
|
173
|
+
const otherEntityOwner = allEntities.find(e => IndexByUUID(e.AlternateBaseViews ?? []).Has(this.ParentID));
|
|
174
|
+
if (otherEntityOwner && !UUIDsEqual(otherEntityOwner.ID, owningEntity.ID)) {
|
|
175
|
+
result.Errors.push(new ValidationErrorInfo('ParentID', `ParentID '${this.ParentID}' belongs to entity '${otherEntityOwner.Name}', not '${owningEntity.Name}'. Derived alternate base views must belong to the same entity.`, this.ParentID, ValidationErrorType.Failure));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
checkParentChainForCycle(result, owningEntity, sameEntityParent, siblingsById) {
|
|
179
|
+
const visited = new Set();
|
|
180
|
+
if (this.ID) {
|
|
181
|
+
visited.add(this.ID.toLowerCase());
|
|
182
|
+
}
|
|
183
|
+
let cursor = sameEntityParent;
|
|
184
|
+
while (cursor) {
|
|
185
|
+
const cursorKey = cursor.ID.toLowerCase();
|
|
186
|
+
if (visited.has(cursorKey)) {
|
|
187
|
+
result.Errors.push(new ValidationErrorInfo('ParentID', `ParentID '${this.ParentID}' introduces a cycle in the alternate base view hierarchy for '${owningEntity.Name}'.`, this.ParentID, ValidationErrorType.Failure));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
visited.add(cursorKey);
|
|
191
|
+
cursor = cursor.ParentID ? siblingsById.Get(cursor.ParentID) : undefined;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Queries `MJ: Entity Alternate Base Views` with `BypassCache: true` so uniqueness,
|
|
196
|
+
* cross-entity collision, and `ParentID` hierarchy checks also catch rows created
|
|
197
|
+
* since the last metadata refresh.
|
|
198
|
+
*/
|
|
199
|
+
async validateAgainstLiveDatabaseRows(result, owningEntity, allEntities) {
|
|
200
|
+
const rvProvider = this.ProviderToUse;
|
|
201
|
+
if (!rvProvider || typeof rvProvider.RunView !== 'function') {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const rvResult = await rvProvider.RunView({
|
|
206
|
+
EntityName: 'MJ: Entity Alternate Base Views',
|
|
207
|
+
Fields: ['ID', 'EntityID', 'ParentID', 'Name', 'ViewName', 'SchemaName'],
|
|
208
|
+
ResultType: 'simple',
|
|
209
|
+
BypassCache: true,
|
|
210
|
+
}, this.ContextCurrentUser);
|
|
211
|
+
if (!rvResult || !rvResult.Success) {
|
|
212
|
+
result.Errors.push(new ValidationErrorInfo('Name', `Could not verify live MJ: Entity Alternate Base Views records (${rvResult?.ErrorMessage ?? 'RunView returned Success: false'}).`, this.Name, ValidationErrorType.Failure));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (!rvResult.Results || rvResult.Results.length === 0) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
this.checkLiveDatabaseRowsForConflicts(result, owningEntity, allEntities, rvResult.Results);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
result.Errors.push(new ValidationErrorInfo('Name', `Could not verify live MJ: Entity Alternate Base Views records (${err instanceof Error ? err.message : String(err)}).`, this.Name, ValidationErrorType.Failure));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
checkLiveDatabaseRowsForConflicts(result, owningEntity, allEntities, rows) {
|
|
225
|
+
const entitiesById = IndexByUUID(allEntities);
|
|
226
|
+
const trimmedName = (this.Name ?? '').trim().toLowerCase();
|
|
227
|
+
const trimmedViewName = (this.ViewName ?? '').trim().toLowerCase();
|
|
228
|
+
const effectiveSchema = ((this.SchemaName ?? '').trim() || owningEntity.SchemaName || '').toLowerCase();
|
|
229
|
+
for (const row of rows) {
|
|
230
|
+
if (!row.ID || UUIDsEqual(row.ID, this.ID)) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const rowEntity = entitiesById.Get(row.EntityID);
|
|
234
|
+
const rowSchema = ((row.SchemaName ?? '').trim() || rowEntity?.SchemaName || '').toLowerCase();
|
|
235
|
+
if (UUIDsEqual(row.EntityID, owningEntity.ID) &&
|
|
236
|
+
trimmedName &&
|
|
237
|
+
(row.Name ?? '').trim().toLowerCase() === trimmedName &&
|
|
238
|
+
!result.Errors.some(e => e.Source === 'Name')) {
|
|
239
|
+
result.Errors.push(new ValidationErrorInfo('Name', `Entity '${owningEntity.Name}' already has an alternate base view named '${row.Name}'.`, this.Name, ValidationErrorType.Failure));
|
|
240
|
+
}
|
|
241
|
+
if (trimmedViewName &&
|
|
242
|
+
rowSchema === effectiveSchema &&
|
|
243
|
+
(row.ViewName ?? '').trim().toLowerCase() === trimmedViewName &&
|
|
244
|
+
!result.Errors.some(e => e.Source === 'ViewName')) {
|
|
245
|
+
result.Errors.push(new ValidationErrorInfo('ViewName', `Physical view '${effectiveSchema}.${this.ViewName}' is already registered as alternate base view '${row.Name}' on entity '${rowEntity?.Name ?? row.EntityID}'.`, this.ViewName, ValidationErrorType.Failure));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* When `Status` is `'Active'` or `'Deprecated'` and a database provider is active, verifies
|
|
251
|
+
* that the physical SQL view exists in the database catalog.
|
|
252
|
+
*/
|
|
253
|
+
async validatePhysicalViewExistsIfUsable(result, owningEntity) {
|
|
254
|
+
if (this.Status !== 'Active' && this.Status !== 'Deprecated') {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const effectiveSchema = (this.SchemaName ?? '').trim() || owningEntity.SchemaName;
|
|
258
|
+
if (!IsValidSQLIdentifier(this.ViewName) || !IsValidSQLIdentifier(effectiveSchema)) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const dbProvider = this.ProviderToUse;
|
|
262
|
+
if (!dbProvider || typeof dbProvider.ExecuteSQL !== 'function') {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const isPostgres = this.ProviderToUse?.PlatformKey === 'postgresql';
|
|
266
|
+
const probeSql = isPostgres
|
|
267
|
+
? `SELECT CASE WHEN to_regclass('"${effectiveSchema}"."${this.ViewName}"') IS NOT NULL THEN 1 ELSE 0 END AS "ViewExists"`
|
|
268
|
+
: `SELECT CASE WHEN OBJECT_ID(N'[${effectiveSchema}].[${this.ViewName}]', N'V') IS NOT NULL THEN 1 ELSE 0 END AS ViewExists`;
|
|
269
|
+
try {
|
|
270
|
+
const rows = await dbProvider.ExecuteSQL(probeSql);
|
|
271
|
+
if (rows && rows.length > 0 && rows[0] && rows[0].ViewExists === 0) {
|
|
272
|
+
result.Errors.push(new ValidationErrorInfo('ViewName', `Physical SQL view '${effectiveSchema}.${this.ViewName}' does not exist in the database.`, this.ViewName, ValidationErrorType.Failure));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
LogWarning(`MJEntityAlternateBaseViewEntityServer.validatePhysicalViewExistsIfUsable: catalog probe skipped for '${effectiveSchema}.${this.ViewName}' (${err instanceof Error ? err.message : String(err)})`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Queries the database catalog for the SQL definition of `EffectiveSchemaName.ViewName`
|
|
281
|
+
* when running against a database provider, then invokes `TryAutoPopulateParentIDFromAST`.
|
|
282
|
+
*/
|
|
283
|
+
async resolveAndPopulateParentIDFromDatabase() {
|
|
284
|
+
if (this.ParentID || !this.EntityID || !IsValidSQLIdentifier(this.ViewName)) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const md = this.ProviderToUse;
|
|
288
|
+
const owningEntity = md?.Entities?.find(e => UUIDsEqual(e.ID, this.EntityID));
|
|
289
|
+
if (!owningEntity || (owningEntity.AlternateBaseViews ?? []).length === 0) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const effectiveSchema = (this.SchemaName ?? '').trim() || owningEntity.SchemaName;
|
|
293
|
+
if (!IsValidSQLIdentifier(effectiveSchema)) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const dbProvider = this.ProviderToUse;
|
|
297
|
+
if (!dbProvider || typeof dbProvider.ExecuteSQL !== 'function') {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const isPostgres = this.ProviderToUse?.PlatformKey === 'postgresql';
|
|
301
|
+
const sql = isPostgres
|
|
302
|
+
? `SELECT pg_get_viewdef(to_regclass('"${effectiveSchema}"."${this.ViewName}"'), true) AS "ViewDefinition"`
|
|
303
|
+
: `SELECT OBJECT_DEFINITION(OBJECT_ID(N'[${effectiveSchema}].[${this.ViewName}]')) AS ViewDefinition`;
|
|
304
|
+
try {
|
|
305
|
+
const rows = await dbProvider.ExecuteSQL(sql);
|
|
306
|
+
const viewDef = rows?.[0]?.ViewDefinition;
|
|
307
|
+
if (viewDef && viewDef.trim().length > 0) {
|
|
308
|
+
this.TryAutoPopulateParentIDFromAST(viewDef);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch (err) {
|
|
312
|
+
LogWarning(`MJEntityAlternateBaseViewEntityServer.resolveAndPopulateParentIDFromDatabase: catalog lookup failed for '${effectiveSchema}.${this.ViewName}' on entity '${owningEntity.Name}': ${err instanceof Error ? err.message : String(err)}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Inspects `viewDefinitionSQL` with `SQLParser.ExtractTableRefs` and, if the SQL
|
|
317
|
+
* references exactly one sibling alternate base view on the same entity (without creating
|
|
318
|
+
* a cycle), automatically populates `this.ParentID`.
|
|
319
|
+
*/
|
|
320
|
+
TryAutoPopulateParentIDFromAST(viewDefinitionSQL) {
|
|
321
|
+
if (this.ParentID || !viewDefinitionSQL || !this.EntityID) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const md = this.ProviderToUse;
|
|
325
|
+
const owningEntity = (md?.Entities ?? []).find(e => UUIDsEqual(e.ID, this.EntityID));
|
|
326
|
+
if (!owningEntity || !owningEntity.AlternateBaseViews || owningEntity.AlternateBaseViews.length === 0) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const isPostgres = this.ProviderToUse?.PlatformKey === 'postgresql';
|
|
330
|
+
try {
|
|
331
|
+
const tableRefs = SQLParser.ExtractTableRefs(viewDefinitionSQL, GetDialect(isPostgres ? 'postgresql' : 'sqlserver'));
|
|
332
|
+
if (!tableRefs || tableRefs.length === 0) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const matchedSiblings = this.findMatchingSiblingsInTableRefs(owningEntity, tableRefs);
|
|
336
|
+
if (matchedSiblings.size === 1) {
|
|
337
|
+
const candidate = Array.from(matchedSiblings.values())[0];
|
|
338
|
+
if (!this.wouldCreateCycle(candidate, owningEntity.AlternateBaseViews)) {
|
|
339
|
+
this.ParentID = candidate.ID;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
LogWarning(`MJEntityAlternateBaseViewEntityServer.TryAutoPopulateParentIDFromAST: failed to parse view definition for '${this.ViewName}' on entity '${owningEntity.Name}': ${err instanceof Error ? err.message : String(err)}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
findMatchingSiblingsInTableRefs(owningEntity, tableRefs) {
|
|
348
|
+
const matchedSiblings = new Map();
|
|
349
|
+
for (const ref of tableRefs) {
|
|
350
|
+
const refTableLower = ref.TableName.toLowerCase();
|
|
351
|
+
const refSchemaLower = ref.SchemaName ? ref.SchemaName.toLowerCase() : null;
|
|
352
|
+
for (const sibling of owningEntity.AlternateBaseViews ?? []) {
|
|
353
|
+
if ((this.ID && UUIDsEqual(sibling.ID, this.ID)) ||
|
|
354
|
+
(this.ViewName && sibling.ViewName.toLowerCase() === this.ViewName.trim().toLowerCase())) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (sibling.ViewName.toLowerCase() !== refTableLower) {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
const siblingSchema = (sibling.EffectiveSchemaName || owningEntity.SchemaName || '').toLowerCase();
|
|
361
|
+
if (refSchemaLower && refSchemaLower !== siblingSchema) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
matchedSiblings.set(sibling.ID.toLowerCase(), sibling);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return matchedSiblings;
|
|
368
|
+
}
|
|
369
|
+
wouldCreateCycle(candidateParent, siblings) {
|
|
370
|
+
if (!this.ID) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
const siblingsById = IndexByUUID(siblings);
|
|
374
|
+
const visited = new Set([this.ID.toLowerCase()]);
|
|
375
|
+
let cursor = candidateParent;
|
|
376
|
+
while (cursor) {
|
|
377
|
+
const key = cursor.ID.toLowerCase();
|
|
378
|
+
if (visited.has(key)) {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
visited.add(key);
|
|
382
|
+
cursor = cursor.ParentID ? siblingsById.Get(cursor.ParentID) : undefined;
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
MJEntityAlternateBaseViewEntityServer = __decorate([
|
|
388
|
+
RegisterClass(BaseEntity, 'MJ: Entity Alternate Base Views')
|
|
389
|
+
], MJEntityAlternateBaseViewEntityServer);
|
|
390
|
+
export { MJEntityAlternateBaseViewEntityServer };
|
|
391
|
+
//# sourceMappingURL=MJEntityAlternateBaseViewEntityServer.server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MJEntityAlternateBaseViewEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJEntityAlternateBaseViewEntityServer.server.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EACH,UAAU,EAOV,4BAA4B,EAC5B,oBAAoB,EACpB,UAAU,EACV,mBAAmB,EACnB,mBAAmB,GAEtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAWzD;;;;;;;;;;;;;;GAcG;AAEI,IAAM,qCAAqC,GAA3C,MAAM,qCAAsC,SAAQ,+BAA+B;IACtE,QAAQ;QACpB,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,IAAI,CAAC,4BAA4B,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,EAAE,GAAG,IAAI,CAAC,aAAyD,CAAC;QAC1E,MAAM,QAAQ,GAAG,EAAE,EAAE,QAAQ,IAAI,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ;YAC9B,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrD,CAAC,CAAC,SAAS,CAAC;QAEhB,IAAI,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACxD,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,aAAa,IAAI,CAAC,QAAQ,qDAAqD,EAC/E,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACF,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;YACvB,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,IAAI,YAAY,EAAE,CAAC;YACf,IAAI,CAAC,+BAA+B,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;YACrE,IAAI,CAAC,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;QAC9D,OAAO,MAAM,CAAC;IAClB,CAAC;IAEe,KAAK,CAAC,aAAa;QAC/B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE,CAAC;QAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,aAAyD,CAAC;QAC1E,MAAM,QAAQ,GAAG,EAAE,EAAE,QAAQ,IAAI,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ;YAC9B,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrD,CAAC,CAAC,SAAS,CAAC;QAChB,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,MAAM,IAAI,CAAC,+BAA+B,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;QAC3E,MAAM,IAAI,CAAC,kCAAkC,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAEpE,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;QAC9D,OAAO,MAAM,CAAC;IAClB,CAAC;IAEe,KAAK,CAAC,IAAI,CAAC,OAA2B;QAClD,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YAC7C,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;QACtE,CAAC;QAED,8FAA8F;QAC9F,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,sCAAsC,EAAE,CAAC;QACxD,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAEO,4BAA4B,CAAC,MAAwB;QACzD,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,KAAK,KAAK,IAAI,EAAE,CAAC;YAClE,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,uEAAuE,EACvE,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;QACN,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,0BAA0B,CAAC,MAAwB;QACvD,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,MAAM,EACN,6BAA6B,IAAI,CAAC,IAAI,IAAI,EAAE,wGAAwG,EACpJ,IAAI,CAAC,IAAI,EACT,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;QACN,CAAC;QAED,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,aAAa,IAAI,CAAC,QAAQ,IAAI,EAAE,0HAA0H,EAC1J,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;QACN,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAChH,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,YAAY,EACZ,eAAe,IAAI,CAAC,UAAU,uCAAuC,EACrE,IAAI,CAAC,UAAU,EACf,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;QACN,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACK,+BAA+B,CACnC,MAAwB,EACxB,YAAwB,EACxB,WAAyB;QAEzB,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3D,MAAM,eAAe,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACnE,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,YAAY,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACxG,IAAI,CAAC,eAAe,EAAE,CAAC;YACnB,OAAO;QACX,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YACd,IAAI,CAAC,mCAAmC,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;QAChF,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,iCAAiC,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;QAC7F,CAAC;IACL,CAAC;IAEO,mCAAmC,CACvC,MAAwB,EACxB,YAAwB,EACxB,WAAmB;QAEnB,MAAM,oBAAoB,GAAG,CAAC,YAAY,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,IAAI,CACrE,GAAG,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,WAAW,CACvF,CAAC;QACF,IAAI,oBAAoB,EAAE,CAAC;YACvB,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,MAAM,EACN,WAAW,YAAY,CAAC,IAAI,+CAA+C,oBAAoB,CAAC,IAAI,IAAI,EACxG,IAAI,CAAC,IAAI,EACT,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;QACN,CAAC;IACL,CAAC;IAEO,iCAAiC,CACrC,MAAwB,EACxB,MAAkB,EAClB,eAAuB,EACvB,eAAuB;QAEvB,MAAM,YAAY,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7D,IAAI,YAAY,KAAK,eAAe,EAAE,CAAC;YACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,eAAe,EAAE,CAAC;gBAC5D,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,kBAAkB,eAAe,IAAI,IAAI,CAAC,QAAQ,qDAAqD,MAAM,CAAC,IAAI,IAAI,EACtH,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACN,CAAC;iBAAM,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,eAAe,EAAE,CAAC;gBACpE,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,kBAAkB,eAAe,IAAI,IAAI,CAAC,QAAQ,4CAA4C,MAAM,CAAC,IAAI,IAAI,EAC7G,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACN,CAAC;QACL,CAAC;QACD,IAAI,CAAC,4CAA4C,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;IACxG,CAAC;IAEO,4CAA4C,CAChD,MAAwB,EACxB,MAAkB,EAClB,eAAuB,EACvB,eAAuB;QAEvB,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,kBAAkB,IAAI,EAAE,EAAE,CAAC;YACxD,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBACtC,SAAS;YACb,CAAC;YACD,MAAM,cAAc,GAAG,CAAC,WAAW,CAAC,mBAAmB,IAAI,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;YAClG,IAAI,cAAc,KAAK,eAAe,IAAI,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,eAAe,EAAE,CAAC;gBACtG,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,kBAAkB,eAAe,IAAI,IAAI,CAAC,QAAQ,mDAAmD,WAAW,CAAC,IAAI,gBAAgB,MAAM,CAAC,IAAI,IAAI,EACpJ,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACN,CAAC;QACL,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACK,uBAAuB,CAC3B,MAAwB,EACxB,YAAwB,EACxB,WAAyB;QAEzB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjB,OAAO;QACX,CAAC;QACD,IAAI,IAAI,CAAC,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,6DAA6D,EAC7D,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACF,OAAO;QACX,CAAC;QAED,MAAM,YAAY,GAAG,WAAW,CAAC,YAAY,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC;QACxE,MAAM,gBAAgB,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzD,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACpB,IAAI,CAAC,2BAA2B,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;YACpE,OAAO;QACX,CAAC;QACD,IAAI,CAAC,wBAAwB,CAAC,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,CAAC,CAAC;IACxF,CAAC;IAEO,2BAA2B,CAC/B,MAAwB,EACxB,YAAwB,EACxB,WAAyB;QAEzB,MAAM,gBAAgB,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAC1C,WAAW,CAAC,CAAC,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAS,CAAC,CAC9D,CAAC;QACF,IAAI,gBAAgB,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,EAAE,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC;YACxE,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,aAAa,IAAI,CAAC,QAAQ,wBAAwB,gBAAgB,CAAC,IAAI,WAAW,YAAY,CAAC,IAAI,iEAAiE,EACpK,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;QACN,CAAC;IACL,CAAC;IAEO,wBAAwB,CAC5B,MAAwB,EACxB,YAAwB,EACxB,gBAA6C,EAC7C,YAA0E;QAE1E,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,MAAM,GAA4C,gBAAgB,CAAC;QACvE,OAAO,MAAM,EAAE,CAAC;YACZ,MAAM,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzB,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,aAAa,IAAI,CAAC,QAAQ,kEAAkE,YAAY,CAAC,IAAI,IAAI,EACjH,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;gBACF,OAAO;YACX,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACvB,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7E,CAAC;IACL,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,+BAA+B,CACzC,MAAwB,EACxB,YAAwB,EACxB,WAAyB;QAEzB,MAAM,UAAU,GAAG,IAAI,CAAC,aAAiE,CAAC;QAC1F,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YAC1D,OAAO;QACX,CAAC;QACD,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,OAAO,CACrC;gBACI,UAAU,EAAE,iCAAiC;gBAC7C,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,CAAC;gBACxE,UAAU,EAAE,QAAQ;gBACpB,WAAW,EAAE,IAAI;aACpB,EACD,IAAI,CAAC,kBAAkB,CAC1B,CAAC;YACF,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACjC,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,MAAM,EACN,kEAAkE,QAAQ,EAAE,YAAY,IAAI,iCAAiC,IAAI,EACjI,IAAI,CAAC,IAAI,EACT,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;gBACF,OAAO;YACX,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACrD,OAAO;YACX,CAAC;YACD,IAAI,CAAC,iCAAiC,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChG,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,MAAM,EACN,kEAAkE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EACtH,IAAI,CAAC,IAAI,EACT,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;QACN,CAAC;IACL,CAAC;IAEO,iCAAiC,CACrC,MAAwB,EACxB,YAAwB,EACxB,WAAyB,EACzB,IAAgC;QAEhC,MAAM,YAAY,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;QAC9C,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3D,MAAM,eAAe,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACnE,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,YAAY,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAExG,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzC,SAAS;YACb,CAAC;YACD,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACjD,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;YAC/F,IACI,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC;gBACzC,WAAW;gBACX,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,WAAW;gBACrD,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,EAC/C,CAAC;gBACC,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,MAAM,EACN,WAAW,YAAY,CAAC,IAAI,+CAA+C,GAAG,CAAC,IAAI,IAAI,EACvF,IAAI,CAAC,IAAI,EACT,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACN,CAAC;YACD,IACI,eAAe;gBACf,SAAS,KAAK,eAAe;gBAC7B,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,eAAe;gBAC7D,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,EACnD,CAAC;gBACC,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,kBAAkB,eAAe,IAAI,IAAI,CAAC,QAAQ,mDAAmD,GAAG,CAAC,IAAI,gBAAgB,SAAS,EAAE,IAAI,IAAI,GAAG,CAAC,QAAQ,IAAI,EAChK,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACN,CAAC;QACL,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,kCAAkC,CAC5C,MAAwB,EACxB,YAAwB;QAExB,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;YAC3D,OAAO;QACX,CAAC;QACD,MAAM,eAAe,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,YAAY,CAAC,UAAU,CAAC;QAClF,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,oBAAoB,CAAC,eAAe,CAAC,EAAE,CAAC;YACjF,OAAO;QACX,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,aAAqE,CAAC;QAC9F,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YAC7D,OAAO;QACX,CAAC;QACD,MAAM,UAAU,GAAI,IAAI,CAAC,aAA0C,EAAE,WAAW,KAAK,YAAY,CAAC;QAClG,MAAM,QAAQ,GAAG,UAAU;YACvB,CAAC,CAAC,kCAAkC,eAAe,MAAM,IAAI,CAAC,QAAQ,mDAAmD;YACzH,CAAC,CAAC,iCAAiC,eAAe,MAAM,IAAI,CAAC,QAAQ,uDAAuD,CAAC;QACjI,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,UAAU,CAA0B,QAAQ,CAAC,CAAC;YAC5E,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;gBACjE,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,UAAU,EACV,sBAAsB,eAAe,IAAI,IAAI,CAAC,QAAQ,mCAAmC,EACzF,IAAI,CAAC,QAAQ,EACb,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACN,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,UAAU,CACN,wGAAwG,eAAe,IAAI,IAAI,CAAC,QAAQ,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CACpM,CAAC;QACN,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,sCAAsC;QAChD,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1E,OAAO;QACX,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC,aAAyD,CAAC;QAC1E,MAAM,YAAY,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC9E,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxE,OAAO;QACX,CAAC;QAED,MAAM,eAAe,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,YAAY,CAAC,UAAU,CAAC;QAClF,IAAI,CAAC,oBAAoB,CAAC,eAAe,CAAC,EAAE,CAAC;YACzC,OAAO;QACX,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,aAAqE,CAAC;QAC9F,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YAC7D,OAAO;QACX,CAAC;QAED,MAAM,UAAU,GAAI,IAAI,CAAC,aAA0C,EAAE,WAAW,KAAK,YAAY,CAAC;QAClG,MAAM,GAAG,GAAG,UAAU;YAClB,CAAC,CAAC,uCAAuC,eAAe,MAAM,IAAI,CAAC,QAAQ,gCAAgC;YAC3G,CAAC,CAAC,yCAAyC,eAAe,MAAM,IAAI,CAAC,QAAQ,wBAAwB,CAAC;QAE1G,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,UAAU,CAAoC,GAAG,CAAC,CAAC;YACjF,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC;YAC1C,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvC,IAAI,CAAC,8BAA8B,CAAC,OAAO,CAAC,CAAC;YACjD,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,UAAU,CACN,4GAA4G,eAAe,IAAI,IAAI,CAAC,QAAQ,gBAAgB,YAAY,CAAC,IAAI,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACxO,CAAC;QACN,CAAC;IACL,CAAC;IAED;;;;OAIG;IACI,8BAA8B,CAAC,iBAAyB;QAC3D,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,iBAAiB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxD,OAAO;QACX,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC,aAAyD,CAAC;QAC1E,MAAM,YAAY,GAAG,CAAC,EAAE,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrF,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,kBAAkB,IAAI,YAAY,CAAC,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpG,OAAO;QACX,CAAC;QAED,MAAM,UAAU,GAAI,IAAI,CAAC,aAA0C,EAAE,WAAW,KAAK,YAAY,CAAC;QAClG,IAAI,CAAC;YACD,MAAM,SAAS,GAAG,SAAS,CAAC,gBAAgB,CACxC,iBAAiB,EACjB,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CACtD,CAAC;YACF,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvC,OAAO;YACX,CAAC;YACD,MAAM,eAAe,GAAG,IAAI,CAAC,+BAA+B,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;YACtF,IAAI,eAAe,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC1D,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,CAAC;oBACrE,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC;gBACjC,CAAC;YACL,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,UAAU,CACN,8GAA8G,IAAI,CAAC,QAAQ,gBAAgB,YAAY,CAAC,IAAI,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACvN,CAAC;QACN,CAAC;IACL,CAAC;IAEO,+BAA+B,CACnC,YAAwB,EACxB,SAAkE;QAElE,MAAM,eAAe,GAAG,IAAI,GAAG,EAAuC,CAAC;QACvE,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,aAAa,GAAG,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YAClD,MAAM,cAAc,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC5E,KAAK,MAAM,OAAO,IAAI,YAAY,CAAC,kBAAkB,IAAI,EAAE,EAAE,CAAC;gBAC1D,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;oBAC5C,CAAC,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;oBAC3F,SAAS;gBACb,CAAC;gBACD,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,aAAa,EAAE,CAAC;oBACnD,SAAS;gBACb,CAAC;gBACD,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,mBAAmB,IAAI,YAAY,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;gBACnG,IAAI,cAAc,IAAI,cAAc,KAAK,aAAa,EAAE,CAAC;oBACrD,SAAS;gBACb,CAAC;gBACD,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC;YAC3D,CAAC;QACL,CAAC;QACD,OAAO,eAAe,CAAC;IAC3B,CAAC;IAEO,gBAAgB,CACpB,eAA4C,EAC5C,QAAuC;QAEvC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,MAAM,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACzD,IAAI,MAAM,GAA4C,eAAe,CAAC;QACtE,OAAO,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;YACpC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACnB,OAAO,IAAI,CAAC;YAChB,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACjB,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7E,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ,CAAA;AApkBY,qCAAqC;IADjD,aAAa,CAAC,UAAU,EAAE,iCAAiC,CAAC;GAChD,qCAAqC,CAokBjD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityDeleteOptions, EntityFieldInfo, EntitySaveOptions, EntityInfo, FieldPermissionRuleVerbs, ValidationResult } from '@memberjunction/core';
|
|
1
|
+
import { EntityDeleteOptions, EntityFieldInfo, EntitySaveOptions, EntityInfo, FieldPermissionRuleVerbs, UserInfo, ValidationResult } from '@memberjunction/core';
|
|
2
2
|
import { MJEntityFieldPermissionEntity } from '@memberjunction/core-entities';
|
|
3
3
|
/**
|
|
4
4
|
* Server-side `MJ: Entity Field Permissions` entity — the save-time half of the
|
|
@@ -31,14 +31,22 @@ import { MJEntityFieldPermissionEntity } from '@memberjunction/core-entities';
|
|
|
31
31
|
* confidentiality cannot ship with a role that quietly reads everything.
|
|
32
32
|
*/
|
|
33
33
|
export declare class MJEntityFieldPermissionEntityServer extends MJEntityFieldPermissionEntity {
|
|
34
|
+
/** This write's place in its transaction group, held from the group check until the write enrols. */
|
|
35
|
+
private pendingGroupWrite;
|
|
34
36
|
Validate(): ValidationResult;
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
38
|
+
* Normalizes Access spellings, then refuses an EDIT that would strip the MJ system user's last
|
|
39
|
+
* `Allow` on a field.
|
|
37
40
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
41
|
+
* **Normalization.** The Access columns' CHECK constraint is case-insensitive, so `'deny'` or
|
|
42
|
+
* `'ALLOW'` would otherwise persist and read one way while enforcing another. Rewriting them to
|
|
43
|
+
* the canonical value-list spelling keeps the stored data honest; the guards compare
|
|
44
|
+
* case-insensitively regardless, so normalization is not the only line of defence.
|
|
45
|
+
*
|
|
46
|
+
* **The access-loss guard** is not in `Validate()` for two reasons. It needs the field's rules
|
|
47
|
+
* as they stand in the DATABASE — loaded metadata lags recent writes, and reading a stale
|
|
48
|
+
* sibling row as `Allow` is exactly how three individually-innocent `No Access` edits get
|
|
49
|
+
* through one at a time. And loading them is asynchronous, which `Validate()` is not.
|
|
42
50
|
*
|
|
43
51
|
* Inserts are deliberately not checked: adding a rule can only add access, never remove an
|
|
44
52
|
* existing `Allow`, so the `Deny` check in `Validate()` covers them completely. It has to —
|
|
@@ -46,26 +54,88 @@ export declare class MJEntityFieldPermissionEntityServer extends MJEntityFieldPe
|
|
|
46
54
|
* the half-built state.
|
|
47
55
|
*/
|
|
48
56
|
Save(options?: EntitySaveOptions): Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Ends this write's hold on its reserved group place: kept when the write enrolled, given back
|
|
59
|
+
* when it was refused, returned false or threw, so a retry is not counted as a second write.
|
|
60
|
+
*/
|
|
61
|
+
private settleGroupWrite;
|
|
62
|
+
/**
|
|
63
|
+
* The transaction-group rule for this write. When it passes, the write's place in the group is
|
|
64
|
+
* reserved in the same step, so a second guarded write in flight at once sees it.
|
|
65
|
+
*/
|
|
66
|
+
private groupRefusal;
|
|
67
|
+
/**
|
|
68
|
+
* The system-user checks for a save, on the role set as stored in the database (the cached one
|
|
69
|
+
* lags a role assigned a moment ago): a `Deny` on a role the system user holds; an edit that
|
|
70
|
+
* strips its last `Allow`; and a transaction group holding this and another guarded write that
|
|
71
|
+
* concerns it. An insert that restricts nothing needs none of these and reads nothing, which
|
|
72
|
+
* keeps reconciliation's bulk inserts free. A read that fails refuses the save.
|
|
73
|
+
*/
|
|
74
|
+
private systemUserSaveRejection;
|
|
75
|
+
/** The refusal when the system user's field access cannot be checked because a read failed. */
|
|
76
|
+
private static unverifiableMessage;
|
|
49
77
|
/**
|
|
50
78
|
* Refuses a DELETE that would strip the MJ system user's last `Allow` on a field — the other
|
|
51
79
|
* way, besides editing one to `No Access`, to remove access without writing a `Deny`.
|
|
52
80
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
81
|
+
* Judged against the row's STORED binding: a delete removes the row by ID, so an unsaved
|
|
82
|
+
* re-point on this object does not change which target loses it.
|
|
83
|
+
*
|
|
84
|
+
* Reconciliation's orphan deletes usually clear this guard — a row whose field became
|
|
85
|
+
* unrestrictable is forced open regardless — but not always: an orphaned row can be the system
|
|
86
|
+
* user's last `Allow` on a field while another of its roles still reads the entity. The
|
|
87
|
+
* reconciler logs and skips such a refusal rather than aborting.
|
|
56
88
|
*/
|
|
57
89
|
Delete(options?: EntityDeleteOptions): Promise<boolean>;
|
|
90
|
+
/** The system-user checks for a delete, on the role set as stored in the database. */
|
|
91
|
+
private systemUserDeleteRejection;
|
|
92
|
+
/** Rewrites each Access value to its canonical spelling; unrecognized values are left for validation to report. */
|
|
93
|
+
private normalizeAccessSpellings;
|
|
58
94
|
/**
|
|
59
95
|
* Records a refusal on the result history so `LatestResult.CompleteMessage` explains it, and
|
|
60
96
|
* returns false — the contract `Save()` and `Delete()` already have for a logical failure.
|
|
61
97
|
*/
|
|
62
98
|
private refuse;
|
|
63
99
|
/**
|
|
64
|
-
* Why the pending
|
|
100
|
+
* Why the pending EDIT would leave the system user short of its entity-level access, or null.
|
|
101
|
+
*
|
|
102
|
+
* An edit that changes `RoleID` or `EntityFieldID` MOVES the rule: it is a delete from the
|
|
103
|
+
* target it leaves as well as an insert into the one it joins. Judging only the destination
|
|
104
|
+
* let a sequence of saves strip the system user's last `Allow` without writing a `Deny` —
|
|
105
|
+
* re-point that row at a role the system user does not hold, and the cheap exit on the NEW
|
|
106
|
+
* role waved it through. So both targets are evaluated:
|
|
107
|
+
*
|
|
108
|
+
* - **the destination field**, with this row's new values projected in, whenever the system
|
|
109
|
+
* user holds the new role — or, when the field is unchanged, the old one (the row's old
|
|
110
|
+
* binding drops out of that same field's aggregate);
|
|
111
|
+
* - **the vacated field**, with this row removed, whenever the field itself changed and the
|
|
112
|
+
* system user holds the old role.
|
|
113
|
+
*
|
|
114
|
+
* The cheap exits are what keep the queries off ordinary permission administration: the
|
|
115
|
+
* aggregation only consults rules bound to roles the user holds, so a change touching no such
|
|
116
|
+
* role cannot move the system user's access at all.
|
|
117
|
+
*/
|
|
118
|
+
private systemUserEditLossReason;
|
|
119
|
+
/** Why deleting this row would leave the system user short of its entity-level access, or null. */
|
|
120
|
+
private systemUserDeleteLossReason;
|
|
121
|
+
/**
|
|
122
|
+
* Projects one field's stored rules with this row's change applied and asks whether the
|
|
123
|
+
* system user keeps its access there.
|
|
65
124
|
*
|
|
66
|
-
* @param
|
|
125
|
+
* @param entityFieldID the field whose aggregate to evaluate
|
|
126
|
+
* @param includeThisRow true to project this row in with its CURRENT values (it is on this
|
|
127
|
+
* field after the change); false to project it out (it is leaving)
|
|
128
|
+
* @param systemUser the system user with its role set as stored
|
|
129
|
+
*/
|
|
130
|
+
private projectedLossReason;
|
|
131
|
+
/**
|
|
132
|
+
* The (role, field) pair this row occupies in the database — the dirty fields' old values,
|
|
133
|
+
* otherwise their current ones. Null when a re-pointed field's prior value cannot be
|
|
134
|
+
* established, which callers treat as a refusal: an unknown vacated target cannot be shown safe.
|
|
67
135
|
*/
|
|
68
|
-
private
|
|
136
|
+
private priorBinding;
|
|
137
|
+
/** A column's stored value; undefined when it is dirty and its old value is not a usable ID. */
|
|
138
|
+
private priorValue;
|
|
69
139
|
/**
|
|
70
140
|
* Every rule currently stored against this field, straight from the database.
|
|
71
141
|
*
|
|
@@ -106,8 +176,10 @@ export declare class MJEntityFieldPermissionEntityServer extends MJEntityFieldPe
|
|
|
106
176
|
* @param roleID the role the rule targets
|
|
107
177
|
* @param rule the rule's three verbs; omitted only by callers that are pre-checking a role
|
|
108
178
|
* rather than a specific rule, which are answered as though the rule denied
|
|
179
|
+
* @param storedSystemUser the system user with its role set as stored in the database;
|
|
180
|
+
* defaults to the user cache's system user
|
|
109
181
|
*/
|
|
110
|
-
static SystemUserRoleRejectionReason(roleID: string | null, rule?: FieldPermissionRuleVerbs): string | null;
|
|
182
|
+
static SystemUserRoleRejectionReason(roleID: string | null, rule?: FieldPermissionRuleVerbs, storedSystemUser?: UserInfo | null): string | null;
|
|
111
183
|
/**
|
|
112
184
|
* Why this rule's Update/Create verbs may not target this field, or null when they may.
|
|
113
185
|
*
|
|
@@ -132,15 +204,6 @@ export declare class MJEntityFieldPermissionEntityServer extends MJEntityFieldPe
|
|
|
132
204
|
* Static + field-driven so the same wording can back a future admin-UI pre-check.
|
|
133
205
|
*/
|
|
134
206
|
static RejectionReason(field: EntityFieldInfo): string | null;
|
|
135
|
-
/**
|
|
136
|
-
* Resolves `EntityFieldID` to its {@link EntityFieldInfo} from loaded metadata.
|
|
137
|
-
*
|
|
138
|
-
* Returns null when metadata cannot resolve it — for instance immediately after a schema
|
|
139
|
-
* change, before the metadata cache has refreshed. That case passes validation rather than
|
|
140
|
-
* blocking the administrator: the foreign key already guarantees the row points at a real
|
|
141
|
-
* field, and the runtime aggregation guards hold regardless of what is stored here. Failing
|
|
142
|
-
* closed would trade a real, recurring workflow block for no additional protection.
|
|
143
|
-
*/
|
|
144
207
|
/**
|
|
145
208
|
* Why this role may not carry a field rule on this entity, or null when it may.
|
|
146
209
|
*
|
|
@@ -162,6 +225,17 @@ export declare class MJEntityFieldPermissionEntityServer extends MJEntityFieldPe
|
|
|
162
225
|
* a role that has entity access, or grant this one an entity permission first.
|
|
163
226
|
*/
|
|
164
227
|
static EntityPermissionRequiredRejectionReason(entity: EntityInfo, roleID: string | null): string | null;
|
|
228
|
+
/**
|
|
229
|
+
* Resolves `EntityFieldID` to its {@link EntityFieldInfo} from loaded metadata.
|
|
230
|
+
*
|
|
231
|
+
* Returns null when metadata cannot resolve it — for instance immediately after a schema
|
|
232
|
+
* change, before the metadata cache has refreshed. That case passes validation rather than
|
|
233
|
+
* blocking the administrator: the foreign key already guarantees the row points at a real
|
|
234
|
+
* field, and the runtime aggregation guards hold regardless of what is stored here. Failing
|
|
235
|
+
* closed would trade a real, recurring workflow block for no additional protection.
|
|
236
|
+
*/
|
|
165
237
|
private resolveTarget;
|
|
238
|
+
/** The field a given `EntityFieldID` names, so the re-point guard can resolve a row's OLD field too. */
|
|
239
|
+
private resolveTargetByFieldID;
|
|
166
240
|
}
|
|
167
241
|
//# sourceMappingURL=MJEntityFieldPermissionEntityServer.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MJEntityFieldPermissionEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJEntityFieldPermissionEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,UAAU,EAGV,wBAAwB,
|
|
1
|
+
{"version":3,"file":"MJEntityFieldPermissionEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJEntityFieldPermissionEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,UAAU,EAGV,wBAAwB,EAIxB,QAAQ,EAGR,gBAAgB,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AA4B9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBACa,mCAAoC,SAAQ,6BAA6B;IAClF,qGAAqG;IACrG,OAAO,CAAC,iBAAiB,CAAyC;IAElD,QAAQ,IAAI,gBAAgB;IAyC5C;;;;;;;;;;;;;;;;;;OAkBG;IACmB,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAezE;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAQpB;;;;;;OAMG;YACW,uBAAuB;IAuBrC,+FAA+F;IAC/F,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAKlC;;;;;;;;;;;OAWG;IACmB,MAAM,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IAc7E,sFAAsF;YACxE,yBAAyB;IAavC,mHAAmH;IACnH,OAAO,CAAC,wBAAwB;IAShC;;;OAGG;IACH,OAAO,CAAC,MAAM;IAWd;;;;;;;;;;;;;;;;;;OAkBG;YACW,wBAAwB;IAoBtC,mGAAmG;YACrF,0BAA0B;IAWxC;;;;;;;;OAQG;YACW,mBAAmB;IAgBjC;;;;OAIG;IACH,OAAO,CAAC,YAAY;IASpB,gGAAgG;IAChG,OAAO,CAAC,UAAU;IASlB;;;;;;;OAOG;YACW,gBAAgB;IAyB9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;WACW,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,wBAAwB,EAAE,gBAAgB,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI;IA2BtJ;;;;;;;;;;;;;;;;;OAiBG;WACW,wBAAwB,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,wBAAwB,GAAG,MAAM,GAAG,IAAI;IAmB7G;;;OAGG;WACW,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI;IAqBpE;;;;;;;;;;;;;;;;;;;OAmBG;WACW,uCAAuC,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI;IAe/G;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;IAIrB,wGAAwG;IACxG,OAAO,CAAC,sBAAsB;CAajC"}
|