@equinor/fusion-framework-vite-plugin-spa 5.2.0 → 5.2.2

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.
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export const version = '5.2.0';
2
+ export const version = '5.2.2';
3
3
  //# sourceMappingURL=version.js.map
@@ -1,4 +1,4 @@
1
- import { c as createErrorClass, _ as __extends, e as errorContext, E as EMPTY_SUBSCRIPTION, S as Subscription, a as arrRemove, O as Observable, b as __values, d as __spreadArray, f as __read, i as isFunction$1, g as SafeSubscriber, h as EmptyError, o as operate, j as executeSchedule, k as innerFrom, l as createOperatorSubscriber, m as map, n as mergeMap, p as identity, q as isArrayLike, r as isScheduler, s as popScheduler, t as popNumber, u as EMPTY, v as from, w as mergeInternals, x as scanInternals, y as noop, z as popResultSelector, A as reduce, B as lastValueFrom, M as ModuleEventLevel, C as filter, D as BaseModuleProvider, F as catchError, G as of, H as object, I as _instanceof, J as _enum, K as string, L as boolean, N as record, P as union, Q as __rest, R as __assign, T as switchMap, U as observeOn, V as deepmerge, W as BaseConfigBuilder, X as TelemetryExceptionSchema, Y as TelemetryItemNames, Z as TelemetryType, $ as TelemetryEventSchema, a0 as TelemetryMetricSchema, a1 as TelemetryCustomEventSchema, a2 as array, a3 as TelemetryLevel, a4 as enableMSAL } from '../module-Xg35JJRU.js';
1
+ import { c as createErrorClass, _ as __extends, e as errorContext, E as EMPTY_SUBSCRIPTION, S as Subscription, O as Observable, a as __values, b as arrRemove, d as __spreadArray, f as __read, i as isFunction$1, g as SafeSubscriber, h as EmptyError, o as operate, j as executeSchedule, k as innerFrom, l as createOperatorSubscriber, m as map, n as mergeMap, p as identity, q as isArrayLike, r as isScheduler, s as popScheduler, t as popNumber, u as EMPTY, v as from, w as mergeInternals, x as scanInternals, y as noop, z as popResultSelector, A as reduce, B as lastValueFrom, M as ModuleEventLevel, C as filter, D as BaseModuleProvider, F as catchError, G as of, H as object, I as _instanceof, J as _enum, K as string, L as boolean, N as record, P as union, Q as __rest, R as __assign, T as switchMap, U as observeOn, V as deepmerge, W as BaseConfigBuilder, X as TelemetryExceptionSchema, Y as TelemetryItemNames, Z as TelemetryType, $ as TelemetryEventSchema, a0 as TelemetryMetricSchema, a1 as TelemetryCustomEventSchema, a2 as array, a3 as TelemetryLevel, a4 as enableMSAL } from '../module-BR47HvRl.js';
2
2
  import { B as BaseTelemetryAdapter } from '../TelemetryAdapter-B6JH_RXA.js';
3
3
 
4
4
  var ObjectUnsubscribedError = createErrorClass(function (_super) {
@@ -4274,7 +4274,7 @@ var objectTraps = {
4274
4274
  const currentState = current2?.[DRAFT_STATE];
4275
4275
  if (currentState && currentState.base_ === value) {
4276
4276
  state.copy_[prop] = value;
4277
- state.assigned_.set(prop, false);
4277
+ state.assigned_.delete(prop);
4278
4278
  return true;
4279
4279
  }
4280
4280
  if (is(value, current2) && (value !== void 0 || has(state.base_, prop, state.type_)))
@@ -5355,7 +5355,7 @@ class TelemetryConfigurator extends BaseConfigBuilder {
5355
5355
  }
5356
5356
 
5357
5357
  // Generated by genversion.
5358
- const version$2 = '7.2.0';
5358
+ const version$2 = '7.2.1';
5359
5359
 
5360
5360
  /**
5361
5361
  * Represents a framework event with customizable details and behavior.
@@ -8498,6 +8498,51 @@ class Query {
8498
8498
  }
8499
8499
  }
8500
8500
 
8501
+ /**
8502
+ * Error thrown by {@link ServiceDiscoveryClient} while service discovery is in a
8503
+ * failure cooldown and no recent service list is available as a fallback.
8504
+ *
8505
+ * After a failed service discovery request the client pauses outgoing requests
8506
+ * for a growing backoff period instead of calling the API again immediately.
8507
+ * Calls made during that pause fail fast with this error, so a misbehaving caller
8508
+ * (retry loop, reload loop, render loop) cannot flood the service discovery API.
8509
+ *
8510
+ * @remarks
8511
+ * Use `cause` to inspect the original failure (for example an
8512
+ * `HttpJsonResponseError` with status `401`) and `retryAt` to decide when it is
8513
+ * worth trying again.
8514
+ *
8515
+ * @example
8516
+ * ```ts
8517
+ * try {
8518
+ * await serviceDiscovery.resolveService('people');
8519
+ * } catch (error) {
8520
+ * if (error instanceof ServiceDiscoveryUnavailableError) {
8521
+ * console.warn(`service discovery paused until ${new Date(error.retryAt).toISOString()}`);
8522
+ * }
8523
+ * }
8524
+ * ```
8525
+ */
8526
+ class ServiceDiscoveryUnavailableError extends Error {
8527
+ /** Epoch milliseconds when the client will attempt a new service discovery request. */
8528
+ retryAt;
8529
+ /** Number of consecutive failed service discovery requests in the current failure streak. */
8530
+ attempt;
8531
+ /**
8532
+ * @param args - Failure details for the current cooldown.
8533
+ * @param args.cause - The error from the last failed service discovery request.
8534
+ * @param args.retryAt - Epoch milliseconds when requests resume.
8535
+ * @param args.attempt - Number of consecutive failures in the current streak.
8536
+ */
8537
+ constructor(args) {
8538
+ const seconds = Math.max(0, Math.ceil((args.retryAt - Date.now()) / 1000));
8539
+ super(`Service discovery is unavailable after ${args.attempt} failed attempt(s); requests are paused for ${seconds}s`, { cause: args.cause });
8540
+ this.name = 'ServiceDiscoveryUnavailableError';
8541
+ this.retryAt = args.retryAt;
8542
+ this.attempt = args.attempt;
8543
+ }
8544
+ }
8545
+
8501
8546
  /**
8502
8547
  * Zod schema for a single service object returned by the service
8503
8548
  * discovery API.
@@ -8535,6 +8580,12 @@ const ApiServices = array(ApiService)
8535
8580
 
8536
8581
  /** Cache key used by the internal {@link Query} instance. */
8537
8582
  const queryKey = 'services';
8583
+ /** Pause after the first failed request; doubles per consecutive failure. */
8584
+ const failureBackoffInitialMs = 1_000;
8585
+ /** Upper bound for the pause between failed requests. */
8586
+ const failureBackoffMaxMs = 30_000;
8587
+ /** Maximum age of the last successful service list that may be returned when a refresh fails. */
8588
+ const staleIfErrorMs = 24 * 60 * 60 * 1_000;
8538
8589
  /**
8539
8590
  * Transforms a fetch {@link Response} into an observable that emits a
8540
8591
  * validated array of {@link Service} objects.
@@ -8568,9 +8619,20 @@ const serviceResponseSelector = (response, postProcess) => {
8568
8619
  * Results are cached for 5 minutes. When `allow_cache` is `true`,
8569
8620
  * {@link resolveServices} returns the first cached value immediately;
8570
8621
  * otherwise it waits for the latest API response.
8622
+ *
8623
+ * Failures are hardened so a broken caller cannot flood the service discovery API:
8624
+ * - After a failed request the client pauses outgoing requests with exponential
8625
+ * backoff (1s, 2s, 4s … capped at 30s). The pause resets on the first
8626
+ * successful response.
8627
+ * - When a refresh fails, or a call arrives during the pause, the last successful
8628
+ * service list is returned if it is younger than 24 hours.
8629
+ * - Without a usable fallback, the request that failed rethrows the original error,
8630
+ * and calls made during the pause throw {@link ServiceDiscoveryUnavailableError}.
8571
8631
  */
8572
8632
  class ServiceDiscoveryClient {
8573
8633
  #query;
8634
+ #failure;
8635
+ #lastKnownGood;
8574
8636
  /** Sub-path appended to the HTTP client's base URI when fetching services. */
8575
8637
  endpoint;
8576
8638
  /** HTTP client used to call the service discovery API. */
@@ -8596,11 +8658,75 @@ class ServiceDiscoveryClient {
8596
8658
  });
8597
8659
  }
8598
8660
  /** {@inheritDoc IServiceDiscoveryClient.resolveServices} */
8599
- resolveServices(allow_cache) {
8600
- // firstValueFrom returns the cached snapshot; lastValueFrom waits for
8601
- // the freshest value from the query
8602
- const fn = allow_cache ? firstValueFrom : lastValueFrom;
8603
- return fn(Query.extractQueryValue(this.#query.query()));
8661
+ async resolveServices(allow_cache) {
8662
+ // Fail fast while cooling down so retry/reload/render loops never reach the network
8663
+ if (this.#failure && Date.now() < this.#failure.retryAt) {
8664
+ return this.#resolveFallback(new ServiceDiscoveryUnavailableError({
8665
+ cause: this.#failure.error,
8666
+ retryAt: this.#failure.retryAt,
8667
+ attempt: this.#failure.attempt,
8668
+ }));
8669
+ }
8670
+ const startedAt = Date.now();
8671
+ try {
8672
+ // firstValueFrom returns the cached snapshot; lastValueFrom waits for
8673
+ // the freshest value from the query
8674
+ const fn = allow_cache ? firstValueFrom : lastValueFrom;
8675
+ const record = await fn(this.#query.query());
8676
+ // Only a fresh response proves discovery works; a cached (possibly expired) snapshot must
8677
+ // not reset the failure streak or renew the age of the fallback list
8678
+ if (record.status === 'complete') {
8679
+ this.#failure = undefined;
8680
+ this.#lastKnownGood = { services: record.value, receivedAt: Date.now() };
8681
+ }
8682
+ return record.value;
8683
+ }
8684
+ catch (error) {
8685
+ this.#registerFailure(error, startedAt);
8686
+ return this.#resolveFallback(error);
8687
+ }
8688
+ }
8689
+ /**
8690
+ * Records a failed request and schedules the next allowed request time.
8691
+ *
8692
+ * Concurrent callers share one in-flight request through {@link Query}, so they all
8693
+ * reject with the same failure; only the first one to settle may extend the streak,
8694
+ * otherwise a single failed request would be counted once per waiting caller.
8695
+ *
8696
+ * @param error - The error from the failed request.
8697
+ * @param startedAt - Epoch milliseconds when the caller started waiting for the request.
8698
+ */
8699
+ #registerFailure(error, startedAt) {
8700
+ // A concurrent caller already recorded this same failed request
8701
+ if (this.#failure && this.#failure.failedAt >= startedAt) {
8702
+ return;
8703
+ }
8704
+ const attempt = (this.#failure?.attempt ?? 0) + 1;
8705
+ const delay = Math.min(failureBackoffMaxMs, failureBackoffInitialMs * 2 ** (attempt - 1));
8706
+ // Up to 20% jitter keeps many clients that failed together from retrying in lockstep
8707
+ const jittered = Math.round(delay * (0.8 + Math.random() * 0.2));
8708
+ const failedAt = Date.now();
8709
+ this.#failure = { error, attempt, failedAt, retryAt: failedAt + jittered };
8710
+ // Warn once per streak rather than once per call to keep consoles readable
8711
+ if (attempt === 1) {
8712
+ console.warn(`[service-discovery] request failed; pausing requests with backoff (max ${failureBackoffMaxMs}ms)`, error);
8713
+ }
8714
+ }
8715
+ /**
8716
+ * Returns the last successful service list when it is still fresh enough,
8717
+ * otherwise rethrows the given error.
8718
+ *
8719
+ * @param error - Error to throw when no usable fallback exists.
8720
+ * @returns The last known good service list.
8721
+ * @throws The provided `error` when no service list younger than 24 hours exists.
8722
+ */
8723
+ #resolveFallback(error) {
8724
+ const lastKnownGood = this.#lastKnownGood;
8725
+ // Prefer a recent service list over an error so callers have nothing to retry
8726
+ if (lastKnownGood && Date.now() - lastKnownGood.receivedAt <= staleIfErrorMs) {
8727
+ return lastKnownGood.services;
8728
+ }
8729
+ throw error;
8604
8730
  }
8605
8731
  /** {@inheritDoc IServiceDiscoveryClient.resolveService} */
8606
8732
  async resolveService(key, allow_cache) {
@@ -8802,7 +8928,7 @@ class ServiceDiscoveryConfigurator extends BaseConfigBuilder {
8802
8928
  }
8803
8929
 
8804
8930
  // Generated by genversion.
8805
- const version$1 = '10.1.2';
8931
+ const version$1 = '10.2.0';
8806
8932
 
8807
8933
  /**
8808
8934
  * Default implementation of {@link IServiceDiscoveryProvider}.
@@ -9402,7 +9528,7 @@ async function registerServiceWorker(framework) {
9402
9528
  }
9403
9529
 
9404
9530
  // Generated by genversion.
9405
- const version = '5.2.0';
9531
+ const version = '5.2.2';
9406
9532
 
9407
9533
  // Allow dynamic import without vite
9408
9534
  const importWithoutVite = (path) => import(/* @vite-ignore */ path);
@@ -9422,7 +9548,7 @@ enableServiceDiscovery(configurator, async (builder) => {
9422
9548
  });
9423
9549
  // Avoid interactive Entra ID authentication when the SPA runs in CI or Playwright.
9424
9550
  if (isEnabledEnvValue(import.meta.env.FUSION_SPA_MSAL_MOCK)) {
9425
- const { enableMsalMock } = await import('../index-CDpTfyge.js');
9551
+ const { enableMsalMock } = await import('../index-glg6-DFO.js');
9426
9552
  const mockServerUrl = import.meta.env.FUSION_SPA_MSAL_MOCK_SERVER_URL;
9427
9553
  enableMsalMock(configurator, (builder) => {
9428
9554
  // The SPA owns the HTTP adapter; MSAL owns applying the returned token and account.
@@ -9445,12 +9571,12 @@ else {
9445
9571
  }
9446
9572
  // Initialize tracing before app startup so network requests and logs share active trace context.
9447
9573
  if (import.meta.env.FUSION_SPA_TELEMETRY_OTLP_TRACES_ENDPOINT) {
9448
- const { enableDevServerTracing } = await import('../enable-dev-server-tracing-DaQc5M9V.js');
9574
+ const { enableDevServerTracing } = await import('../enable-dev-server-tracing-GfRoCLxu.js');
9449
9575
  enableDevServerTracing(new URL(import.meta.env.FUSION_SPA_TELEMETRY_OTLP_TRACES_ENDPOINT, window.location.origin).href);
9450
9576
  }
9451
9577
  // The dev-server environment bridge owns activation; the SPA only consumes its endpoint.
9452
9578
  const devServerOtlpAdapter = import.meta.env.FUSION_SPA_TELEMETRY_OTLP_ENDPOINT
9453
- ? new (await import('../index-BO_d5C6N.js')).OtlpAdapter({
9579
+ ? new (await import('../index-CiqJ1Og1.js')).OtlpAdapter({
9454
9580
  url: new URL(import.meta.env.FUSION_SPA_TELEMETRY_OTLP_ENDPOINT, window.location.origin).href,
9455
9581
  fallback: true,
9456
9582
  serviceName: 'fusion.dev-server',