
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
@meta-1/web-common
Advanced tools
Meta-1 前端通用库,提供 React 组件、Hooks、工具函数和配置,用于构建 Next.js 应用。
此包为 Meta-1 项目内部包,通过 monorepo 工作区使用。
# 在项目根目录安装依赖
pnpm install
用于 Next.js App Router 的根布局,提供全局 Provider 配置。
import { RootLayout } from "@meta-1/web-common/components/layout/root";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<RootLayout locale="zh-cn" theme="system">
{children}
</RootLayout>
);
}
支持 light、dark、system 三种主题切换,带有平滑的 View Transition 动画。
import { ThemeSwitcher } from "@meta-1/web-common/components/theme-switcher";
function Header() {
return (
<div>
<ThemeSwitcher size="md" />
</div>
);
}
Props:
size?: "sm" | "md" | "lg" - 组件尺寸,默认 mdactiveClassName?: string - 激活状态的样式类名className?: string - 自定义样式类名6 位数字验证码输入组件。
import { OTPInput } from "@meta-1/web-common/components/input/otp";
function VerifyCode() {
const [code, setCode] = useState("");
return (
<OTPInput
value={code}
onChange={setCode}
/>
);
}
Props:
value?: string - 当前值onChange?: (value: string) => void - 值变化回调全屏加载动画组件。
import { Loading } from "@meta-1/web-common/components/loading";
function App() {
if (isLoading) {
return <Loading />;
}
return <div>内容</div>;
}
根据条件显示/隐藏内容。
import { Visible } from "@meta-1/web-common/components/visible";
function MyComponent() {
const [show, setShow] = useState(true);
return (
<Visible visible={show}>
<div>条件显示的内容</div>
</Visible>
);
}
Props:
visible?: boolean - 是否显示,默认 truechildren: React.ReactNode - 子元素文件上传组件,基于 @meta-1/design 的 Uploader 封装。
import { Uploader } from "@meta-1/web-common/components/uploader";
function FileUpload() {
const [files, setFiles] = useState([]);
return (
<Uploader
value={files}
onChange={setFiles}
action="/api/upload"
/>
);
}
Props:
value?: UploadFile[] - 已上传的文件列表onChange?: (value: UploadFile[]) => void - 文件变化回调@meta-1/design 的 Uploader基于 TanStack Query 封装的查询 Hook,自动处理 REST API 响应格式和错误提示。
import { useQuery } from "@meta-1/web-common/hooks";
import { get } from "@meta-1/web-common/utils/rest";
function UserList() {
const { data, isLoading, error } = useQuery({
queryKey: ["users"],
queryFn: async () => {
return await get<User[]>("/api/users");
},
showError: true, // 是否自动显示错误消息,默认 false
});
if (isLoading) return <div>加载中...</div>;
if (error) return <div>错误: {error.message}</div>;
return (
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
特点:
RestResult<T> 格式的响应data 字段基于 TanStack Query 封装的变更 Hook,用于 POST、PUT、DELETE 等操作。
import { useMutation } from "@meta-1/web-common/hooks";
import { post } from "@meta-1/web-common/utils/rest";
import { useQueryClient } from "@tanstack/react-query";
function CreateUser() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (data: CreateUserData) => {
return await post<User>("/api/users", data);
},
showError: true, // 是否自动显示错误消息,默认 true
noErrorCode: [400], // 不显示错误消息的错误码列表
onSuccess: () => {
// 成功后刷新列表
queryClient.invalidateQueries({ queryKey: ["users"] });
},
});
const handleSubmit = (data: CreateUserData) => {
mutation.mutate(data);
};
return (
<form onSubmit={handleSubmit}>
{/* 表单内容 */}
<button type="submit" disabled={mutation.isPending}>
{mutation.isPending ? "提交中..." : "提交"}
</button>
</form>
);
}
特点:
RestResult<T> 格式的响应data 字段用于表格分页、筛选、排序等场景的数据加载 Hook,支持 URL 参数同步。
import { useTableLoader } from "@meta-1/web-common/hooks";
import { get } from "@meta-1/web-common/utils/rest";
import type { PageResult } from "@meta-1/web-common/types/rest";
function UserTable() {
const { loading, result, query, load, reset, refetch } = useTableLoader<User>({
defaultValues: {
keyword: "",
status: "active",
},
action: async (params) => {
return await get<PageResult<User>>("/api/users", params);
},
withURL: true, // 是否同步 URL 参数,默认 true
encodeParams: ["keyword"], // 需要 URL 编码的参数
keys: ["extraKey"], // 额外的查询参数键
});
const handleSearch = (keyword: string) => {
load({ keyword, page: 1 });
};
const handleReset = () => {
reset();
};
if (loading) return <div>加载中...</div>;
return (
<div>
<input
value={query.keyword}
onChange={(e) => handleSearch(e.target.value)}
/>
<button onClick={handleReset}>重置</button>
<table>
{/* 表格内容 */}
</table>
<div>
当前页: {query.page}, 每页: {query.size}
总数: {result?.data?.total || 0}
</div>
</div>
);
}
返回值:
loading: boolean - 是否正在加载result: RestResult<PageData<T>> | null - 查询结果query: any - 当前查询参数load: (params?: any) => Promise<void> - 加载数据(合并参数)reset: () => void - 重置到初始状态refetch: () => Promise<void> - 刷新当前数据特点:
用于切换应用语言。
import { useLocale } from "@meta-1/web-common/hooks";
function LanguageSwitcher() {
const [locale, setLocale] = useLocale();
return (
<select value={locale} onChange={(e) => setLocale(e.target.value)}>
<option value="zh-cn">中文</option>
<option value="en">English</option>
</select>
);
}
返回值:
[locale: string, setLocale: (value: string) => void] - 当前语言和设置函数用于将 Zod Schema 的错误消息进行国际化处理。
import { useSchema } from "@meta-1/web-common/hooks";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
const LoginSchema = z.object({
email: z.string().email("validation.email.invalid"),
password: z.string().min(8, "validation.password.min"),
});
function LoginForm() {
const schema = useSchema(LoginSchema);
const form = useForm({
resolver: zodResolver(schema),
});
return (
<form onSubmit={form.handleSubmit(onSubmit)}>
{/* 表单内容 */}
</form>
);
}
特点:
用于对敏感数据进行 RSA 加密。
import { useEncrypt } from "@meta-1/web-common/hooks";
function LoginForm() {
const encrypt = useEncrypt();
const handleSubmit = async (password: string) => {
const encryptedPassword = encrypt(password);
// 发送加密后的密码
await post("/api/login", { password: encryptedPassword });
};
return <form onSubmit={handleSubmit}>...</form>;
}
返回值:
encrypt: (text: string) => string - 加密函数,如果未设置公钥则返回原文提供 REST API 请求方法,自动处理响应格式和错误。
import {
get,
post,
put,
patch,
del,
download,
request,
alias,
config,
} from "@meta-1/web-common/utils/rest";
import type { RestResult } from "@meta-1/web-common/types/rest";
// GET 请求
const result = await get<User[]>("/api/users", { page: 1, size: 10 });
// POST 请求
const result = await post<User>("/api/users", {
name: "John",
email: "john@example.com",
});
// PUT 请求
const result = await put<User>("/api/users/1", {
name: "John Updated",
});
// PATCH 请求
const result = await patch<User>("/api/users/1", {
name: "John Patched",
});
// DELETE 请求
const result = await del("/api/users/1");
// 文件下载
await download("/api/export", { format: "excel" }, {
fileName: "users.xlsx",
});
// 自定义请求
const result = await request<Data>({
url: "/api/custom",
method: "POST",
data: { ... },
});
// 配置 API 别名
alias({
"@api": {
url: "https://api.example.com",
headers: async () => ({
"X-Custom-Header": "value",
}),
},
});
// 使用别名
await get("@api/users");
// 全局配置
config({
onResponse: (data, response) => {
// 处理响应
},
});
响应格式:
所有请求返回 Promise<RestResult<T>>,格式如下:
type RestResult<T> = {
success?: boolean;
code?: number;
message?: string;
data?: T;
};
Token 的存储和读取工具。
import {
getToken,
setToken,
clearToken,
} from "@meta-1/web-common/utils/token";
import type { Token } from "@meta-1/web-common/types/token";
// 客户端获取 Token
const token = await getToken();
// 服务端获取 Token
import { cookies } from "next/headers";
const token = await getToken(cookies());
// 设置 Token
setToken({
token: "eyJhbGciOiJIUzI1NiIs...",
expiresIn: 3600,
});
// 清除 Token
clearToken();
RSA 加密工具。
import { encrypt } from "@meta-1/web-common/utils/crypto";
const publicKey = "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----";
const encrypted = encrypt("sensitive-data", publicKey);
环境检测和工具函数。
import {
isServer,
isMobile,
getRootDomain,
} from "@meta-1/web-common/utils/env";
// 判断是否在服务端
if (isServer) {
// 服务端代码
}
// 判断是否为移动设备
if (isMobile()) {
// 移动端代码
}
// 获取根域名(用于跨子域 Cookie)
const rootDomain = getRootDomain("app.example.com");
// => ".example.com"
数据格式化工具。
import { formatFileSize } from "@meta-1/web-common/utils/format";
const size = formatFileSize(1024 * 1024 * 5);
// => "5 MB"
常用正则表达式。
import {
EMAIL,
URL,
USERNAME,
PASSWORD,
CODE,
PHONE,
NUMBER,
} from "@meta-1/web-common/utils/regular";
EMAIL.test("user@example.com"); // true
PHONE.test("13800138000"); // true
PASSWORD.test("Abc123!@#"); // true
URL 处理工具。
import { getPathname } from "@meta-1/web-common/utils/url";
const pathname = getPathname("/api/users?page=1");
// => "/api/users"
React Query 客户端和预取工具。
import {
getQueryClient,
prefetchQuery,
dehydrate,
HydrationBoundary,
} from "@meta-1/web-common/utils/query";
import { get } from "@meta-1/web-common/utils/rest";
// 获取 QueryClient(服务端每次新建,客户端复用)
const queryClient = getQueryClient();
// 预取数据(服务端)
await prefetchQuery(queryClient, {
queryKey: ["users"],
queryFn: async () => {
return await get<User[]>("/api/users");
},
});
// 在组件中使用
export default function Page() {
const queryClient = getQueryClient();
const dehydratedState = dehydrate(queryClient);
return (
<HydrationBoundary state={dehydratedState}>
<UserList />
</HydrationBoundary>
);
}
客户端和服务端请求头生成工具。
// 客户端
import { get } from "@meta-1/web-common/utils/headers.client";
const headers = await get();
// 包含: Accept-Language, CLIENT-PLATFORM-TYPE, Authorization 等
// 服务端
import { get } from "@meta-1/web-common/utils/headers.server";
const headers = await get();
// 包含: Accept-Language, CLIENT-PLATFORM-TYPE, Authorization 等
i18next 初始化工具。
import { initI18next } from "@meta-1/web-common/utils/i18next";
await initI18next({
"zh-cn": {
translation: {
welcome: "欢迎",
},
},
"en": {
translation: {
welcome: "Welcome",
},
},
});
服务端获取主题配置。
import { getTheme } from "@meta-1/web-common/utils/theme.server";
const theme = await getTheme();
// => "light" | "dark" | "system"
数据格式化工具。
import { join } from "@meta-1/web-common/formatters";
const result = join(["a", "b", "c"], ", ");
// => "a, b, c"
使用 Jotai 管理的全局状态。
import {
localeState,
publicKeyState,
} from "@meta-1/web-common/state/public";
import { useAtom, useAtomValue } from "jotai";
function MyComponent() {
const [locale, setLocale] = useAtom(localeState);
const publicKey = useAtomValue(publicKeyState);
return <div>当前语言: {locale}</div>;
}
import type {
RestResult,
PageData,
PageResult,
PageRequest,
} from "@meta-1/web-common/types/rest";
type User = {
id: number;
name: string;
};
const result: RestResult<User> = {
success: true,
code: 200,
message: "成功",
data: {
id: 1,
name: "John",
},
};
const pageResult: PageResult<User> = {
success: true,
code: 200,
data: {
total: 100,
data: [/* users */],
},
};
import type { Token } from "@meta-1/web-common/types/token";
const token: Token = {
token: "eyJhbGciOiJIUzI1NiIs...",
expiresIn: 3600,
};
全局事件总线,基于 EventEmitter3。
import eventBus from "@meta-1/web-common/events";
// 监听事件
eventBus.on("user-login", (data) => {
console.log("用户登录", data);
});
// 触发事件
eventBus.emit("user-login", { userId: 123 });
// 移除监听
eventBus.off("user-login", handler);
import {
getBaseI18nextConfig,
FALLBACK,
MISSING_KEY_PREFIX,
} from "@meta-1/web-common/config/i18next";
const config = getBaseI18nextConfig(resources);
useQuery - REST API 查询 HookuseMutation - REST API 变更 HookuseTableLoader - 表格数据加载 HookuseLocale - 国际化语言切换 HookuseSchema - Zod Schema 国际化 HookuseEncrypt - RSA 加密 HooklocaleState - 语言状态 AtompublicKeyState - 公钥状态 AtomRestResult, PageResult, PageRequest - REST API 类型Token - Token 类型eventBus - 全局事件总线join - 数组连接工具MIT
FAQs
Meta-1 common
We found that @meta-1/web-common demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.