@memberjunction/integration-engine 5.49.0 → 5.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/IntegrationEngine.d.ts +77 -0
- package/dist/IntegrationEngine.d.ts.map +1 -1
- package/dist/IntegrationEngine.js +247 -51
- package/dist/IntegrationEngine.js.map +1 -1
- package/dist/MatchEngine.d.ts +156 -14
- package/dist/MatchEngine.d.ts.map +1 -1
- package/dist/MatchEngine.js +438 -68
- package/dist/MatchEngine.js.map +1 -1
- package/dist/RecordMapBatch.d.ts +162 -0
- package/dist/RecordMapBatch.d.ts.map +1 -0
- package/dist/RecordMapBatch.js +283 -0
- package/dist/RecordMapBatch.js.map +1 -0
- package/dist/prefetchFilter.d.ts +18 -0
- package/dist/prefetchFilter.d.ts.map +1 -1
- package/dist/prefetchFilter.js +15 -10
- package/dist/prefetchFilter.js.map +1 -1
- package/package.json +7 -7
package/dist/MatchEngine.js
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
|
-
import { Metadata, RunView } from '@memberjunction/core';
|
|
1
|
+
import { DatabaseProviderBase, Metadata, RunView } from '@memberjunction/core';
|
|
2
2
|
import { serializeKeyValue } from './KeySerialization.js';
|
|
3
|
+
import { quoteTextLiteral } from './prefetchFilter.js';
|
|
4
|
+
/**
|
|
5
|
+
* Folds an external ID the way a default SQL Server collation folds it for `=`
|
|
6
|
+
* (`SQL_Latin1_General_CP1_CI_AS`: case-insensitive, trailing blanks ignored).
|
|
7
|
+
*
|
|
8
|
+
* This exists because the batched prefetch and the per-record query compare differently. The
|
|
9
|
+
* per-record query sends `ExternalSystemRecordID = '<id>'` and lets the DATABASE decide equality;
|
|
10
|
+
* the batch reads a set of rows and then pairs them up in JavaScript with `===`, which is
|
|
11
|
+
* case- and whitespace-exact. So a row the database happily matched can miss in the JS map — and
|
|
12
|
+
* a miss on the record map means "no mapping", which turns an UPDATE into a duplicate CREATE.
|
|
13
|
+
* Normalizing gives the batch path a second, laxer attempt that mirrors the database's own rule.
|
|
14
|
+
*
|
|
15
|
+
* **Only applied on a platform that actually folds** — see {@link MatchEngine.platformFoldsStringEquality}.
|
|
16
|
+
* PostgreSQL's `=` on `text`/`varchar` is case-SENSITIVE and NOT blank-insensitive, so applying
|
|
17
|
+
* this fold there would make the index answer a match the database would never have returned:
|
|
18
|
+
* the record would be written onto a different row instead of created. On PG the exact map is the
|
|
19
|
+
* whole answer, and a miss is a real miss.
|
|
20
|
+
*/
|
|
21
|
+
function normalizeExternalID(id) {
|
|
22
|
+
// Spaces only, NOT `\s`. SQL Server's trailing-blank insensitivity is ANSI padding on
|
|
23
|
+
// ASCII 0x20; a trailing tab or newline is significant to it. Folding those too would make
|
|
24
|
+
// the batch index equate `abc\t` with `abc`, which the database does not — a fold the
|
|
25
|
+
// per-record query would never confirm, and a wrong mapping is worse than a missed one.
|
|
26
|
+
return id.replace(/ +$/, '').toLowerCase();
|
|
27
|
+
}
|
|
3
28
|
/**
|
|
4
29
|
* Resolves mapped records against existing MJ data to determine
|
|
5
30
|
* whether each record should be Created, Updated, Skipped, or Deleted.
|
|
@@ -24,21 +49,151 @@ export class MatchEngine {
|
|
|
24
49
|
this._provider = provider;
|
|
25
50
|
const keyFields = fieldMaps.filter(fm => fm.IsKeyField && fm.Status === 'Active');
|
|
26
51
|
const conflictResolution = entityMap.ConflictResolution;
|
|
52
|
+
// Resolve the batch's lookups up front instead of one query per record (tasks.md PR 2
|
|
53
|
+
// item 5). Every record consults the record map — on the deleted path directly, on the
|
|
54
|
+
// live path as the fallback when identity/key matching finds nothing — so on a
|
|
55
|
+
// 500-record batch this replaces up to 500 single-row reads with a single `IN` read.
|
|
56
|
+
//
|
|
57
|
+
// All of it goes out as ONE batched RunViews: the record-map read plus one read per
|
|
58
|
+
// criteria-shape group. These are independent queries with no data dependency between
|
|
59
|
+
// them, which is exactly what RunViews is for — issuing them separately (or as a
|
|
60
|
+
// Promise.all of RunView calls) pays a round trip per leg for no reason.
|
|
61
|
+
const { MapIndex: mapIndex, KeyIndex: keyIndex } = await this.PrefetchBatchLookups(records, entityMap, keyFields, contextUser);
|
|
27
62
|
const results = [];
|
|
28
63
|
for (const record of records) {
|
|
29
|
-
const resolved = await this.ResolveSingleRecord(record, entityMap, keyFields, conflictResolution, contextUser);
|
|
64
|
+
const resolved = await this.ResolveSingleRecord(record, entityMap, keyFields, conflictResolution, contextUser, mapIndex, keyIndex);
|
|
30
65
|
results.push(resolved);
|
|
31
66
|
}
|
|
32
67
|
return results;
|
|
33
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Issues every lookup this batch needs as ONE batched read, and builds both indexes from it.
|
|
71
|
+
*
|
|
72
|
+
* The legs are independent — the record-map read and each criteria-shape group's read share
|
|
73
|
+
* no data dependency — so they go out together via {@link RunView.RunViews} rather than as
|
|
74
|
+
* separate awaits. Result order matches param order, which is how each leg is attributed back.
|
|
75
|
+
*
|
|
76
|
+
* A `null` index means "the read failed, fall back to the per-record query" and is NOT the
|
|
77
|
+
* same as an empty index, which means "asked, and there is nothing". Conflating them would
|
|
78
|
+
* treat every record as unmapped and turn an incremental sync into a batch of duplicate
|
|
79
|
+
* creates, so the two are kept distinct all the way down.
|
|
80
|
+
*/
|
|
81
|
+
async PrefetchBatchLookups(records, entityMap, keyFields, contextUser) {
|
|
82
|
+
const mapParams = this.buildRecordMapViewParams(records, entityMap);
|
|
83
|
+
const keyGroups = this.buildKeyMatchGroups(records, keyFields);
|
|
84
|
+
const emptyMapIndex = { Exact: new Map(), Normalized: new Map(), Ambiguous: new Set() };
|
|
85
|
+
const emptyKeyIndex = { Matched: new Map(), Unmatched: new Set() };
|
|
86
|
+
if (!mapParams && keyGroups.length === 0)
|
|
87
|
+
return { MapIndex: emptyMapIndex, KeyIndex: emptyKeyIndex };
|
|
88
|
+
const params = [];
|
|
89
|
+
if (mapParams)
|
|
90
|
+
params.push(mapParams);
|
|
91
|
+
for (const group of keyGroups)
|
|
92
|
+
params.push(group.Params);
|
|
93
|
+
const rv = new RunView();
|
|
94
|
+
const results = await rv.RunViews(params, contextUser);
|
|
95
|
+
let next = 0;
|
|
96
|
+
const mapIndex = mapParams
|
|
97
|
+
? this.buildRecordMapIndexFromResult(results[next++])
|
|
98
|
+
: emptyMapIndex;
|
|
99
|
+
return {
|
|
100
|
+
MapIndex: mapIndex,
|
|
101
|
+
KeyIndex: this.buildKeyMatchIndex(keyGroups, results.slice(next)),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The read that resolves an entire batch of external IDs against the record map, or null when
|
|
106
|
+
* the batch carries no usable external ID (nothing to ask, so no query is issued).
|
|
107
|
+
*/
|
|
108
|
+
buildRecordMapViewParams(records, entityMap) {
|
|
109
|
+
const externalIDs = Array.from(new Set(records.map(r => r.ExternalRecord.ExternalID).filter(id => id != null && id !== '')));
|
|
110
|
+
if (externalIDs.length === 0)
|
|
111
|
+
return null;
|
|
112
|
+
const inList = externalIDs.map(id => this.quoteLiteral(id)).join(',');
|
|
113
|
+
return {
|
|
114
|
+
EntityName: 'MJ: Company Integration Record Maps',
|
|
115
|
+
ExtraFilter: `CompanyIntegrationID='${entityMap.CompanyIntegrationID}' ` +
|
|
116
|
+
`AND EntityID='${entityMap.EntityID}' ` +
|
|
117
|
+
`AND ExternalSystemRecordID IN (${inList})`,
|
|
118
|
+
Fields: ['ExternalSystemRecordID', 'EntityRecordID'],
|
|
119
|
+
IgnoreMaxRows: true, // a batch can exceed the entity's default row cap
|
|
120
|
+
ResultType: 'simple',
|
|
121
|
+
// Same reason the per-record lookup bypasses the cache: this decides CREATE vs UPDATE,
|
|
122
|
+
// and a stale miss re-creates a record that already exists.
|
|
123
|
+
BypassCache: true,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Builds the record-map index from its leg of the batched read.
|
|
128
|
+
*
|
|
129
|
+
* Returns an index containing only the IDs that HAVE a mapping, so a miss is represented by
|
|
130
|
+
* absence — exactly what the per-record lookup needed to distinguish. Returns null if the read
|
|
131
|
+
* failed, which makes callers fall back to their original per-record query rather than
|
|
132
|
+
* silently treating every record as unmapped.
|
|
133
|
+
*
|
|
134
|
+
* The index carries a second, normalized view of the same rows. The `IN (…)` read is evaluated
|
|
135
|
+
* by the database under its own collation, so a row stored as `abc` comes back for a requested
|
|
136
|
+
* `ABC`; pairing the result up in JavaScript with `===` would then miss it. See
|
|
137
|
+
* {@link normalizeExternalID}.
|
|
138
|
+
*/
|
|
139
|
+
buildRecordMapIndexFromResult(result) {
|
|
140
|
+
if (!result?.Success)
|
|
141
|
+
return null;
|
|
142
|
+
return this.buildRecordMapIndex(result.Results);
|
|
143
|
+
}
|
|
144
|
+
/** Builds the exact + normalized views of a record-map read, separating out ambiguous folds. */
|
|
145
|
+
buildRecordMapIndex(rows) {
|
|
146
|
+
const index = { Exact: new Map(), Normalized: new Map(), Ambiguous: new Set() };
|
|
147
|
+
const ambiguous = index.Ambiguous;
|
|
148
|
+
const folds = this.platformFoldsStringEquality;
|
|
149
|
+
for (const row of rows) {
|
|
150
|
+
// A mapping with no external ID cannot be looked up by one. Skipping it keeps a bad
|
|
151
|
+
// row out of the index instead of taking the whole batch's matching down with it.
|
|
152
|
+
const externalID = row.ExternalSystemRecordID;
|
|
153
|
+
if (typeof externalID !== 'string' || externalID === '')
|
|
154
|
+
continue;
|
|
155
|
+
index.Exact.set(externalID, row.EntityRecordID);
|
|
156
|
+
// Left empty on an exact-comparison platform, so the folded lookup can never answer
|
|
157
|
+
// there — the exact map alone is what the database's own `IN (…)` decided.
|
|
158
|
+
if (!folds)
|
|
159
|
+
continue;
|
|
160
|
+
const norm = normalizeExternalID(externalID);
|
|
161
|
+
const seen = index.Normalized.get(norm);
|
|
162
|
+
if (seen === undefined)
|
|
163
|
+
index.Normalized.set(norm, row.EntityRecordID);
|
|
164
|
+
else if (seen !== row.EntityRecordID)
|
|
165
|
+
ambiguous.add(norm);
|
|
166
|
+
}
|
|
167
|
+
for (const key of ambiguous)
|
|
168
|
+
index.Normalized.delete(key);
|
|
169
|
+
return index;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* True when the platform's `=` on string columns is case- and trailing-blank-insensitive, so
|
|
173
|
+
* the JS-side fold in {@link normalizeExternalID} reproduces what the database already did.
|
|
174
|
+
*
|
|
175
|
+
* SQL Server, under the default `SQL_Latin1_General_CP1_CI_AS` collation, is. PostgreSQL is
|
|
176
|
+
* not: `text`/`varchar` equality there is exact on both counts. Folding on PG would make the
|
|
177
|
+
* batch index return a mapping the per-record query would never return — the record would be
|
|
178
|
+
* UPDATED onto someone else's row instead of created, which is the one error direction this
|
|
179
|
+
* whole index is built to avoid. A provider with no dialect (client-side) gets `false`, the
|
|
180
|
+
* conservative answer.
|
|
181
|
+
*/
|
|
182
|
+
get platformFoldsStringEquality() {
|
|
183
|
+
const provider = this.ProviderToUse;
|
|
184
|
+
// `PlatformKey` rather than `Dialect.PlatformKey`: the provider's `Dialect` getter
|
|
185
|
+
// memoizes the dialect object, so reading the platform through it answers from whatever
|
|
186
|
+
// was cached first. The platform is the property being asked about anyway.
|
|
187
|
+
return provider instanceof DatabaseProviderBase && provider.PlatformKey === 'sqlserver';
|
|
188
|
+
}
|
|
34
189
|
/**
|
|
35
190
|
* Resolves a single record by checking for an existing MJ match.
|
|
36
191
|
*/
|
|
37
|
-
async ResolveSingleRecord(record, entityMap, keyFields, conflictResolution, contextUser) {
|
|
192
|
+
async ResolveSingleRecord(record, entityMap, keyFields, conflictResolution, contextUser, mapIndex, keyIndex) {
|
|
38
193
|
if (record.ExternalRecord.IsDeleted) {
|
|
39
|
-
return this.ResolveDeletedRecord(record, entityMap, contextUser);
|
|
194
|
+
return this.ResolveDeletedRecord(record, entityMap, contextUser, mapIndex);
|
|
40
195
|
}
|
|
41
|
-
const existingID = await this.FindExistingRecord(record, entityMap, keyFields, contextUser);
|
|
196
|
+
const existingID = await this.FindExistingRecord(record, entityMap, keyFields, contextUser, mapIndex, keyIndex);
|
|
42
197
|
if (existingID) {
|
|
43
198
|
return this.ResolveExistingRecord(record, existingID, conflictResolution);
|
|
44
199
|
}
|
|
@@ -47,8 +202,8 @@ export class MatchEngine {
|
|
|
47
202
|
/**
|
|
48
203
|
* Handles records marked as deleted in the external system.
|
|
49
204
|
*/
|
|
50
|
-
async ResolveDeletedRecord(record, entityMap, contextUser) {
|
|
51
|
-
const existingID = await this.FindRecordMapEntry(entityMap.CompanyIntegrationID, record.ExternalRecord.ExternalID, entityMap.EntityID, contextUser);
|
|
205
|
+
async ResolveDeletedRecord(record, entityMap, contextUser, mapIndex) {
|
|
206
|
+
const existingID = await this.FindRecordMapEntry(entityMap.CompanyIntegrationID, record.ExternalRecord.ExternalID, entityMap.EntityID, contextUser, mapIndex);
|
|
52
207
|
if (existingID) {
|
|
53
208
|
return { ...record, ChangeType: 'Delete', MatchedMJRecordID: existingID };
|
|
54
209
|
}
|
|
@@ -64,70 +219,91 @@ export class MatchEngine {
|
|
|
64
219
|
return { ...record, ChangeType: 'Update', MatchedMJRecordID: existingID };
|
|
65
220
|
}
|
|
66
221
|
/**
|
|
67
|
-
* Attempts to find an existing MJ record by
|
|
68
|
-
* to the CompanyIntegrationRecordMap.
|
|
222
|
+
* Attempts to find an existing MJ record by identity (PK) or, failing that, by the
|
|
223
|
+
* configured key fields — then falls back to the CompanyIntegrationRecordMap.
|
|
224
|
+
*
|
|
225
|
+
* The direct lookup is attempted whenever it CAN resolve something:
|
|
226
|
+
* - the mapped record carries a complete PK (single OR composite) — the identity case,
|
|
227
|
+
* and the same key the apply path will address the row by; or
|
|
228
|
+
* - key fields are configured — the fallback case, for mapping external records onto
|
|
229
|
+
* pre-existing MJ rows whose PK the external system does not know.
|
|
69
230
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
231
|
+
* Previously a single-PK entity with no configured key fields skipped this entirely and
|
|
232
|
+
* relied on the record map alone — so a record whose map row was missing (or truncated;
|
|
233
|
+
* see `LoadAllRecordMaps`) re-CREATED a row whose PK already existed. Integration shadow
|
|
234
|
+
* tables are exactly this shape: a single soft PK holding the external ID.
|
|
74
235
|
*/
|
|
75
|
-
async FindExistingRecord(record, entityMap, keyFields, contextUser) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const isCompositePK = (entityInfo?.PrimaryKeys?.length ?? 0) > 1;
|
|
79
|
-
if (keyFields.length > 0 || isCompositePK) {
|
|
80
|
-
const idByKeys = await this.FindByKeyFields(record, keyFields, contextUser);
|
|
236
|
+
async FindExistingRecord(record, entityMap, keyFields, contextUser, mapIndex, keyIndex) {
|
|
237
|
+
if (keyFields.length > 0 || this.hasCompleteMappedPrimaryKey(record)) {
|
|
238
|
+
const idByKeys = await this.LookupByKeyFields(record, keyFields, contextUser, keyIndex);
|
|
81
239
|
if (idByKeys)
|
|
82
240
|
return idByKeys;
|
|
83
241
|
}
|
|
84
|
-
return this.FindRecordMapEntry(entityMap.CompanyIntegrationID, record.ExternalRecord.ExternalID, entityMap.EntityID, contextUser);
|
|
242
|
+
return this.FindRecordMapEntry(entityMap.CompanyIntegrationID, record.ExternalRecord.ExternalID, entityMap.EntityID, contextUser, mapIndex);
|
|
85
243
|
}
|
|
86
244
|
/**
|
|
87
|
-
*
|
|
245
|
+
* Answers the identity/key lookup from the batch prefetch when it can, and only issues the
|
|
246
|
+
* per-record query when the prefetch neither found the record nor proved it absent.
|
|
247
|
+
*/
|
|
248
|
+
async LookupByKeyFields(record, keyFields, contextUser, keyIndex) {
|
|
249
|
+
if (keyIndex) {
|
|
250
|
+
const criteria = this.BuildMatchCriteria(record, keyFields);
|
|
251
|
+
if (criteria) {
|
|
252
|
+
const key = this.CriteriaKey(criteria.Fields, criteria.Values);
|
|
253
|
+
const matched = keyIndex.Matched.get(key);
|
|
254
|
+
if (matched)
|
|
255
|
+
return matched;
|
|
256
|
+
if (keyIndex.Unmatched.has(key))
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return this.FindByKeyFields(record, keyFields, contextUser);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Returns the entity's primary-key fields (soft PKs included — integration shadow tables
|
|
264
|
+
* carry no physical key, so `PrimaryKeys` is populated from `additionalSchemaInfo` and is
|
|
265
|
+
* still the natural identity).
|
|
266
|
+
*/
|
|
267
|
+
primaryKeyFieldsFor(mjEntityName) {
|
|
268
|
+
const entityInfo = this.ProviderToUse.EntityByName(mjEntityName);
|
|
269
|
+
return entityInfo?.PrimaryKeys ?? (entityInfo?.FirstPrimaryKey ? [entityInfo.FirstPrimaryKey] : []);
|
|
270
|
+
}
|
|
271
|
+
/** True when the mapped data carries a value for EVERY PK field — i.e. it asserts an identity. */
|
|
272
|
+
hasCompleteMappedPrimaryKey(record) {
|
|
273
|
+
const pkFields = this.primaryKeyFieldsFor(record.MJEntityName);
|
|
274
|
+
if (pkFields.length === 0)
|
|
275
|
+
return false;
|
|
276
|
+
return pkFields.every(f => record.MappedFields[f.Name] != null);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Searches for an existing MJ record, using ONE definition of identity.
|
|
88
280
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
281
|
+
* **The unified rule (tasks.md PR 2 item 3).** The entity's primary key — including a
|
|
282
|
+
* soft PK, which is what integration shadow tables carry — is the single definition of
|
|
283
|
+
* record identity, for matching AND for saving. Configured key fields (`IsKeyField`) are
|
|
284
|
+
* a *fallback lookup* for records whose PK the mapped data does not carry, never a
|
|
285
|
+
* competing identity.
|
|
92
286
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
287
|
+
* Concretely: when the mapped record carries a COMPLETE PK, match on the PK alone. The
|
|
288
|
+
* apply path already addresses the record by its PK (`CreateRecord.extractMappedPrimaryKey`
|
|
289
|
+
* → `InnerLoad`), so matching on anything else is exactly how a record could match one row
|
|
290
|
+
* and then be written to another — or match a row whose PK then collides on save. When the
|
|
291
|
+
* PK is absent or partial (e.g. mapping external records onto pre-existing MJ rows keyed by
|
|
292
|
+
* a server-assigned UUID), fall back to the configured key fields, plus whatever PK parts
|
|
293
|
+
* ARE present to narrow the result.
|
|
294
|
+
*
|
|
295
|
+
* The returned ID is always the PK values in `PrimaryKeys` order, '|'-joined — the same
|
|
296
|
+
* format `ExternalID`/`EntityRecordID` use — so the caller can load the row directly.
|
|
95
297
|
*/
|
|
96
298
|
async FindByKeyFields(record, keyFields, contextUser) {
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
if (pkFields.length === 0)
|
|
101
|
-
return null;
|
|
102
|
-
// Start with the configured key-field filter clauses
|
|
103
|
-
const filterClauses = this.BuildKeyFieldFilter(record, keyFields);
|
|
104
|
-
// For composite-PK entities, augment the filter with all PK field values
|
|
105
|
-
// taken directly from the mapped record. This ensures uniqueness even when
|
|
106
|
-
// no key fields are configured and prevents matching the wrong row when
|
|
107
|
-
// the configured key fields alone are not unique (e.g. person_id matches
|
|
108
|
-
// all topics for a given person).
|
|
109
|
-
if (pkFields.length > 1) {
|
|
110
|
-
for (const pkField of pkFields) {
|
|
111
|
-
const value = record.MappedFields[pkField.Name];
|
|
112
|
-
if (value == null)
|
|
113
|
-
continue;
|
|
114
|
-
const escaped = serializeKeyValue(value).replace(/'/g, "''");
|
|
115
|
-
// ANSI double-quoted identifier — portable across SQL Server (QUOTED_IDENTIFIER ON,
|
|
116
|
-
// the driver default) and Postgres (exact-case; integration columns are lowercase).
|
|
117
|
-
// Plain identifiers break when a column name is a reserved word (e.g. a soft PK named
|
|
118
|
-
// `open`/`order`); brackets would fix SQL Server but break Postgres, so double-quote.
|
|
119
|
-
const clause = `"${pkField.Name}" = '${escaped}'`;
|
|
120
|
-
if (!filterClauses.includes(clause)) {
|
|
121
|
-
filterClauses.push(clause);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (filterClauses.length === 0)
|
|
299
|
+
const pkFields = this.primaryKeyFieldsFor(record.MJEntityName);
|
|
300
|
+
const match = this.BuildMatchCriteria(record, keyFields);
|
|
301
|
+
if (!match)
|
|
126
302
|
return null;
|
|
127
303
|
const rv = new RunView();
|
|
128
304
|
const result = await rv.RunView({
|
|
129
305
|
EntityName: record.MJEntityName,
|
|
130
|
-
ExtraFilter:
|
|
306
|
+
ExtraFilter: this.CriteriaToSQL(match),
|
|
131
307
|
Fields: pkFields.map(f => f.Name),
|
|
132
308
|
MaxRows: 1,
|
|
133
309
|
ResultType: 'simple',
|
|
@@ -139,32 +315,226 @@ export class MatchEngine {
|
|
|
139
315
|
return pkFields.map(f => result.Results[0][f.Name] ?? '').join('|');
|
|
140
316
|
}
|
|
141
317
|
/**
|
|
142
|
-
* Builds
|
|
318
|
+
* Builds the field/value criteria that identify a record, applying the item-3 identity rule:
|
|
319
|
+
* a COMPLETE primary key matches on the PK alone; otherwise the configured key fields plus
|
|
320
|
+
* whatever PK parts are present. Returns null when nothing can be matched on.
|
|
321
|
+
*
|
|
322
|
+
* Field/value pairs (rather than pre-rendered SQL) so the same criteria can be rendered as a
|
|
323
|
+
* filter clause AND used as a local lookup key when a whole batch is resolved in one query.
|
|
143
324
|
*/
|
|
144
|
-
|
|
145
|
-
const
|
|
325
|
+
BuildMatchCriteria(record, keyFields) {
|
|
326
|
+
const pkFields = this.primaryKeyFieldsFor(record.MJEntityName);
|
|
327
|
+
if (pkFields.length === 0)
|
|
328
|
+
return null;
|
|
329
|
+
// serializeKeyValue mirrors the write-side coercion (objects → JSON, not "[object Object]")
|
|
330
|
+
// so an object-valued key filters against the value actually stored in the column.
|
|
331
|
+
const pk = { Fields: [], Values: [] };
|
|
332
|
+
for (const pkField of pkFields) {
|
|
333
|
+
const value = record.MappedFields[pkField.Name];
|
|
334
|
+
if (value == null)
|
|
335
|
+
continue;
|
|
336
|
+
pk.Fields.push(pkField.Name);
|
|
337
|
+
pk.Values.push(serializeKeyValue(value));
|
|
338
|
+
}
|
|
339
|
+
// Complete PK → identity match, on the PK alone. Configured key fields are deliberately
|
|
340
|
+
// NOT and-ed in here: a key field that disagrees with the PK would turn a correct
|
|
341
|
+
// identity match into a miss, and a miss becomes a duplicate INSERT downstream.
|
|
342
|
+
if (pk.Fields.length === pkFields.length)
|
|
343
|
+
return pk;
|
|
344
|
+
// Partial/absent PK → key fields first, then the PK parts we do have, to narrow.
|
|
345
|
+
// A field can be both a key field and a PK part; `A='x' AND A='x'` is noise, so dedup
|
|
346
|
+
// by name (both read the same MappedFields entry, so the values cannot disagree).
|
|
347
|
+
const merged = { Fields: [], Values: [] };
|
|
348
|
+
const add = (field, value) => {
|
|
349
|
+
if (merged.Fields.includes(field))
|
|
350
|
+
return;
|
|
351
|
+
merged.Fields.push(field);
|
|
352
|
+
merged.Values.push(value);
|
|
353
|
+
};
|
|
146
354
|
for (const kf of keyFields) {
|
|
147
355
|
const value = record.MappedFields[kf.DestinationFieldName];
|
|
148
356
|
if (value == null)
|
|
149
357
|
continue;
|
|
150
|
-
|
|
151
|
-
// so an object-valued key filters against the value actually stored in the column.
|
|
152
|
-
const escaped = serializeKeyValue(value).replace(/'/g, "''");
|
|
153
|
-
// ANSI double-quoted identifier — reserved-word-safe + portable; see CompositePK note above.
|
|
154
|
-
clauses.push(`"${kf.DestinationFieldName}" = '${escaped}'`);
|
|
358
|
+
add(kf.DestinationFieldName, serializeKeyValue(value));
|
|
155
359
|
}
|
|
156
|
-
|
|
360
|
+
for (let i = 0; i < pk.Fields.length; i++)
|
|
361
|
+
add(pk.Fields[i], pk.Values[i]);
|
|
362
|
+
return merged.Fields.length > 0 ? merged : null;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Quotes a value as a SQL string literal for a `RunView` `ExtraFilter`.
|
|
366
|
+
*
|
|
367
|
+
* `ExtraFilter` is SQL text, not a parameter list, so literals must be escaped here rather
|
|
368
|
+
* than bound. Where the provider is a database provider this defers to its dialect — the same
|
|
369
|
+
* rule `RecordMapBatch`'s read path uses, so the two cannot disagree about a value.
|
|
370
|
+
* `IMetadataProvider` in general carries no dialect (a client-side provider has none), so the
|
|
371
|
+
* fallback is ANSI quote-doubling, which is what both supported platforms do.
|
|
372
|
+
*
|
|
373
|
+
* Every value quoted here — external IDs and key-field values alike — is compared against an
|
|
374
|
+
* nvarchar column, so it goes through {@link quoteTextLiteral} rather than the dialect's bare
|
|
375
|
+
* `QuoteStringLiteral`: on SQL Server the bare form is a *varchar* literal and silently drops
|
|
376
|
+
* any character outside the database's collation codepage before the comparison.
|
|
377
|
+
*/
|
|
378
|
+
quoteLiteral(value) {
|
|
379
|
+
const provider = this.ProviderToUse;
|
|
380
|
+
return provider instanceof DatabaseProviderBase
|
|
381
|
+
? quoteTextLiteral(value, provider.Dialect)
|
|
382
|
+
: `'${value.replace(/'/g, "''")}'`;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Renders criteria as a filter clause.
|
|
386
|
+
*
|
|
387
|
+
* ANSI double-quoted identifiers — portable across SQL Server (QUOTED_IDENTIFIER ON, the
|
|
388
|
+
* driver default) and Postgres (exact-case; integration columns are lowercase). Plain
|
|
389
|
+
* identifiers break on a column named for a reserved word (e.g. a soft PK named
|
|
390
|
+
* `open`/`order`); brackets would fix SQL Server but break Postgres, so double-quote.
|
|
391
|
+
*/
|
|
392
|
+
CriteriaToSQL(criteria) {
|
|
393
|
+
return criteria.Fields
|
|
394
|
+
.map((f, i) => `"${f}" = ${this.quoteLiteral(criteria.Values[i])}`)
|
|
395
|
+
.join(' AND ');
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Order-independent local lookup key for a criteria set (and for a row read back for it).
|
|
399
|
+
*
|
|
400
|
+
* `\0` delimits both within and between pairs, because neither a field name nor a value can
|
|
401
|
+
* contain it and no printable delimiter can make that guarantee. With a space, field `A B`
|
|
402
|
+
* = `C` and field `A` = `B C` both render `A B C` — and since this key is what decides which
|
|
403
|
+
* row answers which criteria, a collision attributes a row to the wrong record.
|
|
404
|
+
*/
|
|
405
|
+
CriteriaKey(fields, values) {
|
|
406
|
+
return fields
|
|
407
|
+
.map((f, i) => [f, values[i]])
|
|
408
|
+
.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))
|
|
409
|
+
.map(([f, v]) => `${f}\0${v}`)
|
|
410
|
+
.join('\0');
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Resolves the identity/key lookup for a WHOLE batch in one query per criteria shape,
|
|
414
|
+
* instead of one query per record (tasks.md PR 2 item 5).
|
|
415
|
+
*
|
|
416
|
+
* Records are grouped by entity + the exact set of fields they match on, so every record in
|
|
417
|
+
* a group produces a structurally identical AND-clause; the group's clauses are OR-ed into a
|
|
418
|
+
* single read and the rows attributed back locally by the same field/value pairs that built
|
|
419
|
+
* the filter. A 500-record batch of an integration shadow table (single soft PK) collapses
|
|
420
|
+
* from 500 reads to 1.
|
|
421
|
+
*
|
|
422
|
+
* Attribution is deliberately conservative. `Matched` holds records we found a row for;
|
|
423
|
+
* `Unmatched` holds records we can PROVE have no row — only populated when the group's read
|
|
424
|
+
* returned nothing at all, so a record whose row exists but whose local key comparison
|
|
425
|
+
* differs from the database's (case-insensitive collation, numeric/date formatting) is left
|
|
426
|
+
* in neither set and falls back to its own query. A false "unmatched" would create a
|
|
427
|
+
* duplicate, so it is never inferred.
|
|
428
|
+
*
|
|
429
|
+
* This half is pure: it builds the reads without issuing them, so every group goes out as one
|
|
430
|
+
* leg of the batch's single {@link RunView.RunViews} call. {@link MatchEngine.buildKeyMatchIndex}
|
|
431
|
+
* attributes the results back.
|
|
432
|
+
*/
|
|
433
|
+
buildKeyMatchGroups(records, keyFields) {
|
|
434
|
+
const bySignature = new Map();
|
|
435
|
+
for (const record of records) {
|
|
436
|
+
if (record.ExternalRecord.IsDeleted)
|
|
437
|
+
continue;
|
|
438
|
+
if (keyFields.length === 0 && !this.hasCompleteMappedPrimaryKey(record))
|
|
439
|
+
continue;
|
|
440
|
+
const criteria = this.BuildMatchCriteria(record, keyFields);
|
|
441
|
+
if (!criteria)
|
|
442
|
+
continue;
|
|
443
|
+
// `\0`-delimited for the same reason CriteriaKey is: entity `AB` + field `C` and
|
|
444
|
+
// entity `A` + field `BC` would otherwise share a signature, merging two entities
|
|
445
|
+
// into one group that then reads whichever entity landed first.
|
|
446
|
+
const signature = `${record.MJEntityName}\0${[...criteria.Fields].sort().join('\0')}`;
|
|
447
|
+
const group = bySignature.get(signature) ?? { EntityName: record.MJEntityName, Criteria: [] };
|
|
448
|
+
group.Criteria.push(criteria);
|
|
449
|
+
if (!bySignature.has(signature))
|
|
450
|
+
bySignature.set(signature, group);
|
|
451
|
+
}
|
|
452
|
+
const groups = [];
|
|
453
|
+
for (const group of bySignature.values()) {
|
|
454
|
+
const pkFields = this.primaryKeyFieldsFor(group.EntityName);
|
|
455
|
+
if (pkFields.length === 0)
|
|
456
|
+
continue;
|
|
457
|
+
// Distinct clause sets only — a batch commonly repeats the same external record.
|
|
458
|
+
const clauses = new Map();
|
|
459
|
+
for (const c of group.Criteria)
|
|
460
|
+
clauses.set(this.CriteriaKey(c.Fields, c.Values), c);
|
|
461
|
+
const lookupFields = group.Criteria[0].Fields;
|
|
462
|
+
groups.push({
|
|
463
|
+
PKFields: pkFields,
|
|
464
|
+
LookupFields: lookupFields,
|
|
465
|
+
Clauses: clauses,
|
|
466
|
+
Params: {
|
|
467
|
+
EntityName: group.EntityName,
|
|
468
|
+
ExtraFilter: Array.from(clauses.values())
|
|
469
|
+
.map(c => `(${this.CriteriaToSQL(c)})`)
|
|
470
|
+
.join(' OR '),
|
|
471
|
+
Fields: Array.from(new Set([...pkFields.map(f => f.Name), ...lookupFields])),
|
|
472
|
+
IgnoreMaxRows: true, // a batch's matches can exceed the entity's default row cap
|
|
473
|
+
ResultType: 'simple',
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
return groups;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Attributes the batched group reads back to their criteria, positionally, in the order
|
|
481
|
+
* {@link MatchEngine.buildKeyMatchGroups} emitted them.
|
|
482
|
+
*
|
|
483
|
+
* Returns null if any group read failed — callers then use the original per-record path.
|
|
484
|
+
*/
|
|
485
|
+
buildKeyMatchIndex(groups, results) {
|
|
486
|
+
const index = { Matched: new Map(), Unmatched: new Set() };
|
|
487
|
+
for (let i = 0; i < groups.length; i++) {
|
|
488
|
+
const group = groups[i];
|
|
489
|
+
const result = results[i];
|
|
490
|
+
if (!result?.Success)
|
|
491
|
+
return null;
|
|
492
|
+
const rows = result.Results;
|
|
493
|
+
for (const row of rows) {
|
|
494
|
+
const rowKey = this.CriteriaKey(group.LookupFields, group.LookupFields.map(f => serializeKeyValue(row[f])));
|
|
495
|
+
if (index.Matched.has(rowKey))
|
|
496
|
+
continue; // first row wins, as MaxRows:1 did
|
|
497
|
+
index.Matched.set(rowKey, group.PKFields.map(f => row[f.Name] ?? '').join('|'));
|
|
498
|
+
}
|
|
499
|
+
// Only a completely empty read proves absence for the whole group (see doc above).
|
|
500
|
+
if (rows.length === 0) {
|
|
501
|
+
for (const key of group.Clauses.keys())
|
|
502
|
+
index.Unmatched.add(key);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return index;
|
|
157
506
|
}
|
|
158
507
|
/**
|
|
159
508
|
* Checks the CompanyIntegrationRecordMap for a previous external↔MJ mapping.
|
|
160
509
|
*/
|
|
161
|
-
async FindRecordMapEntry(companyIntegrationID, externalID, entityID, contextUser) {
|
|
510
|
+
async FindRecordMapEntry(companyIntegrationID, externalID, entityID, contextUser, mapIndex) {
|
|
511
|
+
// The batch prefetch read every mapping for this batch's external IDs in one query, so
|
|
512
|
+
// a hit here is the same answer the per-record query would give.
|
|
513
|
+
//
|
|
514
|
+
// Concluding ABSENCE from the index is the delicate part, and it is only sound because the
|
|
515
|
+
// index is consulted the same two ways the database compared: exactly, then folded the way
|
|
516
|
+
// a default collation folds (normalizeExternalID). Two cases the index cannot speak for —
|
|
517
|
+
// an ambiguous fold, and an empty ID that was never in the `IN (…)` list — fall through to
|
|
518
|
+
// the per-record query below and let the database answer.
|
|
519
|
+
if (mapIndex && externalID) {
|
|
520
|
+
const exact = mapIndex.Exact.get(externalID);
|
|
521
|
+
if (exact !== undefined)
|
|
522
|
+
return exact;
|
|
523
|
+
const folded = normalizeExternalID(externalID);
|
|
524
|
+
const near = mapIndex.Normalized.get(folded);
|
|
525
|
+
if (near !== undefined)
|
|
526
|
+
return near;
|
|
527
|
+
// Ambiguous fold: several mappings collapse onto this key and the index cannot say
|
|
528
|
+
// which one the database would pick. Fall through and let it decide.
|
|
529
|
+
if (!mapIndex.Ambiguous.has(folded))
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
162
532
|
const rv = new RunView();
|
|
163
|
-
const
|
|
533
|
+
const quotedExternalID = this.quoteLiteral(externalID);
|
|
164
534
|
const result = await rv.RunView({
|
|
165
535
|
EntityName: 'MJ: Company Integration Record Maps',
|
|
166
536
|
ExtraFilter: `CompanyIntegrationID='${companyIntegrationID}' ` +
|
|
167
|
-
`AND ExternalSystemRecordID
|
|
537
|
+
`AND ExternalSystemRecordID=${quotedExternalID} ` +
|
|
168
538
|
`AND EntityID='${entityID}'`,
|
|
169
539
|
Fields: ['EntityRecordID'],
|
|
170
540
|
MaxRows: 1,
|
package/dist/MatchEngine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MatchEngine.js","sourceRoot":"","sources":["../src/MatchEngine.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MatchEngine.js","sourceRoot":"","sources":["../src/MatchEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAqB,QAAQ,EAAE,OAAO,EAAyD,MAAM,sBAAsB,CAAC;AAGzJ,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA4BvD;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,mBAAmB,CAAC,EAAU;IACnC,sFAAsF;IACtF,2FAA2F;IAC3F,sFAAsF;IACtF,wFAAwF;IACxF,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AAC/C,CAAC;AAyBD;;;GAGG;AACH,MAAM,OAAO,WAAW;IAIpB,4FAA4F;IAC5F,IAAc,aAAa;QACvB,OAAO,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,QAAQ,CAAC;IAC/C,CAAC;IAED;;;;;;;;;OASG;IACI,KAAK,CAAC,OAAO,CAChB,OAAuB,EACvB,SAAuC,EACvC,SAAwC,EACxC,WAAqB,EACrB,QAA4B;QAE5B,IAAI,QAAQ;YAAE,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QACxC,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;QAClF,MAAM,kBAAkB,GAAG,SAAS,CAAC,kBAAwC,CAAC;QAE9E,sFAAsF;QACtF,uFAAuF;QACvF,+EAA+E;QAC/E,qFAAqF;QACrF,EAAE;QACF,oFAAoF;QACpF,sFAAsF;QACtF,iFAAiF;QACjF,yEAAyE;QACzE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAC9E,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,CAC7C,CAAC;QAEF,MAAM,OAAO,GAAmB,EAAE,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAC3C,MAAM,EACN,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,QAAQ,EACR,QAAQ,CACX,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IAED;;;;;;;;;;;OAWG;IACK,KAAK,CAAC,oBAAoB,CAC9B,OAAuB,EACvB,SAAuC,EACvC,SAAwC,EACxC,WAAqB;QAErB,MAAM,SAAS,GAAG,IAAI,CAAC,wBAAwB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACpE,MAAM,SAAS,GAAG,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAE/D,MAAM,aAAa,GAAmB,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;QACxG,MAAM,aAAa,GAAkB,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;QAClF,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;QAEtG,MAAM,MAAM,GAAoB,EAAE,CAAC;QACnC,IAAI,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,KAAK,MAAM,KAAK,IAAI,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAEzD,MAAM,EAAE,GAAG,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAEvD,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,MAAM,QAAQ,GAAG,SAAS;YACtB,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACrD,CAAC,CAAC,aAAa,CAAC;QAEpB,OAAO;YACH,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SACpE,CAAC;IACN,CAAC;IAED;;;OAGG;IACK,wBAAwB,CAC5B,OAAuB,EACvB,SAAuC;QAEvC,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAClC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC,CACtF,CAAC,CAAC;QACH,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAE1C,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtE,OAAO;YACH,UAAU,EAAE,qCAAqC;YACjD,WAAW,EACP,yBAAyB,SAAS,CAAC,oBAAoB,IAAI;gBAC3D,iBAAiB,SAAS,CAAC,QAAQ,IAAI;gBACvC,kCAAkC,MAAM,GAAG;YAC/C,MAAM,EAAE,CAAC,wBAAwB,EAAE,gBAAgB,CAAC;YACpD,aAAa,EAAE,IAAI,EAAE,kDAAkD;YACvE,UAAU,EAAE,QAAQ;YACpB,uFAAuF;YACvF,4DAA4D;YAC5D,WAAW,EAAE,IAAI;SACpB,CAAC;IACN,CAAC;IAED;;;;;;;;;;;;OAYG;IACK,6BAA6B,CAAC,MAAiC;QACnE,IAAI,CAAC,MAAM,EAAE,OAAO;YAAE,OAAO,IAAI,CAAC;QAClC,OAAO,IAAI,CAAC,mBAAmB,CAC3B,MAAM,CAAC,OAA4E,CACtF,CAAC;IACN,CAAC;IAED,gGAAgG;IACxF,mBAAmB,CACvB,IAAkE;QAElE,MAAM,KAAK,GAAmB,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;QAChG,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,2BAA2B,CAAC;QAE/C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,oFAAoF;YACpF,kFAAkF;YAClF,MAAM,UAAU,GAAG,GAAG,CAAC,sBAAsB,CAAC;YAC9C,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,EAAE;gBAAE,SAAS;YAElE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;YAEhD,oFAAoF;YACpF,2EAA2E;YAC3E,IAAI,CAAC,KAAK;gBAAE,SAAS;YAErB,MAAM,IAAI,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;YAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,IAAI,KAAK,SAAS;gBAAE,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;iBAClE,IAAI,IAAI,KAAK,GAAG,CAAC,cAAc;gBAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9D,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,SAAS;YAAE,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1D,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;;;;;;;;;OAUG;IACH,IAAY,2BAA2B;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC;QACpC,mFAAmF;QACnF,wFAAwF;QACxF,2EAA2E;QAC3E,OAAO,QAAQ,YAAY,oBAAoB,IAAI,QAAQ,CAAC,WAAW,KAAK,WAAW,CAAC;IAC5F,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,mBAAmB,CAC7B,MAAoB,EACpB,SAAuC,EACvC,SAAwC,EACxC,kBAAsC,EACtC,WAAqB,EACrB,QAAgC,EAChC,QAA+B;QAE/B,IAAI,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QAC/E,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAC5C,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAChE,CAAC;QAEF,IAAI,UAAU,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,kBAAkB,CAAC,CAAC;QAC9E,CAAC;QAED,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IAC/C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,oBAAoB,CAC9B,MAAoB,EACpB,SAAuC,EACvC,WAAqB,EACrB,QAAgC;QAEhC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAC5C,SAAS,CAAC,oBAAoB,EAC9B,MAAM,CAAC,cAAc,CAAC,UAAU,EAChC,SAAS,CAAC,QAAQ,EAClB,WAAW,EACX,QAAQ,CACX,CAAC;QAEF,IAAI,UAAU,EAAE,CAAC;YACb,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAC;QAC9E,CAAC;QAED,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAC7C,CAAC;IAED;;OAEG;IACK,qBAAqB,CACzB,MAAoB,EACpB,UAAkB,EAClB,kBAAsC;QAEtC,IAAI,kBAAkB,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAC;QAC5E,CAAC;QACD,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAC;IAC9E,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACK,KAAK,CAAC,kBAAkB,CAC5B,MAAoB,EACpB,SAAuC,EACvC,SAAwC,EACxC,WAAqB,EACrB,QAAgC,EAChC,QAA+B;QAE/B,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,2BAA2B,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;YACxF,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAC;QAClC,CAAC;QAED,OAAO,IAAI,CAAC,kBAAkB,CAC1B,SAAS,CAAC,oBAAoB,EAC9B,MAAM,CAAC,cAAc,CAAC,UAAU,EAChC,SAAS,CAAC,QAAQ,EAClB,WAAW,EACX,QAAQ,CACX,CAAC;IACN,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,iBAAiB,CAC3B,MAAoB,EACpB,SAAwC,EACxC,WAAqB,EACrB,QAA+B;QAE/B,IAAI,QAAQ,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAC5D,IAAI,QAAQ,EAAE,CAAC;gBACX,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAC/D,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC1C,IAAI,OAAO;oBAAE,OAAO,OAAO,CAAC;gBAC5B,IAAI,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,OAAO,IAAI,CAAC;YACjD,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IAChE,CAAC;IAED;;;;OAIG;IACK,mBAAmB,CAAC,YAAoB;QAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QACjE,OAAO,UAAU,EAAE,WAAW,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxG,CAAC;IAED,kGAAkG;IAC1F,2BAA2B,CAAC,MAAoB;QACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC/D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACxC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACK,KAAK,CAAC,eAAe,CACzB,MAAoB,EACpB,SAAwC,EACxC,WAAqB;QAErB,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACzD,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,EAAE,GAAG,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAyB;YACpD,UAAU,EAAE,MAAM,CAAC,YAAY;YAC/B,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;YACtC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACjC,OAAO,EAAE,CAAC;YACV,UAAU,EAAE,QAAQ;SACvB,EAAE,WAAW,CAAC,CAAC;QAEhB,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEhE,uEAAuE;QACvE,8EAA8E;QAC9E,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxE,CAAC;IAED;;;;;;;OAOG;IACK,kBAAkB,CACtB,MAAoB,EACpB,SAAwC;QAExC,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC/D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEvC,4FAA4F;QAC5F,mFAAmF;QACnF,MAAM,EAAE,GAAkB,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACrD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,KAAK,IAAI,IAAI;gBAAE,SAAS;YAC5B,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC7B,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,CAAC;QAED,wFAAwF;QACxF,kFAAkF;QAClF,gFAAgF;QAChF,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;QAEpD,iFAAiF;QACjF,sFAAsF;QACtF,kFAAkF;QAClF,MAAM,MAAM,GAAkB,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACzD,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,EAAE;YACzC,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,OAAO;YAC1C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC;QACF,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC;YAC3D,IAAI,KAAK,IAAI,IAAI;gBAAE,SAAS;YAC5B,GAAG,CAAC,EAAE,CAAC,oBAAoB,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3D,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE3E,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACpD,CAAC;IAED;;;;;;;;;;;;;OAaG;IACK,YAAY,CAAC,KAAa;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC;QACpC,OAAO,QAAQ,YAAY,oBAAoB;YAC3C,CAAC,CAAC,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC;YAC3C,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;IAC3C,CAAC;IAED;;;;;;;OAOG;IACK,aAAa,CAAC,QAAuB;QACzC,OAAO,QAAQ,CAAC,MAAM;aACjB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;aAClE,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAED;;;;;;;OAOG;IACK,WAAW,CAAC,MAAgB,EAAE,MAAgB;QAClD,OAAO,MAAM;aACR,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAU,CAAC;aACtC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aACxD,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;aAC7B,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACK,mBAAmB,CACvB,OAAuB,EACvB,SAAwC;QAExC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA6D,CAAC;QACzF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,MAAM,CAAC,cAAc,CAAC,SAAS;gBAAE,SAAS;YAC9C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B,CAAC,MAAM,CAAC;gBAAE,SAAS;YAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAC5D,IAAI,CAAC,QAAQ;gBAAE,SAAS;YACxB,iFAAiF;YACjF,kFAAkF;YAClF,gEAAgE;YAChE,MAAM,SAAS,GAAG,GAAG,MAAM,CAAC,YAAY,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtF,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAC9F,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC;gBAAE,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,MAAM,GAAoB,EAAE,CAAC;QACnC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC5D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAEpC,iFAAiF;YACjF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;YACjD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAErF,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC;gBACR,QAAQ,EAAE,QAAQ;gBAClB,YAAY,EAAE,YAAY;gBAC1B,OAAO,EAAE,OAAO;gBAChB,MAAM,EAAE;oBACJ,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;yBACpC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC;yBACtC,IAAI,CAAC,MAAM,CAAC;oBACjB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;oBAC5E,aAAa,EAAE,IAAI,EAAE,4DAA4D;oBACjF,UAAU,EAAE,QAAQ;iBACvB;aACJ,CAAC,CAAC;QACP,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;;;OAKG;IACK,kBAAkB,CAAC,MAAuB,EAAE,OAAwB;QACxE,MAAM,KAAK,GAAkB,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;QAE1E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,MAAM,EAAE,OAAO;gBAAE,OAAO,IAAI,CAAC;YAElC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAyC,CAAC;YAC9D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAC3B,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CACzD,CAAC;gBACF,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;oBAAE,SAAS,CAAC,mCAAmC;gBAC5E,KAAK,CAAC,OAAO,CAAC,GAAG,CACb,MAAM,EACN,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAmB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAC1E,CAAC;YACN,CAAC;YAED,mFAAmF;YACnF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACpB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;oBAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACrE,CAAC;QACL,CAAC;QAED,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,kBAAkB,CAC5B,oBAA4B,EAC5B,UAAkB,EAClB,QAAgB,EAChB,WAAqB,EACrB,QAAgC;QAEhC,uFAAuF;QACvF,iEAAiE;QACjE,EAAE;QACF,2FAA2F;QAC3F,2FAA2F;QAC3F,0FAA0F;QAC1F,2FAA2F;QAC3F,0DAA0D;QAC1D,IAAI,QAAQ,IAAI,UAAU,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC7C,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;YACtC,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;YAC/C,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC;YACpC,mFAAmF;YACnF,qEAAqE;YACrE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;QACrD,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,gBAAgB,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAA6B;YACxD,UAAU,EAAE,qCAAqC;YACjD,WAAW,EACP,yBAAyB,oBAAoB,IAAI;gBACjD,8BAA8B,gBAAgB,GAAG;gBACjD,iBAAiB,QAAQ,GAAG;YAChC,MAAM,EAAE,CAAC,gBAAgB,CAAC;YAC1B,OAAO,EAAE,CAAC;YACV,UAAU,EAAE,QAAQ;YACpB,sFAAsF;YACtF,wFAAwF;YACxF,uFAAuF;YACvF,WAAW,EAAE,IAAI;SACpB,EAAE,WAAW,CAAC,CAAC;QAEhB,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAChE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;IAC5C,CAAC;CACJ"}
|