From d2f9402921f3656ffdae0ef465265e6c3ed5cf05 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Mon, 10 Aug 2026 19:37:09 +0200 Subject: [PATCH 1/8] feat: extract @module-federation/data-fetch from bridge-react Co-authored-by: Cursor --- packages/bridge/bridge-react/package.json | 1 + .../bridge/bridge-react/src/data-fetch.ts | 22 +- .../bridge/bridge-react/src/lazy/constant.ts | 31 +- .../data-fetch-server-middleware.ts | 197 +-------- .../bridge-react/src/lazy/data-fetch/index.ts | 17 +- .../bridge/bridge-react/src/lazy/index.ts | 20 +- .../bridge/bridge-react/src/lazy/types.ts | 79 +--- .../bridge/bridge-react/src/lazy/utils.ts | 385 +----------------- .../src/shared/size-limited-cache.ts | 116 +----- packages/bridge/bridge-react/vite.config.ts | 5 + packages/bridge/vue3-bridge/package.json | 1 + packages/bridge/vue3-bridge/src/index.ts | 19 + packages/bridge/vue3-bridge/vite.config.ts | 2 +- .../__tests__/prefetch.spec.ts | 87 ++-- packages/data-fetch/package.json | 92 +++++ packages/data-fetch/rstest.config.ts | 17 + .../scripts/write-dts-entrypoints.mjs | 13 + .../src}/cache.spec.ts | 2 +- .../data-fetch => data-fetch/src}/cache.ts | 6 +- .../src}/call-data-fetch.ts | 2 +- packages/data-fetch/src/constant.ts | 25 ++ .../src/data-fetch-server-middleware.ts | 196 +++++++++ packages/data-fetch/src/data-fetch-utils.ts | 16 + packages/data-fetch/src/env.d.ts | 8 + packages/data-fetch/src/index.ts | 55 +++ .../src}/inject-data-fetch.ts | 8 +- packages/data-fetch/src/logger.ts | 6 + .../data-fetch => data-fetch/src}/prefetch.ts | 10 +- .../src}/runtime-plugin.ts | 8 +- .../src}/size-limited-cache.spec.ts | 2 +- packages/data-fetch/src/size-limited-cache.ts | 115 ++++++ packages/data-fetch/src/types.ts | 75 ++++ packages/data-fetch/src/utils.ts | 375 +++++++++++++++++ packages/data-fetch/tsconfig.json | 24 ++ packages/data-fetch/vite.config.ts | 38 ++ 35 files changed, 1190 insertions(+), 885 deletions(-) rename packages/{bridge/bridge-react => data-fetch}/__tests__/prefetch.spec.ts (63%) create mode 100644 packages/data-fetch/package.json create mode 100644 packages/data-fetch/rstest.config.ts create mode 100644 packages/data-fetch/scripts/write-dts-entrypoints.mjs rename packages/{bridge/bridge-react/src/lazy/data-fetch => data-fetch/src}/cache.spec.ts (96%) rename packages/{bridge/bridge-react/src/lazy/data-fetch => data-fetch/src}/cache.ts (98%) rename packages/{bridge/bridge-react/src/lazy/data-fetch => data-fetch/src}/call-data-fetch.ts (85%) create mode 100644 packages/data-fetch/src/constant.ts create mode 100644 packages/data-fetch/src/data-fetch-server-middleware.ts create mode 100644 packages/data-fetch/src/data-fetch-utils.ts create mode 100644 packages/data-fetch/src/env.d.ts create mode 100644 packages/data-fetch/src/index.ts rename packages/{bridge/bridge-react/src/lazy/data-fetch => data-fetch/src}/inject-data-fetch.ts (96%) create mode 100644 packages/data-fetch/src/logger.ts rename packages/{bridge/bridge-react/src/lazy/data-fetch => data-fetch/src}/prefetch.ts (94%) rename packages/{bridge/bridge-react/src/lazy/data-fetch => data-fetch/src}/runtime-plugin.ts (95%) rename packages/{bridge/bridge-react/src/shared => data-fetch/src}/size-limited-cache.spec.ts (97%) create mode 100644 packages/data-fetch/src/size-limited-cache.ts create mode 100644 packages/data-fetch/src/types.ts create mode 100644 packages/data-fetch/src/utils.ts create mode 100644 packages/data-fetch/tsconfig.json create mode 100644 packages/data-fetch/vite.config.ts diff --git a/packages/bridge/bridge-react/package.json b/packages/bridge/bridge-react/package.json index 533275c28b5..3a189b7e045 100644 --- a/packages/bridge/bridge-react/package.json +++ b/packages/bridge/bridge-react/package.json @@ -155,6 +155,7 @@ "test:rstest": "pnpm exec rstest --passWithNoTests" }, "dependencies": { + "@module-federation/data-fetch": "workspace:*", "@module-federation/sdk": "workspace:*" }, "peerDependencies": { diff --git a/packages/bridge/bridge-react/src/data-fetch.ts b/packages/bridge/bridge-react/src/data-fetch.ts index 6a102ca0153..cebcff11baa 100644 --- a/packages/bridge/bridge-react/src/data-fetch.ts +++ b/packages/bridge/bridge-react/src/data-fetch.ts @@ -1,3 +1,13 @@ +export type { + DataFetchParams, + NoSSRRemoteInfo, + CollectSSRAssetsOptions, + CreateLazyComponentOptions, + CacheStatus, + CacheStatsInfo, + PrefetchOptions, +} from './lazy'; + export { ERROR_TYPE, createLazyComponent, @@ -13,17 +23,7 @@ export { revalidateTag, clearStore, prefetch, + flushDataFetch, } from './lazy'; export { lazyLoadComponentPlugin } from './plugins/lazy-load-component-plugin'; -export { flushDataFetch } from './lazy/utils'; - -export type { - DataFetchParams, - NoSSRRemoteInfo, - CollectSSRAssetsOptions, - CreateLazyComponentOptions, - CacheStatus, - CacheStatsInfo, - PrefetchOptions, -} from './lazy'; diff --git a/packages/bridge/bridge-react/src/lazy/constant.ts b/packages/bridge/bridge-react/src/lazy/constant.ts index c6b718ce1ac..5c1ebb1ba05 100644 --- a/packages/bridge/bridge-react/src/lazy/constant.ts +++ b/packages/bridge/bridge-react/src/lazy/constant.ts @@ -1,30 +1,15 @@ export const PLUGIN_IDENTIFIER = '[ Module Federation React ]'; -export const DOWNGRADE_KEY = '_mfSSRDowngrade'; -export const DATA_FETCH_MAP_KEY = '__MF_DATA_FETCH_MAP__'; -export const DATA_FETCH_FUNCTION = '_mfDataFetch'; -export const FS_HREF = '_mfFSHref'; export const ERROR_TYPE = { DATA_FETCH: 1, LOAD_REMOTE: 2, UNKNOWN: 3, }; -export const WRAP_DATA_FETCH_ID_IDENTIFIER = 'wrap_dfip_identifier'; -export const enum MF_DATA_FETCH_TYPE { - FETCH_SERVER = 1, - FETCH_CLIENT = 2, -} -export const enum MF_DATA_FETCH_STATUS { - LOADED = 1, - LOADING = 2, - AWAIT = 0, - ERROR = 3, -} - -export const DATA_FETCH_IDENTIFIER = 'data'; -export const DATA_FETCH_CLIENT_SUFFIX = '.client'; -export const DATA_FETCH_QUERY = 'x-mf-data-fetch'; -export const DATA_FETCH_ERROR_PREFIX = - 'caught the following error during dataFetch: '; -export const LOAD_REMOTE_ERROR_PREFIX = - 'caught the following error during loadRemote: '; +export { + DATA_FETCH_ERROR_PREFIX, + LOAD_REMOTE_ERROR_PREFIX, + DATA_FETCH_FUNCTION, + FS_HREF, + MF_DATA_FETCH_TYPE, + WRAP_DATA_FETCH_ID_IDENTIFIER, +} from '@module-federation/data-fetch'; diff --git a/packages/bridge/bridge-react/src/lazy/data-fetch/data-fetch-server-middleware.ts b/packages/bridge/bridge-react/src/lazy/data-fetch/data-fetch-server-middleware.ts index 0054a0c473a..340dab40a66 100644 --- a/packages/bridge/bridge-react/src/lazy/data-fetch/data-fetch-server-middleware.ts +++ b/packages/bridge/bridge-react/src/lazy/data-fetch/data-fetch-server-middleware.ts @@ -1,196 +1 @@ -import { DATA_FETCH_QUERY, MF_DATA_FETCH_STATUS } from '../constant'; -import { - getDataFetchMap, - fetchData, - initDataFetchMap, - loadDataFetchModule, -} from '../utils'; -import { SEPARATOR, MANIFEST_EXT } from '@module-federation/sdk'; -import logger from '../logger'; - -import type { NoSSRRemoteInfo } from '../types'; -import type { MiddlewareHandler } from 'hono'; - -function wrapSetTimeout( - targetPromise: Promise, - delay = 20000, - id: string, -) { - if (targetPromise && typeof targetPromise.then === 'function') { - return new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { - logger.warn(`Data fetch for ID ${id} timed out after 20 seconds.`); - reject(new Error(`Data fetch for ID ${id} timed out after 20 seconds`)); - }, delay); - - targetPromise - .then((value: any) => { - clearTimeout(timeoutId); - resolve(value); - }) - .catch((err: any) => { - clearTimeout(timeoutId); - reject(err); - }); - }); - } -} - -function addProtocol(url: string) { - if (url.startsWith('//')) { - return 'https:' + url; - } - return url; -} - -const getDecodeQuery = (url: URL, name: string) => { - const res = url.searchParams.get(name); - if (!res) { - return null; - } - return decodeURIComponent(res); -}; - -const dataFetchServerMiddleware: MiddlewareHandler = async (ctx, next) => { - let url: URL; - let dataFetchKey: string | null; - let params: Record; - let remoteInfo: NoSSRRemoteInfo; - try { - url = new URL(ctx.req.url); - dataFetchKey = getDecodeQuery(url, DATA_FETCH_QUERY); - params = JSON.parse(getDecodeQuery(url, 'params') || '{}'); - const remoteInfoQuery = getDecodeQuery(url, 'remoteInfo'); - remoteInfo = remoteInfoQuery ? JSON.parse(remoteInfoQuery) : null; - } catch (e) { - logger.error('fetch data from server, error: ', e); - return next(); - } - - if (!dataFetchKey) { - return next(); - } - logger.log('fetch data from server, dataFetchKey: ', dataFetchKey); - logger.debug( - 'fetch data from server, moduleInfo: ', - globalThis.__FEDERATION__?.moduleInfo, - ); - try { - const dataFetchMap = getDataFetchMap(); - if (!dataFetchMap) { - initDataFetchMap(); - } - const fetchDataPromise = dataFetchMap[dataFetchKey]?.[1]; - logger.debug( - 'fetch data from server, fetchDataPromise: ', - fetchDataPromise, - ); - if ( - fetchDataPromise && - dataFetchMap[dataFetchKey]?.[2] !== MF_DATA_FETCH_STATUS.ERROR - ) { - const targetPromise = fetchDataPromise[0]; - // Ensure targetPromise is thenable - const wrappedPromise = wrapSetTimeout(targetPromise, 20000, dataFetchKey); - if (wrappedPromise) { - const res = await wrappedPromise; - logger.log('fetch data from server, fetchDataPromise res: ', res); - return ctx.json(res); - } - logger.error( - `Expected a Promise from fetchDataPromise[0] for dataFetchKey ${dataFetchKey}, but received:`, - targetPromise, - 'Will try call new dataFetch again...', - ); - } - - if (remoteInfo) { - try { - const hostInstance = globalThis.__FEDERATION__.__INSTANCES__[0]; - const remoteEntry = `${addProtocol(remoteInfo.ssrPublicPath) + remoteInfo.ssrRemoteEntry}`; - if (!hostInstance) { - throw new Error('host instance not found!'); - } - const remote = hostInstance.options.remotes.find( - (remote) => remote.name === remoteInfo.name, - ); - logger.debug('find remote: ', JSON.stringify(remote)); - if (!remote) { - hostInstance.registerRemotes([ - { - name: remoteInfo.name, - entry: remoteEntry, - entryGlobalName: remoteInfo.globalName, - }, - ]); - } else if ( - !('entry' in remote) || - !remote.entry.includes(MANIFEST_EXT) - ) { - const { hostGlobalSnapshot, remoteSnapshot } = - hostInstance.snapshotHandler.getGlobalRemoteInfo(remoteInfo); - logger.debug( - 'find hostGlobalSnapshot: ', - JSON.stringify(hostGlobalSnapshot), - ); - logger.debug('find remoteSnapshot: ', JSON.stringify(remoteSnapshot)); - - if (!hostGlobalSnapshot || !remoteSnapshot) { - if ('version' in remote) { - // @ts-ignore - delete remote.version; - } - // @ts-ignore - remote.entry = remoteEntry; - remote.entryGlobalName = remoteInfo.globalName; - } - } - } catch (e) { - ctx.status(500); - return ctx.text( - `failed to fetch ${remoteInfo.name} data, error:\n ${e}`, - ); - } - } - - const dataFetchItem = dataFetchMap[dataFetchKey]; - logger.debug('fetch data from server, dataFetchItem: ', dataFetchItem); - if (dataFetchItem) { - const callFetchDataPromise = fetchData(dataFetchKey, { - ...params, - isDowngrade: !remoteInfo, - _id: dataFetchKey, - }); - const wrappedPromise = wrapSetTimeout( - callFetchDataPromise, - 20000, - dataFetchKey, - ); - if (wrappedPromise) { - const res = await wrappedPromise; - logger.log('fetch data from server, dataFetchItem res: ', res); - return ctx.json(res); - } - } - - const remoteId = dataFetchKey.split(SEPARATOR)[0]; - const hostInstance = globalThis.__FEDERATION__.__INSTANCES__[0]; - if (!hostInstance) { - throw new Error('host instance not found!'); - } - const dataFetchFn = await loadDataFetchModule(hostInstance, remoteId); - const data = await dataFetchFn({ - ...params, - isDowngrade: !remoteInfo, - _id: dataFetchKey, - }); - logger.log('fetch data from server, loadDataFetchModule res: ', data); - return ctx.json(data); - } catch (e) { - logger.error('server plugin data fetch error: ', e); - ctx.status(500); - return ctx.text(`failed to fetch ${remoteInfo.name} data, error:\n ${e}`); - } -}; - -export default dataFetchServerMiddleware; +export { default } from '@module-federation/data-fetch/server-middleware'; diff --git a/packages/bridge/bridge-react/src/lazy/data-fetch/index.ts b/packages/bridge/bridge-react/src/lazy/data-fetch/index.ts index db35110e60f..55229730a24 100644 --- a/packages/bridge/bridge-react/src/lazy/data-fetch/index.ts +++ b/packages/bridge/bridge-react/src/lazy/data-fetch/index.ts @@ -1,16 +1 @@ -export { callDataFetch } from './call-data-fetch'; -export { injectDataFetch } from './inject-data-fetch'; -export { - CacheSize, - CacheTime, - configureCache, - generateKey, - cache, - revalidateTag, - clearStore, -} from './cache'; - -export type { CacheStatus, CacheStatsInfo } from './cache'; - -export type { PrefetchOptions } from './prefetch'; -export { prefetch } from './prefetch'; +export * from '@module-federation/data-fetch/data-fetch-utils'; diff --git a/packages/bridge/bridge-react/src/lazy/index.ts b/packages/bridge/bridge-react/src/lazy/index.ts index 5c17973a9dd..30b1f03b93a 100644 --- a/packages/bridge/bridge-react/src/lazy/index.ts +++ b/packages/bridge/bridge-react/src/lazy/index.ts @@ -1,7 +1,8 @@ -import autoFetchDataPlugin from './data-fetch/runtime-plugin'; - export { ERROR_TYPE } from './constant'; -export type { DataFetchParams, NoSSRRemoteInfo } from './types'; +export type { + DataFetchParams, + NoSSRRemoteInfo, +} from '@module-federation/data-fetch'; export type { CreateLazyComponentOptions, IProps as CollectSSRAssetsOptions, @@ -9,8 +10,6 @@ export type { export { createLazyComponent, collectSSRAssets } from './createLazyComponent'; -// export { wrapNoSSR } from './wrapNoSSR'; - export { injectDataFetch, callDataFetch, @@ -22,14 +21,13 @@ export { revalidateTag, clearStore, prefetch, -} from './data-fetch'; - -export { setSSREnv } from './utils'; - -export { autoFetchDataPlugin }; + setSSREnv, + autoFetchDataPlugin, + flushDataFetch, +} from '@module-federation/data-fetch'; export type { CacheStatus, CacheStatsInfo, PrefetchOptions, -} from './data-fetch'; +} from '@module-federation/data-fetch'; diff --git a/packages/bridge/bridge-react/src/lazy/types.ts b/packages/bridge/bridge-react/src/lazy/types.ts index 060f0254981..cab029bf2f4 100644 --- a/packages/bridge/bridge-react/src/lazy/types.ts +++ b/packages/bridge/bridge-react/src/lazy/types.ts @@ -1,75 +1,4 @@ -import { MF_DATA_FETCH_TYPE, MF_DATA_FETCH_STATUS } from './constant'; -import type { SizeLimitedCache } from '../shared/size-limited-cache'; - -export type dataFetchFunctionOptions = [ - id?: string, - data?: unknown, - downgrade?: boolean, -]; - -declare global { - var _mfSSRDowngrade: string[] | true | undefined; - var __MF_DATA_FETCH_MAP__: MF_DATA_FETCH_MAP | undefined; - var FEDERATION_SERVER_QUERY: Record | undefined; - var FEDERATION_SSR: boolean | undefined; - var _mfFSHref: string | undefined; - var _mfDataFetch: Array | undefined; - var __MF_DATA_FETCH_CACHE__: MF_DATA_FETCH_CACHE | undefined; -} - -export interface CacheItem { - data: T; - timestamp: number; - isRevalidating?: boolean; -} - -export type DataFetchParams = { - isDowngrade: boolean; - _id?: string; -} & Record; -export type DataFetch = (params: DataFetchParams) => Promise; - -// loading, resolve, reject -export type MF_DATA_FETCH_MAP_VALUE_PROMISE_SET = [ - Promise, - ((data: unknown) => void)?, - ((err: unknown) => void)?, -]; -export type MF_DATA_FETCH_MAP_VALUE = [ - [ - // getDataFetchGetter - () => Promise>, - // data fetch type - MF_DATA_FETCH_TYPE, - // getDataFetchPromise - Promise>?, - ], - MF_DATA_FETCH_MAP_VALUE_PROMISE_SET?, - MF_DATA_FETCH_STATUS?, -]; -export type MF_DATA_FETCH_MAP = Record; -export type MF_SSR_DOWNGRADE = string[] | true | undefined; - -export type NoSSRRemoteInfo = { - name: string; - version: string; - ssrPublicPath: string; - ssrRemoteEntry: string; - globalName: string; -}; - -export interface CacheConfig { - maxSize?: number; - unstable_shouldDisable?: ({ - request, - }: { - request: Request; - }) => boolean | Promise; -} - -// key is the id, default is the same with dataFetchMapId, but it can be custom by passing dataFetchParams.id -export type MF_DATA_FETCH_CACHE = { - cacheStore?: SizeLimitedCache>>; - tagKeyMap?: Map>; - cacheConfig?: CacheConfig; -}; +export type { + DataFetchParams, + NoSSRRemoteInfo, +} from '@module-federation/data-fetch'; diff --git a/packages/bridge/bridge-react/src/lazy/utils.ts b/packages/bridge/bridge-react/src/lazy/utils.ts index 1db67c8f23e..bd5d2d3188c 100644 --- a/packages/bridge/bridge-react/src/lazy/utils.ts +++ b/packages/bridge/bridge-react/src/lazy/utils.ts @@ -1,384 +1 @@ -import { - isBrowserEnvValue, - composeKeyWithSeparator, -} from '@module-federation/sdk'; -import logger from './logger'; -import { - DOWNGRADE_KEY, - MF_DATA_FETCH_STATUS, - WRAP_DATA_FETCH_ID_IDENTIFIER, - DATA_FETCH_QUERY, - MF_DATA_FETCH_TYPE, - DATA_FETCH_IDENTIFIER, - DATA_FETCH_CLIENT_SUFFIX, -} from './constant'; - -import type { GlobalModuleInfo } from '@module-federation/sdk'; -import type { - DataFetchParams, - MF_DATA_FETCH_MAP, - NoSSRRemoteInfo, - MF_SSR_DOWNGRADE, - MF_DATA_FETCH_MAP_VALUE_PROMISE_SET, - MF_DATA_FETCH_CACHE, -} from './types'; -import type { ModuleFederation } from '@module-federation/runtime'; -import { clearStore } from './data-fetch/cache'; - -export const getDataFetchInfo = ({ - name, - alias, - id, - remoteSnapshot, -}: { - id: string; - name: string; - remoteSnapshot: GlobalModuleInfo[string]; - alias?: string; -}) => { - if (!remoteSnapshot) { - return; - } - if (!('modules' in remoteSnapshot)) { - return; - } - const regex = new RegExp(`^${name}(/[^/].*|)$`); - const nameOrAlias = regex.test(id) ? name : alias || name; - - const expose = id.replace(nameOrAlias, ''); - let dataFetchName = ''; - let dataFetchId = ''; - let dataFetchKey = ''; - if (expose.startsWith('/')) { - dataFetchName = `${expose.slice(1)}.${DATA_FETCH_IDENTIFIER}`; - dataFetchId = `${id}.${DATA_FETCH_IDENTIFIER}`; - dataFetchKey = `${name}${expose}.${DATA_FETCH_IDENTIFIER}`; - } else if (expose === '') { - dataFetchName = DATA_FETCH_IDENTIFIER; - dataFetchId = `${id}/${DATA_FETCH_IDENTIFIER}`; - dataFetchKey = `${name}/${DATA_FETCH_IDENTIFIER}`; - } else { - return; - } - - if (!dataFetchName || !dataFetchId || !dataFetchKey) { - return; - } - - if ( - !remoteSnapshot.modules.find( - (module) => module.moduleName === dataFetchName, - ) - ) { - return; - } - - return { - dataFetchName, - dataFetchId, - dataFetchKey, - }; -}; - -export function initDataFetchMap() { - globalThis.__MF_DATA_FETCH_MAP__ ||= {}; -} - -export function getDataFetchItem(id: string) { - return (globalThis.__MF_DATA_FETCH_MAP__ as MF_DATA_FETCH_MAP)?.[id]; -} - -export function getDataFetchMap() { - return globalThis.__MF_DATA_FETCH_MAP__ as MF_DATA_FETCH_MAP; -} - -export function getDataFetchCache() { - return globalThis.__MF_DATA_FETCH_CACHE__ as MF_DATA_FETCH_CACHE; -} - -export const flushDataFetch = () => { - globalThis.__MF_DATA_FETCH_MAP__ = {}; - globalThis[DOWNGRADE_KEY] = undefined; - clearStore(); -}; - -export function setDataFetchItemLoadedStatus(id: string) { - const dataFetchItem = getDataFetchItem(id); - if (!dataFetchItem) { - return; - } - dataFetchItem[2] = MF_DATA_FETCH_STATUS.LOADED; -} - -export function resetDataFetchResult(id: string) { - const dataFetchItem = getDataFetchItem(id); - if (!dataFetchItem) { - return; - } - dataFetchItem[1] = undefined; - dataFetchItem[2] = MF_DATA_FETCH_STATUS.AWAIT; -} - -export const wrapDataFetchId = (id?: string) => { - return `${WRAP_DATA_FETCH_ID_IDENTIFIER}${id}${WRAP_DATA_FETCH_ID_IDENTIFIER}`; -}; - -export const getDataFetchIdWithErrorMsgs = (errMsgs: string) => { - const firstIdentifierIndex = errMsgs.indexOf(WRAP_DATA_FETCH_ID_IDENTIFIER); - if (firstIdentifierIndex === -1) { - return undefined; - } - - const secondIdentifierIndex = errMsgs.indexOf( - WRAP_DATA_FETCH_ID_IDENTIFIER, - firstIdentifierIndex + WRAP_DATA_FETCH_ID_IDENTIFIER.length, - ); - - if (secondIdentifierIndex === -1) { - return undefined; - } - - return errMsgs.substring( - firstIdentifierIndex + WRAP_DATA_FETCH_ID_IDENTIFIER.length, - secondIdentifierIndex, - ); -}; - -export async function fetchData( - id: string, - params: DataFetchParams, - remoteInfo?: NoSSRRemoteInfo, -): Promise { - const callFetchData = async () => { - const item = getDataFetchItem(id); - if (!item) { - return; - } - const [fetchDataFnArr, ..._rest] = item; - - const fetchDataFn = await fetchDataFnArr[2]; - if (!fetchDataFn) { - return; - } - return fetchDataFn({ - ...params, - _id: id, - }); - }; - if (isBrowserEnvValue) { - const dataFetchItem = getDataFetchItem(id); - if (!dataFetchItem) { - throw new Error(`dataFetchItem not found, id: ${id}`); - } - if (dataFetchItem[1]?.[0]) { - return dataFetchItem[1][0]; - } - - if (isCSROnly()) { - logger.debug('==========csr only!'); - return callFetchData(); - } - - if (remoteInfo) { - return callDowngrade(id, params, remoteInfo); - } - - const mfDowngrade = getDowngradeTag(); - if ( - mfDowngrade && - (typeof mfDowngrade === 'boolean' || mfDowngrade.includes(id)) - ) { - return callDowngrade(id, { ...params, isDowngrade: true }); - } - - let res; - let rej; - const p = new Promise((resolve, reject) => { - res = resolve; - rej = reject; - }); - dataFetchItem[1] = [p, res, rej]; - dataFetchItem[2] = MF_DATA_FETCH_STATUS.AWAIT; - return dataFetchItem[1][0]; - } - - return callFetchData(); -} - -export function getDataFetchMapKey( - dataFetchInfo?: ReturnType, - hostInfo?: { name: string; version?: string }, -) { - if (!dataFetchInfo || !hostInfo) { - return; - } - - const { dataFetchKey } = dataFetchInfo; - - return composeKeyWithSeparator(dataFetchKey, hostInfo.name, hostInfo.version); -} - -export async function loadDataFetchModule( - instance: ModuleFederation, - id: string, -) { - return instance.loadRemote(id).then((m) => { - if ( - m && - typeof m === 'object' && - 'fetchData' in m && - typeof m.fetchData === 'function' - ) { - return m.fetchData as (params: DataFetchParams) => Promise; - } - throw new Error( - `fetchData not found in remote ${id}, ${JSON.stringify(m)}`, - ); - }); -} - -export function isDataLoaderExpose(exposeKey: string) { - return ( - exposeKey.endsWith(DATA_FETCH_IDENTIFIER) || - exposeKey.endsWith(DATA_FETCH_CLIENT_SUFFIX) - ); -} - -export function getDowngradeTag() { - return globalThis[DOWNGRADE_KEY] as MF_SSR_DOWNGRADE; -} - -export function callAllDowngrade() { - const dataFetchMap = getDataFetchMap(); - if (!dataFetchMap) { - return; - } - Object.keys(dataFetchMap).forEach((key) => { - callDowngrade(key); - }); -} - -export async function callDowngrade( - id: string, - params?: DataFetchParams, - remoteInfo?: NoSSRRemoteInfo, -) { - const dataFetchMap = getDataFetchMap(); - if (!dataFetchMap) { - return; - } - const mfDataFetch = dataFetchMap[id]; - if (mfDataFetch?.[2] === MF_DATA_FETCH_STATUS.AWAIT) { - mfDataFetch[2] = MF_DATA_FETCH_STATUS.LOADING; - let promise: MF_DATA_FETCH_MAP_VALUE_PROMISE_SET[0]; - let res: MF_DATA_FETCH_MAP_VALUE_PROMISE_SET[1]; - let rej: MF_DATA_FETCH_MAP_VALUE_PROMISE_SET[2]; - if (mfDataFetch[1]) { - promise = mfDataFetch[1][0]; - res = mfDataFetch[1][1]; - rej = mfDataFetch[1][2]; - } else { - promise = new Promise((resolve, reject) => { - res = resolve; - rej = reject; - }); - mfDataFetch[1] = [promise, res, rej]; - } - const dataFetchType = mfDataFetch[0][1]; - if (dataFetchType === MF_DATA_FETCH_TYPE.FETCH_CLIENT) { - try { - mfDataFetch[0][0]().then(async (getDataFetchFn) => { - return getDataFetchFn({ - ...params, - isDowngrade: true, - _id: id, - }).then((data) => { - mfDataFetch[2] = MF_DATA_FETCH_STATUS.LOADED; - res && res(data); - }); - }); - } catch (e) { - mfDataFetch[2] = MF_DATA_FETCH_STATUS.ERROR; - rej && rej(e); - } - } else if (dataFetchType === MF_DATA_FETCH_TYPE.FETCH_SERVER) { - try { - const currentUrl = new URL(window.location.href); - currentUrl.searchParams.set(DATA_FETCH_QUERY, encodeURIComponent(id)); - if (params) { - currentUrl.searchParams.set( - 'params', - encodeURIComponent(JSON.stringify(params)), - ); - } - if (remoteInfo) { - currentUrl.searchParams.set( - 'remoteInfo', - encodeURIComponent(JSON.stringify(remoteInfo)), - ); - } - const fetchServerQuery = globalThis.FEDERATION_SERVER_QUERY; - if (fetchServerQuery && typeof fetchServerQuery === 'object') { - Object.keys(fetchServerQuery).forEach((key) => { - currentUrl.searchParams.set( - key, - JSON.stringify(fetchServerQuery[key]), - ); - }); - } - const fetchUrl = currentUrl.toString(); - const data = await fetch(fetchUrl).then((res) => res.json()); - mfDataFetch[2] = MF_DATA_FETCH_STATUS.LOADED; - res && res(data); - } catch (e) { - mfDataFetch[2] = MF_DATA_FETCH_STATUS.ERROR; - rej && rej(e); - } - } - - return promise; - } -} - -export function isCSROnly() { - // @ts-ignore modern.js will inject window._SSR_DATA if enable ssr - return window._SSR_DATA === undefined; -} - -export function isServerEnv() { - return typeof window === 'undefined'; -} - -export function setSSREnv({ - fetchServerQuery, -}: { - fetchServerQuery?: Record; -}) { - globalThis.FEDERATION_SSR = true; - globalThis.FEDERATION_SERVER_QUERY = fetchServerQuery; -} - -export function getLoadedRemoteInfos( - id: string, - instance: ModuleFederation | null, -) { - if (!instance) { - return; - } - const { name, expose } = instance.remoteHandler.idToRemoteMap[id] || {}; - if (!name) { - return; - } - const module = instance.moduleCache.get(name); - if (!module) { - return; - } - const { remoteSnapshot } = instance.snapshotHandler.getGlobalRemoteInfo( - module.remoteInfo, - ); - return { - ...module.remoteInfo, - snapshot: remoteSnapshot, - expose, - }; -} +export * from '@module-federation/data-fetch/utils'; diff --git a/packages/bridge/bridge-react/src/shared/size-limited-cache.ts b/packages/bridge/bridge-react/src/shared/size-limited-cache.ts index a43c0f84d68..afd2f157156 100644 --- a/packages/bridge/bridge-react/src/shared/size-limited-cache.ts +++ b/packages/bridge/bridge-react/src/shared/size-limited-cache.ts @@ -1,115 +1 @@ -export interface SizeLimitedCacheOptions { - maxSize: number; - sizeCalculation?: (value: V, key: K) => number; -} - -export interface SizeLimitedCacheSetOptions { - size?: number; -} - -interface CacheEntry { - value: V; - size: number; -} - -/** - * A small, size-aware least-recently-used cache. - * - * This intentionally implements only the operations used by federation - * packages. Reading or checking an entry refreshes its recency. - */ -export class SizeLimitedCache { - readonly maxSize: number; - - private readonly entries = new Map>(); - private readonly sizeCalculation?: (value: V, key: K) => number; - private currentSize = 0; - - constructor(options: SizeLimitedCacheOptions) { - if (!Number.isInteger(options.maxSize) || options.maxSize <= 0) { - throw new TypeError('maxSize must be a positive integer'); - } - - this.maxSize = options.maxSize; - this.sizeCalculation = options.sizeCalculation; - } - - get size(): number { - return this.entries.size; - } - - get calculatedSize(): number { - return this.currentSize; - } - - get(key: K): V | undefined { - const entry = this.entries.get(key); - if (!entry) { - return undefined; - } - - this.refresh(key, entry); - return entry.value; - } - - has(key: K): boolean { - const entry = this.entries.get(key); - if (!entry) { - return false; - } - - this.refresh(key, entry); - return true; - } - - set(key: K, value: V, options: SizeLimitedCacheSetOptions = {}): this { - const entrySize = options.size ?? this.sizeCalculation?.(value, key) ?? 1; - - if (!Number.isInteger(entrySize) || entrySize <= 0) { - throw new TypeError('entry size must be a positive integer'); - } - - this.delete(key); - - if (entrySize > this.maxSize) { - return this; - } - - this.entries.set(key, { value, size: entrySize }); - this.currentSize += entrySize; - this.evictUntilWithinLimit(); - - return this; - } - - delete(key: K): boolean { - const entry = this.entries.get(key); - if (!entry) { - return false; - } - - this.currentSize -= entry.size; - return this.entries.delete(key); - } - - clear(): void { - this.entries.clear(); - this.currentSize = 0; - } - - private refresh(key: K, entry: CacheEntry): void { - this.entries.delete(key); - this.entries.set(key, entry); - } - - private evictUntilWithinLimit(): void { - while (this.currentSize > this.maxSize) { - const oldest = this.entries.keys().next(); - if (oldest.done) { - this.currentSize = 0; - return; - } - this.delete(oldest.value); - } - } -} +export * from '@module-federation/data-fetch/size-limited-cache'; diff --git a/packages/bridge/bridge-react/vite.config.ts b/packages/bridge/bridge-react/vite.config.ts index e703d5ea572..f668a299630 100644 --- a/packages/bridge/bridge-react/vite.config.ts +++ b/packages/bridge/bridge-react/vite.config.ts @@ -42,6 +42,11 @@ export default defineConfig({ rollupOptions: { external: [ ...perDepsKeys, + '@module-federation/data-fetch', + '@module-federation/data-fetch/data-fetch-utils', + '@module-federation/data-fetch/server-middleware', + '@module-federation/data-fetch/size-limited-cache', + '@module-federation/data-fetch/utils', '@remix-run/router', /react-dom\/.*/, 'react-router', diff --git a/packages/bridge/vue3-bridge/package.json b/packages/bridge/vue3-bridge/package.json index cebf5755c85..b2846a992e8 100644 --- a/packages/bridge/vue3-bridge/package.json +++ b/packages/bridge/vue3-bridge/package.json @@ -32,6 +32,7 @@ }, "dependencies": { "@module-federation/bridge-shared": "workspace:*", + "@module-federation/data-fetch": "workspace:*", "@module-federation/runtime": "workspace:*", "@module-federation/sdk": "workspace:*" }, diff --git a/packages/bridge/vue3-bridge/src/index.ts b/packages/bridge/vue3-bridge/src/index.ts index fbf7a554075..24dba771e85 100644 --- a/packages/bridge/vue3-bridge/src/index.ts +++ b/packages/bridge/vue3-bridge/src/index.ts @@ -2,3 +2,22 @@ export { createBridgeComponent } from './provider'; export type { ProviderFnParams } from './provider'; export { createRemoteComponent, createRemoteAppComponent } from './create'; export type { RenderFnParams } from '@module-federation/bridge-shared'; + +export { + autoFetchDataPlugin, + cache, + clearStore, + configureCache, + generateKey, + prefetch, + revalidateTag, + CacheSize, + CacheTime, +} from '@module-federation/data-fetch'; + +export type { + CacheStatus, + CacheStatsInfo, + DataFetchParams, + PrefetchOptions, +} from '@module-federation/data-fetch'; diff --git a/packages/bridge/vue3-bridge/vite.config.ts b/packages/bridge/vue3-bridge/vite.config.ts index 1edf7166783..a33999c633e 100644 --- a/packages/bridge/vue3-bridge/vite.config.ts +++ b/packages/bridge/vue3-bridge/vite.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ fileName: 'index', }, rollupOptions: { - external: ['vue', 'vue-router'], + external: ['vue', 'vue-router', '@module-federation/data-fetch'], }, }, define: { diff --git a/packages/bridge/bridge-react/__tests__/prefetch.spec.ts b/packages/data-fetch/__tests__/prefetch.spec.ts similarity index 63% rename from packages/bridge/bridge-react/__tests__/prefetch.spec.ts rename to packages/data-fetch/__tests__/prefetch.spec.ts index 5157ed4c440..be7f7fe744e 100644 --- a/packages/bridge/bridge-react/__tests__/prefetch.spec.ts +++ b/packages/data-fetch/__tests__/prefetch.spec.ts @@ -1,21 +1,34 @@ -import { prefetch } from '../src/lazy/data-fetch/prefetch'; -import * as utils from '../src/lazy/utils'; -import logger from '../src/lazy/logger'; +import { describe, it, expect, beforeEach, rs } from '@rstest/core'; +import { prefetch } from '../src/prefetch'; +import * as utils from '../src/utils'; +import logger from '../src/logger'; import helpers from '@module-federation/runtime/helpers'; -// Mock dependencies -jest.mock('../src/lazy/logger'); -jest.mock('../src/lazy/utils'); -jest.mock('@module-federation/runtime/helpers', () => ({ +rs.mock('../src/logger', () => ({ + default: { + debug: rs.fn(), + log: rs.fn(), + warn: rs.fn(), + error: rs.fn(), + }, +})); + +rs.mock('../src/utils', () => ({ + getDataFetchInfo: rs.fn(), + getDataFetchMap: rs.fn(), + getDataFetchMapKey: rs.fn(), +})); + +rs.mock('@module-federation/runtime/helpers', () => ({ default: { utils: { - matchRemoteWithNameAndExpose: jest.fn(), - getRemoteInfo: jest.fn(), + matchRemoteWithNameAndExpose: rs.fn(), + getRemoteInfo: rs.fn(), }, }, utils: { - matchRemoteWithNameAndExpose: jest.fn(), - getRemoteInfo: jest.fn(), + matchRemoteWithNameAndExpose: rs.fn(), + getRemoteInfo: rs.fn(), }, })); @@ -23,7 +36,7 @@ describe('prefetch', () => { let mockInstance: any; beforeEach(() => { - jest.clearAllMocks(); + rs.clearAllMocks(); mockInstance = { name: 'host', options: { @@ -37,13 +50,13 @@ describe('prefetch', () => { ], }, snapshotHandler: { - loadRemoteSnapshotInfo: jest.fn(), + loadRemoteSnapshotInfo: rs.fn(), }, remoteHandler: { hooks: { lifecycle: { generatePreloadAssets: { - emit: jest.fn(), + emit: rs.fn(), }, }, }, @@ -66,9 +79,9 @@ describe('prefetch', () => { }); it('should log an error if remote is not found', async () => { - (helpers.utils.matchRemoteWithNameAndExpose as jest.Mock).mockReturnValue( - undefined, - ); + ( + helpers.utils.matchRemoteWithNameAndExpose as ReturnType + ).mockReturnValue(undefined); await prefetch({ id: 'nonexistent/component', instance: mockInstance }); expect(logger.error).toHaveBeenCalledWith( `Can not found 'nonexistent/component' in instance.options.remotes!`, @@ -80,33 +93,39 @@ describe('prefetch', () => { remote: { name: 'remote1', alias: 'remote1_alias' }, expose: './component1', }; - (helpers.utils.matchRemoteWithNameAndExpose as jest.Mock).mockReturnValue( - mockRemoteInfo, - ); ( - mockInstance.snapshotHandler.loadRemoteSnapshotInfo as jest.Mock + helpers.utils.matchRemoteWithNameAndExpose as ReturnType + ).mockReturnValue(mockRemoteInfo); + ( + mockInstance.snapshotHandler.loadRemoteSnapshotInfo as ReturnType< + typeof rs.fn + > ).mockResolvedValue({ remoteSnapshot: {}, globalSnapshot: {}, }); - (helpers.utils.getRemoteInfo as jest.Mock).mockReturnValue({}); + (helpers.utils.getRemoteInfo as ReturnType).mockReturnValue( + {}, + ); - const mockDataFetchFn = jest + const mockDataFetchFn = rs .fn() .mockResolvedValue({ data: 'prefetched data' }); - const mockGetDataFetchGetter = jest.fn().mockResolvedValue(mockDataFetchFn); + const mockGetDataFetchGetter = rs.fn().mockResolvedValue(mockDataFetchFn); const mockDataFetchMap = { 'remote1_alias@remote1/component1': [ [mockGetDataFetchGetter, 'GET', undefined], ], }; - (utils.getDataFetchMap as jest.Mock).mockReturnValue(mockDataFetchMap); - (utils.getDataFetchInfo as jest.Mock).mockReturnValue({ + (utils.getDataFetchMap as ReturnType).mockReturnValue( + mockDataFetchMap, + ); + (utils.getDataFetchInfo as ReturnType).mockReturnValue({ name: 'remote1', alias: 'remote1_alias', id: 'remote1/component1', }); - (utils.getDataFetchMapKey as jest.Mock).mockReturnValue( + (utils.getDataFetchMapKey as ReturnType).mockReturnValue( 'remote1_alias@remote1/component1', ); @@ -135,16 +154,20 @@ describe('prefetch', () => { remote: { name: 'remote1', alias: 'remote1_alias' }, expose: './component1', }; - (helpers.utils.matchRemoteWithNameAndExpose as jest.Mock).mockReturnValue( - mockRemoteInfo, - ); ( - mockInstance.snapshotHandler.loadRemoteSnapshotInfo as jest.Mock + helpers.utils.matchRemoteWithNameAndExpose as ReturnType + ).mockReturnValue(mockRemoteInfo); + ( + mockInstance.snapshotHandler.loadRemoteSnapshotInfo as ReturnType< + typeof rs.fn + > ).mockResolvedValue({ remoteSnapshot: {}, globalSnapshot: {}, }); - (utils.getDataFetchMap as jest.Mock).mockReturnValue(undefined); + (utils.getDataFetchMap as ReturnType).mockReturnValue( + undefined, + ); await prefetch({ id: 'remote1/component1', diff --git a/packages/data-fetch/package.json b/packages/data-fetch/package.json new file mode 100644 index 00000000000..30fd41384b2 --- /dev/null +++ b/packages/data-fetch/package.json @@ -0,0 +1,92 @@ +{ + "name": "@module-federation/data-fetch", + "version": "2.8.2", + "sideEffects": false, + "publishConfig": { + "access": "public" + }, + "author": "zhouxiao ", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/module-federation/core.git", + "directory": "packages/data-fetch" + }, + "files": [ + "dist/", + "README.md" + ], + "main": "./dist/index.cjs.js", + "module": "./dist/index.es.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.es.js", + "require": "./dist/index.cjs.js" + }, + "./data-fetch-utils": { + "types": "./dist/data-fetch-utils.d.ts", + "import": "./dist/data-fetch-utils.es.js", + "require": "./dist/data-fetch-utils.cjs.js" + }, + "./server-middleware": { + "types": "./dist/server-middleware.d.ts", + "import": "./dist/server-middleware.es.js", + "require": "./dist/server-middleware.cjs.js" + }, + "./size-limited-cache": { + "types": "./dist/size-limited-cache.d.ts", + "import": "./dist/size-limited-cache.es.js", + "require": "./dist/size-limited-cache.cjs.js" + }, + "./utils": { + "types": "./dist/utils.d.ts", + "import": "./dist/utils.es.js", + "require": "./dist/utils.cjs.js" + } + }, + "typesVersions": { + "*": { + ".": [ + "./dist/index.d.ts" + ], + "data-fetch-utils": [ + "./dist/data-fetch-utils.d.ts" + ], + "server-middleware": [ + "./dist/server-middleware.d.ts" + ], + "size-limited-cache": [ + "./dist/size-limited-cache.d.ts" + ], + "utils": [ + "./dist/utils.d.ts" + ] + } + }, + "scripts": { + "dev": "vite", + "build": "vite build && tsc -p tsconfig.json && node scripts/write-dts-entrypoints.mjs", + "preview": "vite preview", + "test": "rstest" + }, + "dependencies": { + "@module-federation/sdk": "workspace:*" + }, + "peerDependencies": { + "@module-federation/runtime": "*", + "hono": ">=3" + }, + "peerDependenciesMeta": { + "hono": { + "optional": true + } + }, + "devDependencies": { + "@module-federation/runtime": "workspace:*", + "hono": "4.12.26", + "typescript": "7.0.2", + "vite": "^5.4.21" + } +} diff --git a/packages/data-fetch/rstest.config.ts b/packages/data-fetch/rstest.config.ts new file mode 100644 index 00000000000..22a4139882c --- /dev/null +++ b/packages/data-fetch/rstest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from '@rstest/core'; + +export default defineConfig({ + source: { + define: { + __DEV__: true, + __TEST__: true, + __BROWSER__: false, + __VERSION__: '"unknown"', + __APP_VERSION__: '"0.0.0"', + }, + }, + testEnvironment: 'jsdom', + include: ['src/**/*.spec.ts', '__tests__/**/*.spec.ts'], + globals: true, + testTimeout: 10000, +}); diff --git a/packages/data-fetch/scripts/write-dts-entrypoints.mjs b/packages/data-fetch/scripts/write-dts-entrypoints.mjs new file mode 100644 index 00000000000..2a6f2331fcf --- /dev/null +++ b/packages/data-fetch/scripts/write-dts-entrypoints.mjs @@ -0,0 +1,13 @@ +import { mkdir, writeFile } from 'node:fs/promises'; +import { dirname } from 'node:path'; + +const entrypoints = { + 'dist/server-middleware.d.ts': [ + "export { default } from './data-fetch-server-middleware';", + ], +}; + +for (const [file, lines] of Object.entries(entrypoints)) { + await mkdir(dirname(file), { recursive: true }); + await writeFile(file, `${lines.join('\n')}\n`); +} diff --git a/packages/bridge/bridge-react/src/lazy/data-fetch/cache.spec.ts b/packages/data-fetch/src/cache.spec.ts similarity index 96% rename from packages/bridge/bridge-react/src/lazy/data-fetch/cache.spec.ts rename to packages/data-fetch/src/cache.spec.ts index f8a6261281c..4e7335418ba 100644 --- a/packages/bridge/bridge-react/src/lazy/data-fetch/cache.spec.ts +++ b/packages/data-fetch/src/cache.spec.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, rs } from '@rstest/core'; -import { cache, clearStore, configureCache } from './cache'; +import { cache, clearStore, configureCache } from '../src/cache'; describe('data fetch size-limited cache', () => { beforeEach(() => { diff --git a/packages/bridge/bridge-react/src/lazy/data-fetch/cache.ts b/packages/data-fetch/src/cache.ts similarity index 98% rename from packages/bridge/bridge-react/src/lazy/data-fetch/cache.ts rename to packages/data-fetch/src/cache.ts index e24bdc6c178..f0c8c0463ed 100644 --- a/packages/bridge/bridge-react/src/lazy/data-fetch/cache.ts +++ b/packages/data-fetch/src/cache.ts @@ -1,12 +1,12 @@ -import { SizeLimitedCache } from '../../shared/size-limited-cache'; -import { getDataFetchCache } from '../utils'; +import { SizeLimitedCache } from './size-limited-cache'; +import { getDataFetchCache } from './utils'; import type { CacheConfig, CacheItem, DataFetch, DataFetchParams, -} from '../types'; +} from './types'; export const CacheSize = { KB: 1024, diff --git a/packages/bridge/bridge-react/src/lazy/data-fetch/call-data-fetch.ts b/packages/data-fetch/src/call-data-fetch.ts similarity index 85% rename from packages/bridge/bridge-react/src/lazy/data-fetch/call-data-fetch.ts rename to packages/data-fetch/src/call-data-fetch.ts index b3aeb6849ec..a555fa1266b 100644 --- a/packages/bridge/bridge-react/src/lazy/data-fetch/call-data-fetch.ts +++ b/packages/data-fetch/src/call-data-fetch.ts @@ -1,4 +1,4 @@ -import { DATA_FETCH_FUNCTION } from '../constant'; +import { DATA_FETCH_FUNCTION } from './constant'; import { dataFetchFunction } from './inject-data-fetch'; export async function callDataFetch() { diff --git a/packages/data-fetch/src/constant.ts b/packages/data-fetch/src/constant.ts new file mode 100644 index 00000000000..f32a25d974d --- /dev/null +++ b/packages/data-fetch/src/constant.ts @@ -0,0 +1,25 @@ +export const PLUGIN_IDENTIFIER = '[ Module Federation Data Fetch ]'; +export const DOWNGRADE_KEY = '_mfSSRDowngrade'; +export const DATA_FETCH_MAP_KEY = '__MF_DATA_FETCH_MAP__'; +export const DATA_FETCH_FUNCTION = '_mfDataFetch'; +export const FS_HREF = '_mfFSHref'; +export const WRAP_DATA_FETCH_ID_IDENTIFIER = 'wrap_dfip_identifier'; +export const enum MF_DATA_FETCH_TYPE { + FETCH_SERVER = 1, + FETCH_CLIENT = 2, +} + +export const enum MF_DATA_FETCH_STATUS { + LOADED = 1, + LOADING = 2, + AWAIT = 0, + ERROR = 3, +} + +export const DATA_FETCH_IDENTIFIER = 'data'; +export const DATA_FETCH_CLIENT_SUFFIX = '.client'; +export const DATA_FETCH_QUERY = 'x-mf-data-fetch'; +export const DATA_FETCH_ERROR_PREFIX = + 'caught the following error during dataFetch: '; +export const LOAD_REMOTE_ERROR_PREFIX = + 'caught the following error during loadRemote: '; diff --git a/packages/data-fetch/src/data-fetch-server-middleware.ts b/packages/data-fetch/src/data-fetch-server-middleware.ts new file mode 100644 index 00000000000..209f978f060 --- /dev/null +++ b/packages/data-fetch/src/data-fetch-server-middleware.ts @@ -0,0 +1,196 @@ +import { DATA_FETCH_QUERY, MF_DATA_FETCH_STATUS } from './constant'; +import { + getDataFetchMap, + fetchData, + initDataFetchMap, + loadDataFetchModule, +} from './utils'; +import { SEPARATOR, MANIFEST_EXT } from '@module-federation/sdk'; +import logger from './logger'; + +import type { NoSSRRemoteInfo } from './types'; +import type { MiddlewareHandler } from 'hono'; + +function wrapSetTimeout( + targetPromise: Promise, + delay = 20000, + id: string, +) { + if (targetPromise && typeof targetPromise.then === 'function') { + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + logger.warn(`Data fetch for ID ${id} timed out after 20 seconds.`); + reject(new Error(`Data fetch for ID ${id} timed out after 20 seconds`)); + }, delay); + + targetPromise + .then((value: any) => { + clearTimeout(timeoutId); + resolve(value); + }) + .catch((err: any) => { + clearTimeout(timeoutId); + reject(err); + }); + }); + } +} + +function addProtocol(url: string) { + if (url.startsWith('//')) { + return 'https:' + url; + } + return url; +} + +const getDecodeQuery = (url: URL, name: string) => { + const res = url.searchParams.get(name); + if (!res) { + return null; + } + return decodeURIComponent(res); +}; + +const dataFetchServerMiddleware: MiddlewareHandler = async (ctx, next) => { + let url: URL; + let dataFetchKey: string | null; + let params: Record; + let remoteInfo: NoSSRRemoteInfo; + try { + url = new URL(ctx.req.url); + dataFetchKey = getDecodeQuery(url, DATA_FETCH_QUERY); + params = JSON.parse(getDecodeQuery(url, 'params') || '{}'); + const remoteInfoQuery = getDecodeQuery(url, 'remoteInfo'); + remoteInfo = remoteInfoQuery ? JSON.parse(remoteInfoQuery) : null; + } catch (e) { + logger.error('fetch data from server, error: ', e); + return next(); + } + + if (!dataFetchKey) { + return next(); + } + logger.log('fetch data from server, dataFetchKey: ', dataFetchKey); + logger.debug( + 'fetch data from server, moduleInfo: ', + globalThis.__FEDERATION__?.moduleInfo, + ); + try { + const dataFetchMap = getDataFetchMap(); + if (!dataFetchMap) { + initDataFetchMap(); + } + const fetchDataPromise = dataFetchMap[dataFetchKey]?.[1]; + logger.debug( + 'fetch data from server, fetchDataPromise: ', + fetchDataPromise, + ); + if ( + fetchDataPromise && + dataFetchMap[dataFetchKey]?.[2] !== MF_DATA_FETCH_STATUS.ERROR + ) { + const targetPromise = fetchDataPromise[0]; + // Ensure targetPromise is thenable + const wrappedPromise = wrapSetTimeout(targetPromise, 20000, dataFetchKey); + if (wrappedPromise) { + const res = await wrappedPromise; + logger.log('fetch data from server, fetchDataPromise res: ', res); + return ctx.json(res); + } + logger.error( + `Expected a Promise from fetchDataPromise[0] for dataFetchKey ${dataFetchKey}, but received:`, + targetPromise, + 'Will try call new dataFetch again...', + ); + } + + if (remoteInfo) { + try { + const hostInstance = globalThis.__FEDERATION__.__INSTANCES__[0]; + const remoteEntry = `${addProtocol(remoteInfo.ssrPublicPath) + remoteInfo.ssrRemoteEntry}`; + if (!hostInstance) { + throw new Error('host instance not found!'); + } + const remote = hostInstance.options.remotes.find( + (entry: { name: string }) => entry.name === remoteInfo.name, + ); + logger.debug('find remote: ', JSON.stringify(remote)); + if (!remote) { + hostInstance.registerRemotes([ + { + name: remoteInfo.name, + entry: remoteEntry, + entryGlobalName: remoteInfo.globalName, + }, + ]); + } else if ( + !('entry' in remote) || + !remote.entry.includes(MANIFEST_EXT) + ) { + const { hostGlobalSnapshot, remoteSnapshot } = + hostInstance.snapshotHandler.getGlobalRemoteInfo(remoteInfo); + logger.debug( + 'find hostGlobalSnapshot: ', + JSON.stringify(hostGlobalSnapshot), + ); + logger.debug('find remoteSnapshot: ', JSON.stringify(remoteSnapshot)); + + if (!hostGlobalSnapshot || !remoteSnapshot) { + if ('version' in remote) { + // @ts-ignore + delete remote.version; + } + // @ts-ignore + remote.entry = remoteEntry; + remote.entryGlobalName = remoteInfo.globalName; + } + } + } catch (e) { + ctx.status(500); + return ctx.text( + `failed to fetch ${remoteInfo.name} data, error:\n ${e}`, + ); + } + } + + const dataFetchItem = dataFetchMap[dataFetchKey]; + logger.debug('fetch data from server, dataFetchItem: ', dataFetchItem); + if (dataFetchItem) { + const callFetchDataPromise = fetchData(dataFetchKey, { + ...params, + isDowngrade: !remoteInfo, + _id: dataFetchKey, + }); + const wrappedPromise = wrapSetTimeout( + callFetchDataPromise, + 20000, + dataFetchKey, + ); + if (wrappedPromise) { + const res = await wrappedPromise; + logger.log('fetch data from server, dataFetchItem res: ', res); + return ctx.json(res); + } + } + + const remoteId = dataFetchKey.split(SEPARATOR)[0]; + const hostInstance = globalThis.__FEDERATION__.__INSTANCES__[0]; + if (!hostInstance) { + throw new Error('host instance not found!'); + } + const dataFetchFn = await loadDataFetchModule(hostInstance, remoteId); + const data = await dataFetchFn({ + ...params, + isDowngrade: !remoteInfo, + _id: dataFetchKey, + }); + logger.log('fetch data from server, loadDataFetchModule res: ', data); + return ctx.json(data); + } catch (e) { + logger.error('server plugin data fetch error: ', e); + ctx.status(500); + return ctx.text(`failed to fetch ${remoteInfo.name} data, error:\n ${e}`); + } +}; + +export default dataFetchServerMiddleware; diff --git a/packages/data-fetch/src/data-fetch-utils.ts b/packages/data-fetch/src/data-fetch-utils.ts new file mode 100644 index 00000000000..db35110e60f --- /dev/null +++ b/packages/data-fetch/src/data-fetch-utils.ts @@ -0,0 +1,16 @@ +export { callDataFetch } from './call-data-fetch'; +export { injectDataFetch } from './inject-data-fetch'; +export { + CacheSize, + CacheTime, + configureCache, + generateKey, + cache, + revalidateTag, + clearStore, +} from './cache'; + +export type { CacheStatus, CacheStatsInfo } from './cache'; + +export type { PrefetchOptions } from './prefetch'; +export { prefetch } from './prefetch'; diff --git a/packages/data-fetch/src/env.d.ts b/packages/data-fetch/src/env.d.ts new file mode 100644 index 00000000000..aa615ba4a66 --- /dev/null +++ b/packages/data-fetch/src/env.d.ts @@ -0,0 +1,8 @@ +import type { ModuleFederation } from '@module-federation/runtime'; + +declare global { + var __FEDERATION__: { + moduleInfo?: unknown; + __INSTANCES__: ModuleFederation[]; + }; +} diff --git a/packages/data-fetch/src/index.ts b/packages/data-fetch/src/index.ts new file mode 100644 index 00000000000..d278bca8375 --- /dev/null +++ b/packages/data-fetch/src/index.ts @@ -0,0 +1,55 @@ +export { default as autoFetchDataPlugin } from './runtime-plugin'; +export { callDataFetch } from './call-data-fetch'; +export { injectDataFetch } from './inject-data-fetch'; +export { + CacheSize, + CacheTime, + configureCache, + generateKey, + cache, + revalidateTag, + clearStore, +} from './cache'; +export type { CacheStatus, CacheStatsInfo } from './cache'; +export { prefetch } from './prefetch'; +export type { PrefetchOptions } from './prefetch'; +export { + flushDataFetch, + setSSREnv, + getDataFetchInfo, + initDataFetchMap, + getDataFetchItem, + getDataFetchMap, + getDataFetchCache, + setDataFetchItemLoadedStatus, + wrapDataFetchId, + getDataFetchIdWithErrorMsgs, + fetchData, + getDataFetchMapKey, + loadDataFetchModule, + isDataLoaderExpose, + getDowngradeTag, + callAllDowngrade, + callDowngrade, + isCSROnly, + isServerEnv, + getLoadedRemoteInfos, +} from './utils'; +export { + DATA_FETCH_ERROR_PREFIX, + LOAD_REMOTE_ERROR_PREFIX, + DATA_FETCH_FUNCTION, + FS_HREF, + MF_DATA_FETCH_TYPE, + DATA_FETCH_IDENTIFIER, + DATA_FETCH_CLIENT_SUFFIX, + DATA_FETCH_QUERY, + WRAP_DATA_FETCH_ID_IDENTIFIER, +} from './constant'; +export type { + DataFetchParams, + NoSSRRemoteInfo, + DataFetch, + CacheConfig, + MF_DATA_FETCH_MAP, +} from './types'; diff --git a/packages/bridge/bridge-react/src/lazy/data-fetch/inject-data-fetch.ts b/packages/data-fetch/src/inject-data-fetch.ts similarity index 96% rename from packages/bridge/bridge-react/src/lazy/data-fetch/inject-data-fetch.ts rename to packages/data-fetch/src/inject-data-fetch.ts index bc654551df6..eb68909d634 100644 --- a/packages/bridge/bridge-react/src/lazy/data-fetch/inject-data-fetch.ts +++ b/packages/data-fetch/src/inject-data-fetch.ts @@ -4,12 +4,12 @@ import { FS_HREF, MF_DATA_FETCH_STATUS, MF_DATA_FETCH_TYPE, -} from '../constant'; -import logger from '../logger'; +} from './constant'; +import logger from './logger'; import { dataFetchFunctionOptions, MF_DATA_FETCH_MAP_VALUE_PROMISE_SET, -} from '../types'; +} from './types'; import { callAllDowngrade, callDowngrade, @@ -17,7 +17,7 @@ import { getDataFetchMap, getDowngradeTag, initDataFetchMap, -} from '../utils'; +} from './utils'; const dataFetchFunction = async function (options: dataFetchFunctionOptions) { const [id, data, downgrade] = options; diff --git a/packages/data-fetch/src/logger.ts b/packages/data-fetch/src/logger.ts new file mode 100644 index 00000000000..c1be3252163 --- /dev/null +++ b/packages/data-fetch/src/logger.ts @@ -0,0 +1,6 @@ +import { createLogger } from '@module-federation/sdk'; +import { PLUGIN_IDENTIFIER } from './constant'; + +const logger = createLogger(PLUGIN_IDENTIFIER); + +export default logger; diff --git a/packages/bridge/bridge-react/src/lazy/data-fetch/prefetch.ts b/packages/data-fetch/src/prefetch.ts similarity index 94% rename from packages/bridge/bridge-react/src/lazy/data-fetch/prefetch.ts rename to packages/data-fetch/src/prefetch.ts index aa491eee5ca..44cfac5525a 100644 --- a/packages/bridge/bridge-react/src/lazy/data-fetch/prefetch.ts +++ b/packages/data-fetch/src/prefetch.ts @@ -1,12 +1,8 @@ import type { getInstance } from '@module-federation/runtime'; -import { - getDataFetchInfo, - getDataFetchMap, - getDataFetchMapKey, -} from '../utils'; -import logger from '../logger'; +import { getDataFetchInfo, getDataFetchMap, getDataFetchMapKey } from './utils'; +import logger from './logger'; import helpers from '@module-federation/runtime/helpers'; -import { DataFetchParams } from '../types'; +import { DataFetchParams } from './types'; export type PrefetchOptions = { id: string; diff --git a/packages/bridge/bridge-react/src/lazy/data-fetch/runtime-plugin.ts b/packages/data-fetch/src/runtime-plugin.ts similarity index 95% rename from packages/bridge/bridge-react/src/lazy/data-fetch/runtime-plugin.ts rename to packages/data-fetch/src/runtime-plugin.ts index 47a6d956e48..4e9468bf8c8 100644 --- a/packages/bridge/bridge-react/src/lazy/data-fetch/runtime-plugin.ts +++ b/packages/data-fetch/src/runtime-plugin.ts @@ -9,15 +9,15 @@ import { isDataLoaderExpose, loadDataFetchModule, isServerEnv, -} from '../utils'; -import logger from '../logger'; +} from './utils'; +import logger from './logger'; import { MF_DATA_FETCH_TYPE, MF_DATA_FETCH_STATUS, DATA_FETCH_CLIENT_SUFFIX, -} from '../constant'; +} from './constant'; -import type { MF_DATA_FETCH_MAP_VALUE } from '../types'; +import type { MF_DATA_FETCH_MAP_VALUE } from './types'; import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime'; const autoFetchData: () => ModuleFederationRuntimePlugin = () => { diff --git a/packages/bridge/bridge-react/src/shared/size-limited-cache.spec.ts b/packages/data-fetch/src/size-limited-cache.spec.ts similarity index 97% rename from packages/bridge/bridge-react/src/shared/size-limited-cache.spec.ts rename to packages/data-fetch/src/size-limited-cache.spec.ts index b815c62453a..a887baa2c80 100644 --- a/packages/bridge/bridge-react/src/shared/size-limited-cache.spec.ts +++ b/packages/data-fetch/src/size-limited-cache.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from '@rstest/core'; -import { SizeLimitedCache } from './size-limited-cache'; +import { SizeLimitedCache } from '../src/size-limited-cache'; describe('SizeLimitedCache', () => { it('evicts the least recently used entries by total size', () => { diff --git a/packages/data-fetch/src/size-limited-cache.ts b/packages/data-fetch/src/size-limited-cache.ts new file mode 100644 index 00000000000..a43c0f84d68 --- /dev/null +++ b/packages/data-fetch/src/size-limited-cache.ts @@ -0,0 +1,115 @@ +export interface SizeLimitedCacheOptions { + maxSize: number; + sizeCalculation?: (value: V, key: K) => number; +} + +export interface SizeLimitedCacheSetOptions { + size?: number; +} + +interface CacheEntry { + value: V; + size: number; +} + +/** + * A small, size-aware least-recently-used cache. + * + * This intentionally implements only the operations used by federation + * packages. Reading or checking an entry refreshes its recency. + */ +export class SizeLimitedCache { + readonly maxSize: number; + + private readonly entries = new Map>(); + private readonly sizeCalculation?: (value: V, key: K) => number; + private currentSize = 0; + + constructor(options: SizeLimitedCacheOptions) { + if (!Number.isInteger(options.maxSize) || options.maxSize <= 0) { + throw new TypeError('maxSize must be a positive integer'); + } + + this.maxSize = options.maxSize; + this.sizeCalculation = options.sizeCalculation; + } + + get size(): number { + return this.entries.size; + } + + get calculatedSize(): number { + return this.currentSize; + } + + get(key: K): V | undefined { + const entry = this.entries.get(key); + if (!entry) { + return undefined; + } + + this.refresh(key, entry); + return entry.value; + } + + has(key: K): boolean { + const entry = this.entries.get(key); + if (!entry) { + return false; + } + + this.refresh(key, entry); + return true; + } + + set(key: K, value: V, options: SizeLimitedCacheSetOptions = {}): this { + const entrySize = options.size ?? this.sizeCalculation?.(value, key) ?? 1; + + if (!Number.isInteger(entrySize) || entrySize <= 0) { + throw new TypeError('entry size must be a positive integer'); + } + + this.delete(key); + + if (entrySize > this.maxSize) { + return this; + } + + this.entries.set(key, { value, size: entrySize }); + this.currentSize += entrySize; + this.evictUntilWithinLimit(); + + return this; + } + + delete(key: K): boolean { + const entry = this.entries.get(key); + if (!entry) { + return false; + } + + this.currentSize -= entry.size; + return this.entries.delete(key); + } + + clear(): void { + this.entries.clear(); + this.currentSize = 0; + } + + private refresh(key: K, entry: CacheEntry): void { + this.entries.delete(key); + this.entries.set(key, entry); + } + + private evictUntilWithinLimit(): void { + while (this.currentSize > this.maxSize) { + const oldest = this.entries.keys().next(); + if (oldest.done) { + this.currentSize = 0; + return; + } + this.delete(oldest.value); + } + } +} diff --git a/packages/data-fetch/src/types.ts b/packages/data-fetch/src/types.ts new file mode 100644 index 00000000000..a4cc03ff0bd --- /dev/null +++ b/packages/data-fetch/src/types.ts @@ -0,0 +1,75 @@ +import { MF_DATA_FETCH_TYPE, MF_DATA_FETCH_STATUS } from './constant'; +import type { SizeLimitedCache } from './size-limited-cache'; + +export type dataFetchFunctionOptions = [ + id?: string, + data?: unknown, + downgrade?: boolean, +]; + +declare global { + var _mfSSRDowngrade: string[] | true | undefined; + var __MF_DATA_FETCH_MAP__: MF_DATA_FETCH_MAP | undefined; + var FEDERATION_SERVER_QUERY: Record | undefined; + var FEDERATION_SSR: boolean | undefined; + var _mfFSHref: string | undefined; + var _mfDataFetch: Array | undefined; + var __MF_DATA_FETCH_CACHE__: MF_DATA_FETCH_CACHE | undefined; +} + +export interface CacheItem { + data: T; + timestamp: number; + isRevalidating?: boolean; +} + +export type DataFetchParams = { + isDowngrade: boolean; + _id?: string; +} & Record; +export type DataFetch = (params: DataFetchParams) => Promise; + +// loading, resolve, reject +export type MF_DATA_FETCH_MAP_VALUE_PROMISE_SET = [ + Promise, + ((data: unknown) => void)?, + ((err: unknown) => void)?, +]; +export type MF_DATA_FETCH_MAP_VALUE = [ + [ + // getDataFetchGetter + () => Promise>, + // data fetch type + MF_DATA_FETCH_TYPE, + // getDataFetchPromise + Promise>?, + ], + MF_DATA_FETCH_MAP_VALUE_PROMISE_SET?, + MF_DATA_FETCH_STATUS?, +]; +export type MF_DATA_FETCH_MAP = Record; +export type MF_SSR_DOWNGRADE = string[] | true | undefined; + +export type NoSSRRemoteInfo = { + name: string; + version: string; + ssrPublicPath: string; + ssrRemoteEntry: string; + globalName: string; +}; + +export interface CacheConfig { + maxSize?: number; + unstable_shouldDisable?: ({ + request, + }: { + request: Request; + }) => boolean | Promise; +} + +// key is the id, default is the same with dataFetchMapId, but it can be custom by passing dataFetchParams.id +export type MF_DATA_FETCH_CACHE = { + cacheStore?: SizeLimitedCache>>; + tagKeyMap?: Map>; + cacheConfig?: CacheConfig; +}; diff --git a/packages/data-fetch/src/utils.ts b/packages/data-fetch/src/utils.ts new file mode 100644 index 00000000000..19f2794efa5 --- /dev/null +++ b/packages/data-fetch/src/utils.ts @@ -0,0 +1,375 @@ +import { + isBrowserEnvValue, + composeKeyWithSeparator, +} from '@module-federation/sdk'; +import logger from './logger'; +import { + DOWNGRADE_KEY, + MF_DATA_FETCH_STATUS, + WRAP_DATA_FETCH_ID_IDENTIFIER, + DATA_FETCH_QUERY, + MF_DATA_FETCH_TYPE, + DATA_FETCH_IDENTIFIER, + DATA_FETCH_CLIENT_SUFFIX, +} from './constant'; + +import type { GlobalModuleInfo } from '@module-federation/sdk'; +import type { + DataFetchParams, + MF_DATA_FETCH_MAP, + NoSSRRemoteInfo, + MF_SSR_DOWNGRADE, + MF_DATA_FETCH_MAP_VALUE_PROMISE_SET, + MF_DATA_FETCH_CACHE, +} from './types'; +import type { ModuleFederation } from '@module-federation/runtime'; +import { clearStore } from './cache'; + +export const getDataFetchInfo = ({ + name, + alias, + id, + remoteSnapshot, +}: { + id: string; + name: string; + remoteSnapshot: GlobalModuleInfo[string]; + alias?: string; +}) => { + if (!remoteSnapshot) { + return; + } + if (!('modules' in remoteSnapshot)) { + return; + } + const regex = new RegExp(`^${name}(/[^/].*|)$`); + const nameOrAlias = regex.test(id) ? name : alias || name; + + const expose = id.replace(nameOrAlias, ''); + let dataFetchName = ''; + let dataFetchId = ''; + let dataFetchKey = ''; + if (expose.startsWith('/')) { + dataFetchName = `${expose.slice(1)}.${DATA_FETCH_IDENTIFIER}`; + dataFetchId = `${id}.${DATA_FETCH_IDENTIFIER}`; + dataFetchKey = `${name}${expose}.${DATA_FETCH_IDENTIFIER}`; + } else if (expose === '') { + dataFetchName = DATA_FETCH_IDENTIFIER; + dataFetchId = `${id}/${DATA_FETCH_IDENTIFIER}`; + dataFetchKey = `${name}/${DATA_FETCH_IDENTIFIER}`; + } else { + return; + } + + if (!dataFetchName || !dataFetchId || !dataFetchKey) { + return; + } + + if ( + !remoteSnapshot.modules.find( + (module: { moduleName: string }) => module.moduleName === dataFetchName, + ) + ) { + return; + } + + return { + dataFetchName, + dataFetchId, + dataFetchKey, + }; +}; + +export function initDataFetchMap() { + globalThis.__MF_DATA_FETCH_MAP__ ||= {}; +} + +export function getDataFetchItem(id: string) { + return (globalThis.__MF_DATA_FETCH_MAP__ as MF_DATA_FETCH_MAP)?.[id]; +} + +export function getDataFetchMap() { + return globalThis.__MF_DATA_FETCH_MAP__ as MF_DATA_FETCH_MAP; +} + +export function getDataFetchCache() { + return globalThis.__MF_DATA_FETCH_CACHE__ as MF_DATA_FETCH_CACHE; +} + +export const flushDataFetch = () => { + globalThis.__MF_DATA_FETCH_MAP__ = {}; + globalThis[DOWNGRADE_KEY] = undefined; + clearStore(); +}; + +export function setDataFetchItemLoadedStatus(id: string) { + const dataFetchItem = getDataFetchItem(id); + if (!dataFetchItem) { + return; + } + dataFetchItem[2] = MF_DATA_FETCH_STATUS.LOADED; +} + +export const wrapDataFetchId = (id?: string) => { + return `${WRAP_DATA_FETCH_ID_IDENTIFIER}${id}${WRAP_DATA_FETCH_ID_IDENTIFIER}`; +}; + +export const getDataFetchIdWithErrorMsgs = (errMsgs: string) => { + const firstIdentifierIndex = errMsgs.indexOf(WRAP_DATA_FETCH_ID_IDENTIFIER); + if (firstIdentifierIndex === -1) { + return undefined; + } + + const secondIdentifierIndex = errMsgs.indexOf( + WRAP_DATA_FETCH_ID_IDENTIFIER, + firstIdentifierIndex + WRAP_DATA_FETCH_ID_IDENTIFIER.length, + ); + + if (secondIdentifierIndex === -1) { + return undefined; + } + + return errMsgs.substring( + firstIdentifierIndex + WRAP_DATA_FETCH_ID_IDENTIFIER.length, + secondIdentifierIndex, + ); +}; + +export async function fetchData( + id: string, + params: DataFetchParams, + remoteInfo?: NoSSRRemoteInfo, +): Promise { + const callFetchData = async () => { + const item = getDataFetchItem(id); + if (!item) { + return; + } + const [fetchDataFnArr, ..._rest] = item; + + const fetchDataFn = await fetchDataFnArr[2]; + if (!fetchDataFn) { + return; + } + return fetchDataFn({ + ...params, + _id: id, + }); + }; + if (isBrowserEnvValue) { + const dataFetchItem = getDataFetchItem(id); + if (!dataFetchItem) { + throw new Error(`dataFetchItem not found, id: ${id}`); + } + if (dataFetchItem[1]?.[0]) { + return dataFetchItem[1][0]; + } + + if (isCSROnly()) { + logger.debug('==========csr only!'); + return callFetchData(); + } + + if (remoteInfo) { + return callDowngrade(id, params, remoteInfo); + } + + const mfDowngrade = getDowngradeTag(); + if ( + mfDowngrade && + (typeof mfDowngrade === 'boolean' || mfDowngrade.includes(id)) + ) { + return callDowngrade(id, { ...params, isDowngrade: true }); + } + + let res; + let rej; + const p = new Promise((resolve, reject) => { + res = resolve; + rej = reject; + }); + dataFetchItem[1] = [p, res, rej]; + dataFetchItem[2] = MF_DATA_FETCH_STATUS.AWAIT; + return dataFetchItem[1][0]; + } + + return callFetchData(); +} + +export function getDataFetchMapKey( + dataFetchInfo?: ReturnType, + hostInfo?: { name: string; version?: string }, +) { + if (!dataFetchInfo || !hostInfo) { + return; + } + + const { dataFetchKey } = dataFetchInfo; + + return composeKeyWithSeparator(dataFetchKey, hostInfo.name, hostInfo.version); +} + +export async function loadDataFetchModule( + instance: ModuleFederation, + id: string, +) { + return instance.loadRemote(id).then((m: unknown) => { + if ( + m && + typeof m === 'object' && + 'fetchData' in m && + typeof m.fetchData === 'function' + ) { + return m.fetchData as (params: DataFetchParams) => Promise; + } + throw new Error( + `fetchData not found in remote ${id}, ${JSON.stringify(m)}`, + ); + }); +} + +export function isDataLoaderExpose(exposeKey: string) { + return ( + exposeKey.endsWith(DATA_FETCH_IDENTIFIER) || + exposeKey.endsWith(DATA_FETCH_CLIENT_SUFFIX) + ); +} + +export function getDowngradeTag() { + return globalThis[DOWNGRADE_KEY] as MF_SSR_DOWNGRADE; +} + +export function callAllDowngrade() { + const dataFetchMap = getDataFetchMap(); + if (!dataFetchMap) { + return; + } + Object.keys(dataFetchMap).forEach((key) => { + callDowngrade(key); + }); +} + +export async function callDowngrade( + id: string, + params?: DataFetchParams, + remoteInfo?: NoSSRRemoteInfo, +) { + const dataFetchMap = getDataFetchMap(); + if (!dataFetchMap) { + return; + } + const mfDataFetch = dataFetchMap[id]; + if (mfDataFetch?.[2] === MF_DATA_FETCH_STATUS.AWAIT) { + mfDataFetch[2] = MF_DATA_FETCH_STATUS.LOADING; + let promise: MF_DATA_FETCH_MAP_VALUE_PROMISE_SET[0]; + let res: MF_DATA_FETCH_MAP_VALUE_PROMISE_SET[1]; + let rej: MF_DATA_FETCH_MAP_VALUE_PROMISE_SET[2]; + if (mfDataFetch[1]) { + promise = mfDataFetch[1][0]; + res = mfDataFetch[1][1]; + rej = mfDataFetch[1][2]; + } else { + promise = new Promise((resolve, reject) => { + res = resolve; + rej = reject; + }); + mfDataFetch[1] = [promise, res, rej]; + } + const dataFetchType = mfDataFetch[0][1]; + if (dataFetchType === MF_DATA_FETCH_TYPE.FETCH_CLIENT) { + try { + mfDataFetch[0][0]().then(async (getDataFetchFn) => { + return getDataFetchFn({ + ...params, + isDowngrade: true, + _id: id, + }).then((data) => { + mfDataFetch[2] = MF_DATA_FETCH_STATUS.LOADED; + res && res(data); + }); + }); + } catch (e) { + mfDataFetch[2] = MF_DATA_FETCH_STATUS.ERROR; + rej && rej(e); + } + } else if (dataFetchType === MF_DATA_FETCH_TYPE.FETCH_SERVER) { + try { + const currentUrl = new URL(window.location.href); + currentUrl.searchParams.set(DATA_FETCH_QUERY, encodeURIComponent(id)); + if (params) { + currentUrl.searchParams.set( + 'params', + encodeURIComponent(JSON.stringify(params)), + ); + } + if (remoteInfo) { + currentUrl.searchParams.set( + 'remoteInfo', + encodeURIComponent(JSON.stringify(remoteInfo)), + ); + } + const fetchServerQuery = globalThis.FEDERATION_SERVER_QUERY; + if (fetchServerQuery && typeof fetchServerQuery === 'object') { + Object.keys(fetchServerQuery).forEach((key) => { + currentUrl.searchParams.set( + key, + JSON.stringify(fetchServerQuery[key]), + ); + }); + } + const fetchUrl = currentUrl.toString(); + const data = await fetch(fetchUrl).then((res) => res.json()); + mfDataFetch[2] = MF_DATA_FETCH_STATUS.LOADED; + res && res(data); + } catch (e) { + mfDataFetch[2] = MF_DATA_FETCH_STATUS.ERROR; + rej && rej(e); + } + } + + return promise; + } +} + +export function isCSROnly() { + // @ts-ignore modern.js will inject window._SSR_DATA if enable ssr + return window._SSR_DATA === undefined; +} + +export function isServerEnv() { + return typeof window === 'undefined'; +} + +export function setSSREnv({ + fetchServerQuery, +}: { + fetchServerQuery?: Record; +}) { + globalThis.FEDERATION_SSR = true; + globalThis.FEDERATION_SERVER_QUERY = fetchServerQuery; +} + +export function getLoadedRemoteInfos( + id: string, + instance: ModuleFederation | null, +) { + if (!instance) { + return; + } + const { name, expose } = instance.remoteHandler.idToRemoteMap[id] || {}; + if (!name) { + return; + } + const module = instance.moduleCache.get(name); + if (!module) { + return; + } + const { remoteSnapshot } = instance.snapshotHandler.getGlobalRemoteInfo( + module.remoteInfo, + ); + return { + ...module.remoteInfo, + snapshot: remoteSnapshot, + expose, + }; +} diff --git a/packages/data-fetch/tsconfig.json b/packages/data-fetch/tsconfig.json new file mode 100644 index 00000000000..a1cc86f7f11 --- /dev/null +++ b/packages/data-fetch/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "useDefineForClassFields": true, + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "rootDir": "./src", + "module": "commonjs", + "target": "es2015", + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "dist", + "skipLibCheck": true, + "strict": true, + "lib": ["esnext", "dom"], + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "sourceMap": true, + "ignoreDeprecations": "6.0" + }, + "include": ["src/**/*.ts", "src/**/*.d.ts"] +} diff --git a/packages/data-fetch/vite.config.ts b/packages/data-fetch/vite.config.ts new file mode 100644 index 00000000000..3660bb4f1f7 --- /dev/null +++ b/packages/data-fetch/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from 'vite'; +import path from 'path'; +import packageJson from './package.json'; + +const peerDepsKeys = Object.keys(packageJson.peerDependencies ?? {}); + +export default defineConfig({ + build: { + lib: { + entry: { + index: path.resolve(__dirname, 'src/index.ts'), + 'data-fetch-utils': path.resolve(__dirname, 'src/data-fetch-utils.ts'), + 'server-middleware': path.resolve( + __dirname, + 'src/data-fetch-server-middleware.ts', + ), + utils: path.resolve(__dirname, 'src/utils.ts'), + 'size-limited-cache': path.resolve( + __dirname, + 'src/size-limited-cache.ts', + ), + }, + formats: ['cjs', 'es'], + fileName: (format, entryName) => `${entryName}.${format}.js`, + }, + rollupOptions: { + external: [ + ...peerDepsKeys, + '@module-federation/runtime/helpers', + '@module-federation/sdk', + ], + }, + minify: false, + }, + define: { + __APP_VERSION__: JSON.stringify(packageJson.version), + }, +}); From d3b9831844e53e31e1f24c7adae4baea840070de Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Mon, 10 Aug 2026 20:09:25 +0200 Subject: [PATCH 2/8] fix: address data-fetch extraction review findings --- .changeset/config.json | 1 + .changeset/extract-data-fetch.md | 7 ++++ .../scripts/write-dts-entrypoints.mjs | 6 +++ .../bridge/bridge-react/src/data-fetch.ts | 3 ++ packages/data-fetch/README.md | 37 +++++++++++++++++++ .../data-fetch/__tests__/global-types.spec.ts | 24 ++++++++++++ packages/data-fetch/package.json | 5 ++- packages/data-fetch/tsconfig.json | 3 +- 8 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 .changeset/extract-data-fetch.md create mode 100644 packages/data-fetch/README.md create mode 100644 packages/data-fetch/__tests__/global-types.spec.ts diff --git a/.changeset/config.json b/.changeset/config.json index ff7d47e9f9c..71a9d434c34 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -15,6 +15,7 @@ "@module-federation/dts-plugin", "@module-federation/third-party-dts-extractor", "@module-federation/devtools", + "@module-federation/data-fetch", "@module-federation/bridge-react", "@module-federation/bridge-vue3", "@module-federation/bridge-shared", diff --git a/.changeset/extract-data-fetch.md b/.changeset/extract-data-fetch.md new file mode 100644 index 00000000000..ec0fff9fb1a --- /dev/null +++ b/.changeset/extract-data-fetch.md @@ -0,0 +1,7 @@ +--- +"@module-federation/data-fetch": minor +"@module-federation/bridge-react": patch +"@module-federation/bridge-vue3": patch +--- + +Extract data-fetch into the framework-agnostic `@module-federation/data-fetch` package. `@module-federation/bridge-react` and `@module-federation/bridge-vue3` continue to re-export data-fetch APIs for backward compatibility. diff --git a/packages/bridge/bridge-react/scripts/write-dts-entrypoints.mjs b/packages/bridge/bridge-react/scripts/write-dts-entrypoints.mjs index 2f5d48d5189..e02095ad867 100644 --- a/packages/bridge/bridge-react/scripts/write-dts-entrypoints.mjs +++ b/packages/bridge/bridge-react/scripts/write-dts-entrypoints.mjs @@ -16,6 +16,12 @@ const entrypoints = { ], 'dist/lazy-utils.d.ts': ["export * from './lazy/utils';"], 'dist/data-fetch-utils.d.ts': ["export * from './lazy/data-fetch';"], + 'dist/data-fetch.d.ts': [ + "import '@module-federation/data-fetch';", + "export type { DataFetchParams, NoSSRRemoteInfo, CollectSSRAssetsOptions, CreateLazyComponentOptions, CacheStatus, CacheStatsInfo, PrefetchOptions, } from './lazy';", + "export { ERROR_TYPE, createLazyComponent, collectSSRAssets, callDataFetch, setSSREnv, autoFetchDataPlugin, CacheSize, CacheTime, configureCache, generateKey, cache, revalidateTag, clearStore, prefetch, flushDataFetch, } from './lazy';", + "export { lazyLoadComponentPlugin } from './plugins/lazy-load-component-plugin';", + ], 'dist/data-fetch-server-middleware.d.ts': [ "export { default } from './lazy/data-fetch/data-fetch-server-middleware';", ], diff --git a/packages/bridge/bridge-react/src/data-fetch.ts b/packages/bridge/bridge-react/src/data-fetch.ts index cebcff11baa..b04d9a2e4dd 100644 --- a/packages/bridge/bridge-react/src/data-fetch.ts +++ b/packages/bridge/bridge-react/src/data-fetch.ts @@ -1,3 +1,6 @@ +// Side-effect type import so data-fetch global augmentations apply to consumers. +import type {} from '@module-federation/data-fetch'; + export type { DataFetchParams, NoSSRRemoteInfo, diff --git a/packages/data-fetch/README.md b/packages/data-fetch/README.md new file mode 100644 index 00000000000..18951b557ca --- /dev/null +++ b/packages/data-fetch/README.md @@ -0,0 +1,37 @@ +# @module-federation/data-fetch + +Framework-agnostic data fetching utilities for Module Federation: SSR data loaders, caching, prefetch, and the `autoFetchDataPlugin` runtime plugin. + +`@module-federation/bridge-react` and `@module-federation/bridge-vue3` re-export these APIs for backward compatibility. Prefer importing from this package for framework-agnostic usage. + +## Installation + +```bash +pnpm add @module-federation/data-fetch +``` + +Optional peers: + +- `@module-federation/runtime` — required only when registering `autoFetchDataPlugin` +- `hono` — required only for the server middleware entry + +## Main imports + +```ts +// Core APIs and runtime plugin +import { autoFetchDataPlugin, callDataFetch, cache, configureCache, prefetch, flushDataFetch, setSSREnv } from '@module-federation/data-fetch'; + +// Subpath entry points +import {} from /* ... */ '@module-federation/data-fetch/data-fetch-utils'; +import dataFetchServerMiddleware from '@module-federation/data-fetch/server-middleware'; +import { SizeLimitedCache } from '@module-federation/data-fetch/size-limited-cache'; +import {} from /* ... */ '@module-federation/data-fetch/utils'; +``` + +## Bridge re-exports + +Existing bridge entry points remain available: + +```ts +import { callDataFetch, prefetch } from '@module-federation/bridge-react/data-fetch'; +``` diff --git a/packages/data-fetch/__tests__/global-types.spec.ts b/packages/data-fetch/__tests__/global-types.spec.ts new file mode 100644 index 00000000000..6dbb1d469ac --- /dev/null +++ b/packages/data-fetch/__tests__/global-types.spec.ts @@ -0,0 +1,24 @@ +import { test, expect } from '@rstest/core'; +import type { MF_DATA_FETCH_MAP } from '../src/types'; + +type Assert = T; + +// Ensures data-fetch global augmentations are declared for consumers. +type _GlobalsDeclared = Assert< + [ + typeof globalThis.__MF_DATA_FETCH_MAP__, + typeof globalThis._mfSSRDowngrade, + typeof globalThis._mfDataFetch, + ] extends [ + MF_DATA_FETCH_MAP | undefined, + string[] | true | undefined, + Array<[id?: string, data?: unknown, downgrade?: boolean]> | undefined, + ] + ? true + : false +>; + +test('declares data-fetch globals for TypeScript consumers', () => { + void (0 as unknown as _GlobalsDeclared); + expect(true).toBe(true); +}); diff --git a/packages/data-fetch/package.json b/packages/data-fetch/package.json index 30fd41384b2..fe731732390 100644 --- a/packages/data-fetch/package.json +++ b/packages/data-fetch/package.json @@ -75,10 +75,13 @@ "@module-federation/sdk": "workspace:*" }, "peerDependencies": { - "@module-federation/runtime": "*", + "@module-federation/runtime": "workspace:*", "hono": ">=3" }, "peerDependenciesMeta": { + "@module-federation/runtime": { + "optional": true + }, "hono": { "optional": true } diff --git a/packages/data-fetch/tsconfig.json b/packages/data-fetch/tsconfig.json index a1cc86f7f11..f8266aa16c1 100644 --- a/packages/data-fetch/tsconfig.json +++ b/packages/data-fetch/tsconfig.json @@ -20,5 +20,6 @@ "sourceMap": true, "ignoreDeprecations": "6.0" }, - "include": ["src/**/*.ts", "src/**/*.d.ts"] + "include": ["src/**/*.ts", "src/**/*.d.ts"], + "exclude": ["**/*.spec.ts", "**/__tests__/**"] } From 626c2653cb147cbb34e818dc5b58ba72835ef548 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Mon, 10 Aug 2026 20:28:21 +0200 Subject: [PATCH 3/8] fix: use patch bumps for data-fetch extraction changeset Co-authored-by: Cursor --- .changeset/extract-data-fetch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/extract-data-fetch.md b/.changeset/extract-data-fetch.md index ec0fff9fb1a..b9cfc2a31d2 100644 --- a/.changeset/extract-data-fetch.md +++ b/.changeset/extract-data-fetch.md @@ -1,5 +1,5 @@ --- -"@module-federation/data-fetch": minor +"@module-federation/data-fetch": patch "@module-federation/bridge-react": patch "@module-federation/bridge-vue3": patch --- From 1ab112e04032c9ea3e2f92c98a51b91b3d7c1399 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Mon, 10 Aug 2026 21:39:20 +0200 Subject: [PATCH 4/8] fix: align vue3-bridge data-fetch re-exports with agnostic subset Co-authored-by: Cursor --- packages/bridge/vue3-bridge/src/index.ts | 6 ++++++ packages/data-fetch/README.md | 1 + 2 files changed, 7 insertions(+) diff --git a/packages/bridge/vue3-bridge/src/index.ts b/packages/bridge/vue3-bridge/src/index.ts index 24dba771e85..11e7124fc2c 100644 --- a/packages/bridge/vue3-bridge/src/index.ts +++ b/packages/bridge/vue3-bridge/src/index.ts @@ -1,3 +1,6 @@ +// Side-effect type import so data-fetch global augmentations apply to consumers. +import type {} from '@module-federation/data-fetch'; + export { createBridgeComponent } from './provider'; export type { ProviderFnParams } from './provider'; export { createRemoteComponent, createRemoteAppComponent } from './create'; @@ -5,12 +8,15 @@ export type { RenderFnParams } from '@module-federation/bridge-shared'; export { autoFetchDataPlugin, + callDataFetch, cache, clearStore, configureCache, + flushDataFetch, generateKey, prefetch, revalidateTag, + setSSREnv, CacheSize, CacheTime, } from '@module-federation/data-fetch'; diff --git a/packages/data-fetch/README.md b/packages/data-fetch/README.md index 18951b557ca..57bf1a9400b 100644 --- a/packages/data-fetch/README.md +++ b/packages/data-fetch/README.md @@ -34,4 +34,5 @@ Existing bridge entry points remain available: ```ts import { callDataFetch, prefetch } from '@module-federation/bridge-react/data-fetch'; +import { callDataFetch, prefetch } from '@module-federation/bridge-vue3'; ``` From eb6c3c9c7bdb8b6c9bdba3a21939f83f9cd2f465 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Mon, 10 Aug 2026 22:04:16 +0200 Subject: [PATCH 5/8] fix: harden vue3 globals emit and data-fetch type tests Co-authored-by: Cursor --- packages/bridge/vue3-bridge/package.json | 2 +- .../vue3-bridge/scripts/write-dts-entrypoints.mjs | 10 ++++++++++ packages/bridge/vue3-bridge/src/index.ts | 1 + packages/data-fetch/__tests__/global-types.spec.ts | 12 +++++++++++- 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 packages/bridge/vue3-bridge/scripts/write-dts-entrypoints.mjs diff --git a/packages/bridge/vue3-bridge/package.json b/packages/bridge/vue3-bridge/package.json index b2846a992e8..c4e13e883f1 100644 --- a/packages/bridge/vue3-bridge/package.json +++ b/packages/bridge/vue3-bridge/package.json @@ -22,7 +22,7 @@ ], "scripts": { "dev": "vite", - "build": "vite build && tsc -p tsconfig.json", + "build": "vite build && tsc -p tsconfig.json && node scripts/write-dts-entrypoints.mjs", "preview": "vite preview", "test": "rstest" }, diff --git a/packages/bridge/vue3-bridge/scripts/write-dts-entrypoints.mjs b/packages/bridge/vue3-bridge/scripts/write-dts-entrypoints.mjs new file mode 100644 index 00000000000..872aca26d2f --- /dev/null +++ b/packages/bridge/vue3-bridge/scripts/write-dts-entrypoints.mjs @@ -0,0 +1,10 @@ +import { readFile, writeFile } from 'node:fs/promises'; + +const file = 'dist/index.d.ts'; +const sideEffectImport = "import '@module-federation/data-fetch';"; + +let content = await readFile(file, 'utf8'); +if (!content.includes(sideEffectImport)) { + content = `${sideEffectImport}\n${content}`; +} +await writeFile(file, content); diff --git a/packages/bridge/vue3-bridge/src/index.ts b/packages/bridge/vue3-bridge/src/index.ts index 11e7124fc2c..e26ba7a4ac8 100644 --- a/packages/bridge/vue3-bridge/src/index.ts +++ b/packages/bridge/vue3-bridge/src/index.ts @@ -25,5 +25,6 @@ export type { CacheStatus, CacheStatsInfo, DataFetchParams, + NoSSRRemoteInfo, PrefetchOptions, } from '@module-federation/data-fetch'; diff --git a/packages/data-fetch/__tests__/global-types.spec.ts b/packages/data-fetch/__tests__/global-types.spec.ts index 6dbb1d469ac..f7d151c265d 100644 --- a/packages/data-fetch/__tests__/global-types.spec.ts +++ b/packages/data-fetch/__tests__/global-types.spec.ts @@ -1,4 +1,6 @@ import { test, expect } from '@rstest/core'; +import { flushDataFetch, initDataFetchMap } from '../src/index'; +import { DATA_FETCH_MAP_KEY } from '../src/constant'; import type { MF_DATA_FETCH_MAP } from '../src/types'; type Assert = T; @@ -20,5 +22,13 @@ type _GlobalsDeclared = Assert< test('declares data-fetch globals for TypeScript consumers', () => { void (0 as unknown as _GlobalsDeclared); - expect(true).toBe(true); + + delete (globalThis as { __MF_DATA_FETCH_MAP__?: MF_DATA_FETCH_MAP }) + .__MF_DATA_FETCH_MAP__; + initDataFetchMap(); + expect(globalThis.__MF_DATA_FETCH_MAP__).toEqual({}); + expect(DATA_FETCH_MAP_KEY).toBe('__MF_DATA_FETCH_MAP__'); + + flushDataFetch(); + expect(globalThis.__MF_DATA_FETCH_MAP__).toEqual({}); }); From a2ad670777bb32374a527a2e27950116ad3f2a25 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Mon, 10 Aug 2026 23:06:08 +0200 Subject: [PATCH 6/8] docs: document @module-federation/data-fetch and keep website docs in sync Co-authored-by: Cursor --- AGENTS.md | 2 ++ .../docs/en/guide/bridge/overview.mdx | 2 ++ .../en/guide/bridge/react/load-component.mdx | 6 ++++++ .../docs/en/guide/data/data-fetch-cache.mdx | 20 ++++++++++--------- .../docs/en/guide/data/data-fetch.mdx | 8 +++++++- .../docs/pt-BR/guide/bridge/overview.mdx | 2 ++ .../guide/bridge/react/load-component.mdx | 6 ++++++ .../pt-BR/guide/data/data-fetch-cache.mdx | 20 ++++++++++--------- .../docs/pt-BR/guide/data/data-fetch.mdx | 8 +++++++- .../docs/zh/guide/bridge/overview.mdx | 2 ++ .../zh/guide/bridge/react/load-component.mdx | 6 ++++++ .../docs/zh/guide/data/data-fetch-cache.mdx | 20 ++++++++++--------- .../docs/zh/guide/data/data-fetch.mdx | 8 +++++++- 13 files changed, 80 insertions(+), 30 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ec49d591537..d12febb23f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -257,6 +257,8 @@ Guidance: - validation that was run - any failed or skipped checks, with cause - If a PR is docs-only, say that explicitly and keep the body brief. +- When changing user-facing Module Federation APIs or packages (especially publishable packages under `packages/`), update matching documentation in `apps/website-new/docs/` in the same PR, or explain in the PR why docs are deferred. +- Website docs in `apps/website-new/docs/` are the public documentation source of truth and must stay current with published package surfaces. ## Webpack Internal Access diff --git a/apps/website-new/docs/en/guide/bridge/overview.mdx b/apps/website-new/docs/en/guide/bridge/overview.mdx index 613139d0c5b..2c938c78388 100644 --- a/apps/website-new/docs/en/guide/bridge/overview.mdx +++ b/apps/website-new/docs/en/guide/bridge/overview.mdx @@ -143,6 +143,8 @@ In addition to application-level modularity, Bridge also supports component-leve #### Core Features +Core data-fetch utilities (`cache`, `prefetch`, loader types, and related helpers) live in `@module-federation/data-fetch`. `@module-federation/bridge-react/data-fetch` re-exports them and adds React-specific lazy-loading APIs such as `lazyLoadComponentPlugin` and `createLazyComponent`. + - **Data Prefetching**: Support component-level data prefetching, ensuring data is ready when components load - **Error Boundaries**: Built-in error handling mechanisms, component loading failures don't affect the main application - **Loading States**: Rich loading state management with support for custom loading indicators diff --git a/apps/website-new/docs/en/guide/bridge/react/load-component.mdx b/apps/website-new/docs/en/guide/bridge/react/load-component.mdx index c2610253ec8..115b5bdf7cc 100644 --- a/apps/website-new/docs/en/guide/bridge/react/load-component.mdx +++ b/apps/website-new/docs/en/guide/bridge/react/load-component.mdx @@ -8,6 +8,12 @@ This chapter introduces how to use `createLazyComponent` to load remote React co - **Data prefetching**: use `prefetch` to start dependency requests before the component loads, avoiding request waterfalls where data is requested only after the component is ready - **SSR control**: precisely control whether remote components render on the server, helping avoid CSS flickering issues +:::info + +Component-level data-fetch utilities (`cache`, `prefetch`, `DataFetchParams`, and related types) are provided by `@module-federation/data-fetch`. `@module-federation/bridge-react/data-fetch` re-exports them for backward compatibility and adds React-specific APIs such as `lazyLoadComponentPlugin` and `createLazyComponent`. + +::: + ## Installation import { PackageManagerTabs } from '@theme'; diff --git a/apps/website-new/docs/en/guide/data/data-fetch-cache.mdx b/apps/website-new/docs/en/guide/data/data-fetch-cache.mdx index ec9030b9613..2af5c309d98 100644 --- a/apps/website-new/docs/en/guide/data/data-fetch-cache.mdx +++ b/apps/website-new/docs/en/guide/data/data-fetch-cache.mdx @@ -2,10 +2,12 @@ The `cache` function allows you to cache the results of data fetching or computations. It provides fine-grained control over data and is suitable for scenarios such as Client-Side Rendering (CSR) and Server-Side Rendering (SSR). +Examples import from `@module-federation/data-fetch`. Bridge packages (`@module-federation/bridge-react/data-fetch`, `@module-federation/bridge-vue3`) still re-export `cache` and related helpers for backward compatibility. + ## Basic Usage ```ts -import { cache } from '@module-federation/bridge-react/data-fetch'; +import { cache } from '@module-federation/data-fetch'; export type Data = { data: string; @@ -59,7 +61,7 @@ This function is only supported for use within a DataLoader. After each computation, the framework records the time the data was written to the cache. When the function is called again, it checks if the cache has expired based on the `maxAge` parameter. If it has, the `fn` function is re-executed; otherwise, the cached data is returned. ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; const getDashboardStats = cache( async () => { @@ -78,7 +80,7 @@ The `revalidate` parameter sets a time window for revalidating the cache after i In the following example, if `getDashboardStats` is called within the 2-minute non-expired window, it returns cached data. If the cache is expired (between 2 and 3 minutes), incoming requests will first receive the old data, and then a new request will be made in the background to update the cache. ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; const getDashboardStats = cache( async () => { @@ -96,7 +98,7 @@ const getDashboardStats = cache( The `tag` parameter is used to identify a cache with a label, which can be a string or an array of strings. This tag can be used to invalidate the cache, and multiple cache functions can share the same tag. ```ts -import { cache, revalidateTag } from '@module-federation/bridge-react/data-fetch'; +import { cache, revalidateTag } from '@module-federation/data-fetch'; const getDashboardStats = cache( async () => { @@ -124,7 +126,7 @@ revalidateTag('dashboard-stats'); // This will invalidate the caches for both ge The `getKey` parameter allows you to customize how cache keys are generated. For example, you might only need to rely on a subset of the function's parameters to differentiate caches. It is a function that receives the same arguments as the original function and returns a string as the cache key: ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; import { fetchUserData } from './api'; const getUser = cache( @@ -156,7 +158,7 @@ The `generateKey` function ensures that a consistent, unique key is generated ev ::: ```ts -import { cache, CacheTime, generateKey } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime, generateKey } from '@module-federation/data-fetch'; import { fetchUserData } from './api'; const getUser = cache( @@ -190,7 +192,7 @@ If you only want to derive the cache key from some arguments, use `getKey`. Use The example below demonstrates how `customKey` enables cache sharing across functions: ```ts -import { cache } from '@module-federation/bridge-react/data-fetch'; +import { cache } from '@module-federation/data-fetch'; import { fetchUserData } from './api'; // Different functions, but they can share a cache via customKey. @@ -246,7 +248,7 @@ const getUserD = cache( The `onCache` parameter allows you to track cache statistics, such as hit rates. It is a callback function that receives information about each cache operation, including its status, key, parameters, and result. You can return `false` from `onCache` to prevent a cache hit. ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; // Track cache statistics. const stats = { @@ -314,7 +316,7 @@ Considering that the content cached by the `cache` function is not expected to b You can specify the cache storage limit using the `configureCache` function: ```ts -import { configureCache, CacheSize } from '@module-federation/bridge-react/data-fetch'; +import { configureCache, CacheSize } from '@module-federation/data-fetch'; configureCache({ maxSize: CacheSize.MB * 10, // 10MB diff --git a/apps/website-new/docs/en/guide/data/data-fetch.mdx b/apps/website-new/docs/en/guide/data/data-fetch.mdx index ae71c139795..7f96ef081a7 100644 --- a/apps/website-new/docs/en/guide/data/data-fetch.mdx +++ b/apps/website-new/docs/en/guide/data/data-fetch.mdx @@ -20,6 +20,12 @@ The capability described in this document targets the former — "component-leve ## How to Use +:::info Package layout + +Core data-fetch APIs (`cache`, `prefetch`, types such as `DataFetchParams`, and related utilities) live in the framework-agnostic `@module-federation/data-fetch` package. `@module-federation/bridge-react/data-fetch` and `@module-federation/bridge-vue3` re-export these APIs for convenience. React's `createLazyComponent` remains the React UI adapter for loading component remotes (see [Lazy Load and Prefetch Components](/guide/bridge/react/load-component.html)). + +::: + Data fetching supports both SSR and CSR scenarios. The sections below cover producer and consumer separately: ### Producer @@ -49,7 +55,7 @@ Example file layout: The convention file must export a function named `fetchData`, which runs before the remote component renders: ```ts title="List.data.ts" -import type { DataFetchParams } from '@module-federation/bridge-react/data-fetch'; +import type { DataFetchParams } from '@module-federation/data-fetch'; export type Data = { data: string; }; diff --git a/apps/website-new/docs/pt-BR/guide/bridge/overview.mdx b/apps/website-new/docs/pt-BR/guide/bridge/overview.mdx index 896abd5c07d..cfae6c75caf 100644 --- a/apps/website-new/docs/pt-BR/guide/bridge/overview.mdx +++ b/apps/website-new/docs/pt-BR/guide/bridge/overview.mdx @@ -143,6 +143,8 @@ In addition para aplicação-level modularity, Bridge também suporta componente #### Core Features +Core data-fetch utilities (`cache`, `prefetch`, loader types, and related helpers) live in `@module-federation/data-fetch`. `@module-federation/bridge-react/data-fetch` re-exports them and adds React-specific lazy-loading APIs such as `lazyLoadComponentPlugin` and `createLazyComponent`. + - **Pré-busca de dadosing**: Suporte componente-level dados prefetching, ensuring dados é ready quando componentes carregue - **Erro Boundaries**: Built-em tratamento de erros mechanisms, componente carregamento falhas don't affect o principal aplicação - **Carregamento States**: Rich estado de carregamento management com suporte for custom carregamento indicators diff --git a/apps/website-new/docs/pt-BR/guide/bridge/react/load-component.mdx b/apps/website-new/docs/pt-BR/guide/bridge/react/load-component.mdx index 6bbcc925adf..15a8c3add15 100644 --- a/apps/website-new/docs/pt-BR/guide/bridge/react/load-component.mdx +++ b/apps/website-new/docs/pt-BR/guide/bridge/react/load-component.mdx @@ -8,6 +8,12 @@ Este chapter introduces como para usar `createLazyComponent` para carregar remot - **Dados prefetching**: use `prefetch` para começa dependência requests antes o componente carrega, avoiding request waterfalls onde dados é requested apenas depois o componente é ready - **SSR control**: precisely control whether remote componentes render no servidor, helping avoid CSS flickering issues +:::info + +Component-level data-fetch utilities (`cache`, `prefetch`, `DataFetchParams`, and related types) are provided by `@module-federation/data-fetch`. `@module-federation/bridge-react/data-fetch` re-exports them for backward compatibility and adds React-specific APIs such as `lazyLoadComponentPlugin` and `createLazyComponent`. + +::: + ## Instalação import { PackageManagerTabs } from '@theme'; diff --git a/apps/website-new/docs/pt-BR/guide/data/data-fetch-cache.mdx b/apps/website-new/docs/pt-BR/guide/data/data-fetch-cache.mdx index 4639aca57f9..d67023a7ef7 100644 --- a/apps/website-new/docs/pt-BR/guide/data/data-fetch-cache.mdx +++ b/apps/website-new/docs/pt-BR/guide/data/data-fetch-cache.mdx @@ -2,10 +2,12 @@ O `cache` função allows você para cache o results de dados fetching ou computations. It fornece fine-grained control over dados e é suitable for scenarios such as Cliente-Side Rendering (CSR) e Servidor-Side Rendering (SSR). +Examples import from `@module-federation/data-fetch`. Bridge packages (`@module-federation/bridge-react/data-fetch`, `@module-federation/bridge-vue3`) still re-export `cache` and related helpers for backward compatibility. + ## Básico Uso ```ts -import { cache } from '@module-federation/bridge-react/data-fetch'; +import { cache } from '@module-federation/data-fetch'; export type Data = { data: string; @@ -59,7 +61,7 @@ Este função é apenas compatível for use within uma DataLoader. Depois each computation, o framework records o time o dados was written para o cache. Quando o função é chamada again, it checks se o cache has expired based no `maxAge` parameter. Se it has, o `fn` função é re-executed; caso contrário, o cached dados é retornado. ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; const getDashboardStats = cache( async () => { @@ -78,7 +80,7 @@ O `revalidate` parameter sets uma time window for revalidating o cache depois it No seguintes exemplo, se `getDashboardStats` é chamada within o 2-minute non-expired window, ele retorna cached dados. Se o cache é expired (entre 2 e 3 minutes), incoming requests vai primeiro receive o old dados, e then uma nova request vai ser made no background para atualizar o cache. ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; const getDashboardStats = cache( async () => { @@ -96,7 +98,7 @@ const getDashboardStats = cache( O `tag` parameter é usado para identify uma cache com uma label, que pode ser uma string ou um array de strings. Este tag pode ser usado para invalidate o cache, e multiple cache funções pode compartilhe o mesmo tag. ```ts -import { cache, revalidateTag } from '@module-federation/bridge-react/data-fetch'; +import { cache, revalidateTag } from '@module-federation/data-fetch'; const getDashboardStats = cache( async () => { @@ -124,7 +126,7 @@ revalidateTag('dashboard-stats'); // This will invalidate the caches for both ge O `getKey` parameter allows você para customize como cache keys são generated. Por exemplo, você might apenas precisa rely on uma subset do função's parameters para differentiate caches. It é uma função esse receives o mesmo arguments as o original função e retorna uma string as o cache key: ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; import { fetchUserData } from './api'; const getUser = cache( @@ -156,7 +158,7 @@ O `generateKey` função ensures esse uma consistent, unique key é generated ev ::: ```ts -import { cache, CacheTime, generateKey } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime, generateKey } from '@module-federation/data-fetch'; import { fetchUserData } from './api'; const getUser = cache( @@ -190,7 +192,7 @@ Se você apenas want para derive o cache key de some arguments, use `getKey`. Us O exemplo below demonstrates como `customKey` enables cache compartilhamento across funções: ```ts -import { cache } from '@module-federation/bridge-react/data-fetch'; +import { cache } from '@module-federation/data-fetch'; import { fetchUserData } from './api'; // Different functions, but they can share a cache via customKey. @@ -246,7 +248,7 @@ const getUserD = cache( O `onCache` parameter allows você para track cache statistics, such as hit rates. It é uma callback função esse receives information about each cache operation, including its status, key, parameters, e result. Você pode retornar `false` de `onCache` para prevent uma cache hit. ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; // Track cache statistics. const stats = { @@ -314,7 +316,7 @@ Considering esse o content cached pelo `cache` função é não expected para se Você pode specify o cache storage limit usando o `configureCache` função: ```ts -import { configureCache, CacheSize } from '@module-federation/bridge-react/data-fetch'; +import { configureCache, CacheSize } from '@module-federation/data-fetch'; configureCache({ maxSize: CacheSize.MB * 10, // 10MB diff --git a/apps/website-new/docs/pt-BR/guide/data/data-fetch.mdx b/apps/website-new/docs/pt-BR/guide/data/data-fetch.mdx index 50d32ca6e6b..27ce319788e 100644 --- a/apps/website-new/docs/pt-BR/guide/data/data-fetch.mdx +++ b/apps/website-new/docs/pt-BR/guide/data/data-fetch.mdx @@ -20,6 +20,12 @@ O capability described em este document targets o former — "componente-level" ## How para Use +:::info Package layout + +Core data-fetch APIs (`cache`, `prefetch`, types such as `DataFetchParams`, and related utilities) live in the framework-agnostic `@module-federation/data-fetch` package. `@module-federation/bridge-react/data-fetch` and `@module-federation/bridge-vue3` re-export these APIs for convenience. React's `createLazyComponent` remains the React UI adapter for loading component remotes (see [Lazy Carregue e Pré-busca Componentes](/guide/bridge/react/load-component.html)). + +::: + Dados fetching suporta both SSR e CSR scenarios. O sections below cover producer e consumer separadamente: ### Producer @@ -49,7 +55,7 @@ Exemplo arquivo layout: O convention arquivo deve exporte uma função chamado `fetchData`, que runs antes o remote componente renders: ```ts title="List.data.ts" -import type { DataFetchParams } from '@module-federation/bridge-react/data-fetch'; +import type { DataFetchParams } from '@module-federation/data-fetch'; export type Data = { data: string; }; diff --git a/apps/website-new/docs/zh/guide/bridge/overview.mdx b/apps/website-new/docs/zh/guide/bridge/overview.mdx index 4f5263e3f68..b9196734327 100644 --- a/apps/website-new/docs/zh/guide/bridge/overview.mdx +++ b/apps/website-new/docs/zh/guide/bridge/overview.mdx @@ -145,6 +145,8 @@ Bridge 解决了现代前端开发中的几个关键挑战: #### 核心特性 +核心 data-fetch 工具(`cache`、`prefetch`、loader 类型及相关辅助函数)位于 `@module-federation/data-fetch`。`@module-federation/bridge-react/data-fetch` 会重新导出它们,并提供 `lazyLoadComponentPlugin`、`createLazyComponent` 等 React 懒加载 API。 + - **数据预取**:支持组件级别的数据预取,确保组件加载时数据已就绪 - **错误边界**:内置错误处理机制,组件加载失败不影响主应用 - **加载状态**:提供丰富的加载状态管理,支持自定义加载指示器 diff --git a/apps/website-new/docs/zh/guide/bridge/react/load-component.mdx b/apps/website-new/docs/zh/guide/bridge/react/load-component.mdx index e84e62565c8..7f24d9b7f5c 100644 --- a/apps/website-new/docs/zh/guide/bridge/react/load-component.mdx +++ b/apps/website-new/docs/zh/guide/bridge/react/load-component.mdx @@ -8,6 +8,12 @@ - **数据预取**:配合 `prefetch` 在组件加载前发起依赖请求,避免组件就绪后才串行请求数据带来的瀑布 - **SSR 控制**:可精细控制远程组件是否在服务端渲染,避免出现 CSS 闪烁问题 +:::info + +组件级 data-fetch 工具(`cache`、`prefetch`、`DataFetchParams` 及相关类型)由 `@module-federation/data-fetch` 提供。`@module-federation/bridge-react/data-fetch` 会重新导出它们以保持向后兼容,并提供 `lazyLoadComponentPlugin`、`createLazyComponent` 等 React 专用 API。 + +::: + ## 安装 import { PackageManagerTabs } from '@theme'; diff --git a/apps/website-new/docs/zh/guide/data/data-fetch-cache.mdx b/apps/website-new/docs/zh/guide/data/data-fetch-cache.mdx index 45c1cb2cd79..fb22dda1c0b 100644 --- a/apps/website-new/docs/zh/guide/data/data-fetch-cache.mdx +++ b/apps/website-new/docs/zh/guide/data/data-fetch-cache.mdx @@ -2,10 +2,12 @@ `cache` 函数可以让你缓存数据获取或计算的结果,它提供了更精细的数据粒度控制,并且适用于客户端渲染(CSR)、服务端渲染(SSR)等场景。 +下文示例从 `@module-federation/data-fetch` 导入。Bridge 包(`@module-federation/bridge-react/data-fetch`、`@module-federation/bridge-vue3`)仍会重新导出 `cache` 及相关工具以保持向后兼容。 + ## 基本用法 ```ts -import { cache } from '@module-federation/bridge-react/data-fetch'; +import { cache } from '@module-federation/data-fetch'; export type Data = { data: string; @@ -58,7 +60,7 @@ interface CacheOptions { 每次计算完成后,框架会记录写入缓存的时间,当再次调用该函数时,会根据 `maxAge` 参数判断缓存是否过期,如果过期,则重新执行 `fn` 函数,否则返回缓存的数据。 ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; const getDashboardStats = cache( async () => { @@ -78,7 +80,7 @@ const getDashboardStats = cache( 如以下示例,在缓存未过期的 2分钟内,如果调用 `getDashboardStats` 函数,会返回缓存的数据,如果缓存过期,2分到3分钟内,收到的请求会先返回旧数据,然后后台会重新请求数据,并更新缓存。 ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; const getDashboardStats = cache( async () => { @@ -96,7 +98,7 @@ const getDashboardStats = cache( `tag` 参数用于标识缓存的标签,可以传入一个字符串或字符串数组,可以基于这个标签使缓存失效,多个缓存函数可以使用一个标签。 ```ts -import { cache, revalidateTag } from '@module-federation/bridge-react/data-fetch'; +import { cache, revalidateTag } from '@module-federation/data-fetch'; const getDashboardStats = cache( async () => { @@ -124,7 +126,7 @@ revalidateTag('dashboard-stats'); // 会使 getDashboardStats 函数和 getCompl `getKey` 参数用于自定义缓存键的生成方式,例如你可能只需要依赖函数参数的一部分来区分缓存。它是一个函数,接收与原始函数相同的参数,返回一个字符串作为缓存键: ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; import { fetchUserData } from './api'; const getUser = cache( @@ -156,7 +158,7 @@ await getUser(456, { language: 'zh' }); // 不会命中缓存,会重新请求 ::: ```ts -import { cache, CacheTime, generateKey } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime, generateKey } from '@module-federation/data-fetch'; import { fetchUserData } from './api'; const getUser = cache( @@ -191,7 +193,7 @@ const getUser = cache( 下面的示例展示了 `customKey` 跨函数共享缓存的能力: ```ts -import { cache } from '@module-federation/bridge-react/data-fetch'; +import { cache } from '@module-federation/data-fetch'; import { fetchUserData } from './api'; // 不同的函数,但是通过 customKey 可以使它们共享一个缓存 @@ -247,7 +249,7 @@ const getUserD = cache( `onCache` 参数允许你跟踪缓存统计信息,例如命中率。这是一个回调函数,接收有关每次缓存操作的信息,包括状态、键、参数和结果。你可以在 onCache 返回 `false` 来阻止命中缓存。 ```ts -import { cache, CacheTime } from '@module-federation/bridge-react/data-fetch'; +import { cache, CacheTime } from '@module-federation/data-fetch'; // 跟踪缓存统计 const stats = { @@ -315,7 +317,7 @@ await getUser(2); // 缓存未命中 可以通过 `configureCache` 函数指定缓存的存储上限: ```ts -import { configureCache, CacheSize } from '@module-federation/bridge-react/data-fetch'; +import { configureCache, CacheSize } from '@module-federation/data-fetch'; configureCache({ maxSize: CacheSize.MB * 10, // 10MB diff --git a/apps/website-new/docs/zh/guide/data/data-fetch.mdx b/apps/website-new/docs/zh/guide/data/data-fetch.mdx index e17a94fa8b9..adce8cf327f 100644 --- a/apps/website-new/docs/zh/guide/data/data-fetch.mdx +++ b/apps/website-new/docs/zh/guide/data/data-fetch.mdx @@ -24,6 +24,12 @@ import Consumer from '@docs/zh/_components/data-fetch/consumer'; ## 如何使用 +:::info 包结构 + +核心 data-fetch API(`cache`、`prefetch`、`DataFetchParams` 等类型及相关工具)位于框架无关的 `@module-federation/data-fetch` 包中。`@module-federation/bridge-react/data-fetch` 与 `@module-federation/bridge-vue3` 为方便使用会重新导出这些 API。React 的 `createLazyComponent` 仍是用于加载组件远程模块的 React UI 适配器(见[懒加载与预取组件](/guide/bridge/react/load-component.html))。 + +::: + 数据获取能力同时支持 SSR 和 CSR 两种场景,下面分为生产者和消费者两部分来介绍: ### 生产者 @@ -50,7 +56,7 @@ import Consumer from '@docs/zh/_components/data-fetch/consumer'; 约定文件中需要导出名为 `fetchData` 的函数,该函数将会在远程组件渲染前执行: ```ts title="List.data.ts" -import type { DataFetchParams } from '@module-federation/bridge-react/data-fetch'; +import type { DataFetchParams } from '@module-federation/data-fetch'; export type Data = { data: string; }; From a6778e10969b3d1806a4db0ccf230550764e049e Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Mon, 10 Aug 2026 23:25:06 +0200 Subject: [PATCH 7/8] docs: localize pt-BR data-fetch notes and add install guidance Co-authored-by: Cursor --- AGENTS.md | 1 + .../docs/en/guide/data/data-fetch-cache.mdx | 7 +++++++ apps/website-new/docs/en/guide/data/data-fetch.mdx | 9 +++++++++ .../docs/pt-BR/guide/bridge/overview.mdx | 2 +- .../pt-BR/guide/bridge/react/load-component.mdx | 4 ++-- .../docs/pt-BR/guide/data/data-fetch-cache.mdx | 9 ++++++++- .../docs/pt-BR/guide/data/data-fetch.mdx | 13 +++++++++++-- .../docs/zh/guide/data/data-fetch-cache.mdx | 7 +++++++ apps/website-new/docs/zh/guide/data/data-fetch.mdx | 9 +++++++++ 9 files changed, 55 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d12febb23f6..5bfe6447063 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -258,6 +258,7 @@ Guidance: - any failed or skipped checks, with cause - If a PR is docs-only, say that explicitly and keep the body brief. - When changing user-facing Module Federation APIs or packages (especially publishable packages under `packages/`), update matching documentation in `apps/website-new/docs/` in the same PR, or explain in the PR why docs are deferred. +- When updating website docs, keep all published locales for that page (currently `en`, `zh`, and `pt-BR` under `apps/website-new/docs/`) consistent for the same conceptual change; do not leave one locale with untranslated English paste-ins. - Website docs in `apps/website-new/docs/` are the public documentation source of truth and must stay current with published package surfaces. ## Webpack Internal Access diff --git a/apps/website-new/docs/en/guide/data/data-fetch-cache.mdx b/apps/website-new/docs/en/guide/data/data-fetch-cache.mdx index 2af5c309d98..38186ffe546 100644 --- a/apps/website-new/docs/en/guide/data/data-fetch-cache.mdx +++ b/apps/website-new/docs/en/guide/data/data-fetch-cache.mdx @@ -4,6 +4,13 @@ The `cache` function allows you to cache the results of data fetching or computa Examples import from `@module-federation/data-fetch`. Bridge packages (`@module-federation/bridge-react/data-fetch`, `@module-federation/bridge-vue3`) still re-export `cache` and related helpers for backward compatibility. +```bash +npm install @module-federation/data-fetch +# or: pnpm add @module-federation/data-fetch +``` + +If you only import from bridge packages, the re-exports still work without a direct dependency on `@module-federation/data-fetch`. + ## Basic Usage ```ts diff --git a/apps/website-new/docs/en/guide/data/data-fetch.mdx b/apps/website-new/docs/en/guide/data/data-fetch.mdx index 7f96ef081a7..01c8ac15890 100644 --- a/apps/website-new/docs/en/guide/data/data-fetch.mdx +++ b/apps/website-new/docs/en/guide/data/data-fetch.mdx @@ -24,6 +24,15 @@ The capability described in this document targets the former — "component-leve Core data-fetch APIs (`cache`, `prefetch`, types such as `DataFetchParams`, and related utilities) live in the framework-agnostic `@module-federation/data-fetch` package. `@module-federation/bridge-react/data-fetch` and `@module-federation/bridge-vue3` re-export these APIs for convenience. React's `createLazyComponent` remains the React UI adapter for loading component remotes (see [Lazy Load and Prefetch Components](/guide/bridge/react/load-component.html)). +Install `@module-federation/data-fetch` when importing from it directly: + +```bash +npm install @module-federation/data-fetch +# or: pnpm add @module-federation/data-fetch +``` + +If you only import from bridge packages (`@module-federation/bridge-react/data-fetch`, `@module-federation/bridge-vue3`), the re-exports still work without a direct dependency on `@module-federation/data-fetch`. + ::: Data fetching supports both SSR and CSR scenarios. The sections below cover producer and consumer separately: diff --git a/apps/website-new/docs/pt-BR/guide/bridge/overview.mdx b/apps/website-new/docs/pt-BR/guide/bridge/overview.mdx index cfae6c75caf..6651f182943 100644 --- a/apps/website-new/docs/pt-BR/guide/bridge/overview.mdx +++ b/apps/website-new/docs/pt-BR/guide/bridge/overview.mdx @@ -143,7 +143,7 @@ In addition para aplicação-level modularity, Bridge também suporta componente #### Core Features -Core data-fetch utilities (`cache`, `prefetch`, loader types, and related helpers) live in `@module-federation/data-fetch`. `@module-federation/bridge-react/data-fetch` re-exports them and adds React-specific lazy-loading APIs such as `lazyLoadComponentPlugin` and `createLazyComponent`. +Os utilitários principais de data-fetch (`cache`, `prefetch`, tipos de loader e utilitários relacionados) ficam em `@module-federation/data-fetch`. `@module-federation/bridge-react/data-fetch` os reexporta e adiciona APIs de carregamento lazy específicas do React, como `lazyLoadComponentPlugin` e `createLazyComponent`. - **Pré-busca de dadosing**: Suporte componente-level dados prefetching, ensuring dados é ready quando componentes carregue - **Erro Boundaries**: Built-em tratamento de erros mechanisms, componente carregamento falhas don't affect o principal aplicação diff --git a/apps/website-new/docs/pt-BR/guide/bridge/react/load-component.mdx b/apps/website-new/docs/pt-BR/guide/bridge/react/load-component.mdx index 15a8c3add15..c331fb31ebf 100644 --- a/apps/website-new/docs/pt-BR/guide/bridge/react/load-component.mdx +++ b/apps/website-new/docs/pt-BR/guide/bridge/react/load-component.mdx @@ -1,4 +1,4 @@ -# Lazy Carregue e Pré-busca Componentes +# Carregamento lazy e prefetch de componentes Este chapter introduces como para usar `createLazyComponent` para carregar remote React componentes on demand em uma aplicação host. @@ -10,7 +10,7 @@ Este chapter introduces como para usar `createLazyComponent` para carregar remot :::info -Component-level data-fetch utilities (`cache`, `prefetch`, `DataFetchParams`, and related types) are provided by `@module-federation/data-fetch`. `@module-federation/bridge-react/data-fetch` re-exports them for backward compatibility and adds React-specific APIs such as `lazyLoadComponentPlugin` and `createLazyComponent`. +Os utilitários de data-fetch em nível de componente (`cache`, `prefetch`, `DataFetchParams` e tipos relacionados) são fornecidos por `@module-federation/data-fetch`. `@module-federation/bridge-react/data-fetch` os reexporta para compatibilidade com versões anteriores e adiciona APIs específicas do React, como `lazyLoadComponentPlugin` e `createLazyComponent`. ::: diff --git a/apps/website-new/docs/pt-BR/guide/data/data-fetch-cache.mdx b/apps/website-new/docs/pt-BR/guide/data/data-fetch-cache.mdx index d67023a7ef7..0a1a88265c4 100644 --- a/apps/website-new/docs/pt-BR/guide/data/data-fetch-cache.mdx +++ b/apps/website-new/docs/pt-BR/guide/data/data-fetch-cache.mdx @@ -2,7 +2,14 @@ O `cache` função allows você para cache o results de dados fetching ou computations. It fornece fine-grained control over dados e é suitable for scenarios such as Cliente-Side Rendering (CSR) e Servidor-Side Rendering (SSR). -Examples import from `@module-federation/data-fetch`. Bridge packages (`@module-federation/bridge-react/data-fetch`, `@module-federation/bridge-vue3`) still re-export `cache` and related helpers for backward compatibility. +Os exemplos abaixo importam de `@module-federation/data-fetch`. Os pacotes bridge (`@module-federation/bridge-react/data-fetch`, `@module-federation/bridge-vue3`) ainda reexportam `cache` e utilitários relacionados para compatibilidade com versões anteriores. + +```bash +npm install @module-federation/data-fetch +# ou: pnpm add @module-federation/data-fetch +``` + +Se você importar apenas dos pacotes bridge, as reexportações continuam funcionando sem dependência direta em `@module-federation/data-fetch`. ## Básico Uso diff --git a/apps/website-new/docs/pt-BR/guide/data/data-fetch.mdx b/apps/website-new/docs/pt-BR/guide/data/data-fetch.mdx index 27ce319788e..b1e8d6a95c4 100644 --- a/apps/website-new/docs/pt-BR/guide/data/data-fetch.mdx +++ b/apps/website-new/docs/pt-BR/guide/data/data-fetch.mdx @@ -20,9 +20,18 @@ O capability described em este document targets o former — "componente-level" ## How para Use -:::info Package layout +:::info Estrutura dos pacotes -Core data-fetch APIs (`cache`, `prefetch`, types such as `DataFetchParams`, and related utilities) live in the framework-agnostic `@module-federation/data-fetch` package. `@module-federation/bridge-react/data-fetch` and `@module-federation/bridge-vue3` re-export these APIs for convenience. React's `createLazyComponent` remains the React UI adapter for loading component remotes (see [Lazy Carregue e Pré-busca Componentes](/guide/bridge/react/load-component.html)). +As APIs principais de data-fetch (`cache`, `prefetch`, tipos como `DataFetchParams` e utilitários relacionados) ficam no pacote agnóstico de framework `@module-federation/data-fetch`. `@module-federation/bridge-react/data-fetch` e `@module-federation/bridge-vue3` reexportam essas APIs por conveniência. O `createLazyComponent` do React continua sendo o adaptador de UI React para carregar componentes remotos (veja [Carregamento lazy e prefetch de componentes](/guide/bridge/react/load-component.html)). + +Instale `@module-federation/data-fetch` ao importar diretamente dele: + +```bash +npm install @module-federation/data-fetch +# ou: pnpm add @module-federation/data-fetch +``` + +Se você importar apenas dos pacotes bridge (`@module-federation/bridge-react/data-fetch`, `@module-federation/bridge-vue3`), as reexportações continuam funcionando sem dependência direta em `@module-federation/data-fetch`. ::: diff --git a/apps/website-new/docs/zh/guide/data/data-fetch-cache.mdx b/apps/website-new/docs/zh/guide/data/data-fetch-cache.mdx index fb22dda1c0b..25943ecf445 100644 --- a/apps/website-new/docs/zh/guide/data/data-fetch-cache.mdx +++ b/apps/website-new/docs/zh/guide/data/data-fetch-cache.mdx @@ -4,6 +4,13 @@ 下文示例从 `@module-federation/data-fetch` 导入。Bridge 包(`@module-federation/bridge-react/data-fetch`、`@module-federation/bridge-vue3`)仍会重新导出 `cache` 及相关工具以保持向后兼容。 +```bash +npm install @module-federation/data-fetch +# 或:pnpm add @module-federation/data-fetch +``` + +若仅从 bridge 包导入,仍可依赖其重新导出,无需直接安装 `@module-federation/data-fetch`。 + ## 基本用法 ```ts diff --git a/apps/website-new/docs/zh/guide/data/data-fetch.mdx b/apps/website-new/docs/zh/guide/data/data-fetch.mdx index adce8cf327f..3711d4c9101 100644 --- a/apps/website-new/docs/zh/guide/data/data-fetch.mdx +++ b/apps/website-new/docs/zh/guide/data/data-fetch.mdx @@ -28,6 +28,15 @@ import Consumer from '@docs/zh/_components/data-fetch/consumer'; 核心 data-fetch API(`cache`、`prefetch`、`DataFetchParams` 等类型及相关工具)位于框架无关的 `@module-federation/data-fetch` 包中。`@module-federation/bridge-react/data-fetch` 与 `@module-federation/bridge-vue3` 为方便使用会重新导出这些 API。React 的 `createLazyComponent` 仍是用于加载组件远程模块的 React UI 适配器(见[懒加载与预取组件](/guide/bridge/react/load-component.html))。 +直接从 `@module-federation/data-fetch` 导入时,请先安装: + +```bash +npm install @module-federation/data-fetch +# 或:pnpm add @module-federation/data-fetch +``` + +若仅从 bridge 包(`@module-federation/bridge-react/data-fetch`、`@module-federation/bridge-vue3`)导入,仍可依赖其重新导出,无需直接安装 `@module-federation/data-fetch`。 + ::: 数据获取能力同时支持 SSR 和 CSR 两种场景,下面分为生产者和消费者两部分来介绍: From ab092d0de099a776929d42930219b6d9fe5ef8e9 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Fri, 25 Sep 2026 20:25:14 +0200 Subject: [PATCH 8/8] fix(data-fetch): reconcile extraction with latest main --- .../__tests__/data-fetch-utils.spec.ts | 7 +- packages/data-fetch/package.json | 6 +- packages/data-fetch/src/utils.ts | 9 + pnpm-lock.yaml | 857 +++++++++--------- 4 files changed, 461 insertions(+), 418 deletions(-) diff --git a/packages/bridge/bridge-react/__tests__/data-fetch-utils.spec.ts b/packages/bridge/bridge-react/__tests__/data-fetch-utils.spec.ts index 5bed1a17306..c8ae316d93c 100644 --- a/packages/bridge/bridge-react/__tests__/data-fetch-utils.spec.ts +++ b/packages/bridge/bridge-react/__tests__/data-fetch-utils.spec.ts @@ -1,5 +1,8 @@ -import { MF_DATA_FETCH_STATUS, MF_DATA_FETCH_TYPE } from '../src/lazy/constant'; -import { cache, clearStore } from '../src/lazy/data-fetch/cache'; +import { cache, clearStore } from '@module-federation/data-fetch'; +import { + MF_DATA_FETCH_STATUS, + MF_DATA_FETCH_TYPE, +} from '../../../data-fetch/src/constant'; import type { DataFetch, MF_DATA_FETCH_MAP } from '../src/lazy/types'; import { fetchData, resetDataFetchResult } from '../src/lazy/utils'; diff --git a/packages/data-fetch/package.json b/packages/data-fetch/package.json index fe731732390..09b673a676d 100644 --- a/packages/data-fetch/package.json +++ b/packages/data-fetch/package.json @@ -72,22 +72,18 @@ "test": "rstest" }, "dependencies": { + "@module-federation/runtime": "workspace:*", "@module-federation/sdk": "workspace:*" }, "peerDependencies": { - "@module-federation/runtime": "workspace:*", "hono": ">=3" }, "peerDependenciesMeta": { - "@module-federation/runtime": { - "optional": true - }, "hono": { "optional": true } }, "devDependencies": { - "@module-federation/runtime": "workspace:*", "hono": "4.12.26", "typescript": "7.0.2", "vite": "^5.4.21" diff --git a/packages/data-fetch/src/utils.ts b/packages/data-fetch/src/utils.ts index 19f2794efa5..d5778f430ed 100644 --- a/packages/data-fetch/src/utils.ts +++ b/packages/data-fetch/src/utils.ts @@ -110,6 +110,15 @@ export function setDataFetchItemLoadedStatus(id: string) { dataFetchItem[2] = MF_DATA_FETCH_STATUS.LOADED; } +export function resetDataFetchResult(id: string) { + const dataFetchItem = getDataFetchItem(id); + if (!dataFetchItem) { + return; + } + dataFetchItem[1] = undefined; + dataFetchItem[2] = MF_DATA_FETCH_STATUS.AWAIT; +} + export const wrapDataFetchId = (id?: string) => { return `${WRAP_DATA_FETCH_ID_IDENTIFIER}${id}${WRAP_DATA_FETCH_ID_IDENTIFIER}`; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0906df94c59..a2b2c8be753 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3039,6 +3039,9 @@ importers: packages/bridge/bridge-react: dependencies: + '@module-federation/data-fetch': + specifier: workspace:* + version: link:../../data-fetch '@module-federation/sdk': specifier: workspace:* version: link:../../sdk @@ -3140,6 +3143,9 @@ importers: '@module-federation/bridge-shared': specifier: workspace:* version: link:../bridge-shared + '@module-federation/data-fetch': + specifier: workspace:* + version: link:../../data-fetch '@module-federation/runtime': specifier: workspace:* version: link:../../runtime @@ -3340,6 +3346,25 @@ importers: specifier: ^0.2.1 version: 0.2.1(@rsbuild/core@1.7.3) + packages/data-fetch: + dependencies: + '@module-federation/runtime': + specifier: workspace:* + version: link:../runtime + '@module-federation/sdk': + specifier: workspace:* + version: link:../sdk + devDependencies: + hono: + specifier: 4.12.26 + version: 4.12.26 + typescript: + specifier: 7.0.2 + version: 7.0.2 + vite: + specifier: ^5.4.21 + version: 5.4.21(@types/node@26.6.2)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0) + packages/dts-plugin: dependencies: '@module-federation/error-codes': @@ -4275,7 +4300,7 @@ importers: version: 22.5.4(@babel/traverse@7.29.8)(@swc-node/register@1.10.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(@swc/types@0.1.27)(typescript@6.0.3))(@swc/core@1.15.41(@swc/helpers@0.5.23))(@swc/helpers@0.5.23)(esbuild@0.25.5)(nx@22.5.4(@swc-node/register@1.10.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(@swc/types@0.1.27)(typescript@6.0.3))(@swc/core@1.15.41(@swc/helpers@0.5.23)))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@7.0.2)(vue-tsc@2.2.12(typescript@7.0.2))(webpack-cli@5.1.4) '@rsbuild/core': specifier: 2.1.4 - version: 2.1.4(@module-federation/runtime-tools@2.2.2(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.49.0) + version: 2.1.4(@module-federation/runtime-tools@0.21.6)(core-js@3.49.0) '@storybook/core': specifier: ^8.4.6 version: 8.6.14(prettier@3.8.1)(storybook@8.6.17(prettier@3.8.1)) @@ -4467,7 +4492,7 @@ importers: version: 2.6.0 tailwindcss-animate: specifier: 1.0.7 - version: 1.0.7(tailwindcss@3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@22.19.15)(typescript@6.0.3))) + version: 1.0.7(tailwindcss@3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@22.19.15)(typescript@6.0.3))) vaul: specifier: 1.1.2 version: 1.1.2(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -4489,7 +4514,7 @@ importers: version: 2.1.4(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.4(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0))(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.17)) + version: 2.1.0(@rsbuild/core@2.1.4(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0))(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.23)) '@rslib/core': specifier: ^0.23.2 version: 0.23.2(@microsoft/api-extractor@7.57.7(@types/node@22.19.15))(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0)(typescript@6.0.3) @@ -4531,7 +4556,7 @@ importers: version: 8.5.24 tailwindcss: specifier: v3.4.16 - version: 3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@22.19.15)(typescript@6.0.3)) + version: 3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@22.19.15)(typescript@6.0.3)) typescript: specifier: ^6.0.3 version: 6.0.3 @@ -4592,7 +4617,7 @@ importers: version: 13.1.3 ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@20.19.5)(typescript@7.0.2) + version: 10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@20.19.5)(typescript@7.0.2) typescript: specifier: 7.0.2 version: 7.0.2 @@ -20262,6 +20287,10 @@ packages: resolution: {integrity: sha512-5IAMJOXfpA5nT+K0MNjClchzz0IhBHs2Szl7WFAhrFOsbtQsYmNynFyJRg/a3IPsmCfxcrf8txUGiNShXpK5Rg==} engines: {node: '>=16.0.0'} + hono@4.12.26: + resolution: {integrity: sha512-uyZtpnYxM9CmQ7QsQknM4zN8EftNqhON1qYeIKM0Se67CCEe2c44xyGURwB0axX2fBDu1dqHrHAc1hmNT8ITkw==} + engines: {node: '>=16.9.0'} + hono@4.13.5: resolution: {integrity: sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==} engines: {node: '>=16.9.0'} @@ -29293,8 +29322,8 @@ snapshots: '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 @@ -29318,7 +29347,7 @@ snapshots: '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.8 '@babel/helper-annotate-as-pure@7.29.7': dependencies: @@ -29434,14 +29463,14 @@ snapshots: '@babel/helper-module-imports@7.28.6': dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.0 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.28.6(supports-color@5.5.0)': dependencies: '@babel/traverse': 7.29.7(supports-color@5.5.0) - '@babel/types': 7.29.0 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -29608,7 +29637,7 @@ snapshots: '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/parser@7.29.9': dependencies: @@ -31127,11 +31156,11 @@ snapshots: '@babel/traverse@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.9 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -31139,11 +31168,11 @@ snapshots: '@babel/traverse@7.29.7(supports-color@5.5.0)': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.9 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -31666,9 +31695,9 @@ snapshots: dependencies: postcss-selector-parser: 6.1.2 - '@csstools/utilities@1.0.0(postcss@8.5.23)': + '@csstools/utilities@1.0.0(postcss@8.5.24)': dependencies: - postcss: 8.5.23 + postcss: 8.5.24 '@csstools/utilities@1.0.0(postcss@8.5.26)': dependencies: @@ -34269,20 +34298,20 @@ snapshots: '@rsbuild/plugin-typed-css-modules': 1.2.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0)) '@swc/core': 1.15.41(@swc/helpers@0.5.17) '@swc/helpers': 0.5.17 - autoprefixer: 10.4.27(postcss@8.5.23) + autoprefixer: 10.4.27(postcss@8.5.24) browserslist: 4.28.2 core-js: 3.49.0 - cssnano: 6.1.2(postcss@8.5.23) + cssnano: 6.1.2(postcss@8.5.24) html-minifier-terser: 7.2.0 lodash: 4.18.1 - postcss: 8.5.23 - postcss-custom-properties: 13.3.12(postcss@8.5.23) - postcss-flexbugs-fixes: 5.0.2(postcss@8.5.23) - postcss-font-variant: 5.0.0(postcss@8.5.23) - postcss-initial: 4.0.1(postcss@8.5.23) - postcss-media-minmax: 5.0.0(postcss@8.5.23) - postcss-nesting: 12.1.5(postcss@8.5.23) - postcss-page-break: 3.0.4(postcss@8.5.23) + postcss: 8.5.24 + postcss-custom-properties: 13.3.12(postcss@8.5.24) + postcss-flexbugs-fixes: 5.0.2(postcss@8.5.24) + postcss-font-variant: 5.0.0(postcss@8.5.24) + postcss-initial: 4.0.1(postcss@8.5.24) + postcss-media-minmax: 5.0.0(postcss@8.5.24) + postcss-nesting: 12.1.5(postcss@8.5.24) + postcss-page-break: 3.0.4(postcss@8.5.24) rsbuild-plugin-rsc: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.0.6(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.17))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) rspack-manifest-plugin: 5.2.2(@rspack/core@2.0.6(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.17)) ts-deepmerge: 7.0.3 @@ -34321,20 +34350,20 @@ snapshots: '@rsbuild/plugin-typed-css-modules': 1.2.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0)) '@swc/core': 1.15.41(@swc/helpers@0.5.17) '@swc/helpers': 0.5.17 - autoprefixer: 10.4.27(postcss@8.5.23) + autoprefixer: 10.4.27(postcss@8.5.24) browserslist: 4.28.2 core-js: 3.49.0 - cssnano: 6.1.2(postcss@8.5.23) + cssnano: 6.1.2(postcss@8.5.24) html-minifier-terser: 7.2.0 lodash: 4.18.1 - postcss: 8.5.23 - postcss-custom-properties: 13.3.12(postcss@8.5.23) - postcss-flexbugs-fixes: 5.0.2(postcss@8.5.23) - postcss-font-variant: 5.0.0(postcss@8.5.23) - postcss-initial: 4.0.1(postcss@8.5.23) - postcss-media-minmax: 5.0.0(postcss@8.5.23) - postcss-nesting: 12.1.5(postcss@8.5.23) - postcss-page-break: 3.0.4(postcss@8.5.23) + postcss: 8.5.24 + postcss-custom-properties: 13.3.12(postcss@8.5.24) + postcss-flexbugs-fixes: 5.0.2(postcss@8.5.24) + postcss-font-variant: 5.0.0(postcss@8.5.24) + postcss-initial: 4.0.1(postcss@8.5.24) + postcss-media-minmax: 5.0.0(postcss@8.5.24) + postcss-nesting: 12.1.5(postcss@8.5.24) + postcss-page-break: 3.0.4(postcss@8.5.24) rsbuild-plugin-rsc: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.23))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) rspack-manifest-plugin: 5.2.2(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.23)) ts-deepmerge: 7.0.3 @@ -35378,7 +35407,7 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/preset-react': 7.28.5(@babel/core@7.29.7) - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@modern-js/babel-preset': 2.70.5(@rsbuild/core@1.7.3) '@modern-js/flight-server-transform-plugin': 2.70.5 '@modern-js/utils': 2.70.5 @@ -35403,13 +35432,13 @@ snapshots: '@rsbuild/webpack': 1.6.1(@rsbuild/core@1.7.3)(@rspack/core@1.7.9(@swc/helpers@0.5.17))(@swc/core@1.15.8(@swc/helpers@0.5.17))(esbuild@0.25.5)(webpack-cli@5.1.4) '@swc/core': 1.15.8(@swc/helpers@0.5.17) '@swc/helpers': 0.5.17 - autoprefixer: 10.4.23(postcss@8.5.23) + autoprefixer: 10.4.23(postcss@8.5.24) babel-loader: 9.2.1(@babel/core@7.29.7)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.17))(esbuild@0.25.5)(webpack-cli@5.1.4)) babel-plugin-import: 1.13.8 babel-plugin-styled-components: 1.13.3(styled-components@6.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)) babel-plugin-transform-react-remove-prop-types: 0.4.24 browserslist: 4.24.4 - cssnano: 6.1.2(postcss@8.5.23) + cssnano: 6.1.2(postcss@8.5.24) es-module-lexer: 1.5.3 glob: 9.3.5 html-minifier-terser: 7.2.0 @@ -35418,14 +35447,14 @@ snapshots: lodash: 4.18.1 magic-string: 0.30.21 picocolors: 1.1.1 - postcss: 8.5.23 - postcss-custom-properties: 13.3.12(postcss@8.5.23) - postcss-flexbugs-fixes: 5.0.2(postcss@8.5.23) - postcss-font-variant: 5.0.0(postcss@8.5.23) - postcss-initial: 4.0.1(postcss@8.5.23) - postcss-media-minmax: 5.0.0(postcss@8.5.23) - postcss-nesting: 12.1.5(postcss@8.5.23) - postcss-page-break: 3.0.4(postcss@8.5.23) + postcss: 8.5.24 + postcss-custom-properties: 13.3.12(postcss@8.5.24) + postcss-flexbugs-fixes: 5.0.2(postcss@8.5.24) + postcss-font-variant: 5.0.0(postcss@8.5.24) + postcss-initial: 4.0.1(postcss@8.5.24) + postcss-media-minmax: 5.0.0(postcss@8.5.24) + postcss-nesting: 12.1.5(postcss@8.5.24) + postcss-page-break: 3.0.4(postcss@8.5.24) react-refresh: 0.14.2 rspack-manifest-plugin: 5.0.3(@rspack/core@1.7.9(@swc/helpers@0.5.17)) terser-webpack-plugin: 5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.17))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.17))(esbuild@0.25.5)(webpack-cli@5.1.4)) @@ -35483,13 +35512,13 @@ snapshots: '@rsbuild/webpack': 1.6.1(@rsbuild/core@1.7.3)(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.8(@swc/helpers@0.5.17))(esbuild@0.18.20)(webpack-cli@5.1.4) '@swc/core': 1.15.8(@swc/helpers@0.5.17) '@swc/helpers': 0.5.17 - autoprefixer: 10.4.23(postcss@8.5.23) + autoprefixer: 10.4.23(postcss@8.5.24) babel-loader: 9.2.1(@babel/core@7.29.0)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.19))(esbuild@0.18.20)(webpack-cli@5.1.4)) babel-plugin-import: 1.13.8 babel-plugin-styled-components: 1.13.3(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) babel-plugin-transform-react-remove-prop-types: 0.4.24 browserslist: 4.24.4 - cssnano: 6.1.2(postcss@8.5.23) + cssnano: 6.1.2(postcss@8.5.24) es-module-lexer: 1.5.3 glob: 9.3.5 html-minifier-terser: 7.2.0 @@ -35498,14 +35527,14 @@ snapshots: lodash: 4.18.1 magic-string: 0.30.21 picocolors: 1.1.1 - postcss: 8.5.23 - postcss-custom-properties: 13.3.12(postcss@8.5.23) - postcss-flexbugs-fixes: 5.0.2(postcss@8.5.23) - postcss-font-variant: 5.0.0(postcss@8.5.23) - postcss-initial: 4.0.1(postcss@8.5.23) - postcss-media-minmax: 5.0.0(postcss@8.5.23) - postcss-nesting: 12.1.5(postcss@8.5.23) - postcss-page-break: 3.0.4(postcss@8.5.23) + postcss: 8.5.24 + postcss-custom-properties: 13.3.12(postcss@8.5.24) + postcss-flexbugs-fixes: 5.0.2(postcss@8.5.24) + postcss-font-variant: 5.0.0(postcss@8.5.24) + postcss-initial: 4.0.1(postcss@8.5.24) + postcss-media-minmax: 5.0.0(postcss@8.5.24) + postcss-nesting: 12.1.5(postcss@8.5.24) + postcss-page-break: 3.0.4(postcss@8.5.24) react-refresh: 0.14.2 rspack-manifest-plugin: 5.0.3(@rspack/core@1.7.9(@swc/helpers@0.5.19)) terser-webpack-plugin: 5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.17))(esbuild@0.18.20)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.19))(esbuild@0.18.20)(webpack-cli@5.1.4)) @@ -35563,13 +35592,13 @@ snapshots: '@rsbuild/webpack': 1.6.1(@rsbuild/core@1.7.3)(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.8(@swc/helpers@0.5.17))(esbuild@0.25.5)(webpack-cli@5.1.4) '@swc/core': 1.15.8(@swc/helpers@0.5.17) '@swc/helpers': 0.5.17 - autoprefixer: 10.4.23(postcss@8.5.23) + autoprefixer: 10.4.23(postcss@8.5.24) babel-loader: 9.2.1(@babel/core@7.29.0)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.19))(esbuild@0.18.20)(webpack-cli@5.1.4)) babel-plugin-import: 1.13.8 babel-plugin-styled-components: 1.13.3(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) babel-plugin-transform-react-remove-prop-types: 0.4.24 browserslist: 4.24.4 - cssnano: 6.1.2(postcss@8.5.23) + cssnano: 6.1.2(postcss@8.5.24) es-module-lexer: 1.5.3 glob: 9.3.5 html-minifier-terser: 7.2.0 @@ -35578,14 +35607,14 @@ snapshots: lodash: 4.18.1 magic-string: 0.30.21 picocolors: 1.1.1 - postcss: 8.5.23 - postcss-custom-properties: 13.3.12(postcss@8.5.23) - postcss-flexbugs-fixes: 5.0.2(postcss@8.5.23) - postcss-font-variant: 5.0.0(postcss@8.5.23) - postcss-initial: 4.0.1(postcss@8.5.23) - postcss-media-minmax: 5.0.0(postcss@8.5.23) - postcss-nesting: 12.1.5(postcss@8.5.23) - postcss-page-break: 3.0.4(postcss@8.5.23) + postcss: 8.5.24 + postcss-custom-properties: 13.3.12(postcss@8.5.24) + postcss-flexbugs-fixes: 5.0.2(postcss@8.5.24) + postcss-font-variant: 5.0.0(postcss@8.5.24) + postcss-initial: 4.0.1(postcss@8.5.24) + postcss-media-minmax: 5.0.0(postcss@8.5.24) + postcss-nesting: 12.1.5(postcss@8.5.24) + postcss-page-break: 3.0.4(postcss@8.5.24) react-refresh: 0.14.2 rspack-manifest-plugin: 5.0.3(@rspack/core@1.7.9(@swc/helpers@0.5.19)) terser-webpack-plugin: 5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.17))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.19))(esbuild@0.18.20)(webpack-cli@5.1.4)) @@ -36671,20 +36700,20 @@ snapshots: dependencies: '@nx/devkit': 22.5.4(nx@22.5.4(@swc-node/register@1.10.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(@swc/types@0.1.27)(typescript@6.0.3))(@swc/core@1.15.41(@swc/helpers@0.5.23))) '@nx/js': 22.5.4(@babel/traverse@7.29.8)(@swc-node/register@1.10.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(@swc/types@0.1.27)(typescript@6.0.3))(@swc/core@1.15.41(@swc/helpers@0.5.23))(nx@22.5.4(@swc-node/register@1.10.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(@swc/types@0.1.27)(typescript@6.0.3))(@swc/core@1.15.41(@swc/helpers@0.5.23))) - '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.59.0) - '@rollup/plugin-commonjs': 25.0.8(rollup@4.59.0) - '@rollup/plugin-image': 3.0.3(rollup@4.59.0) - '@rollup/plugin-json': 6.1.0(rollup@4.59.0) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.59.0) - '@rollup/plugin-typescript': 12.3.0(rollup@4.59.0)(tslib@2.8.1)(typescript@7.0.2) - autoprefixer: 10.4.20(postcss@8.5.23) + '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.2) + '@rollup/plugin-commonjs': 25.0.8(rollup@4.62.2) + '@rollup/plugin-image': 3.0.3(rollup@4.62.2) + '@rollup/plugin-json': 6.1.0(rollup@4.62.2) + '@rollup/plugin-node-resolve': 15.3.1(rollup@4.62.2) + '@rollup/plugin-typescript': 12.3.0(rollup@4.62.2)(tslib@2.8.1)(typescript@7.0.2) + autoprefixer: 10.4.20(postcss@8.5.24) concat-with-sourcemaps: 1.1.0 picocolors: 1.1.1 picomatch: 4.0.2 - postcss: 8.5.23 - postcss-modules: 6.0.1(postcss@8.5.23) - rollup: 4.59.0 - rollup-plugin-typescript2: 0.36.0(rollup@4.59.0)(typescript@7.0.2) + postcss: 8.5.24 + postcss-modules: 6.0.1(postcss@8.5.24) + rollup: 4.62.2 + rollup-plugin-typescript2: 0.36.0(rollup@4.62.2)(typescript@7.0.2) tslib: 2.8.1 transitivePeerDependencies: - '@babel/core' @@ -39633,50 +39662,50 @@ snapshots: optionalDependencies: rollup: 4.62.2 - '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.59.0)': + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.2)': dependencies: '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.2) optionalDependencies: '@types/babel__core': 7.20.5 - rollup: 4.59.0 + rollup: 4.62.2 transitivePeerDependencies: - supports-color - '@rollup/plugin-commonjs@25.0.8(rollup@4.59.0)': + '@rollup/plugin-commonjs@25.0.8(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.2) commondir: 1.0.1 estree-walker: 2.0.2 glob: 8.1.0 is-reference: 1.2.1 magic-string: 0.30.21 optionalDependencies: - rollup: 4.59.0 + rollup: 4.62.2 - '@rollup/plugin-image@3.0.3(rollup@4.59.0)': + '@rollup/plugin-image@3.0.3(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.2) mini-svg-data-uri: 1.4.4 optionalDependencies: - rollup: 4.59.0 + rollup: 4.62.2 - '@rollup/plugin-json@6.1.0(rollup@4.59.0)': + '@rollup/plugin-json@6.1.0(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.2) optionalDependencies: - rollup: 4.59.0 + rollup: 4.62.2 - '@rollup/plugin-node-resolve@15.3.1(rollup@4.59.0)': + '@rollup/plugin-node-resolve@15.3.1(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.2) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.12 optionalDependencies: - rollup: 4.59.0 + rollup: 4.62.2 '@rollup/plugin-replace@6.0.1(rollup@4.62.2)': dependencies: @@ -39685,13 +39714,13 @@ snapshots: optionalDependencies: rollup: 4.62.2 - '@rollup/plugin-typescript@12.3.0(rollup@4.59.0)(tslib@2.8.1)(typescript@7.0.2)': + '@rollup/plugin-typescript@12.3.0(rollup@4.62.2)(tslib@2.8.1)(typescript@7.0.2)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.2) resolve: 1.22.12 typescript: 7.0.2 optionalDependencies: - rollup: 4.59.0 + rollup: 4.62.2 tslib: 2.8.1 '@rollup/pluginutils@4.2.1': @@ -39699,14 +39728,6 @@ snapshots: estree-walker: 2.0.2 picomatch: 2.3.1 - '@rollup/pluginutils@5.3.0(rollup@4.59.0)': - dependencies: - '@types/estree': 1.0.8 - estree-walker: 2.0.2 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.59.0 - '@rollup/pluginutils@5.3.0(rollup@4.62.2)': dependencies: '@types/estree': 1.0.8 @@ -39964,9 +39985,9 @@ snapshots: transitivePeerDependencies: - '@module-federation/runtime-tools' - '@rsbuild/core@2.1.4(@module-federation/runtime-tools@2.2.2(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.49.0)': + '@rsbuild/core@2.1.4(@module-federation/runtime-tools@0.21.6)(core-js@3.49.0)': dependencies: - '@rspack/core': 2.1.2(@module-federation/runtime-tools@2.2.2(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/core': 2.1.2(@module-federation/runtime-tools@0.21.6)(@swc/helpers@0.5.23) '@swc/helpers': 0.5.23 optionalDependencies: core-js: 3.49.0 @@ -40355,15 +40376,6 @@ snapshots: transitivePeerDependencies: - '@rspack/core' - '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.4(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0))(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.17))': - dependencies: - '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.17))(react-refresh@0.18.0) - react-refresh: 0.18.0 - optionalDependencies: - '@rsbuild/core': 2.1.4(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0) - transitivePeerDependencies: - - '@rspack/core' - '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.4(@module-federation/runtime-tools@2.9.1)(core-js@3.49.0))(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.23))': dependencies: '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.23))(react-refresh@0.18.0) @@ -40392,7 +40404,7 @@ snapshots: '@rsbuild/core': 1.7.3 deepmerge: 4.3.1 loader-utils: 2.0.4 - postcss: 8.5.23 + postcss: 8.5.24 reduce-configs: 1.1.2 sass-embedded: 1.100.0 @@ -40400,7 +40412,7 @@ snapshots: dependencies: deepmerge: 4.3.1 loader-utils: 2.0.4 - postcss: 8.5.23 + postcss: 8.5.24 reduce-configs: 1.1.2 sass-embedded: 1.100.0 optionalDependencies: @@ -41509,11 +41521,11 @@ snapshots: '@module-federation/runtime-tools': 2.9.1 '@swc/helpers': 0.5.23 - '@rspack/core@2.1.2(@module-federation/runtime-tools@2.2.2(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)': + '@rspack/core@2.1.2(@module-federation/runtime-tools@0.21.6)(@swc/helpers@0.5.23)': dependencies: '@rspack/binding': 2.1.2 optionalDependencies: - '@module-federation/runtime-tools': 2.2.2(node-fetch@2.7.0(encoding@0.1.13)) + '@module-federation/runtime-tools': 0.21.6 '@swc/helpers': 0.5.23 '@rspack/core@2.1.2(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.23)': @@ -41523,14 +41535,6 @@ snapshots: '@module-federation/runtime-tools': 2.9.1 '@swc/helpers': 0.5.23 - '@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.17)': - dependencies: - '@rspack/binding': 2.1.8 - optionalDependencies: - '@module-federation/runtime-tools': 2.9.1 - '@swc/helpers': 0.5.17 - optional: true - '@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.23)': dependencies: '@rspack/binding': 2.1.8 @@ -41600,12 +41604,6 @@ snapshots: optionalDependencies: '@rspack/core': 2.0.6(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.17) - '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.17))(react-refresh@0.18.0)': - dependencies: - react-refresh: 0.18.0 - optionalDependencies: - '@rspack/core': 2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.17) - '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.8(@module-federation/runtime-tools@2.9.1)(@swc/helpers@0.5.23))(react-refresh@0.18.0)': dependencies: react-refresh: 0.18.0 @@ -43509,7 +43507,7 @@ snapshots: '@types/estree-jsx@1.0.5': dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/estree@0.0.51': {} @@ -46136,16 +46134,6 @@ snapshots: postcss: 8.4.49 postcss-value-parser: 4.2.0 - autoprefixer@10.4.20(postcss@8.5.23): - dependencies: - browserslist: 4.28.1 - caniuse-lite: 1.0.30001780 - fraction.js: 4.3.7 - normalize-range: 0.1.2 - picocolors: 1.1.1 - postcss: 8.5.23 - postcss-value-parser: 4.2.0 - autoprefixer@10.4.20(postcss@8.5.24): dependencies: browserslist: 4.28.1 @@ -46166,22 +46154,22 @@ snapshots: postcss: 8.5.26 postcss-value-parser: 4.2.0 - autoprefixer@10.4.23(postcss@8.5.23): + autoprefixer@10.4.23(postcss@8.5.24): dependencies: browserslist: 4.28.4 caniuse-lite: 1.0.30001800 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - autoprefixer@10.4.27(postcss@8.5.23): + autoprefixer@10.4.27(postcss@8.5.24): dependencies: browserslist: 4.28.4 caniuse-lite: 1.0.30001800 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -47841,9 +47829,9 @@ snapshots: css-color-keywords@1.0.0: {} - css-declaration-sorter@7.3.1(postcss@8.5.23): + css-declaration-sorter@7.3.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 css-loader@6.11.0(@rspack/core@1.3.9(@swc/helpers@0.5.13))(webpack@5.104.1): dependencies: @@ -47876,9 +47864,9 @@ snapshots: css-minimizer-webpack-plugin@5.0.1(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.23))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 - cssnano: 6.1.2(postcss@8.5.23) + cssnano: 6.1.2(postcss@8.5.24) jest-worker: 29.7.0 - postcss: 8.5.23 + postcss: 8.5.24 schema-utils: 4.3.0 serialize-javascript: 6.0.2 webpack: 5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.23))(esbuild@0.25.5)(webpack-cli@5.1.4) @@ -47888,9 +47876,9 @@ snapshots: css-minimizer-webpack-plugin@7.0.2(esbuild@0.18.20)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.19))(esbuild@0.18.20)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 - cssnano: 7.1.3(postcss@8.5.23) + cssnano: 7.1.3(postcss@8.5.24) jest-worker: 29.7.0 - postcss: 8.5.23 + postcss: 8.5.24 schema-utils: 4.3.0 serialize-javascript: 6.0.2 webpack: 5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.19))(esbuild@0.18.20)(webpack-cli@5.1.4) @@ -47900,9 +47888,9 @@ snapshots: css-minimizer-webpack-plugin@7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.17))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 - cssnano: 7.1.3(postcss@8.5.23) + cssnano: 7.1.3(postcss@8.5.24) jest-worker: 29.7.0 - postcss: 8.5.23 + postcss: 8.5.24 schema-utils: 4.3.0 serialize-javascript: 6.0.2 webpack: 5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.17))(esbuild@0.25.5)(webpack-cli@5.1.4) @@ -47912,9 +47900,9 @@ snapshots: css-minimizer-webpack-plugin@7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.19))(esbuild@0.18.20)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 - cssnano: 7.1.3(postcss@8.5.23) + cssnano: 7.1.3(postcss@8.5.24) jest-worker: 29.7.0 - postcss: 8.5.23 + postcss: 8.5.24 schema-utils: 4.3.0 serialize-javascript: 6.0.2 webpack: 5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.19))(esbuild@0.18.20)(webpack-cli@5.1.4) @@ -47924,9 +47912,9 @@ snapshots: css-minimizer-webpack-plugin@8.0.0(esbuild@0.28.1)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.17))(esbuild@0.28.1)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 - cssnano: 7.1.3(postcss@8.5.23) + cssnano: 7.1.3(postcss@8.5.24) jest-worker: 30.4.1 - postcss: 8.5.23 + postcss: 8.5.24 schema-utils: 4.3.0 serialize-javascript: 7.0.7 webpack: 5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.17))(esbuild@0.28.1)(webpack-cli@5.1.4) @@ -47936,9 +47924,9 @@ snapshots: css-minimizer-webpack-plugin@8.0.0(esbuild@0.28.1)(webpack@5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.23))(esbuild@0.28.1)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))): dependencies: '@jridgewell/trace-mapping': 0.3.31 - cssnano: 7.1.3(postcss@8.5.23) + cssnano: 7.1.3(postcss@8.5.24) jest-worker: 30.4.1 - postcss: 8.5.23 + postcss: 8.5.24 schema-utils: 4.3.0 serialize-javascript: 7.0.7 webpack: 5.104.1(@swc/core@1.15.41(@swc/helpers@0.5.23))(esbuild@0.28.1)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1)) @@ -47988,93 +47976,93 @@ snapshots: cssesc@3.0.0: {} - cssnano-preset-default@6.1.2(postcss@8.5.23): + cssnano-preset-default@6.1.2(postcss@8.5.24): dependencies: browserslist: 4.28.4 - css-declaration-sorter: 7.3.1(postcss@8.5.23) - cssnano-utils: 4.0.2(postcss@8.5.23) - postcss: 8.5.23 - postcss-calc: 9.0.1(postcss@8.5.23) - postcss-colormin: 6.1.0(postcss@8.5.23) - postcss-convert-values: 6.1.0(postcss@8.5.23) - postcss-discard-comments: 6.0.2(postcss@8.5.23) - postcss-discard-duplicates: 6.0.3(postcss@8.5.23) - postcss-discard-empty: 6.0.3(postcss@8.5.23) - postcss-discard-overridden: 6.0.2(postcss@8.5.23) - postcss-merge-longhand: 6.0.5(postcss@8.5.23) - postcss-merge-rules: 6.1.1(postcss@8.5.23) - postcss-minify-font-values: 6.1.0(postcss@8.5.23) - postcss-minify-gradients: 6.0.3(postcss@8.5.23) - postcss-minify-params: 6.1.0(postcss@8.5.23) - postcss-minify-selectors: 6.0.4(postcss@8.5.23) - postcss-normalize-charset: 6.0.2(postcss@8.5.23) - postcss-normalize-display-values: 6.0.2(postcss@8.5.23) - postcss-normalize-positions: 6.0.2(postcss@8.5.23) - postcss-normalize-repeat-style: 6.0.2(postcss@8.5.23) - postcss-normalize-string: 6.0.2(postcss@8.5.23) - postcss-normalize-timing-functions: 6.0.2(postcss@8.5.23) - postcss-normalize-unicode: 6.1.0(postcss@8.5.23) - postcss-normalize-url: 6.0.2(postcss@8.5.23) - postcss-normalize-whitespace: 6.0.2(postcss@8.5.23) - postcss-ordered-values: 6.0.2(postcss@8.5.23) - postcss-reduce-initial: 6.1.0(postcss@8.5.23) - postcss-reduce-transforms: 6.0.2(postcss@8.5.23) - postcss-svgo: 6.0.3(postcss@8.5.23) - postcss-unique-selectors: 6.0.4(postcss@8.5.23) - - cssnano-preset-default@7.0.11(postcss@8.5.23): + css-declaration-sorter: 7.3.1(postcss@8.5.24) + cssnano-utils: 4.0.2(postcss@8.5.24) + postcss: 8.5.24 + postcss-calc: 9.0.1(postcss@8.5.24) + postcss-colormin: 6.1.0(postcss@8.5.24) + postcss-convert-values: 6.1.0(postcss@8.5.24) + postcss-discard-comments: 6.0.2(postcss@8.5.24) + postcss-discard-duplicates: 6.0.3(postcss@8.5.24) + postcss-discard-empty: 6.0.3(postcss@8.5.24) + postcss-discard-overridden: 6.0.2(postcss@8.5.24) + postcss-merge-longhand: 6.0.5(postcss@8.5.24) + postcss-merge-rules: 6.1.1(postcss@8.5.24) + postcss-minify-font-values: 6.1.0(postcss@8.5.24) + postcss-minify-gradients: 6.0.3(postcss@8.5.24) + postcss-minify-params: 6.1.0(postcss@8.5.24) + postcss-minify-selectors: 6.0.4(postcss@8.5.24) + postcss-normalize-charset: 6.0.2(postcss@8.5.24) + postcss-normalize-display-values: 6.0.2(postcss@8.5.24) + postcss-normalize-positions: 6.0.2(postcss@8.5.24) + postcss-normalize-repeat-style: 6.0.2(postcss@8.5.24) + postcss-normalize-string: 6.0.2(postcss@8.5.24) + postcss-normalize-timing-functions: 6.0.2(postcss@8.5.24) + postcss-normalize-unicode: 6.1.0(postcss@8.5.24) + postcss-normalize-url: 6.0.2(postcss@8.5.24) + postcss-normalize-whitespace: 6.0.2(postcss@8.5.24) + postcss-ordered-values: 6.0.2(postcss@8.5.24) + postcss-reduce-initial: 6.1.0(postcss@8.5.24) + postcss-reduce-transforms: 6.0.2(postcss@8.5.24) + postcss-svgo: 6.0.3(postcss@8.5.24) + postcss-unique-selectors: 6.0.4(postcss@8.5.24) + + cssnano-preset-default@7.0.11(postcss@8.5.24): dependencies: browserslist: 4.28.4 - css-declaration-sorter: 7.3.1(postcss@8.5.23) - cssnano-utils: 5.0.1(postcss@8.5.23) - postcss: 8.5.23 - postcss-calc: 10.1.1(postcss@8.5.23) - postcss-colormin: 7.0.6(postcss@8.5.23) - postcss-convert-values: 7.0.9(postcss@8.5.23) - postcss-discard-comments: 7.0.6(postcss@8.5.23) - postcss-discard-duplicates: 7.0.2(postcss@8.5.23) - postcss-discard-empty: 7.0.1(postcss@8.5.23) - postcss-discard-overridden: 7.0.1(postcss@8.5.23) - postcss-merge-longhand: 7.0.5(postcss@8.5.23) - postcss-merge-rules: 7.0.8(postcss@8.5.23) - postcss-minify-font-values: 7.0.1(postcss@8.5.23) - postcss-minify-gradients: 7.0.1(postcss@8.5.23) - postcss-minify-params: 7.0.6(postcss@8.5.23) - postcss-minify-selectors: 7.0.6(postcss@8.5.23) - postcss-normalize-charset: 7.0.1(postcss@8.5.23) - postcss-normalize-display-values: 7.0.1(postcss@8.5.23) - postcss-normalize-positions: 7.0.1(postcss@8.5.23) - postcss-normalize-repeat-style: 7.0.1(postcss@8.5.23) - postcss-normalize-string: 7.0.1(postcss@8.5.23) - postcss-normalize-timing-functions: 7.0.1(postcss@8.5.23) - postcss-normalize-unicode: 7.0.6(postcss@8.5.23) - postcss-normalize-url: 7.0.1(postcss@8.5.23) - postcss-normalize-whitespace: 7.0.1(postcss@8.5.23) - postcss-ordered-values: 7.0.2(postcss@8.5.23) - postcss-reduce-initial: 7.0.6(postcss@8.5.23) - postcss-reduce-transforms: 7.0.1(postcss@8.5.23) - postcss-svgo: 7.1.1(postcss@8.5.23) - postcss-unique-selectors: 7.0.5(postcss@8.5.23) - - cssnano-utils@4.0.2(postcss@8.5.23): + css-declaration-sorter: 7.3.1(postcss@8.5.24) + cssnano-utils: 5.0.1(postcss@8.5.24) + postcss: 8.5.24 + postcss-calc: 10.1.1(postcss@8.5.24) + postcss-colormin: 7.0.6(postcss@8.5.24) + postcss-convert-values: 7.0.9(postcss@8.5.24) + postcss-discard-comments: 7.0.6(postcss@8.5.24) + postcss-discard-duplicates: 7.0.2(postcss@8.5.24) + postcss-discard-empty: 7.0.1(postcss@8.5.24) + postcss-discard-overridden: 7.0.1(postcss@8.5.24) + postcss-merge-longhand: 7.0.5(postcss@8.5.24) + postcss-merge-rules: 7.0.8(postcss@8.5.24) + postcss-minify-font-values: 7.0.1(postcss@8.5.24) + postcss-minify-gradients: 7.0.1(postcss@8.5.24) + postcss-minify-params: 7.0.6(postcss@8.5.24) + postcss-minify-selectors: 7.0.6(postcss@8.5.24) + postcss-normalize-charset: 7.0.1(postcss@8.5.24) + postcss-normalize-display-values: 7.0.1(postcss@8.5.24) + postcss-normalize-positions: 7.0.1(postcss@8.5.24) + postcss-normalize-repeat-style: 7.0.1(postcss@8.5.24) + postcss-normalize-string: 7.0.1(postcss@8.5.24) + postcss-normalize-timing-functions: 7.0.1(postcss@8.5.24) + postcss-normalize-unicode: 7.0.6(postcss@8.5.24) + postcss-normalize-url: 7.0.1(postcss@8.5.24) + postcss-normalize-whitespace: 7.0.1(postcss@8.5.24) + postcss-ordered-values: 7.0.2(postcss@8.5.24) + postcss-reduce-initial: 7.0.6(postcss@8.5.24) + postcss-reduce-transforms: 7.0.1(postcss@8.5.24) + postcss-svgo: 7.1.1(postcss@8.5.24) + postcss-unique-selectors: 7.0.5(postcss@8.5.24) + + cssnano-utils@4.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - cssnano-utils@5.0.1(postcss@8.5.23): + cssnano-utils@5.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - cssnano@6.1.2(postcss@8.5.23): + cssnano@6.1.2(postcss@8.5.24): dependencies: - cssnano-preset-default: 6.1.2(postcss@8.5.23) + cssnano-preset-default: 6.1.2(postcss@8.5.24) lilconfig: 3.1.3 - postcss: 8.5.23 + postcss: 8.5.24 - cssnano@7.1.3(postcss@8.5.23): + cssnano@7.1.3(postcss@8.5.24): dependencies: - cssnano-preset-default: 7.0.11(postcss@8.5.23) + cssnano-preset-default: 7.0.11(postcss@8.5.24) lilconfig: 3.1.3 - postcss: 8.5.23 + postcss: 8.5.24 csso@5.0.5: dependencies: @@ -50113,7 +50101,7 @@ snapshots: estree-util-scope@1.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 devlop: 1.1.0 estree-util-to-js@2.0.0: @@ -51586,6 +51574,8 @@ snapshots: hono@3.12.12: {} + hono@4.12.26: {} + hono@4.13.5: {} hookable@6.1.0: {} @@ -51927,6 +51917,10 @@ snapshots: dependencies: postcss: 8.5.23 + icss-utils@5.1.0(postcss@8.5.24): + dependencies: + postcss: 8.5.24 + icss-utils@5.1.0(postcss@8.5.8): dependencies: postcss: 8.5.8 @@ -54175,7 +54169,7 @@ snapshots: metro-source-map@0.83.5: dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.83.5 @@ -54222,7 +54216,7 @@ snapshots: metro-transform-plugins@0.83.5: dependencies: '@babel/core': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 flow-enums-runtime: 0.0.6 @@ -54253,9 +54247,9 @@ snapshots: metro-transform-worker@0.83.5: dependencies: '@babel/core': 7.29.7 - '@babel/generator': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 flow-enums-runtime: 0.0.6 metro: 0.83.5 metro-babel-transformer: 0.83.5 @@ -56020,15 +56014,15 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-calc@10.1.1(postcss@8.5.23): + postcss-calc@10.1.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 - postcss-calc@9.0.1(postcss@8.5.23): + postcss-calc@9.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 @@ -56038,41 +56032,41 @@ snapshots: postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - postcss-colormin@6.1.0(postcss@8.5.23): + postcss-colormin@6.1.0(postcss@8.5.24): dependencies: browserslist: 4.28.4 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-colormin@7.0.6(postcss@8.5.23): + postcss-colormin@7.0.6(postcss@8.5.24): dependencies: browserslist: 4.28.4 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-convert-values@6.1.0(postcss@8.5.23): + postcss-convert-values@6.1.0(postcss@8.5.24): dependencies: browserslist: 4.28.4 - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-convert-values@7.0.9(postcss@8.5.23): + postcss-convert-values@7.0.9(postcss@8.5.24): dependencies: browserslist: 4.28.4 - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-custom-properties@13.3.12(postcss@8.5.23): + postcss-custom-properties@13.3.12(postcss@8.5.24): dependencies: '@csstools/cascade-layer-name-parser': 1.0.13(@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1))(@csstools/css-tokenizer@2.4.1) '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) '@csstools/css-tokenizer': 2.4.1 - '@csstools/utilities': 1.0.0(postcss@8.5.23) - postcss: 8.5.23 + '@csstools/utilities': 1.0.0(postcss@8.5.24) + postcss: 8.5.24 postcss-value-parser: 4.2.0 postcss-custom-properties@13.3.12(postcss@8.5.26): @@ -56084,46 +56078,46 @@ snapshots: postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-discard-comments@6.0.2(postcss@8.5.23): + postcss-discard-comments@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-discard-comments@7.0.6(postcss@8.5.23): + postcss-discard-comments@7.0.6(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 7.1.1 - postcss-discard-duplicates@6.0.3(postcss@8.5.23): + postcss-discard-duplicates@6.0.3(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-discard-duplicates@7.0.2(postcss@8.5.23): + postcss-discard-duplicates@7.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-discard-empty@6.0.3(postcss@8.5.23): + postcss-discard-empty@6.0.3(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-discard-empty@7.0.1(postcss@8.5.23): + postcss-discard-empty@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-discard-overridden@6.0.2(postcss@8.5.23): + postcss-discard-overridden@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-discard-overridden@7.0.1(postcss@8.5.23): + postcss-discard-overridden@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-flexbugs-fixes@5.0.2(postcss@8.5.23): + postcss-flexbugs-fixes@5.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-font-variant@5.0.0(postcss@8.5.23): + postcss-font-variant@5.0.0(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-import@14.1.0(postcss@8.4.49): dependencies: @@ -56153,9 +56147,9 @@ snapshots: read-cache: 1.0.0 resolve: 1.22.8 - postcss-initial@4.0.1(postcss@8.5.23): + postcss-initial@4.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-js@4.1.0(postcss@8.4.49): dependencies: @@ -56199,21 +56193,21 @@ snapshots: postcss: 8.4.49 ts-node: 10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@26.6.2)(typescript@7.0.2) - postcss-load-config@4.0.2(postcss@8.5.23)(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@22.19.15)(typescript@6.0.3)): + postcss-load-config@4.0.2(postcss@8.5.23)(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@20.19.5)(typescript@7.0.2)): dependencies: lilconfig: 3.1.3 yaml: 2.8.2 optionalDependencies: postcss: 8.5.23 - ts-node: 10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@22.19.15)(typescript@6.0.3) + ts-node: 10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@20.19.5)(typescript@7.0.2) - postcss-load-config@4.0.2(postcss@8.5.23)(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@20.19.5)(typescript@7.0.2)): + postcss-load-config@4.0.2(postcss@8.5.23)(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@22.19.15)(typescript@6.0.3)): dependencies: lilconfig: 3.1.3 yaml: 2.8.2 optionalDependencies: postcss: 8.5.23 - ts-node: 10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@20.19.5)(typescript@7.0.2) + ts-node: 10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@22.19.15)(typescript@6.0.3) postcss-load-config@4.0.2(postcss@8.5.26)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@20.19.5)(typescript@6.0.3)): dependencies: @@ -56251,91 +56245,95 @@ snapshots: transitivePeerDependencies: - typescript - postcss-media-minmax@5.0.0(postcss@8.5.23): + postcss-media-minmax@5.0.0(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-merge-longhand@6.0.5(postcss@8.5.23): + postcss-merge-longhand@6.0.5(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - stylehacks: 6.1.1(postcss@8.5.23) + stylehacks: 6.1.1(postcss@8.5.24) - postcss-merge-longhand@7.0.5(postcss@8.5.23): + postcss-merge-longhand@7.0.5(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - stylehacks: 7.0.8(postcss@8.5.23) + stylehacks: 7.0.8(postcss@8.5.24) - postcss-merge-rules@6.1.1(postcss@8.5.23): + postcss-merge-rules@6.1.1(postcss@8.5.24): dependencies: browserslist: 4.28.4 caniuse-api: 3.0.0 - cssnano-utils: 4.0.2(postcss@8.5.23) - postcss: 8.5.23 + cssnano-utils: 4.0.2(postcss@8.5.24) + postcss: 8.5.24 postcss-selector-parser: 6.1.2 - postcss-merge-rules@7.0.8(postcss@8.5.23): + postcss-merge-rules@7.0.8(postcss@8.5.24): dependencies: browserslist: 4.28.4 caniuse-api: 3.0.0 - cssnano-utils: 5.0.1(postcss@8.5.23) - postcss: 8.5.23 + cssnano-utils: 5.0.1(postcss@8.5.24) + postcss: 8.5.24 postcss-selector-parser: 7.1.1 - postcss-minify-font-values@6.1.0(postcss@8.5.23): + postcss-minify-font-values@6.1.0(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-minify-font-values@7.0.1(postcss@8.5.23): + postcss-minify-font-values@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-minify-gradients@6.0.3(postcss@8.5.23): + postcss-minify-gradients@6.0.3(postcss@8.5.24): dependencies: colord: 2.9.3 - cssnano-utils: 4.0.2(postcss@8.5.23) - postcss: 8.5.23 + cssnano-utils: 4.0.2(postcss@8.5.24) + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-minify-gradients@7.0.1(postcss@8.5.23): + postcss-minify-gradients@7.0.1(postcss@8.5.24): dependencies: colord: 2.9.3 - cssnano-utils: 5.0.1(postcss@8.5.23) - postcss: 8.5.23 + cssnano-utils: 5.0.1(postcss@8.5.24) + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-minify-params@6.1.0(postcss@8.5.23): + postcss-minify-params@6.1.0(postcss@8.5.24): dependencies: browserslist: 4.28.4 - cssnano-utils: 4.0.2(postcss@8.5.23) - postcss: 8.5.23 + cssnano-utils: 4.0.2(postcss@8.5.24) + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-minify-params@7.0.6(postcss@8.5.23): + postcss-minify-params@7.0.6(postcss@8.5.24): dependencies: browserslist: 4.28.4 - cssnano-utils: 5.0.1(postcss@8.5.23) - postcss: 8.5.23 + cssnano-utils: 5.0.1(postcss@8.5.24) + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-minify-selectors@6.0.4(postcss@8.5.23): + postcss-minify-selectors@6.0.4(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 6.1.2 - postcss-minify-selectors@7.0.6(postcss@8.5.23): + postcss-minify-selectors@7.0.6(postcss@8.5.24): dependencies: cssesc: 3.0.0 - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 7.1.1 postcss-modules-extract-imports@3.1.0(postcss@8.5.23): dependencies: postcss: 8.5.23 + postcss-modules-extract-imports@3.1.0(postcss@8.5.24): + dependencies: + postcss: 8.5.24 + postcss-modules-extract-imports@3.1.0(postcss@8.5.8): dependencies: postcss: 8.5.8 @@ -56347,6 +56345,13 @@ snapshots: postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 + postcss-modules-local-by-default@4.2.0(postcss@8.5.24): + dependencies: + icss-utils: 5.1.0(postcss@8.5.24) + postcss: 8.5.24 + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + postcss-modules-local-by-default@4.2.0(postcss@8.5.8): dependencies: icss-utils: 5.1.0(postcss@8.5.8) @@ -56359,6 +56364,11 @@ snapshots: postcss: 8.5.23 postcss-selector-parser: 7.1.1 + postcss-modules-scope@3.2.1(postcss@8.5.24): + dependencies: + postcss: 8.5.24 + postcss-selector-parser: 7.1.1 + postcss-modules-scope@3.2.1(postcss@8.5.8): dependencies: postcss: 8.5.8 @@ -56369,6 +56379,11 @@ snapshots: icss-utils: 5.1.0(postcss@8.5.23) postcss: 8.5.23 + postcss-modules-values@4.0.0(postcss@8.5.24): + dependencies: + icss-utils: 5.1.0(postcss@8.5.24) + postcss: 8.5.24 + postcss-modules-values@4.0.0(postcss@8.5.8): dependencies: icss-utils: 5.1.0(postcss@8.5.8) @@ -56386,16 +56401,16 @@ snapshots: postcss-modules-values: 4.0.0(postcss@8.5.8) string-hash: 1.1.3 - postcss-modules@6.0.1(postcss@8.5.23): + postcss-modules@6.0.1(postcss@8.5.24): dependencies: generic-names: 4.0.0 - icss-utils: 5.1.0(postcss@8.5.23) + icss-utils: 5.1.0(postcss@8.5.24) lodash.camelcase: 4.3.0 - postcss: 8.5.23 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.23) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.23) - postcss-modules-scope: 3.2.1(postcss@8.5.23) - postcss-modules-values: 4.0.0(postcss@8.5.23) + postcss: 8.5.24 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.24) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.24) + postcss-modules-scope: 3.2.1(postcss@8.5.24) + postcss-modules-values: 4.0.0(postcss@8.5.24) string-hash: 1.1.3 postcss-nested@6.2.0(postcss@8.4.49): @@ -56408,139 +56423,139 @@ snapshots: postcss: 8.5.23 postcss-selector-parser: 6.1.2 - postcss-nesting@12.1.5(postcss@8.5.23): + postcss-nesting@12.1.5(postcss@8.5.24): dependencies: '@csstools/selector-resolve-nested': 1.1.0(postcss-selector-parser@6.1.2) '@csstools/selector-specificity': 3.1.1(postcss-selector-parser@6.1.2) - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 6.1.2 - postcss-normalize-charset@6.0.2(postcss@8.5.23): + postcss-normalize-charset@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-normalize-charset@7.0.1(postcss@8.5.23): + postcss-normalize-charset@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-normalize-display-values@6.0.2(postcss@8.5.23): + postcss-normalize-display-values@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-display-values@7.0.1(postcss@8.5.23): + postcss-normalize-display-values@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-positions@6.0.2(postcss@8.5.23): + postcss-normalize-positions@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-positions@7.0.1(postcss@8.5.23): + postcss-normalize-positions@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@6.0.2(postcss@8.5.23): + postcss-normalize-repeat-style@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@7.0.1(postcss@8.5.23): + postcss-normalize-repeat-style@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-string@6.0.2(postcss@8.5.23): + postcss-normalize-string@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-string@7.0.1(postcss@8.5.23): + postcss-normalize-string@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@6.0.2(postcss@8.5.23): + postcss-normalize-timing-functions@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@7.0.1(postcss@8.5.23): + postcss-normalize-timing-functions@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@6.1.0(postcss@8.5.23): + postcss-normalize-unicode@6.1.0(postcss@8.5.24): dependencies: browserslist: 4.28.4 - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@7.0.6(postcss@8.5.23): + postcss-normalize-unicode@7.0.6(postcss@8.5.24): dependencies: browserslist: 4.28.4 - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-url@6.0.2(postcss@8.5.23): + postcss-normalize-url@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-url@7.0.1(postcss@8.5.23): + postcss-normalize-url@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@6.0.2(postcss@8.5.23): + postcss-normalize-whitespace@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@7.0.1(postcss@8.5.23): + postcss-normalize-whitespace@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-ordered-values@6.0.2(postcss@8.5.23): + postcss-ordered-values@6.0.2(postcss@8.5.24): dependencies: - cssnano-utils: 4.0.2(postcss@8.5.23) - postcss: 8.5.23 + cssnano-utils: 4.0.2(postcss@8.5.24) + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-ordered-values@7.0.2(postcss@8.5.23): + postcss-ordered-values@7.0.2(postcss@8.5.24): dependencies: - cssnano-utils: 5.0.1(postcss@8.5.23) - postcss: 8.5.23 + cssnano-utils: 5.0.1(postcss@8.5.24) + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-page-break@3.0.4(postcss@8.5.23): + postcss-page-break@3.0.4(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 - postcss-reduce-initial@6.1.0(postcss@8.5.23): + postcss-reduce-initial@6.1.0(postcss@8.5.24): dependencies: browserslist: 4.28.4 caniuse-api: 3.0.0 - postcss: 8.5.23 + postcss: 8.5.24 - postcss-reduce-initial@7.0.6(postcss@8.5.23): + postcss-reduce-initial@7.0.6(postcss@8.5.24): dependencies: browserslist: 4.28.4 caniuse-api: 3.0.0 - postcss: 8.5.23 + postcss: 8.5.24 - postcss-reduce-transforms@6.0.2(postcss@8.5.23): + postcss-reduce-transforms@6.0.2(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 - postcss-reduce-transforms@7.0.1(postcss@8.5.23): + postcss-reduce-transforms@7.0.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 postcss-selector-parser@6.0.10: @@ -56558,26 +56573,26 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-svgo@6.0.3(postcss@8.5.23): + postcss-svgo@6.0.3(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 svgo: 3.3.3 - postcss-svgo@7.1.1(postcss@8.5.23): + postcss-svgo@7.1.1(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-value-parser: 4.2.0 svgo: 4.0.1 - postcss-unique-selectors@6.0.4(postcss@8.5.23): + postcss-unique-selectors@6.0.4(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 6.1.2 - postcss-unique-selectors@7.0.5(postcss@8.5.23): + postcss-unique-selectors@7.0.5(postcss@8.5.24): dependencies: - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 7.1.1 postcss-url@10.1.3(postcss@8.5.26): @@ -59113,7 +59128,7 @@ snapshots: recma-build-jsx@1.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 estree-util-build-jsx: 3.0.1 vfile: 6.0.3 @@ -59135,7 +59150,7 @@ snapshots: recma-stringify@1.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 estree-util-to-js: 2.0.0 unified: 11.0.5 vfile: 6.0.3 @@ -59251,7 +59266,7 @@ snapshots: rehype-recma@1.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/hast': 3.0.4 hast-util-to-estree: 3.1.3 transitivePeerDependencies: @@ -59439,7 +59454,7 @@ snapshots: adjust-sourcemap-loader: 4.0.0 convert-source-map: 1.9.0 loader-utils: 2.0.4 - postcss: 8.5.23 + postcss: 8.5.24 source-map: 0.6.1 resolve-url@0.2.1: {} @@ -59633,12 +59648,12 @@ snapshots: globby: 10.0.1 is-plain-object: 3.0.1 - rollup-plugin-typescript2@0.36.0(rollup@4.59.0)(typescript@7.0.2): + rollup-plugin-typescript2@0.36.0(rollup@4.62.2)(typescript@7.0.2): dependencies: '@rollup/pluginutils': 4.2.1 find-cache-dir: 3.3.2 fs-extra: 10.1.0 - rollup: 4.59.0 + rollup: 4.62.2 semver: 7.8.5 tslib: 2.8.1 typescript: 7.0.2 @@ -61217,16 +61232,16 @@ snapshots: '@babel/core': 7.29.0 babel-plugin-macros: 3.1.0 - stylehacks@6.1.1(postcss@8.5.23): + stylehacks@6.1.1(postcss@8.5.24): dependencies: browserslist: 4.28.4 - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 6.1.2 - stylehacks@7.0.8(postcss@8.5.23): + stylehacks@7.0.8(postcss@8.5.24): dependencies: browserslist: 4.28.4 - postcss: 8.5.23 + postcss: 8.5.24 postcss-selector-parser: 7.1.1 stylis@4.2.0: {} @@ -61327,9 +61342,9 @@ snapshots: tailwind-merge@3.6.0: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@22.19.15)(typescript@6.0.3))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@22.19.15)(typescript@6.0.3))): dependencies: - tailwindcss: 3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@22.19.15)(typescript@6.0.3)) + tailwindcss: 3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@22.19.15)(typescript@6.0.3)) tailwindcss@3.4.13(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@20.19.5)(typescript@6.0.3)): dependencies: @@ -61439,7 +61454,7 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@22.19.15)(typescript@6.0.3)): + tailwindcss@3.4.16(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@22.19.15)(typescript@6.0.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -61458,7 +61473,7 @@ snapshots: postcss: 8.5.23 postcss-import: 15.1.0(postcss@8.5.23) postcss-js: 4.1.0(postcss@8.5.23) - postcss-load-config: 4.0.2(postcss@8.5.23)(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@22.19.15)(typescript@6.0.3)) + postcss-load-config: 4.0.2(postcss@8.5.23)(ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@22.19.15)(typescript@6.0.3)) postcss-nested: 6.2.0(postcss@8.5.23) postcss-selector-parser: 6.1.2 resolve: 1.22.8 @@ -62090,26 +62105,25 @@ snapshots: optionalDependencies: '@swc/core': 1.7.26(@swc/helpers@0.5.13) - ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@22.19.15)(typescript@6.0.3): + ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@20.19.5)(typescript@7.0.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.19.15 + '@types/node': 20.19.5 acorn: 8.17.0 acorn-walk: 8.3.5 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.4 make-error: 1.3.6 - typescript: 6.0.3 + typescript: 7.0.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: '@swc/core': 1.15.41(@swc/helpers@0.5.17) - optional: true ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.17))(@types/node@26.6.2)(typescript@7.0.2): dependencies: @@ -62193,6 +62207,27 @@ snapshots: optionalDependencies: '@swc/core': 1.15.41(@swc/helpers@0.5.23) + ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@22.19.15)(typescript@6.0.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.19.15 + acorn: 8.17.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 6.0.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.41(@swc/helpers@0.5.23) + optional: true + ts-node@10.9.2(@swc/core@1.15.41(@swc/helpers@0.5.23))(@types/node@26.6.2)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -63117,8 +63152,8 @@ snapshots: vite@5.4.21(@types/node@20.19.5)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0): dependencies: esbuild: 0.21.5 - postcss: 8.4.49 - rollup: 4.59.0 + postcss: 8.5.24 + rollup: 4.62.2 optionalDependencies: '@types/node': 20.19.5 fsevents: 2.3.3 @@ -63130,8 +63165,8 @@ snapshots: vite@5.4.21(@types/node@26.6.2)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0): dependencies: esbuild: 0.21.5 - postcss: 8.4.49 - rollup: 4.59.0 + postcss: 8.5.24 + rollup: 4.62.2 optionalDependencies: '@types/node': 26.6.2 fsevents: 2.3.3