@equinor/fusion-framework-vite-plugin-spa 4.0.8 → 4.0.10

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.
@@ -5218,6 +5218,79 @@ class BaseConfigBuilder {
5218
5218
  }
5219
5219
  }
5220
5220
 
5221
+ /**
5222
+ * Error thrown when a required module fails to initialize within the expected
5223
+ * timeout window.
5224
+ *
5225
+ * Indicates a circular dependency or a module that never resolves.
5226
+ */
5227
+ class RequiredModuleTimeoutError extends Error {
5228
+ constructor() {
5229
+ super('Module initialization timed out');
5230
+ this.name = 'RequiredModuleTimeoutError';
5231
+ }
5232
+ }
5233
+
5234
+ /** Shared base segment for module configurator lifecycle event names. */
5235
+ const ModuleConfiguratorEventBaseName = 'ModuleConfigurator';
5236
+ /**
5237
+ * Event names emitted by `ModulesConfigurator` and its lifecycle phase helpers.
5238
+ *
5239
+ * Use this map instead of inline string literals when emitting or filtering
5240
+ * configurator lifecycle events. Each value follows the
5241
+ * `ModuleConfigurator.{name}.{state}` convention. The emitted names are still
5242
+ * prefixed by `ModulesConfigurator::_registerEvent` at runtime.
5243
+ */
5244
+ const ModuleConfiguratorEventName = {
5245
+ ModuleConfigAdded: `${ModuleConfiguratorEventBaseName}.module.configAdded`,
5246
+ OnConfiguredAdded: `${ModuleConfiguratorEventBaseName}.onConfigured.added`,
5247
+ OnInitializedAdded: `${ModuleConfiguratorEventBaseName}.onInitialized.added`,
5248
+ PluginAdded: `${ModuleConfiguratorEventBaseName}.plugin.added`,
5249
+ InitializeConfigLoaded: `${ModuleConfiguratorEventBaseName}.config.loaded`,
5250
+ InitializeInstanceInitialized: `${ModuleConfiguratorEventBaseName}.instance.initialized`,
5251
+ Initialize: `${ModuleConfiguratorEventBaseName}.initialize.complete`,
5252
+ ConfiguratorCreated: `${ModuleConfiguratorEventBaseName}.configurator.created`,
5253
+ ConfiguratorFailed: `${ModuleConfiguratorEventBaseName}.configurator.failed`,
5254
+ ModulePostConfigured: `${ModuleConfiguratorEventBaseName}.module.postConfigured`,
5255
+ ModulePostConfigureError: `${ModuleConfiguratorEventBaseName}.module.postConfigureError`,
5256
+ PostConfigureHooks: `${ModuleConfiguratorEventBaseName}.postConfigureHooks.started`,
5257
+ PostConfigureHooksComplete: `${ModuleConfiguratorEventBaseName}.postConfigureHooks.complete`,
5258
+ PostConfigureHooksError: `${ModuleConfiguratorEventBaseName}.postConfigureHooks.error`,
5259
+ ModuleInitializeError: `${ModuleConfiguratorEventBaseName}.module.initializeError`,
5260
+ ModuleInitializing: `${ModuleConfiguratorEventBaseName}.module.initializing`,
5261
+ ProviderNotBaseModuleProvider: `${ModuleConfiguratorEventBaseName}.provider.invalidBase`,
5262
+ ProviderVersionWarning: `${ModuleConfiguratorEventBaseName}.provider.versionMissing`,
5263
+ ModuleInitialized: `${ModuleConfiguratorEventBaseName}.module.initialized`,
5264
+ RequireInstanceModuleNotDefined: `${ModuleConfiguratorEventBaseName}.requireInstance.moduleNotDefined`,
5265
+ RequireInstanceModuleAlreadyInitialized: `${ModuleConfiguratorEventBaseName}.requireInstance.moduleAlreadyInitialized`,
5266
+ RequireInstanceAwaitingModule: `${ModuleConfiguratorEventBaseName}.requireInstance.awaitingModule`,
5267
+ RequireInstanceTimeout: `${ModuleConfiguratorEventBaseName}.requireInstance.timeout`,
5268
+ RequireInstanceModuleResolved: `${ModuleConfiguratorEventBaseName}.requireInstance.moduleResolved`,
5269
+ ModuleInitializeComplete: `${ModuleConfiguratorEventBaseName}.modules.initializeComplete`,
5270
+ InitializeComplete: `${ModuleConfiguratorEventBaseName}.initialize.instanceComplete`,
5271
+ ConfiguratorPostInitializeStart: `${ModuleConfiguratorEventBaseName}.postInitialize.started`,
5272
+ ModulePostInitializeStart: `${ModuleConfiguratorEventBaseName}.modulePostInitialize.started`,
5273
+ ModulePostInitializeComplete: `${ModuleConfiguratorEventBaseName}.modulePostInitialize.complete`,
5274
+ ModulePostInitializeError: `${ModuleConfiguratorEventBaseName}.modulePostInitialize.error`,
5275
+ PostInitializeComplete: `${ModuleConfiguratorEventBaseName}.postInitialize.complete`,
5276
+ PostInitializeCompleteNoHooks: `${ModuleConfiguratorEventBaseName}.postInitialize.noHooks`,
5277
+ PostInitializeHooks: `${ModuleConfiguratorEventBaseName}.postInitializeHooks.started`,
5278
+ PostInitializeHooksComplete: `${ModuleConfiguratorEventBaseName}.postInitializeHooks.complete`,
5279
+ PostInitializeHooksError: `${ModuleConfiguratorEventBaseName}.postInitializeHooks.error`,
5280
+ PluginsRegister: `${ModuleConfiguratorEventBaseName}.plugins.registering`,
5281
+ PluginRegistered: `${ModuleConfiguratorEventBaseName}.plugin.registered`,
5282
+ PluginRegisterError: `${ModuleConfiguratorEventBaseName}.plugin.registerError`,
5283
+ PluginsRegistered: `${ModuleConfiguratorEventBaseName}.plugins.registered`,
5284
+ Dispose: `${ModuleConfiguratorEventBaseName}.dispose.started`,
5285
+ PluginsDisposing: `${ModuleConfiguratorEventBaseName}.plugins.disposing`,
5286
+ PluginDisposed: `${ModuleConfiguratorEventBaseName}.plugin.disposed`,
5287
+ PluginDisposeError: `${ModuleConfiguratorEventBaseName}.plugin.disposeError`,
5288
+ ModulesDispose: `${ModuleConfiguratorEventBaseName}.modules.disposing`,
5289
+ ModuleDisposed: `${ModuleConfiguratorEventBaseName}.module.disposed`,
5290
+ ModuleDisposeError: `${ModuleConfiguratorEventBaseName}.module.disposeError`,
5291
+ ModulesDisposed: `${ModuleConfiguratorEventBaseName}.modules.disposed`,
5292
+ };
5293
+
5221
5294
  /* eslint-disable @typescript-eslint/no-explicit-any */
5222
5295
  /**
5223
5296
  * Defines the severity levels for module events.
@@ -5238,6 +5311,170 @@ var ModuleEventLevel;
5238
5311
  ModuleEventLevel[ModuleEventLevel["Critical"] = 4] = "Critical";
5239
5312
  })(ModuleEventLevel || (ModuleEventLevel = {}));
5240
5313
 
5314
+ // biome-ignore-all lint/suspicious/noExplicitAny: internal type-erased dispatch — the configure phase coordinates opaque module configs without knowing their concrete shapes
5315
+ /* eslint-disable @typescript-eslint/no-explicit-any */
5316
+ /**
5317
+ * Creates the raw module config map by calling each module's `configure` factory.
5318
+ *
5319
+ * Runs all module `configure(ref)` calls concurrently via `mergeMap` and
5320
+ * accumulates results into a single config object. The object is seeded with
5321
+ * `onAfterConfiguration` and `onAfterInit` helpers that allow modules to
5322
+ * register additional post-phase callbacks during configuration.
5323
+ *
5324
+ * @param ctx - The configure phase context.
5325
+ * @param ref - Optional reference forwarded to each module's configure factory.
5326
+ * @returns A promise resolving to the merged module config map.
5327
+ * @internal
5328
+ */
5329
+ async function createModuleConfigs(ctx, ref) {
5330
+ const { modules, afterConfiguration, afterInit, registerEvent } = ctx;
5331
+ const config$ = from(modules).pipe(mergeMap(async (module) => {
5332
+ const configStart = performance.now();
5333
+ try {
5334
+ const configurator = await module.configure?.(ref);
5335
+ const configLoadTime = Math.round(performance.now() - configStart);
5336
+ registerEvent({
5337
+ level: ModuleEventLevel.Debug,
5338
+ name: ModuleConfiguratorEventName.ConfiguratorCreated,
5339
+ message: `Configurator created for ${module.name} in ${configLoadTime}ms`,
5340
+ properties: {
5341
+ moduleName: module.name,
5342
+ moduleVersion: module.version?.toString() || 'unknown',
5343
+ configLoadTime,
5344
+ },
5345
+ metric: configLoadTime,
5346
+ });
5347
+ return { [module.name]: configurator };
5348
+ }
5349
+ catch (err) {
5350
+ registerEvent({
5351
+ level: ModuleEventLevel.Error,
5352
+ name: ModuleConfiguratorEventName.ConfiguratorFailed,
5353
+ message: `Failed to create configurator for ${module.name}`,
5354
+ properties: {
5355
+ moduleName: module.name,
5356
+ moduleVersion: module.version?.toString() || 'unknown',
5357
+ },
5358
+ metric: Math.round(performance.now() - configStart),
5359
+ error: err,
5360
+ });
5361
+ throw err;
5362
+ }
5363
+ }), reduce((acc, module) => Object.assign(acc, module),
5364
+ // Seed the config object with hooks so modules can register post-phase callbacks
5365
+ // during their own configure factory (a common pattern for cross-module wiring).
5366
+ {
5367
+ onAfterConfiguration(cb) {
5368
+ afterConfiguration.push(cb);
5369
+ },
5370
+ onAfterInit(cb) {
5371
+ afterInit.push(cb);
5372
+ },
5373
+ }));
5374
+ return lastValueFrom(config$);
5375
+ }
5376
+ /**
5377
+ * Runs the post-configure phase: calls each module's `postConfigure` hook and
5378
+ * then invokes all registered `afterConfiguration` callbacks.
5379
+ *
5380
+ * Module `postConfigure` failures are caught individually and emitted as
5381
+ * Warning events so one failing module cannot block others.
5382
+ *
5383
+ * @param ctx - The configure phase context.
5384
+ * @param config - The merged module config map produced by {@link createModuleConfigs}.
5385
+ * @returns A promise resolving when all post-configure hooks have settled.
5386
+ * @internal
5387
+ */
5388
+ async function runPostConfigureHooks(ctx, config) {
5389
+ const { modules, afterConfiguration, registerEvent } = ctx;
5390
+ // Run each module's postConfigure hook; failures are isolated so one
5391
+ // failing module does not prevent others from completing post-configure.
5392
+ await Promise.allSettled(modules
5393
+ .filter((module) => !!module.postConfigure)
5394
+ .map(async (module) => {
5395
+ try {
5396
+ const postConfigStart = performance.now();
5397
+ await module.postConfigure?.(config);
5398
+ registerEvent({
5399
+ level: ModuleEventLevel.Debug,
5400
+ name: ModuleConfiguratorEventName.ModulePostConfigured,
5401
+ message: `Module ${module.name} post-configured successfully`,
5402
+ properties: {
5403
+ moduleName: module.name,
5404
+ moduleVersion: module.version?.toString() || 'unknown',
5405
+ postConfigTime: Math.round(performance.now() - postConfigStart),
5406
+ },
5407
+ });
5408
+ }
5409
+ catch (err) {
5410
+ registerEvent({
5411
+ level: ModuleEventLevel.Warning,
5412
+ name: ModuleConfiguratorEventName.ModulePostConfigureError,
5413
+ message: `Module ${module.name} post-configure failed`,
5414
+ properties: {
5415
+ moduleName: module.name,
5416
+ moduleVersion: module.version?.toString() || 'unknown',
5417
+ },
5418
+ error: err,
5419
+ });
5420
+ }
5421
+ }));
5422
+ if (!afterConfiguration.length)
5423
+ return;
5424
+ // Run all registered afterConfiguration callbacks. These were added either
5425
+ // by addConfig's afterConfig or via config.onAfterConfiguration inside a
5426
+ // module's configure factory.
5427
+ try {
5428
+ registerEvent({
5429
+ level: ModuleEventLevel.Debug,
5430
+ name: ModuleConfiguratorEventName.PostConfigureHooks,
5431
+ message: `Post configure hooks [${afterConfiguration.length}] called`,
5432
+ });
5433
+ const postConfigHooksStart = performance.now();
5434
+ await Promise.allSettled(afterConfiguration.map((x) => Promise.resolve(x(config))));
5435
+ const postConfigHooksTime = Math.round(performance.now() - postConfigHooksStart);
5436
+ registerEvent({
5437
+ level: ModuleEventLevel.Debug,
5438
+ name: ModuleConfiguratorEventName.PostConfigureHooksComplete,
5439
+ message: 'Post configure hooks complete',
5440
+ properties: {
5441
+ count: afterConfiguration.length,
5442
+ postConfigHooksTime,
5443
+ },
5444
+ metric: postConfigHooksTime,
5445
+ });
5446
+ }
5447
+ catch (err) {
5448
+ registerEvent({
5449
+ level: ModuleEventLevel.Warning,
5450
+ name: ModuleConfiguratorEventName.PostConfigureHooksError,
5451
+ message: 'Post configure hook failed',
5452
+ error: err,
5453
+ });
5454
+ }
5455
+ }
5456
+ /**
5457
+ * Runs the full configure lifecycle phase for a set of modules.
5458
+ *
5459
+ * Orchestrates three sub-steps in order:
5460
+ * 1. {@link createModuleConfigs} — call each module's `configure(ref)` factory.
5461
+ * 2. Apply all user-registered config callbacks (`_configs`) sequentially.
5462
+ * 3. {@link runPostConfigureHooks} — call `postConfigure` and `afterConfiguration` hooks.
5463
+ *
5464
+ * @param ctx - The configure phase context.
5465
+ * @param ref - Optional reference forwarded through all configure hooks.
5466
+ * @returns A promise resolving to the fully configured module config map.
5467
+ */
5468
+ async function runConfigurePhase(ctx, ref) {
5469
+ // Step 1: Create raw config objects for all registered modules
5470
+ const config = await createModuleConfigs(ctx, ref);
5471
+ // Step 2: Apply all user-registered configuration callbacks concurrently.
5472
+ await Promise.all(ctx.configs.map((cb) => Promise.resolve(cb(config, ref))));
5473
+ // Step 3: Run module postConfigure hooks and afterConfiguration callbacks
5474
+ await runPostConfigureHooks(ctx, config);
5475
+ return config;
5476
+ }
5477
+
5241
5478
  /**
5242
5479
  * Abstract base class for Fusion Framework module providers.
5243
5480
  *
@@ -5325,37 +5562,624 @@ class BaseModuleProvider {
5325
5562
  }
5326
5563
  }
5327
5564
 
5328
- // Generated by genversion.
5329
- const version$8 = '6.0.0';
5565
+ /**
5566
+ * Initializes a single module and emits a `[name, instance]` tuple when complete.
5567
+ *
5568
+ * Validates that the module exposes an `initialize` method, calls it with the
5569
+ * provided context, and emits lifecycle events for start, completion, and any
5570
+ * provider contract warnings.
5571
+ *
5572
+ * @internal
5573
+ */
5574
+ async function initializeModule(module, ctx) {
5575
+ const { config, ref, requireInstance, hasModule, registerEvent } = ctx;
5576
+ const key = module.name;
5577
+ if (!module.initialize) {
5578
+ const error = new Error(`Module ${module.name} does not have initialize method`);
5579
+ error.name = 'ModuleInitializeError';
5580
+ registerEvent({
5581
+ level: ModuleEventLevel.Error,
5582
+ name: ModuleConfiguratorEventName.ModuleInitializeError,
5583
+ message: error.message,
5584
+ properties: {
5585
+ moduleName: module.name,
5586
+ moduleVersion: module.version?.toString() || 'unknown',
5587
+ },
5588
+ error,
5589
+ });
5590
+ throw error;
5591
+ }
5592
+ registerEvent({
5593
+ level: ModuleEventLevel.Debug,
5594
+ name: ModuleConfiguratorEventName.ModuleInitializing,
5595
+ message: `Initializing module ${module.name}`,
5596
+ properties: {
5597
+ moduleName: module.name,
5598
+ moduleVersion: module.version?.toString() || 'unknown',
5599
+ },
5600
+ });
5601
+ const moduleInitStart = performance.now();
5602
+ const instance = await module.initialize({
5603
+ ref,
5604
+ config: config[key],
5605
+ requireInstance,
5606
+ hasModule,
5607
+ });
5608
+ // Warn when providers deviate from the expected base class — these modules
5609
+ // may lack version tracking and standard provider interfaces.
5610
+ if (!(instance instanceof BaseModuleProvider)) {
5611
+ registerEvent({
5612
+ level: ModuleEventLevel.Warning,
5613
+ name: ModuleConfiguratorEventName.ProviderNotBaseModuleProvider,
5614
+ message: `Provider for module ${module.name} does not extend BaseModuleProvider`,
5615
+ properties: {
5616
+ moduleName: module.name,
5617
+ moduleVersion: module.version?.toString() || 'unknown',
5618
+ },
5619
+ });
5620
+ }
5621
+ // Providers should expose a version string for diagnostics and compatibility checks.
5622
+ // Warn when absent so module authors catch missing version early rather than at runtime.
5623
+ const maybeVersioned = instance;
5624
+ if (!maybeVersioned.version) {
5625
+ registerEvent({
5626
+ level: ModuleEventLevel.Warning,
5627
+ name: ModuleConfiguratorEventName.ProviderVersionWarning,
5628
+ message: `Provider for module ${module.name} does not expose version`,
5629
+ properties: {
5630
+ moduleName: module.name,
5631
+ moduleVersion: module.version?.toString() || 'unknown',
5632
+ },
5633
+ });
5634
+ }
5635
+ const moduleInitTime = Math.round(performance.now() - moduleInitStart);
5636
+ registerEvent({
5637
+ level: ModuleEventLevel.Debug,
5638
+ name: ModuleConfiguratorEventName.ModuleInitialized,
5639
+ message: `Module ${module.name} initialized in ${moduleInitTime}ms`,
5640
+ properties: {
5641
+ moduleName: module.name,
5642
+ moduleVersion: module.version?.toString() || 'unknown',
5643
+ providerName: typeof instance,
5644
+ providerVersion: maybeVersioned.version?.toString() || 'unknown',
5645
+ moduleInitTime,
5646
+ },
5647
+ metric: moduleInitTime,
5648
+ });
5649
+ return [key, instance];
5650
+ }
5651
+ /**
5652
+ * Creates a `requireInstance` resolver for use during module initialization.
5653
+ *
5654
+ * `requireInstance` is passed into each module's `initialize` call so it can
5655
+ * declare dependencies on other modules without knowing whether those modules
5656
+ * have already finished initializing. The resolver waits up to `wait` seconds
5657
+ * for the target module to appear in the shared `instance$` subject.
5658
+ *
5659
+ * @param moduleNames - Names of all modules registered in this initialization run.
5660
+ * @param instance$ - Shared subject accumulating initialized module instances.
5661
+ * @param registerEvent - Function to emit lifecycle events.
5662
+ * @returns A `requireInstance` function matching the shape expected by `ModuleInitializerArgs`.
5663
+ * @internal
5664
+ */
5665
+ function createRequireInstance(moduleNames, instance$, registerEvent) {
5666
+ // The implementation signature is concrete (name: string) but the public contract
5667
+ // must be the generic shape expected by ModuleInitializerArgs. Module names are
5668
+ // always strings at runtime — the keyof-derived `string | number` constraint comes
5669
+ // from TypeScript's keyof semantics, not actual usage.
5670
+ return function requireInstance(name, wait = 60) {
5671
+ // Fail fast if the caller requests a module that was never registered —
5672
+ // this almost always indicates a misconfiguration rather than a timing issue.
5673
+ if (!moduleNames.includes(name)) {
5674
+ const error = new Error(`Cannot require [${String(name)}] since module is not defined!`);
5675
+ error.name = 'ModuleNotDefinedError';
5676
+ registerEvent({
5677
+ level: ModuleEventLevel.Error,
5678
+ name: ModuleConfiguratorEventName.RequireInstanceModuleNotDefined,
5679
+ message: error.message,
5680
+ properties: { moduleName: String(name), wait },
5681
+ error,
5682
+ });
5683
+ throw error;
5684
+ }
5685
+ // Short-circuit: module is already in the accumulated instance object
5686
+ if (instance$.value[name]) {
5687
+ registerEvent({
5688
+ level: ModuleEventLevel.Debug,
5689
+ name: ModuleConfiguratorEventName.RequireInstanceModuleAlreadyInitialized,
5690
+ message: `Module [${String(name)}] is already initialized, skipping queue`,
5691
+ properties: { moduleName: String(name), wait },
5692
+ });
5693
+ return Promise.resolve(instance$.value[name]);
5694
+ }
5695
+ const requireStart = performance.now();
5696
+ registerEvent({
5697
+ level: ModuleEventLevel.Debug,
5698
+ name: ModuleConfiguratorEventName.RequireInstanceAwaitingModule,
5699
+ message: `Awaiting module [${String(name)}] initialization, timeout ${wait}s`,
5700
+ properties: { moduleName: String(name), wait },
5701
+ });
5702
+ // Wait for the module to appear in the shared instance subject, up to `wait` seconds.
5703
+ return firstValueFrom(instance$.pipe(
5704
+ // Ignore emissions until the requested module has been added to the instance map.
5705
+ filter((x) => !!x[name]),
5706
+ // Resolve the dependency promise with the provider instance, not the full module map.
5707
+ map$1((x) => x[name]),
5708
+ // Convert unresolved dependencies into a typed timeout error with lifecycle diagnostics.
5709
+ timeout({
5710
+ // requireInstance accepts seconds; RxJS timeout expects milliseconds.
5711
+ each: wait * 1000,
5712
+ with: () => throwError(() => {
5713
+ const error = new RequiredModuleTimeoutError();
5714
+ registerEvent({
5715
+ level: ModuleEventLevel.Error,
5716
+ name: ModuleConfiguratorEventName.RequireInstanceTimeout,
5717
+ message: `Module [${String(name)}] initialization timed out after ${wait}s`,
5718
+ properties: { moduleName: String(name), wait },
5719
+ error,
5720
+ });
5721
+ return error;
5722
+ }),
5723
+ }),
5724
+ // Emit timing diagnostics only after the dependency has actually resolved.
5725
+ tap(() => {
5726
+ const requireTime = Math.round(performance.now() - requireStart);
5727
+ registerEvent({
5728
+ level: ModuleEventLevel.Debug,
5729
+ name: ModuleConfiguratorEventName.RequireInstanceModuleResolved,
5730
+ message: `Module [${String(name)}] required in ${requireTime}ms`,
5731
+ properties: { moduleName: String(name), wait, requireTime },
5732
+ metric: requireTime,
5733
+ });
5734
+ })));
5735
+ };
5736
+ }
5737
+ /**
5738
+ * Runs the initialize lifecycle phase for all registered modules.
5739
+ *
5740
+ * Initializes all modules **concurrently** using `mergeMap`. Cross-module
5741
+ * dependency ordering is handled lazily through `requireInstance`, which
5742
+ * waits for a peer module's `initialize` call to complete before resolving.
5743
+ *
5744
+ * Each initialized provider is accumulated into a shared `BehaviorSubject` so
5745
+ * that `requireInstance` callers see updates as soon as a dependency is ready.
5746
+ *
5747
+ * @param ctx - The initialize phase context.
5748
+ * @param config - The merged module config map produced by the configure phase.
5749
+ * @param ref - Optional reference forwarded to each module's `initialize` call.
5750
+ * @returns A promise resolving to the sealed map of initialized module providers.
5751
+ * @throws {Error} When a module's `initialize` method is missing.
5752
+ * @throws {RequiredModuleTimeoutError} When a required dependency does not
5753
+ * initialize within its timeout window.
5754
+ */
5755
+ async function runInitializePhase(ctx, config, ref) {
5756
+ const { modules, registerEvent } = ctx;
5757
+ // Fast-path: no modules registered — return a sealed empty instance immediately
5758
+ // to avoid a subscribe/lastValueFrom race on an already-completed observable.
5759
+ if (modules.length === 0) {
5760
+ return Object.seal({});
5761
+ }
5762
+ const moduleNames = modules.map((m) => m.name);
5763
+ // Accumulates initialized module providers; BehaviorSubject lets requireInstance
5764
+ // reactively wait for a dependency to appear without polling.
5765
+ const instance$ = new BehaviorSubject({});
5766
+ const hasModule = (name) => moduleNames.includes(name);
5767
+ const requireInstance = createRequireInstance(moduleNames, instance$, registerEvent);
5768
+ // Initialize all modules concurrently; modules that depend on peers will
5769
+ // suspend inside requireInstance until the dependency resolves.
5770
+ const init$ = from(modules).pipe(mergeMap((module) => initializeModule(module, { config, ref, requireInstance, hasModule, registerEvent })));
5771
+ const initStart = performance.now();
5772
+ // Accumulate module providers into the shared instance subject as each resolves.
5773
+ // Completing the subject signals that all modules are initialized.
5774
+ init$.subscribe({
5775
+ next: ([name, module]) => {
5776
+ instance$.next(Object.assign(instance$.value, { [name]: module }));
5777
+ },
5778
+ error: (err) => {
5779
+ registerEvent({
5780
+ level: ModuleEventLevel.Error,
5781
+ name: ModuleConfiguratorEventName.ModuleInitializeError,
5782
+ message: `Failed to initialize module ${err.name || 'unknown'}`,
5783
+ error: err,
5784
+ });
5785
+ instance$.error(err);
5786
+ },
5787
+ complete: () => {
5788
+ const loadTime = Math.round(performance.now() - initStart);
5789
+ registerEvent({
5790
+ level: ModuleEventLevel.Debug,
5791
+ name: ModuleConfiguratorEventName.ModuleInitializeComplete,
5792
+ message: `All modules initialized in ${loadTime}ms`,
5793
+ properties: {
5794
+ modules: Object.keys(instance$.value).join(', '),
5795
+ loadTime,
5796
+ },
5797
+ metric: loadTime,
5798
+ });
5799
+ instance$.complete();
5800
+ },
5801
+ });
5802
+ const instanceInitStart = performance.now();
5803
+ const instance = await lastValueFrom(instance$);
5804
+ const initTime = Math.round(performance.now() - instanceInitStart);
5805
+ registerEvent({
5806
+ level: ModuleEventLevel.Debug,
5807
+ name: ModuleConfiguratorEventName.InitializeComplete,
5808
+ message: `Modules instance created in ${initTime}ms`,
5809
+ properties: {
5810
+ modules: Object.keys(instance).join(', '),
5811
+ initTime,
5812
+ },
5813
+ metric: initTime,
5814
+ });
5815
+ Object.seal(instance);
5816
+ return instance;
5817
+ }
5818
+
5819
+ // biome-ignore-all lint/suspicious/noExplicitAny: internal type-erased dispatch — the post-initialize phase forwards opaque instances without inspecting their concrete shapes
5820
+ /* eslint-disable @typescript-eslint/no-explicit-any */
5821
+ /**
5822
+ * Runs the post-initialize lifecycle phase for all registered modules.
5823
+ *
5824
+ * Executes two sub-steps in order:
5825
+ * 1. Calls each module's `postInitialize` hook (if defined) concurrently.
5826
+ * Individual failures are caught and emitted as Warning events so one
5827
+ * failing module cannot block others.
5828
+ * 2. Runs all registered `afterInit` callbacks concurrently via `Promise.allSettled`.
5829
+ *
5830
+ * @param ctx - The post-initialize phase context.
5831
+ * @param instance - The sealed module instance map produced by the initialize phase.
5832
+ * @param ref - Optional reference forwarded to each module's `postInitialize` call.
5833
+ * @returns A promise resolving when all post-initialize hooks and callbacks have settled.
5834
+ */
5835
+ async function runPostInitializePhase(ctx, instance, ref) {
5836
+ const { modules, afterInit, registerEvent } = ctx;
5837
+ registerEvent({
5838
+ level: ModuleEventLevel.Debug,
5839
+ name: ModuleConfiguratorEventName.ConfiguratorPostInitializeStart,
5840
+ message: `Post-initializing all modules [${Object.keys(instance).length}]`,
5841
+ properties: { modules: Object.keys(instance).join(', ') },
5842
+ });
5843
+ // Run postInitialize hooks for modules that declare them. Failures are caught
5844
+ // per-module via catchError so one failure does not abort the others.
5845
+ const postInitialize$ = from(modules).pipe(filter((module) => !!module.postInitialize), tap((module) => {
5846
+ registerEvent({
5847
+ level: ModuleEventLevel.Debug,
5848
+ name: ModuleConfiguratorEventName.ModulePostInitializeStart,
5849
+ message: `Module ${module.name} is being post-initialized`,
5850
+ properties: {
5851
+ moduleName: module.name,
5852
+ moduleVersion: module.version?.toString() || 'unknown',
5853
+ },
5854
+ });
5855
+ }), mergeMap((module) => {
5856
+ const postInitStart = performance.now();
5857
+ return from(module.postInitialize({
5858
+ ref,
5859
+ modules: instance,
5860
+ instance: instance[module.name],
5861
+ })).pipe(tap(() => {
5862
+ const postInitTime = Math.round(performance.now() - postInitStart);
5863
+ registerEvent({
5864
+ level: ModuleEventLevel.Debug,
5865
+ name: ModuleConfiguratorEventName.ModulePostInitializeComplete,
5866
+ message: `Module ${module.name} has been post-initialized in ${postInitTime}ms`,
5867
+ metric: postInitTime,
5868
+ properties: {
5869
+ moduleName: module.name,
5870
+ moduleVersion: module.version?.toString() || 'unknown',
5871
+ postInitTime,
5872
+ },
5873
+ });
5874
+ }), defaultIfEmpty(null), catchError((err) => {
5875
+ registerEvent({
5876
+ level: ModuleEventLevel.Warning,
5877
+ name: ModuleConfiguratorEventName.ModulePostInitializeError,
5878
+ message: `Module ${module.name} post-initialize failed`,
5879
+ properties: {
5880
+ moduleName: module.name,
5881
+ moduleVersion: module.version?.toString() || 'unknown',
5882
+ },
5883
+ error: err,
5884
+ });
5885
+ // Swallow module-level errors — a failing postInitialize should not
5886
+ // prevent other modules from completing their post-initialize work.
5887
+ return EMPTY;
5888
+ }));
5889
+ }), defaultIfEmpty(null));
5890
+ const postInitStart = performance.now();
5891
+ await lastValueFrom(postInitialize$);
5892
+ const postInitTime = Math.round(performance.now() - postInitStart);
5893
+ registerEvent({
5894
+ level: ModuleEventLevel.Debug,
5895
+ name: ModuleConfiguratorEventName.PostInitializeComplete,
5896
+ message: `Post-initialization of all modules completed in ${postInitTime}ms`,
5897
+ properties: { modules: Object.keys(instance).join(', '), postInitTime },
5898
+ metric: postInitTime,
5899
+ });
5900
+ if (!afterInit.length) {
5901
+ registerEvent({
5902
+ level: ModuleEventLevel.Debug,
5903
+ name: ModuleConfiguratorEventName.PostInitializeCompleteNoHooks,
5904
+ message: 'Post-initialization complete',
5905
+ properties: { modules: Object.keys(instance).join(', '), postInitCompleteTime: postInitTime },
5906
+ });
5907
+ return;
5908
+ }
5909
+ // Run all registered afterInit callbacks. These were added either by
5910
+ // addConfig's afterInit or via onInitialized on the configurator.
5911
+ try {
5912
+ registerEvent({
5913
+ level: ModuleEventLevel.Debug,
5914
+ name: ModuleConfiguratorEventName.PostInitializeHooks,
5915
+ message: `Executing post-initialize hooks [${afterInit.length}]`,
5916
+ properties: { hooks: afterInit.map((x) => x.name || 'anonymous').join(', ') },
5917
+ });
5918
+ const afterInitStart = performance.now();
5919
+ await Promise.allSettled(afterInit.map((x) => Promise.resolve(x(instance))));
5920
+ const afterInitTime = Math.round(performance.now() - afterInitStart);
5921
+ registerEvent({
5922
+ level: ModuleEventLevel.Debug,
5923
+ name: ModuleConfiguratorEventName.PostInitializeHooksComplete,
5924
+ message: `Post-initialize hooks completed in ${afterInitTime}ms`,
5925
+ properties: {
5926
+ hooks: afterInit.map((x) => x.name || 'anonymous').join(', '),
5927
+ afterInitTime,
5928
+ },
5929
+ metric: afterInitTime,
5930
+ });
5931
+ }
5932
+ catch (err) {
5933
+ registerEvent({
5934
+ level: ModuleEventLevel.Warning,
5935
+ name: ModuleConfiguratorEventName.PostInitializeHooksError,
5936
+ message: 'Post-initialize hooks failed',
5937
+ properties: { hooks: afterInit.map((x) => x.name || 'anonymous').join(', ') },
5938
+ error: err,
5939
+ });
5940
+ }
5941
+ const postInitCompleteTime = Math.round(performance.now() - postInitStart);
5942
+ registerEvent({
5943
+ level: ModuleEventLevel.Debug,
5944
+ name: ModuleConfiguratorEventName.PostInitializeComplete,
5945
+ message: 'Post-initialization complete',
5946
+ properties: { modules: Object.keys(instance).join(', '), postInitCompleteTime },
5947
+ });
5948
+ }
5330
5949
 
5950
+ // biome-ignore-all lint/suspicious/noExplicitAny: internal type-erased dispatch — plugins are registered with concrete module maps but stored erased by the orchestrator
5331
5951
  /* eslint-disable @typescript-eslint/no-explicit-any */
5332
5952
  /**
5333
- * Error thrown when a required module fails to initialize within the specified timeout period.
5953
+ * Checks whether a plugin return value should be treated as dispose-time cleanup.
5954
+ *
5955
+ * Plugins may return either a plain callback or an object with a `dispose` method.
5956
+ * Any other value is ignored so plugin authors can return `undefined` when no
5957
+ * cleanup is needed.
5334
5958
  *
5335
- * This error indicates that a module dependency was not available when expected,
5336
- * typically due to initialization delays or circular dependencies.
5959
+ * @param value - Value returned by a registered plugin callback.
5960
+ * @returns True when the value is a valid plugin teardown registration.
5337
5961
  */
5338
- class RequiredModuleTimeoutError extends Error {
5339
- constructor() {
5340
- super('Module initialization timed out');
5341
- this.name = 'RequiredModuleTimeoutError';
5962
+ function isPluginTeardown(value) {
5963
+ return (typeof value === 'function' ||
5964
+ (typeof value === 'object' &&
5965
+ value !== null &&
5966
+ 'dispose' in value &&
5967
+ typeof value.dispose === 'function'));
5968
+ }
5969
+ /**
5970
+ * Runs configurator plugins after modules are ready.
5971
+ *
5972
+ * The plugin phase is the final initialization step before `initialize()`
5973
+ * resolves. Each plugin receives the sealed module instance map and optional
5974
+ * initialization reference, which makes this phase suitable for application
5975
+ * side effects such as telemetry bridges, global event listeners, and runtime
5976
+ * subscriptions that require multiple initialized module providers.
5977
+ *
5978
+ * Plugin callbacks run concurrently. Failures are isolated and emitted as
5979
+ * warning events so one failing side effect cannot block application rendering.
5980
+ * Returned teardown callbacks are stored for the dispose phase and are invoked
5981
+ * before module `dispose` hooks.
5982
+ *
5983
+ * @param ctx - The plugin phase context.
5984
+ * @param modules - The initialized module instance map.
5985
+ * @param ref - Optional reference forwarded from module initialization.
5986
+ * @returns A promise resolving when all plugin callbacks have settled.
5987
+ */
5988
+ async function runPluginPhase(ctx, modules, ref) {
5989
+ const { plugins, teardowns, registerEvent } = ctx;
5990
+ if (!plugins.length)
5991
+ return;
5992
+ registerEvent({
5993
+ level: ModuleEventLevel.Debug,
5994
+ name: ModuleConfiguratorEventName.PluginsRegister,
5995
+ message: `Registering plugins [${plugins.length}]`,
5996
+ properties: { count: plugins.length },
5997
+ });
5998
+ const pluginRegistrations = await Promise.all(plugins.map(async (plugin) => {
5999
+ const pluginStart = performance.now();
6000
+ const name = plugin.name || 'anonymous';
6001
+ try {
6002
+ const teardown = await plugin({ modules, ref });
6003
+ const pluginTime = Math.round(performance.now() - pluginStart);
6004
+ registerEvent({
6005
+ level: ModuleEventLevel.Debug,
6006
+ name: ModuleConfiguratorEventName.PluginRegistered,
6007
+ message: `Plugin ${name} registered in ${pluginTime}ms`,
6008
+ properties: { name, pluginTime },
6009
+ metric: pluginTime,
6010
+ });
6011
+ return isPluginTeardown(teardown) ? teardown : undefined;
6012
+ }
6013
+ catch (err) {
6014
+ registerEvent({
6015
+ level: ModuleEventLevel.Warning,
6016
+ name: ModuleConfiguratorEventName.PluginRegisterError,
6017
+ message: `Plugin ${name} registration failed`,
6018
+ properties: { name },
6019
+ error: err,
6020
+ });
6021
+ return undefined;
6022
+ }
6023
+ }));
6024
+ for (const teardown of pluginRegistrations) {
6025
+ if (teardown) {
6026
+ teardowns.push(teardown);
6027
+ }
6028
+ }
6029
+ registerEvent({
6030
+ level: ModuleEventLevel.Debug,
6031
+ name: ModuleConfiguratorEventName.PluginsRegistered,
6032
+ message: 'Plugin registration complete',
6033
+ properties: { count: plugins.length, teardowns: teardowns.length },
6034
+ });
6035
+ }
6036
+
6037
+ function getPluginTeardownName(teardown) {
6038
+ return typeof teardown === 'function' ? teardown.name || 'anonymous' : 'dispose';
6039
+ }
6040
+ async function runPluginTeardown(teardown) {
6041
+ if (typeof teardown === 'function') {
6042
+ await teardown();
6043
+ return;
5342
6044
  }
6045
+ await teardown.dispose();
5343
6046
  }
6047
+ /**
6048
+ * Runs the dispose lifecycle phase for all registered modules.
6049
+ *
6050
+ * Calls each module's `dispose` hook (if defined) concurrently via
6051
+ * `Promise.allSettled` so one failing module cannot block others from
6052
+ * being torn down. After all hooks settle, the event stream is completed.
6053
+ *
6054
+ * @param ctx - The dispose phase context.
6055
+ * @param instance - The initialized module instance map to tear down.
6056
+ * @param ref - Optional reference forwarded to each module's `dispose` call.
6057
+ * @returns A promise resolving when all module dispose hooks have settled and
6058
+ * the event stream has been completed.
6059
+ */
6060
+ async function runDisposePhase(ctx, instance, ref) {
6061
+ const { modules, registerEvent, event$, pluginTeardowns = [] } = ctx;
6062
+ registerEvent({
6063
+ level: ModuleEventLevel.Debug,
6064
+ name: ModuleConfiguratorEventName.Dispose,
6065
+ message: 'Disposing modules instance',
6066
+ properties: { modules: Object.keys(instance).join(', ') },
6067
+ });
6068
+ if (pluginTeardowns.length) {
6069
+ registerEvent({
6070
+ level: ModuleEventLevel.Debug,
6071
+ name: ModuleConfiguratorEventName.PluginsDisposing,
6072
+ message: `Disposing plugins [${pluginTeardowns.length}]`,
6073
+ properties: { count: pluginTeardowns.length },
6074
+ });
6075
+ // Tear down plugins before module providers so side effects can still access
6076
+ // live providers while unsubscribing. LIFO mirrors common subscription stacks.
6077
+ for (const teardown of pluginTeardowns.splice(0).reverse()) {
6078
+ const name = getPluginTeardownName(teardown);
6079
+ try {
6080
+ await runPluginTeardown(teardown);
6081
+ registerEvent({
6082
+ level: ModuleEventLevel.Debug,
6083
+ name: ModuleConfiguratorEventName.PluginDisposed,
6084
+ message: `Plugin ${name} disposed successfully`,
6085
+ properties: { name },
6086
+ });
6087
+ }
6088
+ catch (err) {
6089
+ registerEvent({
6090
+ level: ModuleEventLevel.Warning,
6091
+ name: ModuleConfiguratorEventName.PluginDisposeError,
6092
+ message: `Plugin ${name} dispose failed`,
6093
+ properties: { name },
6094
+ error: err,
6095
+ });
6096
+ }
6097
+ }
6098
+ }
6099
+ registerEvent({
6100
+ level: ModuleEventLevel.Debug,
6101
+ name: ModuleConfiguratorEventName.ModulesDispose,
6102
+ message: 'Disposing modules',
6103
+ properties: { count: modules.length },
6104
+ });
6105
+ // Dispose all modules concurrently; failures are isolated per module so
6106
+ // one bad teardown cannot leave other modules in an inconsistent state.
6107
+ await Promise.allSettled(modules
6108
+ .filter((module) => !!module.dispose)
6109
+ .map(async (module) => {
6110
+ if (!module.dispose)
6111
+ return;
6112
+ try {
6113
+ await module.dispose({
6114
+ ref,
6115
+ modules: instance,
6116
+ instance: instance[module.name],
6117
+ });
6118
+ registerEvent({
6119
+ level: ModuleEventLevel.Debug,
6120
+ name: ModuleConfiguratorEventName.ModuleDisposed,
6121
+ message: `Module ${module.name} disposed successfully`,
6122
+ properties: {
6123
+ moduleName: module.name,
6124
+ moduleVersion: module.version?.toString() || 'unknown',
6125
+ },
6126
+ });
6127
+ }
6128
+ catch (err) {
6129
+ registerEvent({
6130
+ level: ModuleEventLevel.Warning,
6131
+ name: ModuleConfiguratorEventName.ModuleDisposeError,
6132
+ message: `Module ${module.name} dispose failed`,
6133
+ properties: {
6134
+ moduleName: module.name,
6135
+ moduleVersion: module.version?.toString() || 'unknown',
6136
+ },
6137
+ error: err,
6138
+ });
6139
+ }
6140
+ }));
6141
+ registerEvent({
6142
+ level: ModuleEventLevel.Debug,
6143
+ name: ModuleConfiguratorEventName.ModulesDisposed,
6144
+ message: 'Module dispose complete',
6145
+ properties: { count: modules.length },
6146
+ });
6147
+ // Complete the event stream last so all dispose events are captured before
6148
+ // any subscriber teardown triggered by completion.
6149
+ event$.complete();
6150
+ }
6151
+
6152
+ // Generated by genversion.
6153
+ const version$8 = '6.1.0';
6154
+
6155
+ // 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
6156
+ /* eslint-disable @typescript-eslint/no-explicit-any */
5344
6157
  /**
5345
6158
  * Core orchestrator that drives the module lifecycle in Fusion Framework.
5346
6159
  *
5347
- * `ModulesConfigurator` manages the full configureinitialize dispose pipeline
5348
- * for a set of modules. Consumers register modules via {@link addConfig} or
5349
- * {@link configure}, then call {@link initialize} to produce a sealed
5350
- * {@link ModulesInstance} whose properties are the initialized module providers.
6160
+ * `ModulesConfigurator` manages the full **configure post-configure → initialize
6161
+ * post-initialize → plugin → dispose** pipeline for a set of modules. Consumers
6162
+ * register modules via {@link addConfig} or {@link configure}, then call
6163
+ * {@link initialize} to produce a sealed {@link ModulesInstance} whose properties
6164
+ * are the initialized module providers.
5351
6165
  *
5352
- * The lifecycle phases executed during {@link initialize} are:
6166
+ * ### Lifecycle phases (in execution order)
5353
6167
  *
5354
- * 1. **Configure** each module’s `configure()` factory creates a config builder;
5355
- * registered callbacks mutate it; `postConfigure()` hooks run.
5356
- * 2. **Initialize** modules are initialized concurrently via `initialize()`;
5357
- * cross-module dependencies are resolved through `requireInstance()`.
5358
- * 3. **Post-initialize** `postInitialize()` hooks and `onInitialized` callbacks run.
6168
+ * | # | Phase | Entry point | Description |
6169
+ * |---|-------|-------------|-------------|
6170
+ * | 1 | **Configure** | `_configure` | Each module's `configure()` factory creates a config builder; registered callbacks mutate it. |
6171
+ * | 2 | **Post-configure** | `_postConfigure` (inside `_configure`) | `postConfigure()` hooks and `onConfigured` callbacks run. |
6172
+ * | 3 | **Initialize** | `_initialize` | Modules are initialized concurrently; cross-module dependencies are resolved through `requireInstance`. |
6173
+ * | 4 | **Post-initialize** | `_postInitialize` | `postInitialize()` hooks and `onInitialized` callbacks run. |
6174
+ * | 5 | **Plugin** | `_registerPlugins` (inside `initialize`) | Registered application plugins connect side effects after modules are ready. |
6175
+ * | 6 | **Dispose** | `dispose` | Plugin teardowns and module `dispose()` hooks run; the event stream is completed. |
6176
+ *
6177
+ * ### Registering phase callbacks
6178
+ *
6179
+ * - **Per-module callbacks**: use `addConfig({ module, configure, afterConfig, afterInit })`.
6180
+ * - **Global post-configure**: use `onConfigured(cb)`.
6181
+ * - **Global post-initialize**: use `onInitialized(cb)`.
6182
+ * - **Plugins**: use `registerPlugin(cb)` to connect side effects before render.
5359
6183
  *
5360
6184
  * All lifecycle transitions emit {@link ModuleEvent} entries on the {@link event$}
5361
6185
  * observable for telemetry and debugging.
@@ -5376,57 +6200,97 @@ class RequiredModuleTimeoutError extends Error {
5376
6200
  */
5377
6201
  class ModulesConfigurator {
5378
6202
  /**
5379
- * The class name used for event naming. This static property ensures
5380
- * the name is preserved through compilation and minification.
6203
+ * Class name used as a namespace prefix for all emitted lifecycle events.
6204
+ * Preserved as a static string so minification cannot change it at runtime.
5381
6205
  */
5382
6206
  static className = 'ModulesConfigurator';
5383
6207
  get version() {
5384
6208
  return version$8;
5385
6209
  }
5386
- // Buffer up to 100 events to prevent memory leaks while ensuring telemetry can capture
5387
- // module events during configuration. Telemetry attaches via mapConfiguratorEvents and
5388
- // needs to replay events that occurred before it was ready.
5389
- // Memory usage: ~24KB for 100 events at ~240 bytes each.
6210
+ // Buffer up to 100 events to prevent memory leaks while ensuring telemetry
6211
+ // can capture events that fire before a telemetry subscriber attaches.
6212
+ // mapConfiguratorEvents relies on replay to receive events emitted during
6213
+ // configuration before telemetry is wired up.
6214
+ // Memory bound: ~24 KB at ~240 bytes/event × 100 events.
5390
6215
  #event$ = new ReplaySubject(100);
5391
6216
  get event$() {
5392
6217
  return this.#event$.asObservable();
5393
6218
  }
5394
6219
  /**
5395
- * Array of configuration callbacks.
6220
+ * Registered configure-phase callbacks.
6221
+ * Each entry is added by {@link addConfig} when a `configure` callback is provided.
5396
6222
  * @protected
5397
- * @sealed
5398
6223
  */
5399
6224
  _configs = [];
5400
6225
  /**
5401
- * Array of callbacks to be executed after configuration.
6226
+ * Registered post-configure callbacks.
6227
+ * Populated by {@link onConfigured} and by `afterConfig` entries in {@link addConfig}.
6228
+ * Also exposed on the config object as `config.onAfterConfiguration` so modules
6229
+ * can register additional hooks during their own configure factory.
6230
+ *
6231
+ * Typed as `any` because this is an internal dispatch array: callbacks are registered
6232
+ * with concrete module-specific types but stored erased — the orchestrator never
6233
+ * inspects the config shape itself, it only forwards it at call time.
6234
+ * @protected
5402
6235
  */
5403
6236
  _afterConfiguration = [];
5404
6237
  /**
5405
- * Array of callbacks to be executed after initialization.
6238
+ * Registered post-initialize callbacks.
6239
+ * Populated by {@link onInitialized} and by `afterInit` entries in {@link addConfig}.
6240
+ * Also exposed on the config object as `config.onAfterInit`.
6241
+ *
6242
+ * Typed as `any` for the same reason as {@link _afterConfiguration} — type-erased
6243
+ * internal dispatch; concrete instance types are known at registration but not stored.
6244
+ * @protected
5406
6245
  */
5407
6246
  _afterInit = [];
5408
6247
  /**
5409
- * Set of modules.
6248
+ * Registered plugin callbacks.
6249
+ *
6250
+ * Plugins run after all modules have initialized and before initialize resolves.
6251
+ * Typed as `any` because callbacks are registered with concrete module maps but
6252
+ * stored erased by the base orchestrator.
6253
+ * @protected
6254
+ */
6255
+ _plugins = [];
6256
+ /**
6257
+ * Teardown callbacks returned by registered plugins.
6258
+ *
6259
+ * Consumed during dispose and cleared after execution so repeated dispose calls
6260
+ * do not run plugin cleanup more than once.
6261
+ * @protected
6262
+ */
6263
+ _pluginTeardowns = [];
6264
+ /**
6265
+ * Set of all registered module descriptors.
6266
+ * Uses a `Set` for automatic deduplication — the same module registered twice
6267
+ * is treated as a single registration.
6268
+ * @protected
5410
6269
  */
5411
6270
  _modules;
5412
6271
  /**
5413
- * Constructs a new ModulesConfigurator instance.
5414
- * @param modules - Optional array of modules to initialize with.
6272
+ * Creates a new `ModulesConfigurator` with an optional initial set of modules.
6273
+ *
6274
+ * @param modules - Optional array of module descriptors to pre-register.
5415
6275
  */
5416
6276
  constructor(modules) {
5417
- // Use Set for efficient lookups and automatic deduplication of modules
5418
6277
  this._modules = new Set(modules);
5419
6278
  }
5420
6279
  /**
5421
- * Gets the array of modules.
5422
- * @returns Array of modules.
6280
+ * Returns all registered module descriptors as an ordered array.
6281
+ *
6282
+ * @returns Array of registered modules in insertion order.
5423
6283
  */
5424
6284
  get modules() {
5425
6285
  return [...this._modules];
5426
6286
  }
5427
6287
  /**
5428
- * Configures the modules with the provided configurators.
5429
- * @param configs - Array of module configurators.
6288
+ * Registers one or more module configurators.
6289
+ *
6290
+ * Convenience wrapper around {@link addConfig} for registering multiple
6291
+ * modules in a single call.
6292
+ *
6293
+ * @param configs - One or more module configurator descriptors.
5430
6294
  */
5431
6295
  configure(...configs) {
5432
6296
  for (const x of configs) {
@@ -5434,15 +6298,21 @@ class ModulesConfigurator {
5434
6298
  }
5435
6299
  }
5436
6300
  /**
5437
- * Adds a module configurator.
5438
- * @param config - Module configurator.
6301
+ * Registers a single module configurator.
6302
+ *
6303
+ * Adds the module to the known module set and registers the optional
6304
+ * `configure`, `afterConfig`, and `afterInit` callbacks into their
6305
+ * respective lifecycle phase arrays.
6306
+ *
6307
+ * @param config - The module configurator descriptor to register.
6308
+ * @template T - The module type being registered.
5439
6309
  */
5440
6310
  addConfig(config) {
5441
6311
  const { module, afterConfig, afterInit, configure } = config;
5442
6312
  this._modules.add(module);
5443
6313
  this._registerEvent({
5444
6314
  level: ModuleEventLevel.Debug,
5445
- name: 'moduleConfigAdded',
6315
+ name: ModuleConfiguratorEventName.ModuleConfigAdded,
5446
6316
  message: `Module configurator added for ${module.name}`,
5447
6317
  properties: {
5448
6318
  moduleName: module.name,
@@ -5452,19 +6322,28 @@ class ModulesConfigurator {
5452
6322
  afterInit: !!afterInit,
5453
6323
  },
5454
6324
  });
5455
- configure && this._configs.push((config, ref) => configure(config[module.name], ref));
5456
- afterConfig && this._afterConfiguration.push((config) => afterConfig(config[module.name]));
5457
- afterInit && this._afterInit.push((instances) => afterInit(instances[module.name]));
6325
+ // Register each optional callback into its corresponding lifecycle phase array
6326
+ if (configure)
6327
+ this._configs.push((cfg, ref) => configure(cfg[module.name], ref));
6328
+ if (afterConfig)
6329
+ this._afterConfiguration.push((cfg) => afterConfig(cfg[module.name]));
6330
+ if (afterInit)
6331
+ this._afterInit.push((instances) => afterInit(instances[module.name]));
5458
6332
  }
5459
6333
  /**
5460
- * Registers a callback to be executed after configuration.
5461
- * @param cb - Callback function.
6334
+ * Registers a callback for the post-configure phase.
6335
+ *
6336
+ * The callback receives the merged module config map after all `configure`
6337
+ * callbacks have run and before module initialization begins.
6338
+ *
6339
+ * @param cb - Callback receiving the merged module config map.
6340
+ * @template T - Additional modules to include in the config type.
5462
6341
  */
5463
6342
  onConfigured(cb) {
5464
6343
  this._afterConfiguration.push(cb);
5465
6344
  this._registerEvent({
5466
6345
  level: ModuleEventLevel.Debug,
5467
- name: 'addOnConfigured',
6346
+ name: ModuleConfiguratorEventName.OnConfiguredAdded,
5468
6347
  message: 'Added onConfigured callback',
5469
6348
  properties: {
5470
6349
  count: this._afterConfiguration.length,
@@ -5473,14 +6352,19 @@ class ModulesConfigurator {
5473
6352
  });
5474
6353
  }
5475
6354
  /**
5476
- * Registers a callback to be executed after initialization.
5477
- * @param cb - Callback function.
6355
+ * Registers a callback for the post-initialize phase.
6356
+ *
6357
+ * The callback receives the sealed module instance after all modules have
6358
+ * been initialized and their `postInitialize` hooks have run.
6359
+ *
6360
+ * @param cb - Callback receiving the sealed module instance.
6361
+ * @template T - Additional modules to include in the instance type.
5478
6362
  */
5479
6363
  onInitialized(cb) {
5480
6364
  this._afterInit.push(cb);
5481
6365
  this._registerEvent({
5482
6366
  level: ModuleEventLevel.Debug,
5483
- name: 'addOnInitialized',
6367
+ name: ModuleConfiguratorEventName.OnInitializedAdded,
5484
6368
  message: 'Added onInitialized callback',
5485
6369
  properties: {
5486
6370
  count: this._afterInit.length,
@@ -5489,9 +6373,51 @@ class ModulesConfigurator {
5489
6373
  });
5490
6374
  }
5491
6375
  /**
5492
- * Initializes the modules with the provided reference.
5493
- * @param ref - Reference object.
5494
- * @returns Promise that resolves to the initialized module instance.
6376
+ * Registers a plugin that connects side effects after modules are initialized.
6377
+ *
6378
+ * The callback runs after `postInitialize` and `onInitialized` callbacks have
6379
+ * settled, but before {@link initialize} resolves. Return a teardown callback
6380
+ * to clean up subscriptions or listeners during {@link dispose}.
6381
+ *
6382
+ * @param cb - Plugin callback receiving the initialized module map and optional ref.
6383
+ * @template T - Additional modules to include in the plugin module map.
6384
+ * @example
6385
+ * ```typescript
6386
+ * function connectContextTelemetry(args: FrameworkPluginArgs<[EventModule, TelemetryModule]>) {
6387
+ * const teardown = args.modules.event.addEventListener('context:changed', (event) => {
6388
+ * args.modules.telemetry.track('context.changed', event.detail);
6389
+ * });
6390
+ *
6391
+ * return teardown;
6392
+ * }
6393
+ *
6394
+ * configurator.registerPlugin(connectContextTelemetry);
6395
+ * ```
6396
+ */
6397
+ registerPlugin(cb) {
6398
+ this._plugins.push(cb);
6399
+ this._registerEvent({
6400
+ level: ModuleEventLevel.Debug,
6401
+ name: ModuleConfiguratorEventName.PluginAdded,
6402
+ message: 'Added plugin callback',
6403
+ properties: {
6404
+ count: this._plugins.length,
6405
+ name: cb.name || 'anonymous',
6406
+ },
6407
+ });
6408
+ }
6409
+ /**
6410
+ * Runs the full configure → initialize pipeline and returns a sealed module instance.
6411
+ *
6412
+ * Execution order:
6413
+ * 1. {@link _configure} — configure phase (creates config, applies callbacks, post-configure hooks).
6414
+ * 2. {@link _initialize} — initialize phase (concurrent module init with `requireInstance`).
6415
+ * 3. {@link _postInitialize} — post-initialize phase (`postInitialize` hooks + `onInitialized` callbacks).
6416
+ * 4. {@link _registerPlugins} — plugin phase (`registerPlugin` callbacks connect side effects).
6417
+ *
6418
+ * @param ref - Optional reference forwarded to all module lifecycle hooks.
6419
+ * @returns A promise resolving to the sealed, initialized module instance.
6420
+ * @template T - Additional modules to merge into the instance type.
5495
6421
  */
5496
6422
  async initialize(ref) {
5497
6423
  const configStart = performance.now();
@@ -5499,7 +6425,7 @@ class ModulesConfigurator {
5499
6425
  const configLoadTime = Math.round(performance.now() - configStart);
5500
6426
  this._registerEvent({
5501
6427
  level: ModuleEventLevel.Debug,
5502
- name: 'initialize.configLoaded',
6428
+ name: ModuleConfiguratorEventName.InitializeConfigLoaded,
5503
6429
  message: `Modules configured in ${configLoadTime}ms`,
5504
6430
  properties: {
5505
6431
  modules: this.modules.map((m) => m.name).join(', '),
@@ -5513,7 +6439,7 @@ class ModulesConfigurator {
5513
6439
  const instanceLoadTime = Math.round(performance.now() - instanceStart);
5514
6440
  this._registerEvent({
5515
6441
  level: ModuleEventLevel.Debug,
5516
- name: 'initialize.instanceInitialized',
6442
+ name: ModuleConfiguratorEventName.InitializeInstanceInitialized,
5517
6443
  message: `Modules initialized in ${instanceLoadTime}ms`,
5518
6444
  properties: {
5519
6445
  modules: this.modules.map((m) => m.name).join(', '),
@@ -5525,7 +6451,7 @@ class ModulesConfigurator {
5525
6451
  const totalLoadTime = configLoadTime + instanceLoadTime;
5526
6452
  this._registerEvent({
5527
6453
  level: ModuleEventLevel.Information,
5528
- name: 'initialize',
6454
+ name: ModuleConfiguratorEventName.Initialize,
5529
6455
  message: `initialize in ${totalLoadTime}ms`,
5530
6456
  properties: {
5531
6457
  modules: this.modules.map((m) => m.name).join(', '),
@@ -5536,560 +6462,126 @@ class ModulesConfigurator {
5536
6462
  metric: totalLoadTime,
5537
6463
  });
5538
6464
  await this._postInitialize(instance, ref);
5539
- return Object.seal(Object.assign({}, instance, {
6465
+ const modules = Object.seal(Object.assign({}, instance, {
5540
6466
  dispose: () => this.dispose(instance),
5541
6467
  }));
6468
+ await this._registerPlugins(modules, ref);
6469
+ return modules;
5542
6470
  }
5543
6471
  /**
5544
- * Registers a module event by namespacing it with the configurator class name.
6472
+ * Namespaces and emits a lifecycle event into the internal event stream.
5545
6473
  *
5546
- * Event names are prefixed with the constructor name to create unique, namespaced
5547
- * event identifiers that prevent conflicts between different configurator instances.
5548
- * For example, "moduleConfigAdded" becomes "ModulesConfigurator::moduleConfigAdded".
6474
+ * The event name is prefixed with the configurator class name (e.g.
6475
+ * `"ModulesConfigurator::ModuleConfigurator.module.configAdded"`) to prevent
6476
+ * name collisions between nested configurators.
5549
6477
  *
5550
- * @param event - The module event to register
6478
+ * @param event - The lifecycle event to emit.
5551
6479
  * @protected
5552
6480
  */
5553
6481
  _registerEvent(event) {
5554
- // Split event name by '::' to handle already-namespaced events
6482
+ // Split on '::' to avoid double-prefixing already-namespaced event names
5555
6483
  const nameParts = event.name.split('::');
5556
6484
  this.#event$.next({
5557
6485
  ...event,
5558
- // Prefix the event name with the configurator class name
5559
6486
  name: `${this.constructor.className}::${nameParts[nameParts.length - 1]}`,
5560
6487
  });
5561
6488
  }
5562
6489
  /**
5563
- * Configures the modules with the provided reference.
5564
- * @param ref - Reference object.
5565
- * @returns Promise that resolves to the module configuration.
6490
+ * Runs the configure lifecycle phase.
6491
+ *
6492
+ * Delegates to {@link runConfigurePhase} which creates module config builders,
6493
+ * applies registered callbacks, and runs post-configure hooks.
6494
+ *
6495
+ * Override this method in a subclass to customize the configure phase.
6496
+ *
6497
+ * @param ref - Optional reference forwarded to module configure factories.
6498
+ * @returns A promise resolving to the merged module config map.
6499
+ * @protected
5566
6500
  */
5567
6501
  async _configure(ref) {
5568
- const config = await this._createConfig(ref);
5569
- await Promise.all(this._configs.map((x) => Promise.resolve(x(config, ref))));
5570
- await this._postConfigure(config);
5571
- return config;
6502
+ return runConfigurePhase({
6503
+ modules: this.modules,
6504
+ configs: this._configs,
6505
+ afterConfiguration: this._afterConfiguration,
6506
+ afterInit: this._afterInit,
6507
+ registerEvent: this._registerEvent.bind(this),
6508
+ }, ref);
5572
6509
  }
5573
6510
  /**
5574
- * Creates the module configuration with the provided reference.
5575
- * @param ref - Reference object.
5576
- * @returns Promise that resolves to the module configuration.
6511
+ * Runs the initialize lifecycle phase.
6512
+ *
6513
+ * Delegates to {@link runInitializePhase} which initializes all modules
6514
+ * concurrently and resolves cross-module dependencies through `requireInstance`.
6515
+ *
6516
+ * Override this method in a subclass to customize the initialize phase.
6517
+ *
6518
+ * @param config - The merged module config map from the configure phase.
6519
+ * @param ref - Optional reference forwarded to each module's `initialize` call.
6520
+ * @returns A promise resolving to the sealed map of initialized module providers.
6521
+ * @protected
5577
6522
  */
5578
- _createConfig(ref) {
5579
- const { modules, _afterConfiguration, _afterInit } = this;
5580
- const config$ = from(modules).pipe(
5581
- // TODO: Add proper error handling for individual module config creation failures
5582
- mergeMap(async (module) => {
5583
- const configStart = performance.now();
5584
- try {
5585
- const configurator = await module.configure?.(ref);
5586
- const configLoadTime = Math.round(performance.now() - configStart);
5587
- this._registerEvent({
5588
- level: ModuleEventLevel.Debug,
5589
- name: '_createConfig.configuratorCreated',
5590
- message: `Configurator created for ${module.name} in ${configLoadTime}ms`,
5591
- properties: {
5592
- moduleName: module.name,
5593
- moduleVersion: module.version?.toString() || 'unknown',
5594
- configLoadTime,
5595
- },
5596
- metric: configLoadTime,
5597
- });
5598
- return { [module.name]: configurator };
5599
- }
5600
- catch (err) {
5601
- this._registerEvent({
5602
- level: ModuleEventLevel.Error,
5603
- name: '_createConfig.configuratorFailed',
5604
- message: `Failed to create configurator for ${module.name}`,
5605
- properties: {
5606
- moduleName: module.name,
5607
- moduleVersion: module.version?.toString() || 'unknown',
5608
- },
5609
- metric: Math.round(performance.now() - configStart),
5610
- error: err,
5611
- });
5612
- throw err;
5613
- }
5614
- }), reduce((acc, module) => Object.assign(acc, module), {
5615
- onAfterConfiguration(cb) {
5616
- _afterConfiguration.push(cb);
5617
- },
5618
- onAfterInit(cb) {
5619
- _afterInit.push(cb);
5620
- },
5621
- }));
5622
- return lastValueFrom(config$);
6523
+ async _initialize(config, ref) {
6524
+ return runInitializePhase({
6525
+ modules: this.modules,
6526
+ registerEvent: this._registerEvent.bind(this),
6527
+ }, config, ref);
5623
6528
  }
5624
6529
  /**
5625
- * Executes post-configuration tasks.
5626
- * @param config - Module configuration.
5627
- * @returns Promise that resolves when post-configuration tasks are complete.
6530
+ * Runs the post-initialize lifecycle phase.
6531
+ *
6532
+ * Delegates to {@link runPostInitializePhase} which calls each module's
6533
+ * `postInitialize` hook and then runs all `onInitialized` callbacks.
6534
+ *
6535
+ * Override this method in a subclass to customize the post-initialize phase.
6536
+ *
6537
+ * @param instance - The sealed module instance from the initialize phase.
6538
+ * @param ref - Optional reference forwarded to each module's `postInitialize` call.
6539
+ * @protected
5628
6540
  */
5629
- async _postConfigure(config) {
5630
- const { modules, _afterConfiguration: afterConfiguration } = this;
5631
- await Promise.allSettled(modules
5632
- .filter((module) => !!module.postConfigure)
5633
- .map(async (module) => {
5634
- try {
5635
- const postConfigStart = performance.now();
5636
- await module.postConfigure?.(config);
5637
- this._registerEvent({
5638
- level: ModuleEventLevel.Debug,
5639
- name: '_postConfigure.modulePostConfigured',
5640
- message: `Module ${module.name} post-configured successfully`,
5641
- properties: {
5642
- moduleName: module.name,
5643
- moduleVersion: module.version?.toString() || 'unknown',
5644
- postConfigTime: Math.round(performance.now() - postConfigStart),
5645
- },
5646
- });
5647
- }
5648
- catch (err) {
5649
- this._registerEvent({
5650
- level: ModuleEventLevel.Warning,
5651
- name: '_postConfigure.modulePostConfigureError',
5652
- message: `Module ${module.name} post-configure failed`,
5653
- properties: {
5654
- moduleName: module.name,
5655
- moduleVersion: module.version?.toString() || 'unknown',
5656
- },
5657
- error: err,
5658
- });
5659
- }
5660
- }));
5661
- /** call all added post config hooks */
5662
- if (afterConfiguration.length) {
5663
- try {
5664
- this._registerEvent({
5665
- level: ModuleEventLevel.Debug,
5666
- name: '_postConfigure.hooks',
5667
- message: `Post configure hooks [${afterConfiguration.length}] called`,
5668
- });
5669
- const postConfigHooksStart = performance.now();
5670
- await Promise.allSettled(afterConfiguration.map((x) => Promise.resolve(x(config))));
5671
- const postConfigHooksTime = Math.round(performance.now() - postConfigHooksStart);
5672
- this._registerEvent({
5673
- level: ModuleEventLevel.Debug,
5674
- name: '_postConfigure.hooksComplete',
5675
- message: 'Post configure hooks complete',
5676
- properties: {
5677
- count: afterConfiguration.length,
5678
- postConfigHooksTime,
5679
- },
5680
- metric: postConfigHooksTime,
5681
- });
5682
- }
5683
- catch (err) {
5684
- this._registerEvent({
5685
- level: ModuleEventLevel.Warning,
5686
- name: '_postConfigure.hooksError',
5687
- message: 'Post configure hook failed',
5688
- error: err,
5689
- });
5690
- }
5691
- }
6541
+ async _postInitialize(instance, ref) {
6542
+ return runPostInitializePhase({
6543
+ modules: this.modules,
6544
+ afterInit: this._afterInit,
6545
+ registerEvent: this._registerEvent.bind(this),
6546
+ }, instance, ref);
5692
6547
  }
5693
6548
  /**
5694
- * Initializes the modules with the provided configuration and reference.
5695
- * @param config - Module configuration.
5696
- * @param ref - Reference object.
5697
- * @returns Promise that resolves to the initialized module instance.
6549
+ * Runs the plugin lifecycle phase.
6550
+ *
6551
+ * Delegates to {@link runPluginPhase} which calls each registered plugin and
6552
+ * stores returned teardown callbacks for dispose.
6553
+ *
6554
+ * Override this method in a subclass to customize plugin registration.
6555
+ *
6556
+ * @param instance - The sealed module instance from the initialize phase.
6557
+ * @param ref - Optional reference forwarded to each plugin callback.
6558
+ * @protected
5698
6559
  */
5699
- async _initialize(config, ref) {
5700
- const moduleNames = this.modules.map((m) => m.name);
5701
- const instance$ = new BehaviorSubject({});
5702
- /** Method to check if a module is defined */
5703
- const hasModule = (name) => moduleNames.includes(name);
5704
- /**
5705
- * Requires and returns an initialized module instance by its name.
5706
- *
5707
- * If the requested module is already initialized, returns it immediately as a resolved Promise.
5708
- * If the module is not yet initialized, waits for its initialization and resolves with the instance,
5709
- * or rejects with a timeout error if the module is not initialized within the specified time.
5710
- *
5711
- * Throws an error immediately if the requested module name is not defined in the current configuration.
5712
- * Also logs relevant events for debugging and error tracking.
5713
- *
5714
- * @template TKey - The key of the module to require, constrained to the keys of the combined modules instance type.
5715
- * @param name - The name of the module to require.
5716
- * @param wait - The maximum time to wait (in seconds) for the module to initialize before timing out. Defaults to 60 seconds.
5717
- * @returns A Promise that resolves with the initialized module instance of type `ModulesInstanceType<CombinedModules<T, TModules>>[TKey]`.
5718
- * @throws {Error} If the module name is not defined.
5719
- * @throws {RequiredModuleTimeoutError} If the module does not initialize within the specified timeout.
5720
- *
5721
- * @example
5722
- * ```typescript
5723
- * const myModule = await requireInstance('myModuleName', 30);
5724
- * ```
5725
- */
5726
- const requireInstance = (name, wait = 60) => {
5727
- /** if module name is not defined, throw error */
5728
- if (!moduleNames.includes(name)) {
5729
- const error = new Error(`Cannot require [${String(name)}] since module is not defined!`);
5730
- error.name = 'ModuleNotDefinedError';
5731
- this._registerEvent({
5732
- level: ModuleEventLevel.Error,
5733
- name: '_initialize.requireInstance.moduleNotDefined',
5734
- message: error.message,
5735
- properties: {
5736
- moduleName: String(name),
5737
- wait,
5738
- },
5739
- error,
5740
- });
5741
- throw error;
5742
- }
5743
- /** if module is already initialized, return it */
5744
- if (instance$.value[name]) {
5745
- this._registerEvent({
5746
- level: ModuleEventLevel.Debug,
5747
- name: '_initialize.requireInstance.moduleAlreadyInitialized',
5748
- message: `Module [${String(name)}] is already initialized, skipping queue`,
5749
- properties: {
5750
- moduleName: String(name),
5751
- wait,
5752
- },
5753
- });
5754
- return Promise.resolve(instance$.value[name]);
5755
- }
5756
- const requireStart = performance.now();
5757
- this._registerEvent({
5758
- level: ModuleEventLevel.Debug,
5759
- name: '_initialize.requireInstance.awaiting',
5760
- message: `Awaiting module [${String(name)}] initialization, timeout ${wait}s`,
5761
- properties: {
5762
- moduleName: String(name),
5763
- wait,
5764
- },
5765
- });
5766
- return firstValueFrom(instance$.pipe(filter((x) => !!x[name]), map$1((x) => x[name]), timeout({
5767
- each: wait * 1000,
5768
- with: () => throwError(() => {
5769
- const error = new RequiredModuleTimeoutError();
5770
- this._registerEvent({
5771
- level: ModuleEventLevel.Error,
5772
- name: '_initialize.requireInstance.timeout',
5773
- message: `Module [${String(name)}] initialization timed out after ${wait}s`,
5774
- properties: {
5775
- moduleName: String(name),
5776
- wait,
5777
- },
5778
- error,
5779
- });
5780
- return error;
5781
- }),
5782
- }), tap(() => {
5783
- const requireTime = Math.round(performance.now() - requireStart);
5784
- this._registerEvent({
5785
- level: ModuleEventLevel.Debug,
5786
- name: '_initialize.requireInstance.moduleResolved',
5787
- message: `Module [${String(name)}] required in ${requireTime}ms`,
5788
- properties: {
5789
- moduleName: String(name),
5790
- wait,
5791
- requireTime,
5792
- },
5793
- metric: requireTime,
5794
- });
5795
- })));
5796
- };
5797
- // Create observable stream to initialize modules concurrently
5798
- // Each module goes through: validation -> initialization -> result mapping
5799
- const init$ = from(this.modules).pipe(
5800
- /** Process each module individually through initialization pipeline */
5801
- mergeMap((module) => {
5802
- const key = module.name;
5803
- if (!module.initialize) {
5804
- const error = new Error(`Module ${module.name} does not have initialize method`);
5805
- error.name = 'ModuleInitializeError';
5806
- this._registerEvent({
5807
- level: ModuleEventLevel.Error,
5808
- name: '_initialize.moduleInitializeError',
5809
- message: error.message,
5810
- properties: {
5811
- moduleName: module.name,
5812
- moduleVersion: module.version?.toString() || 'unknown',
5813
- },
5814
- error,
5815
- });
5816
- throw error;
5817
- }
5818
- this._registerEvent({
5819
- level: ModuleEventLevel.Debug,
5820
- name: '_initialize.moduleInitializing',
5821
- message: `Initializing module ${module.name}`,
5822
- properties: {
5823
- moduleName: module.name,
5824
- moduleVersion: module.version?.toString() || 'unknown',
5825
- },
5826
- });
5827
- const moduleInitStart = performance.now();
5828
- return from(
5829
- // TODO: Replace Promise.resolve + from() with toObservable() for better RxJS patterns
5830
- Promise.resolve(module.initialize({
5831
- ref,
5832
- config: config[key],
5833
- // @ts-ignore
5834
- requireInstance,
5835
- hasModule,
5836
- }))).pipe(map$1((instance) => {
5837
- if (!(instance instanceof BaseModuleProvider)) {
5838
- this._registerEvent({
5839
- level: ModuleEventLevel.Warning,
5840
- name: '_initialize.providerNotBaseModuleProvider',
5841
- message: `Provider for module ${module.name} does not extend BaseModuleProvider`,
5842
- properties: {
5843
- moduleName: module.name,
5844
- moduleVersion: module.version?.toString() || 'unknown',
5845
- },
5846
- });
5847
- }
5848
- if (!instance.version) {
5849
- this._registerEvent({
5850
- level: ModuleEventLevel.Warning,
5851
- name: '_initialize.providerVersionWarning',
5852
- message: `Provider for module ${module.name} does not expose version`,
5853
- properties: {
5854
- moduleName: module.name,
5855
- moduleVersion: module.version?.toString() || 'unknown',
5856
- },
5857
- });
5858
- }
5859
- const moduleInitTime = Math.round(performance.now() - moduleInitStart);
5860
- this._registerEvent({
5861
- level: ModuleEventLevel.Debug,
5862
- name: '_initialize.moduleInitialized',
5863
- message: `Module ${module.name} initialized in ${moduleInitTime}ms`,
5864
- properties: {
5865
- moduleName: module.name,
5866
- moduleVersion: module.version?.toString() || 'unknown',
5867
- providerName: typeof instance,
5868
- providerVersion: instance.version?.toString() || 'unknown',
5869
- moduleInitTime,
5870
- },
5871
- metric: moduleInitTime,
5872
- });
5873
- return [key, instance];
5874
- }));
5875
- }));
5876
- const initStart = performance.now();
5877
- // Subscribe to module initialization stream and accumulate results
5878
- // Each successful initialization updates the shared instance object
5879
- init$
5880
- .pipe(
5881
- /** ensure that the stream is completed even if there are no modules to initialize */
5882
- defaultIfEmpty([]))
5883
- .subscribe({
5884
- next: ([name, module]) => {
5885
- // Accumulate initialized modules into the shared instance object
5886
- instance$.next(Object.assign(instance$.value, { [name]: module }));
5887
- },
5888
- error: (err) => {
5889
- this._registerEvent({
5890
- level: ModuleEventLevel.Error,
5891
- name: '_initialize.moduleInitializationError',
5892
- message: `Failed to initialize module ${err.name || 'unknown'}`,
5893
- error: err,
5894
- });
5895
- instance$.error(err);
5896
- },
5897
- complete: () => {
5898
- const loadTime = Math.round(performance.now() - initStart);
5899
- this._registerEvent({
5900
- level: ModuleEventLevel.Debug,
5901
- name: '_initialize.moduleInitializationComplete',
5902
- message: `All modules initialized in ${loadTime}ms`,
5903
- properties: {
5904
- modules: Object.keys(instance$.value).join(', '),
5905
- loadTime,
5906
- },
5907
- metric: loadTime,
5908
- });
5909
- return instance$.complete();
5910
- },
5911
- });
5912
- /** await creation of all instances */
5913
- const initStartTime = performance.now();
5914
- const instance = await lastValueFrom(instance$);
5915
- const initTime = Math.round(performance.now() - initStartTime);
5916
- this._registerEvent({
5917
- level: ModuleEventLevel.Debug,
5918
- name: '_initialize.complete',
5919
- message: `Modules instance created in ${initTime}ms`,
5920
- properties: {
5921
- modules: Object.keys(instance).join(', '),
5922
- initTime,
5923
- },
5924
- metric: initTime,
5925
- });
5926
- Object.seal(instance);
5927
- return instance;
6560
+ async _registerPlugins(instance, ref) {
6561
+ return runPluginPhase({
6562
+ plugins: this._plugins,
6563
+ teardowns: this._pluginTeardowns,
6564
+ registerEvent: this._registerEvent.bind(this),
6565
+ }, instance, ref);
5928
6566
  }
5929
6567
  /**
5930
- * Executes post-initialization tasks.
5931
- * @param instance - Initialized module instance.
5932
- * @param ref - Reference object.
6568
+ * Tears down all modules managed by this configurator.
6569
+ *
6570
+ * Delegates to {@link runDisposePhase} which calls each module's `dispose`
6571
+ * hook and then completes the internal event stream.
6572
+ *
6573
+ * @param instance - The initialized module instance to tear down.
6574
+ * @param ref - Optional reference forwarded to module dispose hooks.
6575
+ * @returns A promise resolving when all modules have been disposed.
5933
6576
  */
5934
- async _postInitialize(instance, ref) {
5935
- const { modules, _afterInit: afterInit } = this;
5936
- const postInitialize$ = from(modules).pipe(filter((module) => !!module.postInitialize), tap((module) => {
5937
- this._registerEvent({
5938
- level: ModuleEventLevel.Debug,
5939
- name: '_postInitialize.modulePostInitializing',
5940
- message: `Module ${module.name} is being post-initialized`,
5941
- properties: {
5942
- moduleName: module.name,
5943
- moduleVersion: module.version?.toString() || 'unknown',
5944
- },
5945
- });
5946
- }), mergeMap((module) => {
5947
- const postInitStart = performance.now();
5948
- return from(module.postInitialize({
5949
- ref,
5950
- modules: instance,
5951
- instance: instance[module.name],
5952
- })).pipe(tap(() => {
5953
- const postInitTime = Math.round(performance.now() - postInitStart);
5954
- this._registerEvent({
5955
- level: ModuleEventLevel.Debug,
5956
- name: '_postInitialize.modulePostInitialized',
5957
- message: `Module ${module.name} has been post-initialized in ${postInitTime}ms`,
5958
- metric: postInitTime,
5959
- properties: {
5960
- moduleName: module.name,
5961
- moduleVersion: module.version?.toString() || 'unknown',
5962
- postInitTime,
5963
- },
5964
- });
5965
- }), defaultIfEmpty(null), catchError((err) => {
5966
- this._registerEvent({
5967
- level: ModuleEventLevel.Warning,
5968
- name: '_postInitialize.modulePostInitializeError',
5969
- message: `Module ${module.name} post-initialize failed`,
5970
- properties: {
5971
- moduleName: module.name,
5972
- moduleVersion: module.version?.toString() || 'unknown',
5973
- },
5974
- error: err,
5975
- });
5976
- return EMPTY;
5977
- }));
5978
- }), defaultIfEmpty(null));
5979
- this._registerEvent({
5980
- level: ModuleEventLevel.Debug,
5981
- name: '_postInitialize.modulesPostInitializing',
5982
- message: `Post-initializing all modules [${Object.keys(instance).length}]`,
5983
- properties: {
5984
- modules: Object.keys(instance).join(', '),
5985
- },
5986
- });
5987
- const postInitStart = performance.now();
5988
- await lastValueFrom(postInitialize$);
5989
- const postInitTime = Math.round(performance.now() - postInitStart);
5990
- this._registerEvent({
5991
- level: ModuleEventLevel.Debug,
5992
- name: '_postInitialize.modulesPostInitializeComplete',
5993
- message: `Post-initialization of all modules completed in ${postInitTime}ms`,
5994
- properties: {
5995
- modules: Object.keys(instance).join(', '),
5996
- postInitTime: postInitTime,
5997
- },
5998
- metric: postInitTime,
5999
- });
6000
- if (afterInit.length) {
6001
- try {
6002
- this._registerEvent({
6003
- level: ModuleEventLevel.Debug,
6004
- name: '_postInitialize.afterInitHooks',
6005
- message: `Executing post-initialize hooks [${afterInit.length}]`,
6006
- properties: {
6007
- hooks: afterInit.map((x) => x.name || 'anonymous').join(', '),
6008
- },
6009
- });
6010
- const afterInitStart = performance.now();
6011
- await Promise.allSettled(afterInit.map((x) => Promise.resolve(x(instance))));
6012
- const afterInitTime = Math.round(performance.now() - afterInitStart);
6013
- this._registerEvent({
6014
- level: ModuleEventLevel.Debug,
6015
- name: '_postInitialize.afterInitHooksComplete',
6016
- message: `Post-initialize hooks completed in ${afterInitTime}ms`,
6017
- properties: {
6018
- hooks: afterInit.map((x) => x.name || 'anonymous').join(', '),
6019
- afterInitTime,
6020
- },
6021
- metric: afterInitTime,
6022
- });
6023
- }
6024
- catch (err) {
6025
- this._registerEvent({
6026
- level: ModuleEventLevel.Warning,
6027
- name: '_postInitialize.afterInitHooksError',
6028
- message: 'Post-initialize hooks failed',
6029
- properties: {
6030
- hooks: afterInit.map((x) => x.name || 'anonymous').join(', '),
6031
- },
6032
- error: err,
6033
- });
6034
- }
6035
- }
6036
- const postInitCompleteTime = Math.round(performance.now() - postInitStart);
6037
- this._registerEvent({
6038
- level: ModuleEventLevel.Debug,
6039
- name: '_postInitialize.complete',
6040
- message: 'Post-initialization complete',
6041
- properties: {
6042
- modules: Object.keys(instance).join(', '),
6043
- postInitCompleteTime,
6044
- },
6045
- });
6046
- }
6047
- /**
6048
- * Disposes all modules managed by this configurator.\n *\n * Calls each module\u2019s `dispose` hook (if defined) and completes the\n * internal event stream. After disposal the configurator should not be reused.\n *\n * @param instance - The initialized modules instance to tear down.\n * @param ref - Optional reference object forwarded to module dispose hooks.\n */
6049
6577
  async dispose(instance, ref) {
6050
- this._registerEvent({
6051
- level: ModuleEventLevel.Debug,
6052
- name: 'dispose',
6053
- message: 'Disposing modules instance',
6054
- properties: {
6055
- modules: Object.keys(instance).join(', '),
6056
- },
6057
- });
6058
- await Promise.allSettled(this.modules
6059
- .filter((module) => !!module.dispose)
6060
- .map(async (module) => {
6061
- if (!module.dispose)
6062
- return;
6063
- try {
6064
- await module.dispose({
6065
- ref,
6066
- modules: instance,
6067
- instance: instance[module.name],
6068
- });
6069
- this._registerEvent({
6070
- level: ModuleEventLevel.Debug,
6071
- name: 'dispose.moduleDisposed',
6072
- message: `Module ${module.name} disposed successfully`,
6073
- properties: {
6074
- moduleName: module.name,
6075
- moduleVersion: module.version?.toString() || 'unknown',
6076
- },
6077
- });
6078
- }
6079
- catch (err) {
6080
- this._registerEvent({
6081
- level: ModuleEventLevel.Warning,
6082
- name: 'dispose.moduleDisposeError',
6083
- message: `Module ${module.name} dispose failed`,
6084
- properties: {
6085
- moduleName: module.name,
6086
- moduleVersion: module.version?.toString() || 'unknown',
6087
- },
6088
- error: err,
6089
- });
6090
- }
6091
- }));
6092
- this.#event$.complete();
6578
+ return runDisposePhase({
6579
+ modules: this.modules,
6580
+ registerEvent: this._registerEvent.bind(this),
6581
+ // ReplaySubject extends Subject — dispose only needs .complete() which both have.
6582
+ event$: this.#event$,
6583
+ pluginTeardowns: this._pluginTeardowns,
6584
+ }, instance, ref);
6093
6585
  }
6094
6586
  }
6095
6587
 
@@ -21820,7 +22312,7 @@ class HttpClientConfigurator {
21820
22312
  }
21821
22313
 
21822
22314
  // Generated by genversion.
21823
- const version$6 = '8.0.1';
22315
+ const version$6 = '8.0.2';
21824
22316
 
21825
22317
  /**
21826
22318
  * Thrown when `createClient(name)` is called with an unknown client key.
@@ -24129,7 +24621,7 @@ class TelemetryConfigurator extends BaseConfigBuilder {
24129
24621
  }
24130
24622
 
24131
24623
  // Generated by genversion.
24132
- const version$5 = '5.0.2';
24624
+ const version$5 = '6.0.1';
24133
24625
 
24134
24626
  /**
24135
24627
  * Enum representing the severity levels of telemetry items.
@@ -42227,7 +42719,7 @@ const createClientLogCallback = (provider, metadata, scope) => {
42227
42719
  };
42228
42720
 
42229
42721
  // Generated by genversion.
42230
- const version$2 = '8.0.5';
42722
+ const version$2 = '9.0.0';
42231
42723
 
42232
42724
  /**
42233
42725
  * Zod schema for telemetry configuration validation.
@@ -47044,7 +47536,7 @@ async function registerServiceWorker(framework) {
47044
47536
  }
47045
47537
 
47046
47538
  // Generated by genversion.
47047
- const version = '4.0.8';
47539
+ const version = '4.0.10';
47048
47540
 
47049
47541
  // Allow dynamic import without vite
47050
47542
  const importWithoutVite = (path) => import(/* @vite-ignore */ path);