@real-router/preload-plugin 0.4.1 → 0.4.3

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":"index.d.ts","names":[],"sources":["../../src/types.ts","../../src/factory.ts","../../src/index.ts"],"mappings":";;;;UAEiB,oBAAA;;EAEf,KAAA;EAFe;EAIf,YAAA;AAAA;;;AAOF;;KAAY,SAAA,IAAa,MAAA,EAAQ,MAAA,KAAW,OAAA;;;;;;KAOhC,gBAAA,sBACW,mBAAA,GAAsB,mBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,SAAA;;;iBCjBW,oBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,oBAAA,IACd,aAAA;;;;YCGS,KAAA,sBAA2B,qBAAA;IACnC,OAAA,GAAU,gBAAA,CAAiB,YAAA;EAAA;EAAA,UAGnB,MAAA;IACR,iBAAA,IAAqB,IAAA,aAAiB,KAAA;IACtC,kBAAA,IAAsB,oBAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/types.ts","../../src/factory.ts","../../src/index.ts"],"mappings":";;;;UAEiB,oBAAA;;EAEf,KAAA;EAFe;EAIf,YAAY;AAAA;;AAAA;AAOd;;KAAY,SAAA,IAAa,MAAA,EAAQ,MAAA,KAAW,OAAO;;;;;;KAOvC,gBAAA,sBACW,mBAAA,GAAsB,mBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,SAAA;;;iBCjBW,oBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,oBAAA,IACd,aAAA;;;;YCGS,KAAA,sBAA2B,qBAAA;IACnC,OAAA,GAAU,gBAAA,CAAiB,YAAA;EAAA;EAAA,UAGnB,MAAA;IACR,iBAAA,IAAqB,IAAA,aAAiB,KAAA;IACtC,kBAAA,IAAsB,oBAAA;EAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#router","#api","#options","#getDependency","#removeExtensions","#compiledPreloads","#stateCache","#handleMouseOver","#handleTouchStart","#handleTouchMove","#cleanup","#isGhostMouseEvent","#findAnchor","#currentAnchor","#cancelHover","#resolveAnchorPreload","#hoverTimer","#lastTouchTarget","#lastTouchTimeStamp","#cancelTouch","#touchStartY","#touchTimer","#resolvePreload","#cacheState"],"sources":["../../src/constants.ts","../../src/network.ts","../../src/plugin.ts","../../src/factory.ts"],"sourcesContent":["import type { PreloadPluginOptions } from \"./types\";\n\nexport const defaultOptions: Required<PreloadPluginOptions> = {\n delay: 65,\n networkAware: true,\n};\n\nexport const GHOST_EVENT_THRESHOLD = 2500;\n\nexport const TOUCH_SCROLL_THRESHOLD = 10;\n\nexport const TOUCH_PRELOAD_DELAY = 100;\n\nexport const LISTENER_OPTIONS: AddEventListenerOptions = {\n capture: true,\n passive: true,\n};\n","type NetworkConnection =\n | { saveData?: boolean; effectiveType?: string }\n | undefined;\n\ninterface NavigatorWithConnection extends Navigator {\n connection?: NetworkConnection;\n}\n\nexport function isSlowConnection(): boolean {\n const connection = (navigator as NavigatorWithConnection).connection;\n\n if (!connection) {\n return false;\n }\n if (connection.saveData) {\n return true;\n }\n if (connection.effectiveType?.includes(\"2g\")) {\n return true;\n }\n\n return false;\n}\n","import {\n GHOST_EVENT_THRESHOLD,\n LISTENER_OPTIONS,\n TOUCH_PRELOAD_DELAY,\n TOUCH_SCROLL_THRESHOLD,\n} from \"./constants\";\nimport { isSlowConnection } from \"./network\";\n\nimport type {\n PreloadFn,\n PreloadFnFactory,\n PreloadPluginOptions,\n} from \"./types\";\nimport type {\n Params,\n Plugin,\n PluginFactory,\n Router,\n State,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\ndeclare module \"@real-router/core\" {\n interface Router {\n matchUrl?: (url: string) => State | undefined;\n }\n}\n\nconst STATE_CACHE_LIMIT = 32;\n\nexport class PreloadPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: Required<PreloadPluginOptions>;\n readonly #getDependency: Parameters<PluginFactory>[1];\n readonly #removeExtensions: () => void;\n readonly #compiledPreloads = new Map<\n string,\n { fn: PreloadFn; factory: PreloadFnFactory }\n >();\n // Pre-resolved State cache keyed by anchor href. Populated when a hover/touch\n // resolves a route via router.matchUrl, consumed once via\n // router.getPreloadedState(href). Single-use semantics (delete-on-read) keep\n // the cache from drifting out of sync with current world state — once the\n // consumer commits the snapshot via api.navigateToState, the entry is gone.\n // Bounded with insertion-order eviction (#562).\n readonly #stateCache = new Map<string, State>();\n\n #currentAnchor: HTMLAnchorElement | null = null;\n #hoverTimer: ReturnType<typeof setTimeout> | null = null;\n #touchTimer: ReturnType<typeof setTimeout> | null = null;\n #touchStartY = 0;\n #lastTouchTarget: EventTarget | null = null;\n #lastTouchTimeStamp = Number.NaN;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: Required<PreloadPluginOptions>,\n getDependency: Parameters<PluginFactory>[1],\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#getDependency = getDependency;\n\n const cachedOptions = { ...options };\n\n this.#removeExtensions = api.extendRouter({\n getPreloadSettings: () => cachedOptions,\n getPreloadedState: (href: string): State | undefined => {\n const state = this.#stateCache.get(href);\n\n if (state) {\n this.#stateCache.delete(href);\n }\n\n return state;\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n document.addEventListener(\n \"mouseover\",\n this.#handleMouseOver,\n LISTENER_OPTIONS,\n );\n document.addEventListener(\n \"touchstart\",\n this.#handleTouchStart,\n LISTENER_OPTIONS,\n );\n document.addEventListener(\n \"touchmove\",\n this.#handleTouchMove,\n LISTENER_OPTIONS,\n );\n },\n\n onStop: () => {\n this.#cleanup();\n },\n\n teardown: () => {\n this.#cleanup();\n this.#removeExtensions();\n },\n };\n }\n\n readonly #handleMouseOver = (event: MouseEvent): void => {\n if (this.#isGhostMouseEvent(event)) {\n return;\n }\n\n const anchor = this.#findAnchor(event.target);\n\n if (anchor === this.#currentAnchor) {\n return;\n }\n\n this.#cancelHover();\n this.#currentAnchor = anchor;\n\n const preload = this.#resolveAnchorPreload(anchor);\n\n if (!preload) {\n return;\n }\n\n this.#hoverTimer = setTimeout(() => {\n this.#hoverTimer = null;\n preload.fn(preload.params).catch(() => {});\n }, this.#options.delay);\n };\n\n readonly #handleTouchStart = (event: TouchEvent): void => {\n this.#lastTouchTarget = event.target;\n this.#lastTouchTimeStamp = event.timeStamp;\n\n this.#cancelTouch();\n\n const anchor = this.#findAnchor(event.target);\n const preload = this.#resolveAnchorPreload(anchor);\n\n if (!preload || event.touches.length === 0) {\n return;\n }\n\n this.#touchStartY = event.touches[0].clientY;\n\n this.#touchTimer = setTimeout(() => {\n this.#touchTimer = null;\n preload.fn(preload.params).catch(() => {});\n }, TOUCH_PRELOAD_DELAY);\n };\n\n readonly #handleTouchMove = (event: TouchEvent): void => {\n if (this.#touchTimer === null || event.touches.length === 0) {\n return;\n }\n\n const deltaY = Math.abs(event.touches[0].clientY - this.#touchStartY);\n\n if (deltaY > TOUCH_SCROLL_THRESHOLD) {\n this.#cancelTouch();\n }\n };\n\n #findAnchor(target: EventTarget | null): HTMLAnchorElement | null {\n return target instanceof Element\n ? target.closest<HTMLAnchorElement>(\"a[href]\")\n : null;\n }\n\n #resolveAnchorPreload(\n anchor: HTMLAnchorElement | null | undefined,\n ): { fn: PreloadFn; params: Params } | undefined {\n if (!anchor) {\n return undefined;\n }\n\n if (\"noPreload\" in anchor.dataset) {\n return undefined;\n }\n\n if (this.#options.networkAware && isSlowConnection()) {\n return undefined;\n }\n\n return this.#resolvePreload(anchor);\n }\n\n #resolvePreload(\n anchor: HTMLAnchorElement,\n ): { fn: PreloadFn; params: Params } | undefined {\n const state = this.#router.matchUrl?.(anchor.href);\n\n if (!state) {\n return undefined;\n }\n\n this.#cacheState(anchor.href, state);\n\n const config = this.#api.getRouteConfig(state.name);\n const factory =\n typeof config?.preload === \"function\"\n ? (config.preload as PreloadFnFactory)\n : undefined;\n\n if (!factory) {\n this.#compiledPreloads.delete(state.name);\n\n return undefined;\n }\n\n const cached = this.#compiledPreloads.get(state.name);\n\n if (cached?.factory === factory) {\n return { fn: cached.fn, params: state.params };\n }\n\n let fn: PreloadFn;\n\n try {\n fn = factory(this.#router, this.#getDependency);\n } catch {\n return undefined;\n }\n\n this.#compiledPreloads.set(state.name, { fn, factory });\n\n return { fn, params: state.params };\n }\n\n #cacheState(href: string, state: State): void {\n // Re-insert to refresh recency ordering (Map iteration is insertion order).\n if (this.#stateCache.has(href)) {\n this.#stateCache.delete(href);\n } else if (this.#stateCache.size >= STATE_CACHE_LIMIT) {\n // size >= LIMIT > 0 → iterator has at least one key.\n for (const oldest of this.#stateCache.keys()) {\n this.#stateCache.delete(oldest);\n\n break;\n }\n }\n\n this.#stateCache.set(href, state);\n }\n\n #isGhostMouseEvent(event: MouseEvent): boolean {\n const delta = event.timeStamp - this.#lastTouchTimeStamp;\n\n return (\n delta >= 0 &&\n delta < GHOST_EVENT_THRESHOLD &&\n event.target === this.#lastTouchTarget\n );\n }\n\n #cancelHover(): void {\n if (this.#hoverTimer !== null) {\n clearTimeout(this.#hoverTimer);\n this.#hoverTimer = null;\n }\n\n this.#currentAnchor = null;\n }\n\n #cancelTouch(): void {\n if (this.#touchTimer !== null) {\n clearTimeout(this.#touchTimer);\n this.#touchTimer = null;\n }\n }\n\n #cleanup(): void {\n document.removeEventListener(\n \"mouseover\",\n this.#handleMouseOver,\n LISTENER_OPTIONS,\n );\n document.removeEventListener(\n \"touchstart\",\n this.#handleTouchStart,\n LISTENER_OPTIONS,\n );\n document.removeEventListener(\n \"touchmove\",\n this.#handleTouchMove,\n LISTENER_OPTIONS,\n );\n\n this.#cancelHover();\n this.#cancelTouch();\n this.#lastTouchTarget = null;\n this.#lastTouchTimeStamp = Number.NaN;\n this.#stateCache.clear();\n }\n}\n","import { getPluginApi } from \"@real-router/core/api\";\n\nimport { defaultOptions } from \"./constants\";\nimport { PreloadPlugin } from \"./plugin\";\n\nimport type { PreloadPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\nexport function preloadPluginFactory(\n opts?: Partial<PreloadPluginOptions>,\n): PluginFactory {\n const options: Required<PreloadPluginOptions> = {\n ...defaultOptions,\n ...opts,\n };\n\n if (!Number.isFinite(options.delay) || options.delay < 0) {\n options.delay = 0;\n }\n\n return function preloadPlugin(routerBase, getDependency) {\n if (typeof document === \"undefined\") {\n return {};\n }\n\n const plugin = new PreloadPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n getDependency,\n );\n\n return plugin.getPlugin();\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#router","#api","#options","#getDependency","#removeExtensions","#compiledPreloads","#stateCache","#handleMouseOver","#handleTouchStart","#handleTouchMove","#cleanup","#isGhostMouseEvent","#findAnchor","#currentAnchor","#cancelHover","#resolveAnchorPreload","#hoverTimer","#lastTouchTarget","#lastTouchTimeStamp","#cancelTouch","#touchStartY","#touchTimer","#resolvePreload","#cacheState"],"sources":["../../src/constants.ts","../../src/network.ts","../../src/plugin.ts","../../src/factory.ts"],"sourcesContent":["import type { PreloadPluginOptions } from \"./types\";\n\nexport const defaultOptions: Required<PreloadPluginOptions> = {\n delay: 65,\n networkAware: true,\n};\n\nexport const GHOST_EVENT_THRESHOLD = 2500;\n\nexport const TOUCH_SCROLL_THRESHOLD = 10;\n\nexport const TOUCH_PRELOAD_DELAY = 100;\n\nexport const LISTENER_OPTIONS: AddEventListenerOptions = {\n capture: true,\n passive: true,\n};\n","type NetworkConnection =\n | { saveData?: boolean; effectiveType?: string }\n | undefined;\n\ninterface NavigatorWithConnection extends Navigator {\n connection?: NetworkConnection;\n}\n\nexport function isSlowConnection(): boolean {\n const connection = (navigator as NavigatorWithConnection).connection;\n\n if (!connection) {\n return false;\n }\n if (connection.saveData) {\n return true;\n }\n if (connection.effectiveType?.includes(\"2g\")) {\n return true;\n }\n\n return false;\n}\n","import {\n GHOST_EVENT_THRESHOLD,\n LISTENER_OPTIONS,\n TOUCH_PRELOAD_DELAY,\n TOUCH_SCROLL_THRESHOLD,\n} from \"./constants\";\nimport { isSlowConnection } from \"./network\";\n\nimport type {\n PreloadFn,\n PreloadFnFactory,\n PreloadPluginOptions,\n} from \"./types\";\nimport type {\n Params,\n Plugin,\n PluginFactory,\n Router,\n State,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\ndeclare module \"@real-router/core\" {\n interface Router {\n matchUrl?: (url: string) => State | undefined;\n }\n}\n\nconst STATE_CACHE_LIMIT = 32;\n\nexport class PreloadPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: Required<PreloadPluginOptions>;\n readonly #getDependency: Parameters<PluginFactory>[1];\n readonly #removeExtensions: () => void;\n readonly #compiledPreloads = new Map<\n string,\n { fn: PreloadFn; factory: PreloadFnFactory }\n >();\n // Pre-resolved State cache keyed by anchor href. Populated when a hover/touch\n // resolves a route via router.matchUrl, consumed once via\n // router.getPreloadedState(href). Single-use semantics (delete-on-read) keep\n // the cache from drifting out of sync with current world state — once the\n // consumer commits the snapshot via api.navigateToState, the entry is gone.\n // Bounded with insertion-order eviction (#562).\n readonly #stateCache = new Map<string, State>();\n\n #currentAnchor: HTMLAnchorElement | null = null;\n #hoverTimer: ReturnType<typeof setTimeout> | null = null;\n #touchTimer: ReturnType<typeof setTimeout> | null = null;\n #touchStartY = 0;\n #lastTouchTarget: EventTarget | null = null;\n #lastTouchTimeStamp = Number.NaN;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: Required<PreloadPluginOptions>,\n getDependency: Parameters<PluginFactory>[1],\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#getDependency = getDependency;\n\n const cachedOptions = { ...options };\n\n this.#removeExtensions = api.extendRouter({\n getPreloadSettings: () => cachedOptions,\n getPreloadedState: (href: string): State | undefined => {\n const state = this.#stateCache.get(href);\n\n if (state) {\n this.#stateCache.delete(href);\n }\n\n return state;\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n document.addEventListener(\n \"mouseover\",\n this.#handleMouseOver,\n LISTENER_OPTIONS,\n );\n document.addEventListener(\n \"touchstart\",\n this.#handleTouchStart,\n LISTENER_OPTIONS,\n );\n document.addEventListener(\n \"touchmove\",\n this.#handleTouchMove,\n LISTENER_OPTIONS,\n );\n },\n\n onStop: () => {\n this.#cleanup();\n },\n\n teardown: () => {\n this.#cleanup();\n this.#removeExtensions();\n },\n };\n }\n\n readonly #handleMouseOver = (event: MouseEvent): void => {\n if (this.#isGhostMouseEvent(event)) {\n return;\n }\n\n const anchor = this.#findAnchor(event.target);\n\n if (anchor === this.#currentAnchor) {\n return;\n }\n\n this.#cancelHover();\n this.#currentAnchor = anchor;\n\n const preload = this.#resolveAnchorPreload(anchor);\n\n if (!preload) {\n return;\n }\n\n this.#hoverTimer = setTimeout(() => {\n this.#hoverTimer = null;\n preload.fn(preload.params).catch(() => {});\n }, this.#options.delay);\n };\n\n readonly #handleTouchStart = (event: TouchEvent): void => {\n this.#lastTouchTarget = event.target;\n this.#lastTouchTimeStamp = event.timeStamp;\n\n this.#cancelTouch();\n\n const anchor = this.#findAnchor(event.target);\n const preload = this.#resolveAnchorPreload(anchor);\n\n if (!preload || event.touches.length === 0) {\n return;\n }\n\n this.#touchStartY = event.touches[0].clientY;\n\n this.#touchTimer = setTimeout(() => {\n this.#touchTimer = null;\n preload.fn(preload.params).catch(() => {});\n }, TOUCH_PRELOAD_DELAY);\n };\n\n readonly #handleTouchMove = (event: TouchEvent): void => {\n if (this.#touchTimer === null || event.touches.length === 0) {\n return;\n }\n\n const deltaY = Math.abs(event.touches[0].clientY - this.#touchStartY);\n\n if (deltaY > TOUCH_SCROLL_THRESHOLD) {\n this.#cancelTouch();\n }\n };\n\n #findAnchor(target: EventTarget | null): HTMLAnchorElement | null {\n return target instanceof Element\n ? target.closest<HTMLAnchorElement>(\"a[href]\")\n : null;\n }\n\n #resolveAnchorPreload(\n anchor: HTMLAnchorElement | null | undefined,\n ): { fn: PreloadFn; params: Params } | undefined {\n if (!anchor) {\n return undefined;\n }\n\n if (\"noPreload\" in anchor.dataset) {\n return undefined;\n }\n\n if (this.#options.networkAware && isSlowConnection()) {\n return undefined;\n }\n\n return this.#resolvePreload(anchor);\n }\n\n #resolvePreload(\n anchor: HTMLAnchorElement,\n ): { fn: PreloadFn; params: Params } | undefined {\n const state = this.#router.matchUrl?.(anchor.href);\n\n if (!state) {\n return undefined;\n }\n\n this.#cacheState(anchor.href, state);\n\n const config = this.#api.getRouteConfig(state.name);\n const factory =\n typeof config?.preload === \"function\"\n ? (config.preload as PreloadFnFactory)\n : undefined;\n\n if (!factory) {\n this.#compiledPreloads.delete(state.name);\n\n return undefined;\n }\n\n const cached = this.#compiledPreloads.get(state.name);\n\n if (cached?.factory === factory) {\n return { fn: cached.fn, params: state.params };\n }\n\n let fn: PreloadFn;\n\n try {\n fn = factory(this.#router, this.#getDependency);\n } catch {\n return undefined;\n }\n\n this.#compiledPreloads.set(state.name, { fn, factory });\n\n return { fn, params: state.params };\n }\n\n #cacheState(href: string, state: State): void {\n // Re-insert to refresh recency ordering (Map iteration is insertion order).\n if (this.#stateCache.has(href)) {\n this.#stateCache.delete(href);\n } else if (this.#stateCache.size >= STATE_CACHE_LIMIT) {\n // size >= LIMIT > 0 → iterator has at least one key.\n for (const oldest of this.#stateCache.keys()) {\n this.#stateCache.delete(oldest);\n\n break;\n }\n }\n\n this.#stateCache.set(href, state);\n }\n\n #isGhostMouseEvent(event: MouseEvent): boolean {\n const delta = event.timeStamp - this.#lastTouchTimeStamp;\n\n return (\n delta >= 0 &&\n delta < GHOST_EVENT_THRESHOLD &&\n event.target === this.#lastTouchTarget\n );\n }\n\n #cancelHover(): void {\n if (this.#hoverTimer !== null) {\n clearTimeout(this.#hoverTimer);\n this.#hoverTimer = null;\n }\n\n this.#currentAnchor = null;\n }\n\n #cancelTouch(): void {\n if (this.#touchTimer !== null) {\n clearTimeout(this.#touchTimer);\n this.#touchTimer = null;\n }\n }\n\n #cleanup(): void {\n document.removeEventListener(\n \"mouseover\",\n this.#handleMouseOver,\n LISTENER_OPTIONS,\n );\n document.removeEventListener(\n \"touchstart\",\n this.#handleTouchStart,\n LISTENER_OPTIONS,\n );\n document.removeEventListener(\n \"touchmove\",\n this.#handleTouchMove,\n LISTENER_OPTIONS,\n );\n\n this.#cancelHover();\n this.#cancelTouch();\n this.#lastTouchTarget = null;\n this.#lastTouchTimeStamp = Number.NaN;\n this.#stateCache.clear();\n }\n}\n","import { getPluginApi } from \"@real-router/core/api\";\n\nimport { defaultOptions } from \"./constants\";\nimport { PreloadPlugin } from \"./plugin\";\n\nimport type { PreloadPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\nexport function preloadPluginFactory(\n opts?: Partial<PreloadPluginOptions>,\n): PluginFactory {\n const options: Required<PreloadPluginOptions> = {\n ...defaultOptions,\n ...opts,\n };\n\n if (!Number.isFinite(options.delay) || options.delay < 0) {\n options.delay = 0;\n }\n\n return function preloadPlugin(routerBase, getDependency) {\n if (typeof document === \"undefined\") {\n return {};\n }\n\n const plugin = new PreloadPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n getDependency,\n );\n\n return plugin.getPlugin();\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/types.ts","../../src/factory.ts","../../src/index.ts"],"mappings":";;;;UAEiB,oBAAA;;EAEf,KAAA;EAFe;EAIf,YAAA;AAAA;;;AAOF;;KAAY,SAAA,IAAa,MAAA,EAAQ,MAAA,KAAW,OAAA;;;;;;KAOhC,gBAAA,sBACW,mBAAA,GAAsB,mBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,SAAA;;;iBCjBW,oBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,oBAAA,IACd,aAAA;;;;YCGS,KAAA,sBAA2B,qBAAA;IACnC,OAAA,GAAU,gBAAA,CAAiB,YAAA;EAAA;EAAA,UAGnB,MAAA;IACR,iBAAA,IAAqB,IAAA,aAAiB,KAAA;IACtC,kBAAA,IAAsB,oBAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/types.ts","../../src/factory.ts","../../src/index.ts"],"mappings":";;;;UAEiB,oBAAA;;EAEf,KAAA;EAFe;EAIf,YAAY;AAAA;;AAAA;AAOd;;KAAY,SAAA,IAAa,MAAA,EAAQ,MAAA,KAAW,OAAO;;;;;;KAOvC,gBAAA,sBACW,mBAAA,GAAsB,mBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,SAAA;;;iBCjBW,oBAAA,CACd,IAAA,GAAO,OAAA,CAAQ,oBAAA,IACd,aAAA;;;;YCGS,KAAA,sBAA2B,qBAAA;IACnC,OAAA,GAAU,gBAAA,CAAiB,YAAA;EAAA;EAAA,UAGnB,MAAA;IACR,iBAAA,IAAqB,IAAA,aAAiB,KAAA;IACtC,kBAAA,IAAsB,oBAAA;EAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["#router","#api","#options","#getDependency","#removeExtensions","#compiledPreloads","#stateCache","#handleMouseOver","#handleTouchStart","#handleTouchMove","#cleanup","#isGhostMouseEvent","#findAnchor","#currentAnchor","#cancelHover","#resolveAnchorPreload","#hoverTimer","#lastTouchTarget","#lastTouchTimeStamp","#cancelTouch","#touchStartY","#touchTimer","#resolvePreload","#cacheState"],"sources":["../../src/constants.ts","../../src/network.ts","../../src/plugin.ts","../../src/factory.ts"],"sourcesContent":["import type { PreloadPluginOptions } from \"./types\";\n\nexport const defaultOptions: Required<PreloadPluginOptions> = {\n delay: 65,\n networkAware: true,\n};\n\nexport const GHOST_EVENT_THRESHOLD = 2500;\n\nexport const TOUCH_SCROLL_THRESHOLD = 10;\n\nexport const TOUCH_PRELOAD_DELAY = 100;\n\nexport const LISTENER_OPTIONS: AddEventListenerOptions = {\n capture: true,\n passive: true,\n};\n","type NetworkConnection =\n | { saveData?: boolean; effectiveType?: string }\n | undefined;\n\ninterface NavigatorWithConnection extends Navigator {\n connection?: NetworkConnection;\n}\n\nexport function isSlowConnection(): boolean {\n const connection = (navigator as NavigatorWithConnection).connection;\n\n if (!connection) {\n return false;\n }\n if (connection.saveData) {\n return true;\n }\n if (connection.effectiveType?.includes(\"2g\")) {\n return true;\n }\n\n return false;\n}\n","import {\n GHOST_EVENT_THRESHOLD,\n LISTENER_OPTIONS,\n TOUCH_PRELOAD_DELAY,\n TOUCH_SCROLL_THRESHOLD,\n} from \"./constants\";\nimport { isSlowConnection } from \"./network\";\n\nimport type {\n PreloadFn,\n PreloadFnFactory,\n PreloadPluginOptions,\n} from \"./types\";\nimport type {\n Params,\n Plugin,\n PluginFactory,\n Router,\n State,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\ndeclare module \"@real-router/core\" {\n interface Router {\n matchUrl?: (url: string) => State | undefined;\n }\n}\n\nconst STATE_CACHE_LIMIT = 32;\n\nexport class PreloadPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: Required<PreloadPluginOptions>;\n readonly #getDependency: Parameters<PluginFactory>[1];\n readonly #removeExtensions: () => void;\n readonly #compiledPreloads = new Map<\n string,\n { fn: PreloadFn; factory: PreloadFnFactory }\n >();\n // Pre-resolved State cache keyed by anchor href. Populated when a hover/touch\n // resolves a route via router.matchUrl, consumed once via\n // router.getPreloadedState(href). Single-use semantics (delete-on-read) keep\n // the cache from drifting out of sync with current world state — once the\n // consumer commits the snapshot via api.navigateToState, the entry is gone.\n // Bounded with insertion-order eviction (#562).\n readonly #stateCache = new Map<string, State>();\n\n #currentAnchor: HTMLAnchorElement | null = null;\n #hoverTimer: ReturnType<typeof setTimeout> | null = null;\n #touchTimer: ReturnType<typeof setTimeout> | null = null;\n #touchStartY = 0;\n #lastTouchTarget: EventTarget | null = null;\n #lastTouchTimeStamp = Number.NaN;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: Required<PreloadPluginOptions>,\n getDependency: Parameters<PluginFactory>[1],\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#getDependency = getDependency;\n\n const cachedOptions = { ...options };\n\n this.#removeExtensions = api.extendRouter({\n getPreloadSettings: () => cachedOptions,\n getPreloadedState: (href: string): State | undefined => {\n const state = this.#stateCache.get(href);\n\n if (state) {\n this.#stateCache.delete(href);\n }\n\n return state;\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n document.addEventListener(\n \"mouseover\",\n this.#handleMouseOver,\n LISTENER_OPTIONS,\n );\n document.addEventListener(\n \"touchstart\",\n this.#handleTouchStart,\n LISTENER_OPTIONS,\n );\n document.addEventListener(\n \"touchmove\",\n this.#handleTouchMove,\n LISTENER_OPTIONS,\n );\n },\n\n onStop: () => {\n this.#cleanup();\n },\n\n teardown: () => {\n this.#cleanup();\n this.#removeExtensions();\n },\n };\n }\n\n readonly #handleMouseOver = (event: MouseEvent): void => {\n if (this.#isGhostMouseEvent(event)) {\n return;\n }\n\n const anchor = this.#findAnchor(event.target);\n\n if (anchor === this.#currentAnchor) {\n return;\n }\n\n this.#cancelHover();\n this.#currentAnchor = anchor;\n\n const preload = this.#resolveAnchorPreload(anchor);\n\n if (!preload) {\n return;\n }\n\n this.#hoverTimer = setTimeout(() => {\n this.#hoverTimer = null;\n preload.fn(preload.params).catch(() => {});\n }, this.#options.delay);\n };\n\n readonly #handleTouchStart = (event: TouchEvent): void => {\n this.#lastTouchTarget = event.target;\n this.#lastTouchTimeStamp = event.timeStamp;\n\n this.#cancelTouch();\n\n const anchor = this.#findAnchor(event.target);\n const preload = this.#resolveAnchorPreload(anchor);\n\n if (!preload || event.touches.length === 0) {\n return;\n }\n\n this.#touchStartY = event.touches[0].clientY;\n\n this.#touchTimer = setTimeout(() => {\n this.#touchTimer = null;\n preload.fn(preload.params).catch(() => {});\n }, TOUCH_PRELOAD_DELAY);\n };\n\n readonly #handleTouchMove = (event: TouchEvent): void => {\n if (this.#touchTimer === null || event.touches.length === 0) {\n return;\n }\n\n const deltaY = Math.abs(event.touches[0].clientY - this.#touchStartY);\n\n if (deltaY > TOUCH_SCROLL_THRESHOLD) {\n this.#cancelTouch();\n }\n };\n\n #findAnchor(target: EventTarget | null): HTMLAnchorElement | null {\n return target instanceof Element\n ? target.closest<HTMLAnchorElement>(\"a[href]\")\n : null;\n }\n\n #resolveAnchorPreload(\n anchor: HTMLAnchorElement | null | undefined,\n ): { fn: PreloadFn; params: Params } | undefined {\n if (!anchor) {\n return undefined;\n }\n\n if (\"noPreload\" in anchor.dataset) {\n return undefined;\n }\n\n if (this.#options.networkAware && isSlowConnection()) {\n return undefined;\n }\n\n return this.#resolvePreload(anchor);\n }\n\n #resolvePreload(\n anchor: HTMLAnchorElement,\n ): { fn: PreloadFn; params: Params } | undefined {\n const state = this.#router.matchUrl?.(anchor.href);\n\n if (!state) {\n return undefined;\n }\n\n this.#cacheState(anchor.href, state);\n\n const config = this.#api.getRouteConfig(state.name);\n const factory =\n typeof config?.preload === \"function\"\n ? (config.preload as PreloadFnFactory)\n : undefined;\n\n if (!factory) {\n this.#compiledPreloads.delete(state.name);\n\n return undefined;\n }\n\n const cached = this.#compiledPreloads.get(state.name);\n\n if (cached?.factory === factory) {\n return { fn: cached.fn, params: state.params };\n }\n\n let fn: PreloadFn;\n\n try {\n fn = factory(this.#router, this.#getDependency);\n } catch {\n return undefined;\n }\n\n this.#compiledPreloads.set(state.name, { fn, factory });\n\n return { fn, params: state.params };\n }\n\n #cacheState(href: string, state: State): void {\n // Re-insert to refresh recency ordering (Map iteration is insertion order).\n if (this.#stateCache.has(href)) {\n this.#stateCache.delete(href);\n } else if (this.#stateCache.size >= STATE_CACHE_LIMIT) {\n // size >= LIMIT > 0 → iterator has at least one key.\n for (const oldest of this.#stateCache.keys()) {\n this.#stateCache.delete(oldest);\n\n break;\n }\n }\n\n this.#stateCache.set(href, state);\n }\n\n #isGhostMouseEvent(event: MouseEvent): boolean {\n const delta = event.timeStamp - this.#lastTouchTimeStamp;\n\n return (\n delta >= 0 &&\n delta < GHOST_EVENT_THRESHOLD &&\n event.target === this.#lastTouchTarget\n );\n }\n\n #cancelHover(): void {\n if (this.#hoverTimer !== null) {\n clearTimeout(this.#hoverTimer);\n this.#hoverTimer = null;\n }\n\n this.#currentAnchor = null;\n }\n\n #cancelTouch(): void {\n if (this.#touchTimer !== null) {\n clearTimeout(this.#touchTimer);\n this.#touchTimer = null;\n }\n }\n\n #cleanup(): void {\n document.removeEventListener(\n \"mouseover\",\n this.#handleMouseOver,\n LISTENER_OPTIONS,\n );\n document.removeEventListener(\n \"touchstart\",\n this.#handleTouchStart,\n LISTENER_OPTIONS,\n );\n document.removeEventListener(\n \"touchmove\",\n this.#handleTouchMove,\n LISTENER_OPTIONS,\n );\n\n this.#cancelHover();\n this.#cancelTouch();\n this.#lastTouchTarget = null;\n this.#lastTouchTimeStamp = Number.NaN;\n this.#stateCache.clear();\n }\n}\n","import { getPluginApi } from \"@real-router/core/api\";\n\nimport { defaultOptions } from \"./constants\";\nimport { PreloadPlugin } from \"./plugin\";\n\nimport type { PreloadPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\nexport function preloadPluginFactory(\n opts?: Partial<PreloadPluginOptions>,\n): PluginFactory {\n const options: Required<PreloadPluginOptions> = {\n ...defaultOptions,\n ...opts,\n };\n\n if (!Number.isFinite(options.delay) || options.delay < 0) {\n options.delay = 0;\n }\n\n return function preloadPlugin(routerBase, getDependency) {\n if (typeof document === \"undefined\") {\n return {};\n }\n\n const plugin = new PreloadPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n getDependency,\n );\n\n return plugin.getPlugin();\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["#router","#api","#options","#getDependency","#removeExtensions","#compiledPreloads","#stateCache","#handleMouseOver","#handleTouchStart","#handleTouchMove","#cleanup","#isGhostMouseEvent","#findAnchor","#currentAnchor","#cancelHover","#resolveAnchorPreload","#hoverTimer","#lastTouchTarget","#lastTouchTimeStamp","#cancelTouch","#touchStartY","#touchTimer","#resolvePreload","#cacheState"],"sources":["../../src/constants.ts","../../src/network.ts","../../src/plugin.ts","../../src/factory.ts"],"sourcesContent":["import type { PreloadPluginOptions } from \"./types\";\n\nexport const defaultOptions: Required<PreloadPluginOptions> = {\n delay: 65,\n networkAware: true,\n};\n\nexport const GHOST_EVENT_THRESHOLD = 2500;\n\nexport const TOUCH_SCROLL_THRESHOLD = 10;\n\nexport const TOUCH_PRELOAD_DELAY = 100;\n\nexport const LISTENER_OPTIONS: AddEventListenerOptions = {\n capture: true,\n passive: true,\n};\n","type NetworkConnection =\n | { saveData?: boolean; effectiveType?: string }\n | undefined;\n\ninterface NavigatorWithConnection extends Navigator {\n connection?: NetworkConnection;\n}\n\nexport function isSlowConnection(): boolean {\n const connection = (navigator as NavigatorWithConnection).connection;\n\n if (!connection) {\n return false;\n }\n if (connection.saveData) {\n return true;\n }\n if (connection.effectiveType?.includes(\"2g\")) {\n return true;\n }\n\n return false;\n}\n","import {\n GHOST_EVENT_THRESHOLD,\n LISTENER_OPTIONS,\n TOUCH_PRELOAD_DELAY,\n TOUCH_SCROLL_THRESHOLD,\n} from \"./constants\";\nimport { isSlowConnection } from \"./network\";\n\nimport type {\n PreloadFn,\n PreloadFnFactory,\n PreloadPluginOptions,\n} from \"./types\";\nimport type {\n Params,\n Plugin,\n PluginFactory,\n Router,\n State,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\ndeclare module \"@real-router/core\" {\n interface Router {\n matchUrl?: (url: string) => State | undefined;\n }\n}\n\nconst STATE_CACHE_LIMIT = 32;\n\nexport class PreloadPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: Required<PreloadPluginOptions>;\n readonly #getDependency: Parameters<PluginFactory>[1];\n readonly #removeExtensions: () => void;\n readonly #compiledPreloads = new Map<\n string,\n { fn: PreloadFn; factory: PreloadFnFactory }\n >();\n // Pre-resolved State cache keyed by anchor href. Populated when a hover/touch\n // resolves a route via router.matchUrl, consumed once via\n // router.getPreloadedState(href). Single-use semantics (delete-on-read) keep\n // the cache from drifting out of sync with current world state — once the\n // consumer commits the snapshot via api.navigateToState, the entry is gone.\n // Bounded with insertion-order eviction (#562).\n readonly #stateCache = new Map<string, State>();\n\n #currentAnchor: HTMLAnchorElement | null = null;\n #hoverTimer: ReturnType<typeof setTimeout> | null = null;\n #touchTimer: ReturnType<typeof setTimeout> | null = null;\n #touchStartY = 0;\n #lastTouchTarget: EventTarget | null = null;\n #lastTouchTimeStamp = Number.NaN;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: Required<PreloadPluginOptions>,\n getDependency: Parameters<PluginFactory>[1],\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#getDependency = getDependency;\n\n const cachedOptions = { ...options };\n\n this.#removeExtensions = api.extendRouter({\n getPreloadSettings: () => cachedOptions,\n getPreloadedState: (href: string): State | undefined => {\n const state = this.#stateCache.get(href);\n\n if (state) {\n this.#stateCache.delete(href);\n }\n\n return state;\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n document.addEventListener(\n \"mouseover\",\n this.#handleMouseOver,\n LISTENER_OPTIONS,\n );\n document.addEventListener(\n \"touchstart\",\n this.#handleTouchStart,\n LISTENER_OPTIONS,\n );\n document.addEventListener(\n \"touchmove\",\n this.#handleTouchMove,\n LISTENER_OPTIONS,\n );\n },\n\n onStop: () => {\n this.#cleanup();\n },\n\n teardown: () => {\n this.#cleanup();\n this.#removeExtensions();\n },\n };\n }\n\n readonly #handleMouseOver = (event: MouseEvent): void => {\n if (this.#isGhostMouseEvent(event)) {\n return;\n }\n\n const anchor = this.#findAnchor(event.target);\n\n if (anchor === this.#currentAnchor) {\n return;\n }\n\n this.#cancelHover();\n this.#currentAnchor = anchor;\n\n const preload = this.#resolveAnchorPreload(anchor);\n\n if (!preload) {\n return;\n }\n\n this.#hoverTimer = setTimeout(() => {\n this.#hoverTimer = null;\n preload.fn(preload.params).catch(() => {});\n }, this.#options.delay);\n };\n\n readonly #handleTouchStart = (event: TouchEvent): void => {\n this.#lastTouchTarget = event.target;\n this.#lastTouchTimeStamp = event.timeStamp;\n\n this.#cancelTouch();\n\n const anchor = this.#findAnchor(event.target);\n const preload = this.#resolveAnchorPreload(anchor);\n\n if (!preload || event.touches.length === 0) {\n return;\n }\n\n this.#touchStartY = event.touches[0].clientY;\n\n this.#touchTimer = setTimeout(() => {\n this.#touchTimer = null;\n preload.fn(preload.params).catch(() => {});\n }, TOUCH_PRELOAD_DELAY);\n };\n\n readonly #handleTouchMove = (event: TouchEvent): void => {\n if (this.#touchTimer === null || event.touches.length === 0) {\n return;\n }\n\n const deltaY = Math.abs(event.touches[0].clientY - this.#touchStartY);\n\n if (deltaY > TOUCH_SCROLL_THRESHOLD) {\n this.#cancelTouch();\n }\n };\n\n #findAnchor(target: EventTarget | null): HTMLAnchorElement | null {\n return target instanceof Element\n ? target.closest<HTMLAnchorElement>(\"a[href]\")\n : null;\n }\n\n #resolveAnchorPreload(\n anchor: HTMLAnchorElement | null | undefined,\n ): { fn: PreloadFn; params: Params } | undefined {\n if (!anchor) {\n return undefined;\n }\n\n if (\"noPreload\" in anchor.dataset) {\n return undefined;\n }\n\n if (this.#options.networkAware && isSlowConnection()) {\n return undefined;\n }\n\n return this.#resolvePreload(anchor);\n }\n\n #resolvePreload(\n anchor: HTMLAnchorElement,\n ): { fn: PreloadFn; params: Params } | undefined {\n const state = this.#router.matchUrl?.(anchor.href);\n\n if (!state) {\n return undefined;\n }\n\n this.#cacheState(anchor.href, state);\n\n const config = this.#api.getRouteConfig(state.name);\n const factory =\n typeof config?.preload === \"function\"\n ? (config.preload as PreloadFnFactory)\n : undefined;\n\n if (!factory) {\n this.#compiledPreloads.delete(state.name);\n\n return undefined;\n }\n\n const cached = this.#compiledPreloads.get(state.name);\n\n if (cached?.factory === factory) {\n return { fn: cached.fn, params: state.params };\n }\n\n let fn: PreloadFn;\n\n try {\n fn = factory(this.#router, this.#getDependency);\n } catch {\n return undefined;\n }\n\n this.#compiledPreloads.set(state.name, { fn, factory });\n\n return { fn, params: state.params };\n }\n\n #cacheState(href: string, state: State): void {\n // Re-insert to refresh recency ordering (Map iteration is insertion order).\n if (this.#stateCache.has(href)) {\n this.#stateCache.delete(href);\n } else if (this.#stateCache.size >= STATE_CACHE_LIMIT) {\n // size >= LIMIT > 0 → iterator has at least one key.\n for (const oldest of this.#stateCache.keys()) {\n this.#stateCache.delete(oldest);\n\n break;\n }\n }\n\n this.#stateCache.set(href, state);\n }\n\n #isGhostMouseEvent(event: MouseEvent): boolean {\n const delta = event.timeStamp - this.#lastTouchTimeStamp;\n\n return (\n delta >= 0 &&\n delta < GHOST_EVENT_THRESHOLD &&\n event.target === this.#lastTouchTarget\n );\n }\n\n #cancelHover(): void {\n if (this.#hoverTimer !== null) {\n clearTimeout(this.#hoverTimer);\n this.#hoverTimer = null;\n }\n\n this.#currentAnchor = null;\n }\n\n #cancelTouch(): void {\n if (this.#touchTimer !== null) {\n clearTimeout(this.#touchTimer);\n this.#touchTimer = null;\n }\n }\n\n #cleanup(): void {\n document.removeEventListener(\n \"mouseover\",\n this.#handleMouseOver,\n LISTENER_OPTIONS,\n );\n document.removeEventListener(\n \"touchstart\",\n this.#handleTouchStart,\n LISTENER_OPTIONS,\n );\n document.removeEventListener(\n \"touchmove\",\n this.#handleTouchMove,\n LISTENER_OPTIONS,\n );\n\n this.#cancelHover();\n this.#cancelTouch();\n this.#lastTouchTarget = null;\n this.#lastTouchTimeStamp = Number.NaN;\n this.#stateCache.clear();\n }\n}\n","import { getPluginApi } from \"@real-router/core/api\";\n\nimport { defaultOptions } from \"./constants\";\nimport { PreloadPlugin } from \"./plugin\";\n\nimport type { PreloadPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\nexport function preloadPluginFactory(\n opts?: Partial<PreloadPluginOptions>,\n): PluginFactory {\n const options: Required<PreloadPluginOptions> = {\n ...defaultOptions,\n ...opts,\n };\n\n if (!Number.isFinite(options.delay) || options.delay < 0) {\n options.delay = 0;\n }\n\n return function preloadPlugin(routerBase, getDependency) {\n if (typeof document === \"undefined\") {\n return {};\n }\n\n const plugin = new PreloadPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n getDependency,\n );\n\n return plugin.getPlugin();\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/preload-plugin",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "type": "commonjs",
5
5
  "description": "Preload plugin — trigger data preloading on navigation intent (hover, touch)",
6
6
  "main": "./dist/cjs/index.js",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "sideEffects": false,
47
47
  "dependencies": {
48
- "@real-router/core": "^0.52.0",
48
+ "@real-router/core": "^0.54.1",
49
49
  "@real-router/types": "^0.35.0"
50
50
  },
51
51
  "devDependencies": {