@memberjunction/server 5.41.0 → 5.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  2. package/dist/agentSessions/SessionJanitor.js +5 -1
  3. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  4. package/dist/agentSessions/SessionManager.d.ts +15 -2
  5. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  6. package/dist/agentSessions/SessionManager.js +50 -11
  7. package/dist/agentSessions/SessionManager.js.map +1 -1
  8. package/dist/agentSessions/index.d.ts +1 -0
  9. package/dist/agentSessions/index.d.ts.map +1 -1
  10. package/dist/agentSessions/index.js +5 -0
  11. package/dist/agentSessions/index.js.map +1 -1
  12. package/dist/agentSessions/remoteBrowserGoalEngine.d.ts +102 -0
  13. package/dist/agentSessions/remoteBrowserGoalEngine.d.ts.map +1 -0
  14. package/dist/agentSessions/remoteBrowserGoalEngine.js +164 -0
  15. package/dist/agentSessions/remoteBrowserGoalEngine.js.map +1 -0
  16. package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts +66 -0
  17. package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts.map +1 -0
  18. package/dist/agentSessions/remoteBrowserGoalRegistry.js +86 -0
  19. package/dist/agentSessions/remoteBrowserGoalRegistry.js.map +1 -0
  20. package/dist/auth/initializeProviders.d.ts.map +1 -1
  21. package/dist/auth/initializeProviders.js +4 -7
  22. package/dist/auth/initializeProviders.js.map +1 -1
  23. package/dist/config.d.ts +7 -0
  24. package/dist/config.d.ts.map +1 -1
  25. package/dist/config.js +12 -2
  26. package/dist/config.js.map +1 -1
  27. package/dist/generated/generated.d.ts +612 -0
  28. package/dist/generated/generated.d.ts.map +1 -1
  29. package/dist/generated/generated.js +6934 -3626
  30. package/dist/generated/generated.js.map +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +38 -17
  33. package/dist/index.js.map +1 -1
  34. package/dist/integration/CustomColumnPromoter.d.ts +108 -0
  35. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -0
  36. package/dist/integration/CustomColumnPromoter.js +508 -0
  37. package/dist/integration/CustomColumnPromoter.js.map +1 -0
  38. package/dist/logging/StartupLogger.d.ts +57 -1
  39. package/dist/logging/StartupLogger.d.ts.map +1 -1
  40. package/dist/logging/StartupLogger.js +115 -6
  41. package/dist/logging/StartupLogger.js.map +1 -1
  42. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +46 -0
  43. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -0
  44. package/dist/resolvers/ExecuteRemoteOperationResolver.js +192 -0
  45. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -0
  46. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +101 -1
  47. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  48. package/dist/resolvers/IntegrationDiscoveryResolver.js +629 -58
  49. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  50. package/dist/resolvers/RealtimeBridgeResolver.d.ts +130 -0
  51. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -0
  52. package/dist/resolvers/RealtimeBridgeResolver.js +580 -0
  53. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -0
  54. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +78 -5
  55. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  56. package/dist/resolvers/RemoteBrowserActionResolver.js +227 -15
  57. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  58. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  59. package/dist/resolvers/RunAIAgentResolver.js +0 -7
  60. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  61. package/dist/resolvers/RunTemplateResolver.d.ts +8 -0
  62. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  63. package/dist/resolvers/RunTemplateResolver.js +8 -0
  64. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  65. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  66. package/dist/rest/setupRESTEndpoints.js +5 -3
  67. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  68. package/package.json +83 -78
  69. package/src/__tests__/RealtimeBridgeResolver.test.ts +100 -0
  70. package/src/__tests__/RemoteBrowserAudioStream.test.ts +8 -1
  71. package/src/__tests__/RemoteBrowserGoalEngine.test.ts +144 -0
  72. package/src/__tests__/remoteBrowserGoalRegistry.test.ts +81 -0
  73. package/src/agentSessions/SessionJanitor.ts +15 -1
  74. package/src/agentSessions/SessionManager.ts +59 -10
  75. package/src/agentSessions/index.ts +5 -0
  76. package/src/agentSessions/remoteBrowserGoalEngine.ts +191 -0
  77. package/src/agentSessions/remoteBrowserGoalRegistry.ts +122 -0
  78. package/src/auth/initializeProviders.ts +4 -7
  79. package/src/config.ts +13 -2
  80. package/src/generated/generated.ts +2502 -198
  81. package/src/index.ts +38 -17
  82. package/src/integration/CustomColumnPromoter.ts +597 -0
  83. package/src/logging/StartupLogger.ts +130 -6
  84. package/src/resolvers/ExecuteRemoteOperationResolver.ts +164 -0
  85. package/src/resolvers/IntegrationDiscoveryResolver.ts +490 -53
  86. package/src/resolvers/RealtimeBridgeResolver.ts +493 -0
  87. package/src/resolvers/RemoteBrowserActionResolver.ts +232 -17
  88. package/src/resolvers/RunAIAgentResolver.ts +1 -9
  89. package/src/resolvers/RunTemplateResolver.ts +8 -0
  90. package/src/rest/setupRESTEndpoints.ts +5 -3
@@ -0,0 +1,508 @@
1
+ /**
2
+ * Post-sync custom-column promoter (gaps.md §2 / M2b-server).
3
+ *
4
+ * Registered onto {@link IntegrationEngine} as the post-sync schema-promotion hook (the engine
5
+ * itself has no dependency on RSU/CodeGen — see SetPostSyncSchemaPromotionCallback). After a
6
+ * sync drains, the engine invokes this for the synced entities; here we:
7
+ *
8
+ * 1. GATE — sample the overflow column per entity. No overflow rows → no work (this is what
9
+ * keeps a customs-free sync single-stage / 1×; your hard rule).
10
+ * 2. PLAN — {@link buildOverflowStats} + {@link planPromotions} decide which keys earn a real
11
+ * column and the generously-bounded type for each (pure engine logic, unit-tested).
12
+ * 3. PROMOTE — for the winners: ADD COLUMN via {@link DDLGenerator} + {@link RuntimeSchemaManager}
13
+ * (CodeGen reflects the column into a real EntityField), an IOF row per field (IsCustom,
14
+ * MetadataSource='Discovered'), and a field map so the next sync maps it natively → the
15
+ * capture/promote loop terminates.
16
+ *
17
+ * DIALECT PARITY (crucial, per PR #2752): the coverage scan is RunView (dialect-agnostic); DDL
18
+ * goes through DDLGenerator driven by provider.PlatformKey ('sqlserver' | 'postgresql'); RSU has
19
+ * dual SS/PG setup; IOF + field map are BaseEntity. No SS-only SQL anywhere in this file.
20
+ *
21
+ * The MJAPI restart (so the new column is exposed over GraphQL) + the JSON spread of staged
22
+ * values into the new columns are M3 — this stage applies the schema only (SkipRestart).
23
+ */
24
+ import { LogError, LogStatus, LogStatusEx, Metadata, RunView, } from '@memberjunction/core';
25
+ import { IntegrationEngine, CUSTOM_OVERFLOW_COLUMN, CONTENT_HASH_COLUMN, computeContentHash, buildOverflowStats, planPromotions, sanitizeColumnName, } from '@memberjunction/integration-engine';
26
+ import { DDLGenerator } from '@memberjunction/integration-schema-builder';
27
+ import { RuntimeSchemaManager } from '@memberjunction/schema-engine';
28
+ /** Max rows sampled from the overflow column to compute coverage + infer types (bounded memory). */
29
+ const OVERFLOW_SAMPLE_SIZE = 1000;
30
+ /**
31
+ * Max columns promoted in a SINGLE pass, so a feed that suddenly exposes hundreds of distinct
32
+ * high-coverage keys can't mint hundreds of columns in one sync (bounded schema churn). The
33
+ * remainder is logged + deferred; it promotes on subsequent syncs (the keys stay captured).
34
+ */
35
+ const MAX_PROMOTIONS_PER_PASS = 25;
36
+ /** Registers the post-sync custom-column promotion hook on the IntegrationEngine singleton. */
37
+ export function registerIntegrationCustomColumnPromoter() {
38
+ IntegrationEngine.Instance.SetPostSyncSchemaPromotionCallback(async (ctx) => {
39
+ const user = ctx.ContextUser;
40
+ const provider = ctx.Provider;
41
+ // GATE — promotion runs RSU (ADD COLUMN + register EntityField) + restart, which is disruptive.
42
+ // It is OPT-IN per connection (DEFAULT OFF): by default a sync only CAPTURES unmapped fields into
43
+ // the overflow column; the user triggers promotion on demand via IntegrationPromoteCustomColumns
44
+ // (after reviewing IntegrationListCustomColumnCandidates). Set Configuration.autoPromoteCustomColumns=true
45
+ // to restore automatic post-sync promotion. (Capture is unconditional; only the RSU step is gated.)
46
+ if (!(await readAutoPromoteFlag(ctx.CompanyIntegrationID, user, provider))) {
47
+ LogStatus(`[CustomColumnPromoter] Auto-promote OFF for CI ${ctx.CompanyIntegrationID} — unmapped fields captured to ` +
48
+ `the overflow column; awaiting on-demand promotion (IntegrationPromoteCustomColumns).`);
49
+ return { Promoted: false, ColumnsAdded: [], SchemaUpdatePending: false };
50
+ }
51
+ const promoter = new IntegrationCustomColumnPromoter(user, provider);
52
+ return promoter.PromoteForSync(ctx.CompanyIntegrationID, ctx.SyncedEntityNames);
53
+ });
54
+ // Verbose-only: this is a boot-time registration confirmation, not operator-actionable at
55
+ // standard level. Routes through the global verbose gate (set from the server's telemetry.level).
56
+ LogStatusEx({ message: '[CustomColumnPromoter] Registered post-sync custom-column promotion hook (auto-promote opt-in, default OFF).', verboseOnly: true });
57
+ }
58
+ /** Reads the per-connection `autoPromoteCustomColumns` flag (default false = capture-only, on-demand promotion). */
59
+ async function readAutoPromoteFlag(companyIntegrationID, user, provider) {
60
+ try {
61
+ const md = provider ?? Metadata.Provider;
62
+ const ci = await md.GetEntityObject('MJ: Company Integrations', user);
63
+ if (!(await ci.Load(companyIntegrationID)) || !ci.Configuration)
64
+ return false;
65
+ return JSON.parse(ci.Configuration).autoPromoteCustomColumns === true;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ const NOT_PROMOTED = { Promoted: false, ColumnsAdded: [], SchemaUpdatePending: false };
72
+ /**
73
+ * Orchestrates promotion for one sync run. One instance per invocation (carries the per-sync
74
+ * context user + provider so it respects the bound provider, never the global default blindly).
75
+ */
76
+ export class IntegrationCustomColumnPromoter {
77
+ constructor(user, providerOverride) {
78
+ this.user = user;
79
+ this.providerOverride = providerOverride;
80
+ }
81
+ get provider() {
82
+ return this.providerOverride ?? Metadata.Provider;
83
+ }
84
+ get dbProvider() {
85
+ return this.provider;
86
+ }
87
+ /** Entry point: promote custom columns for every entity touched by the sync. */
88
+ async PromoteForSync(companyIntegrationID, syncedEntityNames) {
89
+ const integrationID = await this.resolveIntegrationID(companyIntegrationID);
90
+ if (!integrationID)
91
+ return NOT_PROMOTED;
92
+ const columnsAdded = [];
93
+ for (const entityName of syncedEntityNames) {
94
+ try {
95
+ const added = await this.promoteEntity(companyIntegrationID, integrationID, entityName);
96
+ columnsAdded.push(...added);
97
+ }
98
+ catch (err) {
99
+ // One entity's promotion failure must not abort the others, and never the sync.
100
+ LogError(`[CustomColumnPromoter] Promotion failed for entity '${entityName}': ${this.msg(err)}`);
101
+ }
102
+ }
103
+ const promoted = columnsAdded.length > 0;
104
+ if (promoted) {
105
+ // Make the freshly-created EntityFields visible in-process for the next sync's mapping.
106
+ try {
107
+ await this.provider.Refresh();
108
+ }
109
+ catch (err) {
110
+ LogError(`[CustomColumnPromoter] provider.Refresh failed: ${this.msg(err)}`);
111
+ }
112
+ }
113
+ return { Promoted: promoted, ColumnsAdded: columnsAdded, SchemaUpdatePending: promoted };
114
+ }
115
+ /** Gate → plan → promote for a single target entity. Returns the columns it added. */
116
+ async promoteEntity(companyIntegrationID, integrationID, entityName) {
117
+ const planned = await this.planWorkForEntity(companyIntegrationID, entityName);
118
+ if (!planned || planned.work.length === 0)
119
+ return []; // no overflow / no entity map / already converged
120
+ const { entityInfo, entityMap } = planned;
121
+ let work = planned.work;
122
+ // M4a: bound schema churn per pass — the remainder stays captured and promotes next sync.
123
+ if (work.length > MAX_PROMOTIONS_PER_PASS) {
124
+ LogStatus(`[CustomColumnPromoter] ${work.length} candidates on ${entityName}; promoting ${MAX_PROMOTIONS_PER_PASS} this pass, ${work.length - MAX_PROMOTIONS_PER_PASS} deferred to next sync.`);
125
+ work = work.slice(0, MAX_PROMOTIONS_PER_PASS);
126
+ }
127
+ // 4. PROMOTE: ADD COLUMN+CodeGen (only the keys that need a column) → IOF rows → field maps → spread.
128
+ const newColumns = work.filter(w => w.needsColumn);
129
+ if (newColumns.length > 0 && !await this.applySchemaChange(entityInfo, newColumns)) {
130
+ return []; // DDL failed — leave everything captured; retry next sync (no partial commit lost)
131
+ }
132
+ await this.createIntegrationObjectFields(integrationID, entityMap.ExternalObjectName, work);
133
+ await this.createFieldMaps(entityMap.ID, work.filter(w => w.needsFieldMap));
134
+ // M3: spread staged values into the real columns + re-baseline the content hash.
135
+ // CRITICAL: refresh metadata FIRST. applySchemaChange just added the columns AND had RSU
136
+ // regenerate the spUpdate sproc to include them — but the running provider's in-memory entity
137
+ // metadata still predates the column add (the Refresh at the top ran before it). Without this,
138
+ // the spread's row.Save() builds a sproc call from the STALE field list that doesn't match the
139
+ // regenerated sproc → "Error executing SQL" (the spread-save failures). Re-loading metadata
140
+ // here aligns the entity's field set with the new DB sproc so the backfill saves succeed.
141
+ try {
142
+ await this.provider.Refresh();
143
+ }
144
+ catch (err) {
145
+ LogError(`[CustomColumnPromoter] pre-spread Refresh failed: ${this.msg(err)}`);
146
+ }
147
+ const refreshedEntityInfo = this.provider.EntityByName(entityName) ?? entityInfo;
148
+ await this.spreadAndRebaseline(entityName, entityMap.ID, refreshedEntityInfo, work);
149
+ LogStatus(`[CustomColumnPromoter] Promoted/recovered ${work.length} custom column(s) on ${entityName}: ${work.map(w => w.columnName).join(', ')}`);
150
+ return work.map(w => ({ EntityName: entityName, ColumnName: w.columnName }));
151
+ }
152
+ /**
153
+ * Dry-run of GATE → scan → PLAN → resolve-work for ONE entity, WITHOUT applying any schema change.
154
+ * Shared by {@link promoteEntity} (which then PROMOTES the work) and {@link ListCandidates} (which only
155
+ * reports it). Returns null when the entity has no overflow column / no captured customs / no entity map.
156
+ * Because the work list is computed live (overflow keys minus already-column-and-mapped), re-running is
157
+ * inherently deduped — a concurrent discovery that already promoted a key yields no work item for it.
158
+ */
159
+ async planWorkForEntity(companyIntegrationID, entityName) {
160
+ const entityInfo = this.provider.EntityByName(entityName);
161
+ if (!entityInfo?.SchemaName || !entityInfo.BaseTable)
162
+ return null;
163
+ // No overflow column on this table (predates the feature) → nothing to promote.
164
+ if (!entityInfo.Fields.some(f => f.Name === CUSTOM_OVERFLOW_COLUMN))
165
+ return null;
166
+ const overflowJson = await this.scanOverflow(entityName);
167
+ if (overflowJson.length === 0)
168
+ return null; // no customs captured
169
+ const passing = planPromotions(buildOverflowStats(overflowJson), {});
170
+ if (passing.length === 0)
171
+ return null;
172
+ const entityMap = await this.findEntityMap(companyIntegrationID, entityName);
173
+ if (!entityMap) {
174
+ LogError(`[CustomColumnPromoter] No entity map for ${entityName} on CI ${companyIntegrationID}.`);
175
+ return null;
176
+ }
177
+ // Skip fully-terminated keys (column + field map both exist); keep promote (needs column) / recover.
178
+ const fieldMapSources = await this.activeFieldMapSources(entityMap.ID);
179
+ const work = this.resolveWorkItems(passing, entityInfo, fieldMapSources);
180
+ return { entityInfo, entityMap, work };
181
+ }
182
+ /**
183
+ * Lists the custom-column CANDIDATES for one entity — the "new columns found" awaiting promotion,
184
+ * computed live from the overflow column minus already-mapped/already-a-column keys (inherently deduped).
185
+ * READ-ONLY: no schema change, no RSU. Backs `IntegrationListCustomColumnCandidates`.
186
+ */
187
+ async ListCandidates(companyIntegrationID, entityName) {
188
+ const planned = await this.planWorkForEntity(companyIntegrationID, entityName);
189
+ if (!planned)
190
+ return [];
191
+ return planned.work.map(w => ({
192
+ EntityName: entityName,
193
+ SourceKey: w.sourceKey,
194
+ ColumnName: w.columnName,
195
+ InferredType: w.candidate.Inferred.SchemaFieldType,
196
+ NeedsColumn: w.needsColumn,
197
+ }));
198
+ }
199
+ /** Samples the overflow column (rows where it is non-null) — dialect-agnostic via RunView. */
200
+ async scanOverflow(entityName) {
201
+ const rv = new RunView();
202
+ const res = await rv.RunView({
203
+ EntityName: entityName,
204
+ Fields: [CUSTOM_OVERFLOW_COLUMN],
205
+ ExtraFilter: `${CUSTOM_OVERFLOW_COLUMN} IS NOT NULL`,
206
+ MaxRows: OVERFLOW_SAMPLE_SIZE,
207
+ ResultType: 'simple',
208
+ }, this.user);
209
+ if (!res.Success) {
210
+ LogError(`[CustomColumnPromoter] Overflow scan failed for ${entityName}: ${res.ErrorMessage}`);
211
+ return [];
212
+ }
213
+ return (res.Results ?? []).map(r => r[CUSTOM_OVERFLOW_COLUMN] ?? null);
214
+ }
215
+ /**
216
+ * Splits coverage-passing keys into actionable work items, dropping any that are fully
217
+ * terminated (column AND field map both exist). A key whose column exists but whose field
218
+ * map is missing becomes a RECOVERY item (covers the crash window between ADD COLUMN and the
219
+ * field-map write) — needsColumn=false, needsFieldMap=true.
220
+ */
221
+ resolveWorkItems(passing, entityInfo, fieldMapSources) {
222
+ const existingByLower = new Map(entityInfo.Fields.map(f => [f.Name.toLowerCase(), f.Name]));
223
+ const taken = new Set(entityInfo.Fields.map(f => f.Name.toLowerCase()));
224
+ const items = [];
225
+ for (const candidate of passing) {
226
+ const existingCol = existingByLower.get(sanitizeColumnName(candidate.Key).toLowerCase());
227
+ const hasColumn = !!existingCol;
228
+ const hasFieldMap = fieldMapSources.has(candidate.Key.toLowerCase());
229
+ if (hasColumn && hasFieldMap)
230
+ continue; // terminated — nothing to do
231
+ const columnName = existingCol ?? this.uniqueColumnName(sanitizeColumnName(candidate.Key), taken);
232
+ if (!hasColumn)
233
+ taken.add(columnName.toLowerCase());
234
+ items.push({ candidate, sourceKey: candidate.Key, columnName, needsColumn: !hasColumn, needsFieldMap: !hasFieldMap });
235
+ }
236
+ return items;
237
+ }
238
+ /** Active field-map SOURCE field names for an entity map (lowercased) — for the terminate/recovery check. */
239
+ async activeFieldMapSources(entityMapID) {
240
+ const rv = new RunView();
241
+ const res = await rv.RunView({
242
+ EntityName: 'MJ: Company Integration Field Maps',
243
+ ExtraFilter: `EntityMapID='${entityMapID}' AND Status='Active'`,
244
+ Fields: ['SourceFieldName'],
245
+ ResultType: 'simple',
246
+ }, this.user);
247
+ return new Set(res.Success ? (res.Results ?? []).map(r => (r.SourceFieldName ?? '').toLowerCase()) : []);
248
+ }
249
+ /** Suffixes _2, _3, … until the sanitized name does not collide with an existing/assigned one. */
250
+ uniqueColumnName(base, taken) {
251
+ if (!taken.has(base.toLowerCase()))
252
+ return base;
253
+ for (let i = 2; i < 1000; i++) {
254
+ const candidate = `${base}_${i}`;
255
+ if (!taken.has(candidate.toLowerCase()))
256
+ return candidate;
257
+ }
258
+ return `${base}_${Date.now() % 100000}`; // pathological fallback (never expected)
259
+ }
260
+ /** Generates ADD COLUMN DDL for the new columns and runs it through RSU (CodeGen reflects them). */
261
+ async applySchemaChange(entityInfo, named) {
262
+ const platform = this.dbProvider.PlatformKey;
263
+ const ddl = new DDLGenerator();
264
+ const statements = named.map(n => ddl.GenerateAlterTableAddColumn(entityInfo.SchemaName, entityInfo.BaseTable, this.toTargetColumn(n, platform), platform));
265
+ const input = {
266
+ MigrationSQL: statements.join('\n'),
267
+ Description: `Promote ${named.length} custom column(s) on ${entityInfo.Name}`,
268
+ AffectedTables: [`${entityInfo.SchemaName}.${entityInfo.BaseTable}`],
269
+ // M3 owns the restart-signal + restart; this stage applies schema only. Runtime
270
+ // promotion creates no git commit (the dev RSU flow does; a per-sync commit is noise).
271
+ SkipRestart: true,
272
+ SkipGitCommit: true,
273
+ };
274
+ const result = await RuntimeSchemaManager.Instance.RunPipeline(input);
275
+ if (!result.Success) {
276
+ LogError(`[CustomColumnPromoter] RSU ADD COLUMN failed on ${entityInfo.Name}: ${result.ErrorMessage ?? result.ErrorStep ?? 'unknown'}`);
277
+ }
278
+ return result.Success;
279
+ }
280
+ /** Builds a per-platform TargetColumnConfig from a planned candidate. */
281
+ toTargetColumn(n, platform) {
282
+ const inferred = n.candidate.Inferred;
283
+ return {
284
+ SourceFieldName: n.sourceKey,
285
+ TargetColumnName: n.columnName,
286
+ TargetSqlType: platform === 'sqlserver' ? inferred.SqlServerType : inferred.PostgresType,
287
+ IsNullable: true, // customs are always nullable (never fabricate NOT NULL)
288
+ MaxLength: inferred.MaxLength,
289
+ Precision: null,
290
+ Scale: null,
291
+ DefaultValue: null,
292
+ };
293
+ }
294
+ /** Creates an IOF row per promoted field (IsCustom, MetadataSource='Discovered'). */
295
+ async createIntegrationObjectFields(integrationID, externalObjectName, named) {
296
+ const objectID = await this.resolveIntegrationObjectID(integrationID, externalObjectName);
297
+ if (!objectID) {
298
+ LogError(`[CustomColumnPromoter] No IntegrationObject '${externalObjectName}' for integration ${integrationID}; IOF rows skipped.`);
299
+ return;
300
+ }
301
+ // Lookup-or-reactivate-or-create — NEVER blind-create over an existing field. A field the source
302
+ // dropped is DEACTIVATED (Status='Inactive'), not deleted, and its column is preserved. When that
303
+ // key reappears in the payload and reaches promotion, the IOF already exists: reactivate it (so the
304
+ // active-filtered ApplyAll re-materializes the still-present column) instead of creating a duplicate.
305
+ // This is the "removed-then-re-added" case + the recovery case (column existed, field map missing).
306
+ const existingIOFs = await this.existingIOFsByName(objectID);
307
+ for (const n of named) {
308
+ const existing = existingIOFs.get(n.sourceKey.toLowerCase());
309
+ if (existing) {
310
+ if (existing.Status !== 'Active') {
311
+ const reIof = await this.provider.GetEntityObject('MJ: Integration Object Fields', this.user);
312
+ if (await reIof.Load(existing.ID)) {
313
+ reIof.Status = 'Active';
314
+ const ok = await reIof.Save();
315
+ if (!ok)
316
+ LogError(`[CustomColumnPromoter] Failed to reactivate IOF '${n.sourceKey}': ${reIof.LatestResult?.CompleteMessage ?? 'unknown'}`);
317
+ }
318
+ }
319
+ continue;
320
+ }
321
+ const iof = await this.provider.GetEntityObject('MJ: Integration Object Fields', this.user);
322
+ iof.NewRecord();
323
+ iof.IntegrationObjectID = objectID;
324
+ iof.Name = n.sourceKey;
325
+ iof.DisplayName = n.sourceKey;
326
+ iof.Description = `Custom field discovered during sync; promoted to column ${n.columnName}.`;
327
+ iof.Type = n.candidate.Inferred.SchemaFieldType;
328
+ iof.Length = n.candidate.Inferred.MaxLength;
329
+ iof.AllowsNull = true;
330
+ iof.IsPrimaryKey = false; // never fabricated for customs (deferred to D4)
331
+ iof.IsUniqueKey = false;
332
+ iof.IsReadOnly = false;
333
+ iof.IsRequired = false;
334
+ iof.IsCustom = true;
335
+ // Provenance is STAMPED by this write path, never inferred later. 'Discovered' = the
336
+ // system found it automatically (vs 'Declared' curated / 'Custom' customer-added). The
337
+ // Configuration marker records the finer truth: it was data-sampled from a flat-file
338
+ // feed (not an authoritative describe endpoint) with this coverage, and its type was
339
+ // INFERRED — so consumers can treat it as soft / safely re-typable.
340
+ iof.MetadataSource = 'Discovered';
341
+ iof.Configuration = JSON.stringify({
342
+ promotedFrom: 'overflow',
343
+ coverage: Number(n.candidate.Coverage.toFixed(3)),
344
+ typeInferredFromData: true,
345
+ });
346
+ iof.Status = 'Active';
347
+ if (!await iof.Save()) {
348
+ LogError(`[CustomColumnPromoter] IOF save failed for ${n.sourceKey}: ${iof.LatestResult?.CompleteMessage ?? 'unknown'}`);
349
+ }
350
+ }
351
+ }
352
+ /** Creates a field map (source key → new column) so the next sync maps natively → terminate. */
353
+ async createFieldMaps(entityMapID, named) {
354
+ for (const n of named) {
355
+ const fm = await this.provider.GetEntityObject('MJ: Company Integration Field Maps', this.user);
356
+ fm.NewRecord();
357
+ fm.EntityMapID = entityMapID;
358
+ fm.SourceFieldName = n.sourceKey;
359
+ fm.DestinationFieldName = n.columnName;
360
+ fm.IsKeyField = false;
361
+ fm.IsRequired = false;
362
+ fm.Status = 'Active';
363
+ if (!await fm.Save()) {
364
+ LogError(`[CustomColumnPromoter] Field map save failed for ${n.sourceKey}: ${fm.LatestResult?.CompleteMessage ?? 'unknown'}`);
365
+ }
366
+ }
367
+ }
368
+ /**
369
+ * Spreads the staged overflow JSON values into the freshly-created real columns, then
370
+ * re-baselines the content hash (gaps.md §2 step 3). JS per-row pass — dialect-agnostic (no
371
+ * cast SQL, which is where PG bugs hide); BaseEntity handles the dialect on write. The overflow
372
+ * column is intentionally NOT cleared: once the field map exists the key is no longer "unmapped",
373
+ * so the next sync stops re-capturing it and planPromotions skips the now-existing column — the
374
+ * stale value self-heals. Bounded to rows that carry overflow, paged, once on the discovery sync.
375
+ */
376
+ async spreadAndRebaseline(entityName, entityMapID, entityInfo, named) {
377
+ const hasHashCol = entityInfo.Fields.some(f => f.Name === CONTENT_HASH_COLUMN);
378
+ const mappedDestFields = hasHashCol ? await this.activeDestinationFields(entityMapID) : [];
379
+ let startRow = 0;
380
+ const pageSize = 500;
381
+ for (;;) {
382
+ const rv = new RunView();
383
+ const res = await rv.RunView({
384
+ EntityName: entityName,
385
+ ExtraFilter: `${CUSTOM_OVERFLOW_COLUMN} IS NOT NULL`,
386
+ ResultType: 'entity_object',
387
+ MaxRows: pageSize,
388
+ StartRow: startRow,
389
+ }, this.user);
390
+ if (!res.Success) {
391
+ LogError(`[CustomColumnPromoter] Spread scan failed for ${entityName}: ${res.ErrorMessage}`);
392
+ return;
393
+ }
394
+ const rows = res.Results ?? [];
395
+ for (const row of rows) {
396
+ await this.spreadOneRow(row, named, hasHashCol, mappedDestFields);
397
+ }
398
+ if (rows.length < pageSize)
399
+ break;
400
+ startRow += pageSize;
401
+ }
402
+ }
403
+ /** Applies the staged values + re-baselined hash to a single row entity and saves it. */
404
+ async spreadOneRow(row, named, hasHashCol, mappedDestFields) {
405
+ // Dynamic .Get/.Set is REQUIRED here: these columns were created at runtime and have no
406
+ // generated typed property in this still-running process (full typed access arrives on the
407
+ // post-promotion restart). This is the sanctioned exception to the no-.Get/.Set rule.
408
+ const overflow = this.parseOverflow(row.Get(CUSTOM_OVERFLOW_COLUMN));
409
+ if (!overflow)
410
+ return;
411
+ let changed = false;
412
+ for (const n of named) {
413
+ if (Object.prototype.hasOwnProperty.call(overflow, n.sourceKey)) {
414
+ row.Set(n.columnName, overflow[n.sourceKey]);
415
+ changed = true;
416
+ }
417
+ }
418
+ if (!changed)
419
+ return;
420
+ if (hasHashCol) {
421
+ // Re-baseline to the next-sync value: hash over all active mapped destination columns
422
+ // (now incl. the new ones) as they sit on the row — matches what the next sync computes.
423
+ const mapped = {};
424
+ for (const dest of mappedDestFields)
425
+ mapped[dest] = row.Get(dest);
426
+ row.Set(CONTENT_HASH_COLUMN, computeContentHash(mapped));
427
+ }
428
+ if (!await row.Save()) {
429
+ LogError(`[CustomColumnPromoter] Spread save failed: ${row.LatestResult?.CompleteMessage ?? 'unknown'}`);
430
+ }
431
+ }
432
+ /** Active field-map destination column names for an entity map (for hash re-baseline). */
433
+ async activeDestinationFields(entityMapID) {
434
+ const rv = new RunView();
435
+ const res = await rv.RunView({
436
+ EntityName: 'MJ: Company Integration Field Maps',
437
+ ExtraFilter: `EntityMapID='${entityMapID}' AND Status='Active'`,
438
+ Fields: ['DestinationFieldName'],
439
+ ResultType: 'simple',
440
+ }, this.user);
441
+ return res.Success ? (res.Results ?? []).map(r => r.DestinationFieldName).filter(Boolean) : [];
442
+ }
443
+ parseOverflow(raw) {
444
+ if (typeof raw !== 'string' || raw.length === 0)
445
+ return null;
446
+ try {
447
+ const parsed = JSON.parse(raw);
448
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
449
+ }
450
+ catch {
451
+ return null;
452
+ }
453
+ }
454
+ /** Loads the CompanyIntegration's IntegrationID. */
455
+ async resolveIntegrationID(companyIntegrationID) {
456
+ const ci = await this.provider.GetEntityObject('MJ: Company Integrations', this.user);
457
+ const loaded = await ci.Load(companyIntegrationID);
458
+ return loaded ? ci.IntegrationID : null;
459
+ }
460
+ /** Finds the entity map (ExternalObject↔Entity) for this CI + target entity. */
461
+ async findEntityMap(companyIntegrationID, entityName) {
462
+ const rv = new RunView();
463
+ const res = await rv.RunView({
464
+ EntityName: 'MJ: Company Integration Entity Maps',
465
+ ExtraFilter: `CompanyIntegrationID='${companyIntegrationID}' AND Entity='${this.escape(entityName)}'`,
466
+ ResultType: 'simple',
467
+ MaxRows: 1,
468
+ }, this.user);
469
+ const row = res.Success ? res.Results?.[0] : undefined;
470
+ return row ? { ID: row.ID, ExternalObjectName: row.ExternalObjectName } : null;
471
+ }
472
+ /** Existing IOF field names (lowercased) for an object — for idempotent IOF creation. */
473
+ async existingIOFsByName(objectID) {
474
+ const rv = new RunView();
475
+ const res = await rv.RunView({
476
+ EntityName: 'MJ: Integration Object Fields',
477
+ ExtraFilter: `IntegrationObjectID='${objectID}'`,
478
+ Fields: ['ID', 'Name', 'Status'],
479
+ ResultType: 'simple',
480
+ }, this.user);
481
+ const map = new Map();
482
+ if (res.Success)
483
+ for (const r of res.Results ?? []) {
484
+ map.set((r.Name ?? '').toLowerCase(), { ID: String(r.ID), Status: r.Status ?? 'Active' });
485
+ }
486
+ return map;
487
+ }
488
+ /** Resolves the IntegrationObject ID for an external object name under an integration. */
489
+ async resolveIntegrationObjectID(integrationID, externalObjectName) {
490
+ const rv = new RunView();
491
+ const res = await rv.RunView({
492
+ EntityName: 'MJ: Integration Objects',
493
+ ExtraFilter: `IntegrationID='${integrationID}' AND Name='${this.escape(externalObjectName)}'`,
494
+ Fields: ['ID', 'Name', 'IntegrationID'],
495
+ ResultType: 'simple',
496
+ MaxRows: 1,
497
+ }, this.user);
498
+ const row = res.Success ? res.Results?.[0] : undefined;
499
+ return row ? row.ID : null;
500
+ }
501
+ escape(value) {
502
+ return value.replace(/'/g, "''");
503
+ }
504
+ msg(err) {
505
+ return err instanceof Error ? err.message : String(err);
506
+ }
507
+ }
508
+ //# sourceMappingURL=CustomColumnPromoter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomColumnPromoter.js","sourceRoot":"","sources":["../../src/integration/CustomColumnPromoter.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,14 @@ export declare class StartupLogger {
34
34
  private readonly resolvedLevel;
35
35
  private readonly phaseTimings;
36
36
  private readonly summary;
37
+ /** Transient boot spinner — active only at `standard` level on a TTY (see {@link spinnerEnabled}). */
38
+ private spinner;
39
+ /** Interval that ticks the live elapsed counter on the spinner line. */
40
+ private bootTimer;
41
+ /** Wall-clock start of the boot indicator, for the live elapsed counter. */
42
+ private bootStartMs;
43
+ /** Most recent phase label, shown on the spinner line. */
44
+ private currentPhase?;
37
45
  /**
38
46
  * @param level Optional explicit level override (primarily for tests).
39
47
  * When omitted, the level is resolved from config.
@@ -67,6 +75,41 @@ export declare class StartupLogger {
67
75
  * {@link EndPhase}.
68
76
  */
69
77
  StartPhase(): number;
78
+ /**
79
+ * True when the transient boot spinner should animate: only at `standard`
80
+ * level AND when stdout is an interactive TTY. In non-TTY contexts (Docker,
81
+ * systemd, piped logs) a spinner emits escape codes that pollute log
82
+ * aggregation, and at `verbose`+ the inline phase lines would fight it — so
83
+ * both fall back to plain, line-oriented output.
84
+ */
85
+ private get spinnerEnabled();
86
+ /** Prefix for the transient boot indicator (no rocket — we haven't launched yet). */
87
+ private static readonly BOOT_LABEL;
88
+ /** Composes the boot label, plus the current phase when known (no elapsed counter). */
89
+ private bootText;
90
+ /** Repaints the spinner line with the current phase and a live elapsed counter. */
91
+ private refreshBootText;
92
+ /**
93
+ * Begins the transient "Bootstrapping…" indicator, replaced by the 🚀 summary
94
+ * block once {@link PrintSummary} runs (so the rocket appears only after launch).
95
+ *
96
+ * - `standard` + TTY: an animated, self-clearing `ora` spinner (its glyph is the
97
+ * leading "working" icon; phase status is appended inline).
98
+ * - `verbose`/`debug`, or non-TTY at `standard`: a single plain line with an
99
+ * hourglass icon (can't be cleared, but inline phase logs / the final block
100
+ * carry the detail). `minimal` stays silent.
101
+ */
102
+ StartBoot(): void;
103
+ /**
104
+ * Marks the beginning of a named phase: updates the spinner status (when active)
105
+ * and returns a start timestamp to pass to {@link EndPhase}. Pure timing helper
106
+ * when the spinner is disabled — equivalent to {@link StartPhase}.
107
+ *
108
+ * @param phase Friendly label for the upcoming phase, shown after the boot label.
109
+ */
110
+ BeginPhase(phase: string): number;
111
+ /** Stops and clears the boot spinner + its elapsed-counter interval. Safe to call when inactive. */
112
+ StopBoot(): void;
70
113
  /**
71
114
  * Records the elapsed time for a phase since `startedAt`, buffering it for the
72
115
  * summary. At `verbose`+ the phase is also printed immediately (matching the
@@ -78,6 +121,8 @@ export declare class StartupLogger {
78
121
  EndPhase(label: string, startedAt: number): number;
79
122
  /** Records the server package version for the summary header. */
80
123
  SetVersion(version: string | undefined): void;
124
+ /** Records the resolved config-file path for the summary `Config` line. */
125
+ SetConfigPath(path: string | undefined): void;
81
126
  /** Records DB platform + connection + entity count for the summary `DB` line. */
82
127
  SetDatabaseInfo(platform: string, connection: string, entityCount: number): void;
83
128
  /** Adds a registered auth-provider name to the summary `Auth` line. */
@@ -106,9 +151,20 @@ export declare class StartupLogger {
106
151
  private buildHeaderLine;
107
152
  /** ` DB <platform> · <connection> · <N> entities`. */
108
153
  private buildDbLine;
154
+ /** ` Config <path>` (omitted when no config file was resolved). */
155
+ private buildConfigLine;
109
156
  /** ` Auth <providers> REST <on/off> · <N> scheduled jobs`. */
110
157
  private buildAuthLine;
111
- /** ` Startup <total>s (<phase> <x>s · <phase> <y>s …)`. */
158
+ /**
159
+ * ` Startup <total>s (<phase> <x>s · … · other <z>s)`.
160
+ *
161
+ * The headline `<total>` is the TRUE wall-clock since process start — in Node,
162
+ * `performance.now()` is measured from `performance.timeOrigin` (≈ process
163
+ * start), so it captures the entire bootstrap (module import, config load,
164
+ * ServerBootstrap, and `serve()`), not just the timed phases. The parenthetical
165
+ * lists each captured phase; `other` is the unattributed remainder (import +
166
+ * config + any untimed setup) shown only when it's non-trivial.
167
+ */
112
168
  private buildStartupLine;
113
169
  /**
114
170
  * Abbreviates a verbose phase label into a compact token for the collapsed
@@ -1 +1 @@
1
- {"version":3,"file":"StartupLogger.d.ts","sourceRoot":"","sources":["../../src/logging/StartupLogger.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AA8C1E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiB;IAC/C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IAErE;;;OAGG;gBACS,KAAK,CAAC,EAAE,cAAc;IAIlC;;OAEG;IACH,IAAW,KAAK,IAAI,cAAc,CAEjC;IAED;;;;;;OAMG;WACW,sBAAsB,IAAI,cAAc;IAQtD;;;OAGG;IACI,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO;IAInD;;;;OAIG;IACI,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAM7D;;;OAGG;IACI,UAAU,IAAI,MAAM;IAI3B;;;;;;;OAOG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAUzD,iEAAiE;IAC1D,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIpD,iFAAiF;IAC1E,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAMvF,uEAAuE;IAChE,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1C,2EAA2E;IACpE,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI7C,0EAA0E;IACnE,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIhD,0DAA0D;IACnD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIrC;;;;;;OAMG;IACI,YAAY,IAAI,IAAI;IAQ3B,iDAAiD;IACjD,OAAO,CAAC,kBAAkB;IAO1B,+DAA+D;IAC/D,OAAO,CAAC,iBAAiB;IAMzB,yEAAyE;IACzE,OAAO,CAAC,iBAAiB;IAUzB,8EAA8E;IAC9E,OAAO,CAAC,eAAe;IAKvB,+DAA+D;IAC/D,OAAO,CAAC,WAAW;IAcnB,8EAA8E;IAC9E,OAAO,CAAC,aAAa;IAerB,gEAAgE;IAChE,OAAO,CAAC,gBAAgB;IAWxB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAuBvB,4BAA4B;IAC5B,OAAO,CAAC,cAAc;CAMvB"}
1
+ {"version":3,"file":"StartupLogger.d.ts","sourceRoot":"","sources":["../../src/logging/StartupLogger.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAgD1E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiB;IAC/C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IACrE,sGAAsG;IACtG,OAAO,CAAC,OAAO,CAAoB;IACnC,wEAAwE;IACxE,OAAO,CAAC,SAAS,CAA+C;IAChE,4EAA4E;IAC5E,OAAO,CAAC,WAAW,CAAK;IACxB,0DAA0D;IAC1D,OAAO,CAAC,YAAY,CAAC,CAAS;IAE9B;;;OAGG;gBACS,KAAK,CAAC,EAAE,cAAc;IAIlC;;OAEG;IACH,IAAW,KAAK,IAAI,cAAc,CAEjC;IAED;;;;;;OAMG;WACW,sBAAsB,IAAI,cAAc;IAQtD;;;OAGG;IACI,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO;IAInD;;;;OAIG;IACI,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAM7D;;;OAGG;IACI,UAAU,IAAI,MAAM;IAI3B;;;;;;OAMG;IACH,OAAO,KAAK,cAAc,GAEzB;IAED,qFAAqF;IACrF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAyC;IAE3E,uFAAuF;IACvF,OAAO,CAAC,QAAQ;IAIhB,mFAAmF;IACnF,OAAO,CAAC,eAAe;IAOvB;;;;;;;;;OASG;IACI,SAAS,IAAI,IAAI;IAiBxB;;;;;;OAMG;IACI,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAMxC,oGAAoG;IAC7F,QAAQ,IAAI,IAAI;IAWvB;;;;;;;OAOG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAUzD,iEAAiE;IAC1D,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIpD,2EAA2E;IACpE,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIpD,iFAAiF;IAC1E,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAMvF,uEAAuE;IAChE,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1C,2EAA2E;IACpE,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI7C,0EAA0E;IACnE,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIhD,0DAA0D;IACnD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIrC;;;;;;OAMG;IACI,YAAY,IAAI,IAAI;IAW3B,iDAAiD;IACjD,OAAO,CAAC,kBAAkB;IAO1B,+DAA+D;IAC/D,OAAO,CAAC,iBAAiB;IAMzB,yEAAyE;IACzE,OAAO,CAAC,iBAAiB;IAWzB,8EAA8E;IAC9E,OAAO,CAAC,eAAe;IAKvB,+DAA+D;IAC/D,OAAO,CAAC,WAAW;IAcnB,wEAAwE;IACxE,OAAO,CAAC,eAAe;IAOvB,8EAA8E;IAC9E,OAAO,CAAC,aAAa;IAerB;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAuBvB,4BAA4B;IAC5B,OAAO,CAAC,cAAc;CAMvB"}