@equinor/fusion-framework-vite-plugin-spa 4.0.12 → 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 +35 -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 +724 -196
- 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}`);
|
|
@@ -23471,7 +23664,7 @@ var objectTraps = {
|
|
|
23471
23664
|
) && isArrayIndex(prop)) {
|
|
23472
23665
|
return value;
|
|
23473
23666
|
}
|
|
23474
|
-
if (value === peek(state.base_, prop)) {
|
|
23667
|
+
if (value === peek(state.base_, prop) || isRelocatedBaseRef(state, prop, value)) {
|
|
23475
23668
|
prepareCopy(state);
|
|
23476
23669
|
const childKey = state.type_ === 1 /* Array */ ? +prop : prop;
|
|
23477
23670
|
const childDraft = createProxy(state.scope_, value, state, childKey);
|
|
@@ -23505,7 +23698,7 @@ var objectTraps = {
|
|
|
23505
23698
|
markChanged(state);
|
|
23506
23699
|
}
|
|
23507
23700
|
if (state.copy_[prop] === value && // special case: handle new props with value 'undefined'
|
|
23508
|
-
(value !== void 0 || prop
|
|
23701
|
+
(value !== void 0 || has(state.copy_, prop, state.type_)) || // special case: NaN
|
|
23509
23702
|
Number.isNaN(value) && Number.isNaN(state.copy_[prop]))
|
|
23510
23703
|
return true;
|
|
23511
23704
|
state.copy_[prop] = value;
|
|
@@ -23574,6 +23767,12 @@ function peek(draft, prop) {
|
|
|
23574
23767
|
const source = state ? latest(state) : draft;
|
|
23575
23768
|
return source[prop];
|
|
23576
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
|
+
}
|
|
23577
23776
|
function readPropFromProto(state, source, prop) {
|
|
23578
23777
|
const desc = getDescriptorFromProto(source, prop);
|
|
23579
23778
|
return desc ? VALUE in desc ? desc[VALUE] : (
|
|
@@ -23899,18 +24098,21 @@ class FlowSubject extends Observable {
|
|
|
23899
24098
|
#state;
|
|
23900
24099
|
/**
|
|
23901
24100
|
* Observable stream of actions dispatched to the subject.
|
|
24101
|
+
* @returns An observable of dispatched actions.
|
|
23902
24102
|
*/
|
|
23903
24103
|
get action$() {
|
|
23904
24104
|
return this.#action.asObservable();
|
|
23905
24105
|
}
|
|
23906
24106
|
/**
|
|
23907
24107
|
* The current value of state.
|
|
24108
|
+
* @returns The current state value.
|
|
23908
24109
|
*/
|
|
23909
24110
|
get value() {
|
|
23910
24111
|
return this.#state.value;
|
|
23911
24112
|
}
|
|
23912
24113
|
/**
|
|
23913
24114
|
* Flag to indicate if the observable is closed.
|
|
24115
|
+
* @returns `true` if both the state and action subjects are closed.
|
|
23914
24116
|
*/
|
|
23915
24117
|
get closed() {
|
|
23916
24118
|
return this.#state.closed || this.#action.closed;
|
|
@@ -23927,6 +24129,7 @@ class FlowSubject extends Observable {
|
|
|
23927
24129
|
});
|
|
23928
24130
|
const initial = 'getInitialState' in reducer ? reducer.getInitialState() : initialState;
|
|
23929
24131
|
this.#state = new BehaviorSubject(initial);
|
|
24132
|
+
// Reduce dispatched actions into state, skipping emissions that produce an unchanged value
|
|
23930
24133
|
this.#action.pipe(scan(reducer, initial), distinctUntilChanged()).subscribe(this.#state);
|
|
23931
24134
|
this.reset = () => this.#state.next(initial);
|
|
23932
24135
|
}
|
|
@@ -23945,27 +24148,32 @@ class FlowSubject extends Observable {
|
|
|
23945
24148
|
/**
|
|
23946
24149
|
* Selects a derived state from the observable state and emits only when the selected state changes.
|
|
23947
24150
|
*
|
|
24151
|
+
* @template T - The derived state type returned by the selector.
|
|
23948
24152
|
* @param selector - A function that takes the current state and returns a derived state.
|
|
23949
24153
|
* @param comparator - An optional function that compares the previous and current derived states to determine if a change has occurred.
|
|
23950
24154
|
* @returns An observable that emits the derived state whenever it changes.
|
|
23951
24155
|
*/
|
|
23952
24156
|
select(selector, comparator) {
|
|
24157
|
+
// Derive and de-duplicate the selected value from the state stream
|
|
23953
24158
|
return this.#state.pipe(map$1(selector), distinctUntilChanged(comparator));
|
|
23954
24159
|
}
|
|
23955
24160
|
/**
|
|
23956
24161
|
* Adds an effect that listens for actions and performs side effects.
|
|
23957
24162
|
*
|
|
24163
|
+
* @template TType - The action type(s) to listen for.
|
|
23958
24164
|
* @param actionTypeOrFn The type of action to listen for, an array of action types, or the effect function itself.
|
|
23959
24165
|
* @param fn The effect function to execute when the action is dispatched, if the first parameter is an action type.
|
|
23960
24166
|
* @returns A subscription to the effect.
|
|
23961
24167
|
*/
|
|
23962
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
|
|
23963
24170
|
const action$ = typeof actionTypeOrFn === 'function'
|
|
23964
24171
|
? this.action$
|
|
23965
24172
|
: Array.isArray(actionTypeOrFn)
|
|
23966
24173
|
? this.action$.pipe(filterAction(...actionTypeOrFn))
|
|
23967
24174
|
: this.action$.pipe(filterAction(actionTypeOrFn));
|
|
23968
24175
|
const mapper = (fn ? fn : actionTypeOrFn);
|
|
24176
|
+
// Run the effect for each matching action, then filter and reschedule any resulting action
|
|
23969
24177
|
return action$
|
|
23970
24178
|
.pipe(mergeMap((action) => from(new Promise((resolve, reject) => {
|
|
23971
24179
|
try {
|
|
@@ -23974,7 +24182,9 @@ class FlowSubject extends Observable {
|
|
|
23974
24182
|
catch (err) {
|
|
23975
24183
|
reject(err);
|
|
23976
24184
|
}
|
|
23977
|
-
}))
|
|
24185
|
+
}))
|
|
24186
|
+
// Swallow effect errors so a single failing effect doesn't tear down the subscription
|
|
24187
|
+
.pipe(catchError((err) => {
|
|
23978
24188
|
console.warn('unhandled effect', err);
|
|
23979
24189
|
return EMPTY;
|
|
23980
24190
|
}))), filter((x) => !!x), observeOn(asyncScheduler))
|
|
@@ -24016,9 +24226,11 @@ class FlowSubject extends Observable {
|
|
|
24016
24226
|
*/
|
|
24017
24227
|
addFlow(fn) {
|
|
24018
24228
|
const epic$ = fn(this.action$, this);
|
|
24229
|
+
// Fail fast if the flow function forgot to return an observable
|
|
24019
24230
|
if (!epic$) {
|
|
24020
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!`);
|
|
24021
24232
|
}
|
|
24233
|
+
// Log and swallow flow errors so a single failing flow doesn't tear down the subject
|
|
24022
24234
|
return epic$
|
|
24023
24235
|
.pipe(catchError((err) => {
|
|
24024
24236
|
console.trace('unhandled exception, epic closed!', err);
|
|
@@ -24054,18 +24266,38 @@ class FlowSubject extends Observable {
|
|
|
24054
24266
|
}
|
|
24055
24267
|
}
|
|
24056
24268
|
|
|
24057
|
-
|
|
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
|
|
24058
24280
|
/**
|
|
24059
24281
|
* taken from https://github.com/reduxjs/redux-toolkit/tree/master/packages/toolkit/src
|
|
24060
24282
|
*/
|
|
24061
|
-
|
|
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
|
+
*/
|
|
24062
24291
|
function createAction(type, prepareAction) {
|
|
24063
24292
|
function actionCreator(...args) {
|
|
24293
|
+
// Use the prepare callback when the caller needs custom payload construction.
|
|
24064
24294
|
if (prepareAction) {
|
|
24065
24295
|
const prepared = prepareAction(...args);
|
|
24296
|
+
// Reject invalid prepare callbacks before reading their action fields.
|
|
24066
24297
|
if (!prepared) {
|
|
24067
24298
|
throw new Error('prepareAction did not return an object');
|
|
24068
24299
|
}
|
|
24300
|
+
// Merge optional metadata and error fields into the prepared action.
|
|
24069
24301
|
return {
|
|
24070
24302
|
type,
|
|
24071
24303
|
payload: prepared.payload,
|
|
@@ -24080,35 +24312,50 @@ function createAction(type, prepareAction) {
|
|
|
24080
24312
|
actionCreator.match = (action) => action.type === type;
|
|
24081
24313
|
return actionCreator;
|
|
24082
24314
|
}
|
|
24083
|
-
const actionSuffixDivider = '::';
|
|
24084
24315
|
|
|
24085
|
-
|
|
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
|
+
*/
|
|
24086
24326
|
function createAsyncAction(type, request, success, failure) {
|
|
24087
24327
|
const action = createAction([type, 'request'].join(actionSuffixDivider), request);
|
|
24328
|
+
// Attach the success action only when the caller supplied its prepare function.
|
|
24088
24329
|
if (success) {
|
|
24330
|
+
// Add the success action while preserving the request creator's identity.
|
|
24089
24331
|
Object.assign(action, {
|
|
24090
24332
|
success: createAction([type, 'success'].join(actionSuffixDivider), success),
|
|
24091
24333
|
});
|
|
24092
24334
|
}
|
|
24335
|
+
// Attach the failure action only for async workflows that support failure results.
|
|
24093
24336
|
if (failure) {
|
|
24337
|
+
// Add the failure action alongside the existing request and success actions.
|
|
24094
24338
|
Object.assign(action, {
|
|
24095
24339
|
failure: createAction([type, 'failure'].join(actionSuffixDivider), failure),
|
|
24096
24340
|
});
|
|
24097
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.
|
|
24098
24344
|
return action;
|
|
24099
24345
|
}
|
|
24100
24346
|
|
|
24101
24347
|
function freezeDraftable(val) {
|
|
24102
|
-
// biome-ignore lint/suspicious/noEmptyBlockStatements: This is a valid use case for an empty block statement
|
|
24103
24348
|
return isDraftable(val) ? produce(val, () => { }) : val;
|
|
24104
24349
|
}
|
|
24105
24350
|
function isStateFunction(x) {
|
|
24106
24351
|
return typeof x === 'function';
|
|
24107
24352
|
}
|
|
24353
|
+
/** @inheritdoc */
|
|
24108
24354
|
function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
24109
24355
|
const [actionsMap, finalActionMatchers, finalDefaultCaseReducer] = executeReducerBuilderCallback(mapOrBuilderCallback);
|
|
24110
24356
|
// Ensure the initial state gets frozen either way (if draftable)
|
|
24111
24357
|
let getInitialState;
|
|
24358
|
+
// A lazy initializer must be invoked before its result can be frozen
|
|
24112
24359
|
if (isStateFunction(initialState)) {
|
|
24113
24360
|
getInitialState = () => freezeDraftable(initialState());
|
|
24114
24361
|
}
|
|
@@ -24119,19 +24366,31 @@ function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
|
24119
24366
|
function reducer(state, action) {
|
|
24120
24367
|
let caseReducers = [
|
|
24121
24368
|
actionsMap[action.type],
|
|
24122
|
-
...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),
|
|
24123
24374
|
];
|
|
24124
|
-
|
|
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) {
|
|
24125
24380
|
caseReducers = [finalDefaultCaseReducer];
|
|
24126
24381
|
}
|
|
24382
|
+
// Run each matched case reducer against the previous state, producing the next state
|
|
24127
24383
|
return caseReducers.reduce((previousState, caseReducer) => {
|
|
24384
|
+
// Only transform state when a case reducer was matched for this slot
|
|
24128
24385
|
if (caseReducer) {
|
|
24386
|
+
// Reuse an existing draft when already inside a `createNextState` call
|
|
24129
24387
|
if (isDraft(previousState)) {
|
|
24130
24388
|
// If it's already a draft, we must already be inside a `createNextState` call,
|
|
24131
24389
|
// likely because this is being wrapped in `createReducer`, `createSlice`, or nested
|
|
24132
24390
|
// inside an existing draft. It's safe to just pass the draft to the mutator.
|
|
24133
24391
|
const draft = previousState; // We can assume this is already a draft
|
|
24134
24392
|
const result = caseReducer(draft, action);
|
|
24393
|
+
// Treat an undefined result as "no change" rather than clearing the state
|
|
24135
24394
|
if (result === undefined) {
|
|
24136
24395
|
return previousState;
|
|
24137
24396
|
}
|
|
@@ -24141,7 +24400,9 @@ function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
|
24141
24400
|
// If state is not draftable (ex: a primitive, such as 0), we want to directly
|
|
24142
24401
|
// return the caseReducer func and not wrap it with produce.
|
|
24143
24402
|
const result = caseReducer(previousState, action);
|
|
24403
|
+
// Treat an undefined result as "no change", unless there's no previous state to fall back to
|
|
24144
24404
|
if (result === undefined) {
|
|
24405
|
+
// A `null` previous state has nothing to fall back to, so undefined is legitimately "no change"
|
|
24145
24406
|
if (previousState === null) {
|
|
24146
24407
|
return previousState;
|
|
24147
24408
|
}
|
|
@@ -24151,7 +24412,7 @@ function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
|
24151
24412
|
}
|
|
24152
24413
|
else {
|
|
24153
24414
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
24154
|
-
// @ts-
|
|
24415
|
+
// @ts-expect-error
|
|
24155
24416
|
// createNextState() produces an Immutable<Draft<S>> rather
|
|
24156
24417
|
// than an Immutable<S>, and TypeScript cannot find out how to reconcile
|
|
24157
24418
|
// these two types.
|
|
@@ -24166,12 +24427,22 @@ function createReducer$2(initialState, mapOrBuilderCallback) {
|
|
|
24166
24427
|
reducer.getInitialState = getInitialState;
|
|
24167
24428
|
return reducer;
|
|
24168
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
|
+
*/
|
|
24169
24439
|
function executeReducerBuilderCallback(builderCallback) {
|
|
24170
24440
|
const actionsMap = {};
|
|
24171
24441
|
const actionMatchers = [];
|
|
24172
24442
|
let defaultCaseReducer;
|
|
24173
24443
|
const builder = {
|
|
24174
24444
|
addCase(typeOrActionCreator, reducer) {
|
|
24445
|
+
// Skip these dev-only ordering checks in production builds to avoid the extra overhead
|
|
24175
24446
|
if (process.env.NODE_ENV !== 'production') {
|
|
24176
24447
|
/*
|
|
24177
24448
|
* to keep the definition by the user in line with actual behavior,
|
|
@@ -24181,11 +24452,13 @@ function executeReducerBuilderCallback(builderCallback) {
|
|
|
24181
24452
|
if (actionMatchers.length > 0) {
|
|
24182
24453
|
throw new Error('`builder.addCase` should only be called before calling `builder.addMatcher`');
|
|
24183
24454
|
}
|
|
24455
|
+
// A default case must always be registered last, after every specific case
|
|
24184
24456
|
if (defaultCaseReducer) {
|
|
24185
24457
|
throw new Error('`builder.addCase` should only be called before calling `builder.addDefaultCase`');
|
|
24186
24458
|
}
|
|
24187
24459
|
}
|
|
24188
24460
|
const type = typeof typeOrActionCreator === 'string' ? typeOrActionCreator : typeOrActionCreator.type;
|
|
24461
|
+
// Each action type may only be handled by a single case reducer
|
|
24189
24462
|
if (type in actionsMap) {
|
|
24190
24463
|
throw new Error('addCase cannot be called with two reducers for the same action type');
|
|
24191
24464
|
}
|
|
@@ -24193,7 +24466,9 @@ function executeReducerBuilderCallback(builderCallback) {
|
|
|
24193
24466
|
return builder;
|
|
24194
24467
|
},
|
|
24195
24468
|
addMatcher(matcher, reducer) {
|
|
24469
|
+
// Skip this dev-only ordering check in production builds to avoid the extra overhead
|
|
24196
24470
|
if (process.env.NODE_ENV !== 'production') {
|
|
24471
|
+
// A default case must always be registered last, after every matcher
|
|
24197
24472
|
if (defaultCaseReducer) {
|
|
24198
24473
|
throw new Error('`builder.addMatcher` should only be called before calling `builder.addDefaultCase`');
|
|
24199
24474
|
}
|
|
@@ -24202,7 +24477,9 @@ function executeReducerBuilderCallback(builderCallback) {
|
|
|
24202
24477
|
return builder;
|
|
24203
24478
|
},
|
|
24204
24479
|
addDefaultCase(reducer) {
|
|
24480
|
+
// Skip this dev-only uniqueness check in production builds to avoid the extra overhead
|
|
24205
24481
|
if (process.env.NODE_ENV !== 'production') {
|
|
24482
|
+
// Only one default case reducer may be registered per builder
|
|
24206
24483
|
if (defaultCaseReducer) {
|
|
24207
24484
|
throw new Error('`builder.addDefaultCase` can only be called once');
|
|
24208
24485
|
}
|
|
@@ -24219,8 +24496,10 @@ function executeReducerBuilderCallback(builderCallback) {
|
|
|
24219
24496
|
];
|
|
24220
24497
|
}
|
|
24221
24498
|
|
|
24499
|
+
/** @inheritdoc */
|
|
24500
|
+
function isObservableInput(
|
|
24222
24501
|
// biome-ignore lint/suspicious/noExplicitAny: Should allow any input type
|
|
24223
|
-
|
|
24502
|
+
input) {
|
|
24224
24503
|
// Check for null or undefined
|
|
24225
24504
|
if (input === null || input === undefined) {
|
|
24226
24505
|
return false;
|
|
@@ -24290,7 +24569,6 @@ function isObservableInput(input) {
|
|
|
24290
24569
|
* // Primitive
|
|
24291
24570
|
* toObservable(42).subscribe(x => console.log(x)); // 42
|
|
24292
24571
|
*/
|
|
24293
|
-
// biome-ignore lint/suspicious/noExplicitAny: must be any to allow any type of input
|
|
24294
24572
|
function toObservable(input, ...args) {
|
|
24295
24573
|
// If input is already an ObservableInput (Observable, Promise, Iterable, etc), use RxJS 'from' to convert it
|
|
24296
24574
|
if (isObservableInput(input)) {
|
|
@@ -24468,6 +24746,7 @@ var deepmerge = /*@__PURE__*/getDefaultExportFromCjs(deepmergeExports);
|
|
|
24468
24746
|
* @returns The merged `MetaData` object, or `undefined` if both inputs are undefined.
|
|
24469
24747
|
*/
|
|
24470
24748
|
const mergeMetadata = (source, target) => {
|
|
24749
|
+
// Nothing to merge when neither side has metadata
|
|
24471
24750
|
if (!source && !target) {
|
|
24472
24751
|
return undefined;
|
|
24473
24752
|
}
|
|
@@ -24490,15 +24769,21 @@ const mergeMetadata = (source, target) => {
|
|
|
24490
24769
|
*
|
|
24491
24770
|
* @internal
|
|
24492
24771
|
*/
|
|
24772
|
+
// Deliberately co-located with `mergeMetadata` above
|
|
24773
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24493
24774
|
const mergeTelemetryItem = (target, source) => {
|
|
24775
|
+
// Both arguments are required to produce a valid merged telemetry item
|
|
24494
24776
|
if (!target || !source) {
|
|
24495
24777
|
throw new Error('Both target and source must be defined for merging telemetry items.');
|
|
24496
24778
|
}
|
|
24779
|
+
// A type mismatch would produce an inconsistent telemetry item, so fail fast
|
|
24497
24780
|
if (target.type && source.type && target.type !== source.type) {
|
|
24498
24781
|
throw new Error('Mismatched telemetry item types.');
|
|
24499
24782
|
}
|
|
24783
|
+
// Combine both scopes and de-duplicate via a Set before flattening back to an array
|
|
24500
24784
|
const scope = [...new Set([...(target.scope ?? []), ...(source.scope ?? [])])];
|
|
24501
24785
|
const metadata = mergeMetadata(target.metadata, source.metadata);
|
|
24786
|
+
// Source properties take precedence over target, with scope/metadata merged separately above
|
|
24502
24787
|
return { ...target, ...source, scope, metadata };
|
|
24503
24788
|
};
|
|
24504
24789
|
|
|
@@ -24529,11 +24814,18 @@ const mergeTelemetryItem = (target, source) => {
|
|
|
24529
24814
|
class TelemetryConfigurator extends BaseConfigBuilder {
|
|
24530
24815
|
#adaptersCallbacks = {};
|
|
24531
24816
|
#metadata = [];
|
|
24817
|
+
/**
|
|
24818
|
+
* Creates a new `TelemetryConfigurator` and wires up the async adapter
|
|
24819
|
+
* resolution and metadata merging pipelines used by `createConfigAsync`.
|
|
24820
|
+
*/
|
|
24532
24821
|
constructor() {
|
|
24533
24822
|
super();
|
|
24534
24823
|
// Configure async adapter resolution using mergeMap to handle Promise/Observable adapter factories
|
|
24535
24824
|
this._set('adapters', (args) => {
|
|
24536
|
-
|
|
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]) => {
|
|
24537
24829
|
acc[identifier] = adapter;
|
|
24538
24830
|
return acc;
|
|
24539
24831
|
}, {}), defaultIfEmpty({}), shareReplay({ bufferSize: 1, refCount: true }));
|
|
@@ -24541,12 +24833,15 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
24541
24833
|
// Configure metadata merging - handles multiple sync/async metadata sources
|
|
24542
24834
|
this._set('metadata', async () => {
|
|
24543
24835
|
const metadataItems = this.#metadata;
|
|
24544
|
-
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 }));
|
|
24545
24839
|
});
|
|
24546
24840
|
}
|
|
24547
24841
|
/**
|
|
24548
24842
|
* Registers a telemetry adapter with the configurator.
|
|
24549
24843
|
*
|
|
24844
|
+
* @param identifier - The key used to register and later resolve this adapter.
|
|
24550
24845
|
* @param adapter - The telemetry adapter to be added. The adapter's identifier is used as the key.
|
|
24551
24846
|
* @returns The current instance of the configurator for method chaining.
|
|
24552
24847
|
*/
|
|
@@ -24556,7 +24851,8 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
24556
24851
|
/**
|
|
24557
24852
|
* Configures a telemetry adapter with the configurator.
|
|
24558
24853
|
*
|
|
24559
|
-
* @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
|
|
24560
24856
|
* @returns The current instance for method chaining
|
|
24561
24857
|
*/
|
|
24562
24858
|
configureAdapter(identifier, adapterFn) {
|
|
@@ -24621,7 +24917,7 @@ class TelemetryConfigurator extends BaseConfigBuilder {
|
|
|
24621
24917
|
}
|
|
24622
24918
|
|
|
24623
24919
|
// Generated by genversion.
|
|
24624
|
-
const version$5 = '7.0.
|
|
24920
|
+
const version$5 = '7.0.1';
|
|
24625
24921
|
|
|
24626
24922
|
/**
|
|
24627
24923
|
* Enum representing the severity levels of telemetry items.
|
|
@@ -24715,6 +25011,8 @@ const TelemetryItemSchema = object({
|
|
|
24715
25011
|
*
|
|
24716
25012
|
* Extends TelemetryItemSchema and sets type to 'event'.
|
|
24717
25013
|
*/
|
|
25014
|
+
// Deliberately co-located with the other telemetry item schemas below
|
|
25015
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24718
25016
|
const TelemetryEventSchema = TelemetryItemSchema.extend({
|
|
24719
25017
|
type: literal(TelemetryType.Event),
|
|
24720
25018
|
});
|
|
@@ -24724,6 +25022,8 @@ const TelemetryEventSchema = TelemetryItemSchema.extend({
|
|
|
24724
25022
|
* Extends TelemetryItemSchema and sets type to 'exception'.
|
|
24725
25023
|
* Adds an exception property of type Error.
|
|
24726
25024
|
*/
|
|
25025
|
+
// Deliberately co-located with the other telemetry item schemas above/below
|
|
25026
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24727
25027
|
const TelemetryExceptionSchema = TelemetryItemSchema.extend({
|
|
24728
25028
|
type: literal(TelemetryType.Exception),
|
|
24729
25029
|
exception: _instanceof(Error).describe('The exception object.'),
|
|
@@ -24734,6 +25034,8 @@ const TelemetryExceptionSchema = TelemetryItemSchema.extend({
|
|
|
24734
25034
|
* Extends TelemetryItemSchema and sets type to 'metric'.
|
|
24735
25035
|
* Adds a value property for the metric value.
|
|
24736
25036
|
*/
|
|
25037
|
+
// Deliberately co-located with the other telemetry item schemas above/below
|
|
25038
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24737
25039
|
const TelemetryMetricSchema = TelemetryItemSchema.extend({
|
|
24738
25040
|
type: literal(TelemetryType.Metric),
|
|
24739
25041
|
value: number$1().describe('The value of the metric.'),
|
|
@@ -24743,6 +25045,8 @@ const TelemetryMetricSchema = TelemetryItemSchema.extend({
|
|
|
24743
25045
|
*
|
|
24744
25046
|
* Extends TelemetryEventSchema and allows passthrough of additional properties.
|
|
24745
25047
|
*/
|
|
25048
|
+
// Deliberately co-located with the other telemetry item schemas above
|
|
25049
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
24746
25050
|
const TelemetryCustomEventSchema = TelemetryEventSchema.extend({
|
|
24747
25051
|
type: literal(TelemetryType.Custom),
|
|
24748
25052
|
}).passthrough();
|
|
@@ -24764,7 +25068,6 @@ const TelemetryCustomEventSchema = TelemetryEventSchema.extend({
|
|
|
24764
25068
|
* @template TInit The type of the event details.
|
|
24765
25069
|
* @template TType The type of the event type.
|
|
24766
25070
|
*/
|
|
24767
|
-
// biome-ignore lint/suspicious/noUnsafeDeclarationMerging: no other way to define a class with multiple signatures
|
|
24768
25071
|
class FrameworkEvent {
|
|
24769
25072
|
__type;
|
|
24770
25073
|
#detail;
|
|
@@ -24843,6 +25146,7 @@ class FrameworkEvent {
|
|
|
24843
25146
|
* If the event is cancelable, this method sets the `canceled` property to `true`.
|
|
24844
25147
|
*/
|
|
24845
25148
|
preventDefault() {
|
|
25149
|
+
// Only cancelable events can be canceled
|
|
24846
25150
|
if (this.cancelable) {
|
|
24847
25151
|
this.#canceled = true;
|
|
24848
25152
|
}
|
|
@@ -24857,44 +25161,54 @@ class FrameworkEvent {
|
|
|
24857
25161
|
}
|
|
24858
25162
|
|
|
24859
25163
|
/**
|
|
24860
|
-
*
|
|
25164
|
+
* Event representing an error that occurred within a telemetry provider.
|
|
24861
25165
|
*
|
|
24862
|
-
*
|
|
24863
|
-
*
|
|
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.
|
|
24864
25169
|
*
|
|
24865
25170
|
* @example
|
|
24866
25171
|
* ```typescript
|
|
24867
|
-
* const
|
|
25172
|
+
* const errorEvent = new TelemetryErrorEvent(new Error('Something went wrong'), telemetryProvider);
|
|
24868
25173
|
* ```
|
|
24869
25174
|
*
|
|
24870
|
-
* @
|
|
24871
|
-
* @param source - The telemetry provider that is the source of this event.
|
|
25175
|
+
* @extends FrameworkEvent
|
|
24872
25176
|
*/
|
|
24873
|
-
class
|
|
24874
|
-
|
|
24875
|
-
|
|
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 });
|
|
24876
25186
|
}
|
|
24877
25187
|
}
|
|
25188
|
+
|
|
24878
25189
|
/**
|
|
24879
|
-
*
|
|
25190
|
+
* Represents a telemetry event within the framework.
|
|
24880
25191
|
*
|
|
24881
|
-
*
|
|
24882
|
-
*
|
|
24883
|
-
* 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.
|
|
24884
25194
|
*
|
|
24885
25195
|
* @example
|
|
24886
25196
|
* ```typescript
|
|
24887
|
-
* const
|
|
25197
|
+
* const event = new TelemetryEvent(item, provider);
|
|
24888
25198
|
* ```
|
|
24889
25199
|
*
|
|
24890
|
-
* @
|
|
24891
|
-
*
|
|
24892
|
-
* @param error - The error instance that was thrown.
|
|
24893
|
-
* @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.
|
|
24894
25202
|
*/
|
|
24895
|
-
class
|
|
24896
|
-
|
|
24897
|
-
|
|
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 });
|
|
24898
25212
|
}
|
|
24899
25213
|
}
|
|
24900
25214
|
|
|
@@ -24942,6 +25256,7 @@ class Measurement {
|
|
|
24942
25256
|
*/
|
|
24943
25257
|
clone(resetOptions = {}) {
|
|
24944
25258
|
const clonedMeasurement = new Measurement(this.#provider, this.#data);
|
|
25259
|
+
// Preserve the original start time when requested, otherwise the clone starts fresh
|
|
24945
25260
|
if (resetOptions.preserveStartTime) {
|
|
24946
25261
|
clonedMeasurement.#startTime = this.#startTime;
|
|
24947
25262
|
}
|
|
@@ -24956,6 +25271,7 @@ class Measurement {
|
|
|
24956
25271
|
* Resets the measured flag and, unless `preserveStartTime` is true, updates the start time to the current performance time.
|
|
24957
25272
|
*/
|
|
24958
25273
|
reset(options) {
|
|
25274
|
+
// Keep the existing start time when preserving, otherwise restart the clock
|
|
24959
25275
|
if (!options?.preserveStartTime) {
|
|
24960
25276
|
this.#startTime = performance.now();
|
|
24961
25277
|
}
|
|
@@ -24970,6 +25286,7 @@ class Measurement {
|
|
|
24970
25286
|
* @param options - Optional measurement options.
|
|
24971
25287
|
* @param options.markAsMeasured - If true, marks this measurement as completed.
|
|
24972
25288
|
* @param options.resetStartTime - If true, resets the start time after measuring.
|
|
25289
|
+
* @returns The elapsed duration in milliseconds since the last start time.
|
|
24973
25290
|
*/
|
|
24974
25291
|
measure(data, options) {
|
|
24975
25292
|
const measuredData = mergeTelemetryItem(this.#data, data ?? {});
|
|
@@ -24978,9 +25295,11 @@ class Measurement {
|
|
|
24978
25295
|
...measuredData,
|
|
24979
25296
|
value: duration,
|
|
24980
25297
|
});
|
|
25298
|
+
// Mark as measured only when explicitly requested by the caller
|
|
24981
25299
|
if (options?.markAsMeasured) {
|
|
24982
25300
|
this.#measured = true;
|
|
24983
25301
|
}
|
|
25302
|
+
// Restart the clock only when explicitly requested by the caller
|
|
24984
25303
|
if (options?.resetStartTime) {
|
|
24985
25304
|
this.#startTime = performance.now();
|
|
24986
25305
|
}
|
|
@@ -24989,7 +25308,7 @@ class Measurement {
|
|
|
24989
25308
|
/**
|
|
24990
25309
|
* Resolves a given promise, measures the result using the provided options, and returns the resolved value.
|
|
24991
25310
|
*
|
|
24992
|
-
* @
|
|
25311
|
+
* @template T - The type of the resolved value from the promise.
|
|
24993
25312
|
* @param promise - The promise to resolve.
|
|
24994
25313
|
* @param options - Optional configuration for resolving and measuring:
|
|
24995
25314
|
* - `data`: A value or a function that receives the resolved result and returns measurement data.
|
|
@@ -25007,7 +25326,7 @@ class Measurement {
|
|
|
25007
25326
|
* The function `fn` can return either a value of type `T` or a Promise of type `T`.
|
|
25008
25327
|
* Optionally, resolution options can be provided.
|
|
25009
25328
|
*
|
|
25010
|
-
* @
|
|
25329
|
+
* @template T - The return type of the function to execute.
|
|
25011
25330
|
* @param fn - A function that returns a value or a Promise to be resolved.
|
|
25012
25331
|
* @param options - Optional resolution options to customize the resolve behavior.
|
|
25013
25332
|
* @returns A Promise that resolves to the result of the executed function.
|
|
@@ -25025,6 +25344,7 @@ class Measurement {
|
|
|
25025
25344
|
* @see https://github.com/tc39/proposal-explicit-resource-management
|
|
25026
25345
|
*/
|
|
25027
25346
|
[Symbol.dispose]() {
|
|
25347
|
+
// Ensure a measurement is always taken before the instance is discarded
|
|
25028
25348
|
if (!this.#measured) {
|
|
25029
25349
|
try {
|
|
25030
25350
|
this.measure();
|
|
@@ -25067,7 +25387,10 @@ const resolveMetadata = (metadata, args) => {
|
|
|
25067
25387
|
* @param args - The arguments required by the metadata extractor, including the telemetry item.
|
|
25068
25388
|
* @returns An Observable emitting the telemetry item with applied metadata, or an error telemetry item if resolution fails.
|
|
25069
25389
|
*/
|
|
25390
|
+
// Deliberately co-located with `resolveMetadata` above
|
|
25391
|
+
// fusion-lint-disable-next-line single-export-per-file
|
|
25070
25392
|
const applyMetadata = (metadata, args) => {
|
|
25393
|
+
// Resolve metadata, merge it into the item, and fall back to an error item on failure
|
|
25071
25394
|
return resolveMetadata(metadata, args).pipe(defaultIfEmpty({}), // Ensure we always have an object to merge with
|
|
25072
25395
|
first(), // Take the first emitted value from the metadata extractor
|
|
25073
25396
|
// Merge the resolved metadata with the telemetry item.
|
|
@@ -25121,20 +25444,39 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25121
25444
|
#filters;
|
|
25122
25445
|
#defaultScope;
|
|
25123
25446
|
#eventProvider;
|
|
25447
|
+
/**
|
|
25448
|
+
* Stream of every telemetry item tracked by this provider.
|
|
25449
|
+
*
|
|
25450
|
+
* @returns An observable emitting each tracked `TelemetryItem`.
|
|
25451
|
+
*/
|
|
25124
25452
|
get items() {
|
|
25125
25453
|
return this.#items.asObservable();
|
|
25126
25454
|
}
|
|
25127
25455
|
/**
|
|
25128
25456
|
* Returns true if the provider has been initialized.
|
|
25457
|
+
*
|
|
25458
|
+
* @returns `true` when {@link initialize} has completed, otherwise `false`.
|
|
25129
25459
|
*/
|
|
25130
25460
|
get initialized() {
|
|
25131
25461
|
return this.#initialized;
|
|
25132
25462
|
}
|
|
25133
25463
|
#metadata;
|
|
25134
25464
|
#modules;
|
|
25465
|
+
/**
|
|
25466
|
+
* Sets the resolved framework module instances used to resolve metadata.
|
|
25467
|
+
*
|
|
25468
|
+
* @param value - The resolved framework module instances.
|
|
25469
|
+
*/
|
|
25135
25470
|
set modules(value) {
|
|
25136
25471
|
this.#modules = value;
|
|
25137
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
|
+
*/
|
|
25138
25480
|
constructor(config, deps) {
|
|
25139
25481
|
super({ version: version$5, config });
|
|
25140
25482
|
this.#adapters = config?.adapters ?? {};
|
|
@@ -25194,11 +25536,13 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25194
25536
|
async _initializeAdapters() {
|
|
25195
25537
|
// Initialize all adapters, do it in parallel
|
|
25196
25538
|
const adapterEntries = Object.entries(this.#adapters);
|
|
25539
|
+
// Kick off every adapter's initialize() concurrently
|
|
25197
25540
|
const initializationPromises = adapterEntries.map(([_identifier, adapter]) => adapter.initialize());
|
|
25198
25541
|
// Wait for all adapters to settle (either resolve or reject)
|
|
25199
25542
|
const results = await Promise.allSettled(initializationPromises);
|
|
25200
25543
|
// Check each result and dispatch errors for failed initializations
|
|
25201
25544
|
for (const [index, result] of results.entries()) {
|
|
25545
|
+
// Only rejected adapters need an error dispatched; fulfilled ones need no action
|
|
25202
25546
|
if (result.status === 'rejected') {
|
|
25203
25547
|
const [identifier] = adapterEntries[index];
|
|
25204
25548
|
this._dispatchError(new Error(`Failed to initialize telemetry adapter "${identifier}"`, {
|
|
@@ -25215,8 +25559,10 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25215
25559
|
*
|
|
25216
25560
|
* @returns Subscription to the telemetry item stream
|
|
25217
25561
|
* @protected
|
|
25562
|
+
* @throws {Error} When the provider has not been initialized yet.
|
|
25218
25563
|
*/
|
|
25219
25564
|
_connectAdapters() {
|
|
25565
|
+
// Adapters can only be connected after the provider has completed initialization
|
|
25220
25566
|
if (!this.#initialized) {
|
|
25221
25567
|
throw new Error('TelemetryProvider is not initialized');
|
|
25222
25568
|
}
|
|
@@ -25269,6 +25615,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25269
25615
|
* @protected
|
|
25270
25616
|
*/
|
|
25271
25617
|
_dispatchEntityEvent(item) {
|
|
25618
|
+
// Only dispatch when an event provider is configured
|
|
25272
25619
|
if (this.#eventProvider) {
|
|
25273
25620
|
// Wrap the telemetry item in a TelemetryEvent and dispatch it
|
|
25274
25621
|
this.#eventProvider.dispatchEvent(new TelemetryEvent(item, this));
|
|
@@ -25281,6 +25628,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25281
25628
|
* @protected
|
|
25282
25629
|
*/
|
|
25283
25630
|
_dispatchError(error) {
|
|
25631
|
+
// Only dispatch when an event provider is configured
|
|
25284
25632
|
if (this.#eventProvider) {
|
|
25285
25633
|
this.#eventProvider.dispatchEvent(new TelemetryErrorEvent(error, this));
|
|
25286
25634
|
}
|
|
@@ -25298,6 +25646,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25298
25646
|
* });
|
|
25299
25647
|
*/
|
|
25300
25648
|
track(item) {
|
|
25649
|
+
// Dispatch to the type-specific tracker so each item is validated with the right schema
|
|
25301
25650
|
switch (item.type) {
|
|
25302
25651
|
case TelemetryType.Event:
|
|
25303
25652
|
this.trackEvent(item);
|
|
@@ -25410,6 +25759,7 @@ class TelemetryProvider extends BaseModuleProvider {
|
|
|
25410
25759
|
*/
|
|
25411
25760
|
getAdapter(identifier) {
|
|
25412
25761
|
const adapter = this.#adapters[identifier];
|
|
25762
|
+
// Report a missing adapter as a telemetry exception rather than throwing
|
|
25413
25763
|
if (!adapter) {
|
|
25414
25764
|
this.trackException({
|
|
25415
25765
|
name: 'TelemetryAdapterNotFound',
|
|
@@ -25477,6 +25827,7 @@ const module$3 = {
|
|
|
25477
25827
|
const mapConfiguratorEvents = (
|
|
25478
25828
|
// biome-ignore lint/suspicious/noExplicitAny: must be any to support all module types
|
|
25479
25829
|
configurator) => {
|
|
25830
|
+
// Map each configurator event into a standardized telemetry item shape
|
|
25480
25831
|
return configurator.event$.pipe(map$1((event) => {
|
|
25481
25832
|
const item = {
|
|
25482
25833
|
name: event.name,
|
|
@@ -25488,6 +25839,7 @@ configurator) => {
|
|
|
25488
25839
|
},
|
|
25489
25840
|
scope: ['configuration'],
|
|
25490
25841
|
};
|
|
25842
|
+
// An event carrying an error is reported as a telemetry exception
|
|
25491
25843
|
if (event.error) {
|
|
25492
25844
|
return {
|
|
25493
25845
|
...item,
|
|
@@ -25495,6 +25847,7 @@ configurator) => {
|
|
|
25495
25847
|
exception: event.error,
|
|
25496
25848
|
};
|
|
25497
25849
|
}
|
|
25850
|
+
// An event carrying a metric value is reported as a telemetry metric
|
|
25498
25851
|
if (event.metric) {
|
|
25499
25852
|
return {
|
|
25500
25853
|
...item,
|
|
@@ -25544,9 +25897,11 @@ configurator, options) => {
|
|
|
25544
25897
|
configurator.addConfig({
|
|
25545
25898
|
module: module$3,
|
|
25546
25899
|
configure: async (builder, ref) => {
|
|
25900
|
+
// Attach configurator events to the telemetry builder only when opted in
|
|
25547
25901
|
{
|
|
25548
25902
|
builder.attachItems(mapConfiguratorEvents(configurator));
|
|
25549
25903
|
}
|
|
25904
|
+
// Run the caller-supplied configure callback when one was provided
|
|
25550
25905
|
if (options?.configure) {
|
|
25551
25906
|
await Promise.resolve(options.configure(builder, ref));
|
|
25552
25907
|
}
|
|
@@ -42493,11 +42848,14 @@ class MsalClient extends PublicClientApplication {
|
|
|
42493
42848
|
* - **Redirect**: Navigates browser to Microsoft login page. Returns `void` because the browser
|
|
42494
42849
|
* navigates to a new page. After redirect completes, the result will be available via
|
|
42495
42850
|
* `handleRedirectPromise()` when the app loads on the new page.
|
|
42851
|
+
*
|
|
42852
|
+
* @throws {Error} If an invalid `options.behavior` value is provided.
|
|
42496
42853
|
*/
|
|
42497
42854
|
async login(options) {
|
|
42498
42855
|
// Attempt silent authentication first if enabled
|
|
42499
42856
|
// This provides better UX by avoiding unnecessary popups/redirects
|
|
42500
42857
|
if (options.silent) {
|
|
42858
|
+
// Warn early when neither an account nor login hint is available for silent SSO
|
|
42501
42859
|
if (!options.request.account && !options.request.loginHint) {
|
|
42502
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);
|
|
42503
42861
|
}
|
|
@@ -42555,6 +42913,7 @@ class MsalClient extends PublicClientApplication {
|
|
|
42555
42913
|
* ```
|
|
42556
42914
|
*/
|
|
42557
42915
|
async logout(options) {
|
|
42916
|
+
// Warn when no account was supplied since the active account will be used instead
|
|
42558
42917
|
if (!options?.account) {
|
|
42559
42918
|
this.getLogger().warning('No account available for logout, please provide an account in the options', FUSION_CORRELATION_ID);
|
|
42560
42919
|
}
|
|
@@ -42586,18 +42945,23 @@ class MsalClient extends PublicClientApplication {
|
|
|
42586
42945
|
*
|
|
42587
42946
|
* The default silent behavior is determined by presence of account in the request.
|
|
42588
42947
|
* This provides optimal UX by minimizing unnecessary user interactions.
|
|
42948
|
+
*
|
|
42949
|
+
* @throws {Error} If no `request` is provided in `options`.
|
|
42589
42950
|
*/
|
|
42590
42951
|
async acquireToken(options) {
|
|
42591
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
|
|
42592
42954
|
if (!request) {
|
|
42593
42955
|
throw new Error('No request provided, please provide a request in the options');
|
|
42594
42956
|
}
|
|
42957
|
+
// Warn when no scopes are requested, since MSAL requires at least one scope
|
|
42595
42958
|
if (request.scopes.length === 0) {
|
|
42596
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);
|
|
42597
42960
|
}
|
|
42598
42961
|
// Attempt silent token acquisition first
|
|
42599
42962
|
// This fetches from cache or uses refresh token without user interaction
|
|
42600
42963
|
if (silent) {
|
|
42964
|
+
// Only silent-acquire when an account is available to look up cached tokens for
|
|
42601
42965
|
if (request.account) {
|
|
42602
42966
|
try {
|
|
42603
42967
|
this.getLogger().verbose('Attempting to acquire token silently', request.correlationId || FUSION_CORRELATION_ID);
|
|
@@ -42657,6 +43021,7 @@ const levelMap = {
|
|
|
42657
43021
|
const parseMsalMessage = (message) => {
|
|
42658
43022
|
// Match the structured format: [timestamp] : [correlationId] : [package] : [level] - [component] - [message]
|
|
42659
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
|
|
42660
43025
|
if (match) {
|
|
42661
43026
|
const [, _timestamp, _correlationId, packageInfo, _logLevel, component, logMessage] = match;
|
|
42662
43027
|
return {
|
|
@@ -42719,7 +43084,7 @@ const createClientLogCallback = (provider, metadata, scope) => {
|
|
|
42719
43084
|
};
|
|
42720
43085
|
|
|
42721
43086
|
// Generated by genversion.
|
|
42722
|
-
const version$2 = '10.0.
|
|
43087
|
+
const version$2 = '10.0.2';
|
|
42723
43088
|
|
|
42724
43089
|
/**
|
|
42725
43090
|
* Zod schema for telemetry configuration validation.
|
|
@@ -42772,6 +43137,7 @@ class MsalConfigurator extends BaseConfigBuilder {
|
|
|
42772
43137
|
* The MSAL module version being configured.
|
|
42773
43138
|
*
|
|
42774
43139
|
* @default Latest
|
|
43140
|
+
* @returns The configured MSAL module version.
|
|
42775
43141
|
*/
|
|
42776
43142
|
get version() {
|
|
42777
43143
|
return version$2;
|
|
@@ -42787,6 +43153,7 @@ class MsalConfigurator extends BaseConfigBuilder {
|
|
|
42787
43153
|
this._set('version', async () => this.version);
|
|
42788
43154
|
// Auto-detect and integrate telemetry module if available
|
|
42789
43155
|
this._set('telemetry.provider', async (args) => {
|
|
43156
|
+
// Only resolve the telemetry instance when the telemetry module is registered
|
|
42790
43157
|
if (args.hasModule('telemetry')) {
|
|
42791
43158
|
const telemetry = await args.requireInstance('telemetry');
|
|
42792
43159
|
return telemetry;
|
|
@@ -43010,7 +43377,7 @@ class MsalConfigurator extends BaseConfigBuilder {
|
|
|
43010
43377
|
const config = await MsalConfigSchema.parseAsync(rawConfig);
|
|
43011
43378
|
// Auto-create client if config provided but no client instance
|
|
43012
43379
|
// This allows users to provide configuration without manually instantiating the client
|
|
43013
|
-
if (!config.client &&
|
|
43380
|
+
if (!config.client && this.#msalConfig) {
|
|
43014
43381
|
const clientConfig = this.#msalConfig;
|
|
43015
43382
|
config.telemetry.provider?.trackEvent({
|
|
43016
43383
|
name: 'module-msal.configurator._processConfig.creating-client',
|
|
@@ -43179,12 +43546,15 @@ class VersionError extends Error {
|
|
|
43179
43546
|
function mapVersionToEnumVersion(version) {
|
|
43180
43547
|
console.log('Resolving version:', version);
|
|
43181
43548
|
const coercedVersion = semver.coerce(version);
|
|
43549
|
+
// An uncoercible version string cannot be mapped to a module version
|
|
43182
43550
|
if (!coercedVersion) {
|
|
43183
43551
|
throw new Error(`Invalid version: ${version}`);
|
|
43184
43552
|
}
|
|
43553
|
+
// Versions before 4.0.0 (including 2.x) map to the v2-compatible module version
|
|
43185
43554
|
if (semver.satisfies(coercedVersion, '<4.0.0')) {
|
|
43186
43555
|
return MsalModuleVersion.V2;
|
|
43187
43556
|
}
|
|
43557
|
+
// Versions before 7.0.0 (4.x and 5.x/6.x) map to the v4-compatible module version
|
|
43188
43558
|
if (semver.satisfies(coercedVersion, '<7.0.0')) {
|
|
43189
43559
|
return MsalModuleVersion.V4;
|
|
43190
43560
|
}
|
|
@@ -43247,6 +43617,7 @@ function resolveVersion(version) {
|
|
|
43247
43617
|
warnings.push(missingVersionWarning.message);
|
|
43248
43618
|
wantedVersion = latestVersion;
|
|
43249
43619
|
}
|
|
43620
|
+
// Warn when the requested major version trails the latest major version
|
|
43250
43621
|
if (wantedVersion.major < latestVersion.major) {
|
|
43251
43622
|
const majorBehindVersionWarning = new VersionError(`Requested major version ${wantedVersion.major} is behind the latest major version ${latestVersion.major}`, wantedVersion, latestVersion);
|
|
43252
43623
|
warnings.push(majorBehindVersionWarning.message);
|
|
@@ -43341,9 +43712,11 @@ function mapAuthenticationResult(result) {
|
|
|
43341
43712
|
function createProxyClient(client) {
|
|
43342
43713
|
const proxy = new Proxy(client, {
|
|
43343
43714
|
get: (target, prop) => {
|
|
43715
|
+
// Adapt each v4 client member to the v2-compatible shape expected by callers
|
|
43344
43716
|
switch (prop) {
|
|
43345
43717
|
case 'getAllAccounts': {
|
|
43346
43718
|
return () => {
|
|
43719
|
+
// Map each v4 account shape to its v2-compatible equivalent
|
|
43347
43720
|
return target.getAllAccounts().map(mapAccountInfo);
|
|
43348
43721
|
};
|
|
43349
43722
|
}
|
|
@@ -43375,6 +43748,7 @@ function createProxyClient(client) {
|
|
|
43375
43748
|
case 'handleRedirectPromise': {
|
|
43376
43749
|
return async () => {
|
|
43377
43750
|
const result = await target.handleRedirectPromise();
|
|
43751
|
+
// No redirect result means there's nothing pending to map
|
|
43378
43752
|
if (!result) {
|
|
43379
43753
|
return null;
|
|
43380
43754
|
}
|
|
@@ -43384,6 +43758,7 @@ function createProxyClient(client) {
|
|
|
43384
43758
|
case 'getActiveAccount': {
|
|
43385
43759
|
return () => {
|
|
43386
43760
|
const account = target.getActiveAccount();
|
|
43761
|
+
// No active account means there's nothing to map
|
|
43387
43762
|
if (!account) {
|
|
43388
43763
|
return null;
|
|
43389
43764
|
}
|
|
@@ -43470,6 +43845,8 @@ function createProxyClient(client) {
|
|
|
43470
43845
|
}
|
|
43471
43846
|
},
|
|
43472
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.
|
|
43473
43850
|
return proxy;
|
|
43474
43851
|
}
|
|
43475
43852
|
|
|
@@ -43480,6 +43857,7 @@ function createProxyClient(client) {
|
|
|
43480
43857
|
* @returns True if the request is in v4 format (has a `request` property with `scopes` and `account`)
|
|
43481
43858
|
*/
|
|
43482
43859
|
function isRequestV4(req) {
|
|
43860
|
+
// Non-object/null values can never match the v4 request shape
|
|
43483
43861
|
if (typeof req !== 'object' || req === null) {
|
|
43484
43862
|
return false;
|
|
43485
43863
|
}
|
|
@@ -43513,6 +43891,7 @@ function createProxyProvider$2(provider) {
|
|
|
43513
43891
|
// Use Proxy to intercept property access and provide v2-compatible implementations
|
|
43514
43892
|
const proxy = new Proxy(provider, {
|
|
43515
43893
|
get: (target, prop) => {
|
|
43894
|
+
// Adapt each v4 provider member to the v2-compatible shape expected by callers
|
|
43516
43895
|
switch (prop) {
|
|
43517
43896
|
case 'version': {
|
|
43518
43897
|
return provider.version;
|
|
@@ -43527,6 +43906,7 @@ function createProxyProvider$2(provider) {
|
|
|
43527
43906
|
case 'defaultClient': {
|
|
43528
43907
|
// Deprecated property - redirect to client with warning
|
|
43529
43908
|
console.warn('defaultClient is deprecated, use client instead');
|
|
43909
|
+
// Same v2-compatible client wrapper as the `client` case above.
|
|
43530
43910
|
return v2Client;
|
|
43531
43911
|
}
|
|
43532
43912
|
case 'defaultAccount': {
|
|
@@ -43610,6 +43990,7 @@ function createProxyProvider$2(provider) {
|
|
|
43610
43990
|
};
|
|
43611
43991
|
}
|
|
43612
43992
|
default: {
|
|
43993
|
+
// The proxy is awaited in some contexts, so `then` must resolve to undefined
|
|
43613
43994
|
if (prop === 'then') {
|
|
43614
43995
|
return undefined;
|
|
43615
43996
|
}
|
|
@@ -43647,6 +44028,7 @@ function createProxyProvider$1(provider) {
|
|
|
43647
44028
|
// Create passthrough proxy that only overrides msalVersion
|
|
43648
44029
|
return new Proxy(provider, {
|
|
43649
44030
|
get: (target, prop) => {
|
|
44031
|
+
// Passthrough every property except msalVersion, which reports V4 compatibility
|
|
43650
44032
|
switch (prop) {
|
|
43651
44033
|
case 'msalVersion': {
|
|
43652
44034
|
// Report as V4 for compatibility tracking
|
|
@@ -43708,6 +44090,9 @@ function createProxyProvider$1(provider) {
|
|
|
43708
44090
|
* @param version - The target version string (e.g., '2.0.0', '4.0.0')
|
|
43709
44091
|
* @returns A proxy provider compatible with the specified version
|
|
43710
44092
|
*
|
|
44093
|
+
* @template T - The provider interface type expected by the target version.
|
|
44094
|
+
* @throws {Error} If the resolved version is not supported.
|
|
44095
|
+
*
|
|
43711
44096
|
* @example
|
|
43712
44097
|
* ```typescript
|
|
43713
44098
|
* const baseProvider = new MsalProvider(config);
|
|
@@ -43717,6 +44102,7 @@ function createProxyProvider$1(provider) {
|
|
|
43717
44102
|
function createProxyProvider(provider, version) {
|
|
43718
44103
|
// Resolve the requested version to determine which proxy to create
|
|
43719
44104
|
const { enumVersion } = resolveVersion(version);
|
|
44105
|
+
// Build the version-appropriate proxy based on the resolved MSAL module version
|
|
43720
44106
|
switch (enumVersion) {
|
|
43721
44107
|
case MsalModuleVersion.V2:
|
|
43722
44108
|
// Create v2-compatible proxy with legacy API adapters
|
|
@@ -43728,6 +44114,7 @@ function createProxyProvider(provider, version) {
|
|
|
43728
44114
|
// Create transparent proxy for v5 - passes through to original provider
|
|
43729
44115
|
return new Proxy(provider, {
|
|
43730
44116
|
get: (target, prop) => {
|
|
44117
|
+
// Delegate every known property/method to the underlying provider
|
|
43731
44118
|
switch (prop) {
|
|
43732
44119
|
case 'version': {
|
|
43733
44120
|
return target.version;
|
|
@@ -43814,6 +44201,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43814
44201
|
* Default OAuth scopes used when the caller provides no scopes.
|
|
43815
44202
|
*
|
|
43816
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.
|
|
43817
44206
|
*/
|
|
43818
44207
|
get defaultScopes() {
|
|
43819
44208
|
const clientId = this.#client.clientId;
|
|
@@ -43836,6 +44225,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43836
44225
|
*
|
|
43837
44226
|
* Provides access to the underlying MSAL PublicClientApplication for advanced use cases.
|
|
43838
44227
|
* Prefer using provider methods for standard authentication operations.
|
|
44228
|
+
*
|
|
44229
|
+
* @returns The underlying MSAL client instance.
|
|
43839
44230
|
*/
|
|
43840
44231
|
get client() {
|
|
43841
44232
|
return this.#client;
|
|
@@ -43845,6 +44236,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43845
44236
|
*
|
|
43846
44237
|
* Returns the active account if a user is authenticated, or null if no user is logged in.
|
|
43847
44238
|
* This is a shorthand for `client.getActiveAccount()`.
|
|
44239
|
+
*
|
|
44240
|
+
* @returns The currently authenticated account, or `null` if no user is logged in.
|
|
43848
44241
|
*/
|
|
43849
44242
|
get account() {
|
|
43850
44243
|
return this.#client.getActiveAccount();
|
|
@@ -43908,6 +44301,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43908
44301
|
*
|
|
43909
44302
|
* The provider will attempt automatic login with empty scopes if requiresAuth is true.
|
|
43910
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.
|
|
43911
44306
|
*/
|
|
43912
44307
|
async initialize() {
|
|
43913
44308
|
// Guard: skip authentication when running inside MSAL's hidden iframe.
|
|
@@ -43934,6 +44329,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43934
44329
|
// Use MSAL's acquireTokenByCode to exchange backend auth code for tokens
|
|
43935
44330
|
// This follows Microsoft's standard SPA Auth Code Flow pattern
|
|
43936
44331
|
const clientId = this.#client.clientId;
|
|
44332
|
+
// A client ID is required to build the default scope used for the auth code exchange
|
|
43937
44333
|
if (!clientId) {
|
|
43938
44334
|
throw new Error('Client ID is required for auth code exchange');
|
|
43939
44335
|
}
|
|
@@ -43977,6 +44373,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43977
44373
|
// Priority 1: Check if returning from redirect-based authentication
|
|
43978
44374
|
// This handles cases where user just completed a login/acquireToken via redirect
|
|
43979
44375
|
const handleRedirectResult = await this.handleRedirect();
|
|
44376
|
+
// A returned account means the user just completed a redirect-based login
|
|
43980
44377
|
if (handleRedirectResult?.account) {
|
|
43981
44378
|
// Successfully authenticated via redirect - set as active account
|
|
43982
44379
|
// This means the user was redirected to Microsoft and came back authenticated
|
|
@@ -43993,6 +44390,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
43993
44390
|
// Note: Using default scopes here as we don't know what scopes the app needs yet
|
|
43994
44391
|
// App should call acquireToken with actual scopes after initialization
|
|
43995
44392
|
const loginResult = await this.login({ request: { scopes: this.defaultScopes } });
|
|
44393
|
+
// Only set the active account when the automatic login actually returned one
|
|
43996
44394
|
if (loginResult?.account) {
|
|
43997
44395
|
// Automatic login successful - set as active account
|
|
43998
44396
|
this.#client.setActiveAccount(loginResult.account);
|
|
@@ -44041,6 +44439,8 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44041
44439
|
* @remark Empty scopes are currently tracked as telemetry exceptions but execution continues for monitoring purposes.
|
|
44042
44440
|
* This behavior will be changed to throw exceptions once sufficient metrics are collected.
|
|
44043
44441
|
*
|
|
44442
|
+
* @throws {Error} Re-throws any error encountered during token acquisition after tracking it via telemetry.
|
|
44443
|
+
*
|
|
44044
44444
|
* @example
|
|
44045
44445
|
* ```typescript
|
|
44046
44446
|
* // Modern API format
|
|
@@ -44083,6 +44483,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44083
44483
|
}
|
|
44084
44484
|
// Handle empty scopes - currently monitoring for telemetry, will throw in future
|
|
44085
44485
|
if (candidateScopes.length === 0) {
|
|
44486
|
+
// Fall back to the client-id-derived default scope when one is available
|
|
44086
44487
|
if (defaultScopes.length > 0) {
|
|
44087
44488
|
this._trackEvent('acquireToken.missing-scope.defaulted', TelemetryLevel.Warning, {
|
|
44088
44489
|
properties: { ...telemetryProperties, defaultScopes },
|
|
@@ -44094,7 +44495,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44094
44495
|
exception,
|
|
44095
44496
|
properties: telemetryProperties,
|
|
44096
44497
|
});
|
|
44097
|
-
// TODO: throw exception when sufficient metrics are collected
|
|
44498
|
+
// TODO(#5113): throw exception when sufficient metrics are collected
|
|
44098
44499
|
// This allows us to monitor how often empty scopes are provided before enforcing validation
|
|
44099
44500
|
}
|
|
44100
44501
|
}
|
|
@@ -44221,6 +44622,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44221
44622
|
// Fall through to interactive authentication
|
|
44222
44623
|
}
|
|
44223
44624
|
}
|
|
44625
|
+
// Perform interactive authentication based on specified behavior
|
|
44224
44626
|
switch (behavior) {
|
|
44225
44627
|
case 'popup':
|
|
44226
44628
|
return await this.#client.loginPopup(request);
|
|
@@ -44302,6 +44704,7 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44302
44704
|
async handleRedirect() {
|
|
44303
44705
|
// Process any pending redirect from authentication flow
|
|
44304
44706
|
const result = await this.client.handleRedirectPromise();
|
|
44707
|
+
// Only track/log when a redirect result is actually returned
|
|
44305
44708
|
if (result) {
|
|
44306
44709
|
// Track successful redirect completion for monitoring
|
|
44307
44710
|
this._trackEvent('handleRedirect.success', TelemetryLevel.Information, {
|
|
@@ -44327,6 +44730,9 @@ class MsalProvider extends BaseModuleProvider {
|
|
|
44327
44730
|
* - Version compatibility is tracked via telemetry
|
|
44328
44731
|
* - Throws error if unsupported version is requested
|
|
44329
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
|
+
*
|
|
44330
44736
|
* @example
|
|
44331
44737
|
* ```typescript
|
|
44332
44738
|
* // Create v2-compatible proxy
|
|
@@ -44456,6 +44862,7 @@ const module$2 = {
|
|
|
44456
44862
|
// Priority 2: Check if provider exists in parent module (proxy compatibility)
|
|
44457
44863
|
// This allows child applications to reuse parent's authentication provider
|
|
44458
44864
|
const hostProvider = init.ref?.auth;
|
|
44865
|
+
// Reuse the parent's provider (via a version-compatible proxy) when available
|
|
44459
44866
|
if (hostProvider) {
|
|
44460
44867
|
try {
|
|
44461
44868
|
const proxyProvider = hostProvider.createProxyProvider(config.version);
|
|
@@ -44464,7 +44871,7 @@ const module$2 = {
|
|
|
44464
44871
|
catch (error) {
|
|
44465
44872
|
console.error('MsalModule::Failed to create proxy provider', error);
|
|
44466
44873
|
// Fallback to host provider to prevent app breakage during migration
|
|
44467
|
-
// 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
|
|
44468
44875
|
return hostProvider;
|
|
44469
44876
|
}
|
|
44470
44877
|
}
|
|
@@ -44499,7 +44906,7 @@ const module$2 = {
|
|
|
44499
44906
|
* ```
|
|
44500
44907
|
*/
|
|
44501
44908
|
const enableMSAL = (
|
|
44502
|
-
//
|
|
44909
|
+
// biome-ignore lint/suspicious/noExplicitAny: must be any to support all module types
|
|
44503
44910
|
configurator, configure) => {
|
|
44504
44911
|
const config = configure ? configureMsal(configure) : { module: module$2 };
|
|
44505
44912
|
configurator.addConfig(config);
|
|
@@ -44533,7 +44940,11 @@ const configureMsal = (configure) => ({
|
|
|
44533
44940
|
* @returns A function that takes an Observable stream of `QueryQueueItem` and returns an Observable
|
|
44534
44941
|
* stream where each item is processed in sequence by the provided callback.
|
|
44535
44942
|
*/
|
|
44536
|
-
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
|
+
|
|
44537
44948
|
/**
|
|
44538
44949
|
* Takes a function that transforms each item in a queue and returns an Observable.
|
|
44539
44950
|
* It processes each item concurrently, potentially leading to out-of-order execution.
|
|
@@ -44544,7 +44955,10 @@ const concatQueue = (...args) => (source$) => source$.pipe(concatMap(...args));
|
|
|
44544
44955
|
* @returns A function that takes an Observable stream of `QueryQueueItem` and returns an Observable
|
|
44545
44956
|
* stream where each item is processed concurrently by the provided callback.
|
|
44546
44957
|
*/
|
|
44547
|
-
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
|
+
|
|
44548
44962
|
/**
|
|
44549
44963
|
* Takes a function that transforms each item in a queue and returns an Observable.
|
|
44550
44964
|
* It processes each item by cancelling the current task if a new one arrives.
|
|
@@ -44556,7 +44970,10 @@ const mergeQueue = (...args) => (source$) => source$.pipe(mergeMap(...args));
|
|
|
44556
44970
|
* stream where each item is processed by the provided callback, but only the latest
|
|
44557
44971
|
* item's result is emitted.
|
|
44558
44972
|
*/
|
|
44559
|
-
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
|
+
|
|
44560
44977
|
/**
|
|
44561
44978
|
* Transforms a query result Observable into a plain value Observable by extracting the `value` property.
|
|
44562
44979
|
*
|
|
@@ -44579,7 +44996,9 @@ const switchQueue = (...args) => (source$) => source$.pipe(switchMap(...args));
|
|
|
44579
44996
|
* });
|
|
44580
44997
|
* ```
|
|
44581
44998
|
*/
|
|
44582
|
-
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));
|
|
44583
45002
|
|
|
44584
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;
|
|
44585
45004
|
|
|
@@ -44788,6 +45207,12 @@ v5.URL = URL$1;
|
|
|
44788
45207
|
class QueryClientError extends Error {
|
|
44789
45208
|
type;
|
|
44790
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
|
+
*/
|
|
44791
45216
|
constructor(type, args) {
|
|
44792
45217
|
super(args.message, { cause: args.cause });
|
|
44793
45218
|
this.type = type;
|
|
@@ -44855,14 +45280,19 @@ const actions$1 = createActions$1();
|
|
|
44855
45280
|
* @param action$ - The stream of actions being dispatched in the system.
|
|
44856
45281
|
* @returns An Observable that emits execute actions for each request action.
|
|
44857
45282
|
*/
|
|
44858
|
-
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
|
+
|
|
44859
45287
|
/**
|
|
44860
45288
|
* Handles the execution of a query.
|
|
44861
45289
|
*
|
|
44862
45290
|
* @param fetch - The function used to execute the query.
|
|
44863
45291
|
* @returns A flow that handles the execution of the query.
|
|
44864
45292
|
*/
|
|
44865
|
-
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]) => {
|
|
44866
45296
|
const transaction = action.payload;
|
|
44867
45297
|
const request = state[transaction];
|
|
44868
45298
|
// Create an AbortController instance to manage cancellation.
|
|
@@ -44875,6 +45305,7 @@ const handleExecution = (fetch) => (action$, state$) => action$.pipe(filter(acti
|
|
|
44875
45305
|
}
|
|
44876
45306
|
}));
|
|
44877
45307
|
try {
|
|
45308
|
+
// map the fetch outcome to a success/failure action, cancelling early if aborted
|
|
44878
45309
|
return from(fetch(request.args, controller.signal)).pipe(map$1((value) => actions$1.execute.success({
|
|
44879
45310
|
...request,
|
|
44880
45311
|
status: 'complete',
|
|
@@ -44889,6 +45320,7 @@ const handleExecution = (fetch) => (action$, state$) => action$.pipe(filter(acti
|
|
|
44889
45320
|
return of(actions$1.execute.failure(err, transaction));
|
|
44890
45321
|
}
|
|
44891
45322
|
}));
|
|
45323
|
+
|
|
44892
45324
|
/**
|
|
44893
45325
|
* Handles execution failure by scheduling retries according to the provided retry options.
|
|
44894
45326
|
* If the maximum number of retries is exceeded or no retry options are provided, it emits an error action.
|
|
@@ -44898,6 +45330,7 @@ const handleExecution = (fetch) => (action$, state$) => action$.pipe(filter(acti
|
|
|
44898
45330
|
*/
|
|
44899
45331
|
const handleFailure = (config) => {
|
|
44900
45332
|
return (action$, state$) => {
|
|
45333
|
+
// filter for execution failures and decide whether to retry or report an error
|
|
44901
45334
|
return action$.pipe(
|
|
44902
45335
|
// Filter for actions that indicate an execution failure.
|
|
44903
45336
|
filter(actions$1.execute.failure.match),
|
|
@@ -44954,6 +45387,7 @@ const handleFailure = (config) => {
|
|
|
44954
45387
|
filter(([_, state]) => !!state[transaction]),
|
|
44955
45388
|
// If the request has been removed, throw an error.
|
|
44956
45389
|
tap(([, state]) => {
|
|
45390
|
+
// Re-check inside the tap since the request could have been removed during the delay
|
|
44957
45391
|
if (!state[transaction]) {
|
|
44958
45392
|
throw new QueryClientError('error', {
|
|
44959
45393
|
message: 'request not found, most likely removed while scheduled for retry',
|
|
@@ -45009,9 +45443,9 @@ const createReducer$1 = (initial = {}) => createReducer$2(initial, (builder) =>
|
|
|
45009
45443
|
builder.addCase(actions$1.execute, (state, action) => {
|
|
45010
45444
|
// Locates the query entry in the state using the transaction ID provided in `action.payload`.
|
|
45011
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.
|
|
45012
45448
|
if (entry) {
|
|
45013
|
-
// If the entry is found, logs the current timestamp to the `execution` array
|
|
45014
|
-
// to indicate when the execution action was triggered.
|
|
45015
45449
|
entry.execution.push(Date.now());
|
|
45016
45450
|
// Updates the status of the entry to 'active' to reflect that the execution
|
|
45017
45451
|
// process has started.
|
|
@@ -45026,9 +45460,9 @@ const createReducer$1 = (initial = {}) => createReducer$2(initial, (builder) =>
|
|
|
45026
45460
|
builder.addCase(actions$1.execute.failure, (state, action) => {
|
|
45027
45461
|
// Locates the query entry in the state using the transaction ID provided in `action.meta`.
|
|
45028
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.
|
|
45029
45465
|
if (entry) {
|
|
45030
|
-
// If the entry is found, the error information from `action.payload.error` is added to the `errors` array of the entry.
|
|
45031
|
-
// This allows tracking of all errors that have occurred during the execution of the query.
|
|
45032
45466
|
entry.errors.push(action.payload.error);
|
|
45033
45467
|
// The status of the entry is updated to 'failed' to indicate that the query execution has encountered an error.
|
|
45034
45468
|
entry.status = 'failed';
|
|
@@ -45063,6 +45497,9 @@ class QueryClientJob extends Observable {
|
|
|
45063
45497
|
get status() {
|
|
45064
45498
|
return this.#status$.value;
|
|
45065
45499
|
}
|
|
45500
|
+
/**
|
|
45501
|
+
* An Observable stream of the job's status, emitting whenever the status changes.
|
|
45502
|
+
*/
|
|
45066
45503
|
get status$() {
|
|
45067
45504
|
return this.#status$.asObservable();
|
|
45068
45505
|
}
|
|
@@ -45182,8 +45619,10 @@ class QueryClientJob extends Observable {
|
|
|
45182
45619
|
* This method should be called to gracefully end a job that has finished its task.
|
|
45183
45620
|
* It will also attempt to cancel the job with a completion reason, transitioning its status to 'canceled'
|
|
45184
45621
|
* if it was in an 'active' state but hadn't yet completed.
|
|
45622
|
+
* @param reason - Optional reason recorded for the completion/cancellation.
|
|
45185
45623
|
*/
|
|
45186
45624
|
complete(reason) {
|
|
45625
|
+
// Only cancel/complete when the job isn't already in a terminal state
|
|
45187
45626
|
if (!this.closed) {
|
|
45188
45627
|
this.cancel(reason ?? `job: ${this.transaction} was completed`);
|
|
45189
45628
|
}
|
|
@@ -45198,6 +45637,9 @@ class QueryClientJob extends Observable {
|
|
|
45198
45637
|
const transaction = this.transaction;
|
|
45199
45638
|
this.#client.cancel(transaction, reason ?? `job: ${transaction} was canceled`);
|
|
45200
45639
|
}
|
|
45640
|
+
/**
|
|
45641
|
+
* Disposes of the job by completing it, releasing any held resources.
|
|
45642
|
+
*/
|
|
45201
45643
|
[Symbol.dispose]() {
|
|
45202
45644
|
this.complete();
|
|
45203
45645
|
}
|
|
@@ -45311,6 +45753,7 @@ class QueryClient extends Observable {
|
|
|
45311
45753
|
* @returns {Observable<TType>} An Observable stream of successful results.
|
|
45312
45754
|
*/
|
|
45313
45755
|
get success$() {
|
|
45756
|
+
// narrow the action stream down to successful execute actions and extract their payload
|
|
45314
45757
|
return this.action$.pipe(filter(actions$1.execute.success.match), map$1(({ payload }) => payload));
|
|
45315
45758
|
}
|
|
45316
45759
|
/**
|
|
@@ -45319,6 +45762,7 @@ class QueryClient extends Observable {
|
|
|
45319
45762
|
* @returns {Observable<QueryClientError>} An Observable stream of query errors.
|
|
45320
45763
|
*/
|
|
45321
45764
|
get error$() {
|
|
45765
|
+
// narrow the action stream down to client errors and wrap them as QueryClientError instances
|
|
45322
45766
|
return this.action$.pipe(filterAction('client/error'), withLatestFrom(this.#state), map$1(([action, state]) => {
|
|
45323
45767
|
const { payload, meta: { transaction }, } = action;
|
|
45324
45768
|
const request = state[transaction];
|
|
@@ -45349,7 +45793,9 @@ class QueryClient extends Observable {
|
|
|
45349
45793
|
// Add flows to handle different aspects of the query lifecycle.
|
|
45350
45794
|
this.#subscription.add(this.#state.addFlow(handleRequests));
|
|
45351
45795
|
this.#subscription.add(this.#state.addFlow(handleExecution(queryFn)));
|
|
45352
|
-
|
|
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)));
|
|
45353
45799
|
/**
|
|
45354
45800
|
* Register effects to emit lifecycle events for different query actions.
|
|
45355
45801
|
*
|
|
@@ -45402,6 +45848,7 @@ class QueryClient extends Observable {
|
|
|
45402
45848
|
*/
|
|
45403
45849
|
query(args, opt) {
|
|
45404
45850
|
const job = new QueryClientJob(this, args, opt);
|
|
45851
|
+
// share a single underlying job execution across all subscribers, replaying past emissions to late subscribers
|
|
45405
45852
|
const job$ = job.pipe(share({ connector: () => new ReplaySubject(), resetOnRefCountZero: false }));
|
|
45406
45853
|
Object.defineProperties(job$, {
|
|
45407
45854
|
transaction: { get: () => job.transaction },
|
|
@@ -45411,6 +45858,8 @@ class QueryClient extends Observable {
|
|
|
45411
45858
|
cancel: { value: (reason) => job.cancel(reason) },
|
|
45412
45859
|
complete: { value: () => job.complete() },
|
|
45413
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.
|
|
45414
45863
|
return job$;
|
|
45415
45864
|
}
|
|
45416
45865
|
/**
|
|
@@ -45439,7 +45888,10 @@ class QueryClient extends Observable {
|
|
|
45439
45888
|
* @returns The request matching the reference, or `undefined` if not found.
|
|
45440
45889
|
*/
|
|
45441
45890
|
getRequestByRef(ref) {
|
|
45442
|
-
|
|
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;
|
|
45443
45895
|
}
|
|
45444
45896
|
/**
|
|
45445
45897
|
* Cancels a task by its reference string. If a reason is provided, it will be included
|
|
@@ -45449,6 +45901,7 @@ class QueryClient extends Observable {
|
|
|
45449
45901
|
*/
|
|
45450
45902
|
cancelTaskByRef(ref, reason) {
|
|
45451
45903
|
const entry = this.getRequestByRef(ref);
|
|
45904
|
+
// Only cancel when a matching request was actually found
|
|
45452
45905
|
if (entry?.ref) {
|
|
45453
45906
|
this.cancel(entry.transaction, reason);
|
|
45454
45907
|
}
|
|
@@ -45460,6 +45913,7 @@ class QueryClient extends Observable {
|
|
|
45460
45913
|
* @param {string} [reason] The reason for cancellation.
|
|
45461
45914
|
*/
|
|
45462
45915
|
cancel(transaction, reason) {
|
|
45916
|
+
// No transaction targeted — cancel every tracked transaction
|
|
45463
45917
|
if (!transaction) {
|
|
45464
45918
|
// If no specific transaction is provided, iterate through all transactions
|
|
45465
45919
|
// in the state and cancel each one, applying a generic cancellation reason.
|
|
@@ -45504,7 +45958,7 @@ class QueryClient extends Observable {
|
|
|
45504
45958
|
* @template TArgs The type of the arguments associated with the cache entry.
|
|
45505
45959
|
* @returns An object containing the cache actions.
|
|
45506
45960
|
*/
|
|
45507
|
-
//
|
|
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
|
|
45508
45962
|
function createActions() {
|
|
45509
45963
|
return {
|
|
45510
45964
|
/**
|
|
@@ -45596,6 +46050,7 @@ function createReducer (actions, initial = {}) {
|
|
|
45596
46050
|
.addCase(actions.insert, (state, action) => {
|
|
45597
46051
|
const { key, entry } = action.payload;
|
|
45598
46052
|
const record = state[key];
|
|
46053
|
+
// Update the existing entry in place, or create a new one if none exists yet
|
|
45599
46054
|
if (record) {
|
|
45600
46055
|
// If the record exists, update the timestamp and increment the update count.
|
|
45601
46056
|
record.updated = Date.now();
|
|
@@ -45623,8 +46078,10 @@ function createReducer (actions, initial = {}) {
|
|
|
45623
46078
|
// Handles the 'invalidate' action to invalidate a cache record.
|
|
45624
46079
|
.addCase(actions.invalidate, (state, action) => {
|
|
45625
46080
|
const invalidKey = action.payload ? [action.payload] : Object.keys(state);
|
|
46081
|
+
// Invalidate either the single targeted key, or every key when none was specified
|
|
45626
46082
|
for (const key of invalidKey) {
|
|
45627
46083
|
const entry = state[key];
|
|
46084
|
+
// Only reset the timestamp when the record actually exists
|
|
45628
46085
|
if (entry) {
|
|
45629
46086
|
// reset the updated timestamp
|
|
45630
46087
|
entry.updated = undefined;
|
|
@@ -45635,6 +46092,7 @@ function createReducer (actions, initial = {}) {
|
|
|
45635
46092
|
.addCase(actions.mutate, (state, action) => {
|
|
45636
46093
|
const { key, value, updated } = action.payload;
|
|
45637
46094
|
const record = state[key];
|
|
46095
|
+
// Only apply the mutation when a record exists for this key
|
|
45638
46096
|
if (record) {
|
|
45639
46097
|
// Update the record with the new value and metadata.
|
|
45640
46098
|
record.value = value;
|
|
@@ -45660,8 +46118,10 @@ function createReducer (actions, initial = {}) {
|
|
|
45660
46118
|
.map(([key]) => key);
|
|
45661
46119
|
// Remove keys that are not in the list of valid keys.
|
|
45662
46120
|
if (currentKeys.length !== validKeys.length) {
|
|
46121
|
+
// Walk all current keys and drop the ones that were trimmed away
|
|
45663
46122
|
for (const key of currentKeys) {
|
|
45664
46123
|
const validKeyIndex = validKeys.indexOf(key);
|
|
46124
|
+
// The key survived trimming — remove it from the search list so later lookups are faster
|
|
45665
46125
|
if (validKeyIndex !== -1) {
|
|
45666
46126
|
// If the key is valid, remove it from the search list.
|
|
45667
46127
|
validKeys.splice(validKeyIndex, 1);
|
|
@@ -45773,6 +46233,7 @@ class QueryCache {
|
|
|
45773
46233
|
constructor(args) {
|
|
45774
46234
|
const { trimming, initial } = args ?? {};
|
|
45775
46235
|
this.#state = new FlowSubject(createReducer(actions, initial));
|
|
46236
|
+
// Only wire up automatic trimming when a trimming policy was provided
|
|
45776
46237
|
if (trimming) {
|
|
45777
46238
|
this.#state.addEffect('cache/set', () => this.#state.next(actions.trim(trimming)));
|
|
45778
46239
|
}
|
|
@@ -45820,6 +46281,7 @@ class QueryCache {
|
|
|
45820
46281
|
invalidate(key) {
|
|
45821
46282
|
const item = key ? this.#state.value[key] : undefined;
|
|
45822
46283
|
this.#state.next(actions.invalidate(key, item));
|
|
46284
|
+
// Only emit an event when a specific key was invalidated (not a full-cache clear)
|
|
45823
46285
|
if (key) {
|
|
45824
46286
|
this._registerEvent('query_cache_entry_invalidated', {
|
|
45825
46287
|
key,
|
|
@@ -45831,9 +46293,12 @@ class QueryCache {
|
|
|
45831
46293
|
* Mutates an item in the query cache by key.
|
|
45832
46294
|
* @param {string} key - The key of the item to mutate.
|
|
45833
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`.
|
|
45834
46298
|
*/
|
|
45835
46299
|
mutate(key, changes) {
|
|
45836
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
|
|
45837
46302
|
if (!current) {
|
|
45838
46303
|
throw new Error(`Cannot mutate cache item with key ${key}: item not found`);
|
|
45839
46304
|
}
|
|
@@ -45857,6 +46322,7 @@ class QueryCache {
|
|
|
45857
46322
|
const beforeKeys = new Set(Object.keys(this.#state.value));
|
|
45858
46323
|
this.#state.next(actions.trim(options));
|
|
45859
46324
|
const afterKeys = new Set(Object.keys(this.#state.value));
|
|
46325
|
+
// keys that existed before trimming but not after were the ones removed
|
|
45860
46326
|
const removedKeys = Array.from(beforeKeys).filter((key) => !afterKeys.has(key));
|
|
45861
46327
|
this._registerEvent('query_cache_trimmed', {
|
|
45862
46328
|
data: {
|
|
@@ -45926,6 +46392,7 @@ class QueryTask extends Subject {
|
|
|
45926
46392
|
#created = Date.now();
|
|
45927
46393
|
/**
|
|
45928
46394
|
* Gets the creation timestamp of the query task.
|
|
46395
|
+
* @returns The Unix timestamp (in milliseconds) when the task was created.
|
|
45929
46396
|
*/
|
|
45930
46397
|
get created() {
|
|
45931
46398
|
return this.#created;
|
|
@@ -45933,6 +46400,7 @@ class QueryTask extends Subject {
|
|
|
45933
46400
|
#uuid = v4();
|
|
45934
46401
|
/**
|
|
45935
46402
|
* Gets the unique identifier of the query task.
|
|
46403
|
+
* @returns The task's generated UUID.
|
|
45936
46404
|
*/
|
|
45937
46405
|
get uuid() {
|
|
45938
46406
|
return this.#uuid;
|
|
@@ -45958,6 +46426,7 @@ class QueryTask extends Subject {
|
|
|
45958
46426
|
* @returns A subscription to the job's result.
|
|
45959
46427
|
*/
|
|
45960
46428
|
processJob(job) {
|
|
46429
|
+
// map the job's completion into a QueryTaskCompleted record for subscribers
|
|
45961
46430
|
return job
|
|
45962
46431
|
.pipe(map$1((result) => {
|
|
45963
46432
|
const { key, uuid, created } = this;
|
|
@@ -45990,6 +46459,13 @@ class QueryEvent {
|
|
|
45990
46459
|
type;
|
|
45991
46460
|
key;
|
|
45992
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
|
+
*/
|
|
45993
46469
|
constructor(type, key, data) {
|
|
45994
46470
|
this.type = type;
|
|
45995
46471
|
this.key = key;
|
|
@@ -46027,10 +46503,12 @@ const defaultCacheValidator = (expires = 0) => (entry) => (entry.updated ?? 0) +
|
|
|
46027
46503
|
* @returns A function that takes a query request and returns an Observable representing the queued request.
|
|
46028
46504
|
*/
|
|
46029
46505
|
const getQueueOperator = (type = 'switch') => {
|
|
46506
|
+
// A custom queue function was provided, use it directly
|
|
46030
46507
|
if (typeof type === 'function') {
|
|
46031
46508
|
return type;
|
|
46032
46509
|
}
|
|
46033
46510
|
return (() => {
|
|
46511
|
+
// Map the named operator type to its concrete implementation
|
|
46034
46512
|
switch (type) {
|
|
46035
46513
|
case 'concat':
|
|
46036
46514
|
return concatQueue;
|
|
@@ -46101,7 +46579,6 @@ const getQueueOperator = (type = 'switch') => {
|
|
|
46101
46579
|
* @see {@link Query.invalidate} for more details on invalidating cache entries.
|
|
46102
46580
|
* @see {@link QueueOperatorType} for more details on the available queue operators.
|
|
46103
46581
|
*/
|
|
46104
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46105
46582
|
class Query {
|
|
46106
46583
|
/**
|
|
46107
46584
|
* Static utility that extracts the raw value from a query result Observable.
|
|
@@ -46161,16 +46638,19 @@ class Query {
|
|
|
46161
46638
|
#event$;
|
|
46162
46639
|
/**
|
|
46163
46640
|
* A public getter for the client instance.
|
|
46164
|
-
* TODO: Implement a proxy to control access to the client.
|
|
46641
|
+
* TODO(#5144): Implement a proxy to control access to the client.
|
|
46165
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.
|
|
46166
46644
|
*/
|
|
46167
46645
|
get client() {
|
|
46168
|
-
// TODO: Proxy
|
|
46646
|
+
// TODO(#5144): Proxy
|
|
46169
46647
|
return this.#client;
|
|
46170
46648
|
}
|
|
46171
46649
|
/**
|
|
46172
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`.
|
|
46173
46652
|
* @param type - The event type
|
|
46653
|
+
* @param key - The cache/query key the event relates to
|
|
46174
46654
|
* @param data - The event data
|
|
46175
46655
|
*/
|
|
46176
46656
|
_registerEvent(type, key, data) {
|
|
@@ -46178,11 +46658,12 @@ class Query {
|
|
|
46178
46658
|
}
|
|
46179
46659
|
/**
|
|
46180
46660
|
* A public getter for the cache instance.
|
|
46181
|
-
* TODO: Implement a proxy to control access to the cache.
|
|
46661
|
+
* TODO(#5144): Implement a proxy to control access to the cache.
|
|
46182
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.
|
|
46183
46664
|
*/
|
|
46184
46665
|
get cache() {
|
|
46185
|
-
// TODO: Proxy
|
|
46666
|
+
// TODO(#5144): Proxy
|
|
46186
46667
|
return this.#cache;
|
|
46187
46668
|
}
|
|
46188
46669
|
/**
|
|
@@ -46247,6 +46728,7 @@ class Query {
|
|
|
46247
46728
|
// shutdown the queue when the query is completed
|
|
46248
46729
|
this.#subscription.add(() => this.#queue$.complete());
|
|
46249
46730
|
this.#subscription.add(this.#queue$
|
|
46731
|
+
// register queued events, then apply the queue operator to the ongoing tasks
|
|
46250
46732
|
.pipe(tap((key) => {
|
|
46251
46733
|
this._registerEvent('query_queued', key);
|
|
46252
46734
|
}),
|
|
@@ -46376,9 +46858,11 @@ class Query {
|
|
|
46376
46858
|
const { skipResolve, ...args } = opt || {};
|
|
46377
46859
|
const fn = skipResolve ? firstValueFrom : lastValueFrom;
|
|
46378
46860
|
return new Promise((resolve, reject) => {
|
|
46861
|
+
// Reject the promise early if the caller aborts via the signal
|
|
46379
46862
|
if (opt?.signal) {
|
|
46380
46863
|
opt.signal.addEventListener('abort', () => reject(new Error('Query aborted')));
|
|
46381
46864
|
}
|
|
46865
|
+
// Throw if the query completes without emitting a value
|
|
46382
46866
|
fn(this._query(payload, args).pipe(throwIfEmpty())).then(resolve, reject);
|
|
46383
46867
|
});
|
|
46384
46868
|
}
|
|
@@ -46400,7 +46884,7 @@ class Query {
|
|
|
46400
46884
|
persistentQuery(args, options) {
|
|
46401
46885
|
const key = this.#generateCacheKey(args);
|
|
46402
46886
|
const original = this._query(args, options);
|
|
46403
|
-
|
|
46887
|
+
const cacheAwareStream = new Observable((subscriber) => {
|
|
46404
46888
|
subscriber.add(original.subscribe({
|
|
46405
46889
|
next: subscriber.next.bind(subscriber),
|
|
46406
46890
|
error: subscriber.error.bind(subscriber),
|
|
@@ -46409,6 +46893,7 @@ class Query {
|
|
|
46409
46893
|
const validateCache = options?.cache?.validate || this.#validateCacheEntry;
|
|
46410
46894
|
// Subscribe to the cache state and filter for the specific cache entry based on the key.
|
|
46411
46895
|
subscriber.add(this.cache.state$
|
|
46896
|
+
// narrow the cache state stream down to just this key's entry
|
|
46412
46897
|
.pipe(filter((x) => key in x), map$1((x) => ({
|
|
46413
46898
|
...x[key],
|
|
46414
46899
|
key,
|
|
@@ -46416,9 +46901,9 @@ class Query {
|
|
|
46416
46901
|
hasValidCache: validateCache(x[key], args),
|
|
46417
46902
|
})))
|
|
46418
46903
|
.subscribe(subscriber));
|
|
46419
|
-
})
|
|
46904
|
+
});
|
|
46420
46905
|
// only emit when the transaction changes
|
|
46421
|
-
distinctUntilChanged((a, b) => a.transaction === b.transaction &&
|
|
46906
|
+
return cacheAwareStream.pipe(distinctUntilChanged((a, b) => a.transaction === b.transaction &&
|
|
46422
46907
|
a.mutated === b.mutated));
|
|
46423
46908
|
}
|
|
46424
46909
|
/**
|
|
@@ -46438,10 +46923,15 @@ class Query {
|
|
|
46438
46923
|
*
|
|
46439
46924
|
* @param args - The arguments that identify the specific cache entry to be mutated.
|
|
46440
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`.
|
|
46441
46929
|
*/
|
|
46442
46930
|
mutate(args, changes, options) {
|
|
46443
46931
|
const key = this.#generateCacheKey(args);
|
|
46932
|
+
// No existing cache entry for this key — decide whether to create one or fail
|
|
46444
46933
|
if (key in this.cache.state === false) {
|
|
46934
|
+
// No explicit allowCreation setting provided — fail closed rather than silently creating
|
|
46445
46935
|
if (options?.allowCreation === undefined) {
|
|
46446
46936
|
throw new Error(`Cannot mutate cache item with key ${key}: item not found and option "allowCreation" is false`);
|
|
46447
46937
|
}
|
|
@@ -46489,6 +46979,7 @@ class Query {
|
|
|
46489
46979
|
* @returns A function that, when called, will unsubscribe the callback from further invalidation events.
|
|
46490
46980
|
*/
|
|
46491
46981
|
onInvalidate(cb) {
|
|
46982
|
+
// only forward cache-invalidation actions to the callback
|
|
46492
46983
|
const subscription = this.#cache.action$
|
|
46493
46984
|
.pipe(filterAction('cache/invalidate'))
|
|
46494
46985
|
.subscribe((action) => cb({ detail: { item: action.meta.item } }));
|
|
@@ -46503,6 +46994,7 @@ class Query {
|
|
|
46503
46994
|
* @returns A function that, when called, will unsubscribe the callback from further mutation events.
|
|
46504
46995
|
*/
|
|
46505
46996
|
onMutate(cb) {
|
|
46997
|
+
// only forward cache-mutation actions to the callback
|
|
46506
46998
|
const subscription = this.#cache.action$
|
|
46507
46999
|
.pipe(filterAction('cache/mutate'))
|
|
46508
47000
|
.subscribe((action) => cb({ detail: { changes: action.payload, current: action.meta.item } }));
|
|
@@ -46560,7 +47052,9 @@ class Query {
|
|
|
46560
47052
|
_createTask(key, args, options) {
|
|
46561
47053
|
// Create a new Observable that represents the query task and will be returned to the caller.
|
|
46562
47054
|
return new Observable((subscriber) => {
|
|
47055
|
+
// Support cancellation via an AbortSignal
|
|
46563
47056
|
if (options?.signal) {
|
|
47057
|
+
// Already aborted before subscription — complete immediately without doing any work
|
|
46564
47058
|
if (options?.signal.aborted) {
|
|
46565
47059
|
this._registerEvent('query_aborted', key);
|
|
46566
47060
|
return subscriber.complete();
|
|
@@ -46592,6 +47086,7 @@ class Query {
|
|
|
46592
47086
|
// the status indicating that this is a cached response, and a flag indicating whether the cache
|
|
46593
47087
|
// is considered valid based on the validation logic.
|
|
46594
47088
|
subscriber.next(record);
|
|
47089
|
+
// Complete without fetching when the cache is valid, or invalid entries are intentionally suppressed
|
|
46595
47090
|
if (hasValidCache || suppressInvalid) {
|
|
46596
47091
|
this._registerEvent('query_completed', key, { data: cacheEntry.value, hasValidCache });
|
|
46597
47092
|
// If the cache is valid, or if invalid cache entries should be suppressed (not re-fetched),
|
|
@@ -46605,6 +47100,7 @@ class Query {
|
|
|
46605
47100
|
}
|
|
46606
47101
|
// If the cache entry does not exist or is invalid, proceed to queue a new query request.
|
|
46607
47102
|
const isExistingTask = key in this.#tasks;
|
|
47103
|
+
// No task is already tracking this key — create one and register it before queuing
|
|
46608
47104
|
if (!isExistingTask) {
|
|
46609
47105
|
this.#tasks[key] = new QueryTask(key, args, options);
|
|
46610
47106
|
this._registerEvent('query_job_created', key, {
|
|
@@ -46683,7 +47179,9 @@ const serviceResponseSelector = (response, postProcess) => {
|
|
|
46683
47179
|
const result$ = from(jsonSelector(response)).pipe(
|
|
46684
47180
|
// parse and validate the response
|
|
46685
47181
|
map$1((value) => ApiServices.default([]).parse(value)));
|
|
47182
|
+
// Only apply a transform pipeline when the caller supplied one
|
|
46686
47183
|
if (postProcess) {
|
|
47184
|
+
// Apply the caller-supplied operator (e.g. session overrides) before emitting
|
|
46687
47185
|
return result$.pipe(postProcess);
|
|
46688
47186
|
}
|
|
46689
47187
|
return result$;
|
|
@@ -46734,7 +47232,9 @@ class ServiceDiscoveryClient {
|
|
|
46734
47232
|
/** {@inheritDoc IServiceDiscoveryClient.resolveService} */
|
|
46735
47233
|
async resolveService(key, allow_cache) {
|
|
46736
47234
|
const services = await this.resolveServices(allow_cache);
|
|
47235
|
+
// Locate the service matching the requested key
|
|
46737
47236
|
const service = services.find((s) => s.key === key);
|
|
47237
|
+
// Fail loudly when the requested key doesn't resolve to a known service
|
|
46738
47238
|
if (!service) {
|
|
46739
47239
|
throw Error(`Failed to resolve service, invalid key [${key}]`);
|
|
46740
47240
|
}
|
|
@@ -46775,6 +47275,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46775
47275
|
* @throws {Error} When the HTTP module is not registered.
|
|
46776
47276
|
*/
|
|
46777
47277
|
async _createConfig(init, initial) {
|
|
47278
|
+
// The service discovery client depends on the http module being registered
|
|
46778
47279
|
if (!init.hasModule('http')) {
|
|
46779
47280
|
throw new Error('http module is required');
|
|
46780
47281
|
}
|
|
@@ -46782,6 +47283,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46782
47283
|
if (!this._has('discoveryClient')) {
|
|
46783
47284
|
// check if http module has a client with key 'service_discovery'
|
|
46784
47285
|
const httpProvider = await init.requireInstance('http');
|
|
47286
|
+
// Auto-configure only when a matching http client has actually been registered
|
|
46785
47287
|
if (httpProvider.hasClient('service_discovery')) {
|
|
46786
47288
|
this.configureServiceDiscoveryClientByClientKey('service_discovery');
|
|
46787
47289
|
}
|
|
@@ -46798,6 +47300,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46798
47300
|
* @throws {Error} When `discoveryClient` has not been configured.
|
|
46799
47301
|
*/
|
|
46800
47302
|
_processConfig(config, init) {
|
|
47303
|
+
// A discovery client is mandatory before the config can be considered complete
|
|
46801
47304
|
if (!config.discoveryClient) {
|
|
46802
47305
|
throw new Error('discoveryClient is required, please configure it');
|
|
46803
47306
|
}
|
|
@@ -46852,6 +47355,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46852
47355
|
configureServiceDiscoveryClient(configCallback) {
|
|
46853
47356
|
this.setServiceDiscoveryClient(async (args) => {
|
|
46854
47357
|
const { httpClient, endpoint } = (await lastValueFrom(from(configCallback(args)))) ?? {};
|
|
47358
|
+
// Only construct the client when the factory actually returned an http client
|
|
46855
47359
|
if (httpClient) {
|
|
46856
47360
|
return new ServiceDiscoveryClient({
|
|
46857
47361
|
http: httpClient,
|
|
@@ -46867,7 +47371,9 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46867
47371
|
// Check if there are any session overrides in session storage.
|
|
46868
47372
|
try {
|
|
46869
47373
|
const sessionOverrides = JSON.parse(storage.getItem('overriddenServiceDiscoveryUrls') || '{}');
|
|
47374
|
+
// Apply each session override onto the service matching its key
|
|
46870
47375
|
for (const [key, { url, scopes }] of Object.entries(sessionOverrides)) {
|
|
47376
|
+
// Locate the service this override applies to
|
|
46871
47377
|
const service = input.find((service) => service.key === key);
|
|
46872
47378
|
// If the service can be found, override the values with the values
|
|
46873
47379
|
// from session override.
|
|
@@ -46879,6 +47385,8 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46879
47385
|
}
|
|
46880
47386
|
}
|
|
46881
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.
|
|
46882
47390
|
console.error('Failed to JSON parse session overrides: "overriddenServiceDiscoveryUrls"', e);
|
|
46883
47391
|
}
|
|
46884
47392
|
return input;
|
|
@@ -46921,7 +47429,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
|
|
|
46921
47429
|
}
|
|
46922
47430
|
|
|
46923
47431
|
// Generated by genversion.
|
|
46924
|
-
const version$1 = '10.0.
|
|
47432
|
+
const version$1 = '10.0.2';
|
|
46925
47433
|
|
|
46926
47434
|
/**
|
|
46927
47435
|
* Default implementation of {@link IServiceDiscoveryProvider}.
|
|
@@ -46956,6 +47464,7 @@ class ServiceDiscoveryProvider extends BaseModuleProvider {
|
|
|
46956
47464
|
/** {@inheritDoc IServiceDiscoveryProvider.createClient} */
|
|
46957
47465
|
async createClient(name, opt) {
|
|
46958
47466
|
const service = await this.resolveService(name);
|
|
47467
|
+
// A missing service configuration means the http client can't be constructed
|
|
46959
47468
|
if (!service) {
|
|
46960
47469
|
throw Error(`Could not load configuration of service [${name}]`);
|
|
46961
47470
|
}
|
|
@@ -47135,7 +47644,7 @@ class BaseTelemetryAdapter {
|
|
|
47135
47644
|
* without performing any initialization. It calls the protected `_initialize` method which
|
|
47136
47645
|
* subclasses can override to provide custom initialization logic.
|
|
47137
47646
|
*
|
|
47138
|
-
* TODO: Consider changing return type from Promise<void> to Promise<TelemetryItem[]>
|
|
47647
|
+
* TODO(#5112): Consider changing return type from Promise<void> to Promise<TelemetryItem[]>
|
|
47139
47648
|
* to allow adapters to return telemetry items (errors/warnings) that occurred during
|
|
47140
47649
|
* initialization, eliminating the chicken-and-egg problem with reporting init errors.
|
|
47141
47650
|
*
|
|
@@ -47202,6 +47711,7 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47202
47711
|
* @returns CSS background color string
|
|
47203
47712
|
*/
|
|
47204
47713
|
_generateTitleBackground(lvl) {
|
|
47714
|
+
// Pick a background color matching the severity of this telemetry level
|
|
47205
47715
|
switch (lvl) {
|
|
47206
47716
|
case TelemetryLevel.Critical:
|
|
47207
47717
|
return 'rgb(255, 0, 0)';
|
|
@@ -47228,12 +47738,12 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47228
47738
|
* @returns Formatted time string (ms, s, or m s format)
|
|
47229
47739
|
*/
|
|
47230
47740
|
_formatMetric = (value) => {
|
|
47741
|
+
// Show milliseconds for values under 1 second
|
|
47231
47742
|
if (value < 1000) {
|
|
47232
|
-
// Show milliseconds for values under 1 second
|
|
47233
47743
|
return `${Math.round(value)}ms`;
|
|
47234
47744
|
}
|
|
47745
|
+
// Show seconds with 1 decimal for values under 1 minute
|
|
47235
47746
|
if (value < 60000) {
|
|
47236
|
-
// Show seconds with 1 decimal for values under 1 minute
|
|
47237
47747
|
return `${(value / 1000).toFixed(1)}s`;
|
|
47238
47748
|
}
|
|
47239
47749
|
// For longer durations, show minutes and seconds
|
|
@@ -47275,6 +47785,7 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47275
47785
|
metadata,
|
|
47276
47786
|
scope,
|
|
47277
47787
|
};
|
|
47788
|
+
// Dispatch to the right console format based on the telemetry item's type
|
|
47278
47789
|
switch (type) {
|
|
47279
47790
|
case TelemetryType.Event:
|
|
47280
47791
|
// Log events with base data
|
|
@@ -47301,6 +47812,7 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47301
47812
|
* @param msg - Additional message arguments to log
|
|
47302
47813
|
*/
|
|
47303
47814
|
_log(lvl, title, ...msg) {
|
|
47815
|
+
// Pick the console method matching this telemetry level's severity
|
|
47304
47816
|
switch (lvl) {
|
|
47305
47817
|
case TelemetryLevel.Critical:
|
|
47306
47818
|
case TelemetryLevel.Error:
|
|
@@ -47334,10 +47846,15 @@ class ConsoleAdapter extends BaseTelemetryAdapter {
|
|
|
47334
47846
|
*/
|
|
47335
47847
|
const createPortalEntryPoint = (...segments) => {
|
|
47336
47848
|
const normalized = segments
|
|
47849
|
+
// Drop undefined/null segments before further processing
|
|
47337
47850
|
.filter((segment) => segment !== undefined && segment !== null)
|
|
47851
|
+
// Trim stray whitespace from each segment
|
|
47338
47852
|
.map((segment) => segment.trim())
|
|
47853
|
+
// Drop any segments that became empty after trimming
|
|
47339
47854
|
.filter(Boolean)
|
|
47855
|
+
// Strip leading slashes from every segment (and trailing slashes from the first)
|
|
47340
47856
|
.map((segment, index) => {
|
|
47857
|
+
// The first segment may have a trailing slash (e.g. a base URL) that needs stripping
|
|
47341
47858
|
if (index === 0) {
|
|
47342
47859
|
return segment.replace(/\/+$/g, '');
|
|
47343
47860
|
}
|
|
@@ -47378,6 +47895,7 @@ const createPortalEntryPoint = (...segments) => {
|
|
|
47378
47895
|
*/
|
|
47379
47896
|
async function registerServiceWorker(framework) {
|
|
47380
47897
|
const telemetry = framework.telemetry;
|
|
47898
|
+
// Bail out early when the browser has no service worker support at all
|
|
47381
47899
|
if ('serviceWorker' in navigator === false) {
|
|
47382
47900
|
const exception = new Error('Service workers are not supported in this browser.');
|
|
47383
47901
|
exception.name = 'ServiceWorkerNotSupported';
|
|
@@ -47388,6 +47906,7 @@ async function registerServiceWorker(framework) {
|
|
|
47388
47906
|
throw exception;
|
|
47389
47907
|
}
|
|
47390
47908
|
const resourceConfigs = import.meta.env.FUSION_SPA_SERVICE_WORKER_RESOURCES;
|
|
47909
|
+
// The worker needs a resource config to know which requests to intercept
|
|
47391
47910
|
if (!resourceConfigs) {
|
|
47392
47911
|
const exception = new Error('Service worker config is not defined.');
|
|
47393
47912
|
exception.name = 'ServiceWorkerConfigNotDefined';
|
|
@@ -47411,10 +47930,12 @@ async function registerServiceWorker(framework) {
|
|
|
47411
47930
|
navigator.serviceWorker.startMessages();
|
|
47412
47931
|
// listen for messages from the service worker (set up before registration)
|
|
47413
47932
|
navigator.serviceWorker.addEventListener('message', async (event) => {
|
|
47933
|
+
// Only the GET_TOKEN message type requests a token from this handler
|
|
47414
47934
|
if (event.data.type === 'GET_TOKEN') {
|
|
47415
47935
|
try {
|
|
47416
47936
|
// extract scopes from the event data
|
|
47417
47937
|
const scopes = event.data.scopes;
|
|
47938
|
+
// Scopes must be a real array before they can be used to request a token
|
|
47418
47939
|
if (!scopes || !Array.isArray(scopes)) {
|
|
47419
47940
|
const error = new Error('Invalid scopes provided');
|
|
47420
47941
|
error.name = 'InvalidScopesProvided';
|
|
@@ -47422,6 +47943,7 @@ async function registerServiceWorker(framework) {
|
|
|
47422
47943
|
}
|
|
47423
47944
|
// request a token from the MSAL module
|
|
47424
47945
|
const token = await framework.auth.acquireToken({ request: { scopes } });
|
|
47946
|
+
// A missing token means acquisition failed and the worker can't proceed
|
|
47425
47947
|
if (!token) {
|
|
47426
47948
|
const error = new Error('Failed to acquire token');
|
|
47427
47949
|
error.name = 'FailedToAcquireToken';
|
|
@@ -47467,9 +47989,11 @@ async function registerServiceWorker(framework) {
|
|
|
47467
47989
|
if (registration.waiting) {
|
|
47468
47990
|
sendConfigToServiceWorker(registration.waiting);
|
|
47469
47991
|
}
|
|
47992
|
+
// A worker still installing needs to reach the 'activated' state before it can receive config
|
|
47470
47993
|
if (registration.installing) {
|
|
47471
47994
|
registration.installing.addEventListener('statechange', (event) => {
|
|
47472
47995
|
const worker = event.target;
|
|
47996
|
+
// Only send config once the worker has fully activated
|
|
47473
47997
|
if (worker.state === 'activated') {
|
|
47474
47998
|
sendConfigToServiceWorker(worker);
|
|
47475
47999
|
}
|
|
@@ -47477,6 +48001,7 @@ async function registerServiceWorker(framework) {
|
|
|
47477
48001
|
}
|
|
47478
48002
|
// Listen for controller changes (happens during hard refresh or updates)
|
|
47479
48003
|
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
48004
|
+
// Only send config once this page is actually controlled by a worker
|
|
47480
48005
|
if (navigator.serviceWorker.controller) {
|
|
47481
48006
|
sendConfigToServiceWorker(navigator.serviceWorker.controller);
|
|
47482
48007
|
}
|
|
@@ -47490,6 +48015,7 @@ async function registerServiceWorker(framework) {
|
|
|
47490
48015
|
});
|
|
47491
48016
|
// ensure we have an active service worker before sending config
|
|
47492
48017
|
const activeWorker = readyRegistration.active;
|
|
48018
|
+
// Without an active worker there's nothing to send config to
|
|
47493
48019
|
if (!activeWorker) {
|
|
47494
48020
|
console.error('[Service Worker Registration] Service worker is not active after ready state');
|
|
47495
48021
|
return;
|
|
@@ -47509,6 +48035,7 @@ async function registerServiceWorker(framework) {
|
|
|
47509
48035
|
navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
|
|
47510
48036
|
// Polling fallback and timeout to prevent infinite waiting
|
|
47511
48037
|
checkInterval = setInterval(() => {
|
|
48038
|
+
// Stop polling once a controller has taken over
|
|
47512
48039
|
if (navigator.serviceWorker.controller)
|
|
47513
48040
|
finish();
|
|
47514
48041
|
}, 200);
|
|
@@ -47536,7 +48063,7 @@ async function registerServiceWorker(framework) {
|
|
|
47536
48063
|
}
|
|
47537
48064
|
|
|
47538
48065
|
// Generated by genversion.
|
|
47539
|
-
const version = '4.0.
|
|
48066
|
+
const version = '4.0.14';
|
|
47540
48067
|
|
|
47541
48068
|
// Allow dynamic import without vite
|
|
47542
48069
|
const importWithoutVite = (path) => import(/* @vite-ignore */ path);
|
|
@@ -47582,6 +48109,7 @@ enableTelemetry(configurator, {
|
|
|
47582
48109
|
},
|
|
47583
48110
|
// biome-ignore lint/suspicious/noExplicitAny: we need to use any here to allow dynamic properties
|
|
47584
48111
|
};
|
|
48112
|
+
// Only attach user metadata when the auth module has resolved an account
|
|
47585
48113
|
if (modules?.auth) {
|
|
47586
48114
|
metadata.fusion.user = {
|
|
47587
48115
|
id: modules.auth.account?.homeAccountId,
|
|
@@ -47646,8 +48174,8 @@ enableTelemetry(configurator, {
|
|
|
47646
48174
|
document.body.innerHTML = '';
|
|
47647
48175
|
document.body.appendChild(el);
|
|
47648
48176
|
const portalEntryPoint = createPortalEntryPoint(portalProxy ? '/portal-proxy' : undefined, portal_manifest.build.assetPath, portal_manifest.build.templateEntry);
|
|
47649
|
-
//
|
|
47650
|
-
//
|
|
48177
|
+
// TODO(#5066): should test if the entrypoint is external or internal
|
|
48178
|
+
// TODO(#5066): add proper return type
|
|
47651
48179
|
const { render } = await measurement
|
|
47652
48180
|
.clone()
|
|
47653
48181
|
.resolve(importWithoutVite(portalEntryPoint), {
|