A library of JavaScript tools
npm install @cc-heart/utilsimport{capitalize}from'@cc-heart/utils'capitalize('string')// Stringimport{Request}from'@cc-heart/utils'importtype{RequestInterceptor}from'@cc-heart/utils'Prefer small focused instances over one instance with all interceptors. Combine them with factory functions:
// ── Building blocks: interceptors are pure functions ──constaddAuth: RequestInterceptor=(config)=>({
...config,headers: { ...config.headers,Authorization: `Bearer ${getToken()}`}})constaddLang: RequestInterceptor=(config)=>({
...config,headers: { ...config.headers,'Accept-Language': 'zh-CN'}})consthandleError=(err: unknown)=>{toast.error(err)returnerr}// ── Compose: each instance handles one concern ──constauthApi=newRequest('https://api.example.com')authApi.useRequestInterceptor(addAuth)authApi.useRequestInterceptor(addLang)authApi.useErrorInterceptor(handleError)constpublicApi=newRequest('https://open.api.com')// ── Or use helper functions ──functionwithInterceptors(req: Request,interceptors: RequestInterceptor[]): Request{interceptors.forEach((i)=>req.useRequestInterceptor(i))returnreq}functionwithBaseUrl(url: string): Request{returnnewRequest(url)}constapi=withInterceptors(withBaseUrl('https://api.example.com'),[addAuth,addLang,])constapi=newRequest('https://api.example.com')// Style 1: async/await (recommended)try{constuser=awaitapi.get<User>('/users/1')setUser(user)}catch(e){if((easError).name==='AbortError')return// user cancelledtoast.error(e)}// Style 2: lifecycle callbacks (React setState friendly)api.get('/users',{onSuccess: setUsers,onError: toast.error,onFinally: ()=>setLoading(false),})// Style 3: promise chainingapi.get<number>('/count').then(n=>n*2).then(setCount).catch(toast.error)// Style 4: mixed (await + callbacks, non-conflicting)constdata=awaitapi.get('/users',{onFinally: ()=>setLoading(false)})// entities/user.tsconstapi=newRequest('/api')exportconstUserApi={list: (page: number)=>api.get<User[]>('/users',{ page }),get: (id: number)=>api.get<User>(`/users/${id}`),create: (data: CreateUserDto)=>api.post<User>('/users',data,{onSuccess: ()=>toast.success('created')}),}// Usageconstusers=awaitUserApi.list(1)constcachedApi=newRequest('/api')// cache and dedup are instance-level, different Request instances are isolatedconstdata1=awaitcachedApi.get('/users',{},{cache: {ttl: 5000}})constdata2=awaitcachedApi.get('/users',{},{cache: {ttl: 5000}})// cache hitconstotherApi=newRequest('/api')// isolated cacheSupports SSE streaming requests, built on Fetch API with these advantages over native EventSource:
- ✅ Custom Headers support
- ✅ POST requests support
- ✅ All HTTP methods supported
import{Request}from'@cc-heart/utils'constapi=newRequest('https://api.example.com')// GET SSEconsthandle=api.sse('/events',{onMessage(event){console.log('Received:',event.data)},onOpen(){console.log('Connection opened')},onError(error){console.error('Connection error:',error)},onClose(){console.log('Connection closed')}})// Cancel connectionhandle.abort()consthandle=api.sse('/chat/completions',{method: 'POST',data: {prompt: 'Hello',model: 'gpt-4'},onMessage(event){// Parse JSON datatry{constdata=JSON.parse(event.data)console.log('AI reply:',data.content)}catch{console.log('Raw data:',event.data)}},onError(err){console.error('Request failed:',err)}})importtype{RequestInterceptor}from'@cc-heart/utils'constaddAuth: RequestInterceptor=(config)=>({
...config,headers: {
...config.headers,Authorization: `Bearer ${getToken()}`}})constapi=newRequest('https://api.example.com')api.useRequestInterceptor(addAuth)// SSE requests automatically include interceptor headersconsthandle=api.sse('/protected/events',{onMessage(event){console.log(event.data)}})interfaceSSEMessageEvent{event?: string// Event typedata: string// Message dataid?: string// Last event IDretry?: number// Retry interval (ms)}interfaceSSECallbacks{onMessage?: (event: SSEMessageEvent)=>voidonOpen?: ()=>voidonError?: (error: unknown)=>voidonClose?: ()=>void}@cc-heart/utils is licensed under the MIT License.