@equinor/fusion-framework-vite-plugin-spa 5.2.4-next.2 → 5.2.4
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/{TelemetryAdapter-C6KyX3cq.js → TelemetryAdapter-qfXdaj7x.js} +2 -3
- package/dist/TelemetryAdapter-qfXdaj7x.js.map +1 -0
- package/dist/{create-legacy-browser-delegate-CNfcydOG.js → create-legacy-browser-delegate-BqMGP3GI.js} +926 -3
- package/dist/create-legacy-browser-delegate-BqMGP3GI.js.map +1 -0
- package/dist/{enable-dev-server-tracing-ac1hNKwa.js → enable-dev-server-tracing-B9rBXrzF.js} +2 -3
- package/dist/{enable-dev-server-tracing-ac1hNKwa.js.map → enable-dev-server-tracing-B9rBXrzF.js.map} +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/html/bootstrap.js +822 -1835
- package/dist/html/bootstrap.js.map +1 -1
- package/dist/{index-By7EVum8.js → mock-Do8FKTMU.js} +2 -3
- package/dist/mock-Do8FKTMU.js.map +1 -0
- package/dist/{module-BnhyaZvt.js → module-9J9oqYEu.js} +2155 -1468
- package/dist/module-9J9oqYEu.js.map +1 -0
- package/dist/{index-CYwi54x_.js → otlp-Dq76ZTYm.js} +6 -38
- package/dist/otlp-Dq76ZTYm.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/html/html.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +6 -6
- package/dist/TelemetryAdapter-C6KyX3cq.js.map +0 -1
- package/dist/create-legacy-browser-delegate-CNfcydOG.js.map +0 -1
- package/dist/index-By7EVum8.js.map +0 -1
- package/dist/index-CYwi54x_.js.map +0 -1
- package/dist/module-BnhyaZvt.js.map +0 -1
- package/dist/trace-api-DzxUyN-s.js +0 -927
- package/dist/trace-api-DzxUyN-s.js.map +0 -1
package/dist/html/bootstrap.js
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { B as BaseTelemetryAdapter } from '../TelemetryAdapter-
|
|
3
|
-
import '../trace-api-DzxUyN-s.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Marks request options as exporter-owned so operational instrumentation skips the export itself.
|
|
7
|
-
* The marker applies only to this request, not unrelated concurrent traffic.
|
|
8
|
-
* @param init - Options forwarded through the export transport's request pipeline.
|
|
9
|
-
* @returns A copy retaining request options with the telemetry export request marker.
|
|
10
|
-
* @template T - Request options type.
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* client.fetch('/v1/logs', markTelemetryExportRequest({ method: 'POST', body }));
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
function markTelemetryExportRequest(init) {
|
|
17
|
-
return { ...init, [telemetryExportRequest]: true };
|
|
18
|
-
}
|
|
1
|
+
import { c as createErrorClass, _ as __extends, e as errorContext, E as EMPTY_SUBSCRIPTION, S as Subscription, O as Observable, a as __values, b as arrRemove, d as __spreadArray, f as __read, i as isFunction$1, g as SafeSubscriber, h as EmptyError, o as operate, j as executeSchedule, k as innerFrom, l as createOperatorSubscriber, m as map, n as mergeMap, p as identity, q as isArrayLike, r as isScheduler, s as popScheduler, t as popNumber, u as EMPTY, v as from, w as mergeInternals, x as scanInternals, y as noop, z as popResultSelector, A as reduce, B as lastValueFrom, M as ModuleEventLevel, C as filter, D as BaseModuleProvider, F as catchError, G as of, H as object, I as _instanceof, J as _enum, K as string, L as boolean, N as record, P as union, Q as __rest, R as __assign, T as switchMap, U as observeOn, V as BaseConfigBuilder, W as TelemetryExceptionSchema, X as TelemetryItemNames, Y as TelemetryType, Z as TelemetryEventSchema, $ as TelemetryMetricSchema, a0 as TelemetryCustomEventSchema, a1 as array, a2 as TelemetryLevel, a3 as enableMSAL } from '../module-9J9oqYEu.js';
|
|
2
|
+
import { B as BaseTelemetryAdapter } from '../TelemetryAdapter-qfXdaj7x.js';
|
|
19
3
|
|
|
20
4
|
var ObjectUnsubscribedError = createErrorClass(function (_super) {
|
|
21
5
|
return function ObjectUnsubscribedErrorImpl() {
|
|
@@ -534,18 +518,6 @@ function mergeAll(concurrent) {
|
|
|
534
518
|
return mergeMap(identity, concurrent);
|
|
535
519
|
}
|
|
536
520
|
|
|
537
|
-
function concatAll() {
|
|
538
|
-
return mergeAll(1);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function concat() {
|
|
542
|
-
var args = [];
|
|
543
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
544
|
-
args[_i] = arguments[_i];
|
|
545
|
-
}
|
|
546
|
-
return concatAll()(from(args, popScheduler(args)));
|
|
547
|
-
}
|
|
548
|
-
|
|
549
521
|
var nodeEventEmitterMethods = ['addListener', 'removeListener'];
|
|
550
522
|
var eventTargetMethods = ['addEventListener', 'removeEventListener'];
|
|
551
523
|
var jqueryMethods = ['on', 'off'];
|
|
@@ -689,12 +661,6 @@ function take(count) {
|
|
|
689
661
|
});
|
|
690
662
|
}
|
|
691
663
|
|
|
692
|
-
function ignoreElements() {
|
|
693
|
-
return operate(function (source, subscriber) {
|
|
694
|
-
source.subscribe(createOperatorSubscriber(subscriber, noop));
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
|
|
698
664
|
function distinctUntilChanged(comparator, keySelector) {
|
|
699
665
|
if (keySelector === void 0) { keySelector = identity; }
|
|
700
666
|
comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare;
|
|
@@ -729,23 +695,14 @@ function defaultErrorFactory() {
|
|
|
729
695
|
return new EmptyError();
|
|
730
696
|
}
|
|
731
697
|
|
|
732
|
-
function
|
|
698
|
+
function finalize$1(callback) {
|
|
733
699
|
return operate(function (source, subscriber) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
innerSub = null;
|
|
741
|
-
isComplete && subscriber.complete();
|
|
742
|
-
});
|
|
743
|
-
innerFrom(project(outerValue, index++)).subscribe(innerSub);
|
|
744
|
-
}
|
|
745
|
-
}, function () {
|
|
746
|
-
isComplete = true;
|
|
747
|
-
!innerSub && subscriber.complete();
|
|
748
|
-
}));
|
|
700
|
+
try {
|
|
701
|
+
source.subscribe(subscriber);
|
|
702
|
+
}
|
|
703
|
+
finally {
|
|
704
|
+
subscriber.add(callback);
|
|
705
|
+
}
|
|
749
706
|
});
|
|
750
707
|
}
|
|
751
708
|
|
|
@@ -923,6 +880,42 @@ function takeWhile(predicate, inclusive) {
|
|
|
923
880
|
});
|
|
924
881
|
}
|
|
925
882
|
|
|
883
|
+
function tap(observerOrNext, error, complete) {
|
|
884
|
+
var tapObserver = isFunction$1(observerOrNext) || error || complete
|
|
885
|
+
?
|
|
886
|
+
{ next: observerOrNext, error: error, complete: complete }
|
|
887
|
+
: observerOrNext;
|
|
888
|
+
return tapObserver
|
|
889
|
+
? operate(function (source, subscriber) {
|
|
890
|
+
var _a;
|
|
891
|
+
(_a = tapObserver.subscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver);
|
|
892
|
+
var isUnsub = true;
|
|
893
|
+
source.subscribe(createOperatorSubscriber(subscriber, function (value) {
|
|
894
|
+
var _a;
|
|
895
|
+
(_a = tapObserver.next) === null || _a === void 0 ? void 0 : _a.call(tapObserver, value);
|
|
896
|
+
subscriber.next(value);
|
|
897
|
+
}, function () {
|
|
898
|
+
var _a;
|
|
899
|
+
isUnsub = false;
|
|
900
|
+
(_a = tapObserver.complete) === null || _a === void 0 ? void 0 : _a.call(tapObserver);
|
|
901
|
+
subscriber.complete();
|
|
902
|
+
}, function (err) {
|
|
903
|
+
var _a;
|
|
904
|
+
isUnsub = false;
|
|
905
|
+
(_a = tapObserver.error) === null || _a === void 0 ? void 0 : _a.call(tapObserver, err);
|
|
906
|
+
subscriber.error(err);
|
|
907
|
+
}, function () {
|
|
908
|
+
var _a, _b;
|
|
909
|
+
if (isUnsub) {
|
|
910
|
+
(_a = tapObserver.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver);
|
|
911
|
+
}
|
|
912
|
+
(_b = tapObserver.finalize) === null || _b === void 0 ? void 0 : _b.call(tapObserver);
|
|
913
|
+
}));
|
|
914
|
+
})
|
|
915
|
+
:
|
|
916
|
+
identity;
|
|
917
|
+
}
|
|
918
|
+
|
|
926
919
|
function withLatestFrom() {
|
|
927
920
|
var inputs = [];
|
|
928
921
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
@@ -984,19 +977,13 @@ const ModuleConfiguratorEventBaseName = 'ModuleConfigurator';
|
|
|
984
977
|
*/
|
|
985
978
|
const ModuleConfiguratorEventName = {
|
|
986
979
|
ModuleConfigAdded: `${ModuleConfiguratorEventBaseName}.module.configAdded`,
|
|
987
|
-
PreparedConfigReplaced: `${ModuleConfiguratorEventBaseName}.module.preparedConfigReplaced`,
|
|
988
980
|
OnConfiguredAdded: `${ModuleConfiguratorEventBaseName}.onConfigured.added`,
|
|
989
981
|
OnInitializedAdded: `${ModuleConfiguratorEventBaseName}.onInitialized.added`,
|
|
990
982
|
PluginAdded: `${ModuleConfiguratorEventBaseName}.plugin.added`,
|
|
991
983
|
InitializeConfigLoaded: `${ModuleConfiguratorEventBaseName}.config.loaded`,
|
|
992
984
|
InitializeInstanceInitialized: `${ModuleConfiguratorEventBaseName}.instance.initialized`,
|
|
993
985
|
Initialize: `${ModuleConfiguratorEventBaseName}.initialize.complete`,
|
|
994
|
-
HostInitializeStarted: `${ModuleConfiguratorEventBaseName}.host.initializeStarted`,
|
|
995
|
-
HostInitialized: `${ModuleConfiguratorEventBaseName}.host.initialized`,
|
|
996
|
-
HostInitializeError: `${ModuleConfiguratorEventBaseName}.host.initializeError`,
|
|
997
986
|
ConfiguratorCreated: `${ModuleConfiguratorEventBaseName}.configurator.created`,
|
|
998
|
-
ConfiguratorPrepared: `${ModuleConfiguratorEventBaseName}.configurator.prepared`,
|
|
999
|
-
ConfiguratorPrepareError: `${ModuleConfiguratorEventBaseName}.configurator.prepareError`,
|
|
1000
987
|
ConfiguratorFailed: `${ModuleConfiguratorEventBaseName}.configurator.failed`,
|
|
1001
988
|
ModulePostConfigured: `${ModuleConfiguratorEventBaseName}.module.postConfigured`,
|
|
1002
989
|
ModulePostConfigureError: `${ModuleConfiguratorEventBaseName}.module.postConfigureError`,
|
|
@@ -1061,10 +1048,7 @@ async function createModuleConfigs(ctx, ref) {
|
|
|
1061
1048
|
const config$ = from(modules).pipe(mergeMap(async (module) => {
|
|
1062
1049
|
const configStart = performance.now();
|
|
1063
1050
|
try {
|
|
1064
|
-
|
|
1065
|
-
const configurator = ctx.configurators?.has(module)
|
|
1066
|
-
? ctx.configurators.get(module)
|
|
1067
|
-
: await module.configure?.(ref);
|
|
1051
|
+
const configurator = await module.configure?.(ref);
|
|
1068
1052
|
const configLoadTime = Math.round(performance.now() - configStart);
|
|
1069
1053
|
registerEvent({
|
|
1070
1054
|
level: ModuleEventLevel.Debug,
|
|
@@ -1204,8 +1188,8 @@ config) {
|
|
|
1204
1188
|
* Runs the full configure lifecycle phase for a set of modules.
|
|
1205
1189
|
*
|
|
1206
1190
|
* Orchestrates three sub-steps in order:
|
|
1207
|
-
* 1. {@link createModuleConfigs} —
|
|
1208
|
-
* 2. Apply all user-registered config callbacks (`_configs`)
|
|
1191
|
+
* 1. {@link createModuleConfigs} — call each module's `configure(ref)` factory.
|
|
1192
|
+
* 2. Apply all user-registered config callbacks (`_configs`) sequentially.
|
|
1209
1193
|
* 3. {@link runPostConfigureHooks} — call `postConfigure` and `afterConfiguration` hooks.
|
|
1210
1194
|
*
|
|
1211
1195
|
* @param ctx - The configure phase context.
|
|
@@ -1225,81 +1209,6 @@ async function runConfigurePhase(ctx, ref) {
|
|
|
1225
1209
|
return config;
|
|
1226
1210
|
}
|
|
1227
1211
|
|
|
1228
|
-
/**
|
|
1229
|
-
* Rejects asynchronous preparation without leaving rejected promises unobserved.
|
|
1230
|
-
* @param result - Factory or preparation callback result.
|
|
1231
|
-
* @param moduleName - Module to identify in the preparation error.
|
|
1232
|
-
* @throws {TypeError} When preparation returned a promise or thenable.
|
|
1233
|
-
*/
|
|
1234
|
-
function assertSynchronous(result, moduleName) {
|
|
1235
|
-
// Preparation must finish before control returns to host configuration.
|
|
1236
|
-
if (result !== null &&
|
|
1237
|
-
(typeof result === 'object' || typeof result === 'function') &&
|
|
1238
|
-
'then' in result &&
|
|
1239
|
-
typeof result.then === 'function') {
|
|
1240
|
-
// Invalid async work cannot be canceled, but its eventual rejection must remain visible.
|
|
1241
|
-
void Promise.resolve(result).catch(() => console.warn(`Asynchronous preparation failed for module "${moduleName}"`));
|
|
1242
|
-
throw new TypeError(`Preparation for module "${moduleName}" must be synchronous`);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
/**
|
|
1246
|
-
* Runs synchronous preparation at the registration boundary, without exposing stored builders.
|
|
1247
|
-
* Supplied builders bypass factories; opt-in factories run once per registered descriptor.
|
|
1248
|
-
* The ordinary configure phase later receives the same object.
|
|
1249
|
-
*
|
|
1250
|
-
* Preparation does not mutate orchestrator state: it returns the builder to commit, so a
|
|
1251
|
-
* failure leaves the previous registration untouched.
|
|
1252
|
-
*
|
|
1253
|
-
* @param ctx - Registration and the host's already prepared builders.
|
|
1254
|
-
* @returns The builder to commit, or `undefined` for an ordinary, unprepared registration.
|
|
1255
|
-
* @template TModule - Registered module descriptor.
|
|
1256
|
-
* @template TConfig - Configuration callback type associated with the registration.
|
|
1257
|
-
* @throws Rethrows preparation errors after emitting a lifecycle failure event.
|
|
1258
|
-
* @internal
|
|
1259
|
-
*/
|
|
1260
|
-
function runPreparePhase(ctx) {
|
|
1261
|
-
const { registration, configurators, registerEvent } = ctx;
|
|
1262
|
-
const { module, configurator, prepare } = registration;
|
|
1263
|
-
// Ordinary registrations keep factory creation in the existing configure phase.
|
|
1264
|
-
if (configurator === undefined && !prepare) {
|
|
1265
|
-
return undefined;
|
|
1266
|
-
}
|
|
1267
|
-
try {
|
|
1268
|
-
// An explicit builder wins; otherwise reuse this descriptor's prepared builder.
|
|
1269
|
-
let builder = configurator ?? configurators.get(module);
|
|
1270
|
-
// Only opted-in, reference-independent factories may run before initialize(ref).
|
|
1271
|
-
if (builder === undefined) {
|
|
1272
|
-
// Never freeze an ordinary factory's missing parent reference by invoking it early.
|
|
1273
|
-
if (!module.prepare) {
|
|
1274
|
-
throw new TypeError(`Module "${module.name}" requires a prepare factory or an explicit configurator for preparation`);
|
|
1275
|
-
}
|
|
1276
|
-
builder = module.prepare();
|
|
1277
|
-
assertSynchronous(builder, module.name);
|
|
1278
|
-
}
|
|
1279
|
-
// Registered callbacks receive their module's builder only within this lifecycle boundary.
|
|
1280
|
-
if (prepare) {
|
|
1281
|
-
assertSynchronous(prepare(builder), module.name);
|
|
1282
|
-
}
|
|
1283
|
-
registerEvent({
|
|
1284
|
-
level: ModuleEventLevel.Debug,
|
|
1285
|
-
name: ModuleConfiguratorEventName.ConfiguratorPrepared,
|
|
1286
|
-
message: `Configuration prepared for ${module.name}`,
|
|
1287
|
-
properties: { moduleName: module.name },
|
|
1288
|
-
});
|
|
1289
|
-
return { configurator: builder };
|
|
1290
|
-
}
|
|
1291
|
-
catch (error) {
|
|
1292
|
-
registerEvent({
|
|
1293
|
-
level: ModuleEventLevel.Error,
|
|
1294
|
-
name: ModuleConfiguratorEventName.ConfiguratorPrepareError,
|
|
1295
|
-
message: `Configuration preparation failed for ${module.name}`,
|
|
1296
|
-
properties: { moduleName: module.name },
|
|
1297
|
-
error,
|
|
1298
|
-
});
|
|
1299
|
-
throw error;
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
1212
|
/**
|
|
1304
1213
|
* Creates a `requireInstance` resolver for use during module initialization.
|
|
1305
1214
|
*
|
|
@@ -1403,7 +1312,6 @@ function createRequireInstance(moduleNames, instance$, registerEvent) {
|
|
|
1403
1312
|
async function initializeModule(module, ctx) {
|
|
1404
1313
|
const { config, ref, requireInstance, hasModule, registerEvent } = ctx;
|
|
1405
1314
|
const key = module.name;
|
|
1406
|
-
const moduleInitStart = performance.now();
|
|
1407
1315
|
// Modules must expose an initialize method to participate in the initialize phase
|
|
1408
1316
|
if (!module.initialize) {
|
|
1409
1317
|
const error = new Error(`Module ${module.name} does not have initialize method`);
|
|
@@ -1417,7 +1325,6 @@ async function initializeModule(module, ctx) {
|
|
|
1417
1325
|
moduleVersion: module.version?.toString() || 'unknown',
|
|
1418
1326
|
},
|
|
1419
1327
|
error,
|
|
1420
|
-
metric: performance.now() - moduleInitStart,
|
|
1421
1328
|
});
|
|
1422
1329
|
throw error;
|
|
1423
1330
|
}
|
|
@@ -1430,26 +1337,13 @@ async function initializeModule(module, ctx) {
|
|
|
1430
1337
|
moduleVersion: module.version?.toString() || 'unknown',
|
|
1431
1338
|
},
|
|
1432
1339
|
});
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
});
|
|
1441
|
-
}
|
|
1442
|
-
catch (error) {
|
|
1443
|
-
registerEvent({
|
|
1444
|
-
level: ModuleEventLevel.Error,
|
|
1445
|
-
name: ModuleConfiguratorEventName.ModuleInitializeError,
|
|
1446
|
-
message: 'Module initialization failed',
|
|
1447
|
-
properties: { moduleName: module.name },
|
|
1448
|
-
metric: performance.now() - moduleInitStart,
|
|
1449
|
-
error,
|
|
1450
|
-
});
|
|
1451
|
-
throw error;
|
|
1452
|
-
}
|
|
1340
|
+
const moduleInitStart = performance.now();
|
|
1341
|
+
const instance = await module.initialize({
|
|
1342
|
+
ref,
|
|
1343
|
+
config: config[key],
|
|
1344
|
+
requireInstance,
|
|
1345
|
+
hasModule,
|
|
1346
|
+
});
|
|
1453
1347
|
// Warn when providers deviate from the expected base class — these modules
|
|
1454
1348
|
// may lack version tracking and standard provider interfaces.
|
|
1455
1349
|
if (!(instance instanceof BaseModuleProvider)) {
|
|
@@ -1497,8 +1391,7 @@ async function initializeModule(module, ctx) {
|
|
|
1497
1391
|
/**
|
|
1498
1392
|
* Runs the initialize lifecycle phase for all registered modules.
|
|
1499
1393
|
*
|
|
1500
|
-
* Initializes all modules concurrently
|
|
1501
|
-
* finish afterwards are passed to `onLateProvider` for cleanup. Cross-module
|
|
1394
|
+
* Initializes all modules **concurrently** using `mergeMap`. Cross-module
|
|
1502
1395
|
* dependency ordering is handled lazily through `requireInstance`, which
|
|
1503
1396
|
* waits for a peer module's `initialize` call to complete before resolving.
|
|
1504
1397
|
*
|
|
@@ -1515,11 +1408,11 @@ async function initializeModule(module, ctx) {
|
|
|
1515
1408
|
* initialize within its timeout window.
|
|
1516
1409
|
*/
|
|
1517
1410
|
async function runInitializePhase(ctx, config, ref) {
|
|
1518
|
-
const { modules, registerEvent
|
|
1411
|
+
const { modules, registerEvent } = ctx;
|
|
1519
1412
|
// Fast-path: no modules registered — return a sealed empty instance immediately
|
|
1520
1413
|
// to avoid a subscribe/lastValueFrom race on an already-completed observable.
|
|
1521
1414
|
if (modules.length === 0) {
|
|
1522
|
-
return Object.seal(
|
|
1415
|
+
return Object.seal({});
|
|
1523
1416
|
}
|
|
1524
1417
|
// Extract module names for dependency lookups before any module has initialized
|
|
1525
1418
|
const moduleNames = modules.map((m) => m.name);
|
|
@@ -1530,56 +1423,55 @@ async function runInitializePhase(ctx, config, ref) {
|
|
|
1530
1423
|
const requireInstance = createRequireInstance(moduleNames, instance$, registerEvent);
|
|
1531
1424
|
// Initialize all modules concurrently; modules that depend on peers will
|
|
1532
1425
|
// suspend inside requireInstance until the dependency resolves.
|
|
1426
|
+
const init$ = from(modules).pipe(mergeMap((module) => initializeModule(module, { config, ref, requireInstance, hasModule, registerEvent })));
|
|
1533
1427
|
const initStart = performance.now();
|
|
1534
|
-
|
|
1535
|
-
//
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
level: ModuleEventLevel.Debug,
|
|
1566
|
-
name: ModuleConfiguratorEventName.ModuleInitializeComplete,
|
|
1567
|
-
message: 'All modules initialized',
|
|
1568
|
-
properties: { modules: Object.keys(instances).join(', '), loadTime: initTime },
|
|
1569
|
-
metric: initTime,
|
|
1428
|
+
// Accumulate module providers into the shared instance subject as each resolves.
|
|
1429
|
+
// Completing the subject signals that all modules are initialized.
|
|
1430
|
+
init$.subscribe({
|
|
1431
|
+
next: ([name, module]) => {
|
|
1432
|
+
// Merge the newly initialized module into the shared instance map without dropping peers
|
|
1433
|
+
const nextInstance = Object.assign(instance$.value, { [name]: module });
|
|
1434
|
+
instance$.next(nextInstance);
|
|
1435
|
+
},
|
|
1436
|
+
error: (err) => {
|
|
1437
|
+
registerEvent({
|
|
1438
|
+
level: ModuleEventLevel.Error,
|
|
1439
|
+
name: ModuleConfiguratorEventName.ModuleInitializeError,
|
|
1440
|
+
message: `Failed to initialize module ${err.name || 'unknown'}`,
|
|
1441
|
+
error: err,
|
|
1442
|
+
});
|
|
1443
|
+
instance$.error(err);
|
|
1444
|
+
},
|
|
1445
|
+
complete: () => {
|
|
1446
|
+
const loadTime = Math.round(performance.now() - initStart);
|
|
1447
|
+
registerEvent({
|
|
1448
|
+
level: ModuleEventLevel.Debug,
|
|
1449
|
+
name: ModuleConfiguratorEventName.ModuleInitializeComplete,
|
|
1450
|
+
message: `All modules initialized in ${loadTime}ms`,
|
|
1451
|
+
properties: {
|
|
1452
|
+
modules: Object.keys(instance$.value).join(', '),
|
|
1453
|
+
loadTime,
|
|
1454
|
+
},
|
|
1455
|
+
metric: loadTime,
|
|
1456
|
+
});
|
|
1457
|
+
instance$.complete();
|
|
1458
|
+
},
|
|
1570
1459
|
});
|
|
1460
|
+
const instanceInitStart = performance.now();
|
|
1461
|
+
const instance = await lastValueFrom(instance$);
|
|
1462
|
+
const initTime = Math.round(performance.now() - instanceInitStart);
|
|
1571
1463
|
registerEvent({
|
|
1572
1464
|
level: ModuleEventLevel.Debug,
|
|
1573
1465
|
name: ModuleConfiguratorEventName.InitializeComplete,
|
|
1574
1466
|
message: `Modules instance created in ${initTime}ms`,
|
|
1575
1467
|
properties: {
|
|
1576
|
-
modules: Object.keys(
|
|
1468
|
+
modules: Object.keys(instance).join(', '),
|
|
1577
1469
|
initTime,
|
|
1578
1470
|
},
|
|
1579
1471
|
metric: initTime,
|
|
1580
1472
|
});
|
|
1581
|
-
Object.seal(
|
|
1582
|
-
return
|
|
1473
|
+
Object.seal(instance);
|
|
1474
|
+
return instance;
|
|
1583
1475
|
}
|
|
1584
1476
|
|
|
1585
1477
|
// biome-ignore-all lint/suspicious/noExplicitAny: internal type-erased dispatch — the post-initialize phase forwards opaque instances without inspecting their concrete shapes
|
|
@@ -1818,8 +1710,6 @@ async function runPluginPhase(ctx, modules, ref) {
|
|
|
1818
1710
|
});
|
|
1819
1711
|
}
|
|
1820
1712
|
|
|
1821
|
-
/** Fixed teardown order: ordinary modules first, so diagnostics and events can still record them. */
|
|
1822
|
-
const disposePhaseOrder = ['default', 'diagnostics', 'events'];
|
|
1823
1713
|
function getPluginTeardownName(teardown) {
|
|
1824
1714
|
return typeof teardown === 'function' ? teardown.name || 'anonymous' : 'dispose';
|
|
1825
1715
|
}
|
|
@@ -1834,9 +1724,9 @@ async function runPluginTeardown(teardown) {
|
|
|
1834
1724
|
/**
|
|
1835
1725
|
* Runs the dispose lifecycle phase for all registered modules.
|
|
1836
1726
|
*
|
|
1837
|
-
* Calls module `dispose`
|
|
1838
|
-
*
|
|
1839
|
-
*
|
|
1727
|
+
* Calls each module's `dispose` hook (if defined) concurrently via
|
|
1728
|
+
* `Promise.allSettled` so one failing module cannot block others from
|
|
1729
|
+
* being torn down. After all hooks settle, the event stream is completed.
|
|
1840
1730
|
*
|
|
1841
1731
|
* @param ctx - The dispose phase context.
|
|
1842
1732
|
* @param instance - The initialized module instance map to tear down.
|
|
@@ -1846,8 +1736,6 @@ async function runPluginTeardown(teardown) {
|
|
|
1846
1736
|
*/
|
|
1847
1737
|
async function runDisposePhase(ctx, instance, ref) {
|
|
1848
1738
|
const { modules, registerEvent, event$, pluginTeardowns = [] } = ctx;
|
|
1849
|
-
const start = performance.now();
|
|
1850
|
-
let failed = false;
|
|
1851
1739
|
registerEvent({
|
|
1852
1740
|
level: ModuleEventLevel.Debug,
|
|
1853
1741
|
name: ModuleConfiguratorEventName.Dispose,
|
|
@@ -1876,7 +1764,6 @@ async function runDisposePhase(ctx, instance, ref) {
|
|
|
1876
1764
|
});
|
|
1877
1765
|
}
|
|
1878
1766
|
catch (err) {
|
|
1879
|
-
failed = true;
|
|
1880
1767
|
registerEvent({
|
|
1881
1768
|
level: ModuleEventLevel.Warning,
|
|
1882
1769
|
name: ModuleConfiguratorEventName.PluginDisposeError,
|
|
@@ -1893,98 +1780,65 @@ async function runDisposePhase(ctx, instance, ref) {
|
|
|
1893
1780
|
message: 'Disposing modules',
|
|
1894
1781
|
properties: { count: modules.length },
|
|
1895
1782
|
});
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1783
|
+
// Dispose all modules concurrently; failures are isolated per module so
|
|
1784
|
+
// one bad teardown cannot leave other modules in an inconsistent state.
|
|
1785
|
+
await Promise.allSettled(modules
|
|
1786
|
+
// Only process modules that define a dispose hook
|
|
1787
|
+
.filter((module) => !!module.dispose)
|
|
1788
|
+
// Dispose each module that defines the hook
|
|
1789
|
+
.map(async (module) => {
|
|
1790
|
+
// Narrow the type for TypeScript; the filter above already guarantees this
|
|
1791
|
+
if (!module.dispose)
|
|
1792
|
+
return;
|
|
1793
|
+
try {
|
|
1794
|
+
await module.dispose({
|
|
1795
|
+
ref,
|
|
1796
|
+
modules: instance,
|
|
1797
|
+
instance: instance[module.name],
|
|
1798
|
+
});
|
|
1910
1799
|
registerEvent({
|
|
1911
|
-
level: ModuleEventLevel.
|
|
1912
|
-
name: ModuleConfiguratorEventName.
|
|
1913
|
-
message: `
|
|
1914
|
-
properties: {
|
|
1915
|
-
|
|
1800
|
+
level: ModuleEventLevel.Debug,
|
|
1801
|
+
name: ModuleConfiguratorEventName.ModuleDisposed,
|
|
1802
|
+
message: `Module ${module.name} disposed successfully`,
|
|
1803
|
+
properties: {
|
|
1804
|
+
moduleName: module.name,
|
|
1805
|
+
moduleVersion: module.version?.toString() || 'unknown',
|
|
1806
|
+
},
|
|
1916
1807
|
});
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
}
|
|
1920
|
-
// Diagnostic infrastructure stays alive through ordinary module teardown.
|
|
1921
|
-
for (const phase of disposePhaseOrder) {
|
|
1922
|
-
await Promise.allSettled(groups[phase]
|
|
1923
|
-
// Only process modules that define a dispose hook
|
|
1924
|
-
.filter((module) => !!module.dispose)
|
|
1925
|
-
// Dispose each module that defines the hook
|
|
1926
|
-
.map(async (module) => {
|
|
1927
|
-
// Narrow the type for TypeScript; the filter above already guarantees this
|
|
1928
|
-
if (!module.dispose)
|
|
1929
|
-
return;
|
|
1930
|
-
const moduleStart = performance.now();
|
|
1931
|
-
try {
|
|
1932
|
-
await module.dispose({
|
|
1933
|
-
ref,
|
|
1934
|
-
modules: instance,
|
|
1935
|
-
instance: instance[module.name],
|
|
1936
|
-
});
|
|
1937
|
-
registerEvent({
|
|
1938
|
-
level: ModuleEventLevel.Debug,
|
|
1939
|
-
name: ModuleConfiguratorEventName.ModuleDisposed,
|
|
1940
|
-
metric: performance.now() - moduleStart,
|
|
1941
|
-
message: `Module ${module.name} disposed successfully`,
|
|
1942
|
-
properties: {
|
|
1943
|
-
moduleName: module.name,
|
|
1944
|
-
moduleVersion: module.version?.toString() || 'unknown',
|
|
1945
|
-
},
|
|
1946
|
-
});
|
|
1947
|
-
}
|
|
1948
|
-
catch (err) {
|
|
1949
|
-
failed = true;
|
|
1950
|
-
registerEvent({
|
|
1951
|
-
level: ModuleEventLevel.Warning,
|
|
1952
|
-
name: ModuleConfiguratorEventName.ModuleDisposeError,
|
|
1953
|
-
metric: performance.now() - moduleStart,
|
|
1954
|
-
message: `Module ${module.name} dispose failed`,
|
|
1955
|
-
properties: {
|
|
1956
|
-
moduleName: module.name,
|
|
1957
|
-
moduleVersion: module.version?.toString() || 'unknown',
|
|
1958
|
-
},
|
|
1959
|
-
error: err,
|
|
1960
|
-
});
|
|
1961
|
-
}
|
|
1962
|
-
}));
|
|
1963
|
-
// Emit exactly one host outcome before flushing sinks, including when no sinks exist.
|
|
1964
|
-
if (phase === 'default') {
|
|
1808
|
+
}
|
|
1809
|
+
catch (err) {
|
|
1965
1810
|
registerEvent({
|
|
1966
|
-
level:
|
|
1967
|
-
name: ModuleConfiguratorEventName.
|
|
1968
|
-
message:
|
|
1969
|
-
|
|
1970
|
-
|
|
1811
|
+
level: ModuleEventLevel.Warning,
|
|
1812
|
+
name: ModuleConfiguratorEventName.ModuleDisposeError,
|
|
1813
|
+
message: `Module ${module.name} dispose failed`,
|
|
1814
|
+
properties: {
|
|
1815
|
+
moduleName: module.name,
|
|
1816
|
+
moduleVersion: module.version?.toString() || 'unknown',
|
|
1817
|
+
},
|
|
1818
|
+
error: err,
|
|
1971
1819
|
});
|
|
1972
1820
|
}
|
|
1973
|
-
}
|
|
1821
|
+
}));
|
|
1822
|
+
registerEvent({
|
|
1823
|
+
level: ModuleEventLevel.Debug,
|
|
1824
|
+
name: ModuleConfiguratorEventName.ModulesDisposed,
|
|
1825
|
+
message: 'Module dispose complete',
|
|
1826
|
+
properties: { count: modules.length },
|
|
1827
|
+
});
|
|
1974
1828
|
// Complete the event stream last so all dispose events are captured before
|
|
1975
1829
|
// any subscriber teardown triggered by completion.
|
|
1976
1830
|
event$.complete();
|
|
1977
1831
|
}
|
|
1978
1832
|
|
|
1979
1833
|
// Generated by genversion.
|
|
1980
|
-
const version$4 = '6.
|
|
1834
|
+
const version$4 = '6.1.6';
|
|
1981
1835
|
|
|
1982
1836
|
// biome-ignore-all lint/suspicious/noExplicitAny: internal type-erased dispatch arrays — callbacks are registered with concrete module types but stored erased; the orchestrator never inspects these shapes itself
|
|
1983
1837
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1984
1838
|
/**
|
|
1985
1839
|
* Core orchestrator that drives the module lifecycle in Fusion Framework.
|
|
1986
1840
|
*
|
|
1987
|
-
* `ModulesConfigurator` manages the full **
|
|
1841
|
+
* `ModulesConfigurator` manages the full **configure → post-configure → initialize →
|
|
1988
1842
|
* post-initialize → plugin → dispose** pipeline for a set of modules. Consumers
|
|
1989
1843
|
* register modules via {@link addConfig} or {@link configure}, then call
|
|
1990
1844
|
* {@link initialize} to produce a sealed {@link ModulesInstance} whose properties
|
|
@@ -1994,7 +1848,6 @@ const version$4 = '6.2.0-next.0';
|
|
|
1994
1848
|
*
|
|
1995
1849
|
* | # | Phase | Entry point | Description |
|
|
1996
1850
|
* |---|-------|-------------|-------------|
|
|
1997
|
-
* | 0 | **Prepare** | `addConfig` | Opt-in synchronous preparation runs during registration, before startup callbacks can fail. |
|
|
1998
1851
|
* | 1 | **Configure** | `_configure` | Each module's `configure()` factory creates a config builder; registered callbacks mutate it. |
|
|
1999
1852
|
* | 2 | **Post-configure** | `_postConfigure` (inside `_configure`) | `postConfigure()` hooks and `onConfigured` callbacks run. |
|
|
2000
1853
|
* | 3 | **Initialize** | `_initialize` | Modules are initialized concurrently; cross-module dependencies are resolved through `requireInstance`. |
|
|
@@ -2046,10 +1899,6 @@ class ModulesConfigurator {
|
|
|
2046
1899
|
// configuration before telemetry is wired up.
|
|
2047
1900
|
// Memory bound: ~24 KB at ~240 bytes/event × 100 events.
|
|
2048
1901
|
#event$ = new ReplaySubject(100);
|
|
2049
|
-
/** Shared disposal per initialized instance, so repeated calls join instead of re-running hooks. */
|
|
2050
|
-
#disposals = new WeakMap();
|
|
2051
|
-
/** Builders supplied or created during preparation, keyed by their exact module descriptor. */
|
|
2052
|
-
#configurators = new Map();
|
|
2053
1902
|
/**
|
|
2054
1903
|
* Stream of lifecycle events emitted while modules are configured, initialized,
|
|
2055
1904
|
* and disposed.
|
|
@@ -2100,14 +1949,10 @@ class ModulesConfigurator {
|
|
|
2100
1949
|
* Teardown callbacks returned by registered plugins.
|
|
2101
1950
|
*
|
|
2102
1951
|
* Consumed during dispose and cleared after execution so repeated dispose calls
|
|
2103
|
-
* do not run plugin cleanup more than once.
|
|
2104
|
-
* instance; this list only collects those from `_registerPlugins` overrides that do not
|
|
2105
|
-
* forward the attempt's list.
|
|
1952
|
+
* do not run plugin cleanup more than once.
|
|
2106
1953
|
* @protected
|
|
2107
1954
|
*/
|
|
2108
1955
|
_pluginTeardowns = [];
|
|
2109
|
-
/** Plugin teardowns owned by each initialized instance, so disposing one never tears down another. */
|
|
2110
|
-
#instanceTeardowns = new WeakMap();
|
|
2111
1956
|
/**
|
|
2112
1957
|
* Set of all registered module descriptors.
|
|
2113
1958
|
* Uses a `Set` for automatic deduplication — the same module registered twice
|
|
@@ -2180,22 +2025,14 @@ class ModulesConfigurator {
|
|
|
2180
2025
|
*
|
|
2181
2026
|
* Adds the module to the known module set and registers the optional
|
|
2182
2027
|
* `configure`, `afterConfig`, and `afterInit` callbacks into their
|
|
2183
|
-
* respective lifecycle phase arrays.
|
|
2184
|
-
* synchronously during registration against the same builder.
|
|
2028
|
+
* respective lifecycle phase arrays.
|
|
2185
2029
|
*
|
|
2186
2030
|
* @param config - The module configurator descriptor to register.
|
|
2187
2031
|
* @template T - The module type being registered.
|
|
2188
2032
|
* @template TConfig - The resolved configuration type for the module.
|
|
2189
|
-
* @throws Rethrows preparation failures; async preparation and unprepared ordinary factories are rejected.
|
|
2190
2033
|
*/
|
|
2191
2034
|
addConfig(config) {
|
|
2192
2035
|
const { module, afterConfig, afterInit, configure } = config;
|
|
2193
|
-
// Prepare before touching registration state, so a failed preparation changes nothing.
|
|
2194
|
-
const prepared = runPreparePhase({
|
|
2195
|
-
registration: config,
|
|
2196
|
-
configurators: this.#configurators,
|
|
2197
|
-
registerEvent: (event) => this._registerEvent(event),
|
|
2198
|
-
});
|
|
2199
2036
|
// Find an existing descriptor so re-registering a name can replace all of its lifecycle hooks.
|
|
2200
2037
|
const existingModule = Array.from(this._modules).find((m) => m.name === module.name);
|
|
2201
2038
|
// Only handle replacement/dedupe when a descriptor is already registered under this name.
|
|
@@ -2206,19 +2043,7 @@ class ModulesConfigurator {
|
|
|
2206
2043
|
// all share one module singleton) must stay additive so multiple calls can register
|
|
2207
2044
|
// multiple named clients without clobbering each other.
|
|
2208
2045
|
if (existingModule !== module) {
|
|
2209
|
-
// A host-prepared builder (for example telemetry identity and startup capture) is lost
|
|
2210
|
-
// with the old descriptor; duplicate package copies make this easy to miss, so say so.
|
|
2211
|
-
if (this.#configurators.has(existingModule)) {
|
|
2212
|
-
console.warn(`Prepared configuration for module "${module.name}" was replaced`);
|
|
2213
|
-
this._registerEvent({
|
|
2214
|
-
level: ModuleEventLevel.Warning,
|
|
2215
|
-
name: ModuleConfiguratorEventName.PreparedConfigReplaced,
|
|
2216
|
-
message: `Prepared configuration for module ${module.name} was replaced by a different descriptor`,
|
|
2217
|
-
properties: { moduleName: module.name },
|
|
2218
|
-
});
|
|
2219
|
-
}
|
|
2220
2046
|
this._removeModuleCallbacks(module.name);
|
|
2221
|
-
this.#configurators.delete(existingModule);
|
|
2222
2047
|
const modules = Array.from(this._modules)
|
|
2223
2048
|
// Preserve every descriptor while substituting the newly registered module.
|
|
2224
2049
|
.map((m) => (m.name === module.name ? module : m));
|
|
@@ -2228,10 +2053,6 @@ class ModulesConfigurator {
|
|
|
2228
2053
|
else {
|
|
2229
2054
|
this._modules.add(module);
|
|
2230
2055
|
}
|
|
2231
|
-
// Commit the prepared builder only once the registration itself is in place.
|
|
2232
|
-
if (prepared) {
|
|
2233
|
-
this.#configurators.set(module, prepared.configurator);
|
|
2234
|
-
}
|
|
2235
2056
|
this._registerEvent({
|
|
2236
2057
|
level: ModuleEventLevel.Debug,
|
|
2237
2058
|
name: ModuleConfiguratorEventName.ModuleConfigAdded,
|
|
@@ -2354,64 +2175,8 @@ class ModulesConfigurator {
|
|
|
2354
2175
|
* @returns A promise resolving to the sealed, initialized module instance.
|
|
2355
2176
|
* @template T - Additional modules to merge into the instance type.
|
|
2356
2177
|
* @template R - The reference type, narrowed to `TRef`.
|
|
2357
|
-
* @throws Rethrows the original configuration or initialization error after partial cleanup.
|
|
2358
2178
|
*/
|
|
2359
2179
|
async initialize(ref) {
|
|
2360
|
-
const start = performance.now();
|
|
2361
|
-
const instances = {};
|
|
2362
|
-
// Plugin cleanup belongs to this attempt, so rollback never drains another instance's.
|
|
2363
|
-
const teardowns = [];
|
|
2364
|
-
this._registerEvent({
|
|
2365
|
-
level: ModuleEventLevel.Debug,
|
|
2366
|
-
name: ModuleConfiguratorEventName.HostInitializeStarted,
|
|
2367
|
-
message: 'Starting host initialization',
|
|
2368
|
-
});
|
|
2369
|
-
try {
|
|
2370
|
-
const instance = await this._initializeModules(instances, teardowns, ref);
|
|
2371
|
-
this._registerEvent({
|
|
2372
|
-
level: ModuleEventLevel.Information,
|
|
2373
|
-
name: ModuleConfiguratorEventName.HostInitialized,
|
|
2374
|
-
message: 'Host initialized',
|
|
2375
|
-
metric: performance.now() - start,
|
|
2376
|
-
});
|
|
2377
|
-
return instance;
|
|
2378
|
-
}
|
|
2379
|
-
catch (error) {
|
|
2380
|
-
this._registerEvent({
|
|
2381
|
-
level: ModuleEventLevel.Error,
|
|
2382
|
-
name: ModuleConfiguratorEventName.HostInitializeError,
|
|
2383
|
-
message: 'Host initialization failed',
|
|
2384
|
-
metric: performance.now() - start,
|
|
2385
|
-
error,
|
|
2386
|
-
});
|
|
2387
|
-
// Rollback owns the same provider map as this attempt, even when startup cannot return it.
|
|
2388
|
-
try {
|
|
2389
|
-
// Never call disposal hooks for modules that did not produce a provider.
|
|
2390
|
-
const initializedModules = this.modules.filter((module) => Object.hasOwn(instances, module.name));
|
|
2391
|
-
await runDisposePhase({
|
|
2392
|
-
modules: initializedModules,
|
|
2393
|
-
registerEvent: this._registerEvent.bind(this),
|
|
2394
|
-
event$: this.#event$,
|
|
2395
|
-
pluginTeardowns: teardowns,
|
|
2396
|
-
}, instances, ref);
|
|
2397
|
-
}
|
|
2398
|
-
catch {
|
|
2399
|
-
// Cleanup health is secondary; callers must receive the original startup rejection.
|
|
2400
|
-
console.warn('Module initialization rollback failed');
|
|
2401
|
-
}
|
|
2402
|
-
throw error;
|
|
2403
|
-
}
|
|
2404
|
-
}
|
|
2405
|
-
/**
|
|
2406
|
-
* Runs initialization inside the host diagnostic boundary.
|
|
2407
|
-
* @param instances - Attempt-owned provider map shared by initialization and rollback.
|
|
2408
|
-
* @param teardowns - Attempt-owned plugin teardowns shared by rollback and disposal.
|
|
2409
|
-
* @param ref - Reference forwarded to module lifecycle hooks.
|
|
2410
|
-
* @returns The sealed module map.
|
|
2411
|
-
* @template T - Additional modules in the resulting instance.
|
|
2412
|
-
* @template R - Reference type accepted by this host.
|
|
2413
|
-
*/
|
|
2414
|
-
async _initializeModules(instances, teardowns, ref) {
|
|
2415
2180
|
const configStart = performance.now();
|
|
2416
2181
|
const config = await this._configure(ref);
|
|
2417
2182
|
const configLoadTime = Math.round(performance.now() - configStart);
|
|
@@ -2430,7 +2195,7 @@ class ModulesConfigurator {
|
|
|
2430
2195
|
metric: configLoadTime,
|
|
2431
2196
|
});
|
|
2432
2197
|
const instanceStart = performance.now();
|
|
2433
|
-
const instance = await this._initialize(config, ref
|
|
2198
|
+
const instance = await this._initialize(config, ref);
|
|
2434
2199
|
const instanceLoadTime = Math.round(performance.now() - instanceStart);
|
|
2435
2200
|
// Build a comma-separated module name list for telemetry properties
|
|
2436
2201
|
// Extract just the module names before joining into a display string
|
|
@@ -2465,13 +2230,10 @@ class ModulesConfigurator {
|
|
|
2465
2230
|
await this._postInitialize(instance, ref);
|
|
2466
2231
|
// `instance` is the freshly built module instance record, which is structurally compatible
|
|
2467
2232
|
// with `ModulesInstance<TModules>` but not nominally assignable across the generic params.
|
|
2468
|
-
// The shorthand disposes the returned map itself, so it shares disposal with
|
|
2469
|
-
// `configurator.dispose(modules)` and teardown never runs twice or concurrently.
|
|
2470
2233
|
const modules = Object.seal(Object.assign({}, instance, {
|
|
2471
|
-
dispose: () => this.dispose(
|
|
2234
|
+
dispose: () => this.dispose(instance),
|
|
2472
2235
|
}));
|
|
2473
|
-
this
|
|
2474
|
-
await this._registerPlugins(modules, ref, teardowns);
|
|
2236
|
+
await this._registerPlugins(modules, ref);
|
|
2475
2237
|
return modules;
|
|
2476
2238
|
}
|
|
2477
2239
|
/**
|
|
@@ -2509,7 +2271,6 @@ class ModulesConfigurator {
|
|
|
2509
2271
|
async _configure(ref) {
|
|
2510
2272
|
return runConfigurePhase({
|
|
2511
2273
|
modules: this.modules,
|
|
2512
|
-
configurators: this.#configurators,
|
|
2513
2274
|
configs: this._configs,
|
|
2514
2275
|
afterConfiguration: this._afterConfiguration,
|
|
2515
2276
|
afterInit: this._afterInit,
|
|
@@ -2526,39 +2287,17 @@ class ModulesConfigurator {
|
|
|
2526
2287
|
*
|
|
2527
2288
|
* @param config - The merged module config map from the configure phase.
|
|
2528
2289
|
* @param ref - Optional reference forwarded to each module's `initialize` call.
|
|
2529
|
-
* @param instances - Attempt-owned provider map; forward to `super` when overriding to preserve rollback ownership.
|
|
2530
2290
|
* @returns A promise resolving to the sealed map of initialized module providers.
|
|
2531
2291
|
* @template T - Additional modules to merge into the instance type.
|
|
2532
2292
|
* @template R - The reference type. Defaults to `TRef`.
|
|
2533
2293
|
* @protected
|
|
2534
2294
|
*/
|
|
2535
|
-
async _initialize(config, ref
|
|
2295
|
+
async _initialize(config, ref) {
|
|
2536
2296
|
return runInitializePhase({
|
|
2537
2297
|
modules: this.modules,
|
|
2538
2298
|
registerEvent: this._registerEvent.bind(this),
|
|
2539
|
-
instances,
|
|
2540
|
-
onLateProvider: (module, provider) => {
|
|
2541
|
-
void ModulesConfigurator.#disposeLateProvider(module, provider, ref);
|
|
2542
|
-
},
|
|
2543
2299
|
}, config, ref);
|
|
2544
2300
|
}
|
|
2545
|
-
/**
|
|
2546
|
-
* Disposes a provider that finished initializing after startup already rejected.
|
|
2547
|
-
* The rollback has completed the event stream by then, so failures are only logged.
|
|
2548
|
-
* @param module - Module whose initializer resolved after the rejection.
|
|
2549
|
-
* @param provider - The late provider to dispose.
|
|
2550
|
-
* @param ref - Reference forwarded to the module dispose hook.
|
|
2551
|
-
* @returns A promise resolving once the dispose hook has settled.
|
|
2552
|
-
*/
|
|
2553
|
-
static async #disposeLateProvider(module, provider, ref) {
|
|
2554
|
-
try {
|
|
2555
|
-
await module.dispose?.({ ref, modules: { [module.name]: provider }, instance: provider });
|
|
2556
|
-
}
|
|
2557
|
-
catch {
|
|
2558
|
-
// Startup already rejected with the original error; a late cleanup failure must not surface.
|
|
2559
|
-
console.warn('Module initialization rollback failed');
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
2301
|
/**
|
|
2563
2302
|
* Runs the post-initialize lifecycle phase.
|
|
2564
2303
|
*
|
|
@@ -2590,16 +2329,14 @@ class ModulesConfigurator {
|
|
|
2590
2329
|
*
|
|
2591
2330
|
* @param instance - The sealed module instance from the initialize phase.
|
|
2592
2331
|
* @param ref - Optional reference forwarded to each plugin callback.
|
|
2593
|
-
* @param teardowns - Attempt-owned teardown list; forward to `super` when overriding so
|
|
2594
|
-
* disposing this instance runs only its own plugin cleanup.
|
|
2595
2332
|
* @template T - Additional modules to merge into the instance type.
|
|
2596
2333
|
* @template R - The reference type, narrowed to `TRef`.
|
|
2597
2334
|
* @protected
|
|
2598
2335
|
*/
|
|
2599
|
-
async _registerPlugins(instance, ref
|
|
2336
|
+
async _registerPlugins(instance, ref) {
|
|
2600
2337
|
return runPluginPhase({
|
|
2601
2338
|
plugins: this._plugins,
|
|
2602
|
-
teardowns,
|
|
2339
|
+
teardowns: this._pluginTeardowns,
|
|
2603
2340
|
registerEvent: this._registerEvent.bind(this),
|
|
2604
2341
|
}, instance, ref);
|
|
2605
2342
|
}
|
|
@@ -2613,26 +2350,14 @@ class ModulesConfigurator {
|
|
|
2613
2350
|
* @param ref - Optional reference forwarded to module dispose hooks.
|
|
2614
2351
|
* @returns A promise resolving when all modules have been disposed.
|
|
2615
2352
|
*/
|
|
2616
|
-
dispose(instance, ref) {
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
...this._pluginTeardowns.splice(0),
|
|
2625
|
-
];
|
|
2626
|
-
disposal = runDisposePhase({
|
|
2627
|
-
modules: this.modules,
|
|
2628
|
-
registerEvent: this._registerEvent.bind(this),
|
|
2629
|
-
// ReplaySubject extends Subject — dispose only needs .complete() which both have.
|
|
2630
|
-
event$: this.#event$,
|
|
2631
|
-
pluginTeardowns,
|
|
2632
|
-
}, instance, ref);
|
|
2633
|
-
this.#disposals.set(instance, disposal);
|
|
2634
|
-
}
|
|
2635
|
-
return disposal;
|
|
2353
|
+
async dispose(instance, ref) {
|
|
2354
|
+
return runDisposePhase({
|
|
2355
|
+
modules: this.modules,
|
|
2356
|
+
registerEvent: this._registerEvent.bind(this),
|
|
2357
|
+
// ReplaySubject extends Subject — dispose only needs .complete() which both have.
|
|
2358
|
+
event$: this.#event$,
|
|
2359
|
+
pluginTeardowns: this._pluginTeardowns,
|
|
2360
|
+
}, instance, ref);
|
|
2636
2361
|
}
|
|
2637
2362
|
}
|
|
2638
2363
|
|
|
@@ -3269,7 +2994,7 @@ class HttpClientConfigurator {
|
|
|
3269
2994
|
}
|
|
3270
2995
|
|
|
3271
2996
|
// Generated by genversion.
|
|
3272
|
-
const version$3 = '8.1.
|
|
2997
|
+
const version$3 = '8.1.4';
|
|
3273
2998
|
|
|
3274
2999
|
/** URL protocols accepted as valid ad-hoc base URIs. */
|
|
3275
3000
|
const SUPPORTED_PROTOCOLS = ['http:', 'https:', 'ws:', 'wss:'];
|
|
@@ -3995,10 +3720,9 @@ const module$3 = {
|
|
|
3995
3720
|
// only attempt to acquire a token when the request actually declares scopes
|
|
3996
3721
|
if (scopes.length) {
|
|
3997
3722
|
/** TODO(#5143): should be try catch, check caller for handling */
|
|
3998
|
-
const
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
const accessToken = await authProvider.acquireAccessToken(telemetryExportRequest in request ? markTelemetryExportRequest(options) : options);
|
|
3723
|
+
const accessToken = await authProvider.acquireAccessToken({
|
|
3724
|
+
request: { scopes },
|
|
3725
|
+
});
|
|
4002
3726
|
// without a token there's nothing to attach, fall through to the default request
|
|
4003
3727
|
if (accessToken) {
|
|
4004
3728
|
const headers = new Headers(request.headers);
|
|
@@ -5134,6 +4858,84 @@ class FlowSubject extends Observable {
|
|
|
5134
4858
|
}
|
|
5135
4859
|
}
|
|
5136
4860
|
|
|
4861
|
+
/**
|
|
4862
|
+
* Internal helper utilities for constructing and inspecting Redux-style
|
|
4863
|
+
* lifecycle action types (`::request`, `::success`, `::failure`).
|
|
4864
|
+
*/
|
|
4865
|
+
/**
|
|
4866
|
+
* Separates an action's base type from its lifecycle suffix.
|
|
4867
|
+
*/
|
|
4868
|
+
const actionSuffixDivider = '::';
|
|
4869
|
+
|
|
4870
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: ported redux-toolkit conditional-type dispatch logic (e.g. IsAny<T, ...>) requires real `any`, not `unknown`, to distinguish the two at the type level
|
|
4871
|
+
// biome-ignore-all lint/suspicious/noConfusingVoidType: `void` here is the ported redux-toolkit sentinel meaning "no payload type provided", distinct from `undefined` (see IfVoid in ts-helpers.ts) — replacing with `undefined` would change dispatch semantics
|
|
4872
|
+
/**
|
|
4873
|
+
* taken from https://github.com/reduxjs/redux-toolkit/tree/master/packages/toolkit/src
|
|
4874
|
+
*/
|
|
4875
|
+
/**
|
|
4876
|
+
* Creates the runtime action creator implementation shared by the overloads above.
|
|
4877
|
+
*
|
|
4878
|
+
* @param type - The action type to assign to created actions.
|
|
4879
|
+
* @param prepareAction - An optional function that prepares payload, metadata, and errors.
|
|
4880
|
+
* @returns A callable action creator with type and matching metadata.
|
|
4881
|
+
* @throws {Error} When the prepare callback does not return an action object.
|
|
4882
|
+
*/
|
|
4883
|
+
function createAction(type, prepareAction) {
|
|
4884
|
+
function actionCreator(...args) {
|
|
4885
|
+
// Use the prepare callback when the caller needs custom payload construction.
|
|
4886
|
+
if (prepareAction) {
|
|
4887
|
+
const prepared = prepareAction(...args);
|
|
4888
|
+
// Reject invalid prepare callbacks before reading their action fields.
|
|
4889
|
+
if (!prepared) {
|
|
4890
|
+
throw new Error('prepareAction did not return an object');
|
|
4891
|
+
}
|
|
4892
|
+
// Merge optional metadata and error fields into the prepared action.
|
|
4893
|
+
return {
|
|
4894
|
+
type,
|
|
4895
|
+
payload: prepared.payload,
|
|
4896
|
+
...('meta' in prepared && { meta: prepared.meta }),
|
|
4897
|
+
...('error' in prepared && { error: prepared.error }),
|
|
4898
|
+
};
|
|
4899
|
+
}
|
|
4900
|
+
return { type, payload: args[0] };
|
|
4901
|
+
}
|
|
4902
|
+
actionCreator.toString = () => `${type}`;
|
|
4903
|
+
actionCreator.type = type;
|
|
4904
|
+
actionCreator.match = (action) => action.type === type;
|
|
4905
|
+
return actionCreator;
|
|
4906
|
+
}
|
|
4907
|
+
|
|
4908
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: generic constraints mirror create-action.ts's ported redux-toolkit PrepareAction<any> dispatch pattern
|
|
4909
|
+
/**
|
|
4910
|
+
* Creates the runtime async action implementation shared by the overloads above.
|
|
4911
|
+
*
|
|
4912
|
+
* @param type - The base action type string.
|
|
4913
|
+
* @param request - A prepare function for the request action payload.
|
|
4914
|
+
* @param success - A prepare function for the success action payload.
|
|
4915
|
+
* @param failure - An optional prepare function for the failure action payload.
|
|
4916
|
+
* @returns An action creator with request, success, and optional failure actions.
|
|
4917
|
+
*/
|
|
4918
|
+
function createAsyncAction(type, request, success, failure) {
|
|
4919
|
+
const action = createAction([type, 'request'].join(actionSuffixDivider), request);
|
|
4920
|
+
// Attach the success action only when the caller supplied its prepare function.
|
|
4921
|
+
if (success) {
|
|
4922
|
+
// Add the success action while preserving the request creator's identity.
|
|
4923
|
+
Object.assign(action, {
|
|
4924
|
+
success: createAction([type, 'success'].join(actionSuffixDivider), success),
|
|
4925
|
+
});
|
|
4926
|
+
}
|
|
4927
|
+
// Attach the failure action only for async workflows that support failure results.
|
|
4928
|
+
if (failure) {
|
|
4929
|
+
// Add the failure action alongside the existing request and success actions.
|
|
4930
|
+
Object.assign(action, {
|
|
4931
|
+
failure: createAction([type, 'failure'].join(actionSuffixDivider), failure),
|
|
4932
|
+
});
|
|
4933
|
+
}
|
|
4934
|
+
// `action` is progressively mutated with `success`/`failure` properties above, so its static
|
|
4935
|
+
// type can't reflect the final shape declared by this function's overload signatures.
|
|
4936
|
+
return action;
|
|
4937
|
+
}
|
|
4938
|
+
|
|
5137
4939
|
function freezeDraftable(val) {
|
|
5138
4940
|
return isDraftable(val) ? produce(val, () => { }) : val;
|
|
5139
4941
|
}
|
|
@@ -5374,11 +5176,89 @@ function toObservable(input, ...args) {
|
|
|
5374
5176
|
return of(input);
|
|
5375
5177
|
}
|
|
5376
5178
|
|
|
5179
|
+
/**
|
|
5180
|
+
* Checks whether a metadata value is an object literal that should merge key by key.
|
|
5181
|
+
* Arrays and class instances such as `Error` are excluded so they are kept intact.
|
|
5182
|
+
*
|
|
5183
|
+
* @param value - Metadata value to inspect.
|
|
5184
|
+
* @returns `true` when the value is a plain object.
|
|
5185
|
+
*/
|
|
5186
|
+
const isPlainMetadata = (value) => {
|
|
5187
|
+
// Primitives and null are kept as-is
|
|
5188
|
+
if (typeof value !== 'object' || value === null) {
|
|
5189
|
+
return false;
|
|
5190
|
+
}
|
|
5191
|
+
const prototype = Object.getPrototypeOf(value);
|
|
5192
|
+
return prototype === Object.prototype || prototype === null;
|
|
5193
|
+
};
|
|
5194
|
+
/**
|
|
5195
|
+
* Lists a metadata object's entries without `__proto__`, which would replace the prototype of an
|
|
5196
|
+
* object it is assigned to (GHSA-gcqx-w48m-w2h4).
|
|
5197
|
+
*
|
|
5198
|
+
* @param value - Metadata object whose entries are listed.
|
|
5199
|
+
* @returns The entries that are safe to assign.
|
|
5200
|
+
*/
|
|
5201
|
+
const safeEntries = (value) =>
|
|
5202
|
+
// `JSON.parse` keeps `__proto__` as an own key, so untrusted metadata can carry one
|
|
5203
|
+
Object.entries(value).filter(([key]) => key !== '__proto__');
|
|
5204
|
+
/**
|
|
5205
|
+
* Copies plain objects and arrays from incoming metadata, dropping `__proto__` keys at any depth
|
|
5206
|
+
* so a value with no existing counterpart is cleaned the same way as a merged one.
|
|
5207
|
+
*
|
|
5208
|
+
* @param value - Incoming metadata value.
|
|
5209
|
+
* @returns A cleaned copy of plain objects and arrays; any other value as-is.
|
|
5210
|
+
*/
|
|
5211
|
+
const sanitizeMetadata = (value) => {
|
|
5212
|
+
// Arrays may contain plain objects that carry `__proto__` keys
|
|
5213
|
+
if (Array.isArray(value)) {
|
|
5214
|
+
// Clean every item, since items never merge with an existing value
|
|
5215
|
+
return value.map(sanitizeMetadata);
|
|
5216
|
+
}
|
|
5217
|
+
// Class instances such as `Error` and primitives are kept as-is
|
|
5218
|
+
if (!isPlainMetadata(value)) {
|
|
5219
|
+
return value;
|
|
5220
|
+
}
|
|
5221
|
+
// Clean each nested value too, since none of them has an existing value to merge with
|
|
5222
|
+
const entries = safeEntries(value).map(([key, entryValue]) => [
|
|
5223
|
+
key,
|
|
5224
|
+
sanitizeMetadata(entryValue),
|
|
5225
|
+
]);
|
|
5226
|
+
return Object.fromEntries(entries);
|
|
5227
|
+
};
|
|
5228
|
+
/**
|
|
5229
|
+
* Merges one metadata value into another: plain objects merge recursively, arrays are appended,
|
|
5230
|
+
* and any other value from `override` wins.
|
|
5231
|
+
*
|
|
5232
|
+
* @param base - Existing metadata value.
|
|
5233
|
+
* @param override - Metadata value that takes precedence.
|
|
5234
|
+
* @returns The merged value, as a new object or array when anything was merged.
|
|
5235
|
+
*/
|
|
5236
|
+
const mergeMetadataValue = (base, override) => {
|
|
5237
|
+
// Lists of values accumulate rather than replace each other
|
|
5238
|
+
if (Array.isArray(base) && Array.isArray(override)) {
|
|
5239
|
+
// Clean incoming items before appending them to the existing list
|
|
5240
|
+
return base.concat(override.map(sanitizeMetadata));
|
|
5241
|
+
}
|
|
5242
|
+
// Anything other than two plain objects is replaced by the override
|
|
5243
|
+
if (!isPlainMetadata(base) || !isPlainMetadata(override)) {
|
|
5244
|
+
return sanitizeMetadata(override);
|
|
5245
|
+
}
|
|
5246
|
+
// Start from the base keys so keys absent from the override survive
|
|
5247
|
+
const result = Object.fromEntries(safeEntries(base));
|
|
5248
|
+
// Overlay each override key, recursing where both sides define it
|
|
5249
|
+
for (const [key, value] of safeEntries(override)) {
|
|
5250
|
+
result[key] = Object.hasOwn(base, key)
|
|
5251
|
+
? mergeMetadataValue(base[key], value)
|
|
5252
|
+
: sanitizeMetadata(value);
|
|
5253
|
+
}
|
|
5254
|
+
return result;
|
|
5255
|
+
};
|
|
5377
5256
|
/**
|
|
5378
5257
|
* Merges two `MetaData` objects into a single object.
|
|
5379
5258
|
*
|
|
5380
5259
|
* If both `source` and `target` are undefined, returns `undefined`.
|
|
5381
5260
|
* Otherwise, performs a deep merge of the two objects, with properties from `target` overriding those in `source`.
|
|
5261
|
+
* Neither input is mutated.
|
|
5382
5262
|
*
|
|
5383
5263
|
* @param source - The base `MetaData` object to merge from.
|
|
5384
5264
|
* @param target - The `MetaData` object whose properties will override those in `source`.
|
|
@@ -5389,7 +5269,7 @@ const mergeMetadata = (source, target) => {
|
|
|
5389
5269
|
if (!source && !target) {
|
|
5390
5270
|
return undefined;
|
|
5391
5271
|
}
|
|
5392
|
-
return
|
|
5272
|
+
return mergeMetadataValue(source ?? {}, target ?? {});
|
|
5393
5273
|
};
|
|
5394
5274
|
/**
|
|
5395
5275
|
* Merges two partial `TelemetryItem` objects into a single `TelemetryItem`.
|
|
@@ -5430,14 +5310,12 @@ const mergeTelemetryItem = (target, source) => {
|
|
|
5430
5310
|
* Configures telemetry settings for the application.
|
|
5431
5311
|
*
|
|
5432
5312
|
* The `TelemetryConfigurator` class extends `BaseConfigBuilder` to provide a fluent API for
|
|
5433
|
-
* setting up telemetry adapters, metadata, default scopes, parent providers
|
|
5434
|
-
* lifecycle records. Every setter records configuration; nothing is resolved until the
|
|
5435
|
-
* telemetry module builds its configuration.
|
|
5313
|
+
* setting up telemetry adapters, metadata, default scopes, and parent providers.
|
|
5436
5314
|
*
|
|
5437
5315
|
* @example
|
|
5438
5316
|
* ```typescript
|
|
5439
5317
|
* const configurator = new TelemetryConfigurator()
|
|
5440
|
-
* .setAdapter(
|
|
5318
|
+
* .setAdapter(myAdapter)
|
|
5441
5319
|
* .setMetadata({ app: 'my-app' })
|
|
5442
5320
|
* .setDefaultScope(['user', 'session'])
|
|
5443
5321
|
* .setParent(parentProvider);
|
|
@@ -5453,7 +5331,7 @@ const mergeTelemetryItem = (target, source) => {
|
|
|
5453
5331
|
* @see ITelemetryConfigurator
|
|
5454
5332
|
*/
|
|
5455
5333
|
class TelemetryConfigurator extends BaseConfigBuilder {
|
|
5456
|
-
#
|
|
5334
|
+
#adaptersCallbacks = {};
|
|
5457
5335
|
#metadata = [];
|
|
5458
5336
|
/**
|
|
5459
5337
|
* Creates a new `TelemetryConfigurator` and wires up the async adapter
|
|
@@ -5464,56 +5342,40 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
5464
5342
|
// Configure async adapter resolution using mergeMap to handle Promise/Observable adapter factories
|
|
5465
5343
|
this._set('adapters', (args) => {
|
|
5466
5344
|
// Resolve every adapter factory concurrently and merge them into a single record
|
|
5467
|
-
return from(Object.entries(this.#
|
|
5345
|
+
return from(Object.entries(this.#adaptersCallbacks)).pipe(mergeMap(([identifier, adapterFn]) =>
|
|
5468
5346
|
// Drop adapters whose factory resolved to a falsy value
|
|
5469
|
-
|
|
5470
|
-
// Factory errors may carry credentials or URLs, so only the failure is reported.
|
|
5471
|
-
console.warn('Telemetry adapter factory failed');
|
|
5472
|
-
return EMPTY;
|
|
5473
|
-
}))), scan((acc, [identifier, adapter]) => {
|
|
5347
|
+
from(adapterFn(args)).pipe(filter((adapter) => !!adapter), map((adapter) => [identifier, adapter]))), scan((acc, [identifier, adapter]) => {
|
|
5474
5348
|
acc[identifier] = adapter;
|
|
5475
5349
|
return acc;
|
|
5476
5350
|
}, {}), defaultIfEmpty({}), shareReplay({ bufferSize: 1, refCount: true }));
|
|
5477
5351
|
});
|
|
5478
|
-
// Ownership is declared configuration: a failed explicit exporter still owns delivery,
|
|
5479
|
-
// so its failure can never activate an ancestor's fallback exporter.
|
|
5480
|
-
this._set('ownsExport', async () =>
|
|
5481
|
-
// Any explicit declaration owns delivery.
|
|
5482
|
-
Object.values(this.#adapters).some(({ explicit }) => explicit));
|
|
5483
5352
|
// Configure metadata merging - handles multiple sync/async metadata sources
|
|
5484
5353
|
this._set('metadata', async () => {
|
|
5485
5354
|
const metadataItems = this.#metadata;
|
|
5486
5355
|
return (...args) =>
|
|
5487
5356
|
// Merge every registered metadata source in order, emitting the final accumulated result
|
|
5488
|
-
from(metadataItems).pipe(concatMap((metadata) => toObservable(metadata, ...args)), scan((acc, current) => mergeMetadata(acc, current) ?? {}, {}),
|
|
5357
|
+
from(metadataItems).pipe(concatMap((metadata) => toObservable(metadata, ...args)), scan((acc, current) => mergeMetadata(acc, current) ?? {}, {}), last(), shareReplay({ bufferSize: 1, refCount: true }));
|
|
5489
5358
|
});
|
|
5490
5359
|
}
|
|
5491
5360
|
/**
|
|
5492
5361
|
* Registers a telemetry adapter with the configurator.
|
|
5493
5362
|
*
|
|
5494
|
-
* A non-fallback adapter is an explicit exporter: it owns delivery, so the parent's fallback
|
|
5495
|
-
* exporters skip records this provider relays.
|
|
5496
|
-
*
|
|
5497
5363
|
* @param identifier - The key used to register and later resolve this adapter.
|
|
5498
|
-
* @param adapter - The telemetry adapter to be added.
|
|
5364
|
+
* @param adapter - The telemetry adapter to be added. The adapter's identifier is used as the key.
|
|
5499
5365
|
* @returns The current instance of the configurator for method chaining.
|
|
5500
5366
|
*/
|
|
5501
5367
|
setAdapter(identifier, adapter) {
|
|
5502
|
-
this
|
|
5503
|
-
return this;
|
|
5368
|
+
return this.configureAdapter(identifier, async () => adapter);
|
|
5504
5369
|
}
|
|
5505
5370
|
/**
|
|
5506
5371
|
* Configures a telemetry adapter with the configurator.
|
|
5507
5372
|
*
|
|
5508
|
-
* A factory's adapter is unknown until it resolves, so it is declared as an explicit exporter;
|
|
5509
|
-
* register fallback adapters with `setAdapter`.
|
|
5510
|
-
*
|
|
5511
5373
|
* @param identifier - The key used to register and later resolve this adapter.
|
|
5512
5374
|
* @param adapterFn - A callback function that returns a telemetry adapter instance
|
|
5513
5375
|
* @returns The current instance for method chaining
|
|
5514
5376
|
*/
|
|
5515
5377
|
configureAdapter(identifier, adapterFn) {
|
|
5516
|
-
this.#
|
|
5378
|
+
this.#adaptersCallbacks[identifier] = adapterFn;
|
|
5517
5379
|
return this;
|
|
5518
5380
|
}
|
|
5519
5381
|
/**
|
|
@@ -5549,15 +5411,6 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
5549
5411
|
this._set('parent', parent);
|
|
5550
5412
|
return this;
|
|
5551
5413
|
}
|
|
5552
|
-
/** @inheritdoc */
|
|
5553
|
-
setSource(source) {
|
|
5554
|
-
// Fail at configuration time; an invalid label would otherwise drop every built-in record.
|
|
5555
|
-
if (source.source !== undefined && !operationalIdentifier.safeParse(source.source).success) {
|
|
5556
|
-
throw new RangeError('Telemetry source kind must be a lowercase kebab-case identifier, such as "portal"');
|
|
5557
|
-
}
|
|
5558
|
-
this._set('source', Object.freeze({ ...source }));
|
|
5559
|
-
return this;
|
|
5560
|
-
}
|
|
5561
5414
|
/**
|
|
5562
5415
|
* Attaches an observable stream of telemetry items to the configurator.
|
|
5563
5416
|
*
|
|
@@ -5568,20 +5421,6 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
5568
5421
|
this._set('items$', item$);
|
|
5569
5422
|
return this;
|
|
5570
5423
|
}
|
|
5571
|
-
/** @inheritdoc */
|
|
5572
|
-
attachLifecycleItems(item$) {
|
|
5573
|
-
this._set('lifecycleItems$', item$);
|
|
5574
|
-
return this;
|
|
5575
|
-
}
|
|
5576
|
-
/** @inheritdoc */
|
|
5577
|
-
hasLifecycleItems() {
|
|
5578
|
-
return this._has('lifecycleItems$');
|
|
5579
|
-
}
|
|
5580
|
-
/** @inheritdoc */
|
|
5581
|
-
setLifecycleItemsEnabled(enabled) {
|
|
5582
|
-
this._set('lifecycleItemsEnabled', enabled);
|
|
5583
|
-
return this;
|
|
5584
|
-
}
|
|
5585
5424
|
/**
|
|
5586
5425
|
* Sets the filter configuration for telemetry items.
|
|
5587
5426
|
*
|
|
@@ -5597,158 +5436,13 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
5597
5436
|
}
|
|
5598
5437
|
|
|
5599
5438
|
// Generated by genversion.
|
|
5600
|
-
const version$2 = '
|
|
5439
|
+
const version$2 = '7.2.3';
|
|
5601
5440
|
|
|
5602
5441
|
/**
|
|
5603
|
-
* Represents a
|
|
5604
|
-
* to a telemetry provider. Supports cloning, resetting, and marking measurements as completed.
|
|
5442
|
+
* Represents a framework event with customizable details and behavior.
|
|
5605
5443
|
*
|
|
5606
|
-
*
|
|
5607
|
-
*
|
|
5608
|
-
* - Provides methods to measure elapsed time, reset the timer, clone the measurement, and
|
|
5609
|
-
* automatically measure on disposal if not already measured.
|
|
5610
|
-
* - Can be used to wrap asynchronous operations and automatically record their duration.
|
|
5611
|
-
*
|
|
5612
|
-
* @example
|
|
5613
|
-
* ```typescript
|
|
5614
|
-
* const measurement = new Measurement(provider, { name: 'operation' });
|
|
5615
|
-
* // ... perform operation ...
|
|
5616
|
-
* measurement.measure();
|
|
5617
|
-
* ```
|
|
5618
|
-
*
|
|
5619
|
-
* @public
|
|
5620
|
-
*/
|
|
5621
|
-
class Measurement {
|
|
5622
|
-
#provider;
|
|
5623
|
-
#data;
|
|
5624
|
-
#startTime;
|
|
5625
|
-
#measured;
|
|
5626
|
-
/**
|
|
5627
|
-
* Initializes a new instance of the Measurement class.
|
|
5628
|
-
*
|
|
5629
|
-
* @param provider - The telemetry provider responsible for handling telemetry data.
|
|
5630
|
-
* @param data - The measurement data to be associated with this instance.
|
|
5631
|
-
*/
|
|
5632
|
-
constructor(provider, data) {
|
|
5633
|
-
this.#data = data;
|
|
5634
|
-
this.#provider = provider;
|
|
5635
|
-
this.#startTime = performance.now();
|
|
5636
|
-
}
|
|
5637
|
-
/**
|
|
5638
|
-
* Creates a new `Measurement` instance that is a copy of the current one.
|
|
5639
|
-
*
|
|
5640
|
-
* @param resetOptions - Options to control which properties are preserved in the clone.
|
|
5641
|
-
* - `preserveStartTime`: If `true`, the start time of the measurement is preserved in the clone.
|
|
5642
|
-
* @returns A new `Measurement` instance with copied data and optionally preserved start time.
|
|
5643
|
-
*/
|
|
5644
|
-
clone(resetOptions = {}) {
|
|
5645
|
-
const clonedMeasurement = new Measurement(this.#provider, this.#data);
|
|
5646
|
-
// Preserve the original start time when requested, otherwise the clone starts fresh
|
|
5647
|
-
if (resetOptions.preserveStartTime) {
|
|
5648
|
-
clonedMeasurement.#startTime = this.#startTime;
|
|
5649
|
-
}
|
|
5650
|
-
return clonedMeasurement;
|
|
5651
|
-
}
|
|
5652
|
-
/**
|
|
5653
|
-
* Resets the measurement state.
|
|
5654
|
-
*
|
|
5655
|
-
* @param options - Optional settings for the reset operation.
|
|
5656
|
-
* @param options.preserveStartTime - If true, the start time will not be reset; otherwise, it will be set to the current time.
|
|
5657
|
-
*
|
|
5658
|
-
* Resets the measured flag and, unless `preserveStartTime` is true, updates the start time to the current performance time.
|
|
5659
|
-
*/
|
|
5660
|
-
reset(options) {
|
|
5661
|
-
// Keep the existing start time when preserving, otherwise restart the clock
|
|
5662
|
-
if (!options?.preserveStartTime) {
|
|
5663
|
-
this.#startTime = performance.now();
|
|
5664
|
-
}
|
|
5665
|
-
this.#measured = false;
|
|
5666
|
-
}
|
|
5667
|
-
/**
|
|
5668
|
-
* Measures the elapsed time since the last start time and sends the measurement data
|
|
5669
|
-
* to the telemetry provider. Optionally marks the measurement as completed and/or
|
|
5670
|
-
* resets the start time for subsequent measurements.
|
|
5671
|
-
*
|
|
5672
|
-
* @param data - Optional additional measurement data to merge with the existing data.
|
|
5673
|
-
* @param options - Optional measurement options.
|
|
5674
|
-
* @param options.markAsMeasured - If true, marks this measurement as completed.
|
|
5675
|
-
* @param options.resetStartTime - If true, resets the start time after measuring.
|
|
5676
|
-
* @returns The elapsed duration in milliseconds since the last start time.
|
|
5677
|
-
*/
|
|
5678
|
-
measure(data, options) {
|
|
5679
|
-
const measuredData = mergeTelemetryItem(this.#data, data ?? {});
|
|
5680
|
-
const duration = performance.now() - this.#startTime;
|
|
5681
|
-
this.#provider.trackMetric({
|
|
5682
|
-
...measuredData,
|
|
5683
|
-
value: duration,
|
|
5684
|
-
});
|
|
5685
|
-
// Mark as measured only when explicitly requested by the caller
|
|
5686
|
-
if (options?.markAsMeasured) {
|
|
5687
|
-
this.#measured = true;
|
|
5688
|
-
}
|
|
5689
|
-
// Restart the clock only when explicitly requested by the caller
|
|
5690
|
-
if (options?.resetStartTime) {
|
|
5691
|
-
this.#startTime = performance.now();
|
|
5692
|
-
}
|
|
5693
|
-
return duration;
|
|
5694
|
-
}
|
|
5695
|
-
/**
|
|
5696
|
-
* Resolves a given promise, measures the result using the provided options, and returns the resolved value.
|
|
5697
|
-
*
|
|
5698
|
-
* @template T - The type of the resolved value from the promise.
|
|
5699
|
-
* @param promise - The promise to resolve.
|
|
5700
|
-
* @param options - Optional configuration for resolving and measuring:
|
|
5701
|
-
* - `data`: A value or a function that receives the resolved result and returns measurement data.
|
|
5702
|
-
* - `options`: Additional options to pass to the `measure` method.
|
|
5703
|
-
* @returns A promise that resolves to the value of the input promise.
|
|
5704
|
-
*/
|
|
5705
|
-
async resolve(promise, options) {
|
|
5706
|
-
const result = await promise;
|
|
5707
|
-
const endData = typeof options?.data === 'function' ? options?.data(result) : options?.data;
|
|
5708
|
-
this.measure(await Promise.resolve(endData), options?.options);
|
|
5709
|
-
return result;
|
|
5710
|
-
}
|
|
5711
|
-
/**
|
|
5712
|
-
* Executes the provided function and resolves its result using the `resolve` method.
|
|
5713
|
-
* The function `fn` can return either a value of type `T` or a Promise of type `T`.
|
|
5714
|
-
* Optionally, resolution options can be provided.
|
|
5715
|
-
*
|
|
5716
|
-
* @template T - The return type of the function to execute.
|
|
5717
|
-
* @param fn - A function that returns a value or a Promise to be resolved.
|
|
5718
|
-
* @param options - Optional resolution options to customize the resolve behavior.
|
|
5719
|
-
* @returns A Promise that resolves to the result of the executed function.
|
|
5720
|
-
*/
|
|
5721
|
-
exec(fn, options) {
|
|
5722
|
-
return this.resolve(Promise.resolve(fn()), options);
|
|
5723
|
-
}
|
|
5724
|
-
/**
|
|
5725
|
-
* Disposes of the measurement instance. If the measurement has not yet been taken,
|
|
5726
|
-
* this method ensures that it is performed before disposal.
|
|
5727
|
-
*
|
|
5728
|
-
* This method is intended to be used with the ECMAScript `Symbol.dispose` protocol,
|
|
5729
|
-
* allowing for automatic resource management when used in a `using` statement.
|
|
5730
|
-
*
|
|
5731
|
-
* @see https://github.com/tc39/proposal-explicit-resource-management
|
|
5732
|
-
*/
|
|
5733
|
-
[Symbol.dispose]() {
|
|
5734
|
-
// Ensure a measurement is always taken before the instance is discarded
|
|
5735
|
-
if (!this.#measured) {
|
|
5736
|
-
try {
|
|
5737
|
-
this.measure();
|
|
5738
|
-
}
|
|
5739
|
-
catch {
|
|
5740
|
-
// Silently handle errors during disposal to ensure cleanup always completes
|
|
5741
|
-
// The measurement failure should not prevent proper resource disposal
|
|
5742
|
-
}
|
|
5743
|
-
}
|
|
5744
|
-
}
|
|
5745
|
-
}
|
|
5746
|
-
|
|
5747
|
-
/**
|
|
5748
|
-
* Represents a framework event with customizable details and behavior.
|
|
5749
|
-
*
|
|
5750
|
-
* The `FrameworkEvent` class implements the `IFrameworkEvent` interface and provides a way to create and manage events
|
|
5751
|
-
* with custom details and behavior. It allows you to:
|
|
5444
|
+
* The `FrameworkEvent` class implements the `IFrameworkEvent` interface and provides a way to create and manage events
|
|
5445
|
+
* with custom details and behavior. It allows you to:
|
|
5752
5446
|
*
|
|
5753
5447
|
* - Specify the event type and initial event details
|
|
5754
5448
|
* - Access the event details
|
|
@@ -5906,34 +5600,148 @@ class TelemetryEvent extends FrameworkEvent {
|
|
|
5906
5600
|
}
|
|
5907
5601
|
|
|
5908
5602
|
/**
|
|
5909
|
-
*
|
|
5603
|
+
* Represents a telemetry measurement that tracks elapsed time and reports measurement data
|
|
5604
|
+
* to a telemetry provider. Supports cloning, resetting, and marking measurements as completed.
|
|
5910
5605
|
*
|
|
5911
|
-
*
|
|
5912
|
-
*
|
|
5606
|
+
* @remarks
|
|
5607
|
+
* - The measurement starts timing upon instantiation.
|
|
5608
|
+
* - Provides methods to measure elapsed time, reset the timer, clone the measurement, and
|
|
5609
|
+
* automatically measure on disposal if not already measured.
|
|
5610
|
+
* - Can be used to wrap asynchronous operations and automatically record their duration.
|
|
5611
|
+
*
|
|
5612
|
+
* @example
|
|
5613
|
+
* ```typescript
|
|
5614
|
+
* const measurement = new Measurement(provider, { name: 'operation' });
|
|
5615
|
+
* // ... perform operation ...
|
|
5616
|
+
* measurement.measure();
|
|
5617
|
+
* ```
|
|
5913
5618
|
*
|
|
5914
|
-
* @
|
|
5915
|
-
* @param item - Record to relay.
|
|
5916
|
-
* @param policy - Child exporter ownership and relay filter.
|
|
5917
|
-
* @returns `true` when the parent accepted the record or the filter intentionally rejected it;
|
|
5918
|
-
* `false` when the parent cannot honour ownership (a legacy parent and an owned record).
|
|
5919
|
-
* @throws Rethrows relay filter and parent delivery errors for the caller's health reporting.
|
|
5619
|
+
* @public
|
|
5920
5620
|
*/
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5621
|
+
class Measurement {
|
|
5622
|
+
#provider;
|
|
5623
|
+
#data;
|
|
5624
|
+
#startTime;
|
|
5625
|
+
#measured;
|
|
5626
|
+
/**
|
|
5627
|
+
* Initializes a new instance of the Measurement class.
|
|
5628
|
+
*
|
|
5629
|
+
* @param provider - The telemetry provider responsible for handling telemetry data.
|
|
5630
|
+
* @param data - The measurement data to be associated with this instance.
|
|
5631
|
+
*/
|
|
5632
|
+
constructor(provider, data) {
|
|
5633
|
+
this.#data = data;
|
|
5634
|
+
this.#provider = provider;
|
|
5635
|
+
this.#startTime = performance.now();
|
|
5925
5636
|
}
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5637
|
+
/**
|
|
5638
|
+
* Creates a new `Measurement` instance that is a copy of the current one.
|
|
5639
|
+
*
|
|
5640
|
+
* @param resetOptions - Options to control which properties are preserved in the clone.
|
|
5641
|
+
* - `preserveStartTime`: If `true`, the start time of the measurement is preserved in the clone.
|
|
5642
|
+
* @returns A new `Measurement` instance with copied data and optionally preserved start time.
|
|
5643
|
+
*/
|
|
5644
|
+
clone(resetOptions = {}) {
|
|
5645
|
+
const clonedMeasurement = new Measurement(this.#provider, this.#data);
|
|
5646
|
+
// Preserve the original start time when requested, otherwise the clone starts fresh
|
|
5647
|
+
if (resetOptions.preserveStartTime) {
|
|
5648
|
+
clonedMeasurement.#startTime = this.#startTime;
|
|
5649
|
+
}
|
|
5650
|
+
return clonedMeasurement;
|
|
5930
5651
|
}
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5652
|
+
/**
|
|
5653
|
+
* Resets the measurement state.
|
|
5654
|
+
*
|
|
5655
|
+
* @param options - Optional settings for the reset operation.
|
|
5656
|
+
* @param options.preserveStartTime - If true, the start time will not be reset; otherwise, it will be set to the current time.
|
|
5657
|
+
*
|
|
5658
|
+
* Resets the measured flag and, unless `preserveStartTime` is true, updates the start time to the current performance time.
|
|
5659
|
+
*/
|
|
5660
|
+
reset(options) {
|
|
5661
|
+
// Keep the existing start time when preserving, otherwise restart the clock
|
|
5662
|
+
if (!options?.preserveStartTime) {
|
|
5663
|
+
this.#startTime = performance.now();
|
|
5664
|
+
}
|
|
5665
|
+
this.#measured = false;
|
|
5666
|
+
}
|
|
5667
|
+
/**
|
|
5668
|
+
* Measures the elapsed time since the last start time and sends the measurement data
|
|
5669
|
+
* to the telemetry provider. Optionally marks the measurement as completed and/or
|
|
5670
|
+
* resets the start time for subsequent measurements.
|
|
5671
|
+
*
|
|
5672
|
+
* @param data - Optional additional measurement data to merge with the existing data.
|
|
5673
|
+
* @param options - Optional measurement options.
|
|
5674
|
+
* @param options.markAsMeasured - If true, marks this measurement as completed.
|
|
5675
|
+
* @param options.resetStartTime - If true, resets the start time after measuring.
|
|
5676
|
+
* @returns The elapsed duration in milliseconds since the last start time.
|
|
5677
|
+
*/
|
|
5678
|
+
measure(data, options) {
|
|
5679
|
+
const measuredData = mergeTelemetryItem(this.#data, data ?? {});
|
|
5680
|
+
const duration = performance.now() - this.#startTime;
|
|
5681
|
+
this.#provider.trackMetric({
|
|
5682
|
+
...measuredData,
|
|
5683
|
+
value: duration,
|
|
5684
|
+
});
|
|
5685
|
+
// Mark as measured only when explicitly requested by the caller
|
|
5686
|
+
if (options?.markAsMeasured) {
|
|
5687
|
+
this.#measured = true;
|
|
5688
|
+
}
|
|
5689
|
+
// Restart the clock only when explicitly requested by the caller
|
|
5690
|
+
if (options?.resetStartTime) {
|
|
5691
|
+
this.#startTime = performance.now();
|
|
5692
|
+
}
|
|
5693
|
+
return duration;
|
|
5694
|
+
}
|
|
5695
|
+
/**
|
|
5696
|
+
* Resolves a given promise, measures the result using the provided options, and returns the resolved value.
|
|
5697
|
+
*
|
|
5698
|
+
* @template T - The type of the resolved value from the promise.
|
|
5699
|
+
* @param promise - The promise to resolve.
|
|
5700
|
+
* @param options - Optional configuration for resolving and measuring:
|
|
5701
|
+
* - `data`: A value or a function that receives the resolved result and returns measurement data.
|
|
5702
|
+
* - `options`: Additional options to pass to the `measure` method.
|
|
5703
|
+
* @returns A promise that resolves to the value of the input promise.
|
|
5704
|
+
*/
|
|
5705
|
+
async resolve(promise, options) {
|
|
5706
|
+
const result = await promise;
|
|
5707
|
+
const endData = typeof options?.data === 'function' ? options?.data(result) : options?.data;
|
|
5708
|
+
this.measure(await Promise.resolve(endData), options?.options);
|
|
5709
|
+
return result;
|
|
5710
|
+
}
|
|
5711
|
+
/**
|
|
5712
|
+
* Executes the provided function and resolves its result using the `resolve` method.
|
|
5713
|
+
* The function `fn` can return either a value of type `T` or a Promise of type `T`.
|
|
5714
|
+
* Optionally, resolution options can be provided.
|
|
5715
|
+
*
|
|
5716
|
+
* @template T - The return type of the function to execute.
|
|
5717
|
+
* @param fn - A function that returns a value or a Promise to be resolved.
|
|
5718
|
+
* @param options - Optional resolution options to customize the resolve behavior.
|
|
5719
|
+
* @returns A Promise that resolves to the result of the executed function.
|
|
5720
|
+
*/
|
|
5721
|
+
exec(fn, options) {
|
|
5722
|
+
return this.resolve(Promise.resolve(fn()), options);
|
|
5723
|
+
}
|
|
5724
|
+
/**
|
|
5725
|
+
* Disposes of the measurement instance. If the measurement has not yet been taken,
|
|
5726
|
+
* this method ensures that it is performed before disposal.
|
|
5727
|
+
*
|
|
5728
|
+
* This method is intended to be used with the ECMAScript `Symbol.dispose` protocol,
|
|
5729
|
+
* allowing for automatic resource management when used in a `using` statement.
|
|
5730
|
+
*
|
|
5731
|
+
* @see https://github.com/tc39/proposal-explicit-resource-management
|
|
5732
|
+
*/
|
|
5733
|
+
[Symbol.dispose]() {
|
|
5734
|
+
// Ensure a measurement is always taken before the instance is discarded
|
|
5735
|
+
if (!this.#measured) {
|
|
5736
|
+
try {
|
|
5737
|
+
this.measure();
|
|
5738
|
+
}
|
|
5739
|
+
catch {
|
|
5740
|
+
// Silently handle errors during disposal to ensure cleanup always completes
|
|
5741
|
+
// The measurement failure should not prevent proper resource disposal
|
|
5742
|
+
}
|
|
5743
|
+
}
|
|
5935
5744
|
}
|
|
5936
|
-
return false;
|
|
5937
5745
|
}
|
|
5938
5746
|
|
|
5939
5747
|
/**
|
|
@@ -5976,656 +5784,80 @@ const applyMetadata = (metadata, args) => {
|
|
|
5976
5784
|
// resolved metadata is always target, else item metadata would be overwritten
|
|
5977
5785
|
map((data) => mergeTelemetryItem({ metadata: data }, args.item)), catchError((error) => {
|
|
5978
5786
|
const errorItem = TelemetryExceptionSchema.parse({
|
|
5979
|
-
type: TelemetryType.Exception,
|
|
5980
|
-
name: TelemetryItemNames.MetadataError,
|
|
5981
|
-
exception: error,
|
|
5982
|
-
properties: {
|
|
5983
|
-
sourceMetricName: args.item.name,
|
|
5984
|
-
sourceMetricType: args.item.type,
|
|
5985
|
-
},
|
|
5986
|
-
});
|
|
5987
|
-
return merge(of(errorItem), of(args.item));
|
|
5988
|
-
}));
|
|
5989
|
-
};
|
|
5990
|
-
|
|
5991
|
-
/**
|
|
5992
|
-
* Creates the reducer recording telemetry provider transitions.
|
|
5993
|
-
* @returns A reducer with a fresh idle initial snapshot.
|
|
5994
|
-
*/
|
|
5995
|
-
function createTelemetryProviderReducer() {
|
|
5996
|
-
return createReducer$2(() => ({
|
|
5997
|
-
status: 'idle',
|
|
5998
|
-
enrichment: { pending: 0, builtIn: 0 },
|
|
5999
|
-
processing: {},
|
|
6000
|
-
failures: {},
|
|
6001
|
-
}), (builder) => builder
|
|
6002
|
-
.addCase(telemetryProviderActions.initialize, (state, { meta }) => {
|
|
6003
|
-
state.initialization = meta.operation;
|
|
6004
|
-
// Only an idle provider starts; disposal already in progress keeps its status.
|
|
6005
|
-
if (state.status === 'idle') {
|
|
6006
|
-
state.status = 'initializing';
|
|
6007
|
-
}
|
|
6008
|
-
})
|
|
6009
|
-
.addCase(telemetryProviderActions.initialize.success, (state) => {
|
|
6010
|
-
// Disposal requested during initialization wins over late readiness.
|
|
6011
|
-
if (state.status === 'initializing') {
|
|
6012
|
-
state.status = 'ready';
|
|
6013
|
-
}
|
|
6014
|
-
})
|
|
6015
|
-
.addCase(telemetryProviderActions.dispose, (state, { meta }) => {
|
|
6016
|
-
state.disposal = meta.operation;
|
|
6017
|
-
state.status = 'disposing';
|
|
6018
|
-
})
|
|
6019
|
-
.addCase(telemetryProviderActions.close, (state) => {
|
|
6020
|
-
state.status = 'disposed';
|
|
6021
|
-
})
|
|
6022
|
-
.addCase(telemetryProviderActions.enrichmentStarted, (state, { payload }) => {
|
|
6023
|
-
state.enrichment.pending++;
|
|
6024
|
-
// Only bounded built-in enrichment delays shutdown; custom enrichment is canceled.
|
|
6025
|
-
if (payload.builtIn) {
|
|
6026
|
-
state.enrichment.builtIn++;
|
|
6027
|
-
}
|
|
6028
|
-
})
|
|
6029
|
-
.addCase(telemetryProviderActions.enrichmentSettled, (state, { payload }) => {
|
|
6030
|
-
state.enrichment.pending--;
|
|
6031
|
-
// Mirror the start transition so the shutdown drain condition stays exact.
|
|
6032
|
-
if (payload.builtIn) {
|
|
6033
|
-
state.enrichment.builtIn--;
|
|
6034
|
-
}
|
|
6035
|
-
})
|
|
6036
|
-
.addCase(telemetryProviderActions.processingStarted, (state, { payload }) => {
|
|
6037
|
-
state.processing[payload] = (state.processing[payload] ?? 0) + 1;
|
|
6038
|
-
})
|
|
6039
|
-
.addCase(telemetryProviderActions.processingSettled, (state, { payload }) => {
|
|
6040
|
-
state.processing[payload] = (state.processing[payload] ?? 1) - 1;
|
|
6041
|
-
})
|
|
6042
|
-
.addCase(telemetryProviderActions.failed, (state, { payload }) => {
|
|
6043
|
-
const failure = state.failures[payload.message];
|
|
6044
|
-
// The first occurrence is retained for reporting; repeats only count.
|
|
6045
|
-
if (failure) {
|
|
6046
|
-
failure.count++;
|
|
6047
|
-
}
|
|
6048
|
-
else {
|
|
6049
|
-
state.failures[payload.message] = { error: payload, count: 1 };
|
|
6050
|
-
}
|
|
6051
|
-
}));
|
|
6052
|
-
}
|
|
6053
|
-
|
|
6054
|
-
/**
|
|
6055
|
-
* Emits once state satisfies a condition, immediately when it already does.
|
|
6056
|
-
* @param state$ - Provider state stream.
|
|
6057
|
-
* @param predicate - Condition over the snapshot.
|
|
6058
|
-
* @returns A single emission of the first matching snapshot.
|
|
6059
|
-
*/
|
|
6060
|
-
const whenState = (state$, predicate) =>
|
|
6061
|
-
// Only the first matching snapshot gates the next step.
|
|
6062
|
-
state$.pipe(filter(predicate), take(1));
|
|
6063
|
-
/**
|
|
6064
|
-
* Connects the provider once per initialize request and settles the shared operation.
|
|
6065
|
-
* @param handlers - Engine work.
|
|
6066
|
-
* @returns A flow emitting `initialize.success` or `initialize.failure`.
|
|
6067
|
-
*/
|
|
6068
|
-
const initializeFlow = (handlers) => (action$) =>
|
|
6069
|
-
// Repeated requests join the running operation, so ignore them while it is in flight.
|
|
6070
|
-
action$.pipe(filter(telemetryProviderActions.initialize.match), exhaustMap(({ payload }) =>
|
|
6071
|
-
// Connecting is the operation; its outcome settles every joined caller.
|
|
6072
|
-
from(handlers.connect(payload)).pipe(map(() => telemetryProviderActions.initialize.success()), catchError((error) => of(telemetryProviderActions.initialize.failure(error))))));
|
|
6073
|
-
/**
|
|
6074
|
-
* Drains owned work in order, closes delivery and shuts down exporters once.
|
|
6075
|
-
*
|
|
6076
|
-
* Order: initialization settles → bounded built-in enrichment drains → delivery closes
|
|
6077
|
-
* (canceling custom enrichment) → asynchronous adapter processing drains → repeated failures
|
|
6078
|
-
* are summarized → exporters shut down.
|
|
6079
|
-
*
|
|
6080
|
-
* @param handlers - Engine work.
|
|
6081
|
-
* @returns A flow emitting `close`, then `dispose.success` or `dispose.failure`.
|
|
6082
|
-
*/
|
|
6083
|
-
const disposeFlow = (handlers) => (action$, state$) =>
|
|
6084
|
-
// Disposal runs once; later requests join the same operation instead.
|
|
6085
|
-
action$.pipe(filter(telemetryProviderActions.dispose.match), exhaustMap(() => {
|
|
6086
|
-
// An in-flight initialization finishes before anything is torn down.
|
|
6087
|
-
const initialized$ = whenState(state$, () => true).pipe(exhaustMap(({ initialization }) => from(Promise.allSettled([initialization]))), ignoreElements());
|
|
6088
|
-
// Built-in enrichment has a deadline; retain terminal teardown records before closing.
|
|
6089
|
-
const closed$ = whenState(state$, ({ enrichment }) => enrichment.builtIn === 0).pipe(tap(() => handlers.close()), map(() => telemetryProviderActions.close()));
|
|
6090
|
-
// Accepted asynchronous processing is owned and must settle before exporters shut down.
|
|
6091
|
-
const drained$ = whenState(state$, ({ processing }) =>
|
|
6092
|
-
// Every adapter must be drained, not just the one that settled last.
|
|
6093
|
-
Object.values(processing).every((pending) => !pending)).pipe(tap(({ failures }) => {
|
|
6094
|
-
// Repeated failures stay visible without one event per failed record.
|
|
6095
|
-
for (const [failure, { count }] of Object.entries(failures)) {
|
|
6096
|
-
// A single occurrence was already reported; summarize only repeats.
|
|
6097
|
-
if (count > 1) {
|
|
6098
|
-
console.warn('Telemetry pipeline failure summary', { failure, count });
|
|
6099
|
-
}
|
|
6100
|
-
}
|
|
6101
|
-
}), ignoreElements());
|
|
6102
|
-
// Shutdown is attempted for every exporter; its outcome settles the operation.
|
|
6103
|
-
const shutdown$ = defer(() => from(handlers.shutdown())).pipe(map(() => telemetryProviderActions.dispose.success()), catchError((error) => of(telemetryProviderActions.dispose.failure(error))));
|
|
6104
|
-
return concat(initialized$, closed$, drained$, shutdown$);
|
|
6105
|
-
}));
|
|
6106
|
-
/**
|
|
6107
|
-
* Reports each pipeline failure once, when it first appears in state.
|
|
6108
|
-
* @param handlers - Engine work.
|
|
6109
|
-
* @returns A flow that only performs reporting and emits no actions.
|
|
6110
|
-
*/
|
|
6111
|
-
const failureFlow = (handlers) => (action$, state$) =>
|
|
6112
|
-
// Pair each failure with the snapshot that already counted it.
|
|
6113
|
-
action$.pipe(filter(telemetryProviderActions.failed.match), withLatestFrom(state$),
|
|
6114
|
-
// Only the first occurrence is actionable; repeats are summarized at teardown.
|
|
6115
|
-
filter(([{ payload }, { failures }]) => failures[payload.message]?.count === 1), tap(([{ payload }, { status }]) => handlers.report(payload, status)), ignoreElements());
|
|
6116
|
-
/**
|
|
6117
|
-
* Creates the flows driving a telemetry provider's operations and health reporting.
|
|
6118
|
-
* @param handlers - Engine work the flows delegate to.
|
|
6119
|
-
* @returns Flows to register on provider state.
|
|
6120
|
-
*/
|
|
6121
|
-
function createTelemetryProviderFlows(handlers) {
|
|
6122
|
-
return [initializeFlow(handlers), disposeFlow(handlers), failureFlow(handlers)];
|
|
6123
|
-
}
|
|
6124
|
-
|
|
6125
|
-
/** Pending metadata enrichment allowed per provider before built-in records skip enrichment. */
|
|
6126
|
-
const METADATA_CAPACITY = 256;
|
|
6127
|
-
/** Deadline for built-in enrichment; on expiry the original safe record is emitted. */
|
|
6128
|
-
const BUILT_IN_METADATA_TIMEOUT_MS = 1_000;
|
|
6129
|
-
/** Pending asynchronous processing allowed per adapter before records are dropped for it. */
|
|
6130
|
-
const ADAPTER_PROCESSING_CAPACITY = 256;
|
|
6131
|
-
/**
|
|
6132
|
-
* The telemetry engine behind a `TelemetryProvider`.
|
|
6133
|
-
*
|
|
6134
|
-
* Owns the record pipeline (scope, built-in envelope protection, bounded metadata enrichment,
|
|
6135
|
-
* adapter delivery, parent relay and event dispatch) and a reactive state of lifecycle,
|
|
6136
|
-
* in-flight work and health. `initialize` and `dispose` are async actions whose flows perform
|
|
6137
|
-
* the work; failures are recorded in state and reported once by a flow.
|
|
6138
|
-
*
|
|
6139
|
-
* Records stay on a synchronous stream rather than becoming actions, so adapter delivery
|
|
6140
|
-
* happens within `track`. Operation outcomes are delivered as actions on the `asyncScheduler`,
|
|
6141
|
-
* so with fake timers, advance timers to settle `initialize` or `dispose`.
|
|
6142
|
-
*
|
|
6143
|
-
* @example
|
|
6144
|
-
* ```ts
|
|
6145
|
-
* const state = new TelemetryProviderState({ config, owner: provider });
|
|
6146
|
-
* await state.initialize({ parent });
|
|
6147
|
-
* state.track(record);
|
|
6148
|
-
* await state.dispose();
|
|
6149
|
-
* ```
|
|
6150
|
-
*/
|
|
6151
|
-
class TelemetryProviderState {
|
|
6152
|
-
#subject;
|
|
6153
|
-
#records = new Subject();
|
|
6154
|
-
/** Delivery subscriptions, released together when delivery closes. */
|
|
6155
|
-
#connections = new Subscription();
|
|
6156
|
-
#config;
|
|
6157
|
-
#context;
|
|
6158
|
-
#modules;
|
|
6159
|
-
/**
|
|
6160
|
-
* Creates the engine and registers the flows driving its operations.
|
|
6161
|
-
* @param context - Configuration, event bus and the public provider.
|
|
6162
|
-
*/
|
|
6163
|
-
constructor({ config, ...context }) {
|
|
6164
|
-
const adapters = config?.adapters ?? {};
|
|
6165
|
-
// Configuration, not delivery success, determines inherited fallback suppression.
|
|
6166
|
-
const ownsExport = config?.ownsExport ?? Object.values(adapters).some((adapter) => !adapter.fallback);
|
|
6167
|
-
this.#config = Object.freeze({
|
|
6168
|
-
...config,
|
|
6169
|
-
adapters,
|
|
6170
|
-
defaultScope: config?.defaultScope ?? [],
|
|
6171
|
-
ownsExport,
|
|
6172
|
-
});
|
|
6173
|
-
this.#context = context;
|
|
6174
|
-
this.#subject = new FlowSubject(createTelemetryProviderReducer());
|
|
6175
|
-
const flows = createTelemetryProviderFlows({
|
|
6176
|
-
connect: (args) => this.#connect(args),
|
|
6177
|
-
close: () => this.#close(),
|
|
6178
|
-
shutdown: () => this.#shutdown(),
|
|
6179
|
-
report: (error, status) => this.#report(error, status),
|
|
6180
|
-
});
|
|
6181
|
-
// Every operation and health reaction is a flow over this state's actions.
|
|
6182
|
-
for (const flow of flows) {
|
|
6183
|
-
this.#subject.addFlow(flow);
|
|
6184
|
-
}
|
|
6185
|
-
}
|
|
6186
|
-
/**
|
|
6187
|
-
* Current snapshot of lifecycle, in-flight work and health.
|
|
6188
|
-
* @returns The latest reduced snapshot.
|
|
6189
|
-
*/
|
|
6190
|
-
get value() {
|
|
6191
|
-
return this.#subject.value;
|
|
6192
|
-
}
|
|
6193
|
-
/**
|
|
6194
|
-
* Every record tracked by the provider, after scope and enrichment.
|
|
6195
|
-
* @returns The record stream without its internal ownership envelope.
|
|
6196
|
-
*/
|
|
6197
|
-
get items() {
|
|
6198
|
-
// Expose records without their internal ownership envelope.
|
|
6199
|
-
return this.#records.pipe(map(({ item }) => item));
|
|
6200
|
-
}
|
|
6201
|
-
/**
|
|
6202
|
-
* Exporters configured for this provider, keyed by identifier.
|
|
6203
|
-
* @returns The resolved adapters.
|
|
6204
|
-
*/
|
|
6205
|
-
get adapters() {
|
|
6206
|
-
return this.#config.adapters;
|
|
6207
|
-
}
|
|
6208
|
-
/**
|
|
6209
|
-
* Sets the resolved framework module instances used to resolve metadata.
|
|
6210
|
-
* @param value - The resolved framework module instances.
|
|
6211
|
-
*/
|
|
6212
|
-
set modules(value) {
|
|
6213
|
-
this.#modules = value;
|
|
6214
|
-
}
|
|
6215
|
-
/**
|
|
6216
|
-
* Selects a derived value, emitting the current value and then only changes.
|
|
6217
|
-
* @param selector - Projection of the snapshot.
|
|
6218
|
-
* @returns The selected value stream.
|
|
6219
|
-
* @template T - Selected value type.
|
|
6220
|
-
*/
|
|
6221
|
-
select(selector) {
|
|
6222
|
-
return this.#subject.select(selector);
|
|
6223
|
-
}
|
|
6224
|
-
/**
|
|
6225
|
-
* Emits once when the snapshot satisfies a condition, immediately when it already does.
|
|
6226
|
-
* @param predicate - Condition over the snapshot.
|
|
6227
|
-
* @returns A single-emission stream suited to cancellation with `takeUntil`.
|
|
6228
|
-
*/
|
|
6229
|
-
when(predicate) {
|
|
6230
|
-
// Only the first matching transition matters for cancellation.
|
|
6231
|
-
return this.select(predicate).pipe(filter((matched) => matched), take(1));
|
|
6232
|
-
}
|
|
6233
|
-
/**
|
|
6234
|
-
* Connects exporters, the parent relay and startup streams once; repeated calls join.
|
|
6235
|
-
* @param args - Optional parent provider and startup records.
|
|
6236
|
-
* @returns Resolves when the provider is connected.
|
|
6237
|
-
* @throws {Error} When the provider has already been disposed.
|
|
6238
|
-
*/
|
|
6239
|
-
initialize(args) {
|
|
6240
|
-
const { status, initialization } = this.value;
|
|
6241
|
-
// A disposed provider must not reconnect exporters or event subscriptions.
|
|
6242
|
-
if (status === 'disposed') {
|
|
6243
|
-
return Promise.reject(new Error('Cannot initialize disposed telemetry'));
|
|
6244
|
-
}
|
|
6245
|
-
// Repeated and reentrant initialization joins the same operation.
|
|
6246
|
-
if (initialization) {
|
|
6247
|
-
return initialization;
|
|
6248
|
-
}
|
|
6249
|
-
const operation = this.#settled(telemetryProviderActions.initialize.success.match, telemetryProviderActions.initialize.failure.match);
|
|
6250
|
-
this.#subject.next(telemetryProviderActions.initialize(args, operation));
|
|
6251
|
-
return operation;
|
|
6252
|
-
}
|
|
6253
|
-
/**
|
|
6254
|
-
* Drains owned work, closes delivery and shuts down exporters once; repeated calls join.
|
|
6255
|
-
* @returns Resolves after every exporter has shut down.
|
|
6256
|
-
* @throws {AggregateError} When any exporter fails to shut down; all are still attempted.
|
|
6257
|
-
*/
|
|
6258
|
-
dispose() {
|
|
6259
|
-
const { disposal } = this.value;
|
|
6260
|
-
// Direct disposal and module disposal must join the same pending shutdown.
|
|
6261
|
-
if (disposal) {
|
|
6262
|
-
return disposal;
|
|
6263
|
-
}
|
|
6264
|
-
const operation = this.#settled(telemetryProviderActions.dispose.success.match, telemetryProviderActions.dispose.failure.match);
|
|
6265
|
-
// Dispatch before any exporter callback runs, so reentrant disposal joins this operation.
|
|
6266
|
-
this.#subject.next(telemetryProviderActions.dispose(operation));
|
|
6267
|
-
return operation;
|
|
6268
|
-
}
|
|
6269
|
-
/**
|
|
6270
|
-
* Records a pipeline health failure; the first occurrence of each is reported.
|
|
6271
|
-
* @param error - Controlled failure description; never a raw business error.
|
|
6272
|
-
*/
|
|
6273
|
-
fail(error) {
|
|
6274
|
-
this.#subject.next(telemetryProviderActions.failed(error));
|
|
6275
|
-
}
|
|
6276
|
-
/**
|
|
6277
|
-
* Tracks a validated record: applies scope, protects built-in envelopes, enriches metadata
|
|
6278
|
-
* within bounds and emits it to adapters, the parent and the event bus.
|
|
6279
|
-
*
|
|
6280
|
-
* @param item - Validated telemetry record.
|
|
6281
|
-
* @param handledByChild - Whether an explicit child exporter already handled the record.
|
|
6282
|
-
*/
|
|
6283
|
-
track(item, handledByChild = false) {
|
|
6284
|
-
const { status, enrichment } = this.value;
|
|
6285
|
-
// Stop accepting records before flushing, while allowing already-owned enrichment to settle.
|
|
6286
|
-
if (status === 'disposing' || status === 'disposed') {
|
|
6287
|
-
return;
|
|
6288
|
-
}
|
|
6289
|
-
const mergedItem = mergeTelemetryItem(item, {
|
|
6290
|
-
scope: item.diagnostic ? undefined : this.#config.defaultScope,
|
|
6291
|
-
});
|
|
6292
|
-
// Snapshot before invoking consumer metadata; neither enrichment nor adapters may rewrite origin.
|
|
6293
|
-
const diagnostic = mergedItem.diagnostic
|
|
6294
|
-
? Object.freeze({ ...mergedItem.diagnostic })
|
|
6295
|
-
: undefined;
|
|
6296
|
-
const operational = diagnostic
|
|
6297
|
-
? {
|
|
6298
|
-
...mergedItem,
|
|
6299
|
-
diagnostic,
|
|
6300
|
-
metadata: undefined,
|
|
6301
|
-
scope: [...mergedItem.scope],
|
|
6302
|
-
properties: mergedItem.properties ? { ...mergedItem.properties } : undefined,
|
|
6303
|
-
}
|
|
6304
|
-
: undefined;
|
|
6305
|
-
// Immutable built-in envelopes keep one adapter from contaminating sibling or parent delivery.
|
|
6306
|
-
if (operational) {
|
|
6307
|
-
Object.freeze(operational.scope);
|
|
6308
|
-
Object.freeze(operational.properties);
|
|
6309
|
-
Object.freeze(operational);
|
|
6310
|
-
}
|
|
6311
|
-
const emit = (nextItem) => {
|
|
6312
|
-
// Late custom metadata emissions cannot revive a completed provider.
|
|
6313
|
-
if (this.value.status !== 'disposed') {
|
|
6314
|
-
// Dynamic metadata is consumer-controlled; built-in records export only their safe envelope.
|
|
6315
|
-
this.#records.next({ item: operational ?? nextItem, handledByChild });
|
|
6316
|
-
}
|
|
6317
|
-
};
|
|
6318
|
-
const { metadata } = this.#config;
|
|
6319
|
-
// Skip metadata application for TelemetryMetadataError items to prevent infinite recursion
|
|
6320
|
-
if (!metadata || mergedItem.name === TelemetryItemNames.MetadataError) {
|
|
6321
|
-
// Providers without metadata still need the same fallback precedence.
|
|
6322
|
-
emit(mergedItem);
|
|
6323
|
-
return;
|
|
6324
|
-
}
|
|
6325
|
-
// Bounded built-in enrichment degrades to the safe original instead of dropping the outcome.
|
|
6326
|
-
if (diagnostic && enrichment.pending >= METADATA_CAPACITY) {
|
|
6327
|
-
this.fail(new Error('Built-in telemetry metadata capacity exceeded'));
|
|
6328
|
-
emit(mergedItem);
|
|
6329
|
-
return;
|
|
6330
|
-
}
|
|
6331
|
-
const builtIn = diagnostic !== undefined;
|
|
6332
|
-
this.#subject.next(telemetryProviderActions.enrichmentStarted(builtIn));
|
|
6333
|
-
const metadata$ = defer(() => applyMetadata(metadata, { modules: this.#modules, item: mergedItem }));
|
|
6334
|
-
// Enrichment is a bounded, cancellable stage whose in-flight work is provider state.
|
|
6335
|
-
metadata$
|
|
6336
|
-
.pipe(
|
|
6337
|
-
// Custom records retain their legacy timing; built-in outcomes have a finite enrichment deadline.
|
|
6338
|
-
builtIn ? timeout({ first: BUILT_IN_METADATA_TIMEOUT_MS }) : (source$) => source$,
|
|
6339
|
-
// Failure recovery preserves the terminal outcome even when metadata throws or times out.
|
|
6340
|
-
catchError(() => {
|
|
6341
|
-
this.fail(new Error('Telemetry metadata resolution failed'));
|
|
6342
|
-
return of(mergedItem);
|
|
6343
|
-
}),
|
|
6344
|
-
// Closing cancels custom enrichment; built-in enrichment has already drained by then.
|
|
6345
|
-
takeUntil(this.when((snapshot) => snapshot.status === 'disposed')),
|
|
6346
|
-
// Leaving flight is state, so disposal can wait on the drained condition.
|
|
6347
|
-
finalize$1(() => this.#subject.next(telemetryProviderActions.enrichmentSettled(builtIn))))
|
|
6348
|
-
.subscribe({
|
|
6349
|
-
next: (nextItem) => {
|
|
6350
|
-
// Raw enrichment errors are health failures, not exportable built-in exceptions.
|
|
6351
|
-
if (diagnostic && nextItem.name === TelemetryItemNames.MetadataError) {
|
|
6352
|
-
this.fail(new Error('Telemetry metadata resolution failed'));
|
|
6353
|
-
return;
|
|
6354
|
-
}
|
|
6355
|
-
emit(nextItem);
|
|
6356
|
-
},
|
|
6357
|
-
error: () => {
|
|
6358
|
-
this.fail(new Error('Telemetry metadata resolution failed'));
|
|
6359
|
-
emit(mergedItem);
|
|
6360
|
-
},
|
|
6361
|
-
});
|
|
6362
|
-
}
|
|
6363
|
-
/**
|
|
6364
|
-
* Settles an operation from its outcome action.
|
|
6365
|
-
* @param succeeded - Matches the operation's success action.
|
|
6366
|
-
* @param failed - Matches the operation's failure action.
|
|
6367
|
-
* @returns Resolves on success; rejects with the failure payload.
|
|
6368
|
-
* @throws The failure action's payload, normally the original operation error.
|
|
6369
|
-
*/
|
|
6370
|
-
#settled(succeeded, failed) {
|
|
6371
|
-
// The first outcome of this operation settles every joined caller.
|
|
6372
|
-
const outcome$ = this.#subject.action$.pipe(filter((action) => succeeded(action) || failed(action)));
|
|
6373
|
-
return firstValueFrom(outcome$).then((action) => {
|
|
6374
|
-
// A failure outcome carries the original error as its payload.
|
|
6375
|
-
if (failed(action)) {
|
|
6376
|
-
throw 'payload' in action ? action.payload : action;
|
|
6377
|
-
}
|
|
6378
|
-
});
|
|
6379
|
-
}
|
|
6380
|
-
/**
|
|
6381
|
-
* Initializes exporters, then connects delivery, the parent relay and startup streams.
|
|
6382
|
-
* @param args - Optional parent provider and startup records.
|
|
6383
|
-
*/
|
|
6384
|
-
async #connect(args) {
|
|
6385
|
-
const { parent, initialItems } = args ?? {};
|
|
6386
|
-
const exporters = Object.entries(this.#config.adapters);
|
|
6387
|
-
// Exporters initialize concurrently; one failing exporter must not block the others.
|
|
6388
|
-
const results = await Promise.allSettled(
|
|
6389
|
-
// Start every exporter's initialization; async wrapping turns synchronous throws into rejections.
|
|
6390
|
-
exporters.map(async ([, adapter]) => adapter.initialize()));
|
|
6391
|
-
// Report each exporter that failed to initialize, without its raw error.
|
|
6392
|
-
for (const [index, result] of results.entries()) {
|
|
6393
|
-
// Fulfilled exporters need no action.
|
|
6394
|
-
if (result.status === 'rejected') {
|
|
6395
|
-
this.fail(new Error(`Failed to initialize telemetry adapter "${exporters[index][0]}"`));
|
|
6396
|
-
}
|
|
6397
|
-
}
|
|
6398
|
-
// Disposal may have closed delivery while an asynchronous exporter initialized.
|
|
6399
|
-
if (this.value.status === 'disposed') {
|
|
6400
|
-
return;
|
|
6401
|
-
}
|
|
6402
|
-
this.#connections.add(this.#records.subscribe((record) => this.#deliver(record)));
|
|
6403
|
-
// Only a configured parent receives relayed records.
|
|
6404
|
-
if (parent) {
|
|
6405
|
-
this.#connections.add(this.#records.subscribe((record) => this.#relay(parent, record)));
|
|
6406
|
-
}
|
|
6407
|
-
this.#connections.add(this.#records.subscribe(({ item }) => this.#dispatchEvent(item)));
|
|
6408
|
-
// Keep subscriptions independent so a failed custom stream cannot terminate host lifecycle capture.
|
|
6409
|
-
// A disabled capture stays attached but unconsumed, so the host can still recover it on failure.
|
|
6410
|
-
if (this.#config.lifecycleItems$ && this.#config.lifecycleItemsEnabled !== false) {
|
|
6411
|
-
// The provider is the authority on host identity once it exists.
|
|
6412
|
-
this.#connections.add(this.#track(from(this.#config.lifecycleItems$).pipe(map((item) => this.#stamp(item)))));
|
|
6413
|
-
}
|
|
6414
|
-
// Startup records passed at initialization are tracked as given.
|
|
6415
|
-
if (initialItems) {
|
|
6416
|
-
this.#connections.add(this.#track(initialItems));
|
|
6417
|
-
}
|
|
6418
|
-
}
|
|
6419
|
-
/**
|
|
6420
|
-
* Stamps this provider's host identity on a built-in lifecycle record, so records captured
|
|
6421
|
-
* before the provider existed share its source and host ID.
|
|
6422
|
-
* @param item - Captured lifecycle record.
|
|
6423
|
-
* @returns The record carrying this provider's identity and scope.
|
|
6424
|
-
*/
|
|
6425
|
-
#stamp(item) {
|
|
6426
|
-
const source = this.#context.owner.source;
|
|
6427
|
-
// Custom records and providers without identity are left as captured.
|
|
6428
|
-
if (!item.diagnostic || !source) {
|
|
6429
|
-
return item;
|
|
6430
|
-
}
|
|
6431
|
-
const { source: kind, hostId, hostName, hostVersion } = source;
|
|
6432
|
-
return {
|
|
6433
|
-
...item,
|
|
6434
|
-
scope: operationalScope(source),
|
|
6435
|
-
diagnostic: { ...item.diagnostic, source: kind, hostId, hostName, hostVersion },
|
|
6436
|
-
};
|
|
6437
|
-
}
|
|
6438
|
-
/**
|
|
6439
|
-
* Tracks a startup stream through the public provider so each record is validated.
|
|
6440
|
-
* @param items - Startup records.
|
|
6441
|
-
* @returns The owned subscription.
|
|
6442
|
-
*/
|
|
6443
|
-
#track(items) {
|
|
6444
|
-
return from(items).subscribe({
|
|
6445
|
-
next: (item) => this.#context.owner.track(item),
|
|
6446
|
-
error: () => this.fail(new Error('Initial telemetry stream failed')),
|
|
6447
|
-
});
|
|
6448
|
-
}
|
|
6449
|
-
/**
|
|
6450
|
-
* Delivers one record to every eligible exporter within per-exporter capacity.
|
|
6451
|
-
* @param record - Record and its fallback ownership.
|
|
6452
|
-
*/
|
|
6453
|
-
#deliver({ item, handledByChild }) {
|
|
6454
|
-
const { adapter: adapterFilter } = this.#config.filter ?? {};
|
|
6455
|
-
try {
|
|
6456
|
-
// A host-level adapter filter intentionally rejects delivery without enabling fallback.
|
|
6457
|
-
if (adapterFilter && !adapterFilter(item)) {
|
|
6458
|
-
return;
|
|
6459
|
-
}
|
|
6460
|
-
}
|
|
6461
|
-
catch {
|
|
6462
|
-
this.fail(new Error('Telemetry adapter filter failed'));
|
|
6463
|
-
return;
|
|
6464
|
-
}
|
|
6465
|
-
// Every exporter is attempted independently.
|
|
6466
|
-
for (const [identifier, adapter] of Object.entries(this.#config.adapters)) {
|
|
6467
|
-
// Explicit child exporters take precedence over inherited development exporters.
|
|
6468
|
-
if (adapter.fallback && handledByChild) {
|
|
6469
|
-
// Keep application-owned records out of parent fallback exporters.
|
|
6470
|
-
continue;
|
|
6471
|
-
}
|
|
6472
|
-
// A slow exporter cannot consume a healthy sibling exporter's processing capacity.
|
|
6473
|
-
if ((this.value.processing[identifier] ?? 0) >= ADAPTER_PROCESSING_CAPACITY) {
|
|
6474
|
-
this.fail(new Error('Telemetry adapter processing capacity exceeded'));
|
|
6475
|
-
// Keep attempting sibling adapters even when this destination is saturated.
|
|
6476
|
-
continue;
|
|
6477
|
-
}
|
|
6478
|
-
try {
|
|
6479
|
-
const result = adapter.processItem(item);
|
|
6480
|
-
// Only asynchronous processing needs shutdown ownership and bounded pending capacity.
|
|
6481
|
-
if (result) {
|
|
6482
|
-
this.#subject.next(telemetryProviderActions.processingStarted(identifier));
|
|
6483
|
-
Promise.resolve(result)
|
|
6484
|
-
.catch(() => this.fail(new Error(`Failed to process telemetry item with adapter "${identifier}"`)))
|
|
6485
|
-
.finally(() => this.#subject.next(telemetryProviderActions.processingSettled(identifier)));
|
|
6486
|
-
}
|
|
6487
|
-
}
|
|
6488
|
-
catch {
|
|
6489
|
-
this.fail(new Error(`Failed to process telemetry item with adapter "${identifier}"`));
|
|
6490
|
-
}
|
|
6491
|
-
}
|
|
6492
|
-
}
|
|
6493
|
-
/**
|
|
6494
|
-
* Relays one record to the parent, carrying exporter ownership.
|
|
6495
|
-
* @param target - Parent provider.
|
|
6496
|
-
* @param record - Record and its fallback ownership.
|
|
6497
|
-
*/
|
|
6498
|
-
#relay(target, { item, handledByChild }) {
|
|
6499
|
-
const policy = {
|
|
6500
|
-
// Carry exporter ownership across intermediate providers without changing record metadata.
|
|
6501
|
-
ownsExport: handledByChild || this.#config.ownsExport,
|
|
6502
|
-
relay: this.#config.filter?.relay,
|
|
6503
|
-
};
|
|
6504
|
-
try {
|
|
6505
|
-
// Legacy parents predate ownership; live relay keeps sending them every record.
|
|
6506
|
-
if (!relayRecord(target, item, policy)) {
|
|
6507
|
-
target.track(item);
|
|
6508
|
-
}
|
|
6509
|
-
}
|
|
6510
|
-
catch {
|
|
6511
|
-
this.fail(new Error('Telemetry parent relay failed'));
|
|
6512
|
-
}
|
|
6513
|
-
}
|
|
6514
|
-
/**
|
|
6515
|
-
* Dispatches one record as an event when an event bus is configured.
|
|
6516
|
-
* @param item - Delivered record.
|
|
6517
|
-
*/
|
|
6518
|
-
#dispatchEvent(item) {
|
|
6519
|
-
const { event, owner } = this.#context;
|
|
6520
|
-
// Only dispatch when an event provider is configured
|
|
6521
|
-
if (!event) {
|
|
6522
|
-
return;
|
|
6523
|
-
}
|
|
6524
|
-
try {
|
|
6525
|
-
Promise.resolve(event.dispatchEvent(new TelemetryEvent(item, owner))).catch(() => this.fail(new Error('Telemetry event dispatch failed')));
|
|
6526
|
-
}
|
|
6527
|
-
catch {
|
|
6528
|
-
this.fail(new Error('Telemetry event dispatch failed'));
|
|
6529
|
-
}
|
|
6530
|
-
}
|
|
6531
|
-
/** Releases delivery subscriptions and completes the record stream. */
|
|
6532
|
-
#close() {
|
|
6533
|
-
this.#connections.unsubscribe();
|
|
6534
|
-
this.#records.complete();
|
|
6535
|
-
}
|
|
6536
|
-
/**
|
|
6537
|
-
* Shuts down every exporter, even when one rejects or throws synchronously.
|
|
6538
|
-
* @throws {AggregateError} When any exporter fails to shut down.
|
|
6539
|
-
*/
|
|
6540
|
-
async #shutdown() {
|
|
6541
|
-
// Attempt every exporter; one failure must not skip the others.
|
|
6542
|
-
const shutdowns = Object.entries(this.#config.adapters).map(async ([identifier, adapter]) => {
|
|
6543
|
-
try {
|
|
6544
|
-
await adapter.shutdown?.();
|
|
6545
|
-
}
|
|
6546
|
-
catch (cause) {
|
|
6547
|
-
throw new Error(`Failed to shut down telemetry adapter "${identifier}"`, { cause });
|
|
6548
|
-
}
|
|
6549
|
-
});
|
|
6550
|
-
const results = await Promise.allSettled(shutdowns);
|
|
6551
|
-
// Keep only failures for the module lifecycle's structured disposal error event.
|
|
6552
|
-
const errors = results.flatMap((result) =>
|
|
6553
|
-
// Successful shutdowns must not obscure another exporter's failure.
|
|
6554
|
-
result.status === 'rejected' ? [result.reason] : []);
|
|
6555
|
-
// Reject only after every exporter had a chance to release its resources.
|
|
6556
|
-
if (errors.length) {
|
|
6557
|
-
throw new AggregateError(errors, 'Failed to shut down telemetry adapters');
|
|
6558
|
-
}
|
|
6559
|
-
}
|
|
6560
|
-
/**
|
|
6561
|
-
* Reports a newly observed failure through the out-of-band health channel.
|
|
6562
|
-
* @param error - First occurrence of the failure.
|
|
6563
|
-
* @param status - Provider status when the failure was recorded.
|
|
6564
|
-
*/
|
|
6565
|
-
#report(error, status) {
|
|
6566
|
-
const { event, owner } = this.#context;
|
|
6567
|
-
// The event bus is an out-of-band health sink, never an automatic telemetry exporter.
|
|
6568
|
-
if (event && status !== 'disposed') {
|
|
6569
|
-
try {
|
|
6570
|
-
Promise.resolve(event.dispatchEvent(new TelemetryErrorEvent(error, owner))).catch(() => console.warn('Telemetry health event delivery failed'));
|
|
6571
|
-
}
|
|
6572
|
-
catch {
|
|
6573
|
-
// A failed health channel must remain visible without recursively dispatching another event.
|
|
6574
|
-
console.warn('Telemetry health event delivery failed');
|
|
6575
|
-
}
|
|
6576
|
-
}
|
|
6577
|
-
else {
|
|
6578
|
-
console.warn(error.message);
|
|
6579
|
-
}
|
|
6580
|
-
}
|
|
6581
|
-
}
|
|
5787
|
+
type: TelemetryType.Exception,
|
|
5788
|
+
name: TelemetryItemNames.MetadataError,
|
|
5789
|
+
exception: error,
|
|
5790
|
+
properties: {
|
|
5791
|
+
sourceMetricName: args.item.name,
|
|
5792
|
+
sourceMetricType: args.item.type,
|
|
5793
|
+
},
|
|
5794
|
+
});
|
|
5795
|
+
return merge(of(errorItem), of(args.item));
|
|
5796
|
+
}));
|
|
5797
|
+
};
|
|
6582
5798
|
|
|
6583
5799
|
/**
|
|
6584
|
-
*
|
|
6585
|
-
*
|
|
5800
|
+
* Provides telemetry tracking, event dispatching, and adapter integration for application instrumentation.
|
|
5801
|
+
*
|
|
5802
|
+
* The `TelemetryProvider` class is responsible for collecting, processing, and relaying telemetry data such as events,
|
|
5803
|
+
* exceptions, metrics, and custom events. It supports adapter-based processing, event provider integration, and
|
|
5804
|
+
* hierarchical telemetry relaying to parent providers. Telemetry items are validated and dispatched to registered
|
|
5805
|
+
* adapters and event providers, with robust error handling and metadata merging.
|
|
6586
5806
|
*
|
|
6587
|
-
*
|
|
6588
|
-
*
|
|
6589
|
-
*
|
|
5807
|
+
* @remarks
|
|
5808
|
+
* - Integrates with adapters for extensible telemetry processing.
|
|
5809
|
+
* - Supports event provider for dispatching telemetry as events.
|
|
5810
|
+
* - Allows relaying telemetry data to a parent provider, merging metadata as needed.
|
|
5811
|
+
* - Provides utility methods for tracking and measuring telemetry items.
|
|
5812
|
+
* - Handles error reporting via telemetry error events.
|
|
6590
5813
|
*
|
|
6591
5814
|
* @example
|
|
6592
5815
|
* ```typescript
|
|
6593
5816
|
* const provider = new TelemetryProvider(config, { event: eventProvider });
|
|
6594
|
-
*
|
|
6595
|
-
* provider.trackEvent({ name: 'user_login', metadata: { userId: '123' } });
|
|
5817
|
+
* provider.track({ type: TelemetryType.Event, name: 'user_login', metadata: { userId: '123' } });
|
|
6596
5818
|
* const end = provider.measure({ name: 'load_time' });
|
|
6597
5819
|
* // ... do work ...
|
|
6598
5820
|
* end();
|
|
6599
|
-
* await provider.dispose();
|
|
6600
5821
|
* ```
|
|
5822
|
+
*
|
|
5823
|
+
* @typeParam TelemetryConfig - The configuration type for telemetry.
|
|
5824
|
+
* @implements ITelemetryProvider
|
|
5825
|
+
* @extends BaseModuleProvider<TelemetryConfig>
|
|
6601
5826
|
*/
|
|
6602
5827
|
class TelemetryProvider extends BaseModuleProvider {
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
#
|
|
5828
|
+
#items = new Subject();
|
|
5829
|
+
#initialized = false;
|
|
5830
|
+
#disposal;
|
|
5831
|
+
#adapters = {};
|
|
5832
|
+
#handledByChild = new WeakSet();
|
|
5833
|
+
#filters;
|
|
5834
|
+
#defaultScope;
|
|
5835
|
+
#eventProvider;
|
|
6606
5836
|
/**
|
|
6607
5837
|
* Stream of every telemetry item tracked by this provider.
|
|
6608
5838
|
*
|
|
6609
5839
|
* @returns An observable emitting each tracked `TelemetryItem`.
|
|
6610
5840
|
*/
|
|
6611
5841
|
get items() {
|
|
6612
|
-
return this.#
|
|
5842
|
+
return this.#items.asObservable();
|
|
6613
5843
|
}
|
|
6614
5844
|
/**
|
|
6615
|
-
* Returns true
|
|
5845
|
+
* Returns true if the provider has been initialized.
|
|
6616
5846
|
*
|
|
6617
|
-
* @returns `true`
|
|
5847
|
+
* @returns `true` when {@link initialize} has completed, otherwise `false`.
|
|
6618
5848
|
*/
|
|
6619
5849
|
get initialized() {
|
|
6620
|
-
return this.#
|
|
5850
|
+
return this.#initialized;
|
|
6621
5851
|
}
|
|
5852
|
+
#metadata;
|
|
5853
|
+
#modules;
|
|
6622
5854
|
/**
|
|
6623
5855
|
* Sets the resolved framework module instances used to resolve metadata.
|
|
6624
5856
|
*
|
|
6625
5857
|
* @param value - The resolved framework module instances.
|
|
6626
5858
|
*/
|
|
6627
5859
|
set modules(value) {
|
|
6628
|
-
this.#
|
|
5860
|
+
this.#modules = value;
|
|
6629
5861
|
}
|
|
6630
5862
|
/**
|
|
6631
5863
|
* Creates a new `TelemetryProvider`.
|
|
@@ -6636,27 +5868,51 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
6636
5868
|
*/
|
|
6637
5869
|
constructor(config, deps) {
|
|
6638
5870
|
super({ version: version$2, config });
|
|
6639
|
-
this
|
|
6640
|
-
this.#
|
|
5871
|
+
this.#adapters = config?.adapters ?? {};
|
|
5872
|
+
this.#metadata = config?.metadata;
|
|
5873
|
+
this.#defaultScope = config?.defaultScope ?? [];
|
|
5874
|
+
this.#eventProvider = deps?.event;
|
|
5875
|
+
this.#filters = config?.filter;
|
|
6641
5876
|
}
|
|
6642
5877
|
/**
|
|
6643
|
-
*
|
|
6644
|
-
* are reported but do not prevent the provider from becoming usable.
|
|
5878
|
+
* Initializes the telemetry provider with adapters, parent provider, and optional initial items.
|
|
6645
5879
|
*
|
|
6646
|
-
*
|
|
5880
|
+
* This method sets up the provider for operation by:
|
|
5881
|
+
* 1. Storing the provided adapters
|
|
5882
|
+
* 2. Initializing all adapters that support async initialization (failures are logged but don't prevent initialization)
|
|
5883
|
+
* 3. Setting up subscriptions for telemetry processing
|
|
5884
|
+
* 4. Handling initial telemetry items if provided
|
|
6647
5885
|
*
|
|
6648
|
-
* @param args -
|
|
6649
|
-
* @
|
|
6650
|
-
* @
|
|
5886
|
+
* @param args - Initialization arguments
|
|
5887
|
+
* @param args.adapters - Array of telemetry adapters to use
|
|
5888
|
+
* @param args.parent - Optional parent telemetry provider for relaying data
|
|
5889
|
+
* @param args.initialItems - Optional observable of initial telemetry items to process
|
|
5890
|
+
* @returns A promise that resolves when initialization is complete
|
|
6651
5891
|
*/
|
|
6652
|
-
initialize(args) {
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
5892
|
+
async initialize(args) {
|
|
5893
|
+
const { parent, initialItems } = args ?? {};
|
|
5894
|
+
// Mark as initialized early to allow adapter connections
|
|
5895
|
+
this.#initialized = true;
|
|
5896
|
+
// Initialize all adapters that support async initialization
|
|
5897
|
+
await this._initializeAdapters();
|
|
5898
|
+
// Connect adapters to the telemetry items, processing each item as it is emitted
|
|
5899
|
+
this._addTeardown(this._connectAdapters());
|
|
5900
|
+
// If a parent telemetry provider is provided, relay telemetry data to it
|
|
5901
|
+
if (parent) {
|
|
5902
|
+
this._addTeardown(this._relayTelemetryData(parent));
|
|
5903
|
+
}
|
|
5904
|
+
// Emit telemetry items as events if an event provider is available
|
|
5905
|
+
this._addTeardown(this.#items.subscribe(this._dispatchEntityEvent.bind(this)));
|
|
5906
|
+
// Handle initial telemetry items if provided
|
|
5907
|
+
if (initialItems) {
|
|
5908
|
+
this._addTeardown(from(initialItems).subscribe((item) => {
|
|
5909
|
+
this.track(item);
|
|
5910
|
+
}));
|
|
5911
|
+
}
|
|
5912
|
+
// Ensure the subject is completed when the provider is destroyed
|
|
5913
|
+
this._addTeardown(() => {
|
|
5914
|
+
this.#items.complete();
|
|
5915
|
+
});
|
|
6660
5916
|
}
|
|
6661
5917
|
/**
|
|
6662
5918
|
* Stops telemetry subscriptions and waits for every adapter to flush and shut down.
|
|
@@ -6672,7 +5928,158 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
6672
5928
|
* ```
|
|
6673
5929
|
*/
|
|
6674
5930
|
dispose() {
|
|
6675
|
-
|
|
5931
|
+
// Direct disposal and module disposal must join the same pending shutdown.
|
|
5932
|
+
if (this.#disposal) {
|
|
5933
|
+
return this.#disposal;
|
|
5934
|
+
}
|
|
5935
|
+
super.dispose();
|
|
5936
|
+
// Defer adapter callbacks until the shared promise is assigned, including reentrant disposal.
|
|
5937
|
+
this.#disposal = Promise.resolve().then(async () => {
|
|
5938
|
+
// Shut down all exporters even when one rejects or throws synchronously.
|
|
5939
|
+
const results = await Promise.allSettled(Object.entries(this.#adapters).map(async ([identifier, adapter]) => {
|
|
5940
|
+
try {
|
|
5941
|
+
await adapter.shutdown?.();
|
|
5942
|
+
}
|
|
5943
|
+
catch (cause) {
|
|
5944
|
+
throw new Error(`Failed to shut down telemetry adapter "${identifier}"`, { cause });
|
|
5945
|
+
}
|
|
5946
|
+
}));
|
|
5947
|
+
const errors = [];
|
|
5948
|
+
// Retain each failure for the module lifecycle's structured disposal error event.
|
|
5949
|
+
for (const result of results) {
|
|
5950
|
+
// Successful shutdowns must not obscure another exporter's failure.
|
|
5951
|
+
if (result.status === 'rejected') {
|
|
5952
|
+
errors.push(result.reason);
|
|
5953
|
+
}
|
|
5954
|
+
}
|
|
5955
|
+
// Reject only after all exporters have had a chance to release their resources.
|
|
5956
|
+
if (errors.length) {
|
|
5957
|
+
throw new AggregateError(errors, 'Failed to shut down telemetry adapters');
|
|
5958
|
+
}
|
|
5959
|
+
});
|
|
5960
|
+
return this.#disposal;
|
|
5961
|
+
}
|
|
5962
|
+
/**
|
|
5963
|
+
* Initializes all telemetry adapters that support async initialization.
|
|
5964
|
+
* Uses Promise.allSettled to ensure all adapters attempt initialization,
|
|
5965
|
+
* with individual error reporting for any failures.
|
|
5966
|
+
* Subclasses can override this method to customize adapter initialization behavior.
|
|
5967
|
+
*
|
|
5968
|
+
* @protected
|
|
5969
|
+
*/
|
|
5970
|
+
async _initializeAdapters() {
|
|
5971
|
+
// Initialize all adapters, do it in parallel
|
|
5972
|
+
const adapterEntries = Object.entries(this.#adapters);
|
|
5973
|
+
// Kick off every adapter's initialize() concurrently
|
|
5974
|
+
const initializationPromises = adapterEntries.map(([_identifier, adapter]) => adapter.initialize());
|
|
5975
|
+
// Wait for all adapters to settle (either resolve or reject)
|
|
5976
|
+
const results = await Promise.allSettled(initializationPromises);
|
|
5977
|
+
// Check each result and dispatch errors for failed initializations
|
|
5978
|
+
for (const [index, result] of results.entries()) {
|
|
5979
|
+
// Only rejected adapters need an error dispatched; fulfilled ones need no action
|
|
5980
|
+
if (result.status === 'rejected') {
|
|
5981
|
+
const [identifier] = adapterEntries[index];
|
|
5982
|
+
this._dispatchError(new Error(`Failed to initialize telemetry adapter "${identifier}"`, {
|
|
5983
|
+
cause: result.reason,
|
|
5984
|
+
}));
|
|
5985
|
+
}
|
|
5986
|
+
}
|
|
5987
|
+
}
|
|
5988
|
+
/**
|
|
5989
|
+
* Subscribes all adapters to the telemetry item stream.
|
|
5990
|
+
* Each adapter processes every telemetry item as it is emitted.
|
|
5991
|
+
* If an adapter filter is configured, only items that pass the filter will be sent to adapters.
|
|
5992
|
+
* If an adapter throws, the error is dispatched as a telemetry error event.
|
|
5993
|
+
*
|
|
5994
|
+
* @returns Subscription to the telemetry item stream
|
|
5995
|
+
* @protected
|
|
5996
|
+
* @throws {Error} When the provider has not been initialized yet.
|
|
5997
|
+
*/
|
|
5998
|
+
_connectAdapters() {
|
|
5999
|
+
// Adapters can only be connected after the provider has completed initialization
|
|
6000
|
+
if (!this.#initialized) {
|
|
6001
|
+
throw new Error('TelemetryProvider is not initialized');
|
|
6002
|
+
}
|
|
6003
|
+
return this.#items.subscribe((item) => {
|
|
6004
|
+
const { adapter: adapterFilter } = this.#filters ?? {};
|
|
6005
|
+
// Apply adapter filter if configured - skip item if filter returns false
|
|
6006
|
+
if (adapterFilter && !adapterFilter(item)) {
|
|
6007
|
+
return;
|
|
6008
|
+
}
|
|
6009
|
+
// Iterate through all registered adapters
|
|
6010
|
+
for (const [identifier, adapter] of Object.entries(this.#adapters)) {
|
|
6011
|
+
// Explicit child exporters take precedence over inherited development exporters.
|
|
6012
|
+
if (adapter.fallback && this.#handledByChild.has(item)) {
|
|
6013
|
+
// Keep application-owned records out of parent fallback exporters.
|
|
6014
|
+
continue;
|
|
6015
|
+
}
|
|
6016
|
+
try {
|
|
6017
|
+
// Let the adapter process the telemetry item
|
|
6018
|
+
Promise.resolve(adapter.processItem(item));
|
|
6019
|
+
}
|
|
6020
|
+
catch (error) {
|
|
6021
|
+
// If processing fails, dispatch an error event
|
|
6022
|
+
this._dispatchError(new Error(`Failed to process telemetry item with adapter "${identifier}"`, {
|
|
6023
|
+
cause: error,
|
|
6024
|
+
}));
|
|
6025
|
+
}
|
|
6026
|
+
}
|
|
6027
|
+
});
|
|
6028
|
+
}
|
|
6029
|
+
/**
|
|
6030
|
+
* Relays telemetry data items to a target telemetry provider.
|
|
6031
|
+
*
|
|
6032
|
+
* Subscribes to the internal telemetry item stream and forwards each processed item
|
|
6033
|
+
* to the specified target provider by invoking its `track` method.
|
|
6034
|
+
* If a relay filter is configured, only items that pass the filter will be relayed.
|
|
6035
|
+
*
|
|
6036
|
+
* @param target - The telemetry provider to which telemetry data should be relayed.
|
|
6037
|
+
* @returns A Subscription object that can be used to unsubscribe from the relay.
|
|
6038
|
+
*/
|
|
6039
|
+
_relayTelemetryData(target) {
|
|
6040
|
+
// Subscribe to the processed stream and forward each item to the parent provider
|
|
6041
|
+
return this.#items.subscribe((item) => {
|
|
6042
|
+
const { relay: relayFilter } = this.#filters ?? {};
|
|
6043
|
+
// Apply relay filter if configured - skip item if filter returns false
|
|
6044
|
+
if (relayFilter && !relayFilter(item)) {
|
|
6045
|
+
return;
|
|
6046
|
+
}
|
|
6047
|
+
// Older/custom providers retain the original track-only relay contract.
|
|
6048
|
+
if (target.trackRelayed) {
|
|
6049
|
+
// Carry exporter ownership across intermediate providers without changing record metadata.
|
|
6050
|
+
const hasAdapters = this.#handledByChild.has(item) ||
|
|
6051
|
+
Object.values(this.#adapters).some((adapter) => !adapter.fallback);
|
|
6052
|
+
target.trackRelayed(item, hasAdapters);
|
|
6053
|
+
}
|
|
6054
|
+
else {
|
|
6055
|
+
target.track(item);
|
|
6056
|
+
}
|
|
6057
|
+
});
|
|
6058
|
+
}
|
|
6059
|
+
/**
|
|
6060
|
+
* Dispatches a telemetry event to the event provider, if available.
|
|
6061
|
+
*
|
|
6062
|
+
* @param item - The telemetry item to dispatch as an event
|
|
6063
|
+
* @protected
|
|
6064
|
+
*/
|
|
6065
|
+
_dispatchEntityEvent(item) {
|
|
6066
|
+
// Only dispatch when an event provider is configured
|
|
6067
|
+
if (this.#eventProvider) {
|
|
6068
|
+
// Wrap the telemetry item in a TelemetryEvent and dispatch it
|
|
6069
|
+
this.#eventProvider.dispatchEvent(new TelemetryEvent(item, this));
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
/**
|
|
6073
|
+
* Dispatches a telemetry error event using the internal event provider, if available.
|
|
6074
|
+
*
|
|
6075
|
+
* @param error - The error instance to be dispatched with the telemetry error event.
|
|
6076
|
+
* @protected
|
|
6077
|
+
*/
|
|
6078
|
+
_dispatchError(error) {
|
|
6079
|
+
// Only dispatch when an event provider is configured
|
|
6080
|
+
if (this.#eventProvider) {
|
|
6081
|
+
this.#eventProvider.dispatchEvent(new TelemetryErrorEvent(error, this));
|
|
6082
|
+
}
|
|
6676
6083
|
}
|
|
6677
6084
|
/**
|
|
6678
6085
|
* Tracks a telemetry item by type, merging the default scope with the provided scope.
|
|
@@ -6710,7 +6117,44 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
6710
6117
|
* @param hasAdapters - Whether an explicit child adapter has already handled this record.
|
|
6711
6118
|
*/
|
|
6712
6119
|
trackRelayed(item, hasAdapters) {
|
|
6713
|
-
this
|
|
6120
|
+
this._track(item, hasAdapters);
|
|
6121
|
+
}
|
|
6122
|
+
/**
|
|
6123
|
+
* Tracks a telemetry item by merging the default scope with the item's scope (if provided),
|
|
6124
|
+
* ensuring that all telemetry items have the correct scope context. The merged item is then
|
|
6125
|
+
* emitted to downstream handlers.
|
|
6126
|
+
*
|
|
6127
|
+
* @param item - The telemetry item to be tracked, conforming to the input schema of `TelemetryItemSchema`.
|
|
6128
|
+
* @param handledByChild - Whether fallback adapters should skip this relayed item.
|
|
6129
|
+
*
|
|
6130
|
+
* @protected
|
|
6131
|
+
*/
|
|
6132
|
+
async _track(item, handledByChild = false) {
|
|
6133
|
+
const mergedItem = mergeTelemetryItem(item, {
|
|
6134
|
+
scope: this.#defaultScope,
|
|
6135
|
+
});
|
|
6136
|
+
// Skip metadata application for TelemetryMetadataError items to prevent infinite recursion
|
|
6137
|
+
if (this.#metadata && mergedItem.name !== TelemetryItemNames.MetadataError) {
|
|
6138
|
+
applyMetadata(this.#metadata, {
|
|
6139
|
+
modules: this.#modules,
|
|
6140
|
+
item: mergedItem,
|
|
6141
|
+
}).subscribe((nextItem) => {
|
|
6142
|
+
// Async metadata resolution creates new objects; retain ownership on the emitted record.
|
|
6143
|
+
if (handledByChild) {
|
|
6144
|
+
this.#handledByChild.add(nextItem);
|
|
6145
|
+
}
|
|
6146
|
+
// Emit the item after applying metadata
|
|
6147
|
+
this.#items.next(nextItem);
|
|
6148
|
+
});
|
|
6149
|
+
}
|
|
6150
|
+
else {
|
|
6151
|
+
// Providers without metadata still need the same fallback precedence.
|
|
6152
|
+
if (handledByChild) {
|
|
6153
|
+
this.#handledByChild.add(mergedItem);
|
|
6154
|
+
}
|
|
6155
|
+
// If no metadata extractor is provided or item is TelemetryMetadataError, emit directly
|
|
6156
|
+
this.#items.next(mergedItem);
|
|
6157
|
+
}
|
|
6714
6158
|
}
|
|
6715
6159
|
/**
|
|
6716
6160
|
* Starts a metric measurement and returns a function to end the measurement.
|
|
@@ -6736,7 +6180,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
6736
6180
|
* provider.trackEvent({ name: 'button_click', metadata: { button: 'save' } });
|
|
6737
6181
|
*/
|
|
6738
6182
|
trackEvent(data) {
|
|
6739
|
-
this
|
|
6183
|
+
this._track(TelemetryEventSchema.parse({ ...data, type: TelemetryType.Event }));
|
|
6740
6184
|
}
|
|
6741
6185
|
/**
|
|
6742
6186
|
* Tracks a telemetry exception.
|
|
@@ -6750,7 +6194,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
6750
6194
|
* });
|
|
6751
6195
|
*/
|
|
6752
6196
|
trackException(data) {
|
|
6753
|
-
this
|
|
6197
|
+
this._track(TelemetryExceptionSchema.parse({ ...data, type: TelemetryType.Exception }));
|
|
6754
6198
|
}
|
|
6755
6199
|
/**
|
|
6756
6200
|
* Tracks a telemetry metric.
|
|
@@ -6760,7 +6204,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
6760
6204
|
* provider.trackMetric({ name: 'response_time', value: 123 });
|
|
6761
6205
|
*/
|
|
6762
6206
|
trackMetric(data) {
|
|
6763
|
-
this
|
|
6207
|
+
this._track(TelemetryMetricSchema.parse({ ...data, type: TelemetryType.Metric }));
|
|
6764
6208
|
}
|
|
6765
6209
|
/**
|
|
6766
6210
|
* Tracks a custom telemetry event.
|
|
@@ -6770,7 +6214,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
6770
6214
|
* provider.trackCustom({ name: 'custom_event', metadata: { foo: 'bar' } });
|
|
6771
6215
|
*/
|
|
6772
6216
|
trackCustom(data) {
|
|
6773
|
-
this
|
|
6217
|
+
this._track(TelemetryCustomEventSchema.parse({ ...data, type: TelemetryType.Custom }));
|
|
6774
6218
|
}
|
|
6775
6219
|
/**
|
|
6776
6220
|
* Retrieves a telemetry adapter by its identifier.
|
|
@@ -6780,7 +6224,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
6780
6224
|
* @returns The telemetry adapter corresponding to the given identifier, or `undefined` if not found.
|
|
6781
6225
|
*/
|
|
6782
6226
|
getAdapter(identifier) {
|
|
6783
|
-
const adapter = this.#
|
|
6227
|
+
const adapter = this.#adapters[identifier];
|
|
6784
6228
|
// Report a missing adapter as a telemetry exception rather than throwing
|
|
6785
6229
|
if (!adapter) {
|
|
6786
6230
|
this.trackException({
|
|
@@ -6799,17 +6243,10 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
6799
6243
|
* @returns True if the adapter exists, false otherwise.
|
|
6800
6244
|
*/
|
|
6801
6245
|
hasAdapter(identifier) {
|
|
6802
|
-
return identifier in this.#
|
|
6246
|
+
return identifier in this.#adapters;
|
|
6803
6247
|
}
|
|
6804
6248
|
}
|
|
6805
6249
|
|
|
6806
|
-
/**
|
|
6807
|
-
* Creates a reference-independent builder for either preparation or normal configuration.
|
|
6808
|
-
* @returns A fresh telemetry configuration builder.
|
|
6809
|
-
*/
|
|
6810
|
-
function createTelemetryConfigurator() {
|
|
6811
|
-
return new TelemetryConfigurator();
|
|
6812
|
-
}
|
|
6813
6250
|
/**
|
|
6814
6251
|
* Telemetry module definition for the Fusion Framework.
|
|
6815
6252
|
*
|
|
@@ -6827,9 +6264,7 @@ function createTelemetryConfigurator() {
|
|
|
6827
6264
|
*/
|
|
6828
6265
|
const module$2 = {
|
|
6829
6266
|
name: 'telemetry',
|
|
6830
|
-
|
|
6831
|
-
prepare: createTelemetryConfigurator,
|
|
6832
|
-
configure: createTelemetryConfigurator,
|
|
6267
|
+
configure: () => new TelemetryConfigurator(),
|
|
6833
6268
|
initialize: async (args) => {
|
|
6834
6269
|
const config = await args.config.createConfigAsync(args);
|
|
6835
6270
|
const event = args.hasModule('event') ? await args.requireInstance('event') : undefined;
|
|
@@ -6852,399 +6287,69 @@ const module$2 = {
|
|
|
6852
6287
|
};
|
|
6853
6288
|
|
|
6854
6289
|
/**
|
|
6855
|
-
*
|
|
6856
|
-
* configuration chatter, is dropped rather than projected.
|
|
6857
|
-
*/
|
|
6858
|
-
const terminalBoundaries = new Map([
|
|
6859
|
-
[
|
|
6860
|
-
ModuleConfiguratorEventName.ModuleInitialized,
|
|
6861
|
-
{ operation: 'initialize', perModule: true, failure: false },
|
|
6862
|
-
],
|
|
6863
|
-
[
|
|
6864
|
-
ModuleConfiguratorEventName.ModuleInitializeError,
|
|
6865
|
-
{ operation: 'initialize', perModule: true, failure: true },
|
|
6866
|
-
],
|
|
6867
|
-
[
|
|
6868
|
-
ModuleConfiguratorEventName.ModuleDisposed,
|
|
6869
|
-
{ operation: 'dispose', perModule: true, failure: false },
|
|
6870
|
-
],
|
|
6871
|
-
[
|
|
6872
|
-
ModuleConfiguratorEventName.ModuleDisposeError,
|
|
6873
|
-
{ operation: 'dispose', perModule: true, failure: true },
|
|
6874
|
-
],
|
|
6875
|
-
[
|
|
6876
|
-
ModuleConfiguratorEventName.HostInitialized,
|
|
6877
|
-
{ operation: 'initialize', perModule: false, failure: false },
|
|
6878
|
-
],
|
|
6879
|
-
[
|
|
6880
|
-
ModuleConfiguratorEventName.HostInitializeError,
|
|
6881
|
-
{ operation: 'initialize', perModule: false, failure: true },
|
|
6882
|
-
],
|
|
6883
|
-
[
|
|
6884
|
-
ModuleConfiguratorEventName.ModulesDisposed,
|
|
6885
|
-
{ operation: 'dispose', perModule: false, failure: false },
|
|
6886
|
-
],
|
|
6887
|
-
]);
|
|
6888
|
-
/**
|
|
6889
|
-
* Projects only terminal lifecycle events into allowlisted operational records.
|
|
6890
|
-
* Raw messages, arbitrary properties, errors, and dependency-wait chatter are never forwarded.
|
|
6891
|
-
* @param configurator - Lifecycle stream to observe, preferably before user configuration runs.
|
|
6892
|
-
* @param source - Host identity, or a lookup into the owning configuration before each record is captured.
|
|
6893
|
-
* @returns Terminal duration metrics and controlled failure exceptions.
|
|
6894
|
-
*/
|
|
6895
|
-
const mapConfiguratorEvents = (configurator, source = { hostId: crypto.randomUUID() }) => {
|
|
6896
|
-
// Project before buffering so raw lifecycle payloads never enter the diagnostic queue.
|
|
6897
|
-
return configurator.event$.pipe(mergeMap((event) => {
|
|
6898
|
-
// Hosts prefix event names with their class name; the boundary is the unprefixed name.
|
|
6899
|
-
const boundary = terminalBoundaries.get(event.name.split('::').at(-1) ?? '');
|
|
6900
|
-
// Only measured terminal boundaries belong to foundational operational telemetry.
|
|
6901
|
-
if (!boundary) {
|
|
6902
|
-
return [];
|
|
6903
|
-
}
|
|
6904
|
-
// Zero is a valid measurement; missing or invalid timings cannot form a duration record.
|
|
6905
|
-
if (event.metric === undefined || !Number.isFinite(event.metric) || event.metric < 0) {
|
|
6906
|
-
return [];
|
|
6907
|
-
}
|
|
6908
|
-
let module = 'host';
|
|
6909
|
-
// Module boundaries are attributed to a static, kebab-case module label.
|
|
6910
|
-
if (boundary.perModule) {
|
|
6911
|
-
const moduleName = event.properties?.moduleName;
|
|
6912
|
-
// Aggregate legacy errors cannot be attributed to an individual module.
|
|
6913
|
-
if (typeof moduleName !== 'string') {
|
|
6914
|
-
return [];
|
|
6915
|
-
}
|
|
6916
|
-
module = moduleName.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
6917
|
-
// Reject dynamic labels rather than admitting paths or payloads into record names.
|
|
6918
|
-
if (!operationalIdentifier.safeParse(module).success) {
|
|
6919
|
-
console.warn('Unsupported module identifier in lifecycle telemetry');
|
|
6920
|
-
return [];
|
|
6921
|
-
}
|
|
6922
|
-
}
|
|
6923
|
-
return createOperationalRecords({
|
|
6924
|
-
source: typeof source === 'function' ? source() : source,
|
|
6925
|
-
module,
|
|
6926
|
-
operation: boundary.operation,
|
|
6927
|
-
// Error boundaries fail by kind; host disposal isolates module failures and reports `failed`.
|
|
6928
|
-
outcome: boundary.failure || event.properties?.failed ? 'failure' : 'success',
|
|
6929
|
-
duration: event.metric,
|
|
6930
|
-
});
|
|
6931
|
-
}));
|
|
6932
|
-
};
|
|
6933
|
-
|
|
6934
|
-
/**
|
|
6935
|
-
* Startup records retained before a provider exists. One further slot is reserved for the
|
|
6936
|
-
* overflow summary, so a startup burst never retains more than 256 records.
|
|
6937
|
-
*/
|
|
6938
|
-
const STARTUP_RECORD_CAPACITY = 255;
|
|
6939
|
-
/**
|
|
6940
|
-
* Failure evidence: an error-level record, or a measurement whose outcome failed even though
|
|
6941
|
-
* the measurement itself is reported at a lower level.
|
|
6942
|
-
* @param record - Buffered startup record.
|
|
6943
|
-
* @returns Whether the record documents a failure.
|
|
6944
|
-
*/
|
|
6945
|
-
const isFailure = (record) => record.level >= TelemetryLevel.Error || record.diagnostic?.outcome === 'failure';
|
|
6946
|
-
/**
|
|
6947
|
-
* Bounded buffer for records captured before a telemetry provider exists.
|
|
6290
|
+
* Transforms events emitted by a given modules configurator into standardized telemetry items.
|
|
6948
6291
|
*
|
|
6949
|
-
*
|
|
6950
|
-
*
|
|
6951
|
-
* summary.
|
|
6292
|
+
* @param configurator - The modules configurator instance emitting events to be mapped.
|
|
6293
|
+
* @returns An observable stream of `TelemetryItem` objects, each representing a mapped event.
|
|
6952
6294
|
*
|
|
6953
|
-
*
|
|
6954
|
-
*
|
|
6955
|
-
*
|
|
6956
|
-
*
|
|
6957
|
-
* const { items, dropped } = buffer.drain();
|
|
6958
|
-
* ```
|
|
6959
|
-
*/
|
|
6960
|
-
class StartupBuffer {
|
|
6961
|
-
capacity;
|
|
6962
|
-
#items = [];
|
|
6963
|
-
#dropped = 0;
|
|
6964
|
-
/**
|
|
6965
|
-
* Creates an empty buffer.
|
|
6966
|
-
* @param capacity - Records retained before records are dropped.
|
|
6967
|
-
*/
|
|
6968
|
-
constructor(capacity = STARTUP_RECORD_CAPACITY) {
|
|
6969
|
-
this.capacity = capacity;
|
|
6970
|
-
}
|
|
6971
|
-
/**
|
|
6972
|
-
* Retains a record, preferring failure records once the buffer is full.
|
|
6973
|
-
* @param item - Sanitized startup record.
|
|
6974
|
-
*/
|
|
6975
|
-
push(item) {
|
|
6976
|
-
// Retain records until capacity.
|
|
6977
|
-
if (this.#items.length < this.capacity) {
|
|
6978
|
-
this.#items.push(item);
|
|
6979
|
-
return;
|
|
6980
|
-
}
|
|
6981
|
-
// Prefer failure evidence over successful startup measurements when capacity is exhausted.
|
|
6982
|
-
const replace = this.#items.findIndex((record) => !isFailure(record));
|
|
6983
|
-
// Replacing only non-failure entries prevents later successes from evicting failures.
|
|
6984
|
-
if (isFailure(item) && replace >= 0) {
|
|
6985
|
-
this.#items.splice(replace, 1, item);
|
|
6986
|
-
}
|
|
6987
|
-
this.#dropped++;
|
|
6988
|
-
}
|
|
6989
|
-
/**
|
|
6990
|
-
* Takes every retained record and the dropped count, leaving the buffer empty.
|
|
6991
|
-
* @returns Retained records, oldest first, and how many were dropped.
|
|
6992
|
-
*/
|
|
6993
|
-
drain() {
|
|
6994
|
-
const drained = { items: this.#items, dropped: this.#dropped };
|
|
6995
|
-
this.#items = [];
|
|
6996
|
-
this.#dropped = 0;
|
|
6997
|
-
return drained;
|
|
6998
|
-
}
|
|
6999
|
-
}
|
|
7000
|
-
|
|
7001
|
-
/**
|
|
7002
|
-
* Captures safe host lifecycle outcomes from before any telemetry provider exists.
|
|
7003
|
-
*
|
|
7004
|
-
* Telemetry is itself a module, so it cannot observe the startup it is part of. A host creates
|
|
7005
|
-
* a capture as soon as it is configured, before any user code runs, and attaches its
|
|
7006
|
-
* {@link items} to the telemetry configurator with `attachLifecycleItems`. The capture:
|
|
7007
|
-
* - buffers sanitized terminal records (bounded, failures preferred) until the provider
|
|
7008
|
-
* consumes {@link items}, then forwards live;
|
|
7009
|
-
* - when startup fails before any provider exists, hands the buffer to the host's parent,
|
|
7010
|
-
* creating the host failure record when the host never emitted one (for example, a failing
|
|
7011
|
-
* host configuration callback). The child's exporters and relay filter are not known yet,
|
|
7012
|
-
* so recovery relays without ownership; nothing is dropped.
|
|
6295
|
+
* The function listens to the `event$` observable of the configurator and maps each event to a `TelemetryItem`.
|
|
6296
|
+
* - If the event contains an `error`, the item is marked as an exception.
|
|
6297
|
+
* - If the event contains a `metric`, the item is marked as a metric with its value.
|
|
6298
|
+
* - Otherwise, the item is treated as a standard event.
|
|
7013
6299
|
*
|
|
7014
|
-
*
|
|
7015
|
-
* ```ts
|
|
7016
|
-
* const source = { source: 'portal', hostId: crypto.randomUUID() };
|
|
7017
|
-
* const capture = new StartupCapture(configurator, { source, parent });
|
|
7018
|
-
* configurator.addConfig({
|
|
7019
|
-
* module: telemetryModule,
|
|
7020
|
-
* prepare: (builder) => builder.setSource(source).setParent(parent).attachLifecycleItems(capture.items),
|
|
7021
|
-
* });
|
|
7022
|
-
* try {
|
|
7023
|
-
* await configureHost(configurator);
|
|
7024
|
-
* } catch (error) {
|
|
7025
|
-
* capture.fail();
|
|
7026
|
-
* throw error;
|
|
7027
|
-
* }
|
|
7028
|
-
* ```
|
|
6300
|
+
* The resulting telemetry items include event name, type, level, message, properties, and a fixed scope of `['configuration']`.
|
|
7029
6301
|
*/
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
/**
|
|
7043
|
-
* Starts capture immediately, not when the telemetry configuration eventually resolves.
|
|
7044
|
-
* @param host - Host lifecycle stream.
|
|
7045
|
-
* @param options - Host identity and the parent receiving records if startup fails.
|
|
7046
|
-
*/
|
|
7047
|
-
constructor(host, options = {}) {
|
|
7048
|
-
this.#options = { ...options, source: options.source ?? { hostId: crypto.randomUUID() } };
|
|
7049
|
-
this.items = new Observable((subscriber) => {
|
|
7050
|
-
const closed = this.#status === 'closed';
|
|
7051
|
-
// Join live delivery first, so nothing captured while the buffer drains is lost. A closed
|
|
7052
|
-
// capture's live subject is complete and would end the subscriber before the drain.
|
|
7053
|
-
const live = closed ? undefined : this.#live.subscribe(subscriber);
|
|
7054
|
-
// A capture that already closed stays closed; an active one now forwards live.
|
|
7055
|
-
if (!closed) {
|
|
7056
|
-
this.#status = 'live';
|
|
7057
|
-
}
|
|
7058
|
-
const { items, dropped } = this.#buffer.drain();
|
|
7059
|
-
// Release the bounded startup buffer exactly once, to the provider that consumes it.
|
|
7060
|
-
for (const item of items) {
|
|
7061
|
-
subscriber.next(item);
|
|
7062
|
-
}
|
|
7063
|
-
// Overflow is reported once, alongside the records that survived it.
|
|
7064
|
-
if (dropped) {
|
|
7065
|
-
subscriber.next(this.#overflowSummary(dropped));
|
|
7066
|
-
}
|
|
7067
|
-
// A late consumer of a closed capture receives the buffer and nothing more.
|
|
7068
|
-
if (closed) {
|
|
7069
|
-
subscriber.complete();
|
|
7070
|
-
}
|
|
7071
|
-
return () => {
|
|
7072
|
-
live?.unsubscribe();
|
|
7073
|
-
this.#close();
|
|
7074
|
-
};
|
|
7075
|
-
});
|
|
7076
|
-
this.#subscription = mapConfiguratorEvents(host, this.#options.source).subscribe({
|
|
7077
|
-
next: (item) => {
|
|
7078
|
-
this.#capture(item);
|
|
7079
|
-
// Recover synchronously: a host rolling back completes its stream right after this record.
|
|
7080
|
-
if (item.name === 'fusion.host.initialize.failure') {
|
|
7081
|
-
this.fail();
|
|
7082
|
-
}
|
|
7083
|
-
},
|
|
7084
|
-
error: () => {
|
|
7085
|
-
console.warn('Startup telemetry capture failed');
|
|
7086
|
-
this.fail();
|
|
7087
|
-
},
|
|
7088
|
-
complete: () => {
|
|
7089
|
-
// Keep the buffer: a provider created after completion may still drain it.
|
|
7090
|
-
this.#status = 'closed';
|
|
7091
|
-
this.#live.complete();
|
|
6302
|
+
const mapConfiguratorEvents = (
|
|
6303
|
+
// biome-ignore lint/suspicious/noExplicitAny: must be any to support all module types
|
|
6304
|
+
configurator) => {
|
|
6305
|
+
// Map each configurator event into a standardized telemetry item shape
|
|
6306
|
+
return configurator.event$.pipe(map((event) => {
|
|
6307
|
+
const item = {
|
|
6308
|
+
name: event.name,
|
|
6309
|
+
type: TelemetryType.Event,
|
|
6310
|
+
level: Number(event.level),
|
|
6311
|
+
properties: {
|
|
6312
|
+
message: event.message,
|
|
6313
|
+
...event.properties,
|
|
7092
6314
|
},
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
if (this.#status === 'closed') {
|
|
7103
|
-
return;
|
|
7104
|
-
}
|
|
7105
|
-
// Configuration callback errors occur before the module host can emit its own boundary.
|
|
7106
|
-
if (!this.#hostReported) {
|
|
7107
|
-
// Keep the failure duration and sanitized exception together.
|
|
7108
|
-
for (const record of createOperationalRecords({
|
|
7109
|
-
source: this.#options.source,
|
|
7110
|
-
module: 'host',
|
|
7111
|
-
operation: 'initialize',
|
|
7112
|
-
outcome: 'failure',
|
|
7113
|
-
duration: performance.now() - this.#startedAt,
|
|
7114
|
-
})) {
|
|
7115
|
-
this.#capture(record);
|
|
7116
|
-
}
|
|
7117
|
-
}
|
|
7118
|
-
// An attached provider keeps its records through partial cleanup and sink flush.
|
|
7119
|
-
if (this.#status === 'live') {
|
|
7120
|
-
return;
|
|
7121
|
-
}
|
|
7122
|
-
const { items, dropped } = this.#buffer.drain();
|
|
7123
|
-
this.#close();
|
|
7124
|
-
this.#recover(items, dropped);
|
|
7125
|
-
}
|
|
7126
|
-
/** Stops capture when configuration explicitly opts out before initialization. */
|
|
7127
|
-
stop() {
|
|
7128
|
-
this.#buffer.drain();
|
|
7129
|
-
this.#close();
|
|
7130
|
-
}
|
|
7131
|
-
/**
|
|
7132
|
-
* Records one sanitized record: buffered until a provider consumes, then forwarded live.
|
|
7133
|
-
* @param item - Sanitized lifecycle record.
|
|
7134
|
-
*/
|
|
7135
|
-
#capture(item) {
|
|
7136
|
-
// Avoid manufacturing a second host outcome once the host reported its own.
|
|
7137
|
-
if (item.diagnostic?.module === 'host' && item.diagnostic.operation === 'initialize') {
|
|
7138
|
-
this.#hostReported = true;
|
|
7139
|
-
}
|
|
7140
|
-
// Live delivery avoids retaining records after the provider is ready.
|
|
7141
|
-
if (this.#status === 'live') {
|
|
7142
|
-
this.#live.next(item);
|
|
7143
|
-
}
|
|
7144
|
-
else if (this.#status === 'capturing') {
|
|
7145
|
-
this.#buffer.push(item);
|
|
7146
|
-
}
|
|
7147
|
-
}
|
|
7148
|
-
/** Ends capture: releases the host subscription and completes live delivery. */
|
|
7149
|
-
#close() {
|
|
7150
|
-
this.#status = 'closed';
|
|
7151
|
-
this.#subscription?.unsubscribe();
|
|
7152
|
-
this.#live.complete();
|
|
7153
|
-
}
|
|
7154
|
-
/**
|
|
7155
|
-
* Hands buffered startup records to the parent after startup failed before a provider existed.
|
|
7156
|
-
* @param items - Records retained before recovery.
|
|
7157
|
-
* @param dropped - Records discarded at capacity.
|
|
7158
|
-
*/
|
|
7159
|
-
#recover(items, dropped) {
|
|
7160
|
-
const { parent } = this.#options;
|
|
7161
|
-
let fallbackFailureReported = false;
|
|
7162
|
-
// Route only the already sanitized, bounded buffer.
|
|
7163
|
-
for (const item of items) {
|
|
7164
|
-
// Delivered or intentionally filtered records need no local fallback.
|
|
7165
|
-
if (this.#relayToParent(item)) {
|
|
7166
|
-
// Move on to the next buffered record.
|
|
7167
|
-
continue;
|
|
7168
|
-
}
|
|
7169
|
-
// A parent that cannot honour ownership is reported once, not once per record.
|
|
7170
|
-
if (parent && !fallbackFailureReported) {
|
|
7171
|
-
fallbackFailureReported = true;
|
|
7172
|
-
console.warn('Startup telemetry fallback delivery failed or could not preserve ownership');
|
|
7173
|
-
}
|
|
7174
|
-
else if (!parent && item.name === 'fusion.host.initialize.failure') {
|
|
7175
|
-
console.warn('Host initialization failed before telemetry became available');
|
|
7176
|
-
}
|
|
7177
|
-
}
|
|
7178
|
-
// Make overflow visible without retaining or forwarding dropped payloads.
|
|
7179
|
-
if (dropped) {
|
|
7180
|
-
const summary = this.#overflowSummary(dropped);
|
|
7181
|
-
// Local, sanitized health reporting is the last resort when no parent can take it.
|
|
7182
|
-
if (!this.#relayToParent(summary)) {
|
|
7183
|
-
console.warn('Startup telemetry queue overflow', summary.properties);
|
|
7184
|
-
}
|
|
7185
|
-
}
|
|
7186
|
-
}
|
|
7187
|
-
/**
|
|
7188
|
-
* Delivers one startup record to the host's parent.
|
|
7189
|
-
* @param item - Sanitized startup record.
|
|
7190
|
-
* @returns Whether the record was delivered or intentionally filtered.
|
|
7191
|
-
*/
|
|
7192
|
-
#relayToParent(item) {
|
|
7193
|
-
const { parent } = this.#options;
|
|
7194
|
-
// Without a parent there is nowhere to recover to.
|
|
7195
|
-
if (!parent) {
|
|
7196
|
-
return false;
|
|
7197
|
-
}
|
|
7198
|
-
try {
|
|
7199
|
-
// The child's exporters are not configured yet, so it cannot claim ownership.
|
|
7200
|
-
return relayRecord(parent, item, { ownsExport: false });
|
|
6315
|
+
scope: ['configuration'],
|
|
6316
|
+
};
|
|
6317
|
+
// An event carrying an error is reported as a telemetry exception
|
|
6318
|
+
if (event.error) {
|
|
6319
|
+
return {
|
|
6320
|
+
...item,
|
|
6321
|
+
type: TelemetryType.Exception,
|
|
6322
|
+
exception: event.error,
|
|
6323
|
+
};
|
|
7201
6324
|
}
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
return
|
|
6325
|
+
// An event carrying a metric value is reported as a telemetry metric
|
|
6326
|
+
if (event.metric) {
|
|
6327
|
+
return {
|
|
6328
|
+
...item,
|
|
6329
|
+
type: TelemetryType.Metric,
|
|
6330
|
+
value: event.metric,
|
|
6331
|
+
};
|
|
7205
6332
|
}
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
* @param dropped - Number of records discarded.
|
|
7210
|
-
* @returns A safe overflow event carrying only the dropped count.
|
|
7211
|
-
*/
|
|
7212
|
-
#overflowSummary(dropped) {
|
|
7213
|
-
const { source } = this.#options;
|
|
7214
|
-
return {
|
|
7215
|
-
name: 'fusion.telemetry.startup-overflow',
|
|
7216
|
-
type: TelemetryType.Event,
|
|
7217
|
-
level: TelemetryLevel.Warning,
|
|
7218
|
-
properties: { dropped },
|
|
7219
|
-
// Scope the summary like the records it stands in for.
|
|
7220
|
-
scope: operationalScope(source),
|
|
7221
|
-
diagnostic: createDiagnosticContext({
|
|
7222
|
-
source,
|
|
7223
|
-
module: 'telemetry',
|
|
7224
|
-
operation: 'startup-overflow',
|
|
7225
|
-
outcome: 'failure',
|
|
7226
|
-
}),
|
|
7227
|
-
};
|
|
7228
|
-
}
|
|
7229
|
-
}
|
|
6333
|
+
return item;
|
|
6334
|
+
}));
|
|
6335
|
+
};
|
|
7230
6336
|
|
|
7231
6337
|
/**
|
|
7232
6338
|
* Enables telemetry for a given module configurator.
|
|
7233
6339
|
*
|
|
7234
|
-
* Registers the telemetry module and an optional configuration callback. Framework, app and
|
|
7235
|
-
* widget hosts already capture their own startup lifecycle; `attachConfiguratorEvents` only
|
|
7236
|
-
* matters for custom hosts and for opting out.
|
|
7237
|
-
*
|
|
7238
6340
|
* @param configurator - The module configurator instance to which telemetry should be attached.
|
|
7239
6341
|
* @param options - Optional configuration for telemetry setup.
|
|
7240
|
-
* @param options.attachConfiguratorEvents -
|
|
7241
|
-
* its host already does; `false` stops lifecycle records reaching this provider. A host still
|
|
7242
|
-
* reports a failed startup to its parent.
|
|
6342
|
+
* @param options.attachConfiguratorEvents - If true, attaches configurator events to the telemetry builder.
|
|
7243
6343
|
* @param options.configure - An optional callback to further configure the telemetry builder. Can be synchronous or asynchronous.
|
|
7244
6344
|
*
|
|
7245
6345
|
* @remarks
|
|
7246
|
-
*
|
|
7247
|
-
*
|
|
6346
|
+
* This function adds telemetry configuration to the provided configurator. It supports attaching configurator events
|
|
6347
|
+
* and allows for additional custom configuration via the `configure` callback.
|
|
6348
|
+
*
|
|
6349
|
+
* @remarks
|
|
6350
|
+
* `attachConfiguratorEvents` allows for the automatic attachment of configurator events to the telemetry builder,
|
|
6351
|
+
* ensuring that relevant events are captured and processed. __NOTE__ if the configurator crashes before the telemetry
|
|
6352
|
+
* builder is fully initialized, some events may be missed.
|
|
7248
6353
|
*
|
|
7249
6354
|
* @example
|
|
7250
6355
|
* ```typescript
|
|
@@ -7264,19 +6369,18 @@ class StartupCapture {
|
|
|
7264
6369
|
const enableTelemetry = (
|
|
7265
6370
|
// biome-ignore lint/suspicious/noExplicitAny: must be any to support all module types
|
|
7266
6371
|
configurator, options) => {
|
|
7267
|
-
const attach = options?.attachConfiguratorEvents;
|
|
7268
6372
|
configurator.addConfig({
|
|
7269
6373
|
module: module$2,
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
builder.
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
}
|
|
7279
|
-
|
|
6374
|
+
configure: async (builder, ref) => {
|
|
6375
|
+
// Attach configurator events to the telemetry builder only when opted in
|
|
6376
|
+
{
|
|
6377
|
+
builder.attachItems(mapConfiguratorEvents(configurator));
|
|
6378
|
+
}
|
|
6379
|
+
// Run the caller-supplied configure callback when one was provided
|
|
6380
|
+
if (options?.configure) {
|
|
6381
|
+
await Promise.resolve(options.configure(builder, ref));
|
|
6382
|
+
}
|
|
6383
|
+
},
|
|
7280
6384
|
});
|
|
7281
6385
|
};
|
|
7282
6386
|
|
|
@@ -9610,9 +8714,6 @@ class ServiceDiscoveryClient {
|
|
|
9610
8714
|
#query;
|
|
9611
8715
|
#failure;
|
|
9612
8716
|
#lastKnownGood;
|
|
9613
|
-
#diagnostic = new Subject();
|
|
9614
|
-
/** {@inheritDoc IServiceDiscoveryClient.diagnostic$} */
|
|
9615
|
-
diagnostic$ = this.#diagnostic.asObservable();
|
|
9616
8717
|
/** Sub-path appended to the HTTP client's base URI when fetching services. */
|
|
9617
8718
|
endpoint;
|
|
9618
8719
|
/** HTTP client used to call the service discovery API. */
|
|
@@ -9639,40 +8740,32 @@ class ServiceDiscoveryClient {
|
|
|
9639
8740
|
}
|
|
9640
8741
|
/** {@inheritDoc IServiceDiscoveryClient.resolveServices} */
|
|
9641
8742
|
async resolveServices(allow_cache) {
|
|
9642
|
-
const started = performance.now();
|
|
9643
8743
|
// Fail fast while cooling down so retry/reload/render loops never reach the network
|
|
9644
8744
|
if (this.#failure && Date.now() < this.#failure.retryAt) {
|
|
9645
8745
|
return this.#resolveFallback(new ServiceDiscoveryUnavailableError({
|
|
9646
8746
|
cause: this.#failure.error,
|
|
9647
8747
|
retryAt: this.#failure.retryAt,
|
|
9648
8748
|
attempt: this.#failure.attempt,
|
|
9649
|
-
})
|
|
8749
|
+
}));
|
|
9650
8750
|
}
|
|
9651
8751
|
const startedAt = Date.now();
|
|
9652
|
-
let record;
|
|
9653
8752
|
try {
|
|
9654
8753
|
// firstValueFrom returns the cached snapshot; lastValueFrom waits for
|
|
9655
8754
|
// the freshest value from the query
|
|
9656
8755
|
const fn = allow_cache ? firstValueFrom : lastValueFrom;
|
|
9657
|
-
record = await fn(this.#query.query());
|
|
8756
|
+
const record = await fn(this.#query.query());
|
|
8757
|
+
// Only a fresh response proves discovery works; a cached (possibly expired) snapshot must
|
|
8758
|
+
// not reset the failure streak or renew the age of the fallback list
|
|
8759
|
+
if (record.status === 'complete') {
|
|
8760
|
+
this.#failure = undefined;
|
|
8761
|
+
this.#lastKnownGood = { services: record.value, receivedAt: Date.now() };
|
|
8762
|
+
}
|
|
8763
|
+
return record.value;
|
|
9658
8764
|
}
|
|
9659
8765
|
catch (error) {
|
|
9660
8766
|
this.#registerFailure(error, startedAt);
|
|
9661
|
-
return this.#resolveFallback(error
|
|
9662
|
-
}
|
|
9663
|
-
// Only a fresh response proves discovery works; a cached (possibly expired) snapshot must
|
|
9664
|
-
// not reset the failure streak or renew the age of the fallback list
|
|
9665
|
-
if (record.status === 'complete') {
|
|
9666
|
-
this.#failure = undefined;
|
|
9667
|
-
this.#lastKnownGood = { services: record.value, receivedAt: Date.now() };
|
|
9668
|
-
}
|
|
9669
|
-
this.#emit({
|
|
9670
|
-
type: 'resolve',
|
|
9671
|
-
outcome: 'success',
|
|
9672
|
-
origin: record.status === 'complete' ? 'network' : 'cache',
|
|
9673
|
-
duration: performance.now() - started,
|
|
9674
|
-
});
|
|
9675
|
-
return record.value;
|
|
8767
|
+
return this.#resolveFallback(error);
|
|
8768
|
+
}
|
|
9676
8769
|
}
|
|
9677
8770
|
/**
|
|
9678
8771
|
* Records a failed request and schedules the next allowed request time.
|
|
@@ -9705,43 +8798,17 @@ class ServiceDiscoveryClient {
|
|
|
9705
8798
|
* otherwise rethrows the given error.
|
|
9706
8799
|
*
|
|
9707
8800
|
* @param error - Error to throw when no usable fallback exists.
|
|
9708
|
-
* @param started - `performance.now()` timestamp when resolution started.
|
|
9709
|
-
* @param failedOrigin - Classification reported when no fallback can be served.
|
|
9710
8801
|
* @returns The last known good service list.
|
|
9711
8802
|
* @throws The provided `error` when no service list younger than 24 hours exists.
|
|
9712
8803
|
*/
|
|
9713
|
-
#resolveFallback(error
|
|
8804
|
+
#resolveFallback(error) {
|
|
9714
8805
|
const lastKnownGood = this.#lastKnownGood;
|
|
9715
8806
|
// Prefer a recent service list over an error so callers have nothing to retry
|
|
9716
8807
|
if (lastKnownGood && Date.now() - lastKnownGood.receivedAt <= staleIfErrorMs) {
|
|
9717
|
-
this.#emit({
|
|
9718
|
-
type: 'resolve',
|
|
9719
|
-
outcome: 'success',
|
|
9720
|
-
origin: 'fallback',
|
|
9721
|
-
duration: performance.now() - started,
|
|
9722
|
-
});
|
|
9723
8808
|
return lastKnownGood.services;
|
|
9724
8809
|
}
|
|
9725
|
-
this.#emit({
|
|
9726
|
-
type: 'resolve',
|
|
9727
|
-
outcome: 'failure',
|
|
9728
|
-
origin: failedOrigin,
|
|
9729
|
-
duration: performance.now() - started,
|
|
9730
|
-
});
|
|
9731
8810
|
throw error;
|
|
9732
8811
|
}
|
|
9733
|
-
/**
|
|
9734
|
-
* Publishes a safe diagnostic outcome without letting an observer affect resolution.
|
|
9735
|
-
* @param diagnostic - Classified outcome without keys, URIs, scopes or errors.
|
|
9736
|
-
*/
|
|
9737
|
-
#emit(diagnostic) {
|
|
9738
|
-
try {
|
|
9739
|
-
this.#diagnostic.next(diagnostic);
|
|
9740
|
-
}
|
|
9741
|
-
catch {
|
|
9742
|
-
// Observers are diagnostic only; resolution results must stay unchanged.
|
|
9743
|
-
}
|
|
9744
|
-
}
|
|
9745
8812
|
/** {@inheritDoc IServiceDiscoveryClient.resolveService} */
|
|
9746
8813
|
async resolveService(key, allow_cache) {
|
|
9747
8814
|
const services = await this.resolveServices(allow_cache);
|
|
@@ -9749,7 +8816,6 @@ class ServiceDiscoveryClient {
|
|
|
9749
8816
|
const service = services.find((s) => s.key === key);
|
|
9750
8817
|
// Fail loudly when the requested key doesn't resolve to a known service
|
|
9751
8818
|
if (!service) {
|
|
9752
|
-
this.#emit({ type: 'unknown-service' });
|
|
9753
8819
|
throw Error(`Failed to resolve service, invalid key [${key}]`);
|
|
9754
8820
|
}
|
|
9755
8821
|
return service;
|
|
@@ -9943,7 +9009,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
9943
9009
|
}
|
|
9944
9010
|
|
|
9945
9011
|
// Generated by genversion.
|
|
9946
|
-
const version$1 = '10.
|
|
9012
|
+
const version$1 = '10.2.0';
|
|
9947
9013
|
|
|
9948
9014
|
/**
|
|
9949
9015
|
* Default implementation of {@link IServiceDiscoveryProvider}.
|
|
@@ -9966,67 +9032,6 @@ class ServiceDiscoveryProvider extends BaseModuleProvider {
|
|
|
9966
9032
|
});
|
|
9967
9033
|
this.config = config;
|
|
9968
9034
|
this._http = _http;
|
|
9969
|
-
this._addTeardown(() => {
|
|
9970
|
-
this.#diagnostics?.unsubscribe();
|
|
9971
|
-
this.#telemetry.disconnect();
|
|
9972
|
-
});
|
|
9973
|
-
}
|
|
9974
|
-
#telemetry = createOperationalTelemetry('service-discovery');
|
|
9975
|
-
#diagnostics;
|
|
9976
|
-
/**
|
|
9977
|
-
* Connects optional operational telemetry for the client this provider owns.
|
|
9978
|
-
*
|
|
9979
|
-
* Records one `fusion.service-discovery.resolve.duration` per network, fallback or cooldown
|
|
9980
|
-
* resolution, plus `fallback`, `cooldown` and `unknown-service` notifications. Cached
|
|
9981
|
-
* resolutions record only a Debug-level `cache-hit` notification, so hosts filtering at
|
|
9982
|
-
* Information or above export nothing for them. No keys,
|
|
9983
|
-
* URIs, scopes or errors are recorded, and HTTP attempts are left to HTTP telemetry.
|
|
9984
|
-
* Calling it again replaces the provider; `undefined` records nothing until reconnected.
|
|
9985
|
-
*
|
|
9986
|
-
* @param telemetry - Host telemetry provider, or `undefined` to record nothing.
|
|
9987
|
-
*/
|
|
9988
|
-
connectTelemetry(telemetry) {
|
|
9989
|
-
this.#telemetry.connect(telemetry);
|
|
9990
|
-
const diagnostic$ = this.client.diagnostic$;
|
|
9991
|
-
// One subscription serves every connection; custom clients without a stream are skipped.
|
|
9992
|
-
if (this.#diagnostics || !this.#telemetry.connected || !diagnostic$) {
|
|
9993
|
-
return;
|
|
9994
|
-
}
|
|
9995
|
-
const report = this.#telemetry.report;
|
|
9996
|
-
this.#diagnostics = diagnostic$.subscribe((diagnostic) => {
|
|
9997
|
-
// An unmatched key is a notification only; it has no measured resolution of its own.
|
|
9998
|
-
if (diagnostic.type === 'unknown-service') {
|
|
9999
|
-
report({
|
|
10000
|
-
notification: true,
|
|
10001
|
-
operation: 'unknown-service',
|
|
10002
|
-
outcome: 'failure',
|
|
10003
|
-
level: TelemetryLevel.Warning,
|
|
10004
|
-
});
|
|
10005
|
-
return;
|
|
10006
|
-
}
|
|
10007
|
-
const { outcome, origin, duration } = diagnostic;
|
|
10008
|
-
// Cache hits dominate lookup volume and carry no diagnostic value at default levels,
|
|
10009
|
-
// so they are a single Debug notification rather than a measured resolution.
|
|
10010
|
-
if (origin === 'cache') {
|
|
10011
|
-
report({
|
|
10012
|
-
notification: true,
|
|
10013
|
-
operation: 'cache-hit',
|
|
10014
|
-
outcome,
|
|
10015
|
-
level: TelemetryLevel.Debug,
|
|
10016
|
-
});
|
|
10017
|
-
return;
|
|
10018
|
-
}
|
|
10019
|
-
report({ operation: 'resolve', outcome, duration });
|
|
10020
|
-
// Network resolutions are fully described by the measurement and the HTTP attempt.
|
|
10021
|
-
if (origin !== 'network') {
|
|
10022
|
-
report({
|
|
10023
|
-
notification: true,
|
|
10024
|
-
operation: origin,
|
|
10025
|
-
outcome,
|
|
10026
|
-
level: TelemetryLevel.Warning,
|
|
10027
|
-
});
|
|
10028
|
-
}
|
|
10029
|
-
});
|
|
10030
9035
|
}
|
|
10031
9036
|
/** {@inheritDoc IServiceDiscoveryProvider.client} */
|
|
10032
9037
|
get client() {
|
|
@@ -10061,11 +9066,6 @@ class ServiceDiscoveryProvider extends BaseModuleProvider {
|
|
|
10061
9066
|
}
|
|
10062
9067
|
}
|
|
10063
9068
|
|
|
10064
|
-
/**
|
|
10065
|
-
* Providers this module created, so host disposal tears down only its own instances and
|
|
10066
|
-
* their telemetry, never a provider supplied or shared by another host.
|
|
10067
|
-
*/
|
|
10068
|
-
const ownedProviders = new WeakMap();
|
|
10069
9069
|
/**
|
|
10070
9070
|
* Module identifier used to register and look up the Service Discovery module
|
|
10071
9071
|
* within the Fusion Framework module system.
|
|
@@ -10106,20 +9106,7 @@ const module$1 = {
|
|
|
10106
9106
|
// service discovery requires http module to be initialized
|
|
10107
9107
|
const httpModule = await requireInstance('http');
|
|
10108
9108
|
// return service discovery provider
|
|
10109
|
-
|
|
10110
|
-
ownedProviders.set(provider, provider);
|
|
10111
|
-
return provider;
|
|
10112
|
-
},
|
|
10113
|
-
postInitialize: async ({ instance, modules, ref }) => {
|
|
10114
|
-
// A child reusing its parent's client leaves reporting to the parent, so each
|
|
10115
|
-
// resolution is recorded once, by the host that owns the client.
|
|
10116
|
-
if (instance.client !== ref?.serviceDiscovery?.client) {
|
|
10117
|
-
instance.connectTelemetry?.(modules.telemetry);
|
|
10118
|
-
}
|
|
10119
|
-
},
|
|
10120
|
-
dispose: ({ instance }) => {
|
|
10121
|
-
ownedProviders.get(instance)?.dispose();
|
|
10122
|
-
ownedProviders.delete(instance);
|
|
9109
|
+
return new ServiceDiscoveryProvider(config, httpModule);
|
|
10123
9110
|
},
|
|
10124
9111
|
};
|
|
10125
9112
|
/**
|
|
@@ -10622,7 +9609,7 @@ async function registerServiceWorker(framework) {
|
|
|
10622
9609
|
}
|
|
10623
9610
|
|
|
10624
9611
|
// Generated by genversion.
|
|
10625
|
-
const version = '5.2.4
|
|
9612
|
+
const version = '5.2.4';
|
|
10626
9613
|
|
|
10627
9614
|
// Allow dynamic import without vite
|
|
10628
9615
|
const importWithoutVite = (path) => import(/* @vite-ignore */ path);
|
|
@@ -10642,7 +9629,7 @@ enableServiceDiscovery(configurator, async (builder) => {
|
|
|
10642
9629
|
});
|
|
10643
9630
|
// Avoid interactive Entra ID authentication when the SPA runs in CI or Playwright.
|
|
10644
9631
|
if (isEnabledEnvValue(import.meta.env.FUSION_SPA_MSAL_MOCK)) {
|
|
10645
|
-
const { enableMsalMock } = await import('../
|
|
9632
|
+
const { enableMsalMock } = await import('../mock-Do8FKTMU.js');
|
|
10646
9633
|
const mockServerUrl = import.meta.env.FUSION_SPA_MSAL_MOCK_SERVER_URL;
|
|
10647
9634
|
enableMsalMock(configurator, (builder) => {
|
|
10648
9635
|
// The SPA owns the HTTP adapter; MSAL owns applying the returned token and account.
|
|
@@ -10665,12 +9652,12 @@ else {
|
|
|
10665
9652
|
}
|
|
10666
9653
|
// Initialize tracing before app startup so network requests and logs share active trace context.
|
|
10667
9654
|
if (import.meta.env.FUSION_SPA_TELEMETRY_OTLP_TRACES_ENDPOINT) {
|
|
10668
|
-
const { enableDevServerTracing } = await import('../enable-dev-server-tracing-
|
|
9655
|
+
const { enableDevServerTracing } = await import('../enable-dev-server-tracing-B9rBXrzF.js');
|
|
10669
9656
|
enableDevServerTracing(new URL(import.meta.env.FUSION_SPA_TELEMETRY_OTLP_TRACES_ENDPOINT, window.location.origin).href);
|
|
10670
9657
|
}
|
|
10671
9658
|
// The dev-server environment bridge owns activation; the SPA only consumes its endpoint.
|
|
10672
9659
|
const devServerOtlpAdapter = import.meta.env.FUSION_SPA_TELEMETRY_OTLP_ENDPOINT
|
|
10673
|
-
? new (await import('../
|
|
9660
|
+
? new (await import('../otlp-Dq76ZTYm.js')).OtlpAdapter({
|
|
10674
9661
|
url: new URL(import.meta.env.FUSION_SPA_TELEMETRY_OTLP_ENDPOINT, window.location.origin).href,
|
|
10675
9662
|
fallback: true,
|
|
10676
9663
|
serviceName: 'fusion.dev-server',
|