@braidlabs/angular-router 0.2.0 → 0.4.0
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"braidlabs-angular-router.mjs","sources":["../../../../../libs/angular/router/src/lib/config.ts","../../../../../libs/angular/router/src/lib/lazy.ts","../../../../../libs/angular/router/src/lib/unsaved-work.ts","../../../../../libs/angular/router/src/lib/recovery.service.ts","../../../../../libs/angular/router/src/lib/provide.ts","../../../../../libs/angular/router/src/lib/disabled.provider.ts","../../../../../libs/angular/router/src/braidlabs-angular-router.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport type { BuildIdentity } from '@braidlabs/skew';\n\n/**\n * What to do when a lazy chunk cannot be loaded.\n *\n * These are not interchangeable. A missing chunk, an offline device, a deleted\n * route, and an origin serving a stale entry document all arrive as the same\n * rejected dynamic import, and each wants a different response — which is why\n * classification happens before dispatch.\n */\nexport type StaleChunkAction =\n /**\n * Hard-navigate to the URL the user was trying to reach.\n *\n * Distinct from a plain reload for a subtle reason: Angular's default\n * `urlUpdateStrategy` is `'deferred'`, so after a failed navigation the\n * address bar still shows the *previous* route. `location.reload()` would\n * therefore return the user where they started and silently discard the\n * navigation they attempted.\n */\n | 'reload-at-target'\n /** Refresh the current route, abandoning the attempted navigation. */\n | 'reload-in-place'\n /** Client-side redirect to `fallbackRoute`. Correct when the route is gone. */\n | 'redirect-to-fallback'\n /** Take no automatic action; expose it and let the application decide. */\n | 'notify'\n /** Leave `NavigationError` to propagate untouched. */\n | 'ignore';\n\n/** Everything known about the failure at the moment a strategy is chosen. */\nexport interface StaleChunkContext {\n /** The URL the user was navigating to. */\n readonly targetUrl: string;\n /** Where they still are, since the navigation failed. */\n readonly currentUrl: string;\n /** Logical id passed to `lazy()`, when one was supplied. */\n readonly moduleId?: string;\n readonly error: unknown;\n\n /** Recoveries already spent this session, for loop protection. */\n readonly attempt: number;\n readonly isOnline: boolean;\n\n readonly clientBuildId: string;\n /** Present only when the manifest probe succeeded. */\n readonly serverBuildId?: string;\n\n /**\n * False when the module is absent from the new build's manifest — the route\n * was deleted, so reloading would land on a router 404.\n */\n readonly moduleStillExists: boolean;\n /**\n * True when the origin reports an *older* build than the one running.\n * Reloading fetches the same stale bundle and fails again — forever.\n */\n readonly entryDocumentStale: boolean;\n /** True when the probe reached the origin and it is ahead of us. */\n readonly newAssetsReachable: boolean;\n\n /** True when something on the page has registered unsaved work. */\n readonly hasUnsavedWork: boolean;\n}\n\nexport type StaleChunkStrategy =\n | StaleChunkAction\n | ((context: StaleChunkContext) => StaleChunkAction | Promise<StaleChunkAction>);\n\nexport interface SkewRecoveryOptions {\n /** Identity of this build. Generate with `@braidlabs/build`. */\n readonly identity: BuildIdentity;\n /**\n * Manifest the origin serves, `Cache-Control: no-store`. Without it the\n * library still recovers, but cannot distinguish a deleted route or a stale\n * origin — so it degrades to conservative behaviour.\n */\n readonly manifestUrl?: string;\n /** Default `'reload-at-target'`. */\n readonly onStaleChunk?: StaleChunkStrategy;\n /** Transient-failure retries inside `lazy()`. Default 1. */\n readonly retryAttempts?: number;\n /** Backoff between retries. Default 250ms. */\n readonly retryDelayMs?: number;\n /**\n * Hard cap on automatic recoveries per session. Default 1.\n *\n * The blunt guard against reload loops; the manifest probe is the precise\n * one. Both exist because the failure mode — a bricked tab — is severe.\n */\n readonly maxRecoveries?: number;\n /** Target for `'redirect-to-fallback'`. Default `'/'`. */\n readonly fallbackRoute?: string;\n /**\n * Degrade to `'notify'` when unsaved work is registered. Default true.\n *\n * Angular does not let a library ask the router whether a `CanDeactivate`\n * guard would block, so components opt in via `trackUnsavedWork()`.\n */\n readonly respectUnsavedWork?: boolean;\n}\n\n/** Fully-resolved options, with defaults applied. */\nexport interface ResolvedSkewRecoveryOptions extends SkewRecoveryOptions {\n readonly onStaleChunk: StaleChunkStrategy;\n readonly retryAttempts: number;\n readonly retryDelayMs: number;\n readonly maxRecoveries: number;\n readonly fallbackRoute: string;\n readonly respectUnsavedWork: boolean;\n}\n\nexport const SKEW_RECOVERY_OPTIONS = new InjectionToken<ResolvedSkewRecoveryOptions>(\n 'SKEW_RECOVERY_OPTIONS',\n);\n\nexport function resolveOptions(options: SkewRecoveryOptions): ResolvedSkewRecoveryOptions {\n return {\n ...options,\n onStaleChunk: options.onStaleChunk ?? 'reload-at-target',\n retryAttempts: options.retryAttempts ?? 1,\n retryDelayMs: options.retryDelayMs ?? 250,\n maxRecoveries: options.maxRecoveries ?? 1,\n fallbackRoute: options.fallbackRoute ?? '/',\n respectUnsavedWork: options.respectUnsavedWork ?? true,\n };\n}\n\n/** A recovery the application has been asked to complete itself. */\nexport interface PendingSkew {\n readonly targetUrl: string;\n readonly moduleId?: string;\n readonly serverBuildId?: string;\n readonly reason: 'notify' | 'exhausted' | 'loop-detected' | 'offline';\n readonly context: StaleChunkContext;\n}\n","/**\n * Wrapping the dynamic import.\n *\n * Two problems are solved here, both before any Angular machinery is involved:\n *\n * 1. **Attribution.** By the time an import rejects, the runtime holds\n * `chunk-ABC123.js` — not `./admin/routes`. Without a logical id there is no\n * way to ask the new build's manifest whether that route still exists.\n * 2. **Transience.** A flaky network, a CDN edge miss, and a genuinely purged\n * asset are indistinguishable from the error alone. A bounded retry resolves\n * the first two without inflicting a page reload on anyone.\n *\n * Retry is therefore a *precondition* to recovery, not one of the strategies.\n */\n\nimport { isSkewDisabled } from '@braidlabs/skew';\n\n/** Defaults for `lazy()`, overridable globally by `provideSkewRecovery()`. */\nexport const lazyDefaults = {\n retryAttempts: 1,\n retryDelayMs: 250,\n};\n\n/** Restores defaults. Exposed for tests, which must not leak configuration. */\nexport function resetLazyDefaults(): void {\n lazyDefaults.retryAttempts = 1;\n lazyDefaults.retryDelayMs = 250;\n}\n\n/**\n * Thrown when a lazy load fails after its retries are exhausted.\n *\n * Angular surfaces this through `NavigationError`, where the recovery service\n * recognises it. Carrying the module id across that boundary is the whole\n * reason this error type exists.\n */\nexport class ChunkLoadFailure extends Error {\n override readonly name = 'ChunkLoadFailure';\n constructor(\n readonly moduleId: string | undefined,\n readonly cause: unknown,\n readonly attempts: number,\n ) {\n super(\n `[skew] failed to load ${moduleId ? `\"${moduleId}\"` : 'a lazy chunk'} ` +\n `after ${attempts} attempt${attempts === 1 ? '' : 's'}`,\n );\n }\n}\n\nexport function isChunkLoadFailure(value: unknown): value is ChunkLoadFailure {\n return value instanceof ChunkLoadFailure;\n}\n\n/**\n * Heuristic for \"this looks like a module that failed to load\" rather than an\n * error thrown *by* the module once it ran.\n *\n * Deliberately broad: bundlers and browsers disagree on the shape here\n * (`ChunkLoadError`, a bare `TypeError`, a CSP violation), and a false positive\n * costs one wasted retry while a false negative costs the user their session.\n */\nexport function looksLikeChunkError(error: unknown): boolean {\n if (!error) return false;\n const name = (error as Error).name ?? '';\n const message = (error as Error).message ?? '';\n return (\n name === 'ChunkLoadError' ||\n /loading (chunk|css chunk)/i.test(message) ||\n /failed to fetch dynamically imported module/i.test(message) ||\n /error loading dynamically imported module/i.test(message) ||\n /importing a module script failed/i.test(message)\n );\n}\n\nexport interface LazyOptions {\n readonly retryAttempts?: number;\n readonly retryDelayMs?: number;\n}\n\n/**\n * Wraps a dynamic import for use as `loadChildren` or `loadComponent`.\n *\n * ```ts\n * { path: 'admin', loadChildren: lazy('admin.routes', () => import('./admin/routes')) }\n * { path: 'help', loadComponent: lazy('help.page', () => import('./help').then(m => m.Help)) }\n * ```\n *\n * The returned function is a plain loader — the router needs no knowledge of\n * this library, and routes remain usable if the package is removed.\n */\nexport function lazy<T>(\n moduleId: string,\n loader: () => Promise<T>,\n options?: LazyOptions,\n): () => Promise<T>;\nexport function lazy<T>(loader: () => Promise<T>, options?: LazyOptions): () => Promise<T>;\nexport function lazy<T>(\n moduleIdOrLoader: string | (() => Promise<T>),\n loaderOrOptions?: (() => Promise<T>) | LazyOptions,\n maybeOptions?: LazyOptions,\n): () => Promise<T> {\n const hasId = typeof moduleIdOrLoader === 'string';\n const moduleId = hasId ? moduleIdOrLoader : undefined;\n const loader = (hasId ? loaderOrOptions : moduleIdOrLoader) as () => Promise<T>;\n const options = (hasId ? maybeOptions : (loaderOrOptions as LazyOptions)) ?? {};\n\n if (typeof loader !== 'function') {\n throw new TypeError('[skew] lazy() requires a loader function');\n }\n\n return async () => {\n // Not public API — see `disabled.ts` in @braidlabs/skew. A bare dynamic import\n // with no retry and no attribution: the rejection propagates exactly as the\n // bundler produced it, and nothing downstream knows which module it was.\n if (isSkewDisabled()) return loader();\n\n const attempts = Math.max(0, options.retryAttempts ?? lazyDefaults.retryAttempts) + 1;\n const delay = options.retryDelayMs ?? lazyDefaults.retryDelayMs;\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n return await loader();\n } catch (error) {\n lastError = error;\n // An error thrown by module *evaluation* is a bug in the module, not a\n // transport failure. Retrying it just runs the bug again.\n if (!looksLikeChunkError(error)) throw error;\n if (attempt < attempts && delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay * attempt));\n }\n }\n }\n throw new ChunkLoadFailure(moduleId, lastError, attempts);\n };\n}\n","import { DestroyRef, Injectable, inject, signal } from '@angular/core';\n\n/**\n * Unsaved-work tracking.\n *\n * A hard reload destroys in-memory state, so recovery must know whether that\n * matters. Angular already has a declaration for exactly this — `CanDeactivate`\n * — but the router exposes no way to ask \"would a guard block here?\", so a\n * library cannot reuse it.\n *\n * This registry is the workaround: components declare dirtiness explicitly and\n * the recovery service consults it. If Angular ever exposes guard\n * introspection, this whole file becomes deletable.\n */\n@Injectable({ providedIn: 'root' })\nexport class UnsavedWorkRegistry {\n private readonly sources = signal<ReadonlyArray<() => boolean>>([]);\n\n /** Registers a predicate. Returns a disposer. */\n register(isDirty: () => boolean): () => void {\n this.sources.update((current) => [...current, isDirty]);\n return () => {\n this.sources.update((current) => current.filter((entry) => entry !== isDirty));\n };\n }\n\n /**\n * True when any registered source reports unsaved work.\n *\n * A throwing predicate is treated as *clean* rather than dirty: a broken\n * dirty-check should not permanently block recovery and strand the user.\n */\n isDirty(): boolean {\n return this.sources().some((isDirty) => {\n try {\n return isDirty();\n } catch {\n return false;\n }\n });\n }\n\n /** Count of registered sources. Diagnostics only. */\n get size(): number {\n return this.sources().length;\n }\n}\n\n/**\n * Declares that this component holds unsaved work, so automatic recovery will\n * not discard it. Unregisters automatically on destroy.\n *\n * ```ts\n * export class BulletinEditor {\n * protected readonly form = …;\n * constructor() {\n * trackUnsavedWork(() => this.form.dirty);\n * }\n * }\n * ```\n *\n * Must be called in an injection context.\n */\nexport function trackUnsavedWork(isDirty: () => boolean): void {\n const registry = inject(UnsavedWorkRegistry);\n const destroyRef = inject(DestroyRef);\n const dispose = registry.register(isDirty);\n destroyRef.onDestroy(dispose);\n}\n","import { DOCUMENT, Injectable, PLATFORM_ID, computed, inject, signal } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { NavigationError, Router } from '@angular/router';\nimport {\n type SkewStatus,\n type VersionProbe,\n createVersionProbe,\n isSkewDisabled,\n moduleWasRemoved,\n} from '@braidlabs/skew';\nimport {\n type PendingSkew,\n SKEW_RECOVERY_OPTIONS,\n type StaleChunkAction,\n type StaleChunkContext,\n} from './config';\nimport { ChunkLoadFailure, isChunkLoadFailure } from './lazy';\nimport { UnsavedWorkRegistry } from './unsaved-work';\n\nconst LOOP_GUARD_KEY = 'skew:recoveries';\n\n/**\n * Orchestrates recovery from a failed lazy load.\n *\n * Wiring is via router events rather than a global registry: `lazy()` throws a\n * {@link ChunkLoadFailure}, Angular surfaces it as `NavigationError`, and this\n * service recognises it there. That keeps `lazy()` dependency-free and usable\n * in route definitions, which are evaluated long before DI exists.\n *\n * Zoneless-safe and SSR-safe: no `NgZone`, and every `location` /\n * `sessionStorage` touch is guarded.\n */\n@Injectable({ providedIn: 'root' })\nexport class SkewRecoveryService {\n private readonly options = inject(SKEW_RECOVERY_OPTIONS);\n private readonly router = inject(Router);\n private readonly document = inject(DOCUMENT);\n private readonly unsavedWork = inject(UnsavedWorkRegistry);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private readonly probe: VersionProbe | null;\n\n private readonly pendingSignal = signal<PendingSkew | null>(null);\n private readonly statusSignal = signal<SkewStatus | null>(null);\n\n /** A recovery the application has been asked to complete itself. */\n readonly pending = this.pendingSignal.asReadonly();\n /** Last known comparison against the origin. */\n readonly status = this.statusSignal.asReadonly();\n /** True when a newer deployment is known to exist. */\n readonly updateAvailable = computed(() => this.statusSignal()?.kind === 'staleClient');\n\n constructor() {\n this.probe = this.options.manifestUrl\n ? createVersionProbe({\n identity: this.options.identity,\n manifestUrl: this.options.manifestUrl,\n })\n : null;\n\n if (this.isBrowser) this.listen();\n }\n\n private listen(): void {\n this.router.events.subscribe((event) => {\n if (event instanceof NavigationError && isChunkLoadFailure(event.error)) {\n // Not public API — see `disabled.ts` in @braidlabs/skew. The NavigationError\n // is left to propagate untouched: no retry, no manifest probe, no\n // classification, no decision. Whatever the application does next is\n // whatever it would have done without this package installed.\n if (isSkewDisabled()) return;\n void this.handle(event.error, event.url);\n }\n });\n }\n\n /** Re-checks the origin. Safe to call from a \"check for updates\" control. */\n async check(): Promise<SkewStatus | null> {\n if (!this.probe) return null;\n const status = await this.probe.check();\n this.statusSignal.set(status);\n return status;\n }\n\n /** Completes a deferred recovery — what a \"Reload\" button calls. */\n recover(): void {\n const pending = this.pendingSignal();\n if (!pending) return;\n this.pendingSignal.set(null);\n this.assign(pending.targetUrl);\n }\n\n /** Dismisses a pending recovery without acting on it. */\n dismiss(): void {\n this.pendingSignal.set(null);\n }\n\n private async handle(failure: ChunkLoadFailure, targetUrl: string): Promise<void> {\n const context = await this.classify(failure, targetUrl);\n const action = await this.chooseAction(context);\n this.dispatch(action, context);\n }\n\n private async classify(\n failure: ChunkLoadFailure,\n targetUrl: string,\n ): Promise<StaleChunkContext> {\n const status = await this.check();\n\n const serverBuildId =\n status?.kind === 'current'\n ? status.buildId\n : status && 'remote' in status\n ? status.remote\n : undefined;\n\n // Absent a module map we must assume the route still exists. Claiming it\n // was deleted would redirect users away from a route that merely moved —\n // a worse failure than the one being recovered from.\n const manifest = this.probe?.lastManifest() ?? null;\n const moduleStillExists =\n failure.moduleId === undefined ||\n manifest === null ||\n !moduleWasRemoved(manifest, failure.moduleId);\n\n return {\n targetUrl,\n currentUrl: this.router.url,\n moduleId: failure.moduleId,\n error: failure,\n attempt: this.recoveryCount(),\n isOnline: this.isOnline(),\n clientBuildId: this.options.identity.buildId,\n serverBuildId,\n moduleStillExists,\n entryDocumentStale: status?.kind === 'staleOrigin',\n newAssetsReachable: status?.kind === 'staleClient',\n hasUnsavedWork: this.options.respectUnsavedWork && this.unsavedWork.isDirty(),\n };\n }\n\n private async chooseAction(context: StaleChunkContext): Promise<StaleChunkAction> {\n // Guard rails run before any application policy, because each of these is a\n // case where reloading is actively harmful rather than merely unhelpful.\n if (!context.isOnline) return 'notify';\n if (context.entryDocumentStale) return 'notify'; // reloading would loop\n if (context.attempt >= this.options.maxRecoveries) return 'notify';\n if (context.hasUnsavedWork) return 'notify';\n if (!context.moduleStillExists) return 'redirect-to-fallback';\n\n const strategy = this.options.onStaleChunk;\n if (typeof strategy === 'function') {\n try {\n return await strategy(context);\n } catch {\n // A throwing policy must not strand the user with nothing.\n return 'notify';\n }\n }\n return strategy;\n }\n\n private dispatch(action: StaleChunkAction, context: StaleChunkContext): void {\n switch (action) {\n case 'reload-at-target':\n this.countRecovery();\n this.assign(context.targetUrl);\n return;\n case 'reload-in-place':\n this.countRecovery();\n this.reload();\n return;\n case 'redirect-to-fallback':\n void this.router.navigateByUrl(this.options.fallbackRoute);\n return;\n case 'ignore':\n return;\n case 'notify':\n this.pendingSignal.set({\n targetUrl: context.targetUrl,\n moduleId: context.moduleId,\n serverBuildId: context.serverBuildId,\n reason: !context.isOnline\n ? 'offline'\n : context.entryDocumentStale\n ? 'loop-detected'\n : context.attempt >= this.options.maxRecoveries\n ? 'exhausted'\n : 'notify',\n context,\n });\n return;\n }\n }\n\n // --- environment access, all browser-guarded ---------------------------\n\n private isOnline(): boolean {\n if (!this.isBrowser) return true;\n const nav = this.document.defaultView?.navigator;\n return nav?.onLine ?? true;\n }\n\n private assign(url: string): void {\n if (!this.isBrowser) return;\n this.document.defaultView?.location.assign(url);\n }\n\n private reload(): void {\n if (!this.isBrowser) return;\n this.document.defaultView?.location.reload();\n }\n\n /**\n * Recoveries already attempted, kept in `sessionStorage` so it survives the\n * very reload it is counting. Without this, an origin serving a stale entry\n * document turns one failure into an infinite reload loop.\n */\n private recoveryCount(): number {\n if (!this.isBrowser) return 0;\n try {\n const raw = this.document.defaultView?.sessionStorage.getItem(LOOP_GUARD_KEY);\n const parsed = raw ? JSON.parse(raw) : null;\n // Scoped to a build: a genuinely new deployment gets a fresh budget.\n return parsed?.buildId === this.options.identity.buildId ? (parsed.count ?? 0) : 0;\n } catch {\n return 0;\n }\n }\n\n private countRecovery(): void {\n if (!this.isBrowser) return;\n try {\n this.document.defaultView?.sessionStorage.setItem(\n LOOP_GUARD_KEY,\n JSON.stringify({ buildId: this.options.identity.buildId, count: this.recoveryCount() + 1 }),\n );\n } catch {\n // Private mode. The manifest probe remains as the precise loop guard.\n }\n }\n}\n","import {\n type EnvironmentProviders,\n provideEnvironmentInitializer,\n makeEnvironmentProviders,\n inject,\n} from '@angular/core';\nimport { SKEW_RECOVERY_OPTIONS, type SkewRecoveryOptions, resolveOptions } from './config';\nimport { SkewRecoveryService } from './recovery.service';\nimport { lazyDefaults } from './lazy';\n\n/**\n * Enables skew recovery.\n *\n * ```ts\n * bootstrapApplication(App, {\n * providers: [\n * provideRouter(routes),\n * provideSkewRecovery({\n * identity: BUILD_IDENTITY, // from @braidlabs/build\n * manifestUrl: '/skew-manifest.json',\n * }),\n * ],\n * });\n * ```\n *\n * Deliberately *not* a `RouterFeature`: Angular's feature factory is internal,\n * so third parties cannot construct one. This composes alongside\n * `provideRouter` instead, and works with any router configuration.\n *\n * The service is instantiated eagerly — it subscribes to router events, and a\n * lazily-created recovery service would miss the very first failure.\n */\nexport function provideSkewRecovery(options: SkewRecoveryOptions): EnvironmentProviders {\n const resolved = resolveOptions(options);\n\n return makeEnvironmentProviders([\n { provide: SKEW_RECOVERY_OPTIONS, useValue: resolved },\n provideEnvironmentInitializer(() => {\n // Route definitions are evaluated before DI exists, so `lazy()` cannot\n // inject its retry policy. Publishing the resolved values here keeps a\n // single source of configuration without making `lazy()` DI-aware.\n lazyDefaults.retryAttempts = resolved.retryAttempts;\n lazyDefaults.retryDelayMs = resolved.retryDelayMs;\n\n inject(SkewRecoveryService);\n }),\n ]);\n}\n","import {\n type EnvironmentProviders,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\nimport { setSkewDisabled } from '@braidlabs/skew';\n\n/**\n * ⚠️ NOT PUBLIC API. Undocumented on purpose. Do not use in an application.\n *\n * Angular-shaped wrapper around `setSkewDisabled()` from `@braidlabs/skew`. Read the\n * module comment in `@braidlabs/skew`'s `disabled.ts` first — it explains what this\n * turns off, why it exists, and why it is deliberately absent from the README,\n * the package docs, and every example.\n *\n * ```ts\n * // demo apps only\n * providers: [provideSkewRecovery({ … }), provideSkewDisabled()]\n * ```\n *\n * Two things worth knowing before using it anyway:\n *\n * **It is not scoped to this injector.** The flag lives in a module-level\n * variable in `@braidlabs/skew`, because core is framework-agnostic and has no\n * injector to read from. Calling this affects every `@braidlabs` package sharing\n * that instance of core — including any other Angular application on the page.\n * It is `provide*`-shaped for familiarity, not because it is scoped.\n *\n * **It applies at bootstrap.** For a control that flips at runtime — which is\n * what a before/after demo actually wants — call `setSkewDisabled()` directly.\n * Every check reads the flag at the moment it runs, so a later toggle takes\n * effect on the next read, navigation or write with no reload.\n *\n * This lives in `@braidlabs/angular-router` for want of a shared Angular package;\n * it belongs in `@braidlabs/angular-core` once that lands, since it governs all of\n * them and nothing here is router-specific.\n *\n * @internal\n */\nexport function provideSkewDisabled(disabled = true): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideEnvironmentInitializer(() => setSkewDisabled(disabled)),\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;MAiHa,qBAAqB,GAAG,IAAI,cAAc,CACrD,uBAAuB;AAGnB,SAAU,cAAc,CAAC,OAA4B,EAAA;IACzD,OAAO;AACL,QAAA,GAAG,OAAO;AACV,QAAA,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,kBAAkB;AACxD,QAAA,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,CAAC;AACzC,QAAA,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,GAAG;AACzC,QAAA,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,CAAC;AACzC,QAAA,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,GAAG;AAC3C,QAAA,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,IAAI,IAAI;KACvD;AACH;;AC/HA;;;;;;;;;;;;;AAaG;AAIH;AACO,MAAM,YAAY,GAAG;AAC1B,IAAA,aAAa,EAAE,CAAC;AAChB,IAAA,YAAY,EAAE,GAAG;;AAGnB;SACgB,iBAAiB,GAAA;AAC/B,IAAA,YAAY,CAAC,aAAa,GAAG,CAAC;AAC9B,IAAA,YAAY,CAAC,YAAY,GAAG,GAAG;AACjC;AAEA;;;;;;AAMG;AACG,MAAO,gBAAiB,SAAQ,KAAK,CAAA;AAG9B,IAAA,QAAA;AACA,IAAA,KAAA;AACA,IAAA,QAAA;IAJO,IAAI,GAAG,kBAAkB;AAC3C,IAAA,WAAA,CACW,QAA4B,EAC5B,KAAc,EACd,QAAgB,EAAA;AAEzB,QAAA,KAAK,CACH,CAAA,sBAAA,EAAyB,QAAQ,GAAG,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA,CAAG,GAAG,cAAc,CAAA,CAAA,CAAG;AACrE,YAAA,CAAA,MAAA,EAAS,QAAQ,CAAA,QAAA,EAAW,QAAQ,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,CAAA,CAAE,CAC1D;QAPQ,IAAA,CAAA,QAAQ,GAAR,QAAQ;QACR,IAAA,CAAA,KAAK,GAAL,KAAK;QACL,IAAA,CAAA,QAAQ,GAAR,QAAQ;IAMnB;AACD;AAEK,SAAU,kBAAkB,CAAC,KAAc,EAAA;IAC/C,OAAO,KAAK,YAAY,gBAAgB;AAC1C;AAEA;;;;;;;AAOG;AACG,SAAU,mBAAmB,CAAC,KAAc,EAAA;AAChD,IAAA,IAAI,CAAC,KAAK;AAAE,QAAA,OAAO,KAAK;AACxB,IAAA,MAAM,IAAI,GAAI,KAAe,CAAC,IAAI,IAAI,EAAE;AACxC,IAAA,MAAM,OAAO,GAAI,KAAe,CAAC,OAAO,IAAI,EAAE;IAC9C,QACE,IAAI,KAAK,gBAAgB;AACzB,QAAA,4BAA4B,CAAC,IAAI,CAAC,OAAO,CAAC;AAC1C,QAAA,8CAA8C,CAAC,IAAI,CAAC,OAAO,CAAC;AAC5D,QAAA,4CAA4C,CAAC,IAAI,CAAC,OAAO,CAAC;AAC1D,QAAA,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC;AAErD;SAwBgB,IAAI,CAClB,gBAA6C,EAC7C,eAAkD,EAClD,YAA0B,EAAA;AAE1B,IAAA,MAAM,KAAK,GAAG,OAAO,gBAAgB,KAAK,QAAQ;IAClD,MAAM,QAAQ,GAAG,KAAK,GAAG,gBAAgB,GAAG,SAAS;AACrD,IAAA,MAAM,MAAM,IAAI,KAAK,GAAG,eAAe,GAAG,gBAAgB,CAAqB;AAC/E,IAAA,MAAM,OAAO,GAAG,CAAC,KAAK,GAAG,YAAY,GAAI,eAA+B,KAAK,EAAE;AAE/E,IAAA,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE;AAChC,QAAA,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC;IACjE;IAEA,OAAO,YAAW;;;;AAIhB,QAAA,IAAI,cAAc,EAAE;YAAE,OAAO,MAAM,EAAE;AAErC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,aAAa,IAAI,YAAY,CAAC,aAAa,CAAC,GAAG,CAAC;QACrF,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,IAAI,YAAY,CAAC,YAAY;AAE/D,QAAA,IAAI,SAAkB;AACtB,QAAA,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,QAAQ,EAAE,OAAO,EAAE,EAAE;AACpD,YAAA,IAAI;gBACF,OAAO,MAAM,MAAM,EAAE;YACvB;YAAE,OAAO,KAAK,EAAE;gBACd,SAAS,GAAG,KAAK;;;AAGjB,gBAAA,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC;AAAE,oBAAA,MAAM,KAAK;gBAC5C,IAAI,OAAO,GAAG,QAAQ,IAAI,KAAK,GAAG,CAAC,EAAE;AACnC,oBAAA,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,KAAK,GAAG,OAAO,CAAC,CAAC;gBACtE;YACF;QACF;QACA,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;AAC3D,IAAA,CAAC;AACH;;ACtIA;;;;;;;;;;;AAWG;MAEU,mBAAmB,CAAA;IACb,OAAO,GAAG,MAAM,CAA+B,EAAE;gFAAC;;AAGnE,IAAA,QAAQ,CAAC,OAAsB,EAAA;AAC7B,QAAA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,KAAK,CAAC,GAAG,OAAO,EAAE,OAAO,CAAC,CAAC;AACvD,QAAA,OAAO,MAAK;YACV,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,KAAK,KAAK,OAAO,CAAC,CAAC;AAChF,QAAA,CAAC;IACH;AAEA;;;;;AAKG;IACH,OAAO,GAAA;QACL,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,KAAI;AACrC,YAAA,IAAI;gBACF,OAAO,OAAO,EAAE;YAClB;AAAE,YAAA,MAAM;AACN,gBAAA,OAAO,KAAK;YACd;AACF,QAAA,CAAC,CAAC;IACJ;;AAGA,IAAA,IAAI,IAAI,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM;IAC9B;uGA9BW,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,mBAAmB,cADN,MAAM,EAAA,CAAA;;2FACnB,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAD/B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;AAkClC;;;;;;;;;;;;;;AAcG;AACG,SAAU,gBAAgB,CAAC,OAAsB,EAAA;AACrD,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,mBAAmB,CAAC;AAC5C,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;AAC1C,IAAA,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC;AAC/B;;ACjDA,MAAM,cAAc,GAAG,iBAAiB;AAExC;;;;;;;;;;AAUG;MAEU,mBAAmB,CAAA;AACb,IAAA,OAAO,GAAG,MAAM,CAAC,qBAAqB,CAAC;AACvC,IAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AACvB,IAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AAC3B,IAAA,WAAW,GAAG,MAAM,CAAC,mBAAmB,CAAC;IACzC,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAElD,IAAA,KAAK;IAEL,aAAa,GAAG,MAAM,CAAqB,IAAI;sFAAC;IAChD,YAAY,GAAG,MAAM,CAAoB,IAAI;qFAAC;;AAGtD,IAAA,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE;;AAEzC,IAAA,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE;;AAEvC,IAAA,eAAe,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,KAAK,aAAa;wFAAC;AAEtF,IAAA,WAAA,GAAA;AACE,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC;cACtB,kBAAkB,CAAC;AACjB,gBAAA,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;AAC/B,gBAAA,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;aACtC;cACD,IAAI;QAER,IAAI,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,MAAM,EAAE;IACnC;IAEQ,MAAM,GAAA;QACZ,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,KAAI;YACrC,IAAI,KAAK,YAAY,eAAe,IAAI,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;;;;;AAKvE,gBAAA,IAAI,cAAc,EAAE;oBAAE;AACtB,gBAAA,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC;YAC1C;AACF,QAAA,CAAC,CAAC;IACJ;;AAGA,IAAA,MAAM,KAAK,GAAA;QACT,IAAI,CAAC,IAAI,CAAC,KAAK;AAAE,YAAA,OAAO,IAAI;QAC5B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;AACvC,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC;AAC7B,QAAA,OAAO,MAAM;IACf;;IAGA,OAAO,GAAA;AACL,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE;AACpC,QAAA,IAAI,CAAC,OAAO;YAAE;AACd,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;AAC5B,QAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;IAChC;;IAGA,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;IAC9B;AAEQ,IAAA,MAAM,MAAM,CAAC,OAAyB,EAAE,SAAiB,EAAA;QAC/D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;AAC/C,QAAA,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAChC;AAEQ,IAAA,MAAM,QAAQ,CACpB,OAAyB,EACzB,SAAiB,EAAA;AAEjB,QAAA,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE;AAEjC,QAAA,MAAM,aAAa,GACjB,MAAM,EAAE,IAAI,KAAK;cACb,MAAM,CAAC;AACT,cAAE,MAAM,IAAI,QAAQ,IAAI;kBACpB,MAAM,CAAC;kBACP,SAAS;;;;QAKjB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,IAAI;AACnD,QAAA,MAAM,iBAAiB,GACrB,OAAO,CAAC,QAAQ,KAAK,SAAS;AAC9B,YAAA,QAAQ,KAAK,IAAI;YACjB,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC;QAE/C,OAAO;YACL,SAAS;AACT,YAAA,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG;YAC3B,QAAQ,EAAE,OAAO,CAAC,QAAQ;AAC1B,YAAA,KAAK,EAAE,OAAO;AACd,YAAA,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE;AAC7B,YAAA,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;AACzB,YAAA,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO;YAC5C,aAAa;YACb,iBAAiB;AACjB,YAAA,kBAAkB,EAAE,MAAM,EAAE,IAAI,KAAK,aAAa;AAClD,YAAA,kBAAkB,EAAE,MAAM,EAAE,IAAI,KAAK,aAAa;AAClD,YAAA,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,kBAAkB,IAAI,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE;SAC9E;IACH;IAEQ,MAAM,YAAY,CAAC,OAA0B,EAAA;;;QAGnD,IAAI,CAAC,OAAO,CAAC,QAAQ;AAAE,YAAA,OAAO,QAAQ;QACtC,IAAI,OAAO,CAAC,kBAAkB;YAAE,OAAO,QAAQ,CAAC;QAChD,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa;AAAE,YAAA,OAAO,QAAQ;QAClE,IAAI,OAAO,CAAC,cAAc;AAAE,YAAA,OAAO,QAAQ;QAC3C,IAAI,CAAC,OAAO,CAAC,iBAAiB;AAAE,YAAA,OAAO,sBAAsB;AAE7D,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY;AAC1C,QAAA,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;AAClC,YAAA,IAAI;AACF,gBAAA,OAAO,MAAM,QAAQ,CAAC,OAAO,CAAC;YAChC;AAAE,YAAA,MAAM;;AAEN,gBAAA,OAAO,QAAQ;YACjB;QACF;AACA,QAAA,OAAO,QAAQ;IACjB;IAEQ,QAAQ,CAAC,MAAwB,EAAE,OAA0B,EAAA;QACnE,QAAQ,MAAM;AACZ,YAAA,KAAK,kBAAkB;gBACrB,IAAI,CAAC,aAAa,EAAE;AACpB,gBAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;gBAC9B;AACF,YAAA,KAAK,iBAAiB;gBACpB,IAAI,CAAC,aAAa,EAAE;gBACpB,IAAI,CAAC,MAAM,EAAE;gBACb;AACF,YAAA,KAAK,sBAAsB;AACzB,gBAAA,KAAK,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;gBAC1D;AACF,YAAA,KAAK,QAAQ;gBACX;AACF,YAAA,KAAK,QAAQ;AACX,gBAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;oBACrB,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,aAAa,EAAE,OAAO,CAAC,aAAa;AACpC,oBAAA,MAAM,EAAE,CAAC,OAAO,CAAC;AACf,0BAAE;0BACA,OAAO,CAAC;AACR,8BAAE;8BACA,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC;AAChC,kCAAE;AACF,kCAAE,QAAQ;oBAChB,OAAO;AACR,iBAAA,CAAC;gBACF;;IAEN;;IAIQ,QAAQ,GAAA;QACd,IAAI,CAAC,IAAI,CAAC,SAAS;AAAE,YAAA,OAAO,IAAI;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,SAAS;AAChD,QAAA,OAAO,GAAG,EAAE,MAAM,IAAI,IAAI;IAC5B;AAEQ,IAAA,MAAM,CAAC,GAAW,EAAA;QACxB,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE;QACrB,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC;IACjD;IAEQ,MAAM,GAAA;QACZ,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE;QACrB,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,EAAE;IAC9C;AAEA;;;;AAIG;IACK,aAAa,GAAA;QACnB,IAAI,CAAC,IAAI,CAAC,SAAS;AAAE,YAAA,OAAO,CAAC;AAC7B,QAAA,IAAI;AACF,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC;AAC7E,YAAA,MAAM,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI;;YAE3C,OAAO,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC;QACpF;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,CAAC;QACV;IACF;IAEQ,aAAa,GAAA;QACnB,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE;AACrB,QAAA,IAAI;AACF,YAAA,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC,OAAO,CAC/C,cAAc,EACd,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,EAAE,CAAC,CAC5F;QACH;AAAE,QAAA,MAAM;;QAER;IACF;uGA/MW,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,mBAAmB,cADN,MAAM,EAAA,CAAA;;2FACnB,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAD/B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACtBlC;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,mBAAmB,CAAC,OAA4B,EAAA;AAC9D,IAAA,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC;AAExC,IAAA,OAAO,wBAAwB,CAAC;AAC9B,QAAA,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,QAAQ,EAAE;QACtD,6BAA6B,CAAC,MAAK;;;;AAIjC,YAAA,YAAY,CAAC,aAAa,GAAG,QAAQ,CAAC,aAAa;AACnD,YAAA,YAAY,CAAC,YAAY,GAAG,QAAQ,CAAC,YAAY;YAEjD,MAAM,CAAC,mBAAmB,CAAC;AAC7B,QAAA,CAAC,CAAC;AACH,KAAA,CAAC;AACJ;;ACxCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BG;AACG,SAAU,mBAAmB,CAAC,QAAQ,GAAG,IAAI,EAAA;AACjD,IAAA,OAAO,wBAAwB,CAAC;QAC9B,6BAA6B,CAAC,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;AAC/D,KAAA,CAAC;AACJ;;AC3CA;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"braidlabs-angular-router.mjs","names":[],"sources":["../../../../../libs/angular/router/src/lib/config.ts","../../../../../libs/angular/router/src/lib/lazy.ts","../../../../../libs/angular/router/src/lib/unsaved-work.ts","../../../../../libs/angular/router/src/lib/recovery.service.ts","../../../../../libs/angular/router/src/lib/provide.ts","../../../../../libs/angular/router/src/lib/disabled.provider.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport type { BuildIdentity } from '@braidlabs/skew';\n\n/**\n * What to do when a lazy chunk cannot be loaded.\n *\n * These are not interchangeable. A missing chunk, an offline device, a deleted\n * route, and an origin serving a stale entry document all arrive as the same\n * rejected dynamic import, and each wants a different response — which is why\n * classification happens before dispatch.\n */\nexport type StaleChunkAction =\n /**\n * Hard-navigate to the URL the user was trying to reach.\n *\n * Distinct from a plain reload for a subtle reason: Angular's default\n * `urlUpdateStrategy` is `'deferred'`, so after a failed navigation the\n * address bar still shows the *previous* route. `location.reload()` would\n * therefore return the user where they started and silently discard the\n * navigation they attempted.\n */\n | 'reload-at-target'\n /** Refresh the current route, abandoning the attempted navigation. */\n | 'reload-in-place'\n /** Client-side redirect to `fallbackRoute`. Correct when the route is gone. */\n | 'redirect-to-fallback'\n /** Take no automatic action; expose it and let the application decide. */\n | 'notify'\n /** Leave `NavigationError` to propagate untouched. */\n | 'ignore';\n\n/** Everything known about the failure at the moment a strategy is chosen. */\nexport interface StaleChunkContext {\n /** The URL the user was navigating to. */\n readonly targetUrl: string;\n /** Where they still are, since the navigation failed. */\n readonly currentUrl: string;\n /** Logical id passed to `lazy()`, when one was supplied. */\n readonly moduleId?: string;\n readonly error: unknown;\n\n /** Recoveries already spent this session, for loop protection. */\n readonly attempt: number;\n readonly isOnline: boolean;\n\n readonly clientBuildId: string;\n /** Present only when the manifest probe succeeded. */\n readonly serverBuildId?: string;\n\n /**\n * False when the module is absent from the new build's manifest — the route\n * was deleted, so reloading would land on a router 404.\n */\n readonly moduleStillExists: boolean;\n /**\n * True when the origin reports an *older* build than the one running.\n * Reloading fetches the same stale bundle and fails again — forever.\n */\n readonly entryDocumentStale: boolean;\n /** True when the probe reached the origin and it is ahead of us. */\n readonly newAssetsReachable: boolean;\n\n /** True when something on the page has registered unsaved work. */\n readonly hasUnsavedWork: boolean;\n}\n\nexport type StaleChunkStrategy =\n | StaleChunkAction\n | ((context: StaleChunkContext) => StaleChunkAction | Promise<StaleChunkAction>);\n\nexport interface SkewRecoveryOptions {\n /** Identity of this build. Generate with `@braidlabs/build`. */\n readonly identity: BuildIdentity;\n /**\n * Manifest the origin serves, `Cache-Control: no-store`. Without it the\n * library still recovers, but cannot distinguish a deleted route or a stale\n * origin — so it degrades to conservative behaviour.\n */\n readonly manifestUrl?: string;\n /** Default `'reload-at-target'`. */\n readonly onStaleChunk?: StaleChunkStrategy;\n /** Transient-failure retries inside `lazy()`. Default 1. */\n readonly retryAttempts?: number;\n /** Backoff between retries. Default 250ms. */\n readonly retryDelayMs?: number;\n /**\n * Hard cap on automatic recoveries per session. Default 1.\n *\n * The blunt guard against reload loops; the manifest probe is the precise\n * one. Both exist because the failure mode — a bricked tab — is severe.\n */\n readonly maxRecoveries?: number;\n /** Target for `'redirect-to-fallback'`. Default `'/'`. */\n readonly fallbackRoute?: string;\n /**\n * Degrade to `'notify'` when unsaved work is registered. Default true.\n *\n * Angular does not let a library ask the router whether a `CanDeactivate`\n * guard would block, so components opt in via `trackUnsavedWork()`.\n */\n readonly respectUnsavedWork?: boolean;\n}\n\n/** Fully-resolved options, with defaults applied. */\nexport interface ResolvedSkewRecoveryOptions extends SkewRecoveryOptions {\n readonly onStaleChunk: StaleChunkStrategy;\n readonly retryAttempts: number;\n readonly retryDelayMs: number;\n readonly maxRecoveries: number;\n readonly fallbackRoute: string;\n readonly respectUnsavedWork: boolean;\n}\n\nexport const SKEW_RECOVERY_OPTIONS = new InjectionToken<ResolvedSkewRecoveryOptions>(\n 'SKEW_RECOVERY_OPTIONS',\n);\n\nexport function resolveOptions(options: SkewRecoveryOptions): ResolvedSkewRecoveryOptions {\n return {\n ...options,\n onStaleChunk: options.onStaleChunk ?? 'reload-at-target',\n retryAttempts: options.retryAttempts ?? 1,\n retryDelayMs: options.retryDelayMs ?? 250,\n maxRecoveries: options.maxRecoveries ?? 1,\n fallbackRoute: options.fallbackRoute ?? '/',\n respectUnsavedWork: options.respectUnsavedWork ?? true,\n };\n}\n\n/** A recovery the application has been asked to complete itself. */\nexport interface PendingSkew {\n readonly targetUrl: string;\n readonly moduleId?: string;\n readonly serverBuildId?: string;\n readonly reason: 'notify' | 'exhausted' | 'loop-detected' | 'offline';\n readonly context: StaleChunkContext;\n}\n","/**\n * Wrapping the dynamic import.\n *\n * Two problems are solved here, both before any Angular machinery is involved:\n *\n * 1. **Attribution.** By the time an import rejects, the runtime holds\n * `chunk-ABC123.js` — not `./admin/routes`. Without a logical id there is no\n * way to ask the new build's manifest whether that route still exists.\n * 2. **Transience.** A flaky network, a CDN edge miss, and a genuinely purged\n * asset are indistinguishable from the error alone. A bounded retry resolves\n * the first two without inflicting a page reload on anyone.\n *\n * Retry is therefore a *precondition* to recovery, not one of the strategies.\n */\n\nimport { isSkewDisabled } from '@braidlabs/skew';\n\n/** Defaults for `lazy()`, overridable globally by `provideSkewRecovery()`. */\nexport const lazyDefaults = {\n retryAttempts: 1,\n retryDelayMs: 250,\n};\n\n/** Restores defaults. Exposed for tests, which must not leak configuration. */\nexport function resetLazyDefaults(): void {\n lazyDefaults.retryAttempts = 1;\n lazyDefaults.retryDelayMs = 250;\n}\n\n/**\n * Thrown when a lazy load fails after its retries are exhausted.\n *\n * Angular surfaces this through `NavigationError`, where the recovery service\n * recognises it. Carrying the module id across that boundary is the whole\n * reason this error type exists.\n */\nexport class ChunkLoadFailure extends Error {\n override readonly name = 'ChunkLoadFailure';\n constructor(\n readonly moduleId: string | undefined,\n readonly cause: unknown,\n readonly attempts: number,\n ) {\n super(\n `[skew] failed to load ${moduleId ? `\"${moduleId}\"` : 'a lazy chunk'} ` +\n `after ${attempts} attempt${attempts === 1 ? '' : 's'}`,\n );\n }\n}\n\nexport function isChunkLoadFailure(value: unknown): value is ChunkLoadFailure {\n return value instanceof ChunkLoadFailure;\n}\n\n/**\n * Heuristic for \"this looks like a module that failed to load\" rather than an\n * error thrown *by* the module once it ran.\n *\n * Deliberately broad: bundlers and browsers disagree on the shape here\n * (`ChunkLoadError`, a bare `TypeError`, a CSP violation), and a false positive\n * costs one wasted retry while a false negative costs the user their session.\n */\nexport function looksLikeChunkError(error: unknown): boolean {\n if (!error) return false;\n const name = (error as Error).name ?? '';\n const message = (error as Error).message ?? '';\n return (\n name === 'ChunkLoadError' ||\n /loading (chunk|css chunk)/i.test(message) ||\n /failed to fetch dynamically imported module/i.test(message) ||\n /error loading dynamically imported module/i.test(message) ||\n /importing a module script failed/i.test(message)\n );\n}\n\nexport interface LazyOptions {\n readonly retryAttempts?: number;\n readonly retryDelayMs?: number;\n}\n\n/**\n * Wraps a dynamic import for use as `loadChildren` or `loadComponent`.\n *\n * ```ts\n * { path: 'admin', loadChildren: lazy('admin.routes', () => import('./admin/routes')) }\n * { path: 'help', loadComponent: lazy('help.page', () => import('./help').then(m => m.Help)) }\n * ```\n *\n * The returned function is a plain loader — the router needs no knowledge of\n * this library, and routes remain usable if the package is removed.\n */\nexport function lazy<T>(\n moduleId: string,\n loader: () => Promise<T>,\n options?: LazyOptions,\n): () => Promise<T>;\nexport function lazy<T>(loader: () => Promise<T>, options?: LazyOptions): () => Promise<T>;\nexport function lazy<T>(\n moduleIdOrLoader: string | (() => Promise<T>),\n loaderOrOptions?: (() => Promise<T>) | LazyOptions,\n maybeOptions?: LazyOptions,\n): () => Promise<T> {\n const hasId = typeof moduleIdOrLoader === 'string';\n const moduleId = hasId ? moduleIdOrLoader : undefined;\n const loader = (hasId ? loaderOrOptions : moduleIdOrLoader) as () => Promise<T>;\n const options = (hasId ? maybeOptions : (loaderOrOptions as LazyOptions)) ?? {};\n\n if (typeof loader !== 'function') {\n throw new TypeError('[skew] lazy() requires a loader function');\n }\n\n return async () => {\n // Not public API — see `disabled.ts` in @braidlabs/skew. A bare dynamic import\n // with no retry and no attribution: the rejection propagates exactly as the\n // bundler produced it, and nothing downstream knows which module it was.\n if (isSkewDisabled()) return loader();\n\n const attempts = Math.max(0, options.retryAttempts ?? lazyDefaults.retryAttempts) + 1;\n const delay = options.retryDelayMs ?? lazyDefaults.retryDelayMs;\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n return await loader();\n } catch (error) {\n lastError = error;\n // An error thrown by module *evaluation* is a bug in the module, not a\n // transport failure. Retrying it just runs the bug again.\n if (!looksLikeChunkError(error)) throw error;\n if (attempt < attempts && delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay * attempt));\n }\n }\n }\n throw new ChunkLoadFailure(moduleId, lastError, attempts);\n };\n}\n","import { DestroyRef, Injectable, inject, signal } from '@angular/core';\n\n/**\n * Unsaved-work tracking.\n *\n * A hard reload destroys in-memory state, so recovery must know whether that\n * matters. Angular already has a declaration for exactly this — `CanDeactivate`\n * — but the router exposes no way to ask \"would a guard block here?\", so a\n * library cannot reuse it.\n *\n * This registry is the workaround: components declare dirtiness explicitly and\n * the recovery service consults it. If Angular ever exposes guard\n * introspection, this whole file becomes deletable.\n */\n@Injectable({ providedIn: 'root' })\nexport class UnsavedWorkRegistry {\n private readonly sources = signal<ReadonlyArray<() => boolean>>([]);\n\n /** Registers a predicate. Returns a disposer. */\n register(isDirty: () => boolean): () => void {\n this.sources.update((current) => [...current, isDirty]);\n return () => {\n this.sources.update((current) => current.filter((entry) => entry !== isDirty));\n };\n }\n\n /**\n * True when any registered source reports unsaved work.\n *\n * A throwing predicate is treated as *clean* rather than dirty: a broken\n * dirty-check should not permanently block recovery and strand the user.\n */\n isDirty(): boolean {\n return this.sources().some((isDirty) => {\n try {\n return isDirty();\n } catch {\n return false;\n }\n });\n }\n\n /** Count of registered sources. Diagnostics only. */\n get size(): number {\n return this.sources().length;\n }\n}\n\n/**\n * Declares that this component holds unsaved work, so automatic recovery will\n * not discard it. Unregisters automatically on destroy.\n *\n * ```ts\n * export class BulletinEditor {\n * protected readonly form = …;\n * constructor() {\n * trackUnsavedWork(() => this.form.dirty);\n * }\n * }\n * ```\n *\n * Must be called in an injection context.\n */\nexport function trackUnsavedWork(isDirty: () => boolean): void {\n const registry = inject(UnsavedWorkRegistry);\n const destroyRef = inject(DestroyRef);\n const dispose = registry.register(isDirty);\n destroyRef.onDestroy(dispose);\n}\n","import { DOCUMENT, Injectable, PLATFORM_ID, computed, inject, signal } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { NavigationError, Router } from '@angular/router';\nimport {\n type SkewStatus,\n type VersionProbe,\n createVersionProbe,\n isSkewDisabled,\n moduleWasRemoved,\n} from '@braidlabs/skew';\nimport {\n type PendingSkew,\n SKEW_RECOVERY_OPTIONS,\n type StaleChunkAction,\n type StaleChunkContext,\n} from './config';\nimport { ChunkLoadFailure, isChunkLoadFailure } from './lazy';\nimport { UnsavedWorkRegistry } from './unsaved-work';\n\nconst LOOP_GUARD_KEY = 'skew:recoveries';\n\n/**\n * Orchestrates recovery from a failed lazy load.\n *\n * Wiring is via router events rather than a global registry: `lazy()` throws a\n * {@link ChunkLoadFailure}, Angular surfaces it as `NavigationError`, and this\n * service recognises it there. That keeps `lazy()` dependency-free and usable\n * in route definitions, which are evaluated long before DI exists.\n *\n * Zoneless-safe and SSR-safe: no `NgZone`, and every `location` /\n * `sessionStorage` touch is guarded.\n */\n@Injectable({ providedIn: 'root' })\nexport class SkewRecoveryService {\n private readonly options = inject(SKEW_RECOVERY_OPTIONS);\n private readonly router = inject(Router);\n private readonly document = inject(DOCUMENT);\n private readonly unsavedWork = inject(UnsavedWorkRegistry);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private readonly probe: VersionProbe | null;\n\n private readonly pendingSignal = signal<PendingSkew | null>(null);\n private readonly statusSignal = signal<SkewStatus | null>(null);\n\n /** A recovery the application has been asked to complete itself. */\n readonly pending = this.pendingSignal.asReadonly();\n /** Last known comparison against the origin. */\n readonly status = this.statusSignal.asReadonly();\n /** True when a newer deployment is known to exist. */\n readonly updateAvailable = computed(() => this.statusSignal()?.kind === 'staleClient');\n\n constructor() {\n this.probe = this.options.manifestUrl\n ? createVersionProbe({\n identity: this.options.identity,\n manifestUrl: this.options.manifestUrl,\n })\n : null;\n\n if (this.isBrowser) this.listen();\n }\n\n private listen(): void {\n this.router.events.subscribe((event) => {\n if (event instanceof NavigationError && isChunkLoadFailure(event.error)) {\n // Not public API — see `disabled.ts` in @braidlabs/skew. The NavigationError\n // is left to propagate untouched: no retry, no manifest probe, no\n // classification, no decision. Whatever the application does next is\n // whatever it would have done without this package installed.\n if (isSkewDisabled()) return;\n void this.handle(event.error, event.url);\n }\n });\n }\n\n /** Re-checks the origin. Safe to call from a \"check for updates\" control. */\n async check(): Promise<SkewStatus | null> {\n if (!this.probe) return null;\n const status = await this.probe.check();\n this.statusSignal.set(status);\n return status;\n }\n\n /** Completes a deferred recovery — what a \"Reload\" button calls. */\n recover(): void {\n const pending = this.pendingSignal();\n if (!pending) return;\n this.pendingSignal.set(null);\n this.assign(pending.targetUrl);\n }\n\n /** Dismisses a pending recovery without acting on it. */\n dismiss(): void {\n this.pendingSignal.set(null);\n }\n\n private async handle(failure: ChunkLoadFailure, targetUrl: string): Promise<void> {\n const context = await this.classify(failure, targetUrl);\n const action = await this.chooseAction(context);\n this.dispatch(action, context);\n }\n\n private async classify(\n failure: ChunkLoadFailure,\n targetUrl: string,\n ): Promise<StaleChunkContext> {\n const status = await this.check();\n\n const serverBuildId =\n status?.kind === 'current'\n ? status.buildId\n : status && 'remote' in status\n ? status.remote\n : undefined;\n\n // Absent a module map we must assume the route still exists. Claiming it\n // was deleted would redirect users away from a route that merely moved —\n // a worse failure than the one being recovered from.\n const manifest = this.probe?.lastManifest() ?? null;\n const moduleStillExists =\n failure.moduleId === undefined ||\n manifest === null ||\n !moduleWasRemoved(manifest, failure.moduleId);\n\n return {\n targetUrl,\n currentUrl: this.router.url,\n moduleId: failure.moduleId,\n error: failure,\n attempt: this.recoveryCount(),\n isOnline: this.isOnline(),\n clientBuildId: this.options.identity.buildId,\n serverBuildId,\n moduleStillExists,\n entryDocumentStale: status?.kind === 'staleOrigin',\n newAssetsReachable: status?.kind === 'staleClient',\n hasUnsavedWork: this.options.respectUnsavedWork && this.unsavedWork.isDirty(),\n };\n }\n\n private async chooseAction(context: StaleChunkContext): Promise<StaleChunkAction> {\n // Guard rails run before any application policy, because each of these is a\n // case where reloading is actively harmful rather than merely unhelpful.\n if (!context.isOnline) return 'notify';\n if (context.entryDocumentStale) return 'notify'; // reloading would loop\n if (context.attempt >= this.options.maxRecoveries) return 'notify';\n if (context.hasUnsavedWork) return 'notify';\n if (!context.moduleStillExists) return 'redirect-to-fallback';\n\n const strategy = this.options.onStaleChunk;\n if (typeof strategy === 'function') {\n try {\n return await strategy(context);\n } catch {\n // A throwing policy must not strand the user with nothing.\n return 'notify';\n }\n }\n return strategy;\n }\n\n private dispatch(action: StaleChunkAction, context: StaleChunkContext): void {\n switch (action) {\n case 'reload-at-target':\n this.countRecovery();\n this.assign(context.targetUrl);\n return;\n case 'reload-in-place':\n this.countRecovery();\n this.reload();\n return;\n case 'redirect-to-fallback':\n void this.router.navigateByUrl(this.options.fallbackRoute);\n return;\n case 'ignore':\n return;\n case 'notify':\n this.pendingSignal.set({\n targetUrl: context.targetUrl,\n moduleId: context.moduleId,\n serverBuildId: context.serverBuildId,\n reason: !context.isOnline\n ? 'offline'\n : context.entryDocumentStale\n ? 'loop-detected'\n : context.attempt >= this.options.maxRecoveries\n ? 'exhausted'\n : 'notify',\n context,\n });\n return;\n }\n }\n\n // --- environment access, all browser-guarded ---------------------------\n\n private isOnline(): boolean {\n if (!this.isBrowser) return true;\n const nav = this.document.defaultView?.navigator;\n return nav?.onLine ?? true;\n }\n\n private assign(url: string): void {\n if (!this.isBrowser) return;\n this.document.defaultView?.location.assign(url);\n }\n\n private reload(): void {\n if (!this.isBrowser) return;\n this.document.defaultView?.location.reload();\n }\n\n /**\n * Recoveries already attempted, kept in `sessionStorage` so it survives the\n * very reload it is counting. Without this, an origin serving a stale entry\n * document turns one failure into an infinite reload loop.\n */\n private recoveryCount(): number {\n if (!this.isBrowser) return 0;\n try {\n const raw = this.document.defaultView?.sessionStorage.getItem(LOOP_GUARD_KEY);\n const parsed = raw ? JSON.parse(raw) : null;\n // Scoped to a build: a genuinely new deployment gets a fresh budget.\n return parsed?.buildId === this.options.identity.buildId ? (parsed.count ?? 0) : 0;\n } catch {\n return 0;\n }\n }\n\n private countRecovery(): void {\n if (!this.isBrowser) return;\n try {\n this.document.defaultView?.sessionStorage.setItem(\n LOOP_GUARD_KEY,\n JSON.stringify({ buildId: this.options.identity.buildId, count: this.recoveryCount() + 1 }),\n );\n } catch {\n // Private mode. The manifest probe remains as the precise loop guard.\n }\n }\n}\n","import {\n type EnvironmentProviders,\n provideEnvironmentInitializer,\n makeEnvironmentProviders,\n inject,\n} from '@angular/core';\nimport { SKEW_RECOVERY_OPTIONS, type SkewRecoveryOptions, resolveOptions } from './config';\nimport { SkewRecoveryService } from './recovery.service';\nimport { lazyDefaults } from './lazy';\n\n/**\n * Enables skew recovery.\n *\n * ```ts\n * bootstrapApplication(App, {\n * providers: [\n * provideRouter(routes),\n * provideSkewRecovery({\n * identity: BUILD_IDENTITY, // from @braidlabs/build\n * manifestUrl: '/skew-manifest.json',\n * }),\n * ],\n * });\n * ```\n *\n * Deliberately *not* a `RouterFeature`: Angular's feature factory is internal,\n * so third parties cannot construct one. This composes alongside\n * `provideRouter` instead, and works with any router configuration.\n *\n * The service is instantiated eagerly — it subscribes to router events, and a\n * lazily-created recovery service would miss the very first failure.\n */\nexport function provideSkewRecovery(options: SkewRecoveryOptions): EnvironmentProviders {\n const resolved = resolveOptions(options);\n\n return makeEnvironmentProviders([\n { provide: SKEW_RECOVERY_OPTIONS, useValue: resolved },\n provideEnvironmentInitializer(() => {\n // Route definitions are evaluated before DI exists, so `lazy()` cannot\n // inject its retry policy. Publishing the resolved values here keeps a\n // single source of configuration without making `lazy()` DI-aware.\n lazyDefaults.retryAttempts = resolved.retryAttempts;\n lazyDefaults.retryDelayMs = resolved.retryDelayMs;\n\n inject(SkewRecoveryService);\n }),\n ]);\n}\n","import {\n type EnvironmentProviders,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\nimport { setSkewDisabled } from '@braidlabs/skew';\n\n/**\n * ⚠️ NOT PUBLIC API. Undocumented on purpose. Do not use in an application.\n *\n * Angular-shaped wrapper around `setSkewDisabled()` from `@braidlabs/skew`. Read the\n * module comment in `@braidlabs/skew`'s `disabled.ts` first — it explains what this\n * turns off, why it exists, and why it is deliberately absent from the README,\n * the package docs, and every example.\n *\n * ```ts\n * // demo apps only\n * providers: [provideSkewRecovery({ … }), provideSkewDisabled()]\n * ```\n *\n * Two things worth knowing before using it anyway:\n *\n * **It is not scoped to this injector.** The flag lives in a module-level\n * variable in `@braidlabs/skew`, because core is framework-agnostic and has no\n * injector to read from. Calling this affects every `@braidlabs` package sharing\n * that instance of core — including any other Angular application on the page.\n * It is `provide*`-shaped for familiarity, not because it is scoped.\n *\n * **It applies at bootstrap.** For a control that flips at runtime — which is\n * what a before/after demo actually wants — call `setSkewDisabled()` directly.\n * Every check reads the flag at the moment it runs, so a later toggle takes\n * effect on the next read, navigation or write with no reload.\n *\n * This lives in `@braidlabs/angular-router` for want of a shared Angular package;\n * it belongs in `@braidlabs/angular-core` once that lands, since it governs all of\n * them and nothing here is router-specific.\n *\n * @internal\n */\nexport function provideSkewDisabled(disabled = true): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideEnvironmentInitializer(() => setSkewDisabled(disabled)),\n ]);\n}\n"],"mappings":";;;;;AAiHA,MAAa,wBAAwB,IAAI,eACvC,uBAAuB;AAGzB,SAAgB,eAAe,SAA4B;CACzD,OAAO;EACL,GAAG;EACH,cAAc,QAAQ,gBAAgB;EACtC,eAAe,QAAQ,iBAAiB;EACxC,cAAc,QAAQ,gBAAgB;EACtC,eAAe,QAAQ,iBAAiB;EACxC,eAAe,QAAQ,iBAAiB;EACxC,oBAAoB,QAAQ,sBAAsB;CACnD;AACH;AC7GA,MAAa,eAAe;CAC1B,eAAe;CACf,cAAc;AACf;AAGD,SAAgB,oBAAiB;CAC/B,aAAa,gBAAgB;CAC7B,aAAa,eAAe;AAC9B;AASA,IAAa,mBAAb,cAAsC,MAAK;CAG9B;CACA;CACA;CAJO,OAAO;CACzB,YACW,UACA,OACA,UAAgB;EAEzB,MACE,yBAAyB,WAAW,IAAI,SAAQ,KAAM,eAAc,SACzD,SAAQ,UAAW,aAAa,IAAI,KAAK,KAAK;EANlD,KAAA,WAAA;EACA,KAAA,QAAA;EACA,KAAA,WAAA;CAMX;AACD;AAED,SAAgB,mBAAmB,OAAc;CAC/C,OAAO,iBAAiB;AAC1B;AAUA,SAAgB,oBAAoB,OAAc;CAChD,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,OAAQ,MAAgB,QAAQ;CACtC,MAAM,UAAW,MAAgB,WAAW;CAC5C,OACE,SAAS,oBACT,6BAA6B,KAAK,OAAO,KACzC,+CAA+C,KAAK,OAAO,KAC3D,6CAA6C,KAAK,OAAO,KACzD,oCAAoC,KAAK,OAAO;AAEpD;AAwBA,SAAgB,KACd,kBACA,iBACA,cAA0B;CAE1B,MAAM,QAAQ,OAAO,qBAAqB;CAC1C,MAAM,WAAW,QAAQ,mBAAmB;CAC5C,MAAM,SAAU,QAAQ,kBAAkB;CAC1C,MAAM,WAAW,QAAQ,eAAgB,oBAAoC,CAAA;CAE7E,IAAI,OAAO,WAAW,YACpB,MAAM,IAAI,UAAU,0CAA0C;CAGhE,OAAO,YAAW;EAIhB,IAAI,eAAc,GAAI,OAAO,OAAM;EAEnC,MAAM,WAAW,KAAK,IAAI,GAAG,QAAQ,iBAAiB,aAAa,aAAa,IAAI;EACpF,MAAM,QAAQ,QAAQ,gBAAgB,aAAa;EAEnD,IAAI;EACJ,KAAK,IAAI,UAAU,GAAG,WAAW,UAAU,WACzC,IAAI;GACF,OAAO,MAAM,OAAM;EACrB,SAAS,OAAO;GACd,YAAY;GAGZ,IAAI,CAAC,oBAAoB,KAAK,GAAG,MAAM;GACvC,IAAI,UAAU,YAAY,QAAQ,GAChC,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,QAAQ,OAAO,CAAC;EAEvE;EAEF,MAAM,IAAI,iBAAiB,UAAU,WAAW,QAAQ;CAC1D;AACF;ACzHA,IAAa,sBAAb,MAAa,oBAAmB;CACb,UAAU,OAAqC,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAGnE,SAAS,SAAsB;EAC7B,KAAK,QAAQ,QAAQ,YAAY,CAAC,GAAG,SAAS,OAAO,CAAC;EACtD,aAAY;GACV,KAAK,QAAQ,QAAQ,YAAY,QAAQ,QAAQ,UAAU,UAAU,OAAO,CAAC;EAC/E;CACF;CAQA,UAAO;EACL,OAAO,KAAK,QAAO,CAAE,CAAC,MAAM,YAAW;GACrC,IAAI;IACF,OAAO,QAAO;GAChB,QAAQ;IACN,OAAO;GACT;EACF,CAAC;CACH;CAGA,IAAI,OAAI;EACN,OAAO,KAAK,QAAO,CAAE,CAAC;CACxB;CA9BW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAnB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,YADN;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAmB,YAAA,CAAA;EAD/B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;AAiDlC,SAAgB,iBAAiB,SAAsB;CACrD,MAAM,WAAW,OAAO,mBAAmB;CAC3C,MAAM,aAAa,OAAO,UAAU;CACpC,MAAM,UAAU,SAAS,SAAS,OAAO;CACzC,WAAW,UAAU,OAAO;AAC9B;ACjDA,MAAM,iBAAiB;AAcvB,IAAa,sBAAb,MAAa,oBAAmB;CACb,UAAU,OAAO,qBAAqB;CACtC,SAAS,OAAO,MAAM;CACtB,WAAW,OAAO,QAAQ;CAC1B,cAAc,OAAO,mBAAmB;CACxC,YAAY,kBAAkB,OAAO,WAAW,CAAC;CAEjD;CAEA,gBAAgB,OAA2B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAChD,eAAe,OAA0B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAGtD,UAAU,KAAK,cAAc,WAAU;CAEvC,SAAS,KAAK,aAAa,WAAU;CAErC,kBAAkB,eAAe,KAAK,aAAY,CAAE,EAAE,SAAS,eAAc,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;CAEtF,cAAA;EACE,KAAK,QAAQ,KAAK,QAAQ,cACtB,mBAAmB;GACjB,UAAU,KAAK,QAAQ;GACvB,aAAa,KAAK,QAAQ;EAC3B,CAAA,IACD;EAEJ,IAAI,KAAK,WAAW,KAAK,OAAM;CACjC;CAEQ,SAAM;EACZ,KAAK,OAAO,OAAO,WAAW,UAAS;GACrC,IAAI,iBAAiB,mBAAmB,mBAAmB,MAAM,KAAK,GAAG;IAKvE,IAAI,eAAc,GAAI;IACtB,AAAK,KAAK,OAAO,MAAM,OAAO,MAAM,GAAG;GACzC;EACF,CAAC;CACH;CAGA,MAAM,QAAK;EACT,IAAI,CAAC,KAAK,OAAO,OAAO;EACxB,MAAM,SAAS,MAAM,KAAK,MAAM,MAAK;EACrC,KAAK,aAAa,IAAI,MAAM;EAC5B,OAAO;CACT;CAGA,UAAO;EACL,MAAM,UAAU,KAAK,cAAa;EAClC,IAAI,CAAC,SAAS;EACd,KAAK,cAAc,IAAI,IAAI;EAC3B,KAAK,OAAO,QAAQ,SAAS;CAC/B;CAGA,UAAO;EACL,KAAK,cAAc,IAAI,IAAI;CAC7B;CAEQ,MAAM,OAAO,SAA2B,WAAiB;EAC/D,MAAM,UAAU,MAAM,KAAK,SAAS,SAAS,SAAS;EACtD,MAAM,SAAS,MAAM,KAAK,aAAa,OAAO;EAC9C,KAAK,SAAS,QAAQ,OAAO;CAC/B;CAEQ,MAAM,SACZ,SACA,WAAiB;EAEjB,MAAM,SAAS,MAAM,KAAK,MAAK;EAE/B,MAAM,gBACJ,QAAQ,SAAS,YACb,OAAO,UACP,UAAU,YAAY,SACpB,OAAO,SACP;EAKR,MAAM,WAAW,KAAK,OAAO,aAAY,KAAM;EAC/C,MAAM,oBACJ,QAAQ,aAAa,UACrB,aAAa,QACb,CAAC,iBAAiB,UAAU,QAAQ,QAAQ;EAE9C,OAAO;GACL;GACA,YAAY,KAAK,OAAO;GACxB,UAAU,QAAQ;GAClB,OAAO;GACP,SAAS,KAAK,cAAa;GAC3B,UAAU,KAAK,SAAQ;GACvB,eAAe,KAAK,QAAQ,SAAS;GACrC;GACA;GACA,oBAAoB,QAAQ,SAAS;GACrC,oBAAoB,QAAQ,SAAS;GACrC,gBAAgB,KAAK,QAAQ,sBAAsB,KAAK,YAAY,QAAO;EAC5E;CACH;CAEQ,MAAM,aAAa,SAA0B;EAGnD,IAAI,CAAC,QAAQ,UAAU,OAAO;EAC9B,IAAI,QAAQ,oBAAoB,OAAO;EACvC,IAAI,QAAQ,WAAW,KAAK,QAAQ,eAAe,OAAO;EAC1D,IAAI,QAAQ,gBAAgB,OAAO;EACnC,IAAI,CAAC,QAAQ,mBAAmB,OAAO;EAEvC,MAAM,WAAW,KAAK,QAAQ;EAC9B,IAAI,OAAO,aAAa,YACtB,IAAI;GACF,OAAO,MAAM,SAAS,OAAO;EAC/B,QAAQ;GAEN,OAAO;EACT;EAEF,OAAO;CACT;CAEQ,SAAS,QAA0B,SAA0B;EACnE,QAAQ,QAAR;GACE,KAAK;IACH,KAAK,cAAa;IAClB,KAAK,OAAO,QAAQ,SAAS;IAC7B;GACF,KAAK;IACH,KAAK,cAAa;IAClB,KAAK,OAAM;IACX;GACF,KAAK;IACH,AAAK,KAAK,OAAO,cAAc,KAAK,QAAQ,aAAa;IACzD;GACF,KAAK,UACH;GACF,KAAK;IACH,KAAK,cAAc,IAAI;KACrB,WAAW,QAAQ;KACnB,UAAU,QAAQ;KAClB,eAAe,QAAQ;KACvB,QAAQ,CAAC,QAAQ,WACb,YACA,QAAQ,qBACN,kBACA,QAAQ,WAAW,KAAK,QAAQ,gBAC9B,cACA;KACR;IACD,CAAA;IACD;EACJ;CACF;CAIQ,WAAQ;EACd,IAAI,CAAC,KAAK,WAAW,OAAO;EAE5B,QADY,KAAK,SAAS,aAAa,UAChC,EAAK,UAAU;CACxB;CAEQ,OAAO,KAAW;EACxB,IAAI,CAAC,KAAK,WAAW;EACrB,KAAK,SAAS,aAAa,SAAS,OAAO,GAAG;CAChD;CAEQ,SAAM;EACZ,IAAI,CAAC,KAAK,WAAW;EACrB,KAAK,SAAS,aAAa,SAAS,OAAM;CAC5C;CAOQ,gBAAa;EACnB,IAAI,CAAC,KAAK,WAAW,OAAO;EAC5B,IAAI;GACF,MAAM,MAAM,KAAK,SAAS,aAAa,eAAe,QAAQ,cAAc;GAC5E,MAAM,SAAS,MAAM,KAAK,MAAM,GAAG,IAAI;GAEvC,OAAO,QAAQ,YAAY,KAAK,QAAQ,SAAS,UAAW,OAAO,SAAS,IAAK;EACnF,QAAQ;GACN,OAAO;EACT;CACF;CAEQ,gBAAa;EACnB,IAAI,CAAC,KAAK,WAAW;EACrB,IAAI;GACF,KAAK,SAAS,aAAa,eAAe,QACxC,gBACA,KAAK,UAAU;IAAE,SAAS,KAAK,QAAQ,SAAS;IAAS,OAAO,KAAK,cAAa,IAAK;GAAC,CAAE,CAAC;EAE/F,QAAQ,CAER;CACF;CA/MW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAnB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,YADN;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAmB,YAAA,CAAA;EAD/B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;;ACAlC,SAAgB,oBAAoB,SAA4B;CAC9D,MAAM,WAAW,eAAe,OAAO;CAEvC,OAAO,yBAAyB,CAC9B;EAAE,SAAS;EAAuB,UAAU;CAAQ,GACpD,oCAAmC;EAIjC,aAAa,gBAAgB,SAAS;EACtC,aAAa,eAAe,SAAS;EAErC,OAAO,mBAAmB;CAC5B,CAAC,CACF,CAAA;AACH;ACRA,SAAgB,oBAAoB,WAAW,MAAI;CACjD,OAAO,yBAAyB,CAC9B,oCAAoC,gBAAgB,QAAQ,CAAC,CAC9D,CAAA;AACH"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@braidlabs/angular-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/core": "^22.0.0",
|
|
6
6
|
"@angular/router": "^22.0.0",
|
|
7
7
|
"@angular/common": "^22.0.0",
|
|
8
|
-
"@braidlabs/skew": "^0.
|
|
8
|
+
"@braidlabs/skew": "^0.4.0"
|
|
9
9
|
},
|
|
10
10
|
"sideEffects": false,
|
|
11
11
|
"description": "Recovers from stale lazy-chunk failures after a deploy, with classification that avoids reload loops.",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import * as i0 from
|
|
2
|
-
import {
|
|
3
|
-
import { BuildIdentity, SkewStatus } from
|
|
4
|
-
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { EnvironmentProviders, InjectionToken } from "@angular/core";
|
|
3
|
+
import { BuildIdentity, SkewStatus } from "@braidlabs/skew";
|
|
5
4
|
/**
|
|
6
5
|
* What to do when a lazy chunk cannot be loaded.
|
|
7
6
|
*
|
|
@@ -10,7 +9,7 @@ import { BuildIdentity, SkewStatus } from '@braidlabs/skew';
|
|
|
10
9
|
* rejected dynamic import, and each wants a different response — which is why
|
|
11
10
|
* classification happens before dispatch.
|
|
12
11
|
*/
|
|
13
|
-
type StaleChunkAction =
|
|
12
|
+
export type StaleChunkAction =
|
|
14
13
|
/**
|
|
15
14
|
* Hard-navigate to the URL the user was trying to reach.
|
|
16
15
|
*
|
|
@@ -20,98 +19,97 @@ type StaleChunkAction =
|
|
|
20
19
|
* therefore return the user where they started and silently discard the
|
|
21
20
|
* navigation they attempted.
|
|
22
21
|
*/
|
|
23
|
-
'reload-at-target'
|
|
22
|
+
'reload-at-target' |
|
|
24
23
|
/** Refresh the current route, abandoning the attempted navigation. */
|
|
25
|
-
|
|
24
|
+
'reload-in-place' |
|
|
26
25
|
/** Client-side redirect to `fallbackRoute`. Correct when the route is gone. */
|
|
27
|
-
|
|
26
|
+
'redirect-to-fallback' |
|
|
28
27
|
/** Take no automatic action; expose it and let the application decide. */
|
|
29
|
-
|
|
28
|
+
'notify' |
|
|
30
29
|
/** Leave `NavigationError` to propagate untouched. */
|
|
31
|
-
|
|
30
|
+
'ignore';
|
|
32
31
|
/** Everything known about the failure at the moment a strategy is chosen. */
|
|
33
|
-
interface StaleChunkContext {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
32
|
+
export interface StaleChunkContext {
|
|
33
|
+
/** The URL the user was navigating to. */
|
|
34
|
+
readonly targetUrl: string;
|
|
35
|
+
/** Where they still are, since the navigation failed. */
|
|
36
|
+
readonly currentUrl: string;
|
|
37
|
+
/** Logical id passed to `lazy()`, when one was supplied. */
|
|
38
|
+
readonly moduleId?: string;
|
|
39
|
+
readonly error: unknown;
|
|
40
|
+
/** Recoveries already spent this session, for loop protection. */
|
|
41
|
+
readonly attempt: number;
|
|
42
|
+
readonly isOnline: boolean;
|
|
43
|
+
readonly clientBuildId: string;
|
|
44
|
+
/** Present only when the manifest probe succeeded. */
|
|
45
|
+
readonly serverBuildId?: string;
|
|
46
|
+
/**
|
|
47
|
+
* False when the module is absent from the new build's manifest — the route
|
|
48
|
+
* was deleted, so reloading would land on a router 404.
|
|
49
|
+
*/
|
|
50
|
+
readonly moduleStillExists: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* True when the origin reports an *older* build than the one running.
|
|
53
|
+
* Reloading fetches the same stale bundle and fails again — forever.
|
|
54
|
+
*/
|
|
55
|
+
readonly entryDocumentStale: boolean;
|
|
56
|
+
/** True when the probe reached the origin and it is ahead of us. */
|
|
57
|
+
readonly newAssetsReachable: boolean;
|
|
58
|
+
/** True when something on the page has registered unsaved work. */
|
|
59
|
+
readonly hasUnsavedWork: boolean;
|
|
61
60
|
}
|
|
62
|
-
type StaleChunkStrategy = StaleChunkAction | ((context: StaleChunkContext) => StaleChunkAction | Promise<StaleChunkAction>);
|
|
63
|
-
interface SkewRecoveryOptions {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
61
|
+
export type StaleChunkStrategy = StaleChunkAction | ((context: StaleChunkContext) => StaleChunkAction | Promise<StaleChunkAction>);
|
|
62
|
+
export interface SkewRecoveryOptions {
|
|
63
|
+
/** Identity of this build. Generate with `@braidlabs/build`. */
|
|
64
|
+
readonly identity: BuildIdentity;
|
|
65
|
+
/**
|
|
66
|
+
* Manifest the origin serves, `Cache-Control: no-store`. Without it the
|
|
67
|
+
* library still recovers, but cannot distinguish a deleted route or a stale
|
|
68
|
+
* origin — so it degrades to conservative behaviour.
|
|
69
|
+
*/
|
|
70
|
+
readonly manifestUrl?: string;
|
|
71
|
+
/** Default `'reload-at-target'`. */
|
|
72
|
+
readonly onStaleChunk?: StaleChunkStrategy;
|
|
73
|
+
/** Transient-failure retries inside `lazy()`. Default 1. */
|
|
74
|
+
readonly retryAttempts?: number;
|
|
75
|
+
/** Backoff between retries. Default 250ms. */
|
|
76
|
+
readonly retryDelayMs?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Hard cap on automatic recoveries per session. Default 1.
|
|
79
|
+
*
|
|
80
|
+
* The blunt guard against reload loops; the manifest probe is the precise
|
|
81
|
+
* one. Both exist because the failure mode — a bricked tab — is severe.
|
|
82
|
+
*/
|
|
83
|
+
readonly maxRecoveries?: number;
|
|
84
|
+
/** Target for `'redirect-to-fallback'`. Default `'/'`. */
|
|
85
|
+
readonly fallbackRoute?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Degrade to `'notify'` when unsaved work is registered. Default true.
|
|
88
|
+
*
|
|
89
|
+
* Angular does not let a library ask the router whether a `CanDeactivate`
|
|
90
|
+
* guard would block, so components opt in via `trackUnsavedWork()`.
|
|
91
|
+
*/
|
|
92
|
+
readonly respectUnsavedWork?: boolean;
|
|
94
93
|
}
|
|
95
94
|
/** Fully-resolved options, with defaults applied. */
|
|
96
|
-
interface ResolvedSkewRecoveryOptions extends SkewRecoveryOptions {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
95
|
+
export interface ResolvedSkewRecoveryOptions extends SkewRecoveryOptions {
|
|
96
|
+
readonly onStaleChunk: StaleChunkStrategy;
|
|
97
|
+
readonly retryAttempts: number;
|
|
98
|
+
readonly retryDelayMs: number;
|
|
99
|
+
readonly maxRecoveries: number;
|
|
100
|
+
readonly fallbackRoute: string;
|
|
101
|
+
readonly respectUnsavedWork: boolean;
|
|
103
102
|
}
|
|
104
|
-
declare const SKEW_RECOVERY_OPTIONS: InjectionToken<ResolvedSkewRecoveryOptions>;
|
|
105
|
-
declare function resolveOptions(options: SkewRecoveryOptions): ResolvedSkewRecoveryOptions;
|
|
103
|
+
export declare const SKEW_RECOVERY_OPTIONS: InjectionToken<ResolvedSkewRecoveryOptions>;
|
|
104
|
+
export declare function resolveOptions(options: SkewRecoveryOptions): ResolvedSkewRecoveryOptions;
|
|
106
105
|
/** A recovery the application has been asked to complete itself. */
|
|
107
|
-
interface PendingSkew {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
export interface PendingSkew {
|
|
107
|
+
readonly targetUrl: string;
|
|
108
|
+
readonly moduleId?: string;
|
|
109
|
+
readonly serverBuildId?: string;
|
|
110
|
+
readonly reason: 'notify' | 'exhausted' | 'loop-detected' | 'offline';
|
|
111
|
+
readonly context: StaleChunkContext;
|
|
113
112
|
}
|
|
114
|
-
|
|
115
113
|
/**
|
|
116
114
|
* Wrapping the dynamic import.
|
|
117
115
|
*
|
|
@@ -127,12 +125,12 @@ interface PendingSkew {
|
|
|
127
125
|
* Retry is therefore a *precondition* to recovery, not one of the strategies.
|
|
128
126
|
*/
|
|
129
127
|
/** Defaults for `lazy()`, overridable globally by `provideSkewRecovery()`. */
|
|
130
|
-
declare const lazyDefaults: {
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
export declare const lazyDefaults: {
|
|
129
|
+
retryAttempts: number;
|
|
130
|
+
retryDelayMs: number;
|
|
133
131
|
};
|
|
134
132
|
/** Restores defaults. Exposed for tests, which must not leak configuration. */
|
|
135
|
-
declare function resetLazyDefaults(): void;
|
|
133
|
+
export declare function resetLazyDefaults(): void;
|
|
136
134
|
/**
|
|
137
135
|
* Thrown when a lazy load fails after its retries are exhausted.
|
|
138
136
|
*
|
|
@@ -140,14 +138,14 @@ declare function resetLazyDefaults(): void;
|
|
|
140
138
|
* recognises it. Carrying the module id across that boundary is the whole
|
|
141
139
|
* reason this error type exists.
|
|
142
140
|
*/
|
|
143
|
-
declare class ChunkLoadFailure extends Error {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
141
|
+
export declare class ChunkLoadFailure extends Error {
|
|
142
|
+
readonly moduleId: string | undefined;
|
|
143
|
+
readonly cause: unknown;
|
|
144
|
+
readonly attempts: number;
|
|
145
|
+
readonly name = "ChunkLoadFailure";
|
|
146
|
+
constructor(moduleId: string | undefined, cause: unknown, attempts: number);
|
|
149
147
|
}
|
|
150
|
-
declare function isChunkLoadFailure(value: unknown): value is ChunkLoadFailure;
|
|
148
|
+
export declare function isChunkLoadFailure(value: unknown): value is ChunkLoadFailure;
|
|
151
149
|
/**
|
|
152
150
|
* Heuristic for "this looks like a module that failed to load" rather than an
|
|
153
151
|
* error thrown *by* the module once it ran.
|
|
@@ -156,10 +154,10 @@ declare function isChunkLoadFailure(value: unknown): value is ChunkLoadFailure;
|
|
|
156
154
|
* (`ChunkLoadError`, a bare `TypeError`, a CSP violation), and a false positive
|
|
157
155
|
* costs one wasted retry while a false negative costs the user their session.
|
|
158
156
|
*/
|
|
159
|
-
declare function looksLikeChunkError(error: unknown): boolean;
|
|
160
|
-
interface LazyOptions {
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
export declare function looksLikeChunkError(error: unknown): boolean;
|
|
158
|
+
export interface LazyOptions {
|
|
159
|
+
readonly retryAttempts?: number;
|
|
160
|
+
readonly retryDelayMs?: number;
|
|
163
161
|
}
|
|
164
162
|
/**
|
|
165
163
|
* Wraps a dynamic import for use as `loadChildren` or `loadComponent`.
|
|
@@ -172,9 +170,8 @@ interface LazyOptions {
|
|
|
172
170
|
* The returned function is a plain loader — the router needs no knowledge of
|
|
173
171
|
* this library, and routes remain usable if the package is removed.
|
|
174
172
|
*/
|
|
175
|
-
declare function lazy<T>(moduleId: string, loader: () => Promise<T>, options?: LazyOptions): () => Promise<T>;
|
|
176
|
-
declare function lazy<T>(loader: () => Promise<T>, options?: LazyOptions): () => Promise<T>;
|
|
177
|
-
|
|
173
|
+
export declare function lazy<T>(moduleId: string, loader: () => Promise<T>, options?: LazyOptions): () => Promise<T>;
|
|
174
|
+
export declare function lazy<T>(loader: () => Promise<T>, options?: LazyOptions): () => Promise<T>;
|
|
178
175
|
/**
|
|
179
176
|
* Unsaved-work tracking.
|
|
180
177
|
*
|
|
@@ -187,21 +184,21 @@ declare function lazy<T>(loader: () => Promise<T>, options?: LazyOptions): () =>
|
|
|
187
184
|
* the recovery service consults it. If Angular ever exposes guard
|
|
188
185
|
* introspection, this whole file becomes deletable.
|
|
189
186
|
*/
|
|
190
|
-
declare class UnsavedWorkRegistry {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
187
|
+
export declare class UnsavedWorkRegistry {
|
|
188
|
+
private readonly sources;
|
|
189
|
+
/** Registers a predicate. Returns a disposer. */
|
|
190
|
+
register(isDirty: () => boolean): () => void;
|
|
191
|
+
/**
|
|
192
|
+
* True when any registered source reports unsaved work.
|
|
193
|
+
*
|
|
194
|
+
* A throwing predicate is treated as *clean* rather than dirty: a broken
|
|
195
|
+
* dirty-check should not permanently block recovery and strand the user.
|
|
196
|
+
*/
|
|
197
|
+
isDirty(): boolean;
|
|
198
|
+
/** Count of registered sources. Diagnostics only. */
|
|
199
|
+
get size(): number;
|
|
200
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<UnsavedWorkRegistry, never>;
|
|
201
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
205
202
|
}
|
|
206
203
|
/**
|
|
207
204
|
* Declares that this component holds unsaved work, so automatic recovery will
|
|
@@ -218,8 +215,7 @@ declare class UnsavedWorkRegistry {
|
|
|
218
215
|
*
|
|
219
216
|
* Must be called in an injection context.
|
|
220
217
|
*/
|
|
221
|
-
declare function trackUnsavedWork(isDirty: () => boolean): void;
|
|
222
|
-
|
|
218
|
+
export declare function trackUnsavedWork(isDirty: () => boolean): void;
|
|
223
219
|
/**
|
|
224
220
|
* Orchestrates recovery from a failed lazy load.
|
|
225
221
|
*
|
|
@@ -231,47 +227,46 @@ declare function trackUnsavedWork(isDirty: () => boolean): void;
|
|
|
231
227
|
* Zoneless-safe and SSR-safe: no `NgZone`, and every `location` /
|
|
232
228
|
* `sessionStorage` touch is guarded.
|
|
233
229
|
*/
|
|
234
|
-
declare class SkewRecoveryService {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
230
|
+
export declare class SkewRecoveryService {
|
|
231
|
+
private readonly options;
|
|
232
|
+
private readonly router;
|
|
233
|
+
private readonly document;
|
|
234
|
+
private readonly unsavedWork;
|
|
235
|
+
private readonly isBrowser;
|
|
236
|
+
private readonly probe;
|
|
237
|
+
private readonly pendingSignal;
|
|
238
|
+
private readonly statusSignal;
|
|
239
|
+
/** A recovery the application has been asked to complete itself. */
|
|
240
|
+
readonly pending: import("@angular/core").Signal<PendingSkew | null>;
|
|
241
|
+
/** Last known comparison against the origin. */
|
|
242
|
+
readonly status: import("@angular/core").Signal<SkewStatus | null>;
|
|
243
|
+
/** True when a newer deployment is known to exist. */
|
|
244
|
+
readonly updateAvailable: import("@angular/core").Signal<boolean>;
|
|
245
|
+
constructor();
|
|
246
|
+
private listen;
|
|
247
|
+
/** Re-checks the origin. Safe to call from a "check for updates" control. */
|
|
248
|
+
check(): Promise<SkewStatus | null>;
|
|
249
|
+
/** Completes a deferred recovery — what a "Reload" button calls. */
|
|
250
|
+
recover(): void;
|
|
251
|
+
/** Dismisses a pending recovery without acting on it. */
|
|
252
|
+
dismiss(): void;
|
|
253
|
+
private handle;
|
|
254
|
+
private classify;
|
|
255
|
+
private chooseAction;
|
|
256
|
+
private dispatch;
|
|
257
|
+
private isOnline;
|
|
258
|
+
private assign;
|
|
259
|
+
private reload;
|
|
260
|
+
/**
|
|
261
|
+
* Recoveries already attempted, kept in `sessionStorage` so it survives the
|
|
262
|
+
* very reload it is counting. Without this, an origin serving a stale entry
|
|
263
|
+
* document turns one failure into an infinite reload loop.
|
|
264
|
+
*/
|
|
265
|
+
private recoveryCount;
|
|
266
|
+
private countRecovery;
|
|
267
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SkewRecoveryService, never>;
|
|
268
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
273
269
|
}
|
|
274
|
-
|
|
275
270
|
/**
|
|
276
271
|
* Enables skew recovery.
|
|
277
272
|
*
|
|
@@ -294,8 +289,7 @@ declare class SkewRecoveryService {
|
|
|
294
289
|
* The service is instantiated eagerly — it subscribes to router events, and a
|
|
295
290
|
* lazily-created recovery service would miss the very first failure.
|
|
296
291
|
*/
|
|
297
|
-
declare function provideSkewRecovery(options: SkewRecoveryOptions): EnvironmentProviders;
|
|
298
|
-
|
|
292
|
+
export declare function provideSkewRecovery(options: SkewRecoveryOptions): EnvironmentProviders;
|
|
299
293
|
/**
|
|
300
294
|
* ⚠️ NOT PUBLIC API. Undocumented on purpose. Do not use in an application.
|
|
301
295
|
*
|
|
@@ -328,7 +322,4 @@ declare function provideSkewRecovery(options: SkewRecoveryOptions): EnvironmentP
|
|
|
328
322
|
*
|
|
329
323
|
* @internal
|
|
330
324
|
*/
|
|
331
|
-
declare function provideSkewDisabled(disabled?: boolean): EnvironmentProviders;
|
|
332
|
-
|
|
333
|
-
export { ChunkLoadFailure, SKEW_RECOVERY_OPTIONS, SkewRecoveryService, UnsavedWorkRegistry, isChunkLoadFailure, lazy, lazyDefaults, looksLikeChunkError, provideSkewDisabled, provideSkewRecovery, resetLazyDefaults, resolveOptions, trackUnsavedWork };
|
|
334
|
-
export type { LazyOptions, PendingSkew, ResolvedSkewRecoveryOptions, SkewRecoveryOptions, StaleChunkAction, StaleChunkContext, StaleChunkStrategy };
|
|
325
|
+
export declare function provideSkewDisabled(disabled?: boolean): EnvironmentProviders;
|