import { type AddonManifestChangedMessage, type ManifestHotSwapClient, type WireHotSwapInvalidationOptions } from './manifest-hotswap-subscriber'; /** * One of three strategies for reacting to an `ADDON_MANIFEST_CHANGED` event: * * * `"rekey"` — re-mount the addon route by flipping the key. Default. * * `"page-reload"` — `window.location.reload()`. Opt-in. * * `"manual"` — no automatic action; the host handles it via `onSwap`. */ export type HotSwapReloadStrategy = 'rekey' | 'page-reload' | 'manual'; /** * Config for {@link useHotSwapReload}. `strategy` is the only required field * — pass `{ strategy: "rekey" }` for the default behaviour or omit the * config entirely. */ export interface HotSwapReloadConfig { /** Reload policy. See {@link HotSwapReloadStrategy}. */ strategy?: HotSwapReloadStrategy; /** * Optional gate invoked **before** the reload action fires. Return * `false` (or a Promise resolving to `false`) to cancel — useful for * "unsaved changes" prompts on immersive addons. Receives the original * `ADDON_MANIFEST_CHANGED` message so the prompt can name the addon. * * Runs for `"page-reload"` (cancels the `window.location.reload()`) * and `"rekey"` (cancels the version bump, leaving the addon mounted * with the old code — the host can re-trigger the swap later by * re-calling the hook output's `reload()` method). * * Ignored for `"manual"` — the host owns the reload there. */ onBeforeReload?: (event: AddonManifestChangedMessage) => boolean | Promise; /** * Side-effect hook invoked after the policy has run (or after * `onBeforeReload` returned `false`). Receives the message and the * effective action that was taken: `"rekey"`, `"page-reload"`, * `"cancelled"` or `"manual"`. Hosts wire telemetry / toasts here. */ onSwap?: (event: AddonManifestChangedMessage, action: 'rekey' | 'page-reload' | 'cancelled' | 'manual') => void; /** * Optional matcher forwarded to the underlying * {@link useManifestHotSwapSubscriber} for cache invalidation. */ matcher?: WireHotSwapInvalidationOptions['matcher']; } export interface UseHotSwapReloadResult { /** * Reactive map `addonKey → hashShort`. Stable identity per render * (only changes when a swap lands). Wire it into * `` so React * re-keys the subtree on hash change. * * Missing entries return `undefined`; the AddonRoute treats that as * "no version pinned yet" and keeps a stable key. */ addonVersionMap: Record; } /** * Subscribe to manifest hot-swap events and apply a reload policy. * * **Strategy = `"rekey"` (default):** * maintains `addonVersionMap` so `` re-keys * the subtree on every swap. The federation loader picks the new hash * up via {@link withVersionParam}, fetches a fresh `remoteEntry.js`, * and registers a new container. * * **Strategy = `"page-reload"` (opt-in):** * calls `onBeforeReload` (if supplied); if it resolves truthy, * `window.location.reload()` fires. The `addonVersionMap` is still * updated for callers that want to mirror it elsewhere. * * **Strategy = `"manual"`:** * no automatic action. The `onSwap` callback fires with `"manual"`; * the host decides what to do. `addonVersionMap` is updated so a * later opt-in remount picks up the right hash. * * @example * const ws = useWebSocket() * useManifestHotSwapSubscriber(ws) // invalidates metadata cache * const { addonVersionMap } = useHotSwapReload({ strategy: 'rekey' }) * // …in your router: * * * */ /** * Effect that {@link applyHotSwapReload} can take. Useful as a discriminator * for tests and telemetry callbacks. `"noop"` is emitted when a malformed * message is ignored (e.g. missing `addonKey`). */ export type HotSwapReloadAction = 'rekey' | 'page-reload' | 'cancelled' | 'manual' | 'noop'; export interface HotSwapReloadDeps { /** Hash → versionMap setter. Receives an updater fn, à la React state. */ setVersionMap: (updater: (prev: Record) => Record) => void; /** Defaults to `window.location.reload`. Overridable for tests / SSR. */ reload?: () => void; } /** * Pure (testable) implementation of the swap handler. Decides the action * given a message + config + deps, applies side effects via `deps`, and * returns the action it took so callers can fire telemetry. * * Exported for unit tests; the React hook below composes it with React * state. Hosts that want to drive the policy from a non-React context * (e.g. a vanilla web component shell) can call this directly. */ export declare function applyHotSwapReload(message: AddonManifestChangedMessage, config: HotSwapReloadConfig, deps: HotSwapReloadDeps): Promise; export declare function useHotSwapReload(client: ManifestHotSwapClient | undefined | null, config?: HotSwapReloadConfig): UseHotSwapReloadResult; /** * Append a `?v=` query string to a `remoteEntry.js` URL so the * browser treats it as a distinct resource and bypasses any HTTP / module * cache. Idempotent — calling twice with the same hash returns the same * URL. Preserves existing query params; replaces a previous `v=` entry if * present so successive bumps don't accumulate stale parameters. * * Pure function (no `window` access) — safe to call in SSR. * * @example * withVersionParam('/api/addons/pos/frontend/remoteEntry.js', 'abc123ef') * // → '/api/addons/pos/frontend/remoteEntry.js?v=abc123ef' * * withVersionParam('/r.js?foo=1', 'abc123ef') * // → '/r.js?foo=1&v=abc123ef' * * withVersionParam('/r.js?v=oldhash', 'abc123ef') * // → '/r.js?v=abc123ef' */ export declare function withVersionParam(url: string, hash: string | undefined): string; /** * @deprecated Legacy `@originjs/vite-plugin-federation` helper. Under the * current `@module-federation/runtime` loader ({@link AddonLoader}), container * replacement on hot-swap is handled by `registerRemotes(..., { force: true })` * with the new `?v=` URL — there is no `window[scope]` container to delete. * * Kept for backward compatibility so existing host `onSwap` wiring keeps * compiling. Best-effort: removes a stale `window[scope]` if a legacy * `@originjs` remote left one behind, otherwise a no-op. Returns `true` if a * value was removed, `false` otherwise. */ export declare function clearFederationContainer(scope: string): boolean; /** * Normalise a manifest hash for cache-busting. Accepts the full kernel * format (`sha256:abc...`), a bare hex digest, or `undefined`. Returns * an 8-character lowercase prefix that's short enough to keep URLs * readable while remaining collision-resistant across realistic addon * versioning timelines. * * Exported for tests; hosts that want the full hash for their own * telemetry should read `message.payload.newHash` directly. */ export declare function shortenHash(hash: string | undefined): string | undefined; //# sourceMappingURL=hotswap-reload-policy.d.ts.map