@equinor/fusion-framework-vite-plugin-spa 4.0.13 → 4.0.14
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/CHANGELOG.md +28 -0
- package/dist/esm/html/bootstrap.js +3 -2
- package/dist/esm/html/bootstrap.js.map +1 -1
- package/dist/esm/html/create-portal-entry-point.js +5 -0
- package/dist/esm/html/create-portal-entry-point.js.map +1 -1
- package/dist/esm/html/{index.html.js → html.js} +1 -1
- package/dist/esm/html/html.js.map +1 -0
- package/dist/esm/html/register-service-worker.js +10 -0
- package/dist/esm/html/register-service-worker.js.map +1 -1
- package/dist/esm/html/sw.js +6 -0
- package/dist/esm/html/sw.js.map +1 -1
- package/dist/esm/plugin.js +4 -2
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/util/{load-env.js → load-environment.js} +1 -1
- package/dist/esm/util/load-environment.js.map +1 -0
- package/dist/esm/util/object-to-env.js +2 -0
- package/dist/esm/util/object-to-env.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/html/bootstrap.js +723 -219
- package/dist/html/bootstrap.js.map +1 -1
- package/dist/html/sw.js +6 -0
- package/dist/html/sw.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/html/{index.html.d.ts → html.d.ts} +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +7 -7
- package/src/html/bootstrap.ts +4 -3
- package/src/html/create-portal-entry-point.ts +5 -0
- package/src/html/register-service-worker.ts +10 -0
- package/src/html/sw.ts +6 -0
- package/src/plugin.ts +4 -2
- package/src/util/object-to-env.ts +2 -0
- package/src/version.ts +1 -1
- package/dist/esm/html/index.html.js.map +0 -1
- package/dist/esm/util/load-env.js.map +0 -1
- /package/dist/types/util/{load-env.d.ts → load-environment.d.ts} +0 -0
- /package/src/html/{index.html.ts → html.ts} +0 -0
- /package/src/util/{load-env.ts → load-environment.ts} +0 -0
package/dist/html/bootstrap.js
CHANGED
|
@@ -5093,6 +5093,7 @@ class BaseConfigBuilder {
|
|
|
5093
5093
|
* If a function is provided as `value_or_cb`, it will be used as a callback for deferred or computed configuration values.
|
|
5094
5094
|
* Otherwise, the value is wrapped in an async function for consistency.
|
|
5095
5095
|
*
|
|
5096
|
+
* @template TTarget - The dot-path string representing the target property in the configuration.
|
|
5096
5097
|
* @protected
|
|
5097
5098
|
* @sealed
|
|
5098
5099
|
*/
|
|
@@ -5105,6 +5106,7 @@ class BaseConfigBuilder {
|
|
|
5105
5106
|
*
|
|
5106
5107
|
* @param target - The target path in the configuration to retrieve the callback for.
|
|
5107
5108
|
* @returns The configuration builder callback for the specified target, or `undefined` if no callback is registered.
|
|
5109
|
+
* @template TTarget - The dot-path string representing the target property in the configuration.
|
|
5108
5110
|
* @protected
|
|
5109
5111
|
* @sealed
|
|
5110
5112
|
*/
|
|
@@ -5115,6 +5117,7 @@ class BaseConfigBuilder {
|
|
|
5115
5117
|
* Checks if the given target path exists in the configuration callbacks.
|
|
5116
5118
|
* @param target - The target path to check.
|
|
5117
5119
|
* @returns `true` if the target path exists in the configuration callbacks, `false` otherwise.
|
|
5120
|
+
* @template TTarget - The dot-path string representing the target property in the configuration.
|
|
5118
5121
|
* @protected
|
|
5119
5122
|
* @sealed
|
|
5120
5123
|
*/
|
|
@@ -5168,9 +5171,12 @@ class BaseConfigBuilder {
|
|
|
5168
5171
|
* @readonly
|
|
5169
5172
|
*/
|
|
5170
5173
|
_buildConfig(init, initial) {
|
|
5174
|
+
// Execute every registered config callback and collect its target-value pair
|
|
5171
5175
|
return from(Object.entries(this.#configCallbacks)).pipe(
|
|
5172
5176
|
// Transform each config callback into a target-value pair
|
|
5173
|
-
mergeMap(([target, cb]) =>
|
|
5177
|
+
mergeMap(([target, cb]) =>
|
|
5178
|
+
// Run the callback and shape its result into a target-value pair, skipping void results
|
|
5179
|
+
from(cb(init)).pipe(
|
|
5174
5180
|
// Filter out undefined values, mostly for void return types
|
|
5175
5181
|
filter((value) => value !== undefined),
|
|
5176
5182
|
// Map the value to a target-value pair
|
|
@@ -5225,6 +5231,9 @@ class BaseConfigBuilder {
|
|
|
5225
5231
|
* Indicates a circular dependency or a module that never resolves.
|
|
5226
5232
|
*/
|
|
5227
5233
|
class RequiredModuleTimeoutError extends Error {
|
|
5234
|
+
/**
|
|
5235
|
+
* Creates a new `RequiredModuleTimeoutError`.
|
|
5236
|
+
*/
|
|
5228
5237
|
constructor() {
|
|
5229
5238
|
super('Module initialization timed out');
|
|
5230
5239
|
this.name = 'RequiredModuleTimeoutError';
|
|
@@ -5233,6 +5242,7 @@ class RequiredModuleTimeoutError extends Error {
|
|
|
5233
5242
|
|
|
5234
5243
|
/** Shared base segment for module configurator lifecycle event names. */
|
|
5235
5244
|
const ModuleConfiguratorEventBaseName = 'ModuleConfigurator';
|
|
5245
|
+
|
|
5236
5246
|
/**
|
|
5237
5247
|
* Event names emitted by `ModulesConfigurator` and its lifecycle phase helpers.
|
|
5238
5248
|
*
|
|
@@ -5291,7 +5301,6 @@ const ModuleConfiguratorEventName = {
|
|
|
5291
5301
|
ModulesDisposed: `${ModuleConfiguratorEventBaseName}.modules.disposed`,
|
|
5292
5302
|
};
|
|
5293
5303
|
|
|
5294
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5295
5304
|
/**
|
|
5296
5305
|
* Defines the severity levels for module events.
|
|
5297
5306
|
* Used to categorize events by their importance and impact.
|
|
@@ -5324,10 +5333,13 @@ var ModuleEventLevel;
|
|
|
5324
5333
|
* @param ctx - The configure phase context.
|
|
5325
5334
|
* @param ref - Optional reference forwarded to each module's configure factory.
|
|
5326
5335
|
* @returns A promise resolving to the merged module config map.
|
|
5336
|
+
* @template TRef - Reference type forwarded to module configure hooks.
|
|
5337
|
+
* @throws Re-throws any error from a module's `configure` factory after emitting a failure event.
|
|
5327
5338
|
* @internal
|
|
5328
5339
|
*/
|
|
5329
5340
|
async function createModuleConfigs(ctx, ref) {
|
|
5330
5341
|
const { modules, afterConfiguration, afterInit, registerEvent } = ctx;
|
|
5342
|
+
// Run each module's configure factory concurrently and track timing/failures
|
|
5331
5343
|
const config$ = from(modules).pipe(mergeMap(async (module) => {
|
|
5332
5344
|
const configStart = performance.now();
|
|
5333
5345
|
try {
|
|
@@ -5360,7 +5372,11 @@ async function createModuleConfigs(ctx, ref) {
|
|
|
5360
5372
|
});
|
|
5361
5373
|
throw err;
|
|
5362
5374
|
}
|
|
5363
|
-
}), reduce((acc, module) =>
|
|
5375
|
+
}), reduce((acc, module) => {
|
|
5376
|
+
// Merge each module's config object into the shared accumulator
|
|
5377
|
+
const merged = Object.assign(acc, module);
|
|
5378
|
+
return merged;
|
|
5379
|
+
},
|
|
5364
5380
|
// Seed the config object with hooks so modules can register post-phase callbacks
|
|
5365
5381
|
// during their own configure factory (a common pattern for cross-module wiring).
|
|
5366
5382
|
{
|
|
@@ -5373,6 +5389,7 @@ async function createModuleConfigs(ctx, ref) {
|
|
|
5373
5389
|
}));
|
|
5374
5390
|
return lastValueFrom(config$);
|
|
5375
5391
|
}
|
|
5392
|
+
|
|
5376
5393
|
/**
|
|
5377
5394
|
* Runs the post-configure phase: calls each module's `postConfigure` hook and
|
|
5378
5395
|
* then invokes all registered `afterConfiguration` callbacks.
|
|
@@ -5383,14 +5400,19 @@ async function createModuleConfigs(ctx, ref) {
|
|
|
5383
5400
|
* @param ctx - The configure phase context.
|
|
5384
5401
|
* @param config - The merged module config map produced by {@link createModuleConfigs}.
|
|
5385
5402
|
* @returns A promise resolving when all post-configure hooks have settled.
|
|
5403
|
+
* @template TRef - Reference type forwarded to module configure hooks.
|
|
5386
5404
|
* @internal
|
|
5387
5405
|
*/
|
|
5388
|
-
async function runPostConfigureHooks(ctx,
|
|
5406
|
+
async function runPostConfigureHooks(ctx,
|
|
5407
|
+
// biome-ignore lint/suspicious/noExplicitAny: internal type-erased dispatch — the configure phase coordinates opaque module configs without knowing their concrete shapes
|
|
5408
|
+
config) {
|
|
5389
5409
|
const { modules, afterConfiguration, registerEvent } = ctx;
|
|
5390
5410
|
// Run each module's postConfigure hook; failures are isolated so one
|
|
5391
5411
|
// failing module does not prevent others from completing post-configure.
|
|
5392
5412
|
await Promise.allSettled(modules
|
|
5413
|
+
// Only process modules that define a postConfigure hook
|
|
5393
5414
|
.filter((module) => !!module.postConfigure)
|
|
5415
|
+
// Call postConfigure on each module that defines the hook
|
|
5394
5416
|
.map(async (module) => {
|
|
5395
5417
|
try {
|
|
5396
5418
|
const postConfigStart = performance.now();
|
|
@@ -5419,6 +5441,7 @@ async function runPostConfigureHooks(ctx, config) {
|
|
|
5419
5441
|
});
|
|
5420
5442
|
}
|
|
5421
5443
|
}));
|
|
5444
|
+
// Skip the afterConfiguration hooks entirely when none are registered
|
|
5422
5445
|
if (!afterConfiguration.length)
|
|
5423
5446
|
return;
|
|
5424
5447
|
// Run all registered afterConfiguration callbacks. These were added either
|
|
@@ -5431,7 +5454,9 @@ async function runPostConfigureHooks(ctx, config) {
|
|
|
5431
5454
|
message: `Post configure hooks [${afterConfiguration.length}] called`,
|
|
5432
5455
|
});
|
|
5433
5456
|
const postConfigHooksStart = performance.now();
|
|
5434
|
-
await Promise.allSettled(afterConfiguration
|
|
5457
|
+
await Promise.allSettled(afterConfiguration
|
|
5458
|
+
// Invoke every registered afterConfiguration callback with the final config
|
|
5459
|
+
.map((x) => Promise.resolve(x(config))));
|
|
5435
5460
|
const postConfigHooksTime = Math.round(performance.now() - postConfigHooksStart);
|
|
5436
5461
|
registerEvent({
|
|
5437
5462
|
level: ModuleEventLevel.Debug,
|
|
@@ -5453,6 +5478,7 @@ async function runPostConfigureHooks(ctx, config) {
|
|
|
5453
5478
|
});
|
|
5454
5479
|
}
|
|
5455
5480
|
}
|
|
5481
|
+
|
|
5456
5482
|
/**
|
|
5457
5483
|
* Runs the full configure lifecycle phase for a set of modules.
|
|
5458
5484
|
*
|
|
@@ -5464,12 +5490,15 @@ async function runPostConfigureHooks(ctx, config) {
|
|
|
5464
5490
|
* @param ctx - The configure phase context.
|
|
5465
5491
|
* @param ref - Optional reference forwarded through all configure hooks.
|
|
5466
5492
|
* @returns A promise resolving to the fully configured module config map.
|
|
5493
|
+
* @template TRef - Reference type forwarded to module configure hooks.
|
|
5467
5494
|
*/
|
|
5468
5495
|
async function runConfigurePhase(ctx, ref) {
|
|
5469
5496
|
// Step 1: Create raw config objects for all registered modules
|
|
5470
5497
|
const config = await createModuleConfigs(ctx, ref);
|
|
5471
5498
|
// Step 2: Apply all user-registered configuration callbacks concurrently.
|
|
5472
|
-
await Promise.all(
|
|
5499
|
+
await Promise.all(
|
|
5500
|
+
// Invoke every registered configure callback with the merged config
|
|
5501
|
+
ctx.configs.map((cb) => Promise.resolve(cb(config, ref))));
|
|
5473
5502
|
// Step 3: Run module postConfigure hooks and afterConfiguration callbacks
|
|
5474
5503
|
await runPostConfigureHooks(ctx, config);
|
|
5475
5504
|
return config;
|
|
@@ -5519,6 +5548,7 @@ class BaseModuleProvider {
|
|
|
5519
5548
|
// where inherited properties are acceptable for instanceof checks
|
|
5520
5549
|
'version' in instance &&
|
|
5521
5550
|
'dispose' in instance;
|
|
5551
|
+
// Only coerce version/dispose once the basic shape check has passed
|
|
5522
5552
|
if (hasStructure) {
|
|
5523
5553
|
const obj = instance;
|
|
5524
5554
|
const version = semverExports.coerce(String(obj.version));
|
|
@@ -5529,6 +5559,11 @@ class BaseModuleProvider {
|
|
|
5529
5559
|
}
|
|
5530
5560
|
#version;
|
|
5531
5561
|
#subscriptions;
|
|
5562
|
+
/**
|
|
5563
|
+
* The resolved semantic version of this module provider.
|
|
5564
|
+
*
|
|
5565
|
+
* @returns The provider's semantic version.
|
|
5566
|
+
*/
|
|
5532
5567
|
get version() {
|
|
5533
5568
|
return this.#version;
|
|
5534
5569
|
}
|
|
@@ -5562,92 +5597,6 @@ class BaseModuleProvider {
|
|
|
5562
5597
|
}
|
|
5563
5598
|
}
|
|
5564
5599
|
|
|
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
5600
|
/**
|
|
5652
5601
|
* Creates a `requireInstance` resolver for use during module initialization.
|
|
5653
5602
|
*
|
|
@@ -5660,6 +5609,10 @@ async function initializeModule(module, ctx) {
|
|
|
5660
5609
|
* @param instance$ - Shared subject accumulating initialized module instances.
|
|
5661
5610
|
* @param registerEvent - Function to emit lifecycle events.
|
|
5662
5611
|
* @returns A `requireInstance` function matching the shape expected by `ModuleInitializerArgs`.
|
|
5612
|
+
* @template T - The shape of the accumulated module instance map.
|
|
5613
|
+
* @throws {Error} When the requested module name was never registered.
|
|
5614
|
+
* @throws {RequiredModuleTimeoutError} When the requested module does not
|
|
5615
|
+
* initialize within its timeout window.
|
|
5663
5616
|
* @internal
|
|
5664
5617
|
*/
|
|
5665
5618
|
function createRequireInstance(moduleNames, instance$, registerEvent) {
|
|
@@ -5734,6 +5687,95 @@ function createRequireInstance(moduleNames, instance$, registerEvent) {
|
|
|
5734
5687
|
})));
|
|
5735
5688
|
};
|
|
5736
5689
|
}
|
|
5690
|
+
|
|
5691
|
+
/**
|
|
5692
|
+
* Initializes a single module and emits a `[name, instance]` tuple when complete.
|
|
5693
|
+
*
|
|
5694
|
+
* Validates that the module exposes an `initialize` method, calls it with the
|
|
5695
|
+
* provided context, and emits lifecycle events for start, completion, and any
|
|
5696
|
+
* provider contract warnings.
|
|
5697
|
+
*
|
|
5698
|
+
* @internal
|
|
5699
|
+
*/
|
|
5700
|
+
async function initializeModule(module, ctx) {
|
|
5701
|
+
const { config, ref, requireInstance, hasModule, registerEvent } = ctx;
|
|
5702
|
+
const key = module.name;
|
|
5703
|
+
// Modules must expose an initialize method to participate in the initialize phase
|
|
5704
|
+
if (!module.initialize) {
|
|
5705
|
+
const error = new Error(`Module ${module.name} does not have initialize method`);
|
|
5706
|
+
error.name = 'ModuleInitializeError';
|
|
5707
|
+
registerEvent({
|
|
5708
|
+
level: ModuleEventLevel.Error,
|
|
5709
|
+
name: ModuleConfiguratorEventName.ModuleInitializeError,
|
|
5710
|
+
message: error.message,
|
|
5711
|
+
properties: {
|
|
5712
|
+
moduleName: module.name,
|
|
5713
|
+
moduleVersion: module.version?.toString() || 'unknown',
|
|
5714
|
+
},
|
|
5715
|
+
error,
|
|
5716
|
+
});
|
|
5717
|
+
throw error;
|
|
5718
|
+
}
|
|
5719
|
+
registerEvent({
|
|
5720
|
+
level: ModuleEventLevel.Debug,
|
|
5721
|
+
name: ModuleConfiguratorEventName.ModuleInitializing,
|
|
5722
|
+
message: `Initializing module ${module.name}`,
|
|
5723
|
+
properties: {
|
|
5724
|
+
moduleName: module.name,
|
|
5725
|
+
moduleVersion: module.version?.toString() || 'unknown',
|
|
5726
|
+
},
|
|
5727
|
+
});
|
|
5728
|
+
const moduleInitStart = performance.now();
|
|
5729
|
+
const instance = await module.initialize({
|
|
5730
|
+
ref,
|
|
5731
|
+
config: config[key],
|
|
5732
|
+
requireInstance,
|
|
5733
|
+
hasModule,
|
|
5734
|
+
});
|
|
5735
|
+
// Warn when providers deviate from the expected base class — these modules
|
|
5736
|
+
// may lack version tracking and standard provider interfaces.
|
|
5737
|
+
if (!(instance instanceof BaseModuleProvider)) {
|
|
5738
|
+
registerEvent({
|
|
5739
|
+
level: ModuleEventLevel.Warning,
|
|
5740
|
+
name: ModuleConfiguratorEventName.ProviderNotBaseModuleProvider,
|
|
5741
|
+
message: `Provider for module ${module.name} does not extend BaseModuleProvider`,
|
|
5742
|
+
properties: {
|
|
5743
|
+
moduleName: module.name,
|
|
5744
|
+
moduleVersion: module.version?.toString() || 'unknown',
|
|
5745
|
+
},
|
|
5746
|
+
});
|
|
5747
|
+
}
|
|
5748
|
+
// Providers should expose a version string for diagnostics and compatibility checks.
|
|
5749
|
+
// Warn when absent so module authors catch missing version early rather than at runtime.
|
|
5750
|
+
const maybeVersioned = instance;
|
|
5751
|
+
// Only warn about missing version metadata now that we know it's actually absent
|
|
5752
|
+
if (!maybeVersioned.version) {
|
|
5753
|
+
registerEvent({
|
|
5754
|
+
level: ModuleEventLevel.Warning,
|
|
5755
|
+
name: ModuleConfiguratorEventName.ProviderVersionWarning,
|
|
5756
|
+
message: `Provider for module ${module.name} does not expose version`,
|
|
5757
|
+
properties: {
|
|
5758
|
+
moduleName: module.name,
|
|
5759
|
+
moduleVersion: module.version?.toString() || 'unknown',
|
|
5760
|
+
},
|
|
5761
|
+
});
|
|
5762
|
+
}
|
|
5763
|
+
const moduleInitTime = Math.round(performance.now() - moduleInitStart);
|
|
5764
|
+
registerEvent({
|
|
5765
|
+
level: ModuleEventLevel.Debug,
|
|
5766
|
+
name: ModuleConfiguratorEventName.ModuleInitialized,
|
|
5767
|
+
message: `Module ${module.name} initialized in ${moduleInitTime}ms`,
|
|
5768
|
+
properties: {
|
|
5769
|
+
moduleName: module.name,
|
|
5770
|
+
moduleVersion: module.version?.toString() || 'unknown',
|
|
5771
|
+
providerName: typeof instance,
|
|
5772
|
+
providerVersion: maybeVersioned.version?.toString() || 'unknown',
|
|
5773
|
+
moduleInitTime,
|
|
5774
|
+
},
|
|
5775
|
+
metric: moduleInitTime,
|
|
5776
|
+
});
|
|
5777
|
+
return [key, instance];
|
|
5778
|
+
}
|
|
5737
5779
|
/**
|
|
5738
5780
|
* Runs the initialize lifecycle phase for all registered modules.
|
|
5739
5781
|
*
|
|
@@ -5748,6 +5790,7 @@ function createRequireInstance(moduleNames, instance$, registerEvent) {
|
|
|
5748
5790
|
* @param config - The merged module config map produced by the configure phase.
|
|
5749
5791
|
* @param ref - Optional reference forwarded to each module's `initialize` call.
|
|
5750
5792
|
* @returns A promise resolving to the sealed map of initialized module providers.
|
|
5793
|
+
* @template T - The shape of the resolved module instance map.
|
|
5751
5794
|
* @throws {Error} When a module's `initialize` method is missing.
|
|
5752
5795
|
* @throws {RequiredModuleTimeoutError} When a required dependency does not
|
|
5753
5796
|
* initialize within its timeout window.
|
|
@@ -5759,6 +5802,7 @@ async function runInitializePhase(ctx, config, ref) {
|
|
|
5759
5802
|
if (modules.length === 0) {
|
|
5760
5803
|
return Object.seal({});
|
|
5761
5804
|
}
|
|
5805
|
+
// Extract module names for dependency lookups before any module has initialized
|
|
5762
5806
|
const moduleNames = modules.map((m) => m.name);
|
|
5763
5807
|
// Accumulates initialized module providers; BehaviorSubject lets requireInstance
|
|
5764
5808
|
// reactively wait for a dependency to appear without polling.
|
|
@@ -5773,7 +5817,9 @@ async function runInitializePhase(ctx, config, ref) {
|
|
|
5773
5817
|
// Completing the subject signals that all modules are initialized.
|
|
5774
5818
|
init$.subscribe({
|
|
5775
5819
|
next: ([name, module]) => {
|
|
5776
|
-
|
|
5820
|
+
// Merge the newly initialized module into the shared instance map without dropping peers
|
|
5821
|
+
const nextInstance = Object.assign(instance$.value, { [name]: module });
|
|
5822
|
+
instance$.next(nextInstance);
|
|
5777
5823
|
},
|
|
5778
5824
|
error: (err) => {
|
|
5779
5825
|
registerEvent({
|
|
@@ -5842,7 +5888,9 @@ async function runPostInitializePhase(ctx, instance, ref) {
|
|
|
5842
5888
|
});
|
|
5843
5889
|
// Run postInitialize hooks for modules that declare them. Failures are caught
|
|
5844
5890
|
// per-module via catchError so one failure does not abort the others.
|
|
5845
|
-
const postInitialize$ = from(modules).pipe(
|
|
5891
|
+
const postInitialize$ = from(modules).pipe(
|
|
5892
|
+
// Only process modules that define a postInitialize hook
|
|
5893
|
+
filter((module) => !!module.postInitialize), tap((module) => {
|
|
5846
5894
|
registerEvent({
|
|
5847
5895
|
level: ModuleEventLevel.Debug,
|
|
5848
5896
|
name: ModuleConfiguratorEventName.ModulePostInitializeStart,
|
|
@@ -5854,6 +5902,7 @@ async function runPostInitializePhase(ctx, instance, ref) {
|
|
|
5854
5902
|
});
|
|
5855
5903
|
}), mergeMap((module) => {
|
|
5856
5904
|
const postInitStart = performance.now();
|
|
5905
|
+
// Emit completion/error events for this module's postInitialize call as it settles
|
|
5857
5906
|
return from(module.postInitialize({
|
|
5858
5907
|
ref,
|
|
5859
5908
|
modules: instance,
|
|
@@ -5897,6 +5946,7 @@ async function runPostInitializePhase(ctx, instance, ref) {
|
|
|
5897
5946
|
properties: { modules: Object.keys(instance).join(', '), postInitTime },
|
|
5898
5947
|
metric: postInitTime,
|
|
5899
5948
|
});
|
|
5949
|
+
// Skip the afterInit hooks entirely when none are registered
|
|
5900
5950
|
if (!afterInit.length) {
|
|
5901
5951
|
registerEvent({
|
|
5902
5952
|
level: ModuleEventLevel.Debug,
|
|
@@ -5909,32 +5959,40 @@ async function runPostInitializePhase(ctx, instance, ref) {
|
|
|
5909
5959
|
// Run all registered afterInit callbacks. These were added either by
|
|
5910
5960
|
// addConfig's afterInit or via onInitialized on the configurator.
|
|
5911
5961
|
try {
|
|
5962
|
+
// Build a display string of registered hook names for the start event
|
|
5963
|
+
// Extract each hook's name, falling back for anonymous functions
|
|
5964
|
+
const hookNames = afterInit.map((x) => x.name || 'anonymous').join(', ');
|
|
5912
5965
|
registerEvent({
|
|
5913
5966
|
level: ModuleEventLevel.Debug,
|
|
5914
5967
|
name: ModuleConfiguratorEventName.PostInitializeHooks,
|
|
5915
5968
|
message: `Executing post-initialize hooks [${afterInit.length}]`,
|
|
5916
|
-
properties: { hooks:
|
|
5969
|
+
properties: { hooks: hookNames },
|
|
5917
5970
|
});
|
|
5918
5971
|
const afterInitStart = performance.now();
|
|
5919
|
-
await Promise.allSettled(afterInit
|
|
5972
|
+
await Promise.allSettled(afterInit
|
|
5973
|
+
// Invoke every registered afterInit callback with the initialized module instance map
|
|
5974
|
+
.map((x) => Promise.resolve(x(instance))));
|
|
5920
5975
|
const afterInitTime = Math.round(performance.now() - afterInitStart);
|
|
5921
5976
|
registerEvent({
|
|
5922
5977
|
level: ModuleEventLevel.Debug,
|
|
5923
5978
|
name: ModuleConfiguratorEventName.PostInitializeHooksComplete,
|
|
5924
5979
|
message: `Post-initialize hooks completed in ${afterInitTime}ms`,
|
|
5925
5980
|
properties: {
|
|
5926
|
-
hooks:
|
|
5981
|
+
hooks: hookNames,
|
|
5927
5982
|
afterInitTime,
|
|
5928
5983
|
},
|
|
5929
5984
|
metric: afterInitTime,
|
|
5930
5985
|
});
|
|
5931
5986
|
}
|
|
5932
5987
|
catch (err) {
|
|
5988
|
+
// Build a display string of registered hook names for the error event
|
|
5989
|
+
// Extract each hook's name, falling back for anonymous functions
|
|
5990
|
+
const hookNames = afterInit.map((x) => x.name || 'anonymous').join(', ');
|
|
5933
5991
|
registerEvent({
|
|
5934
5992
|
level: ModuleEventLevel.Warning,
|
|
5935
5993
|
name: ModuleConfiguratorEventName.PostInitializeHooksError,
|
|
5936
5994
|
message: 'Post-initialize hooks failed',
|
|
5937
|
-
properties: { hooks:
|
|
5995
|
+
properties: { hooks: hookNames },
|
|
5938
5996
|
error: err,
|
|
5939
5997
|
});
|
|
5940
5998
|
}
|
|
@@ -5984,9 +6042,11 @@ function isPluginTeardown(value) {
|
|
|
5984
6042
|
* @param modules - The initialized module instance map.
|
|
5985
6043
|
* @param ref - Optional reference forwarded from module initialization.
|
|
5986
6044
|
* @returns A promise resolving when all plugin callbacks have settled.
|
|
6045
|
+
* @template TRef - Reference type forwarded to plugin callbacks.
|
|
5987
6046
|
*/
|
|
5988
6047
|
async function runPluginPhase(ctx, modules, ref) {
|
|
5989
6048
|
const { plugins, teardowns, registerEvent } = ctx;
|
|
6049
|
+
// Skip the plugin phase entirely when no plugins were registered
|
|
5990
6050
|
if (!plugins.length)
|
|
5991
6051
|
return;
|
|
5992
6052
|
registerEvent({
|
|
@@ -5995,7 +6055,9 @@ async function runPluginPhase(ctx, modules, ref) {
|
|
|
5995
6055
|
message: `Registering plugins [${plugins.length}]`,
|
|
5996
6056
|
properties: { count: plugins.length },
|
|
5997
6057
|
});
|
|
5998
|
-
const pluginRegistrations = await Promise.all(plugins
|
|
6058
|
+
const pluginRegistrations = await Promise.all(plugins
|
|
6059
|
+
// Run every plugin callback concurrently so one slow plugin cannot delay the others
|
|
6060
|
+
.map(async (plugin) => {
|
|
5999
6061
|
const pluginStart = performance.now();
|
|
6000
6062
|
const name = plugin.name || 'anonymous';
|
|
6001
6063
|
try {
|
|
@@ -6021,7 +6083,9 @@ async function runPluginPhase(ctx, modules, ref) {
|
|
|
6021
6083
|
return undefined;
|
|
6022
6084
|
}
|
|
6023
6085
|
}));
|
|
6086
|
+
// Queue every plugin teardown returned above so the dispose phase can invoke them later
|
|
6024
6087
|
for (const teardown of pluginRegistrations) {
|
|
6088
|
+
// Only queue teardowns for plugins that actually returned one
|
|
6025
6089
|
if (teardown) {
|
|
6026
6090
|
teardowns.push(teardown);
|
|
6027
6091
|
}
|
|
@@ -6038,6 +6102,7 @@ function getPluginTeardownName(teardown) {
|
|
|
6038
6102
|
return typeof teardown === 'function' ? teardown.name || 'anonymous' : 'dispose';
|
|
6039
6103
|
}
|
|
6040
6104
|
async function runPluginTeardown(teardown) {
|
|
6105
|
+
// Function-shaped teardowns are called directly; object-shaped ones expose a dispose() method
|
|
6041
6106
|
if (typeof teardown === 'function') {
|
|
6042
6107
|
await teardown();
|
|
6043
6108
|
return;
|
|
@@ -6065,6 +6130,7 @@ async function runDisposePhase(ctx, instance, ref) {
|
|
|
6065
6130
|
message: 'Disposing modules instance',
|
|
6066
6131
|
properties: { modules: Object.keys(instance).join(', ') },
|
|
6067
6132
|
});
|
|
6133
|
+
// Only run the plugin teardown sequence when plugins were actually registered
|
|
6068
6134
|
if (pluginTeardowns.length) {
|
|
6069
6135
|
registerEvent({
|
|
6070
6136
|
level: ModuleEventLevel.Debug,
|
|
@@ -6105,8 +6171,11 @@ async function runDisposePhase(ctx, instance, ref) {
|
|
|
6105
6171
|
// Dispose all modules concurrently; failures are isolated per module so
|
|
6106
6172
|
// one bad teardown cannot leave other modules in an inconsistent state.
|
|
6107
6173
|
await Promise.allSettled(modules
|
|
6174
|
+
// Only process modules that define a dispose hook
|
|
6108
6175
|
.filter((module) => !!module.dispose)
|
|
6176
|
+
// Dispose each module that defines the hook
|
|
6109
6177
|
.map(async (module) => {
|
|
6178
|
+
// Narrow the type for TypeScript; the filter above already guarantees this
|
|
6110
6179
|
if (!module.dispose)
|
|
6111
6180
|
return;
|
|
6112
6181
|
try {
|
|
@@ -6150,7 +6219,7 @@ async function runDisposePhase(ctx, instance, ref) {
|
|
|
6150
6219
|
}
|
|
6151
6220
|
|
|
6152
6221
|
// Generated by genversion.
|
|
6153
|
-
const version$8 = '6.1.
|
|
6222
|
+
const version$8 = '6.1.1';
|
|
6154
6223
|
|
|
6155
6224
|
// 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
6225
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -6204,6 +6273,11 @@ class ModulesConfigurator {
|
|
|
6204
6273
|
* Preserved as a static string so minification cannot change it at runtime.
|
|
6205
6274
|
*/
|
|
6206
6275
|
static className = 'ModulesConfigurator';
|
|
6276
|
+
/**
|
|
6277
|
+
* The current package version of the module configurator.
|
|
6278
|
+
*
|
|
6279
|
+
* @returns The semantic version string.
|
|
6280
|
+
*/
|
|
6207
6281
|
get version() {
|
|
6208
6282
|
return version$8;
|
|
6209
6283
|
}
|
|
@@ -6213,6 +6287,12 @@ class ModulesConfigurator {
|
|
|
6213
6287
|
// configuration before telemetry is wired up.
|
|
6214
6288
|
// Memory bound: ~24 KB at ~240 bytes/event × 100 events.
|
|
6215
6289
|
#event$ = new ReplaySubject(100);
|
|
6290
|
+
/**
|
|
6291
|
+
* Stream of lifecycle events emitted while modules are configured, initialized,
|
|
6292
|
+
* and disposed.
|
|
6293
|
+
*
|
|
6294
|
+
* @returns An observable of {@link ModuleEvent} entries.
|
|
6295
|
+
*/
|
|
6216
6296
|
get event$() {
|
|
6217
6297
|
return this.#event$.asObservable();
|
|
6218
6298
|
}
|
|
@@ -6293,6 +6373,7 @@ class ModulesConfigurator {
|
|
|
6293
6373
|
* @param configs - One or more module configurator descriptors.
|
|
6294
6374
|
*/
|
|
6295
6375
|
configure(...configs) {
|
|
6376
|
+
// Delegate each descriptor to addConfig so registration logic stays in one place
|
|
6296
6377
|
for (const x of configs) {
|
|
6297
6378
|
this.addConfig(x);
|
|
6298
6379
|
}
|
|
@@ -6306,6 +6387,7 @@ class ModulesConfigurator {
|
|
|
6306
6387
|
*
|
|
6307
6388
|
* @param config - The module configurator descriptor to register.
|
|
6308
6389
|
* @template T - The module type being registered.
|
|
6390
|
+
* @template TConfig - The resolved configuration type for the module.
|
|
6309
6391
|
*/
|
|
6310
6392
|
addConfig(config) {
|
|
6311
6393
|
const { module, afterConfig, afterInit, configure } = config;
|
|
@@ -6325,8 +6407,10 @@ class ModulesConfigurator {
|
|
|
6325
6407
|
// Register each optional callback into its corresponding lifecycle phase array
|
|
6326
6408
|
if (configure)
|
|
6327
6409
|
this._configs.push((cfg, ref) => configure(cfg[module.name], ref));
|
|
6410
|
+
// Register the afterConfig callback, if provided
|
|
6328
6411
|
if (afterConfig)
|
|
6329
6412
|
this._afterConfiguration.push((cfg) => afterConfig(cfg[module.name]));
|
|
6413
|
+
// Register the afterInit callback, if provided
|
|
6330
6414
|
if (afterInit)
|
|
6331
6415
|
this._afterInit.push((instances) => afterInit(instances[module.name]));
|
|
6332
6416
|
}
|
|
@@ -6418,17 +6502,21 @@ class ModulesConfigurator {
|
|
|
6418
6502
|
* @param ref - Optional reference forwarded to all module lifecycle hooks.
|
|
6419
6503
|
* @returns A promise resolving to the sealed, initialized module instance.
|
|
6420
6504
|
* @template T - Additional modules to merge into the instance type.
|
|
6505
|
+
* @template R - The reference type, narrowed to `TRef`.
|
|
6421
6506
|
*/
|
|
6422
6507
|
async initialize(ref) {
|
|
6423
6508
|
const configStart = performance.now();
|
|
6424
6509
|
const config = await this._configure(ref);
|
|
6425
6510
|
const configLoadTime = Math.round(performance.now() - configStart);
|
|
6511
|
+
// Build a comma-separated module name list for telemetry properties
|
|
6512
|
+
// Extract just the module names before joining into a display string
|
|
6513
|
+
const configModuleNames = this.modules.map((m) => m.name).join(', ');
|
|
6426
6514
|
this._registerEvent({
|
|
6427
6515
|
level: ModuleEventLevel.Debug,
|
|
6428
6516
|
name: ModuleConfiguratorEventName.InitializeConfigLoaded,
|
|
6429
6517
|
message: `Modules configured in ${configLoadTime}ms`,
|
|
6430
6518
|
properties: {
|
|
6431
|
-
modules:
|
|
6519
|
+
modules: configModuleNames,
|
|
6432
6520
|
count: this.modules.length,
|
|
6433
6521
|
loadTime: configLoadTime,
|
|
6434
6522
|
},
|
|
@@ -6437,24 +6525,30 @@ class ModulesConfigurator {
|
|
|
6437
6525
|
const instanceStart = performance.now();
|
|
6438
6526
|
const instance = await this._initialize(config, ref);
|
|
6439
6527
|
const instanceLoadTime = Math.round(performance.now() - instanceStart);
|
|
6528
|
+
// Build a comma-separated module name list for telemetry properties
|
|
6529
|
+
// Extract just the module names before joining into a display string
|
|
6530
|
+
const instanceModuleNames = this.modules.map((m) => m.name).join(', ');
|
|
6440
6531
|
this._registerEvent({
|
|
6441
6532
|
level: ModuleEventLevel.Debug,
|
|
6442
6533
|
name: ModuleConfiguratorEventName.InitializeInstanceInitialized,
|
|
6443
6534
|
message: `Modules initialized in ${instanceLoadTime}ms`,
|
|
6444
6535
|
properties: {
|
|
6445
|
-
modules:
|
|
6536
|
+
modules: instanceModuleNames,
|
|
6446
6537
|
count: this.modules.length,
|
|
6447
6538
|
loadTime: instanceLoadTime,
|
|
6448
6539
|
},
|
|
6449
6540
|
metric: instanceLoadTime,
|
|
6450
6541
|
});
|
|
6451
6542
|
const totalLoadTime = configLoadTime + instanceLoadTime;
|
|
6543
|
+
// Build a comma-separated module name list for telemetry properties
|
|
6544
|
+
// Extract just the module names before joining into a display string
|
|
6545
|
+
const totalModuleNames = this.modules.map((m) => m.name).join(', ');
|
|
6452
6546
|
this._registerEvent({
|
|
6453
6547
|
level: ModuleEventLevel.Information,
|
|
6454
6548
|
name: ModuleConfiguratorEventName.Initialize,
|
|
6455
6549
|
message: `initialize in ${totalLoadTime}ms`,
|
|
6456
6550
|
properties: {
|
|
6457
|
-
modules:
|
|
6551
|
+
modules: totalModuleNames,
|
|
6458
6552
|
configLoadTime,
|
|
6459
6553
|
instanceLoadTime,
|
|
6460
6554
|
totalLoadTime,
|
|
@@ -6462,6 +6556,8 @@ class ModulesConfigurator {
|
|
|
6462
6556
|
metric: totalLoadTime,
|
|
6463
6557
|
});
|
|
6464
6558
|
await this._postInitialize(instance, ref);
|
|
6559
|
+
// `instance` is the freshly built module instance record, which is structurally compatible
|
|
6560
|
+
// with `ModulesInstance<TModules>` but not nominally assignable across the generic params.
|
|
6465
6561
|
const modules = Object.seal(Object.assign({}, instance, {
|
|
6466
6562
|
dispose: () => this.dispose(instance),
|
|
6467
6563
|
}));
|
|
@@ -6496,6 +6592,8 @@ class ModulesConfigurator {
|
|
|
6496
6592
|
*
|
|
6497
6593
|
* @param ref - Optional reference forwarded to module configure factories.
|
|
6498
6594
|
* @returns A promise resolving to the merged module config map.
|
|
6595
|
+
* @template T - Additional modules to merge into the instance type.
|
|
6596
|
+
* @template R - The reference type, narrowed to `TRef`.
|
|
6499
6597
|
* @protected
|
|
6500
6598
|
*/
|
|
6501
6599
|
async _configure(ref) {
|
|
@@ -6518,6 +6616,8 @@ class ModulesConfigurator {
|
|
|
6518
6616
|
* @param config - The merged module config map from the configure phase.
|
|
6519
6617
|
* @param ref - Optional reference forwarded to each module's `initialize` call.
|
|
6520
6618
|
* @returns A promise resolving to the sealed map of initialized module providers.
|
|
6619
|
+
* @template T - Additional modules to merge into the instance type.
|
|
6620
|
+
* @template R - The reference type. Defaults to `TRef`.
|
|
6521
6621
|
* @protected
|
|
6522
6622
|
*/
|
|
6523
6623
|
async _initialize(config, ref) {
|
|
@@ -6536,6 +6636,8 @@ class ModulesConfigurator {
|
|
|
6536
6636
|
*
|
|
6537
6637
|
* @param instance - The sealed module instance from the initialize phase.
|
|
6538
6638
|
* @param ref - Optional reference forwarded to each module's `postInitialize` call.
|
|
6639
|
+
* @template T - Additional modules to merge into the instance type.
|
|
6640
|
+
* @template R - The reference type. Defaults to `TRef`.
|
|
6539
6641
|
* @protected
|
|
6540
6642
|
*/
|
|
6541
6643
|
async _postInitialize(instance, ref) {
|
|
@@ -6555,6 +6657,8 @@ class ModulesConfigurator {
|
|
|
6555
6657
|
*
|
|
6556
6658
|
* @param instance - The sealed module instance from the initialize phase.
|
|
6557
6659
|
* @param ref - Optional reference forwarded to each plugin callback.
|
|
6660
|
+
* @template T - Additional modules to merge into the instance type.
|
|
6661
|
+
* @template R - The reference type, narrowed to `TRef`.
|
|
6558
6662
|
* @protected
|
|
6559
6663
|
*/
|
|
6560
6664
|
async _registerPlugins(instance, ref) {
|
|
@@ -6612,6 +6716,7 @@ class ProcessOperators {
|
|
|
6612
6716
|
* It can be either an instance of IProcessOperators<T> or a record of string keys and ProcessOperator<T> values.
|
|
6613
6717
|
*/
|
|
6614
6718
|
constructor(operators) {
|
|
6719
|
+
// accept either a raw operators record or another IProcessOperators instance to clone from
|
|
6615
6720
|
if (operators && 'operators' in operators) {
|
|
6616
6721
|
this._operators = { ...operators.operators };
|
|
6617
6722
|
}
|
|
@@ -6627,6 +6732,7 @@ class ProcessOperators {
|
|
|
6627
6732
|
* @throws Error if an operator with the same key already exists.
|
|
6628
6733
|
*/
|
|
6629
6734
|
add(key, operator) {
|
|
6735
|
+
// guard against silently overwriting an existing operator under the same key
|
|
6630
6736
|
if (Object.keys(this._operators).includes(key))
|
|
6631
6737
|
throw Error(`Operator [${key}] already defined`);
|
|
6632
6738
|
return this.set(key, operator);
|
|
@@ -6670,6 +6776,7 @@ class ProcessOperators {
|
|
|
6670
6776
|
if (!operators.length) {
|
|
6671
6777
|
return of(request);
|
|
6672
6778
|
}
|
|
6779
|
+
// feed the request through each operator sequentially, keeping the previous value when one returns void
|
|
6673
6780
|
return from(Object.values(this._operators)).pipe(mergeScan(
|
|
6674
6781
|
// resolve current operator and return result or previous if void
|
|
6675
6782
|
(value, operator) => Promise.resolve(operator(value)).then((x) => x ?? value),
|
|
@@ -6714,7 +6821,7 @@ class HttpRequestHandler extends ProcessOperators {
|
|
|
6714
6821
|
*/
|
|
6715
6822
|
setHeader(key, value) {
|
|
6716
6823
|
const operator = requestOperatorHeader(key, value);
|
|
6717
|
-
return this.set(
|
|
6824
|
+
return this.set(`header-${key}`, operator);
|
|
6718
6825
|
}
|
|
6719
6826
|
}
|
|
6720
6827
|
|
|
@@ -22018,7 +22125,9 @@ const fetchRequestSchema = requestInitSchema.extend({
|
|
|
22018
22125
|
const capitalizeRequestMethodOperator = (options) => (request) => {
|
|
22019
22126
|
const { error, success, data } = requestMethodCasing().safeParse(request.method);
|
|
22020
22127
|
request.method = success ? data : request.method?.toUpperCase();
|
|
22128
|
+
// surface schema validation issues as warnings when not running silently
|
|
22021
22129
|
if (error && true) {
|
|
22130
|
+
// one warning per issue so callers can see exactly what failed to validate
|
|
22022
22131
|
for (const e of error.issues) {
|
|
22023
22132
|
console.warn(e.message);
|
|
22024
22133
|
}
|
|
@@ -22047,6 +22156,7 @@ const requestValidationOperator = (options) => (request) => {
|
|
|
22047
22156
|
return parse ? result : void 0;
|
|
22048
22157
|
}
|
|
22049
22158
|
catch (error) {
|
|
22159
|
+
// re-throw so callers relying on `parse` can react to validation failures directly
|
|
22050
22160
|
if (parse) {
|
|
22051
22161
|
throw error;
|
|
22052
22162
|
}
|
|
@@ -22061,11 +22171,17 @@ const requestValidationOperator = (options) => (request) => {
|
|
|
22061
22171
|
class HttpResponseError extends Error {
|
|
22062
22172
|
response;
|
|
22063
22173
|
static Name = 'HttpResponseError';
|
|
22174
|
+
/**
|
|
22175
|
+
* @param message - The error message.
|
|
22176
|
+
* @param response - The HTTP response associated with the error.
|
|
22177
|
+
* @param options - Additional error options.
|
|
22178
|
+
*/
|
|
22064
22179
|
constructor(message, response, options) {
|
|
22065
22180
|
super(message, options);
|
|
22066
22181
|
this.response = response;
|
|
22067
22182
|
}
|
|
22068
22183
|
}
|
|
22184
|
+
|
|
22069
22185
|
/**
|
|
22070
22186
|
* Represents an error that occurs when handling a JSON response in an HTTP request.
|
|
22071
22187
|
* Extends the base `HttpResponseError` class.
|
|
@@ -22089,6 +22205,7 @@ class HttpJsonResponseError extends HttpResponseError {
|
|
|
22089
22205
|
this.data = options?.data;
|
|
22090
22206
|
}
|
|
22091
22207
|
}
|
|
22208
|
+
|
|
22092
22209
|
/**
|
|
22093
22210
|
* Represents an error that occurs when handling a server-sent event (SSE) HTTP response.
|
|
22094
22211
|
*
|
|
@@ -22112,6 +22229,15 @@ class ServerSentEventResponseError extends HttpResponseError {
|
|
|
22112
22229
|
}
|
|
22113
22230
|
}
|
|
22114
22231
|
|
|
22232
|
+
/**
|
|
22233
|
+
* Thrown when `createClient(name)` is called with an unknown client key.
|
|
22234
|
+
*
|
|
22235
|
+
* This is only used when the provided string is neither a registered client name
|
|
22236
|
+
* nor an absolute `http:` or `https:` URL.
|
|
22237
|
+
*/
|
|
22238
|
+
class ClientNotFoundException extends Error {
|
|
22239
|
+
}
|
|
22240
|
+
|
|
22115
22241
|
const defaultDataParser = (data) => {
|
|
22116
22242
|
try {
|
|
22117
22243
|
return JSON.parse(data);
|
|
@@ -22205,28 +22331,38 @@ async function* readStream(reader, options) {
|
|
|
22205
22331
|
: [options.eventFilter]
|
|
22206
22332
|
: null;
|
|
22207
22333
|
const decoder = new TextDecoder();
|
|
22334
|
+
// keep reading chunks from the stream until the reader signals completion
|
|
22208
22335
|
while (true) {
|
|
22209
22336
|
const { done, value } = await reader.read();
|
|
22337
|
+
// the underlying stream has ended, nothing more to read
|
|
22210
22338
|
if (done) {
|
|
22339
|
+
// exit the read loop rather than yielding any further events
|
|
22211
22340
|
break;
|
|
22212
22341
|
}
|
|
22213
22342
|
const text = decoder.decode(value, { stream: true });
|
|
22214
22343
|
const events = parseEvents(text, { dataParser: options?.dataParser });
|
|
22344
|
+
// a chunk may contain multiple complete SSE events, emit each in turn
|
|
22215
22345
|
for (const event of events) {
|
|
22346
|
+
// a retry directive reconfigures the reconnection delay rather than being a data event
|
|
22216
22347
|
if (event.retry) {
|
|
22217
22348
|
await new Promise((resolve) => setTimeout(resolve, Number.parseInt(event.retry ?? '300', 10)));
|
|
22349
|
+
// nothing to yield for a retry-only event
|
|
22218
22350
|
continue;
|
|
22219
22351
|
}
|
|
22352
|
+
// heartbeat filtering is opt-in via the skipHeartbeats option
|
|
22220
22353
|
if (skipHeartbeats) {
|
|
22221
22354
|
// Skip comment-based heartbeats (no event, data, or id)
|
|
22222
22355
|
if (!event.event && !event.data && !event.id) {
|
|
22356
|
+
// this event carries no payload, so treat it as a heartbeat and skip it
|
|
22223
22357
|
continue;
|
|
22224
22358
|
}
|
|
22225
22359
|
// Skip named heartbeat events (e.g., event: heartbeat or event: ping)
|
|
22226
22360
|
if (event.event && ['heartbeat', 'ping'].includes(event.event)) {
|
|
22361
|
+
// an explicitly named heartbeat/ping event should also be skipped
|
|
22227
22362
|
continue;
|
|
22228
22363
|
}
|
|
22229
22364
|
}
|
|
22365
|
+
// only emit events that pass the caller-provided event type filter, if any
|
|
22230
22366
|
if (!eventFilter || (event.event && eventFilter.includes(event.event))) {
|
|
22231
22367
|
yield event;
|
|
22232
22368
|
}
|
|
@@ -22246,21 +22382,26 @@ async function* readStream(reader, options) {
|
|
|
22246
22382
|
*/
|
|
22247
22383
|
const createSseSelector = (options) => {
|
|
22248
22384
|
return (response) => {
|
|
22385
|
+
// an unsuccessful HTTP status means there is no valid SSE stream to read
|
|
22249
22386
|
if (!response.ok) {
|
|
22250
22387
|
throw new ServerSentEventResponseError(`HTTP error! Status: ${response.status}`, response);
|
|
22251
22388
|
}
|
|
22389
|
+
// a missing body means there is nothing to stream from
|
|
22252
22390
|
if (!response.body) {
|
|
22253
22391
|
throw new ServerSentEventResponseError('Response body is not readable', response);
|
|
22254
22392
|
}
|
|
22393
|
+
// guard against consuming a non-SSE response as if it were an event stream
|
|
22255
22394
|
if (!response.headers.get('Content-Type')?.includes('text/event-stream')) {
|
|
22256
22395
|
throw new ServerSentEventResponseError('Response is not a text/event-stream', response);
|
|
22257
22396
|
}
|
|
22258
22397
|
const reader = response.body.getReader();
|
|
22259
|
-
return from(readStream(reader, {
|
|
22398
|
+
return (from(readStream(reader, {
|
|
22260
22399
|
dataParser: options?.dataParser,
|
|
22261
22400
|
skipHeartbeats: options?.skipHeartbeats,
|
|
22262
22401
|
eventFilter: options?.eventFilter,
|
|
22263
|
-
}))
|
|
22402
|
+
}))
|
|
22403
|
+
// stop the stream on abort and always release the underlying reader when done
|
|
22404
|
+
.pipe(
|
|
22264
22405
|
// Stop reading if the abort signal is triggered
|
|
22265
22406
|
takeUntil(options?.abortSignal ? fromEvent(options.abortSignal, 'abort') : EMPTY), finalize$2(async () => {
|
|
22266
22407
|
// cancel just in case of a pre-mature exit
|
|
@@ -22268,14 +22409,18 @@ const createSseSelector = (options) => {
|
|
|
22268
22409
|
/** ignore cancellation errors */
|
|
22269
22410
|
});
|
|
22270
22411
|
reader.releaseLock();
|
|
22271
|
-
}));
|
|
22412
|
+
})));
|
|
22272
22413
|
};
|
|
22273
22414
|
};
|
|
22274
22415
|
|
|
22275
22416
|
/** @inheritdoc */
|
|
22276
22417
|
class HttpClientConfigurator {
|
|
22277
22418
|
_clients = {};
|
|
22278
|
-
/**
|
|
22419
|
+
/**
|
|
22420
|
+
* Gets a shallow clone of all named client configurations.
|
|
22421
|
+
*
|
|
22422
|
+
* @returns A shallow clone of the named client configurations.
|
|
22423
|
+
*/
|
|
22279
22424
|
get clients() {
|
|
22280
22425
|
return { ...this._clients };
|
|
22281
22426
|
}
|
|
@@ -22303,6 +22448,8 @@ class HttpClientConfigurator {
|
|
|
22303
22448
|
configureClient(name, args) {
|
|
22304
22449
|
const argFn = typeof args === 'string' ? { baseUri: args } : args;
|
|
22305
22450
|
const options = typeof argFn === 'function' ? { onCreate: argFn } : argFn;
|
|
22451
|
+
// `options` is `HttpClientOptions<T>` (this call's generic), but `_clients` is keyed by the
|
|
22452
|
+
// configurator's own `TClient` — callers are expected to keep the two in sync.
|
|
22306
22453
|
this._clients[name] = {
|
|
22307
22454
|
...this._clients[name],
|
|
22308
22455
|
...options,
|
|
@@ -22312,16 +22459,8 @@ class HttpClientConfigurator {
|
|
|
22312
22459
|
}
|
|
22313
22460
|
|
|
22314
22461
|
// Generated by genversion.
|
|
22315
|
-
const version$6 = '8.0.
|
|
22462
|
+
const version$6 = '8.0.4';
|
|
22316
22463
|
|
|
22317
|
-
/**
|
|
22318
|
-
* Thrown when `createClient(name)` is called with an unknown client key.
|
|
22319
|
-
*
|
|
22320
|
-
* This is only used when the provided string is neither a registered client name
|
|
22321
|
-
* nor an absolute `http:` or `https:` URL.
|
|
22322
|
-
*/
|
|
22323
|
-
class ClientNotFoundException extends Error {
|
|
22324
|
-
}
|
|
22325
22464
|
/** URL protocols accepted as valid ad-hoc base URIs. */
|
|
22326
22465
|
const SUPPORTED_PROTOCOLS = ['http:', 'https:', 'ws:', 'wss:'];
|
|
22327
22466
|
/**
|
|
@@ -22359,6 +22498,10 @@ class HttpClientProvider extends BaseModuleProvider {
|
|
|
22359
22498
|
get defaultHttpRequestHandler() {
|
|
22360
22499
|
return this.config.defaultHttpRequestHandler;
|
|
22361
22500
|
}
|
|
22501
|
+
/**
|
|
22502
|
+
* Creates a new `HttpClientProvider`.
|
|
22503
|
+
* @param config - The configurator providing client definitions and defaults.
|
|
22504
|
+
*/
|
|
22362
22505
|
constructor(config) {
|
|
22363
22506
|
super({
|
|
22364
22507
|
version: version$6,
|
|
@@ -22395,6 +22538,7 @@ class HttpClientProvider extends BaseModuleProvider {
|
|
|
22395
22538
|
const { baseUri, defaultScopes = [], onCreate, ctor = this.config.defaultHttpClientCtor, requestHandler = this.defaultHttpRequestHandler, responseHandler, } = config;
|
|
22396
22539
|
const options = { requestHandler, responseHandler };
|
|
22397
22540
|
const instance = new ctor(baseUri || '', options);
|
|
22541
|
+
// attach the resolved default scopes onto the instance without overwriting other own properties
|
|
22398
22542
|
Object.assign(instance, { defaultScopes });
|
|
22399
22543
|
onCreate?.(instance);
|
|
22400
22544
|
return instance;
|
|
@@ -22402,6 +22546,7 @@ class HttpClientProvider extends BaseModuleProvider {
|
|
|
22402
22546
|
/**
|
|
22403
22547
|
* Creates a client instance and returns it as the requested custom client type.
|
|
22404
22548
|
*
|
|
22549
|
+
* @template T - The custom `HttpClient` implementation type to cast the created instance to.
|
|
22405
22550
|
* @param key - The key of the pre-configured HTTP client to create.
|
|
22406
22551
|
* @returns The created HTTP client instance, cast to the specified type `T`.
|
|
22407
22552
|
*
|
|
@@ -22412,6 +22557,8 @@ class HttpClientProvider extends BaseModuleProvider {
|
|
|
22412
22557
|
* is configured to use a different implementation.
|
|
22413
22558
|
*/
|
|
22414
22559
|
createCustomClient(key) {
|
|
22560
|
+
// `createClient` always returns the provider's configured `HttpClient` implementation —
|
|
22561
|
+
// cast through `unknown` to hand back the caller-requested implementation type `T`.
|
|
22415
22562
|
return this.createClient(key);
|
|
22416
22563
|
}
|
|
22417
22564
|
/**
|
|
@@ -22423,14 +22570,19 @@ class HttpClientProvider extends BaseModuleProvider {
|
|
|
22423
22570
|
*
|
|
22424
22571
|
* @param keyOrConfig - The key or configuration object for the HTTP client.
|
|
22425
22572
|
* @returns The resolved HTTP client configuration.
|
|
22573
|
+
* @throws {ClientNotFoundException} When `keyOrConfig` is a string that is neither a registered
|
|
22574
|
+
* client key nor a URL-like value.
|
|
22426
22575
|
*/
|
|
22427
22576
|
_resolveConfig(keyOrConfig) {
|
|
22577
|
+
// a string may reference a registered client key or an ad-hoc URL; anything else is used as-is
|
|
22428
22578
|
if (typeof keyOrConfig === 'string') {
|
|
22429
22579
|
const config = this.config.clients[keyOrConfig];
|
|
22580
|
+
// an absolute http(s) URL can be used directly as an ad-hoc baseUri
|
|
22430
22581
|
if (!config && isURL(keyOrConfig)) {
|
|
22431
22582
|
return { baseUri: keyOrConfig };
|
|
22432
22583
|
}
|
|
22433
22584
|
else if (!config && looksLikeURL(keyOrConfig)) {
|
|
22585
|
+
// recover from a missing protocol instead of failing outright, but warn so it can be fixed
|
|
22434
22586
|
console.warn(`[HttpClientProvider] "${keyOrConfig}" looks like a URL but is missing the http:// or https:// protocol. ` +
|
|
22435
22587
|
`Treating it as "https://${keyOrConfig}". ` +
|
|
22436
22588
|
`Pass a fully-qualified URL to silence this warning.`);
|
|
@@ -22543,8 +22695,8 @@ const jsonSelector = async (response) => {
|
|
|
22543
22695
|
* @throws {Error} If the response is not successful or if there is an error parsing the response.
|
|
22544
22696
|
*/
|
|
22545
22697
|
const blobSelector = async (response) => {
|
|
22698
|
+
// treat any non-2xx response as a failure rather than attempting to read its body
|
|
22546
22699
|
if (!response.ok) {
|
|
22547
|
-
// Throw an error if the network response is not successful
|
|
22548
22700
|
throw new Error('network response was not OK');
|
|
22549
22701
|
}
|
|
22550
22702
|
// Status code 204 indicates no content, so throw an error
|
|
@@ -22552,6 +22704,7 @@ const blobSelector = async (response) => {
|
|
|
22552
22704
|
throw new Error('no content');
|
|
22553
22705
|
}
|
|
22554
22706
|
// Extract the filename from the 'content-disposition' header
|
|
22707
|
+
// locate the segment carrying the filename directive; other directives (e.g. inline/attachment) are ignored
|
|
22555
22708
|
const filename = response.headers
|
|
22556
22709
|
.get('content-disposition')
|
|
22557
22710
|
?.split(';')
|
|
@@ -22563,7 +22716,7 @@ const blobSelector = async (response) => {
|
|
|
22563
22716
|
const blob = await response.blob();
|
|
22564
22717
|
return { filename, blob };
|
|
22565
22718
|
}
|
|
22566
|
-
catch (
|
|
22719
|
+
catch (_err) {
|
|
22567
22720
|
// Throw an error if there's a problem parsing the response
|
|
22568
22721
|
throw Error('failed to parse response');
|
|
22569
22722
|
}
|
|
@@ -22599,16 +22752,23 @@ class HttpClient {
|
|
|
22599
22752
|
_abort$ = new Subject();
|
|
22600
22753
|
/**
|
|
22601
22754
|
* A stream of requests that are about to be executed.
|
|
22755
|
+
* @returns An `Observable` that emits each request before it is executed.
|
|
22602
22756
|
*/
|
|
22603
22757
|
get request$() {
|
|
22604
22758
|
return this._request$.asObservable();
|
|
22605
22759
|
}
|
|
22606
22760
|
/**
|
|
22607
22761
|
* A stream of responses that have been received.
|
|
22762
|
+
* @returns An `Observable` that emits each response as it is received.
|
|
22608
22763
|
*/
|
|
22609
22764
|
get response$() {
|
|
22610
22765
|
return this._response$.asObservable();
|
|
22611
22766
|
}
|
|
22767
|
+
/**
|
|
22768
|
+
* Creates a new `HttpClient`.
|
|
22769
|
+
* @param uri - The base URI used to resolve relative request paths.
|
|
22770
|
+
* @param options - Optional request and response handlers.
|
|
22771
|
+
*/
|
|
22612
22772
|
constructor(uri, options) {
|
|
22613
22773
|
this.uri = uri;
|
|
22614
22774
|
this.requestHandler = new HttpRequestHandler(options?.requestHandler);
|
|
@@ -22627,6 +22787,7 @@ class HttpClient {
|
|
|
22627
22787
|
/**
|
|
22628
22788
|
* Fetches data from the specified path and returns a stream response.
|
|
22629
22789
|
*
|
|
22790
|
+
* @template T - The expected shape of the fetched data.
|
|
22630
22791
|
* @param path - The path to fetch data from.
|
|
22631
22792
|
* @param args - Optional request initialization options, including a custom selector function.
|
|
22632
22793
|
* @returns A stream response containing the fetched data.
|
|
@@ -22637,6 +22798,7 @@ class HttpClient {
|
|
|
22637
22798
|
/**
|
|
22638
22799
|
* Fetches data from the specified path and returns a Promise containing the fetched data.
|
|
22639
22800
|
*
|
|
22801
|
+
* @template T - The expected shape of the fetched data.
|
|
22640
22802
|
* @param path - The path to fetch data from.
|
|
22641
22803
|
* @param args - Optional request initialization options, including a custom selector function.
|
|
22642
22804
|
* @returns A Promise containing the fetched data.
|
|
@@ -22644,13 +22806,20 @@ class HttpClient {
|
|
|
22644
22806
|
fetch(path, args) {
|
|
22645
22807
|
return firstValueFrom(this.fetch$(path, args));
|
|
22646
22808
|
}
|
|
22647
|
-
/**
|
|
22809
|
+
/**
|
|
22810
|
+
* @deprecated Use {@link fetch} instead.
|
|
22811
|
+
* @template T - The expected shape of the fetched data.
|
|
22812
|
+
* @param path - The path to fetch data from.
|
|
22813
|
+
* @param args - Optional request initialization options, including a custom selector function.
|
|
22814
|
+
* @returns A Promise containing the fetched data.
|
|
22815
|
+
*/
|
|
22648
22816
|
fetchAsync(path, args) {
|
|
22649
22817
|
return this.fetch(path, args);
|
|
22650
22818
|
}
|
|
22651
22819
|
/**
|
|
22652
22820
|
* Fetches data from the specified path and returns a stream response containing the data in JSON format.
|
|
22653
22821
|
*
|
|
22822
|
+
* @template T - The expected shape of the parsed JSON data.
|
|
22654
22823
|
* @param path - The path to fetch the data from.
|
|
22655
22824
|
* @param args - Optional request initialization options, including a custom selector function and request body.
|
|
22656
22825
|
* - `body`: The request body, which will be automatically serialized to JSON if it's an object.
|
|
@@ -22674,6 +22843,7 @@ class HttpClient {
|
|
|
22674
22843
|
/**
|
|
22675
22844
|
* Fetches data from the specified path and returns a Promise containing the fetched data in JSON format.
|
|
22676
22845
|
*
|
|
22846
|
+
* @template T - The expected shape of the parsed JSON data.
|
|
22677
22847
|
* @param path - The path to fetch the data from.
|
|
22678
22848
|
* @param args - Optional request initialization options, including a custom selector function and request body.
|
|
22679
22849
|
* - `body`: The request body, which will be automatically serialized to JSON if it's an object.
|
|
@@ -22687,6 +22857,7 @@ class HttpClient {
|
|
|
22687
22857
|
/**
|
|
22688
22858
|
* Fetches a blob resource from the specified path and returns a stream response.
|
|
22689
22859
|
*
|
|
22860
|
+
* @template T - The expected shape of the fetched blob data.
|
|
22690
22861
|
* @param path - The path to the blob resource.
|
|
22691
22862
|
* @param args - Optional request initialization options, including a custom selector function.
|
|
22692
22863
|
* @returns A stream response containing the fetched blob data.
|
|
@@ -22705,6 +22876,7 @@ class HttpClient {
|
|
|
22705
22876
|
/**
|
|
22706
22877
|
* Fetches a blob from the specified path and returns a Promise that resolves to the blob result.
|
|
22707
22878
|
*
|
|
22879
|
+
* @template T - The expected shape of the fetched blob data.
|
|
22708
22880
|
* @param path - The path to fetch the blob from.
|
|
22709
22881
|
* @param args - Optional arguments for the fetch request, including request body, headers, and response type.
|
|
22710
22882
|
* @returns A Promise that resolves to the blob result.
|
|
@@ -22746,13 +22918,21 @@ class HttpClient {
|
|
|
22746
22918
|
// Call the fetch$ method with the provided path and the constructed init object
|
|
22747
22919
|
return this._fetch$(path, { selector, ...args, headers });
|
|
22748
22920
|
}
|
|
22749
|
-
/**
|
|
22921
|
+
/**
|
|
22922
|
+
* @deprecated Use {@link json} instead.
|
|
22923
|
+
* @template T - The expected shape of the parsed JSON data.
|
|
22924
|
+
* @param path - The path to fetch the data from.
|
|
22925
|
+
* @param args - Optional request initialization options, including a custom selector function and request body.
|
|
22926
|
+
* @returns A Promise containing the fetched data in JSON format.
|
|
22927
|
+
*/
|
|
22750
22928
|
jsonAsync(path, args) {
|
|
22751
22929
|
return this.json(path, args);
|
|
22752
22930
|
}
|
|
22753
22931
|
/**
|
|
22754
22932
|
* Executes an HTTP request using the specified method and path.
|
|
22755
22933
|
*
|
|
22934
|
+
* @template T - The expected shape of the result.
|
|
22935
|
+
* @template TMethod - The name of the `IHttpClient` method to invoke.
|
|
22756
22936
|
* @param method - The HTTP method to use for the request, such as 'fetch', 'json', or 'blob'.
|
|
22757
22937
|
* @param path - The path to the resource to fetch.
|
|
22758
22938
|
* @param init - Optional request initialization options, including request body, headers, and response type.
|
|
@@ -22772,10 +22952,13 @@ class HttpClient {
|
|
|
22772
22952
|
/**
|
|
22773
22953
|
* Fetches data from the specified path and returns an Observable that emits the response.
|
|
22774
22954
|
*
|
|
22955
|
+
* @template T - The expected shape of the emitted data.
|
|
22775
22956
|
* @param path - The path to fetch the data from.
|
|
22776
22957
|
* @param args - Optional arguments for the fetch request, including a response selector function, request body, headers, and response type.
|
|
22777
22958
|
* @returns {Observable<T>} An Observable that emits the response data.
|
|
22778
22959
|
*
|
|
22960
|
+
* @throws {HttpResponseError} When the optional `selector` throws while transforming the response.
|
|
22961
|
+
*
|
|
22779
22962
|
* This method handles the following steps:
|
|
22780
22963
|
* 1. Resolves the full URL by combining the base URI and the provided path.
|
|
22781
22964
|
* 2. Prepares the request by passing it through the `requestHandler.process()` method.
|
|
@@ -22786,6 +22969,9 @@ class HttpClient {
|
|
|
22786
22969
|
*/
|
|
22787
22970
|
_fetch$(path, args) {
|
|
22788
22971
|
const { selector, ...options } = args || {};
|
|
22972
|
+
// `fromFetch` yields the raw fetch `Response`, but `responseHandler.process()` (called via
|
|
22973
|
+
// `_prepareResponse`) expects the pipeline's generic `TResponse` shape — cast through
|
|
22974
|
+
// `unknown` since the two are only compatible after that processing step.
|
|
22789
22975
|
const response$ = of({
|
|
22790
22976
|
...options,
|
|
22791
22977
|
path,
|
|
@@ -22803,6 +22989,7 @@ class HttpClient {
|
|
|
22803
22989
|
tap((x) => this._response$.next(x)),
|
|
22804
22990
|
/** execute selector */
|
|
22805
22991
|
switchMap((response) => {
|
|
22992
|
+
// only run the selector when one was provided; otherwise pass the response through untouched
|
|
22806
22993
|
if (selector) {
|
|
22807
22994
|
try {
|
|
22808
22995
|
return selector(response);
|
|
@@ -22817,6 +23004,8 @@ class HttpClient {
|
|
|
22817
23004
|
}),
|
|
22818
23005
|
/** cancel request on abort signal */
|
|
22819
23006
|
takeUntil(this._abort$));
|
|
23007
|
+
// The pipe above resolves to the per-call generic `T` (via the optional `selector`), but
|
|
23008
|
+
// the observable's static type tracks the class-level `TResponse` — cast to the caller's `T`.
|
|
22820
23009
|
return response$;
|
|
22821
23010
|
}
|
|
22822
23011
|
/**
|
|
@@ -22886,6 +23075,7 @@ class HttpClientMsal extends HttpClient {
|
|
|
22886
23075
|
*
|
|
22887
23076
|
* @overrides HttpClient.fetch$
|
|
22888
23077
|
*
|
|
23078
|
+
* @template T - The expected response type.
|
|
22889
23079
|
* @param path - The path to the resource to fetch.
|
|
22890
23080
|
* @param init - An optional `MsalFetchRequestInit` object that can include the `scopes` property.
|
|
22891
23081
|
* @returns An `Observable` that emits the fetched resource.
|
|
@@ -22937,15 +23127,18 @@ const module$4 = {
|
|
|
22937
23127
|
*/
|
|
22938
23128
|
initialize: async ({ config, hasModule, requireInstance, }) => {
|
|
22939
23129
|
const httpProvider = new HttpClientProvider(config);
|
|
23130
|
+
// wire up an MSAL bearer-token handler only when the auth module is registered
|
|
22940
23131
|
if (hasModule('auth')) {
|
|
22941
23132
|
const authProvider = await requireInstance('auth');
|
|
22942
23133
|
httpProvider.defaultHttpRequestHandler.set('MSAL', async (request) => {
|
|
22943
23134
|
const { scopes = [] } = request;
|
|
23135
|
+
// only attempt to acquire a token when the request actually declares scopes
|
|
22944
23136
|
if (scopes.length) {
|
|
22945
|
-
/** TODO should be try catch, check caller for handling */
|
|
23137
|
+
/** TODO(#5143): should be try catch, check caller for handling */
|
|
22946
23138
|
const accessToken = await authProvider.acquireAccessToken({
|
|
22947
23139
|
request: { scopes },
|
|
22948
23140
|
});
|
|
23141
|
+
// without a token there's nothing to attach, fall through to the default request
|
|
22949
23142
|
if (accessToken) {
|
|
22950
23143
|
const headers = new Headers(request.headers);
|
|
22951
23144
|
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
@@ -23451,24 +23644,6 @@ var objectTraps = {
|
|
|
23451
23644
|
get(state, prop) {
|
|
23452
23645
|
if (prop === DRAFT_STATE)
|
|
23453
23646
|
return state;
|
|
23454
|
-
if (prop === "constructor" || prop === "__proto__") {
|
|
23455
|
-
const source2 = latest(state);
|
|
23456
|
-
const value2 = source2[prop];
|
|
23457
|
-
return new Proxy(value2 || {}, {
|
|
23458
|
-
get: (target, key) => {
|
|
23459
|
-
if (key === "__proto__" || key === "prototype") {
|
|
23460
|
-
return Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
23461
|
-
}
|
|
23462
|
-
return Reflect.get(target, key);
|
|
23463
|
-
},
|
|
23464
|
-
set: () => {
|
|
23465
|
-
return true;
|
|
23466
|
-
},
|
|
23467
|
-
apply: (target, thisArg, args) => {
|
|
23468
|
-
return Reflect.apply(target, thisArg, args);
|
|
23469
|
-
}
|
|
23470
|
-
});
|
|
23471
|
-
}
|
|
23472
23647
|
let arrayPlugin = state.scope_.arrayMethodsPlugin_;
|
|
23473
23648
|
const isArrayWithStringProp = state.type_ === 1 /* Array */ && typeof prop === "string";
|
|
23474
23649
|
if (isArrayWithStringProp) {
|
|
@@ -23489,7 +23664,7 @@ var objectTraps = {
|
|
|
23489
23664
|
) && isArrayIndex(prop)) {
|
|
23490
23665
|
return value;
|
|
23491
23666
|
}
|
|
23492
|
-
if (value === peek(state.base_, prop)) {
|
|
23667
|
+
if (value === peek(state.base_, prop) || isRelocatedBaseRef(state, prop, value)) {
|
|
23493
23668
|
prepareCopy(state);
|
|
23494
23669
|
const childKey = state.type_ === 1 /* Array */ ? +prop : prop;
|
|
23495
23670
|
const childDraft = createProxy(state.scope_, value, state, childKey);
|
|
@@ -23498,18 +23673,12 @@ var objectTraps = {
|
|
|
23498
23673
|
return value;
|
|
23499
23674
|
},
|
|
23500
23675
|
has(state, prop) {
|
|
23501
|
-
if (prop === "constructor" || prop === "__proto__" || prop === "prototype") {
|
|
23502
|
-
return false;
|
|
23503
|
-
}
|
|
23504
23676
|
return prop in latest(state);
|
|
23505
23677
|
},
|
|
23506
23678
|
ownKeys(state) {
|
|
23507
23679
|
return Reflect.ownKeys(latest(state));
|
|
23508
23680
|
},
|
|
23509
23681
|
set(state, prop, value) {
|
|
23510
|
-
if (prop === "constructor" || prop === "__proto__" || prop === "prototype") {
|
|
23511
|
-
return true;
|
|
23512
|
-
}
|
|
23513
23682
|
const desc = getDescriptorFromProto(latest(state), prop);
|
|
23514
23683
|
if (desc?.set) {
|
|
23515
23684
|
desc.set.call(state.draft_, value);
|
|
@@ -23598,6 +23767,12 @@ function peek(draft, prop) {
|
|
|
23598
23767
|
const source = state ? latest(state) : draft;
|
|
23599
23768
|
return source[prop];
|
|
23600
23769
|
}
|
|
23770
|
+
function isRelocatedBaseRef(state, prop, value) {
|
|
23771
|
+
if (state.type_ !== 1 /* Array */ || !state.allIndicesReassigned_ || state.assigned_?.get(prop) || !isDraftable(value) || value[DRAFT_STATE]) {
|
|
23772
|
+
return false;
|
|
23773
|
+
}
|
|
23774
|
+
return state.baseRefs_.has(value);
|
|
23775
|
+
}
|
|
23601
23776
|
function readPropFromProto(state, source, prop) {
|
|
23602
23777
|
const desc = getDescriptorFromProto(source, prop);
|
|
23603
23778
|
return desc ? VALUE in desc ? desc[VALUE] : (
|
|
@@ -23923,18 +24098,21 @@ class FlowSubject extends Observable {
|
|
|
23923
24098
|
#state;
|
|
23924
24099
|
/**
|
|
23925
24100
|
* Observable stream of actions dispatched to the subject.
|
|
24101
|
+
* @returns An observable of dispatched actions.
|
|
23926
24102
|
*/
|
|
23927
24103
|
get action$() {
|
|
23928
24104
|
return this.#action.asObservable();
|
|
23929
24105
|
}
|
|
23930
24106
|
/**
|
|
23931
24107
|
* The current value of state.
|
|
24108
|
+
* @returns The current state value.
|
|
23932
24109
|
*/
|
|
23933
24110
|
get value() {
|
|
23934
24111
|
return this.#state.value;
|
|
23935
24112
|
}
|
|
23936
24113
|
/**
|
|
23937
24114
|
* Flag to indicate if the observable is closed.
|
|
24115
|
+
* @returns `true` if both the state and action subjects are closed.
|
|
23938
24116
|
*/
|
|
23939
24117
|
get closed() {
|
|
23940
24118
|
return this.#state.closed || this.#action.closed;
|
|
@@ -23951,6 +24129,7 @@ class FlowSubject extends Observable {
|
|
|
23951
24129
|
});
|
|
23952
24130
|
const initial = 'getInitialState' in reducer ? reducer.getInitialState() : initialState;
|
|
23953
24131
|
this.#state = new BehaviorSubject(initial);
|
|
24132
|
+
// Reduce dispatched actions into state, skipping emissions that produce an unchanged value
|
|
23954
24133
|
this.#action.pipe(scan(reducer, initial), distinctUntilChanged()).subscribe(this.#state);
|
|
23955
24134
|
this.reset = () => this.#state.next(initial);
|
|
23956
24135
|
}
|
|
@@ -23969,27 +24148,32 @@ class FlowSubject extends Observable {
|
|
|
23969
24148
|
/**
|
|
23970
24149
|
* Selects a derived state from the observable state and emits only when the selected state changes.
|
|
23971
24150
|
*
|
|
24151
|
+
* @template T - The derived state type returned by the selector.
|
|
23972
24152
|
* @param selector - A function that takes the current state and returns a derived state.
|
|
23973
24153
|
* @param comparator - An optional function that compares the previous and current derived states to determine if a change has occurred.
|
|
23974
24154
|
* @returns An observable that emits the derived state whenever it changes.
|
|
23975
24155
|
*/
|
|
23976
24156
|
select(selector, comparator) {
|
|
24157
|
+
// Derive and de-duplicate the selected value from the state stream
|
|
23977
24158
|
return this.#state.pipe(map$1(selector), distinctUntilChanged(comparator));
|
|
23978
24159
|
}
|
|
23979
24160
|
/**
|
|
23980
24161
|
* Adds an effect that listens for actions and performs side effects.
|
|
23981
24162
|
*
|
|
24163
|
+
* @template TType - The action type(s) to listen for.
|
|
23982
24164
|
* @param actionTypeOrFn The type of action to listen for, an array of action types, or the effect function itself.
|
|
23983
24165
|
* @param fn The effect function to execute when the action is dispatched, if the first parameter is an action type.
|
|
23984
24166
|
* @returns A subscription to the effect.
|
|
23985
24167
|
*/
|
|
23986
24168
|
addEffect(actionTypeOrFn, fn) {
|
|
24169
|
+
// Narrow the action stream to the requested action type(s), or use it as-is when a plain effect function was given
|
|
23987
24170
|
const action$ = typeof actionTypeOrFn === 'function'
|
|
23988
24171
|
? this.action$
|
|
23989
24172
|
: Array.isArray(actionTypeOrFn)
|
|
23990
24173
|
? this.action$.pipe(filterAction(...actionTypeOrFn))
|
|
23991
24174
|
: this.action$.pipe(filterAction(actionTypeOrFn));
|
|
23992
24175
|
const mapper = (fn ? fn : actionTypeOrFn);
|
|
24176
|
+
// Run the effect for each matching action, then filter and reschedule any resulting action
|
|
23993
24177
|
return action$
|
|
23994
24178
|
.pipe(mergeMap((action) => from(new Promise((resolve, reject) => {
|
|
23995
24179
|
try {
|
|
@@ -23998,7 +24182,9 @@ class FlowSubject extends Observable {
|
|
|
23998
24182
|
catch (err) {
|
|
23999
24183
|
reject(err);
|
|
24000
24184
|
}
|
|
24001
|
-
}))
|
|
24185
|
+
}))
|
|
24186
|
+
// Swallow effect errors so a single failing effect doesn't tear down the subscription
|
|
24187
|
+
.pipe(catchError((err) => {
|
|
24002
24188
|
console.warn('unhandled effect', err);
|
|
24003
24189
|
return EMPTY;
|
|
24004
24190
|
}))), filter((x) => !!x), observeOn(asyncScheduler))
|
|
@@ -24040,9 +24226,11 @@ class FlowSubject extends Observable {
|
|
|
24040
24226
|
*/
|
|
24041
24227
|
addFlow(fn) {
|
|
24042
24228
|
const epic$ = fn(this.action$, this);
|
|
24229
|
+
// Fail fast if the flow function forgot to return an observable
|
|
24043
24230
|
if (!epic$) {
|
|
24044
24231
|
throw new TypeError(`addEpic: one of the provided effects "${fn.name || '<anonymous>'}" does not return a stream. Double check you're not missing a return statement!`);
|
|
24045
24232
|
}
|
|
24233
|
+
// Log and swallow flow errors so a single failing flow doesn't tear down the subject
|
|
24046
24234
|
return epic$
|
|
24047
24235
|
.pipe(catchError((err) => {
|
|
24048
24236
|
console.trace('unhandled exception, epic closed!', err);
|
|
@@ -24078,18 +24266,38 @@ class FlowSubject extends Observable {
|
|
|
24078
24266
|
}
|
|
24079
24267
|
}
|
|
24080
24268
|
|
|
24081
|
-
|
|
24269
|
+
/**
|
|
24270
|
+
* Internal helper utilities for constructing and inspecting Redux-style
|
|
24271
|
+
* lifecycle action types (`::request`, `::success`, `::failure`).
|
|
24272
|
+
*/
|
|
24273
|
+
/**
|
|
24274
|
+
* Separates an action's base type from its lifecycle suffix.
|
|
24275
|
+
*/
|
|
24276
|
+
const actionSuffixDivider = '::';
|
|
24277
|
+
|
|
24278
|
+
// 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
|
|
24279
|
+
// 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
|
|
24082
24280
|
/**
|
|
24083
24281
|
* taken from https://github.com/reduxjs/redux-toolkit/tree/master/packages/toolkit/src
|
|
24084
24282
|
*/
|
|
24085
|
-
|
|
24283
|
+
/**
|
|
24284
|
+
* Creates the runtime action creator implementation shared by the overloads above.
|
|
24285
|
+
*
|
|
24286
|
+
* @param type - The action type to assign to created actions.
|
|
24287
|
+
* @param prepareAction - An optional function that prepares payload, metadata, and errors.
|
|
24288
|
+
* @returns A callable action creator with type and matching metadata.
|
|
24289
|
+
* @throws {Error} When the prepare callback does not return an action object.
|
|
24290
|
+
*/
|
|
24086
24291
|
function createAction(type, prepareAction) {
|
|
24087
24292
|
function actionCreator(...args) {
|
|
24293
|
+
// Use the prepare callback when the caller needs custom payload construction.
|
|
24088
24294
|
if (prepareAction) {
|
|
24089
24295
|
const prepared = prepareAction(...args);
|
|
24296
|
+
// Reject invalid prepare callbacks before reading their action fields.
|
|
24090
24297
|
if (!prepared) {
|
|
24091
24298
|
throw new Error('prepareAction did not return an object');
|
|
24092
24299
|
}
|
|
24300
|
+
// Merge optional metadata and error fields into the prepared action.
|
|
24093
24301
|
return {
|
|
24094
24302
|
type,
|
|
24095
24303
|
payload: prepared.payload,
|
|
@@ -24104,35 +24312,50 @@ function createAction(type, prepareAction) {
|
|
|
24104
24312
|
actionCreator.match = (action) => action.type === type;
|
|
24105
24313
|
return actionCreator;
|
|
24106
24314
|
}
|
|
24107
|
-
const actionSuffixDivider = '::';
|
|
24108
24315
|
|
|
24109
|
-
|
|
24316
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: generic constraints mirror create-action.ts's ported redux-toolkit PrepareAction<any> dispatch pattern
|
|
24317
|
+
/**
|
|
24318
|
+
* Creates the runtime async action implementation shared by the overloads above.
|
|
24319
|
+
*
|
|
24320
|
+
* @param type - The base action type string.
|
|
24321
|
+
* @param request - A prepare function for the request action payload.
|
|
24322
|
+
* @param success - A prepare function for the success action payload.
|
|
24323
|
+
* @param failure - An optional prepare function for the failure action payload.
|
|
24324
|
+
* @returns An action creator with request, success, and optional failure actions.
|
|
24325
|
+
*/
|
|
24110
24326
|
function createAsyncAction(type, request, success, failure) {
|
|
24111
24327
|
const action = createAction([type, 'request'].join(actionSuffixDivider), request);
|
|
24328
|
+
// Attach the success action only when the caller supplied its prepare function.
|
|
24112
24329
|
if (success) {
|
|
24330
|
+
// Add the success action while preserving the request creator's identity.
|
|
24113
24331
|
Object.assign(action, {
|
|
24114
24332
|
success: createAction([type, 'success'].join(actionSuffixDivider), success),
|
|
24115
24333
|
});
|
|
24116
24334
|
}
|
|
24335
|
+
// Attach the failure action only for async workflows that support failure results.
|
|
24117
24336
|
if (failure) {
|
|
24337
|
+
// Add the failure action alongside the existing request and success actions.
|
|
24118
24338
|
Object.assign(action, {
|
|
24119
24339
|
failure: createAction([type, 'failure'].join(actionSuffixDivider), failure),
|
|
24120
24340
|
});
|
|
24121
24341
|
}
|
|
24342
|
+
// `action` is progressively mutated with `success`/`failure` properties above, so its static
|
|
24343
|
+
// type can't reflect the final shape declared by this function's overload signatures.
|
|
24122
24344
|
return action;
|
|
24123
24345
|
}
|
|
24124
24346
|
|
|
24125
24347
|
function freezeDraftable(val) {
|
|
24126
|
-
// biome-ignore lint/suspicious/noEmptyBlockStatements: This is a valid use case for an empty block statement
|
|
24127
24348
|
return isDraftable(val) ? produce(val, () => { }) : val;
|
|
24128
24349
|
}
|
|
24129
24350
|
function isStateFunction(x) {
|
|
24130
24351
|
return typeof x === 'function';
|
|
24131
24352
|
}
|
|
24353
|
+
/** @inheritdoc */
|
|
24132
24354
|
function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
24133
24355
|
const [actionsMap, finalActionMatchers, finalDefaultCaseReducer] = executeReducerBuilderCallback(mapOrBuilderCallback);
|
|
24134
24356
|
// Ensure the initial state gets frozen either way (if draftable)
|
|
24135
24357
|
let getInitialState;
|
|
24358
|
+
// A lazy initializer must be invoked before its result can be frozen
|
|
24136
24359
|
if (isStateFunction(initialState)) {
|
|
24137
24360
|
getInitialState = () => freezeDraftable(initialState());
|
|
24138
24361
|
}
|
|
@@ -24143,19 +24366,31 @@ function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
|
24143
24366
|
function reducer(state, action) {
|
|
24144
24367
|
let caseReducers = [
|
|
24145
24368
|
actionsMap[action.type],
|
|
24146
|
-
...finalActionMatchers
|
|
24369
|
+
...finalActionMatchers
|
|
24370
|
+
// Keep only matchers whose predicate matches this action
|
|
24371
|
+
.filter(({ matcher }) => matcher(action))
|
|
24372
|
+
// Extract just the matched reducers
|
|
24373
|
+
.map(({ reducer }) => reducer),
|
|
24147
24374
|
];
|
|
24148
|
-
|
|
24375
|
+
// Fall back to the default case reducer when nothing else matched this action
|
|
24376
|
+
// Check whether any slot actually holds a reducer
|
|
24377
|
+
const hasMatchedReducer = caseReducers.filter((cr) => !!cr).length > 0;
|
|
24378
|
+
// Only substitute the default case when no case/matcher reducer actually matched
|
|
24379
|
+
if (finalDefaultCaseReducer && !hasMatchedReducer) {
|
|
24149
24380
|
caseReducers = [finalDefaultCaseReducer];
|
|
24150
24381
|
}
|
|
24382
|
+
// Run each matched case reducer against the previous state, producing the next state
|
|
24151
24383
|
return caseReducers.reduce((previousState, caseReducer) => {
|
|
24384
|
+
// Only transform state when a case reducer was matched for this slot
|
|
24152
24385
|
if (caseReducer) {
|
|
24386
|
+
// Reuse an existing draft when already inside a `createNextState` call
|
|
24153
24387
|
if (isDraft(previousState)) {
|
|
24154
24388
|
// If it's already a draft, we must already be inside a `createNextState` call,
|
|
24155
24389
|
// likely because this is being wrapped in `createReducer`, `createSlice`, or nested
|
|
24156
24390
|
// inside an existing draft. It's safe to just pass the draft to the mutator.
|
|
24157
24391
|
const draft = previousState; // We can assume this is already a draft
|
|
24158
24392
|
const result = caseReducer(draft, action);
|
|
24393
|
+
// Treat an undefined result as "no change" rather than clearing the state
|
|
24159
24394
|
if (result === undefined) {
|
|
24160
24395
|
return previousState;
|
|
24161
24396
|
}
|
|
@@ -24165,7 +24400,9 @@ function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
|
24165
24400
|
// If state is not draftable (ex: a primitive, such as 0), we want to directly
|
|
24166
24401
|
// return the caseReducer func and not wrap it with produce.
|
|
24167
24402
|
const result = caseReducer(previousState, action);
|
|
24403
|
+
// Treat an undefined result as "no change", unless there's no previous state to fall back to
|
|
24168
24404
|
if (result === undefined) {
|
|
24405
|
+
// A `null` previous state has nothing to fall back to, so undefined is legitimately "no change"
|
|
24169
24406
|
if (previousState === null) {
|
|
24170
24407
|
return previousState;
|
|
24171
24408
|
}
|
|
@@ -24175,7 +24412,7 @@ function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
|
24175
24412
|
}
|
|
24176
24413
|
else {
|
|
24177
24414
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
24178
|
-
// @ts-
|
|
24415
|
+
// @ts-expect-error
|
|
24179
24416
|
// createNextState() produces an Immutable<Draft<S>> rather
|
|
24180
24417
|
// than an Immutable<S>, and TypeScript cannot find out how to reconcile
|
|
24181
24418
|
// these two types.
|
|
@@ -24190,12 +24427,22 @@ function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
|
24190
24427
|
reducer.getInitialState = getInitialState;
|
|
24191
24428
|
return reducer;
|
|
24192
24429
|
}
|
|
24430
|
+
/**
|
|
24431
|
+
* Executes a reducer builder callback against a fresh {@link ActionReducerMapBuilder},
|
|
24432
|
+
* collecting the case reducers, matchers, and default case it registered.
|
|
24433
|
+
*
|
|
24434
|
+
* @template TState - The reducer's state type.
|
|
24435
|
+
* @template TAction - The union of actions the reducer handles.
|
|
24436
|
+
* @param builderCallback - The callback passed to {@link createReducer} that registers cases via the builder.
|
|
24437
|
+
* @returns A tuple of the action-to-reducer map, matcher list, and optional default case reducer.
|
|
24438
|
+
*/
|
|
24193
24439
|
function executeReducerBuilderCallback(builderCallback) {
|
|
24194
24440
|
const actionsMap = {};
|
|
24195
24441
|
const actionMatchers = [];
|
|
24196
24442
|
let defaultCaseReducer;
|
|
24197
24443
|
const builder = {
|
|
24198
24444
|
addCase(typeOrActionCreator, reducer) {
|
|
24445
|
+
// Skip these dev-only ordering checks in production builds to avoid the extra overhead
|
|
24199
24446
|
if (process.env.NODE_ENV !== 'production') {
|
|
24200
24447
|
/*
|
|
24201
24448
|
* to keep the definition by the user in line with actual behavior,
|
|
@@ -24205,11 +24452,13 @@ function executeReducerBuilderCallback(builderCallback) {
|
|
|
24205
24452
|
if (actionMatchers.length > 0) {
|
|
24206
24453
|
throw new Error('`builder.addCase` should only be called before calling `builder.addMatcher`');
|
|
24207
24454
|
}
|
|
24455
|
+
// A default case must always be registered last, after every specific case
|
|
24208
24456
|
if (defaultCaseReducer) {
|
|
24209
24457
|
throw new Error('`builder.addCase` should only be called before calling `builder.addDefaultCase`');
|
|
24210
24458
|
}
|
|
24211
24459
|
}
|
|
24212
24460
|
const type = typeof typeOrActionCreator === 'string' ? typeOrActionCreator : typeOrActionCreator.type;
|
|
24461
|
+
// Each action type may only be handled by a single case reducer
|
|
24213
24462
|
if (type in actionsMap) {
|
|
24214
24463
|
throw new Error('addCase cannot be called with two reducers for the same action type');
|
|
24215
24464
|
}
|
|
@@ -24217,7 +24466,9 @@ function executeReducerBuilderCallback(builderCallback) {
|
|
|
24217
24466
|
return builder;
|
|
24218
24467
|
},
|
|
24219
24468
|
addMatcher(matcher, reducer) {
|
|
24469
|
+
// Skip this dev-only ordering check in production builds to avoid the extra overhead
|
|
24220
24470
|
if (process.env.NODE_ENV !== 'production') {
|
|
24471
|
+
// A default case must always be registered last, after every matcher
|
|
24221
24472
|
if (defaultCaseReducer) {
|
|
24222
24473
|
throw new Error('`builder.addMatcher` should only be called before calling `builder.addDefaultCase`');
|
|
24223
24474
|
}
|
|
@@ -24226,7 +24477,9 @@ function executeReducerBuilderCallback(builderCallback) {
|
|
|
24226
24477
|
return builder;
|
|
24227
24478
|
},
|
|
24228
24479
|
addDefaultCase(reducer) {
|
|
24480
|
+
// Skip this dev-only uniqueness check in production builds to avoid the extra overhead
|
|
24229
24481
|
if (process.env.NODE_ENV !== 'production') {
|
|
24482
|
+
// Only one default case reducer may be registered per builder
|
|
24230
24483
|
if (defaultCaseReducer) {
|
|
24231
24484
|
throw new Error('`builder.addDefaultCase` can only be called once');
|
|
24232
24485
|
}
|
|
@@ -24243,8 +24496,10 @@ function executeReducerBuilderCallback(builderCallback) {
|
|
|
24243
24496
|
];
|
|
24244
24497
|
}
|
|
24245
24498
|
|
|
24499
|
+
/** @inheritdoc */
|
|
24500
|
+
function isObservableInput(
|
|
24246
24501
|
// biome-ignore lint/suspicious/noExplicitAny: Should allow any input type
|
|
24247
|
-
|
|
24502
|
+
input) {
|
|
24248
24503
|
// Check for null or undefined
|
|
24249
24504
|
if (input === null || input === undefined) {
|
|
24250
24505
|
return false;
|
|
@@ -24314,7 +24569,6 @@ function isObservableInput(input) {
|
|
|
24314
24569
|
* // Primitive
|
|
24315
24570
|
* toObservable(42).subscribe(x => console.log(x)); // 42
|
|
24316
24571
|
*/
|
|
24317
|
-
// biome-ignore lint/suspicious/noExplicitAny: must be any to allow any type of input
|
|
24318
24572
|
function toObservable(input, ...args) {
|
|
24319
24573
|
// If input is already an ObservableInput (Observable, Promise, Iterable, etc), use RxJS 'from' to convert it
|
|
24320
24574
|
if (isObservableInput(input)) {
|
|
@@ -24492,6 +24746,7 @@ var deepmerge = /*@__PURE__*/getDefaultExportFromCjs(deepmergeExports);
|
|
|
24492
24746
|
* @returns The merged `MetaData` object, or `undefined` if both inputs are undefined.
|
|
24493
24747
|
*/
|
|
24494
24748
|
const mergeMetadata = (source, target) => {
|
|
24749
|
+
// Nothing to merge when neither side has metadata
|
|
24495
24750
|
if (!source && !target) {
|
|
24496
24751
|
return undefined;
|
|
24497
24752
|
}
|
|
@@ -24514,15 +24769,21 @@ const mergeMetadata = (source, target) => {
|
|
|
24514
24769
|
*
|
|
24515
24770
|
* @internal
|
|
24516
24771
|
*/
|
|
24772
|
+
// Deliberately co-located with `mergeMetadata` above
|
|
24773
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24517
24774
|
const mergeTelemetryItem = (target, source) => {
|
|
24775
|
+
// Both arguments are required to produce a valid merged telemetry item
|
|
24518
24776
|
if (!target || !source) {
|
|
24519
24777
|
throw new Error('Both target and source must be defined for merging telemetry items.');
|
|
24520
24778
|
}
|
|
24779
|
+
// A type mismatch would produce an inconsistent telemetry item, so fail fast
|
|
24521
24780
|
if (target.type && source.type && target.type !== source.type) {
|
|
24522
24781
|
throw new Error('Mismatched telemetry item types.');
|
|
24523
24782
|
}
|
|
24783
|
+
// Combine both scopes and de-duplicate via a Set before flattening back to an array
|
|
24524
24784
|
const scope = [...new Set([...(target.scope ?? []), ...(source.scope ?? [])])];
|
|
24525
24785
|
const metadata = mergeMetadata(target.metadata, source.metadata);
|
|
24786
|
+
// Source properties take precedence over target, with scope/metadata merged separately above
|
|
24526
24787
|
return { ...target, ...source, scope, metadata };
|
|
24527
24788
|
};
|
|
24528
24789
|
|
|
@@ -24553,11 +24814,18 @@ const mergeTelemetryItem = (target, source) => {
|
|
|
24553
24814
|
class TelemetryConfigurator extends BaseConfigBuilder {
|
|
24554
24815
|
#adaptersCallbacks = {};
|
|
24555
24816
|
#metadata = [];
|
|
24817
|
+
/**
|
|
24818
|
+
* Creates a new `TelemetryConfigurator` and wires up the async adapter
|
|
24819
|
+
* resolution and metadata merging pipelines used by `createConfigAsync`.
|
|
24820
|
+
*/
|
|
24556
24821
|
constructor() {
|
|
24557
24822
|
super();
|
|
24558
24823
|
// Configure async adapter resolution using mergeMap to handle Promise/Observable adapter factories
|
|
24559
24824
|
this._set('adapters', (args) => {
|
|
24560
|
-
|
|
24825
|
+
// Resolve every adapter factory concurrently and merge them into a single record
|
|
24826
|
+
return from(Object.entries(this.#adaptersCallbacks)).pipe(mergeMap(([identifier, adapterFn]) =>
|
|
24827
|
+
// Drop adapters whose factory resolved to a falsy value
|
|
24828
|
+
from(adapterFn(args)).pipe(filter((adapter) => !!adapter), map$1((adapter) => [identifier, adapter]))), scan((acc, [identifier, adapter]) => {
|
|
24561
24829
|
acc[identifier] = adapter;
|
|
24562
24830
|
return acc;
|
|
24563
24831
|
}, {}), defaultIfEmpty({}), shareReplay({ bufferSize: 1, refCount: true }));
|
|
@@ -24565,12 +24833,15 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
24565
24833
|
// Configure metadata merging - handles multiple sync/async metadata sources
|
|
24566
24834
|
this._set('metadata', async () => {
|
|
24567
24835
|
const metadataItems = this.#metadata;
|
|
24568
|
-
return (...args) =>
|
|
24836
|
+
return (...args) =>
|
|
24837
|
+
// Merge every registered metadata source in order, emitting the final accumulated result
|
|
24838
|
+
from(metadataItems).pipe(concatMap((metadata) => toObservable(metadata, ...args)), scan((acc, current) => mergeMetadata(acc, current) ?? {}, {}), last(), shareReplay({ bufferSize: 1, refCount: true }));
|
|
24569
24839
|
});
|
|
24570
24840
|
}
|
|
24571
24841
|
/**
|
|
24572
24842
|
* Registers a telemetry adapter with the configurator.
|
|
24573
24843
|
*
|
|
24844
|
+
* @param identifier - The key used to register and later resolve this adapter.
|
|
24574
24845
|
* @param adapter - The telemetry adapter to be added. The adapter's identifier is used as the key.
|
|
24575
24846
|
* @returns The current instance of the configurator for method chaining.
|
|
24576
24847
|
*/
|
|
@@ -24580,7 +24851,8 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
24580
24851
|
/**
|
|
24581
24852
|
* Configures a telemetry adapter with the configurator.
|
|
24582
24853
|
*
|
|
24583
|
-
* @param
|
|
24854
|
+
* @param identifier - The key used to register and later resolve this adapter.
|
|
24855
|
+
* @param adapterFn - A callback function that returns a telemetry adapter instance
|
|
24584
24856
|
* @returns The current instance for method chaining
|
|
24585
24857
|
*/
|
|
24586
24858
|
configureAdapter(identifier, adapterFn) {
|
|
@@ -24645,7 +24917,7 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
24645
24917
|
}
|
|
24646
24918
|
|
|
24647
24919
|
// Generated by genversion.
|
|
24648
|
-
const version$5 = '7.0.
|
|
24920
|
+
const version$5 = '7.0.1';
|
|
24649
24921
|
|
|
24650
24922
|
/**
|
|
24651
24923
|
* Enum representing the severity levels of telemetry items.
|
|
@@ -24739,6 +25011,8 @@ const TelemetryItemSchema = object({
|
|
|
24739
25011
|
*
|
|
24740
25012
|
* Extends TelemetryItemSchema and sets type to 'event'.
|
|
24741
25013
|
*/
|
|
25014
|
+
// Deliberately co-located with the other telemetry item schemas below
|
|
25015
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24742
25016
|
const TelemetryEventSchema = TelemetryItemSchema.extend({
|
|
24743
25017
|
type: literal(TelemetryType.Event),
|
|
24744
25018
|
});
|
|
@@ -24748,6 +25022,8 @@ const TelemetryEventSchema = TelemetryItemSchema.extend({
|
|
|
24748
25022
|
* Extends TelemetryItemSchema and sets type to 'exception'.
|
|
24749
25023
|
* Adds an exception property of type Error.
|
|
24750
25024
|
*/
|
|
25025
|
+
// Deliberately co-located with the other telemetry item schemas above/below
|
|
25026
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24751
25027
|
const TelemetryExceptionSchema = TelemetryItemSchema.extend({
|
|
24752
25028
|
type: literal(TelemetryType.Exception),
|
|
24753
25029
|
exception: _instanceof(Error).describe('The exception object.'),
|
|
@@ -24758,6 +25034,8 @@ const TelemetryExceptionSchema = TelemetryItemSchema.extend({
|
|
|
24758
25034
|
* Extends TelemetryItemSchema and sets type to 'metric'.
|
|
24759
25035
|
* Adds a value property for the metric value.
|
|
24760
25036
|
*/
|
|
25037
|
+
// Deliberately co-located with the other telemetry item schemas above/below
|
|
25038
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24761
25039
|
const TelemetryMetricSchema = TelemetryItemSchema.extend({
|
|
24762
25040
|
type: literal(TelemetryType.Metric),
|
|
24763
25041
|
value: number$1().describe('The value of the metric.'),
|
|
@@ -24767,6 +25045,8 @@ const TelemetryMetricSchema = TelemetryItemSchema.extend({
|
|
|
24767
25045
|
*
|
|
24768
25046
|
* Extends TelemetryEventSchema and allows passthrough of additional properties.
|
|
24769
25047
|
*/
|
|
25048
|
+
// Deliberately co-located with the other telemetry item schemas above
|
|
25049
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24770
25050
|
const TelemetryCustomEventSchema = TelemetryEventSchema.extend({
|
|
24771
25051
|
type: literal(TelemetryType.Custom),
|
|
24772
25052
|
}).passthrough();
|
|
@@ -24788,7 +25068,6 @@ const TelemetryCustomEventSchema = TelemetryEventSchema.extend({
|
|
|
24788
25068
|
* @template TInit The type of the event details.
|
|
24789
25069
|
* @template TType The type of the event type.
|
|
24790
25070
|
*/
|
|
24791
|
-
// biome-ignore lint/suspicious/noUnsafeDeclarationMerging: no other way to define a class with multiple signatures
|
|
24792
25071
|
class FrameworkEvent {
|
|
24793
25072
|
__type;
|
|
24794
25073
|
#detail;
|
|
@@ -24867,6 +25146,7 @@ class FrameworkEvent {
|
|
|
24867
25146
|
* If the event is cancelable, this method sets the `canceled` property to `true`.
|
|
24868
25147
|
*/
|
|
24869
25148
|
preventDefault() {
|
|
25149
|
+
// Only cancelable events can be canceled
|
|
24870
25150
|
if (this.cancelable) {
|
|
24871
25151
|
this.#canceled = true;
|
|
24872
25152
|
}
|
|
@@ -24881,44 +25161,54 @@ class FrameworkEvent {
|
|
|
24881
25161
|
}
|
|
24882
25162
|
|
|
24883
25163
|
/**
|
|
24884
|
-
*
|
|
25164
|
+
* Event representing an error that occurred within a telemetry provider.
|
|
24885
25165
|
*
|
|
24886
|
-
*
|
|
24887
|
-
*
|
|
25166
|
+
* @remarks
|
|
25167
|
+
* This event is emitted when an error is encountered by an {@link ITelemetryProvider}.
|
|
25168
|
+
* It encapsulates the error details and the source provider.
|
|
24888
25169
|
*
|
|
24889
25170
|
* @example
|
|
24890
25171
|
* ```typescript
|
|
24891
|
-
* const
|
|
25172
|
+
* const errorEvent = new TelemetryErrorEvent(new Error('Something went wrong'), telemetryProvider);
|
|
24892
25173
|
* ```
|
|
24893
25174
|
*
|
|
24894
|
-
* @
|
|
24895
|
-
* @param source - The telemetry provider that is the source of this event.
|
|
25175
|
+
* @extends FrameworkEvent
|
|
24896
25176
|
*/
|
|
24897
|
-
class
|
|
24898
|
-
|
|
24899
|
-
|
|
25177
|
+
class TelemetryErrorEvent extends FrameworkEvent {
|
|
25178
|
+
/**
|
|
25179
|
+
* Creates a new `TelemetryErrorEvent`.
|
|
25180
|
+
*
|
|
25181
|
+
* @param error - The error instance that was thrown.
|
|
25182
|
+
* @param source - The telemetry provider where the error originated.
|
|
25183
|
+
*/
|
|
25184
|
+
constructor(error, source) {
|
|
25185
|
+
super('onTelemetryError', { detail: { error }, source });
|
|
24900
25186
|
}
|
|
24901
25187
|
}
|
|
25188
|
+
|
|
24902
25189
|
/**
|
|
24903
|
-
*
|
|
25190
|
+
* Represents a telemetry event within the framework.
|
|
24904
25191
|
*
|
|
24905
|
-
*
|
|
24906
|
-
*
|
|
24907
|
-
* It encapsulates the error details and the source provider.
|
|
25192
|
+
* This event encapsulates a `TelemetryItem` and is associated with a specific `ITelemetryProvider` source.
|
|
25193
|
+
* It extends the `FrameworkEvent` class, providing a standardized way to emit and handle telemetry-related events.
|
|
24908
25194
|
*
|
|
24909
25195
|
* @example
|
|
24910
25196
|
* ```typescript
|
|
24911
|
-
* const
|
|
25197
|
+
* const event = new TelemetryEvent(item, provider);
|
|
24912
25198
|
* ```
|
|
24913
25199
|
*
|
|
24914
|
-
* @
|
|
24915
|
-
*
|
|
24916
|
-
* @param error - The error instance that was thrown.
|
|
24917
|
-
* @param source - The telemetry provider where the error originated.
|
|
25200
|
+
* @param item - The telemetry item containing event data.
|
|
25201
|
+
* @param source - The telemetry provider that is the source of this event.
|
|
24918
25202
|
*/
|
|
24919
|
-
class
|
|
24920
|
-
|
|
24921
|
-
|
|
25203
|
+
class TelemetryEvent extends FrameworkEvent {
|
|
25204
|
+
/**
|
|
25205
|
+
* Creates a new `TelemetryEvent`.
|
|
25206
|
+
*
|
|
25207
|
+
* @param item - The telemetry item containing event data.
|
|
25208
|
+
* @param source - The telemetry provider that is the source of this event.
|
|
25209
|
+
*/
|
|
25210
|
+
constructor(item, source) {
|
|
25211
|
+
super('onTelemetry', { detail: { item }, source });
|
|
24922
25212
|
}
|
|
24923
25213
|
}
|
|
24924
25214
|
|
|
@@ -24966,6 +25256,7 @@ class Measurement {
|
|
|
24966
25256
|
*/
|
|
24967
25257
|
clone(resetOptions = {}) {
|
|
24968
25258
|
const clonedMeasurement = new Measurement(this.#provider, this.#data);
|
|
25259
|
+
// Preserve the original start time when requested, otherwise the clone starts fresh
|
|
24969
25260
|
if (resetOptions.preserveStartTime) {
|
|
24970
25261
|
clonedMeasurement.#startTime = this.#startTime;
|
|
24971
25262
|
}
|
|
@@ -24980,6 +25271,7 @@ class Measurement {
|
|
|
24980
25271
|
* Resets the measured flag and, unless `preserveStartTime` is true, updates the start time to the current performance time.
|
|
24981
25272
|
*/
|
|
24982
25273
|
reset(options) {
|
|
25274
|
+
// Keep the existing start time when preserving, otherwise restart the clock
|
|
24983
25275
|
if (!options?.preserveStartTime) {
|
|
24984
25276
|
this.#startTime = performance.now();
|
|
24985
25277
|
}
|
|
@@ -24994,6 +25286,7 @@ class Measurement {
|
|
|
24994
25286
|
* @param options - Optional measurement options.
|
|
24995
25287
|
* @param options.markAsMeasured - If true, marks this measurement as completed.
|
|
24996
25288
|
* @param options.resetStartTime - If true, resets the start time after measuring.
|
|
25289
|
+
* @returns The elapsed duration in milliseconds since the last start time.
|
|
24997
25290
|
*/
|
|
24998
25291
|
measure(data, options) {
|
|
24999
25292
|
const measuredData = mergeTelemetryItem(this.#data, data ?? {});
|
|
@@ -25002,9 +25295,11 @@ class Measurement {
|
|
|
25002
25295
|
...measuredData,
|
|
25003
25296
|
value: duration,
|
|
25004
25297
|
});
|
|
25298
|
+
// Mark as measured only when explicitly requested by the caller
|
|
25005
25299
|
if (options?.markAsMeasured) {
|
|
25006
25300
|
this.#measured = true;
|
|
25007
25301
|
}
|
|
25302
|
+
// Restart the clock only when explicitly requested by the caller
|
|
25008
25303
|
if (options?.resetStartTime) {
|
|
25009
25304
|
this.#startTime = performance.now();
|
|
25010
25305
|
}
|
|
@@ -25013,7 +25308,7 @@ class Measurement {
|
|
|
25013
25308
|
/**
|
|
25014
25309
|
* Resolves a given promise, measures the result using the provided options, and returns the resolved value.
|
|
25015
25310
|
*
|
|
25016
|
-
* @
|
|
25311
|
+
* @template T - The type of the resolved value from the promise.
|
|
25017
25312
|
* @param promise - The promise to resolve.
|
|
25018
25313
|
* @param options - Optional configuration for resolving and measuring:
|
|
25019
25314
|
* - `data`: A value or a function that receives the resolved result and returns measurement data.
|
|
@@ -25031,7 +25326,7 @@ class Measurement {
|
|
|
25031
25326
|
* The function `fn` can return either a value of type `T` or a Promise of type `T`.
|
|
25032
25327
|
* Optionally, resolution options can be provided.
|
|
25033
25328
|
*
|
|
25034
|
-
* @
|
|
25329
|
+
* @template T - The return type of the function to execute.
|
|
25035
25330
|
* @param fn - A function that returns a value or a Promise to be resolved.
|
|
25036
25331
|
* @param options - Optional resolution options to customize the resolve behavior.
|
|
25037
25332
|
* @returns A Promise that resolves to the result of the executed function.
|
|
@@ -25049,6 +25344,7 @@ class Measurement {
|
|
|
25049
25344
|
* @see https://github.com/tc39/proposal-explicit-resource-management
|
|
25050
25345
|
*/
|
|
25051
25346
|
[Symbol.dispose]() {
|
|
25347
|
+
// Ensure a measurement is always taken before the instance is discarded
|
|
25052
25348
|
if (!this.#measured) {
|
|
25053
25349
|
try {
|
|
25054
25350
|
this.measure();
|
|
@@ -25091,7 +25387,10 @@ const resolveMetadata = (metadata, args) => {
|
|
|
25091
25387
|
* @param args - The arguments required by the metadata extractor, including the telemetry item.
|
|
25092
25388
|
* @returns An Observable emitting the telemetry item with applied metadata, or an error telemetry item if resolution fails.
|
|
25093
25389
|
*/
|
|
25390
|
+
// Deliberately co-located with `resolveMetadata` above
|
|
25391
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
25094
25392
|
const applyMetadata = (metadata, args) => {
|
|
25393
|
+
// Resolve metadata, merge it into the item, and fall back to an error item on failure
|
|
25095
25394
|
return resolveMetadata(metadata, args).pipe(defaultIfEmpty({}), // Ensure we always have an object to merge with
|
|
25096
25395
|
first(), // Take the first emitted value from the metadata extractor
|
|
25097
25396
|
// Merge the resolved metadata with the telemetry item.
|
|
@@ -25145,20 +25444,39 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25145
25444
|
#filters;
|
|
25146
25445
|
#defaultScope;
|
|
25147
25446
|
#eventProvider;
|
|
25447
|
+
/**
|
|
25448
|
+
* Stream of every telemetry item tracked by this provider.
|
|
25449
|
+
*
|
|
25450
|
+
* @returns An observable emitting each tracked `TelemetryItem`.
|
|
25451
|
+
*/
|
|
25148
25452
|
get items() {
|
|
25149
25453
|
return this.#items.asObservable();
|
|
25150
25454
|
}
|
|
25151
25455
|
/**
|
|
25152
25456
|
* Returns true if the provider has been initialized.
|
|
25457
|
+
*
|
|
25458
|
+
* @returns `true` when {@link initialize} has completed, otherwise `false`.
|
|
25153
25459
|
*/
|
|
25154
25460
|
get initialized() {
|
|
25155
25461
|
return this.#initialized;
|
|
25156
25462
|
}
|
|
25157
25463
|
#metadata;
|
|
25158
25464
|
#modules;
|
|
25465
|
+
/**
|
|
25466
|
+
* Sets the resolved framework module instances used to resolve metadata.
|
|
25467
|
+
*
|
|
25468
|
+
* @param value - The resolved framework module instances.
|
|
25469
|
+
*/
|
|
25159
25470
|
set modules(value) {
|
|
25160
25471
|
this.#modules = value;
|
|
25161
25472
|
}
|
|
25473
|
+
/**
|
|
25474
|
+
* Creates a new `TelemetryProvider`.
|
|
25475
|
+
*
|
|
25476
|
+
* @param config - Resolved telemetry configuration (adapters, metadata, scope, filters).
|
|
25477
|
+
* @param deps - Optional dependencies; supply an event module provider to dispatch
|
|
25478
|
+
* telemetry items as events.
|
|
25479
|
+
*/
|
|
25162
25480
|
constructor(config, deps) {
|
|
25163
25481
|
super({ version: version$5, config });
|
|
25164
25482
|
this.#adapters = config?.adapters ?? {};
|
|
@@ -25218,11 +25536,13 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25218
25536
|
async _initializeAdapters() {
|
|
25219
25537
|
// Initialize all adapters, do it in parallel
|
|
25220
25538
|
const adapterEntries = Object.entries(this.#adapters);
|
|
25539
|
+
// Kick off every adapter's initialize() concurrently
|
|
25221
25540
|
const initializationPromises = adapterEntries.map(([_identifier, adapter]) => adapter.initialize());
|
|
25222
25541
|
// Wait for all adapters to settle (either resolve or reject)
|
|
25223
25542
|
const results = await Promise.allSettled(initializationPromises);
|
|
25224
25543
|
// Check each result and dispatch errors for failed initializations
|
|
25225
25544
|
for (const [index, result] of results.entries()) {
|
|
25545
|
+
// Only rejected adapters need an error dispatched; fulfilled ones need no action
|
|
25226
25546
|
if (result.status === 'rejected') {
|
|
25227
25547
|
const [identifier] = adapterEntries[index];
|
|
25228
25548
|
this._dispatchError(new Error(`Failed to initialize telemetry adapter "${identifier}"`, {
|
|
@@ -25239,8 +25559,10 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25239
25559
|
*
|
|
25240
25560
|
* @returns Subscription to the telemetry item stream
|
|
25241
25561
|
* @protected
|
|
25562
|
+
* @throws {Error} When the provider has not been initialized yet.
|
|
25242
25563
|
*/
|
|
25243
25564
|
_connectAdapters() {
|
|
25565
|
+
// Adapters can only be connected after the provider has completed initialization
|
|
25244
25566
|
if (!this.#initialized) {
|
|
25245
25567
|
throw new Error('TelemetryProvider is not initialized');
|
|
25246
25568
|
}
|
|
@@ -25293,6 +25615,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25293
25615
|
* @protected
|
|
25294
25616
|
*/
|
|
25295
25617
|
_dispatchEntityEvent(item) {
|
|
25618
|
+
// Only dispatch when an event provider is configured
|
|
25296
25619
|
if (this.#eventProvider) {
|
|
25297
25620
|
// Wrap the telemetry item in a TelemetryEvent and dispatch it
|
|
25298
25621
|
this.#eventProvider.dispatchEvent(new TelemetryEvent(item, this));
|
|
@@ -25305,6 +25628,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25305
25628
|
* @protected
|
|
25306
25629
|
*/
|
|
25307
25630
|
_dispatchError(error) {
|
|
25631
|
+
// Only dispatch when an event provider is configured
|
|
25308
25632
|
if (this.#eventProvider) {
|
|
25309
25633
|
this.#eventProvider.dispatchEvent(new TelemetryErrorEvent(error, this));
|
|
25310
25634
|
}
|
|
@@ -25322,6 +25646,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25322
25646
|
* });
|
|
25323
25647
|
*/
|
|
25324
25648
|
track(item) {
|
|
25649
|
+
// Dispatch to the type-specific tracker so each item is validated with the right schema
|
|
25325
25650
|
switch (item.type) {
|
|
25326
25651
|
case TelemetryType.Event:
|
|
25327
25652
|
this.trackEvent(item);
|
|
@@ -25434,6 +25759,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25434
25759
|
*/
|
|
25435
25760
|
getAdapter(identifier) {
|
|
25436
25761
|
const adapter = this.#adapters[identifier];
|
|
25762
|
+
// Report a missing adapter as a telemetry exception rather than throwing
|
|
25437
25763
|
if (!adapter) {
|
|
25438
25764
|
this.trackException({
|
|
25439
25765
|
name: 'TelemetryAdapterNotFound',
|
|
@@ -25501,6 +25827,7 @@ const module$3 = {
|
|
|
25501
25827
|
const mapConfiguratorEvents = (
|
|
25502
25828
|
// biome-ignore lint/suspicious/noExplicitAny: must be any to support all module types
|
|
25503
25829
|
configurator) => {
|
|
25830
|
+
// Map each configurator event into a standardized telemetry item shape
|
|
25504
25831
|
return configurator.event$.pipe(map$1((event) => {
|
|
25505
25832
|
const item = {
|
|
25506
25833
|
name: event.name,
|
|
@@ -25512,6 +25839,7 @@ configurator) => {
|
|
|
25512
25839
|
},
|
|
25513
25840
|
scope: ['configuration'],
|
|
25514
25841
|
};
|
|
25842
|
+
// An event carrying an error is reported as a telemetry exception
|
|
25515
25843
|
if (event.error) {
|
|
25516
25844
|
return {
|
|
25517
25845
|
...item,
|
|
@@ -25519,6 +25847,7 @@ configurator) => {
|
|
|
25519
25847
|
exception: event.error,
|
|
25520
25848
|
};
|
|
25521
25849
|
}
|
|
25850
|
+
// An event carrying a metric value is reported as a telemetry metric
|
|
25522
25851
|
if (event.metric) {
|
|
25523
25852
|
return {
|
|
25524
25853
|
...item,
|
|
@@ -25568,9 +25897,11 @@ configurator, options) => {
|
|
|
25568
25897
|
configurator.addConfig({
|
|
25569
25898
|
module: module$3,
|
|
25570
25899
|
configure: async (builder, ref) => {
|
|
25900
|
+
// Attach configurator events to the telemetry builder only when opted in
|
|
25571
25901
|
{
|
|
25572
25902
|
builder.attachItems(mapConfiguratorEvents(configurator));
|
|
25573
25903
|
}
|
|
25904
|
+
// Run the caller-supplied configure callback when one was provided
|
|
25574
25905
|
if (options?.configure) {
|
|
25575
25906
|
await Promise.resolve(options.configure(builder, ref));
|
|
25576
25907
|
}
|
|
@@ -42517,11 +42848,14 @@ class MsalClient extends PublicClientApplication {
|
|
|
42517
42848
|
* - **Redirect**: Navigates browser to Microsoft login page. Returns `void` because the browser
|
|
42518
42849
|
* navigates to a new page. After redirect completes, the result will be available via
|
|
42519
42850
|
* `handleRedirectPromise()` when the app loads on the new page.
|
|
42851
|
+
*
|
|
42852
|
+
* @throws {Error} If an invalid `options.behavior` value is provided.
|
|
42520
42853
|
*/
|
|
42521
42854
|
async login(options) {
|
|
42522
42855
|
// Attempt silent authentication first if enabled
|
|
42523
42856
|
// This provides better UX by avoiding unnecessary popups/redirects
|
|
42524
42857
|
if (options.silent) {
|
|
42858
|
+
// Warn early when neither an account nor login hint is available for silent SSO
|
|
42525
42859
|
if (!options.request.account && !options.request.loginHint) {
|
|
42526
42860
|
this.getLogger().warning('No account or login hint provided, please provide an account or login hint in the request', options.request.correlationId || FUSION_CORRELATION_ID);
|
|
42527
42861
|
}
|
|
@@ -42579,6 +42913,7 @@ class MsalClient extends PublicClientApplication {
|
|
|
42579
42913
|
* ```
|
|
42580
42914
|
*/
|
|
42581
42915
|
async logout(options) {
|
|
42916
|
+
// Warn when no account was supplied since the active account will be used instead
|
|
42582
42917
|
if (!options?.account) {
|
|
42583
42918
|
this.getLogger().warning('No account available for logout, please provide an account in the options', FUSION_CORRELATION_ID);
|
|
42584
42919
|
}
|
|
@@ -42610,18 +42945,23 @@ class MsalClient extends PublicClientApplication {
|
|
|
42610
42945
|
*
|
|
42611
42946
|
* The default silent behavior is determined by presence of account in the request.
|
|
42612
42947
|
* This provides optimal UX by minimizing unnecessary user interactions.
|
|
42948
|
+
*
|
|
42949
|
+
* @throws {Error} If no `request` is provided in `options`.
|
|
42613
42950
|
*/
|
|
42614
42951
|
async acquireToken(options) {
|
|
42615
42952
|
const { behavior = 'redirect', silent = !!options.request?.account, request } = options;
|
|
42953
|
+
// A request is required to know which scopes/account to acquire a token for
|
|
42616
42954
|
if (!request) {
|
|
42617
42955
|
throw new Error('No request provided, please provide a request in the options');
|
|
42618
42956
|
}
|
|
42957
|
+
// Warn when no scopes are requested, since MSAL requires at least one scope
|
|
42619
42958
|
if (request.scopes.length === 0) {
|
|
42620
42959
|
this.getLogger().warning('No scopes provided, please provide scopes in the request option, see options.request for more information.', request.correlationId || FUSION_CORRELATION_ID);
|
|
42621
42960
|
}
|
|
42622
42961
|
// Attempt silent token acquisition first
|
|
42623
42962
|
// This fetches from cache or uses refresh token without user interaction
|
|
42624
42963
|
if (silent) {
|
|
42964
|
+
// Only silent-acquire when an account is available to look up cached tokens for
|
|
42625
42965
|
if (request.account) {
|
|
42626
42966
|
try {
|
|
42627
42967
|
this.getLogger().verbose('Attempting to acquire token silently', request.correlationId || FUSION_CORRELATION_ID);
|
|
@@ -42681,6 +43021,7 @@ const levelMap = {
|
|
|
42681
43021
|
const parseMsalMessage = (message) => {
|
|
42682
43022
|
// Match the structured format: [timestamp] : [correlationId] : [package] : [level] - [component] - [message]
|
|
42683
43023
|
const match = message.match(/^\[([^\]]+)\]\s*:\s*\[([^\]]*)\]\s*:\s*([^:]+)\s*:\s*(\w+)\s*-\s*([^-]+)\s*-\s*(.*)$/);
|
|
43024
|
+
// A structured match means we can extract package/component/message parts
|
|
42684
43025
|
if (match) {
|
|
42685
43026
|
const [, _timestamp, _correlationId, packageInfo, _logLevel, component, logMessage] = match;
|
|
42686
43027
|
return {
|
|
@@ -42743,7 +43084,7 @@ const createClientLogCallback = (provider, metadata, scope) => {
|
|
|
42743
43084
|
};
|
|
42744
43085
|
|
|
42745
43086
|
// Generated by genversion.
|
|
42746
|
-
const version$2 = '10.0.
|
|
43087
|
+
const version$2 = '10.0.2';
|
|
42747
43088
|
|
|
42748
43089
|
/**
|
|
42749
43090
|
* Zod schema for telemetry configuration validation.
|
|
@@ -42796,6 +43137,7 @@ class MsalConfigurator extends BaseConfigBuilder {
|
|
|
42796
43137
|
* The MSAL module version being configured.
|
|
42797
43138
|
*
|
|
42798
43139
|
* @default Latest
|
|
43140
|
+
* @returns The configured MSAL module version.
|
|
42799
43141
|
*/
|
|
42800
43142
|
get version() {
|
|
42801
43143
|
return version$2;
|
|
@@ -42811,6 +43153,7 @@ class MsalConfigurator extends BaseConfigBuilder {
|
|
|
42811
43153
|
this._set('version', async () => this.version);
|
|
42812
43154
|
// Auto-detect and integrate telemetry module if available
|
|
42813
43155
|
this._set('telemetry.provider', async (args) => {
|
|
43156
|
+
// Only resolve the telemetry instance when the telemetry module is registered
|
|
42814
43157
|
if (args.hasModule('telemetry')) {
|
|
42815
43158
|
const telemetry = await args.requireInstance('telemetry');
|
|
42816
43159
|
return telemetry;
|
|
@@ -43034,7 +43377,7 @@ class MsalConfigurator extends BaseConfigBuilder {
|
|
|
43034
43377
|
const config = await MsalConfigSchema.parseAsync(rawConfig);
|
|
43035
43378
|
// Auto-create client if config provided but no client instance
|
|
43036
43379
|
// This allows users to provide configuration without manually instantiating the client
|
|
43037
|
-
if (!config.client &&
|
|
43380
|
+
if (!config.client && this.#msalConfig) {
|
|
43038
43381
|
const clientConfig = this.#msalConfig;
|
|
43039
43382
|
config.telemetry.provider?.trackEvent({
|
|
43040
43383
|
name: 'module-msal.configurator._processConfig.creating-client',
|
|
@@ -43203,12 +43546,15 @@ class VersionError extends Error {
|
|
|
43203
43546
|
function mapVersionToEnumVersion(version) {
|
|
43204
43547
|
console.log('Resolving version:', version);
|
|
43205
43548
|
const coercedVersion = semver.coerce(version);
|
|
43549
|
+
// An uncoercible version string cannot be mapped to a module version
|
|
43206
43550
|
if (!coercedVersion) {
|
|
43207
43551
|
throw new Error(`Invalid version: ${version}`);
|
|
43208
43552
|
}
|
|
43553
|
+
// Versions before 4.0.0 (including 2.x) map to the v2-compatible module version
|
|
43209
43554
|
if (semver.satisfies(coercedVersion, '<4.0.0')) {
|
|
43210
43555
|
return MsalModuleVersion.V2;
|
|
43211
43556
|
}
|
|
43557
|
+
// Versions before 7.0.0 (4.x and 5.x/6.x) map to the v4-compatible module version
|
|
43212
43558
|
if (semver.satisfies(coercedVersion, '<7.0.0')) {
|
|
43213
43559
|
return MsalModuleVersion.V4;
|
|
43214
43560
|
}
|
|
@@ -43271,6 +43617,7 @@ function resolveVersion(version) {
|
|
|
43271
43617
|
warnings.push(missingVersionWarning.message);
|
|
43272
43618
|
wantedVersion = latestVersion;
|
|
43273
43619
|
}
|
|
43620
|
+
// Warn when the requested major version trails the latest major version
|
|
43274
43621
|
if (wantedVersion.major < latestVersion.major) {
|
|
43275
43622
|
const majorBehindVersionWarning = new VersionError(`Requested major version ${wantedVersion.major} is behind the latest major version ${latestVersion.major}`, wantedVersion, latestVersion);
|
|
43276
43623
|
warnings.push(majorBehindVersionWarning.message);
|
|
@@ -43365,9 +43712,11 @@ function mapAuthenticationResult(result) {
|
|
|
43365
43712
|
function createProxyClient(client) {
|
|
43366
43713
|
const proxy = new Proxy(client, {
|
|
43367
43714
|
get: (target, prop) => {
|
|
43715
|
+
// Adapt each v4 client member to the v2-compatible shape expected by callers
|
|
43368
43716
|
switch (prop) {
|
|
43369
43717
|
case 'getAllAccounts': {
|
|
43370
43718
|
return () => {
|
|
43719
|
+
// Map each v4 account shape to its v2-compatible equivalent
|
|
43371
43720
|
return target.getAllAccounts().map(mapAccountInfo);
|
|
43372
43721
|
};
|
|
43373
43722
|
}
|
|
@@ -43399,6 +43748,7 @@ function createProxyClient(client) {
|
|
|
43399
43748
|
case 'handleRedirectPromise': {
|
|
43400
43749
|
return async () => {
|
|
43401
43750
|
const result = await target.handleRedirectPromise();
|
|
43751
|
+
// No redirect result means there's nothing pending to map
|
|
43402
43752
|
if (!result) {
|
|
43403
43753
|
return null;
|
|
43404
43754
|
}
|
|
@@ -43408,6 +43758,7 @@ function createProxyClient(client) {
|
|
|
43408
43758
|
case 'getActiveAccount': {
|
|
43409
43759
|
return () => {
|
|
43410
43760
|
const account = target.getActiveAccount();
|
|
43761
|
+
// No active account means there's nothing to map
|
|
43411
43762
|
if (!account) {
|
|
43412
43763
|
return null;
|
|
43413
43764
|
}
|
|
@@ -43494,6 +43845,8 @@ function createProxyClient(client) {
|
|
|
43494
43845
|
}
|
|
43495
43846
|
},
|
|
43496
43847
|
});
|
|
43848
|
+
// The Proxy handler above implements every member of `IAuthClient` at runtime via its `get`
|
|
43849
|
+
// trap, but TypeScript can't verify a `Proxy<T>` satisfies `T` structurally.
|
|
43497
43850
|
return proxy;
|
|
43498
43851
|
}
|
|
43499
43852
|
|
|
@@ -43504,6 +43857,7 @@ function createProxyClient(client) {
|
|
|
43504
43857
|
* @returns True if the request is in v4 format (has a `request` property with `scopes` and `account`)
|
|
43505
43858
|
*/
|
|
43506
43859
|
function isRequestV4(req) {
|
|
43860
|
+
// Non-object/null values can never match the v4 request shape
|
|
43507
43861
|
if (typeof req !== 'object' || req === null) {
|
|
43508
43862
|
return false;
|
|
43509
43863
|
}
|
|
@@ -43537,6 +43891,7 @@ function createProxyProvider$2(provider) {
|
|
|
43537
43891
|
// Use Proxy to intercept property access and provide v2-compatible implementations
|
|
43538
43892
|
const proxy = new Proxy(provider, {
|
|
43539
43893
|
get: (target, prop) => {
|
|
43894
|
+
// Adapt each v4 provider member to the v2-compatible shape expected by callers
|
|
43540
43895
|
switch (prop) {
|
|
43541
43896
|
case 'version': {
|
|
43542
43897
|
return provider.version;
|
|
@@ -43551,6 +43906,7 @@ function createProxyProvider$2(provider) {
|
|
|
43551
43906
|
case 'defaultClient': {
|
|
43552
43907
|
// Deprecated property - redirect to client with warning
|
|
43553
43908
|
console.warn('defaultClient is deprecated, use client instead');
|
|
43909
|
+
// Same v2-compatible client wrapper as the `client` case above.
|
|
43554
43910
|
return v2Client;
|
|
43555
43911
|
}
|
|
43556
43912
|
case 'defaultAccount': {
|
|
@@ -43634,6 +43990,7 @@ function createProxyProvider$2(provider) {
|
|
|
43634
43990
|
};
|
|
43635
43991
|
}
|
|
43636
43992
|
default: {
|
|
43993
|
+
// The proxy is awaited in some contexts, so `then` must resolve to undefined
|
|
43637
43994
|
if (prop === 'then') {
|
|
43638
43995
|
return undefined;
|
|
43639
43996
|
}
|
|
@@ -43671,6 +44028,7 @@ function createProxyProvider$1(provider) {
|
|
|
43671
44028
|
// Create passthrough proxy that only overrides msalVersion
|
|
43672
44029
|
return new Proxy(provider, {
|
|
43673
44030
|
get: (target, prop) => {
|
|
44031
|
+
// Passthrough every property except msalVersion, which reports V4 compatibility
|
|
43674
44032
|
switch (prop) {
|
|
43675
44033
|
case 'msalVersion': {
|
|
43676
44034
|
// Report as V4 for compatibility tracking
|
|
@@ -43732,6 +44090,9 @@ function createProxyProvider$1(provider) {
|
|
|
43732
44090
|
* @param version - The target version string (e.g., '2.0.0', '4.0.0')
|
|
43733
44091
|
* @returns A proxy provider compatible with the specified version
|
|
43734
44092
|
*
|
|
44093
|
+
* @template T - The provider interface type expected by the target version.
|
|
44094
|
+
* @throws {Error} If the resolved version is not supported.
|
|
44095
|
+
*
|
|
43735
44096
|
* @example
|
|
43736
44097
|
* ```typescript
|
|
43737
44098
|
* const baseProvider = new MsalProvider(config);
|
|
@@ -43741,6 +44102,7 @@ function createProxyProvider$1(provider) {
|
|
|
43741
44102
|
function createProxyProvider(provider, version) {
|
|
43742
44103
|
// Resolve the requested version to determine which proxy to create
|
|
43743
44104
|
const { enumVersion } = resolveVersion(version);
|
|
44105
|
+
// Build the version-appropriate proxy based on the resolved MSAL module version
|
|
43744
44106
|
switch (enumVersion) {
|
|
43745
44107
|
case MsalModuleVersion.V2:
|
|
43746
44108
|
// Create v2-compatible proxy with legacy API adapters
|
|
@@ -43752,6 +44114,7 @@ function createProxyProvider(provider, version) {
|
|
|
43752
44114
|
// Create transparent proxy for v5 - passes through to original provider
|
|
43753
44115
|
return new Proxy(provider, {
|
|
43754
44116
|
get: (target, prop) => {
|
|
44117
|
+
// Delegate every known property/method to the underlying provider
|
|
43755
44118
|
switch (prop) {
|
|
43756
44119
|
case 'version': {
|
|
43757
44120
|
return target.version;
|
|
@@ -43838,6 +44201,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43838
44201
|
* Default OAuth scopes used when the caller provides no scopes.
|
|
43839
44202
|
*
|
|
43840
44203
|
* Resolves to the app's Entra ID configured permissions via the `/.default` scope.
|
|
44204
|
+
*
|
|
44205
|
+
* @returns The default OAuth scopes derived from the configured client ID.
|
|
43841
44206
|
*/
|
|
43842
44207
|
get defaultScopes() {
|
|
43843
44208
|
const clientId = this.#client.clientId;
|
|
@@ -43860,6 +44225,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43860
44225
|
*
|
|
43861
44226
|
* Provides access to the underlying MSAL PublicClientApplication for advanced use cases.
|
|
43862
44227
|
* Prefer using provider methods for standard authentication operations.
|
|
44228
|
+
*
|
|
44229
|
+
* @returns The underlying MSAL client instance.
|
|
43863
44230
|
*/
|
|
43864
44231
|
get client() {
|
|
43865
44232
|
return this.#client;
|
|
@@ -43869,6 +44236,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43869
44236
|
*
|
|
43870
44237
|
* Returns the active account if a user is authenticated, or null if no user is logged in.
|
|
43871
44238
|
* This is a shorthand for `client.getActiveAccount()`.
|
|
44239
|
+
*
|
|
44240
|
+
* @returns The currently authenticated account, or `null` if no user is logged in.
|
|
43872
44241
|
*/
|
|
43873
44242
|
get account() {
|
|
43874
44243
|
return this.#client.getActiveAccount();
|
|
@@ -43932,6 +44301,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43932
44301
|
*
|
|
43933
44302
|
* The provider will attempt automatic login with empty scopes if requiresAuth is true.
|
|
43934
44303
|
* Apps should call acquireToken with actual scopes after initialization completes.
|
|
44304
|
+
*
|
|
44305
|
+
* @throws {Error} If auth code exchange requires a client ID but none is configured.
|
|
43935
44306
|
*/
|
|
43936
44307
|
async initialize() {
|
|
43937
44308
|
// Guard: skip authentication when running inside MSAL's hidden iframe.
|
|
@@ -43958,6 +44329,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43958
44329
|
// Use MSAL's acquireTokenByCode to exchange backend auth code for tokens
|
|
43959
44330
|
// This follows Microsoft's standard SPA Auth Code Flow pattern
|
|
43960
44331
|
const clientId = this.#client.clientId;
|
|
44332
|
+
// A client ID is required to build the default scope used for the auth code exchange
|
|
43961
44333
|
if (!clientId) {
|
|
43962
44334
|
throw new Error('Client ID is required for auth code exchange');
|
|
43963
44335
|
}
|
|
@@ -44001,6 +44373,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44001
44373
|
// Priority 1: Check if returning from redirect-based authentication
|
|
44002
44374
|
// This handles cases where user just completed a login/acquireToken via redirect
|
|
44003
44375
|
const handleRedirectResult = await this.handleRedirect();
|
|
44376
|
+
// A returned account means the user just completed a redirect-based login
|
|
44004
44377
|
if (handleRedirectResult?.account) {
|
|
44005
44378
|
// Successfully authenticated via redirect - set as active account
|
|
44006
44379
|
// This means the user was redirected to Microsoft and came back authenticated
|
|
@@ -44017,6 +44390,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44017
44390
|
// Note: Using default scopes here as we don't know what scopes the app needs yet
|
|
44018
44391
|
// App should call acquireToken with actual scopes after initialization
|
|
44019
44392
|
const loginResult = await this.login({ request: { scopes: this.defaultScopes } });
|
|
44393
|
+
// Only set the active account when the automatic login actually returned one
|
|
44020
44394
|
if (loginResult?.account) {
|
|
44021
44395
|
// Automatic login successful - set as active account
|
|
44022
44396
|
this.#client.setActiveAccount(loginResult.account);
|
|
@@ -44065,6 +44439,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44065
44439
|
* @remark Empty scopes are currently tracked as telemetry exceptions but execution continues for monitoring purposes.
|
|
44066
44440
|
* This behavior will be changed to throw exceptions once sufficient metrics are collected.
|
|
44067
44441
|
*
|
|
44442
|
+
* @throws {Error} Re-throws any error encountered during token acquisition after tracking it via telemetry.
|
|
44443
|
+
*
|
|
44068
44444
|
* @example
|
|
44069
44445
|
* ```typescript
|
|
44070
44446
|
* // Modern API format
|
|
@@ -44107,6 +44483,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44107
44483
|
}
|
|
44108
44484
|
// Handle empty scopes - currently monitoring for telemetry, will throw in future
|
|
44109
44485
|
if (candidateScopes.length === 0) {
|
|
44486
|
+
// Fall back to the client-id-derived default scope when one is available
|
|
44110
44487
|
if (defaultScopes.length > 0) {
|
|
44111
44488
|
this._trackEvent('acquireToken.missing-scope.defaulted', TelemetryLevel.Warning, {
|
|
44112
44489
|
properties: { ...telemetryProperties, defaultScopes },
|
|
@@ -44118,7 +44495,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44118
44495
|
exception,
|
|
44119
44496
|
properties: telemetryProperties,
|
|
44120
44497
|
});
|
|
44121
|
-
// TODO: throw exception when sufficient metrics are collected
|
|
44498
|
+
// TODO(#5113): throw exception when sufficient metrics are collected
|
|
44122
44499
|
// This allows us to monitor how often empty scopes are provided before enforcing validation
|
|
44123
44500
|
}
|
|
44124
44501
|
}
|
|
@@ -44245,6 +44622,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44245
44622
|
// Fall through to interactive authentication
|
|
44246
44623
|
}
|
|
44247
44624
|
}
|
|
44625
|
+
// Perform interactive authentication based on specified behavior
|
|
44248
44626
|
switch (behavior) {
|
|
44249
44627
|
case 'popup':
|
|
44250
44628
|
return await this.#client.loginPopup(request);
|
|
@@ -44326,6 +44704,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44326
44704
|
async handleRedirect() {
|
|
44327
44705
|
// Process any pending redirect from authentication flow
|
|
44328
44706
|
const result = await this.client.handleRedirectPromise();
|
|
44707
|
+
// Only track/log when a redirect result is actually returned
|
|
44329
44708
|
if (result) {
|
|
44330
44709
|
// Track successful redirect completion for monitoring
|
|
44331
44710
|
this._trackEvent('handleRedirect.success', TelemetryLevel.Information, {
|
|
@@ -44351,6 +44730,9 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44351
44730
|
* - Version compatibility is tracked via telemetry
|
|
44352
44731
|
* - Throws error if unsupported version is requested
|
|
44353
44732
|
*
|
|
44733
|
+
* @template T - The provider interface type expected by the target version.
|
|
44734
|
+
* @throws {Error} If the requested version cannot be resolved or the proxy fails to create.
|
|
44735
|
+
*
|
|
44354
44736
|
* @example
|
|
44355
44737
|
* ```typescript
|
|
44356
44738
|
* // Create v2-compatible proxy
|
|
@@ -44480,6 +44862,7 @@ const module$2 = {
|
|
|
44480
44862
|
// Priority 2: Check if provider exists in parent module (proxy compatibility)
|
|
44481
44863
|
// This allows child applications to reuse parent's authentication provider
|
|
44482
44864
|
const hostProvider = init.ref?.auth;
|
|
44865
|
+
// Reuse the parent's provider (via a version-compatible proxy) when available
|
|
44483
44866
|
if (hostProvider) {
|
|
44484
44867
|
try {
|
|
44485
44868
|
const proxyProvider = hostProvider.createProxyProvider(config.version);
|
|
@@ -44488,7 +44871,7 @@ const module$2 = {
|
|
|
44488
44871
|
catch (error) {
|
|
44489
44872
|
console.error('MsalModule::Failed to create proxy provider', error);
|
|
44490
44873
|
// Fallback to host provider to prevent app breakage during migration
|
|
44491
|
-
// TODO: Consider throwing error instead once all apps are migrated to v4
|
|
44874
|
+
// TODO(#5114): Consider throwing error instead once all apps are migrated to v4
|
|
44492
44875
|
return hostProvider;
|
|
44493
44876
|
}
|
|
44494
44877
|
}
|
|
@@ -44523,7 +44906,7 @@ const module$2 = {
|
|
|
44523
44906
|
* ```
|
|
44524
44907
|
*/
|
|
44525
44908
|
const enableMSAL = (
|
|
44526
|
-
//
|
|
44909
|
+
// biome-ignore lint/suspicious/noExplicitAny: must be any to support all module types
|
|
44527
44910
|
configurator, configure) => {
|
|
44528
44911
|
const config = configure ? configureMsal(configure) : { module: module$2 };
|
|
44529
44912
|
configurator.addConfig(config);
|
|
@@ -44557,7 +44940,11 @@ const configureMsal = (configure) => ({
|
|
|
44557
44940
|
* @returns A function that takes an Observable stream of `QueryQueueItem` and returns an Observable
|
|
44558
44941
|
* stream where each item is processed in sequence by the provided callback.
|
|
44559
44942
|
*/
|
|
44560
|
-
const concatQueue = (...args) => (source$) =>
|
|
44943
|
+
const concatQueue = (...args) => (source$) => {
|
|
44944
|
+
// process queue items one after another, waiting for each to complete before starting the next
|
|
44945
|
+
return source$.pipe(concatMap(...args));
|
|
44946
|
+
};
|
|
44947
|
+
|
|
44561
44948
|
/**
|
|
44562
44949
|
* Takes a function that transforms each item in a queue and returns an Observable.
|
|
44563
44950
|
* It processes each item concurrently, potentially leading to out-of-order execution.
|
|
@@ -44568,7 +44955,10 @@ const concatQueue = (...args) => (source$) => source$.pipe(concatMap(...args));
|
|
|
44568
44955
|
* @returns A function that takes an Observable stream of `QueryQueueItem` and returns an Observable
|
|
44569
44956
|
* stream where each item is processed concurrently by the provided callback.
|
|
44570
44957
|
*/
|
|
44571
|
-
const mergeQueue = (...args) => (source$) =>
|
|
44958
|
+
const mergeQueue = (...args) => (source$) =>
|
|
44959
|
+
// process all queue items concurrently, without waiting for previous items to complete
|
|
44960
|
+
source$.pipe(mergeMap(...args));
|
|
44961
|
+
|
|
44572
44962
|
/**
|
|
44573
44963
|
* Takes a function that transforms each item in a queue and returns an Observable.
|
|
44574
44964
|
* It processes each item by cancelling the current task if a new one arrives.
|
|
@@ -44580,7 +44970,10 @@ const mergeQueue = (...args) => (source$) => source$.pipe(mergeMap(...args));
|
|
|
44580
44970
|
* stream where each item is processed by the provided callback, but only the latest
|
|
44581
44971
|
* item's result is emitted.
|
|
44582
44972
|
*/
|
|
44583
|
-
const switchQueue = (...args) => (source$) =>
|
|
44973
|
+
const switchQueue = (...args) => (source$) =>
|
|
44974
|
+
// cancel the previous in-flight item whenever a new item arrives
|
|
44975
|
+
source$.pipe(switchMap(...args));
|
|
44976
|
+
|
|
44584
44977
|
/**
|
|
44585
44978
|
* Transforms a query result Observable into a plain value Observable by extracting the `value` property.
|
|
44586
44979
|
*
|
|
@@ -44603,7 +44996,9 @@ const switchQueue = (...args) => (source$) => source$.pipe(switchMap(...args));
|
|
|
44603
44996
|
* });
|
|
44604
44997
|
* ```
|
|
44605
44998
|
*/
|
|
44606
|
-
const queryValue = (source$) =>
|
|
44999
|
+
const queryValue = (source$) =>
|
|
45000
|
+
// strip query metadata, keeping only the raw data value
|
|
45001
|
+
source$.pipe(map$1((entry) => entry.value));
|
|
44607
45002
|
|
|
44608
45003
|
var REGEX = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
|
|
44609
45004
|
|
|
@@ -44812,6 +45207,12 @@ v5.URL = URL$1;
|
|
|
44812
45207
|
class QueryClientError extends Error {
|
|
44813
45208
|
type;
|
|
44814
45209
|
request;
|
|
45210
|
+
/**
|
|
45211
|
+
* Creates a new `QueryClientError`.
|
|
45212
|
+
*
|
|
45213
|
+
* @param type - Whether this error represents a standard failure or an aborted request.
|
|
45214
|
+
* @param args - The error message, optional underlying cause, and optional request context.
|
|
45215
|
+
*/
|
|
44815
45216
|
constructor(type, args) {
|
|
44816
45217
|
super(args.message, { cause: args.cause });
|
|
44817
45218
|
this.type = type;
|
|
@@ -44879,14 +45280,19 @@ const actions$1 = createActions$1();
|
|
|
44879
45280
|
* @param action$ - The stream of actions being dispatched in the system.
|
|
44880
45281
|
* @returns An Observable that emits execute actions for each request action.
|
|
44881
45282
|
*/
|
|
44882
|
-
const handleRequests = (action$) =>
|
|
45283
|
+
const handleRequests = (action$) =>
|
|
45284
|
+
// convert each incoming request action into an execute action for its transaction
|
|
45285
|
+
action$.pipe(filter(actions$1.request.match), map$1((action) => actions$1.execute(action.meta.transaction)));
|
|
45286
|
+
|
|
44883
45287
|
/**
|
|
44884
45288
|
* Handles the execution of a query.
|
|
44885
45289
|
*
|
|
44886
45290
|
* @param fetch - The function used to execute the query.
|
|
44887
45291
|
* @returns A flow that handles the execution of the query.
|
|
44888
45292
|
*/
|
|
44889
|
-
const handleExecution = (fetch) => (action$, state$) =>
|
|
45293
|
+
const handleExecution = (fetch) => (action$, state$) =>
|
|
45294
|
+
// execute the query for each request and translate the outcome back into actions
|
|
45295
|
+
action$.pipe(filter(actions$1.execute.match), withLatestFrom(state$), mergeMap(([action, state]) => {
|
|
44890
45296
|
const transaction = action.payload;
|
|
44891
45297
|
const request = state[transaction];
|
|
44892
45298
|
// Create an AbortController instance to manage cancellation.
|
|
@@ -44899,6 +45305,7 @@ const handleExecution = (fetch) => (action$, state$) => action$.pipe(filter(acti
|
|
|
44899
45305
|
}
|
|
44900
45306
|
}));
|
|
44901
45307
|
try {
|
|
45308
|
+
// map the fetch outcome to a success/failure action, cancelling early if aborted
|
|
44902
45309
|
return from(fetch(request.args, controller.signal)).pipe(map$1((value) => actions$1.execute.success({
|
|
44903
45310
|
...request,
|
|
44904
45311
|
status: 'complete',
|
|
@@ -44913,6 +45320,7 @@ const handleExecution = (fetch) => (action$, state$) => action$.pipe(filter(acti
|
|
|
44913
45320
|
return of(actions$1.execute.failure(err, transaction));
|
|
44914
45321
|
}
|
|
44915
45322
|
}));
|
|
45323
|
+
|
|
44916
45324
|
/**
|
|
44917
45325
|
* Handles execution failure by scheduling retries according to the provided retry options.
|
|
44918
45326
|
* If the maximum number of retries is exceeded or no retry options are provided, it emits an error action.
|
|
@@ -44922,6 +45330,7 @@ const handleExecution = (fetch) => (action$, state$) => action$.pipe(filter(acti
|
|
|
44922
45330
|
*/
|
|
44923
45331
|
const handleFailure = (config) => {
|
|
44924
45332
|
return (action$, state$) => {
|
|
45333
|
+
// filter for execution failures and decide whether to retry or report an error
|
|
44925
45334
|
return action$.pipe(
|
|
44926
45335
|
// Filter for actions that indicate an execution failure.
|
|
44927
45336
|
filter(actions$1.execute.failure.match),
|
|
@@ -44978,6 +45387,7 @@ const handleFailure = (config) => {
|
|
|
44978
45387
|
filter(([_, state]) => !!state[transaction]),
|
|
44979
45388
|
// If the request has been removed, throw an error.
|
|
44980
45389
|
tap(([, state]) => {
|
|
45390
|
+
// Re-check inside the tap since the request could have been removed during the delay
|
|
44981
45391
|
if (!state[transaction]) {
|
|
44982
45392
|
throw new QueryClientError('error', {
|
|
44983
45393
|
message: 'request not found, most likely removed while scheduled for retry',
|
|
@@ -45033,9 +45443,9 @@ const createReducer$1 = (initial = {}) => createReducer$2(initial, (builder) =>
|
|
|
45033
45443
|
builder.addCase(actions$1.execute, (state, action) => {
|
|
45034
45444
|
// Locates the query entry in the state using the transaction ID provided in `action.payload`.
|
|
45035
45445
|
const entry = state[action.payload];
|
|
45446
|
+
// If the entry is found, logs the current timestamp to the `execution` array
|
|
45447
|
+
// to indicate when the execution action was triggered.
|
|
45036
45448
|
if (entry) {
|
|
45037
|
-
// If the entry is found, logs the current timestamp to the `execution` array
|
|
45038
|
-
// to indicate when the execution action was triggered.
|
|
45039
45449
|
entry.execution.push(Date.now());
|
|
45040
45450
|
// Updates the status of the entry to 'active' to reflect that the execution
|
|
45041
45451
|
// process has started.
|
|
@@ -45050,9 +45460,9 @@ const createReducer$1 = (initial = {}) => createReducer$2(initial, (builder) =>
|
|
|
45050
45460
|
builder.addCase(actions$1.execute.failure, (state, action) => {
|
|
45051
45461
|
// Locates the query entry in the state using the transaction ID provided in `action.meta`.
|
|
45052
45462
|
const entry = state[action.meta.transaction];
|
|
45463
|
+
// If the entry is found, the error information from `action.payload.error` is added to the `errors` array of the entry.
|
|
45464
|
+
// This allows tracking of all errors that have occurred during the execution of the query.
|
|
45053
45465
|
if (entry) {
|
|
45054
|
-
// If the entry is found, the error information from `action.payload.error` is added to the `errors` array of the entry.
|
|
45055
|
-
// This allows tracking of all errors that have occurred during the execution of the query.
|
|
45056
45466
|
entry.errors.push(action.payload.error);
|
|
45057
45467
|
// The status of the entry is updated to 'failed' to indicate that the query execution has encountered an error.
|
|
45058
45468
|
entry.status = 'failed';
|
|
@@ -45087,6 +45497,9 @@ class QueryClientJob extends Observable {
|
|
|
45087
45497
|
get status() {
|
|
45088
45498
|
return this.#status$.value;
|
|
45089
45499
|
}
|
|
45500
|
+
/**
|
|
45501
|
+
* An Observable stream of the job's status, emitting whenever the status changes.
|
|
45502
|
+
*/
|
|
45090
45503
|
get status$() {
|
|
45091
45504
|
return this.#status$.asObservable();
|
|
45092
45505
|
}
|
|
@@ -45206,8 +45619,10 @@ class QueryClientJob extends Observable {
|
|
|
45206
45619
|
* This method should be called to gracefully end a job that has finished its task.
|
|
45207
45620
|
* It will also attempt to cancel the job with a completion reason, transitioning its status to 'canceled'
|
|
45208
45621
|
* if it was in an 'active' state but hadn't yet completed.
|
|
45622
|
+
* @param reason - Optional reason recorded for the completion/cancellation.
|
|
45209
45623
|
*/
|
|
45210
45624
|
complete(reason) {
|
|
45625
|
+
// Only cancel/complete when the job isn't already in a terminal state
|
|
45211
45626
|
if (!this.closed) {
|
|
45212
45627
|
this.cancel(reason ?? `job: ${this.transaction} was completed`);
|
|
45213
45628
|
}
|
|
@@ -45222,6 +45637,9 @@ class QueryClientJob extends Observable {
|
|
|
45222
45637
|
const transaction = this.transaction;
|
|
45223
45638
|
this.#client.cancel(transaction, reason ?? `job: ${transaction} was canceled`);
|
|
45224
45639
|
}
|
|
45640
|
+
/**
|
|
45641
|
+
* Disposes of the job by completing it, releasing any held resources.
|
|
45642
|
+
*/
|
|
45225
45643
|
[Symbol.dispose]() {
|
|
45226
45644
|
this.complete();
|
|
45227
45645
|
}
|
|
@@ -45335,6 +45753,7 @@ class QueryClient extends Observable {
|
|
|
45335
45753
|
* @returns {Observable<TType>} An Observable stream of successful results.
|
|
45336
45754
|
*/
|
|
45337
45755
|
get success$() {
|
|
45756
|
+
// narrow the action stream down to successful execute actions and extract their payload
|
|
45338
45757
|
return this.action$.pipe(filter(actions$1.execute.success.match), map$1(({ payload }) => payload));
|
|
45339
45758
|
}
|
|
45340
45759
|
/**
|
|
@@ -45343,6 +45762,7 @@ class QueryClient extends Observable {
|
|
|
45343
45762
|
* @returns {Observable<QueryClientError>} An Observable stream of query errors.
|
|
45344
45763
|
*/
|
|
45345
45764
|
get error$() {
|
|
45765
|
+
// narrow the action stream down to client errors and wrap them as QueryClientError instances
|
|
45346
45766
|
return this.action$.pipe(filterAction('client/error'), withLatestFrom(this.#state), map$1(([action, state]) => {
|
|
45347
45767
|
const { payload, meta: { transaction }, } = action;
|
|
45348
45768
|
const request = state[transaction];
|
|
@@ -45373,7 +45793,9 @@ class QueryClient extends Observable {
|
|
|
45373
45793
|
// Add flows to handle different aspects of the query lifecycle.
|
|
45374
45794
|
this.#subscription.add(this.#state.addFlow(handleRequests));
|
|
45375
45795
|
this.#subscription.add(this.#state.addFlow(handleExecution(queryFn)));
|
|
45376
|
-
|
|
45796
|
+
// merge caller-provided retry options over the defaults
|
|
45797
|
+
const retryOptions = Object.assign({ count: 0, delay: 0 }, options?.retry);
|
|
45798
|
+
this.#subscription.add(this.#state.addFlow(handleFailure(retryOptions)));
|
|
45377
45799
|
/**
|
|
45378
45800
|
* Register effects to emit lifecycle events for different query actions.
|
|
45379
45801
|
*
|
|
@@ -45426,6 +45848,7 @@ class QueryClient extends Observable {
|
|
|
45426
45848
|
*/
|
|
45427
45849
|
query(args, opt) {
|
|
45428
45850
|
const job = new QueryClientJob(this, args, opt);
|
|
45851
|
+
// share a single underlying job execution across all subscribers, replaying past emissions to late subscribers
|
|
45429
45852
|
const job$ = job.pipe(share({ connector: () => new ReplaySubject(), resetOnRefCountZero: false }));
|
|
45430
45853
|
Object.defineProperties(job$, {
|
|
45431
45854
|
transaction: { get: () => job.transaction },
|
|
@@ -45435,6 +45858,8 @@ class QueryClient extends Observable {
|
|
|
45435
45858
|
cancel: { value: (reason) => job.cancel(reason) },
|
|
45436
45859
|
complete: { value: () => job.complete() },
|
|
45437
45860
|
});
|
|
45861
|
+
// `job$` is a plain shared Observable; the properties defined above give it the
|
|
45862
|
+
// `QueryClientJob` shape at runtime, which TypeScript can't infer structurally.
|
|
45438
45863
|
return job$;
|
|
45439
45864
|
}
|
|
45440
45865
|
/**
|
|
@@ -45463,7 +45888,10 @@ class QueryClient extends Observable {
|
|
|
45463
45888
|
* @returns The request matching the reference, or `undefined` if not found.
|
|
45464
45889
|
*/
|
|
45465
45890
|
getRequestByRef(ref) {
|
|
45466
|
-
|
|
45891
|
+
const requests = Object.values(this.#state.value);
|
|
45892
|
+
// scan all tracked requests for the one matching the given ref
|
|
45893
|
+
const match = requests.find((x) => x.ref === ref);
|
|
45894
|
+
return match;
|
|
45467
45895
|
}
|
|
45468
45896
|
/**
|
|
45469
45897
|
* Cancels a task by its reference string. If a reason is provided, it will be included
|
|
@@ -45473,6 +45901,7 @@ class QueryClient extends Observable {
|
|
|
45473
45901
|
*/
|
|
45474
45902
|
cancelTaskByRef(ref, reason) {
|
|
45475
45903
|
const entry = this.getRequestByRef(ref);
|
|
45904
|
+
// Only cancel when a matching request was actually found
|
|
45476
45905
|
if (entry?.ref) {
|
|
45477
45906
|
this.cancel(entry.transaction, reason);
|
|
45478
45907
|
}
|
|
@@ -45484,6 +45913,7 @@ class QueryClient extends Observable {
|
|
|
45484
45913
|
* @param {string} [reason] The reason for cancellation.
|
|
45485
45914
|
*/
|
|
45486
45915
|
cancel(transaction, reason) {
|
|
45916
|
+
// No transaction targeted — cancel every tracked transaction
|
|
45487
45917
|
if (!transaction) {
|
|
45488
45918
|
// If no specific transaction is provided, iterate through all transactions
|
|
45489
45919
|
// in the state and cancel each one, applying a generic cancellation reason.
|
|
@@ -45528,7 +45958,7 @@ class QueryClient extends Observable {
|
|
|
45528
45958
|
* @template TArgs The type of the arguments associated with the cache entry.
|
|
45529
45959
|
* @returns An object containing the cache actions.
|
|
45530
45960
|
*/
|
|
45531
|
-
//
|
|
45961
|
+
// biome-ignore lint/suspicious/noExplicitAny: default must be bivariant `any`; `unknown` breaks assignability of concrete `Actions<TType, TArgs>` streams (e.g. in `Observable<Actions<TType, TArgs>>` subscriptions) to the default-typed generic
|
|
45532
45962
|
function createActions() {
|
|
45533
45963
|
return {
|
|
45534
45964
|
/**
|
|
@@ -45620,6 +46050,7 @@ function createReducer (actions, initial = {}) {
|
|
|
45620
46050
|
.addCase(actions.insert, (state, action) => {
|
|
45621
46051
|
const { key, entry } = action.payload;
|
|
45622
46052
|
const record = state[key];
|
|
46053
|
+
// Update the existing entry in place, or create a new one if none exists yet
|
|
45623
46054
|
if (record) {
|
|
45624
46055
|
// If the record exists, update the timestamp and increment the update count.
|
|
45625
46056
|
record.updated = Date.now();
|
|
@@ -45647,8 +46078,10 @@ function createReducer (actions, initial = {}) {
|
|
|
45647
46078
|
// Handles the 'invalidate' action to invalidate a cache record.
|
|
45648
46079
|
.addCase(actions.invalidate, (state, action) => {
|
|
45649
46080
|
const invalidKey = action.payload ? [action.payload] : Object.keys(state);
|
|
46081
|
+
// Invalidate either the single targeted key, or every key when none was specified
|
|
45650
46082
|
for (const key of invalidKey) {
|
|
45651
46083
|
const entry = state[key];
|
|
46084
|
+
// Only reset the timestamp when the record actually exists
|
|
45652
46085
|
if (entry) {
|
|
45653
46086
|
// reset the updated timestamp
|
|
45654
46087
|
entry.updated = undefined;
|
|
@@ -45659,6 +46092,7 @@ function createReducer (actions, initial = {}) {
|
|
|
45659
46092
|
.addCase(actions.mutate, (state, action) => {
|
|
45660
46093
|
const { key, value, updated } = action.payload;
|
|
45661
46094
|
const record = state[key];
|
|
46095
|
+
// Only apply the mutation when a record exists for this key
|
|
45662
46096
|
if (record) {
|
|
45663
46097
|
// Update the record with the new value and metadata.
|
|
45664
46098
|
record.value = value;
|
|
@@ -45684,8 +46118,10 @@ function createReducer (actions, initial = {}) {
|
|
|
45684
46118
|
.map(([key]) => key);
|
|
45685
46119
|
// Remove keys that are not in the list of valid keys.
|
|
45686
46120
|
if (currentKeys.length !== validKeys.length) {
|
|
46121
|
+
// Walk all current keys and drop the ones that were trimmed away
|
|
45687
46122
|
for (const key of currentKeys) {
|
|
45688
46123
|
const validKeyIndex = validKeys.indexOf(key);
|
|
46124
|
+
// The key survived trimming — remove it from the search list so later lookups are faster
|
|
45689
46125
|
if (validKeyIndex !== -1) {
|
|
45690
46126
|
// If the key is valid, remove it from the search list.
|
|
45691
46127
|
validKeys.splice(validKeyIndex, 1);
|
|
@@ -45797,6 +46233,7 @@ class QueryCache {
|
|
|
45797
46233
|
constructor(args) {
|
|
45798
46234
|
const { trimming, initial } = args ?? {};
|
|
45799
46235
|
this.#state = new FlowSubject(createReducer(actions, initial));
|
|
46236
|
+
// Only wire up automatic trimming when a trimming policy was provided
|
|
45800
46237
|
if (trimming) {
|
|
45801
46238
|
this.#state.addEffect('cache/set', () => this.#state.next(actions.trim(trimming)));
|
|
45802
46239
|
}
|
|
@@ -45844,6 +46281,7 @@ class QueryCache {
|
|
|
45844
46281
|
invalidate(key) {
|
|
45845
46282
|
const item = key ? this.#state.value[key] : undefined;
|
|
45846
46283
|
this.#state.next(actions.invalidate(key, item));
|
|
46284
|
+
// Only emit an event when a specific key was invalidated (not a full-cache clear)
|
|
45847
46285
|
if (key) {
|
|
45848
46286
|
this._registerEvent('query_cache_entry_invalidated', {
|
|
45849
46287
|
key,
|
|
@@ -45855,9 +46293,12 @@ class QueryCache {
|
|
|
45855
46293
|
* Mutates an item in the query cache by key.
|
|
45856
46294
|
* @param {string} key - The key of the item to mutate.
|
|
45857
46295
|
* @param {QueryCacheMutation | ((current: TType) => QueryCacheMutation)} changes - The changes to apply to the item.
|
|
46296
|
+
* @returns A function that reverts the mutation, restoring the previous value.
|
|
46297
|
+
* @throws {Error} When no cache entry exists for `key`.
|
|
45858
46298
|
*/
|
|
45859
46299
|
mutate(key, changes) {
|
|
45860
46300
|
const current = key in this.#state.value ? this.#state.value[key] : undefined;
|
|
46301
|
+
// A missing entry cannot be mutated — mutation only ever updates existing entries
|
|
45861
46302
|
if (!current) {
|
|
45862
46303
|
throw new Error(`Cannot mutate cache item with key ${key}: item not found`);
|
|
45863
46304
|
}
|
|
@@ -45881,6 +46322,7 @@ class QueryCache {
|
|
|
45881
46322
|
const beforeKeys = new Set(Object.keys(this.#state.value));
|
|
45882
46323
|
this.#state.next(actions.trim(options));
|
|
45883
46324
|
const afterKeys = new Set(Object.keys(this.#state.value));
|
|
46325
|
+
// keys that existed before trimming but not after were the ones removed
|
|
45884
46326
|
const removedKeys = Array.from(beforeKeys).filter((key) => !afterKeys.has(key));
|
|
45885
46327
|
this._registerEvent('query_cache_trimmed', {
|
|
45886
46328
|
data: {
|
|
@@ -45950,6 +46392,7 @@ class QueryTask extends Subject {
|
|
|
45950
46392
|
#created = Date.now();
|
|
45951
46393
|
/**
|
|
45952
46394
|
* Gets the creation timestamp of the query task.
|
|
46395
|
+
* @returns The Unix timestamp (in milliseconds) when the task was created.
|
|
45953
46396
|
*/
|
|
45954
46397
|
get created() {
|
|
45955
46398
|
return this.#created;
|
|
@@ -45957,6 +46400,7 @@ class QueryTask extends Subject {
|
|
|
45957
46400
|
#uuid = v4();
|
|
45958
46401
|
/**
|
|
45959
46402
|
* Gets the unique identifier of the query task.
|
|
46403
|
+
* @returns The task's generated UUID.
|
|
45960
46404
|
*/
|
|
45961
46405
|
get uuid() {
|
|
45962
46406
|
return this.#uuid;
|
|
@@ -45982,6 +46426,7 @@ class QueryTask extends Subject {
|
|
|
45982
46426
|
* @returns A subscription to the job's result.
|
|
45983
46427
|
*/
|
|
45984
46428
|
processJob(job) {
|
|
46429
|
+
// map the job's completion into a QueryTaskCompleted record for subscribers
|
|
45985
46430
|
return job
|
|
45986
46431
|
.pipe(map$1((result) => {
|
|
45987
46432
|
const { key, uuid, created } = this;
|
|
@@ -46014,6 +46459,13 @@ class QueryEvent {
|
|
|
46014
46459
|
type;
|
|
46015
46460
|
key;
|
|
46016
46461
|
data;
|
|
46462
|
+
/**
|
|
46463
|
+
* Creates a new Query event.
|
|
46464
|
+
*
|
|
46465
|
+
* @param type - The specific event type identifier.
|
|
46466
|
+
* @param key - The cache key associated with this event.
|
|
46467
|
+
* @param data - Optional event-specific data payload.
|
|
46468
|
+
*/
|
|
46017
46469
|
constructor(type, key, data) {
|
|
46018
46470
|
this.type = type;
|
|
46019
46471
|
this.key = key;
|
|
@@ -46051,10 +46503,12 @@ const defaultCacheValidator = (expires = 0) => (entry) => (entry.updated ?? 0) +
|
|
|
46051
46503
|
* @returns A function that takes a query request and returns an Observable representing the queued request.
|
|
46052
46504
|
*/
|
|
46053
46505
|
const getQueueOperator = (type = 'switch') => {
|
|
46506
|
+
// A custom queue function was provided, use it directly
|
|
46054
46507
|
if (typeof type === 'function') {
|
|
46055
46508
|
return type;
|
|
46056
46509
|
}
|
|
46057
46510
|
return (() => {
|
|
46511
|
+
// Map the named operator type to its concrete implementation
|
|
46058
46512
|
switch (type) {
|
|
46059
46513
|
case 'concat':
|
|
46060
46514
|
return concatQueue;
|
|
@@ -46125,7 +46579,6 @@ const getQueueOperator = (type = 'switch') => {
|
|
|
46125
46579
|
* @see {@link Query.invalidate} for more details on invalidating cache entries.
|
|
46126
46580
|
* @see {@link QueueOperatorType} for more details on the available queue operators.
|
|
46127
46581
|
*/
|
|
46128
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46129
46582
|
class Query {
|
|
46130
46583
|
/**
|
|
46131
46584
|
* Static utility that extracts the raw value from a query result Observable.
|
|
@@ -46185,16 +46638,19 @@ class Query {
|
|
|
46185
46638
|
#event$;
|
|
46186
46639
|
/**
|
|
46187
46640
|
* A public getter for the client instance.
|
|
46188
|
-
* TODO: Implement a proxy to control access to the client.
|
|
46641
|
+
* TODO(#5144): Implement a proxy to control access to the client.
|
|
46189
46642
|
* This proxy would allow for additional functionality or restrictions when accessing the client instance.
|
|
46643
|
+
* @returns The internal `QueryClient` instance used to fetch data.
|
|
46190
46644
|
*/
|
|
46191
46645
|
get client() {
|
|
46192
|
-
// TODO: Proxy
|
|
46646
|
+
// TODO(#5144): Proxy
|
|
46193
46647
|
return this.#client;
|
|
46194
46648
|
}
|
|
46195
46649
|
/**
|
|
46196
46650
|
* Protected helper method to register events if an event observer is configured.
|
|
46651
|
+
* @template TKey - The event type key, constrained to the keys of `QueryEvents`.
|
|
46197
46652
|
* @param type - The event type
|
|
46653
|
+
* @param key - The cache/query key the event relates to
|
|
46198
46654
|
* @param data - The event data
|
|
46199
46655
|
*/
|
|
46200
46656
|
_registerEvent(type, key, data) {
|
|
@@ -46202,11 +46658,12 @@ class Query {
|
|
|
46202
46658
|
}
|
|
46203
46659
|
/**
|
|
46204
46660
|
* A public getter for the cache instance.
|
|
46205
|
-
* TODO: Implement a proxy to control access to the cache.
|
|
46661
|
+
* TODO(#5144): Implement a proxy to control access to the cache.
|
|
46206
46662
|
* This proxy would allow for additional functionality or restrictions when accessing the cache instance.
|
|
46663
|
+
* @returns The internal `QueryCache` instance used to cache query results.
|
|
46207
46664
|
*/
|
|
46208
46665
|
get cache() {
|
|
46209
|
-
// TODO: Proxy
|
|
46666
|
+
// TODO(#5144): Proxy
|
|
46210
46667
|
return this.#cache;
|
|
46211
46668
|
}
|
|
46212
46669
|
/**
|
|
@@ -46271,6 +46728,7 @@ class Query {
|
|
|
46271
46728
|
// shutdown the queue when the query is completed
|
|
46272
46729
|
this.#subscription.add(() => this.#queue$.complete());
|
|
46273
46730
|
this.#subscription.add(this.#queue$
|
|
46731
|
+
// register queued events, then apply the queue operator to the ongoing tasks
|
|
46274
46732
|
.pipe(tap((key) => {
|
|
46275
46733
|
this._registerEvent('query_queued', key);
|
|
46276
46734
|
}),
|
|
@@ -46400,9 +46858,11 @@ class Query {
|
|
|
46400
46858
|
const { skipResolve, ...args } = opt || {};
|
|
46401
46859
|
const fn = skipResolve ? firstValueFrom : lastValueFrom;
|
|
46402
46860
|
return new Promise((resolve, reject) => {
|
|
46861
|
+
// Reject the promise early if the caller aborts via the signal
|
|
46403
46862
|
if (opt?.signal) {
|
|
46404
46863
|
opt.signal.addEventListener('abort', () => reject(new Error('Query aborted')));
|
|
46405
46864
|
}
|
|
46865
|
+
// Throw if the query completes without emitting a value
|
|
46406
46866
|
fn(this._query(payload, args).pipe(throwIfEmpty())).then(resolve, reject);
|
|
46407
46867
|
});
|
|
46408
46868
|
}
|
|
@@ -46424,7 +46884,7 @@ class Query {
|
|
|
46424
46884
|
persistentQuery(args, options) {
|
|
46425
46885
|
const key = this.#generateCacheKey(args);
|
|
46426
46886
|
const original = this._query(args, options);
|
|
46427
|
-
|
|
46887
|
+
const cacheAwareStream = new Observable((subscriber) => {
|
|
46428
46888
|
subscriber.add(original.subscribe({
|
|
46429
46889
|
next: subscriber.next.bind(subscriber),
|
|
46430
46890
|
error: subscriber.error.bind(subscriber),
|
|
@@ -46433,6 +46893,7 @@ class Query {
|
|
|
46433
46893
|
const validateCache = options?.cache?.validate || this.#validateCacheEntry;
|
|
46434
46894
|
// Subscribe to the cache state and filter for the specific cache entry based on the key.
|
|
46435
46895
|
subscriber.add(this.cache.state$
|
|
46896
|
+
// narrow the cache state stream down to just this key's entry
|
|
46436
46897
|
.pipe(filter((x) => key in x), map$1((x) => ({
|
|
46437
46898
|
...x[key],
|
|
46438
46899
|
key,
|
|
@@ -46440,9 +46901,9 @@ class Query {
|
|
|
46440
46901
|
hasValidCache: validateCache(x[key], args),
|
|
46441
46902
|
})))
|
|
46442
46903
|
.subscribe(subscriber));
|
|
46443
|
-
})
|
|
46904
|
+
});
|
|
46444
46905
|
// only emit when the transaction changes
|
|
46445
|
-
distinctUntilChanged((a, b) => a.transaction === b.transaction &&
|
|
46906
|
+
return cacheAwareStream.pipe(distinctUntilChanged((a, b) => a.transaction === b.transaction &&
|
|
46446
46907
|
a.mutated === b.mutated));
|
|
46447
46908
|
}
|
|
46448
46909
|
/**
|
|
@@ -46462,10 +46923,15 @@ class Query {
|
|
|
46462
46923
|
*
|
|
46463
46924
|
* @param args - The arguments that identify the specific cache entry to be mutated.
|
|
46464
46925
|
* @param changes - A function that defines the changes to be applied to the cache entry.
|
|
46926
|
+
* @param options - Optional settings; `allowCreation` controls whether a missing cache entry may be created.
|
|
46927
|
+
* @returns A function that reverts the mutation when called.
|
|
46928
|
+
* @throws {Error} When no cache entry exists for `args` and `options.allowCreation` is `undefined`.
|
|
46465
46929
|
*/
|
|
46466
46930
|
mutate(args, changes, options) {
|
|
46467
46931
|
const key = this.#generateCacheKey(args);
|
|
46932
|
+
// No existing cache entry for this key — decide whether to create one or fail
|
|
46468
46933
|
if (key in this.cache.state === false) {
|
|
46934
|
+
// No explicit allowCreation setting provided — fail closed rather than silently creating
|
|
46469
46935
|
if (options?.allowCreation === undefined) {
|
|
46470
46936
|
throw new Error(`Cannot mutate cache item with key ${key}: item not found and option "allowCreation" is false`);
|
|
46471
46937
|
}
|
|
@@ -46513,6 +46979,7 @@ class Query {
|
|
|
46513
46979
|
* @returns A function that, when called, will unsubscribe the callback from further invalidation events.
|
|
46514
46980
|
*/
|
|
46515
46981
|
onInvalidate(cb) {
|
|
46982
|
+
// only forward cache-invalidation actions to the callback
|
|
46516
46983
|
const subscription = this.#cache.action$
|
|
46517
46984
|
.pipe(filterAction('cache/invalidate'))
|
|
46518
46985
|
.subscribe((action) => cb({ detail: { item: action.meta.item } }));
|
|
@@ -46527,6 +46994,7 @@ class Query {
|
|
|
46527
46994
|
* @returns A function that, when called, will unsubscribe the callback from further mutation events.
|
|
46528
46995
|
*/
|
|
46529
46996
|
onMutate(cb) {
|
|
46997
|
+
// only forward cache-mutation actions to the callback
|
|
46530
46998
|
const subscription = this.#cache.action$
|
|
46531
46999
|
.pipe(filterAction('cache/mutate'))
|
|
46532
47000
|
.subscribe((action) => cb({ detail: { changes: action.payload, current: action.meta.item } }));
|
|
@@ -46584,7 +47052,9 @@ class Query {
|
|
|
46584
47052
|
_createTask(key, args, options) {
|
|
46585
47053
|
// Create a new Observable that represents the query task and will be returned to the caller.
|
|
46586
47054
|
return new Observable((subscriber) => {
|
|
47055
|
+
// Support cancellation via an AbortSignal
|
|
46587
47056
|
if (options?.signal) {
|
|
47057
|
+
// Already aborted before subscription — complete immediately without doing any work
|
|
46588
47058
|
if (options?.signal.aborted) {
|
|
46589
47059
|
this._registerEvent('query_aborted', key);
|
|
46590
47060
|
return subscriber.complete();
|
|
@@ -46616,6 +47086,7 @@ class Query {
|
|
|
46616
47086
|
// the status indicating that this is a cached response, and a flag indicating whether the cache
|
|
46617
47087
|
// is considered valid based on the validation logic.
|
|
46618
47088
|
subscriber.next(record);
|
|
47089
|
+
// Complete without fetching when the cache is valid, or invalid entries are intentionally suppressed
|
|
46619
47090
|
if (hasValidCache || suppressInvalid) {
|
|
46620
47091
|
this._registerEvent('query_completed', key, { data: cacheEntry.value, hasValidCache });
|
|
46621
47092
|
// If the cache is valid, or if invalid cache entries should be suppressed (not re-fetched),
|
|
@@ -46629,6 +47100,7 @@ class Query {
|
|
|
46629
47100
|
}
|
|
46630
47101
|
// If the cache entry does not exist or is invalid, proceed to queue a new query request.
|
|
46631
47102
|
const isExistingTask = key in this.#tasks;
|
|
47103
|
+
// No task is already tracking this key — create one and register it before queuing
|
|
46632
47104
|
if (!isExistingTask) {
|
|
46633
47105
|
this.#tasks[key] = new QueryTask(key, args, options);
|
|
46634
47106
|
this._registerEvent('query_job_created', key, {
|
|
@@ -46707,7 +47179,9 @@ const serviceResponseSelector = (response, postProcess) => {
|
|
|
46707
47179
|
const result$ = from(jsonSelector(response)).pipe(
|
|
46708
47180
|
// parse and validate the response
|
|
46709
47181
|
map$1((value) => ApiServices.default([]).parse(value)));
|
|
47182
|
+
// Only apply a transform pipeline when the caller supplied one
|
|
46710
47183
|
if (postProcess) {
|
|
47184
|
+
// Apply the caller-supplied operator (e.g. session overrides) before emitting
|
|
46711
47185
|
return result$.pipe(postProcess);
|
|
46712
47186
|
}
|
|
46713
47187
|
return result$;
|
|
@@ -46758,7 +47232,9 @@ class ServiceDiscoveryClient {
|
|
|
46758
47232
|
/** {@inheritDoc IServiceDiscoveryClient.resolveService} */
|
|
46759
47233
|
async resolveService(key, allow_cache) {
|
|
46760
47234
|
const services = await this.resolveServices(allow_cache);
|
|
47235
|
+
// Locate the service matching the requested key
|
|
46761
47236
|
const service = services.find((s) => s.key === key);
|
|
47237
|
+
// Fail loudly when the requested key doesn't resolve to a known service
|
|
46762
47238
|
if (!service) {
|
|
46763
47239
|
throw Error(`Failed to resolve service, invalid key [${key}]`);
|
|
46764
47240
|
}
|
|
@@ -46799,6 +47275,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46799
47275
|
* @throws {Error} When the HTTP module is not registered.
|
|
46800
47276
|
*/
|
|
46801
47277
|
async _createConfig(init, initial) {
|
|
47278
|
+
// The service discovery client depends on the http module being registered
|
|
46802
47279
|
if (!init.hasModule('http')) {
|
|
46803
47280
|
throw new Error('http module is required');
|
|
46804
47281
|
}
|
|
@@ -46806,6 +47283,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46806
47283
|
if (!this._has('discoveryClient')) {
|
|
46807
47284
|
// check if http module has a client with key 'service_discovery'
|
|
46808
47285
|
const httpProvider = await init.requireInstance('http');
|
|
47286
|
+
// Auto-configure only when a matching http client has actually been registered
|
|
46809
47287
|
if (httpProvider.hasClient('service_discovery')) {
|
|
46810
47288
|
this.configureServiceDiscoveryClientByClientKey('service_discovery');
|
|
46811
47289
|
}
|
|
@@ -46822,6 +47300,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46822
47300
|
* @throws {Error} When `discoveryClient` has not been configured.
|
|
46823
47301
|
*/
|
|
46824
47302
|
_processConfig(config, init) {
|
|
47303
|
+
// A discovery client is mandatory before the config can be considered complete
|
|
46825
47304
|
if (!config.discoveryClient) {
|
|
46826
47305
|
throw new Error('discoveryClient is required, please configure it');
|
|
46827
47306
|
}
|
|
@@ -46876,6 +47355,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46876
47355
|
configureServiceDiscoveryClient(configCallback) {
|
|
46877
47356
|
this.setServiceDiscoveryClient(async (args) => {
|
|
46878
47357
|
const { httpClient, endpoint } = (await lastValueFrom(from(configCallback(args)))) ?? {};
|
|
47358
|
+
// Only construct the client when the factory actually returned an http client
|
|
46879
47359
|
if (httpClient) {
|
|
46880
47360
|
return new ServiceDiscoveryClient({
|
|
46881
47361
|
http: httpClient,
|
|
@@ -46891,7 +47371,9 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46891
47371
|
// Check if there are any session overrides in session storage.
|
|
46892
47372
|
try {
|
|
46893
47373
|
const sessionOverrides = JSON.parse(storage.getItem('overriddenServiceDiscoveryUrls') || '{}');
|
|
47374
|
+
// Apply each session override onto the service matching its key
|
|
46894
47375
|
for (const [key, { url, scopes }] of Object.entries(sessionOverrides)) {
|
|
47376
|
+
// Locate the service this override applies to
|
|
46895
47377
|
const service = input.find((service) => service.key === key);
|
|
46896
47378
|
// If the service can be found, override the values with the values
|
|
46897
47379
|
// from session override.
|
|
@@ -46903,6 +47385,8 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46903
47385
|
}
|
|
46904
47386
|
}
|
|
46905
47387
|
catch (e) {
|
|
47388
|
+
// Malformed or missing session storage overrides are not fatal — fall back to
|
|
47389
|
+
// the unmodified input and just log for visibility.
|
|
46906
47390
|
console.error('Failed to JSON parse session overrides: "overriddenServiceDiscoveryUrls"', e);
|
|
46907
47391
|
}
|
|
46908
47392
|
return input;
|
|
@@ -46945,7 +47429,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46945
47429
|
}
|
|
46946
47430
|
|
|
46947
47431
|
// Generated by genversion.
|
|
46948
|
-
const version$1 = '10.0.
|
|
47432
|
+
const version$1 = '10.0.2';
|
|
46949
47433
|
|
|
46950
47434
|
/**
|
|
46951
47435
|
* Default implementation of {@link IServiceDiscoveryProvider}.
|
|
@@ -46980,6 +47464,7 @@ class ServiceDiscoveryProvider extends BaseModuleProvider {
|
|
|
46980
47464
|
/** {@inheritDoc IServiceDiscoveryProvider.createClient} */
|
|
46981
47465
|
async createClient(name, opt) {
|
|
46982
47466
|
const service = await this.resolveService(name);
|
|
47467
|
+
// A missing service configuration means the http client can't be constructed
|
|
46983
47468
|
if (!service) {
|
|
46984
47469
|
throw Error(`Could not load configuration of service [${name}]`);
|
|
46985
47470
|
}
|
|
@@ -47159,7 +47644,7 @@ class BaseTelemetryAdapter {
|
|
|
47159
47644
|
* without performing any initialization. It calls the protected `_initialize` method which
|
|
47160
47645
|
* subclasses can override to provide custom initialization logic.
|
|
47161
47646
|
*
|
|
47162
|
-
* TODO: Consider changing return type from Promise<void> to Promise<TelemetryItem[]>
|
|
47647
|
+
* TODO(#5112): Consider changing return type from Promise<void> to Promise<TelemetryItem[]>
|
|
47163
47648
|
* to allow adapters to return telemetry items (errors/warnings) that occurred during
|
|
47164
47649
|
* initialization, eliminating the chicken-and-egg problem with reporting init errors.
|
|
47165
47650
|
*
|
|
@@ -47226,6 +47711,7 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47226
47711
|
* @returns CSS background color string
|
|
47227
47712
|
*/
|
|
47228
47713
|
_generateTitleBackground(lvl) {
|
|
47714
|
+
// Pick a background color matching the severity of this telemetry level
|
|
47229
47715
|
switch (lvl) {
|
|
47230
47716
|
case TelemetryLevel.Critical:
|
|
47231
47717
|
return 'rgb(255, 0, 0)';
|
|
@@ -47252,12 +47738,12 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47252
47738
|
* @returns Formatted time string (ms, s, or m s format)
|
|
47253
47739
|
*/
|
|
47254
47740
|
_formatMetric = (value) => {
|
|
47741
|
+
// Show milliseconds for values under 1 second
|
|
47255
47742
|
if (value < 1000) {
|
|
47256
|
-
// Show milliseconds for values under 1 second
|
|
47257
47743
|
return `${Math.round(value)}ms`;
|
|
47258
47744
|
}
|
|
47745
|
+
// Show seconds with 1 decimal for values under 1 minute
|
|
47259
47746
|
if (value < 60000) {
|
|
47260
|
-
// Show seconds with 1 decimal for values under 1 minute
|
|
47261
47747
|
return `${(value / 1000).toFixed(1)}s`;
|
|
47262
47748
|
}
|
|
47263
47749
|
// For longer durations, show minutes and seconds
|
|
@@ -47299,6 +47785,7 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47299
47785
|
metadata,
|
|
47300
47786
|
scope,
|
|
47301
47787
|
};
|
|
47788
|
+
// Dispatch to the right console format based on the telemetry item's type
|
|
47302
47789
|
switch (type) {
|
|
47303
47790
|
case TelemetryType.Event:
|
|
47304
47791
|
// Log events with base data
|
|
@@ -47325,6 +47812,7 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47325
47812
|
* @param msg - Additional message arguments to log
|
|
47326
47813
|
*/
|
|
47327
47814
|
_log(lvl, title, ...msg) {
|
|
47815
|
+
// Pick the console method matching this telemetry level's severity
|
|
47328
47816
|
switch (lvl) {
|
|
47329
47817
|
case TelemetryLevel.Critical:
|
|
47330
47818
|
case TelemetryLevel.Error:
|
|
@@ -47358,10 +47846,15 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47358
47846
|
*/
|
|
47359
47847
|
const createPortalEntryPoint = (...segments) => {
|
|
47360
47848
|
const normalized = segments
|
|
47849
|
+
// Drop undefined/null segments before further processing
|
|
47361
47850
|
.filter((segment) => segment !== undefined && segment !== null)
|
|
47851
|
+
// Trim stray whitespace from each segment
|
|
47362
47852
|
.map((segment) => segment.trim())
|
|
47853
|
+
// Drop any segments that became empty after trimming
|
|
47363
47854
|
.filter(Boolean)
|
|
47855
|
+
// Strip leading slashes from every segment (and trailing slashes from the first)
|
|
47364
47856
|
.map((segment, index) => {
|
|
47857
|
+
// The first segment may have a trailing slash (e.g. a base URL) that needs stripping
|
|
47365
47858
|
if (index === 0) {
|
|
47366
47859
|
return segment.replace(/\/+$/g, '');
|
|
47367
47860
|
}
|
|
@@ -47402,6 +47895,7 @@ const createPortalEntryPoint = (...segments) => {
|
|
|
47402
47895
|
*/
|
|
47403
47896
|
async function registerServiceWorker(framework) {
|
|
47404
47897
|
const telemetry = framework.telemetry;
|
|
47898
|
+
// Bail out early when the browser has no service worker support at all
|
|
47405
47899
|
if ('serviceWorker' in navigator === false) {
|
|
47406
47900
|
const exception = new Error('Service workers are not supported in this browser.');
|
|
47407
47901
|
exception.name = 'ServiceWorkerNotSupported';
|
|
@@ -47412,6 +47906,7 @@ async function registerServiceWorker(framework) {
|
|
|
47412
47906
|
throw exception;
|
|
47413
47907
|
}
|
|
47414
47908
|
const resourceConfigs = import.meta.env.FUSION_SPA_SERVICE_WORKER_RESOURCES;
|
|
47909
|
+
// The worker needs a resource config to know which requests to intercept
|
|
47415
47910
|
if (!resourceConfigs) {
|
|
47416
47911
|
const exception = new Error('Service worker config is not defined.');
|
|
47417
47912
|
exception.name = 'ServiceWorkerConfigNotDefined';
|
|
@@ -47435,10 +47930,12 @@ async function registerServiceWorker(framework) {
|
|
|
47435
47930
|
navigator.serviceWorker.startMessages();
|
|
47436
47931
|
// listen for messages from the service worker (set up before registration)
|
|
47437
47932
|
navigator.serviceWorker.addEventListener('message', async (event) => {
|
|
47933
|
+
// Only the GET_TOKEN message type requests a token from this handler
|
|
47438
47934
|
if (event.data.type === 'GET_TOKEN') {
|
|
47439
47935
|
try {
|
|
47440
47936
|
// extract scopes from the event data
|
|
47441
47937
|
const scopes = event.data.scopes;
|
|
47938
|
+
// Scopes must be a real array before they can be used to request a token
|
|
47442
47939
|
if (!scopes || !Array.isArray(scopes)) {
|
|
47443
47940
|
const error = new Error('Invalid scopes provided');
|
|
47444
47941
|
error.name = 'InvalidScopesProvided';
|
|
@@ -47446,6 +47943,7 @@ async function registerServiceWorker(framework) {
|
|
|
47446
47943
|
}
|
|
47447
47944
|
// request a token from the MSAL module
|
|
47448
47945
|
const token = await framework.auth.acquireToken({ request: { scopes } });
|
|
47946
|
+
// A missing token means acquisition failed and the worker can't proceed
|
|
47449
47947
|
if (!token) {
|
|
47450
47948
|
const error = new Error('Failed to acquire token');
|
|
47451
47949
|
error.name = 'FailedToAcquireToken';
|
|
@@ -47491,9 +47989,11 @@ async function registerServiceWorker(framework) {
|
|
|
47491
47989
|
if (registration.waiting) {
|
|
47492
47990
|
sendConfigToServiceWorker(registration.waiting);
|
|
47493
47991
|
}
|
|
47992
|
+
// A worker still installing needs to reach the 'activated' state before it can receive config
|
|
47494
47993
|
if (registration.installing) {
|
|
47495
47994
|
registration.installing.addEventListener('statechange', (event) => {
|
|
47496
47995
|
const worker = event.target;
|
|
47996
|
+
// Only send config once the worker has fully activated
|
|
47497
47997
|
if (worker.state === 'activated') {
|
|
47498
47998
|
sendConfigToServiceWorker(worker);
|
|
47499
47999
|
}
|
|
@@ -47501,6 +48001,7 @@ async function registerServiceWorker(framework) {
|
|
|
47501
48001
|
}
|
|
47502
48002
|
// Listen for controller changes (happens during hard refresh or updates)
|
|
47503
48003
|
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
48004
|
+
// Only send config once this page is actually controlled by a worker
|
|
47504
48005
|
if (navigator.serviceWorker.controller) {
|
|
47505
48006
|
sendConfigToServiceWorker(navigator.serviceWorker.controller);
|
|
47506
48007
|
}
|
|
@@ -47514,6 +48015,7 @@ async function registerServiceWorker(framework) {
|
|
|
47514
48015
|
});
|
|
47515
48016
|
// ensure we have an active service worker before sending config
|
|
47516
48017
|
const activeWorker = readyRegistration.active;
|
|
48018
|
+
// Without an active worker there's nothing to send config to
|
|
47517
48019
|
if (!activeWorker) {
|
|
47518
48020
|
console.error('[Service Worker Registration] Service worker is not active after ready state');
|
|
47519
48021
|
return;
|
|
@@ -47533,6 +48035,7 @@ async function registerServiceWorker(framework) {
|
|
|
47533
48035
|
navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
|
|
47534
48036
|
// Polling fallback and timeout to prevent infinite waiting
|
|
47535
48037
|
checkInterval = setInterval(() => {
|
|
48038
|
+
// Stop polling once a controller has taken over
|
|
47536
48039
|
if (navigator.serviceWorker.controller)
|
|
47537
48040
|
finish();
|
|
47538
48041
|
}, 200);
|
|
@@ -47560,7 +48063,7 @@ async function registerServiceWorker(framework) {
|
|
|
47560
48063
|
}
|
|
47561
48064
|
|
|
47562
48065
|
// Generated by genversion.
|
|
47563
|
-
const version = '4.0.
|
|
48066
|
+
const version = '4.0.14';
|
|
47564
48067
|
|
|
47565
48068
|
// Allow dynamic import without vite
|
|
47566
48069
|
const importWithoutVite = (path) => import(/* @vite-ignore */ path);
|
|
@@ -47606,6 +48109,7 @@ enableTelemetry(configurator, {
|
|
|
47606
48109
|
},
|
|
47607
48110
|
// biome-ignore lint/suspicious/noExplicitAny: we need to use any here to allow dynamic properties
|
|
47608
48111
|
};
|
|
48112
|
+
// Only attach user metadata when the auth module has resolved an account
|
|
47609
48113
|
if (modules?.auth) {
|
|
47610
48114
|
metadata.fusion.user = {
|
|
47611
48115
|
id: modules.auth.account?.homeAccountId,
|
|
@@ -47670,8 +48174,8 @@ enableTelemetry(configurator, {
|
|
|
47670
48174
|
document.body.innerHTML = '';
|
|
47671
48175
|
document.body.appendChild(el);
|
|
47672
48176
|
const portalEntryPoint = createPortalEntryPoint(portalProxy ? '/portal-proxy' : undefined, portal_manifest.build.assetPath, portal_manifest.build.templateEntry);
|
|
47673
|
-
//
|
|
47674
|
-
//
|
|
48177
|
+
// TODO(#5066): should test if the entrypoint is external or internal
|
|
48178
|
+
// TODO(#5066): add proper return type
|
|
47675
48179
|
const { render } = await measurement
|
|
47676
48180
|
.clone()
|
|
47677
48181
|
.resolve(importWithoutVite(portalEntryPoint), {
|