@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.
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +5 -1
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +15 -2
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +50 -11
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/index.d.ts +1 -0
- package/dist/agentSessions/index.d.ts.map +1 -1
- package/dist/agentSessions/index.js +5 -0
- package/dist/agentSessions/index.js.map +1 -1
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts +102 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.js +164 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.js.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts +66 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.js +86 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.js.map +1 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +4 -7
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -2
- package/dist/config.js.map +1 -1
- package/dist/generated/generated.d.ts +612 -0
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +6934 -3626
- package/dist/generated/generated.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -17
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts +108 -0
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -0
- package/dist/integration/CustomColumnPromoter.js +508 -0
- package/dist/integration/CustomColumnPromoter.js.map +1 -0
- package/dist/logging/StartupLogger.d.ts +57 -1
- package/dist/logging/StartupLogger.d.ts.map +1 -1
- package/dist/logging/StartupLogger.js +115 -6
- package/dist/logging/StartupLogger.js.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +46 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.js +192 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -0
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +101 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +629 -58
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +130 -0
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.js +580 -0
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +78 -5
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +227 -15
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +0 -7
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts +8 -0
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +8 -0
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +5 -3
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/package.json +83 -78
- package/src/__tests__/RealtimeBridgeResolver.test.ts +100 -0
- package/src/__tests__/RemoteBrowserAudioStream.test.ts +8 -1
- package/src/__tests__/RemoteBrowserGoalEngine.test.ts +144 -0
- package/src/__tests__/remoteBrowserGoalRegistry.test.ts +81 -0
- package/src/agentSessions/SessionJanitor.ts +15 -1
- package/src/agentSessions/SessionManager.ts +59 -10
- package/src/agentSessions/index.ts +5 -0
- package/src/agentSessions/remoteBrowserGoalEngine.ts +191 -0
- package/src/agentSessions/remoteBrowserGoalRegistry.ts +122 -0
- package/src/auth/initializeProviders.ts +4 -7
- package/src/config.ts +13 -2
- package/src/generated/generated.ts +2502 -198
- package/src/index.ts +38 -17
- package/src/integration/CustomColumnPromoter.ts +597 -0
- package/src/logging/StartupLogger.ts +130 -6
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +164 -0
- package/src/resolvers/IntegrationDiscoveryResolver.ts +490 -53
- package/src/resolvers/RealtimeBridgeResolver.ts +493 -0
- package/src/resolvers/RemoteBrowserActionResolver.ts +232 -17
- package/src/resolvers/RunAIAgentResolver.ts +1 -9
- package/src/resolvers/RunTemplateResolver.ts +8 -0
- 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
|
-
/**
|
|
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":"
|
|
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"}
|