소스 정보
- 저장소
- thiagofernandes1987-create/APEX
- 최근 소스 활동
- 2026년 7월 21일 11:53
- 감지된 SKILL.md 언어
- 영어
- 스타
- 2
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/thiagofernandes1987-create/APEX --skill fp-ts-react명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Token-aware reasoning workflow with real tools: picks an operating mode to control cost, runs a structured pipeline (decompose → validate → verify → snapshot), and gives Claude Program-of-Thought, RK4/Euler, a code gate, and a safe skill router. Use when: multi-step or high-stakes tasks, real math, precise computation, audits, or the user mentions APEX, PoT, pipeline, or scientific mode.
**v00.33.0**: Ingested from antigravity-awesome-skills community repo
run multiple local CLI agents in parallel (separate tmux sessions)
SOC 직업 분류 기준
SKILL.md 표시 중
| name | fp-ts-react |
| description | "When building React apps with fp-ts for type-safe state management" |
Practical patterns for React apps. No jargon, just code that works.
| Pattern | Use When |
|---|---|
Option | Value might be missing (user not loaded yet) |
Either | Operation might fail (form validation) |
TaskEither | Async operation might fail (API calls) |
RemoteData | Need to show loading/error/success states |
pipe | Chaining multiple transformations |
Use Option instead of null | undefined for clearer intent.
import { useState } from 'react'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/function'
interface User {
id: string
name: string
email: string
}
function UserProfile() {
// Option says "this might not exist yet"
const [user, setUser] = useState<O.Option<User>>(O.none)
const handleLogin = (userData: User) => {
setUser(O.some(userData))
}
const handleLogout = () => {
setUser(O.none)
}
return pipe(
user,
O.match(
// When there's no user
() => <button onClick={() => handleLogin({ id: '1', name: 'Alice', email: 'alice@example.com' })}>
Log In
</button>,
// When there's a user
(u) => (
<div>
<p>Welcome, {u.name}!</p>
<button onClick={handleLogout}>Log Out</button>
</div>
)
)
)
}
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/function'
interface Profile {
user: O.Option<{
name: string
settings: O.Option<{
theme: string
}>
}>
}
function getTheme(profile: Profile): string {
return pipe(
profile.user,
O.flatMap(u => u.settings),
O.map(s => s.theme),
O.getOrElse(() => 'light') // default
)
}
Either is perfect for validation: Left = errors, Right = valid data.
import * as E from 'fp-ts/Either'
import * as A from 'fp-ts/Array'
import { pipe } from 'fp-ts/function'
// Validation functions return Either<ErrorMessage, ValidValue>
const validateEmail = (email: string): E.Either<string, string> =>
email.includes('@')
? E.right(email)
: E.left('Invalid email address')
const validatePassword = (password: string): E.Either<string, string> =>
password.length >= 8
? E.right(password)
: E.left('Password must be at least 8 characters')
const validateName = (name: string): E.Either<string, string> =>
name.trim().length > 0
? E.right(name.trim())
: E.left('Name is required')
import * as E from 'fp-ts/Either'
import { sequenceS } from 'fp-ts/Apply'
import { getSemigroup } from 'fp-ts/NonEmptyArray'
import { pipe } from 'fp-ts/function'
// This collects ALL errors, not just the first one
const validateAll = sequenceS(E.getApplicativeValidation(getSemigroup<string>()))
interface SignupForm {
name: string
email: string
password: string
}
interface ValidatedForm {
name: string
email: string
password: string
}
function validateForm(form: SignupForm): E.Either<string[], ValidatedForm> {
return pipe(
validateAll({
name: pipe(validateName(form.name), E.mapLeft(e => [e])),
: ((form.), E.( [e])),
: ((form.), E.( [e])),
})
)
}
() {
[form, setForm] = ({ : , : , : })
[errors, setErrors] = useState<[]>([])
= () => {
(
(form),
E.(
(errs),
{
([])
(valid)
}
)
)
}
(
)
}
type FieldErrors = Partial<Record<keyof SignupForm, string>>
function validateFormWithFieldErrors(form: SignupForm): E.Either<FieldErrors, ValidatedForm> {
const errors: FieldErrors = {}
pipe(validateName(form.name), E.mapLeft(e => { errors.name = e }))
pipe(validateEmail(form.email), E.mapLeft(e => { errors.email = e }))
pipe(validatePassword(form.password), E.mapLeft(e => { errors.password = e }))
return Object.keys(errors).length > 0
? E.left(errors)
: E.right({ name: form.name.trim(), email: form.email, password: form.password })
}
{errors. && }
TaskEither = async operation that might fail. Perfect for API calls.
import { useState, useEffect } from 'react'
import * as TE from 'fp-ts/TaskEither'
import * as E from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
// Wrap fetch in TaskEither
const fetchJson = <T>(url: string): TE.TaskEither<Error, T> =>
TE.tryCatch(
async () => {
const res = await fetch(url)
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.json()
},
(err) => err instanceof Error ? err : new Error(String(err))
)
// Custom hook
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null)
const [error, setError] = useState< | >()
[loading, setLoading] = ()
( {
()
()
(
fetchJson<T>(url),
.(
{
(err)
()
},
{
(result)
()
}
)
)()
}, [url])
{ data, error, loading }
}
() {
{ data, error, loading } = useFetch<[]>()
(loading)
(error)
(
)
}
// Fetch user, then fetch their posts
const fetchUserWithPosts = (userId: string) => pipe(
fetchJson<User>(`/api/users/${userId}`),
TE.flatMap(user => pipe(
fetchJson<Post[]>(`/api/users/${userId}/posts`),
TE.map(posts => ({ ...user, posts }))
))
)
import { sequenceT } from 'fp-ts/Apply'
// Fetch multiple things at once
const fetchDashboardData = () => pipe(
sequenceT(TE.ApplyPar)(
fetchJson<User>('/api/user'),
fetchJson<Stats>('/api/stats'),
fetchJson<Notifications[]>('/api/notifications')
),
TE.map(([user, stats, notifications]) => ({
user,
stats,
notifications
}))
)
Stop using { data, loading, error } booleans. Use a proper state machine.
// RemoteData has exactly 4 states - no impossible combinations
type RemoteData<E, A> =
| { _tag: 'NotAsked' } // Haven't started yet
| { _tag: 'Loading' } // In progress
| { _tag: 'Failure'; error: E } // Failed
| { _tag: 'Success'; data: A } // Got it!
// Constructors
const notAsked = <E, A>(): RemoteData<E, A> => ({ _tag: 'NotAsked' })
const loading = <E, A>(): RemoteData<E, A> => ({ _tag: 'Loading' })
const failure = <E, A>(error: E): RemoteData<E, A> => ({ _tag: 'Failure', error })
const success = <E, A>(data: A): RemoteData<E, A> => ({ _tag: 'Success', data })
// Pattern match all states
function fold<E, A, R>(
rd: RemoteData<E, A>,
onNotAsked: () => R,
onLoading: () => R,
onFailure: (e: E) => R,
onSuccess: () => R
): R {
(rd.) {
: ()
: ()
: (rd.)
: (rd.)
}
}
function useRemoteData<T>(fetchFn: () => Promise<T>) {
const [state, setState] = useState<RemoteData<Error, T>>(notAsked())
const execute = async () => {
setState(loading())
try {
const data = await fetchFn()
setState(success(data))
} catch (err) {
setState(failure(err instanceof Error ? err : new Error(String(err))))
}
}
return { state, execute }
}
// Usage
function UserProfile({ userId }: { userId: string }) {
const { state, execute } = useRemoteData(() =>
fetch(`/api/users/${userId}`).then(r => r.json())
)
useEffect(() => { execute() }, [userId])
return fold(
state,
() => ,
,
,
)
}
// ❌ BAD: Impossible states are possible
interface BadState {
data: User | null
loading: boolean
error: Error | null
}
// Can have: { data: user, loading: true, error: someError } - what does that mean?!
// ✅ GOOD: Only valid states exist
type GoodState = RemoteData<Error, User>
// Can only be: NotAsked | Loading | Failure | Success
fp-ts values like O.some(1) create new objects each render. React sees them as "changed".
// ❌ BAD: Creates new Option every render
function BadComponent() {
const [value, setValue] = useState(O.some(1))
useEffect(() => {
// This runs EVERY render because O.some(1) !== O.some(1)
console.log('value changed')
}, [value])
}
// ✅ GOOD: Memoize Option creation
function GoodComponent() {
const [rawValue, setRawValue] = useState<number | null>(1)
const value = useMemo(
() => O.fromNullable(rawValue),
[rawValue] // Only recreate when rawValue changes
)
useEffect(() => {
// Now this only runs when rawValue actually changes
console.log('value changed')
}, [rawValue]) // Depend on raw value, not Option
}
npm install fp-ts-react-stable-hooks
import { useStableO, useStableEffect } from 'fp-ts-react-stable-hooks'
import * as O from 'fp-ts/Option'
import * as Eq from 'fp-ts/Eq'
function StableComponent() {
// Uses fp-ts equality instead of reference equality
const [value, setValue] = useStableO(O.some(1))
// Effect that understands Option equality
useStableEffect(
() => { console.log('value changed') },
[value],
Eq.tuple(O.getEq(Eq.eqNumber)) // Custom equality
)
}
Use ReaderTaskEither for testable components with injected dependencies.
import * as RTE from 'fp-ts/ReaderTaskEither'
import { pipe } from 'fp-ts/function'
import { createContext, useContext, ReactNode } from 'react'
// Define what services your app needs
interface AppDependencies {
api: {
getUser: (id: string) => Promise<User>
updateUser: (id: string, data: Partial<User>) => Promise<User>
}
analytics: {
track: (event: string, data?: object) => void
}
}
// Create context
const DepsContext = createContext<AppDependencies | null>(null)
// Provider
function AppProvider({ deps, children }: { deps: AppDependencies; children: ReactNode }) {
return
}
(): {
deps = ()
(!deps) ()
deps
}
function UserProfile({ userId }: { userId: string }) {
const { api, analytics } = useDeps()
const [user, setUser] = useState<RemoteData<Error, User>>(notAsked())
useEffect(() => {
setUser(loading())
api.getUser(userId)
.then(u => {
setUser(success(u))
analytics.track('user_viewed', { userId })
})
.catch(e => setUser(failure(e)))
}, [userId, api, analytics])
// render...
}
const mockDeps: AppDependencies = {
api: {
getUser: jest.fn().mockResolvedValue({ id: '1', name: 'Test User' }),
updateUser: jest.fn().mockResolvedValue({ id: '1', name: 'Updated' }),
},
analytics: {
track: jest.fn(),
},
}
test('loads user on mount', async () => {
render(
<AppProvider deps={mockDeps}>
<UserProfile userId="1" />
</AppProvider>
)
await screen.findByText('Test User')
expect(mockDeps.api.getUser).toHaveBeenCalledWith('1')
})
import { use, Suspense } from 'react'
// Instead of useEffect + useState for data fetching
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise) // Suspends until resolved
return <div>{user.name}</div>
}
// Parent provides the promise
function App() {
const userPromise = fetchUser('1') // Start fetching immediately
return (
<Suspense fallback={<Spinner />}>
<UserProfile userPromise={userPromise} />
</Suspense>
)
}
import { useActionState } from 'react'
import * as E from 'fp-ts/Either'
interface FormState {
errors: string[]
success: boolean
}
async function submitForm(
prevState: FormState,
formData: FormData
): Promise<FormState> {
const data = {
email: formData.get('email') as string,
password: formData.get('password') as string,
}
// Use Either for validation
const result = pipe(
validateForm(data),
E.match(
(errors) => ({ errors, success: false }),
async (valid) => {
await saveToServer(valid)
return { errors: [], success: true }
}
)
)
return result
}
function () {
[state, formAction, isPending] = (submitForm, {
: [],
:
})
(
)
}
import { useOptimistic } from 'react'
function TodoList({ todos }: { todos: Todo[] }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo: Todo) => [...state, { ...newTodo, pending: true }]
)
const addTodo = async (text: string) => {
const newTodo = { id: crypto.randomUUID(), text, done: false }
// Immediately show in UI
addOptimisticTodo(newTodo)
// Actually save (will reconcile when done)
await saveTodo(newTodo)
}
return (
<ul>
{optimisticTodos.map(todo => (
<li key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
{todo.text}
</li>
))}
</ul>
)
}
// Pattern 1: match
pipe(
maybeUser,
O.match(
() => <LoginButton />,
(user) => <UserMenu user={user} />
)
)
// Pattern 2: fold (same as match)
O.fold(
() => <LoginButton />,
(user) => <UserMenu user={user} />
)(maybeUser)
// Pattern 3: getOrElse for simple defaults
const name = pipe(
maybeUser,
O.map(u => u.name),
O.getOrElse(() => 'Guest')
)
pipe(
validationResult,
E.match(
(errors) => <ErrorList errors={errors} />,
(data) => <SuccessMessage data={data} />
)
)
import * as A from 'fp-ts/Array'
// Get first item safely
const firstUser = pipe(
users,
A.head,
O.map(user => <Featured user={user} />),
O.getOrElse(() => <NoFeaturedUser />)
)
// Find specific item
const adminUser = pipe(
users,
A.findFirst(u => u.role === 'admin'),
O.map(admin => <AdminBadge user={admin} />),
O.toNullable // or O.getOrElse(() => null)
)
// Add props only if value exists
const modalProps = {
isOpen: true,
...pipe(
maybeTitle,
O.map(title => ({ title })),
O.getOrElse(() => ({}))
)
}
| Situation | Use |
|---|---|
| Value might not exist | Option<T> |
| Operation might fail (sync) | Either<E, A> |
| Async operation might fail | TaskEither<E, A> |
| Need loading/error/success UI | RemoteData<E, A> |
| Form with multiple validations | Either with validation applicative |
| Dependency injection | Context + ReaderTaskEither |
| Prevent re-renders with fp-ts | useMemo or fp-ts-react-stable-hooks |
Implement —