functype-react 0.1.0 → 0.60.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/async/TaskBoundary.tsx"],"sourcesContent":["\"use client\"\n\nimport { Component, type ErrorInfo, type ReactElement, type ReactNode, Suspense } from \"react\"\n\ntype Props = {\n readonly pending: ReactNode\n readonly fallback: (error: unknown, reset: () => void) => ReactNode\n readonly children: ReactNode\n}\n\ntype State = { readonly _tag: \"Ok\" } | { readonly _tag: \"Errored\"; readonly error: unknown }\n\nclass TaskErrorBoundary extends Component<\n { readonly fallback: Props[\"fallback\"]; readonly children: ReactNode },\n State\n> {\n override state: State = { _tag: \"Ok\" }\n\n static getDerivedStateFromError(error: unknown): State {\n return { _tag: \"Errored\", error }\n }\n\n override componentDidCatch(_error: unknown, _info: ErrorInfo): void {\n // hook for telemetry; intentionally noop in v0.1\n }\n\n reset = (): void => {\n this.setState({ _tag: \"Ok\" })\n }\n\n override render(): ReactNode {\n if (this.state._tag === \"Errored\") {\n return this.props.fallback(this.state.error, this.reset)\n }\n return this.props.children\n }\n}\n\n/**\n * Combines `<Suspense>` (for pending Tasks consumed via `useTaskValue`) with\n * an ErrorBoundary that catches thrown failures. The `fallback` render prop\n * receives the thrown value (typed `unknown` — consumers narrow) and a\n * `reset` callback that clears the error so children can be re-attempted.\n *\n * The ErrorBoundary wraps the Suspense, matching React's documented rule\n * (otherwise Suspense would catch errors instead of the boundary).\n */\nexport function TaskBoundary(props: Props): ReactElement {\n return (\n <TaskErrorBoundary fallback={props.fallback}>\n <Suspense fallback={props.pending}>{props.children}</Suspense>\n </TaskErrorBoundary>\n )\n}\n"],"mappings":"qOAYA,IAAM,EAAN,cAAgC,CAG9B,+BACA,KAAS,MAAe,CAAE,KAAM,KAAM,CAUtC,KAAA,UAAoB,CAClB,KAAK,SAAS,CAAE,KAAM,KAAM,CAAC,EAT/B,OAAO,yBAAyB,EAAuB,CACrD,MAAO,CAAE,KAAM,UAAW,QAAO,CAGnC,kBAA2B,EAAiB,EAAwB,EAQpE,QAA6B,CAI3B,OAHI,KAAK,MAAM,OAAS,UACf,KAAK,MAAM,SAAS,KAAK,MAAM,MAAO,KAAK,MAAM,CAEnD,KAAK,MAAM,WAatB,SAAgB,EAAa,EAA4B,CACvD,OACE,EAAC,EAAD,CAAmB,SAAU,EAAM,kBACjC,EAAC,EAAD,CAAU,SAAU,EAAM,iBAAU,EAAM,SAAoB,CAAA,CAC5C,CAAA"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/async/TaskBoundary.tsx"],"sourcesContent":["\"use client\"\n\nimport { Component, type ErrorInfo, type ReactElement, type ReactNode, Suspense } from \"react\"\n\ntype Props = {\n readonly pending: ReactNode\n readonly fallback: (error: unknown, reset: () => void) => ReactNode\n readonly children: ReactNode\n}\n\ntype State = { readonly _tag: \"Ok\" } | { readonly _tag: \"Errored\"; readonly error: unknown }\n\nclass TaskErrorBoundary extends Component<\n { readonly fallback: Props[\"fallback\"]; readonly children: ReactNode },\n State\n> {\n override state: State = { _tag: \"Ok\" }\n\n static getDerivedStateFromError(error: unknown): State {\n return { _tag: \"Errored\", error }\n }\n\n override componentDidCatch(_error: unknown, _info: ErrorInfo): void {\n // hook for telemetry; intentionally noop in v0.1\n }\n\n reset = (): void => {\n this.setState({ _tag: \"Ok\" })\n }\n\n override render(): ReactNode {\n if (this.state._tag === \"Errored\") {\n return this.props.fallback(this.state.error, this.reset)\n }\n return this.props.children\n }\n}\n\n/**\n * Combines `<Suspense>` (for pending Tasks consumed via `useTaskValue`) with\n * an ErrorBoundary that catches thrown failures. The `fallback` render prop\n * receives the thrown value (typed `unknown` — consumers narrow) and a\n * `reset` callback that clears the error so children can be re-attempted.\n *\n * The ErrorBoundary wraps the Suspense, matching React's documented rule\n * (otherwise Suspense would catch errors instead of the boundary).\n */\nexport function TaskBoundary(props: Props): ReactElement {\n return (\n <TaskErrorBoundary fallback={props.fallback}>\n <Suspense fallback={props.pending}>{props.children}</Suspense>\n </TaskErrorBoundary>\n )\n}\n"],"mappings":"qOAYA,IAAM,EAAN,cAAgC,CAG9B,+BACA,KAAS,MAAe,CAAE,KAAM,IAAK,EAUrC,KAAA,UAAoB,CAClB,KAAK,SAAS,CAAE,KAAM,IAAK,CAAC,CAC9B,EAVA,OAAO,yBAAyB,EAAuB,CACrD,MAAO,CAAE,KAAM,UAAW,OAAM,CAClC,CAEA,kBAA2B,EAAiB,EAAwB,CAEpE,CAMA,QAA6B,CAI3B,OAHI,KAAK,MAAM,OAAS,UACf,KAAK,MAAM,SAAS,KAAK,MAAM,MAAO,KAAK,KAAK,EAElD,KAAK,MAAM,QACpB,CACF,EAWA,SAAgB,EAAa,EAA4B,CACvD,OACE,EAAC,EAAD,CAAmB,SAAU,EAAM,kBACjC,EAAC,EAAD,CAAU,SAAU,EAAM,iBAAU,EAAM,QAAmB,CAAA,CAC5C,CAAA,CAEvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"useTask.js","names":[],"sources":["../../src/async/useTask.ts"],"sourcesContent":["\"use client\"\n\nimport { createCancellationTokenSource, Task, type Throwable } from \"functype\"\nimport { useCallback, useEffect, useReducer, useRef } from \"react\"\n\nimport type { TaskState } from \"./TaskState\"\n\ntype Action<E, A> =\n | { readonly type: \"PENDING\" }\n | { readonly type: \"SUCCESS\"; readonly value: A }\n | { readonly type: \"FAILURE\"; readonly error: E }\n\nfunction reducer<E, A>(_state: TaskState<E, A>, action: Action<E, A>): TaskState<E, A> {\n if (action.type === \"PENDING\") return { _tag: \"Pending\" }\n if (action.type === \"SUCCESS\") return { _tag: \"Success\", value: action.value }\n return { _tag: \"Failure\", error: action.error }\n}\n\nexport type UseTaskResult<E, A> = TaskState<E, A> & {\n readonly isIdle: boolean\n readonly isPending: boolean\n readonly isSuccess: boolean\n readonly isFailure: boolean\n refetch: () => void\n}\n\n/**\n * Run an async operation tied to a React component's lifecycle.\n *\n * - Returns a discriminated `TaskState<Throwable, A>` plus boolean flags and\n * a `refetch` trigger.\n * - The `task` callback receives an `AbortSignal` wired to a cancellation\n * token that fires when the component unmounts or `deps` change. Pass the\n * signal to `fetch` (or any abortable API) to cancel in-flight work.\n * - StrictMode-safe: the cleanup fn cancels the token and discards any\n * late-arriving result.\n */\nexport function useTask<A>(\n task: (signal: AbortSignal) => Promise<A> | A,\n deps: ReadonlyArray<unknown>,\n): UseTaskResult<Throwable, A> {\n const [state, dispatch] = useReducer(reducer<Throwable, A>, { _tag: \"Idle\" })\n const [refetchTick, forceRefetch] = useReducer((n: number) => n + 1, 0)\n const taskRef = useRef(task)\n taskRef.current = task\n\n useEffect(() => {\n const cancelled = { value: false }\n const tokenSource = createCancellationTokenSource()\n dispatch({ type: \"PENDING\" })\n\n void Task<A>()\n .Async<A>(() => taskRef.current(tokenSource.token.signal) as Promise<A>, undefined, undefined, tokenSource.token)\n .then((outcome) => {\n if (cancelled.value) return\n if (outcome.isOk()) {\n dispatch({ type: \"SUCCESS\", value: outcome.value as A })\n } else {\n dispatch({ type: \"FAILURE\", error: outcome.error as Throwable })\n }\n })\n\n return () => {\n cancelled.value = true\n tokenSource.cancel()\n }\n }, [...deps, refetchTick])\n\n const refetch = useCallback(() => forceRefetch(), [])\n\n return {\n ...state,\n isIdle: state._tag === \"Idle\",\n isPending: state._tag === \"Pending\",\n isSuccess: state._tag === \"Success\",\n isFailure: state._tag === \"Failure\",\n refetch,\n }\n}\n"],"mappings":"+JAYA,SAAS,EAAc,EAAyB,EAAuC,CAGrF,OAFI,EAAO,OAAS,UAAkB,CAAE,KAAM,UAAW,CACrD,EAAO,OAAS,UAAkB,CAAE,KAAM,UAAW,MAAO,EAAO,MAAO,CACvE,CAAE,KAAM,UAAW,MAAO,EAAO,MAAO,CAsBjD,SAAgB,EACd,EACA,EAC6B,CAC7B,GAAM,CAAC,EAAO,GAAY,EAAW,EAAuB,CAAE,KAAM,OAAQ,CAAC,CACvE,CAAC,EAAa,GAAgB,EAAY,GAAc,EAAI,EAAG,EAAE,CACjE,EAAU,EAAO,EAAK,CAC5B,EAAQ,QAAU,EAElB,MAAgB,CACd,IAAM,EAAY,CAAE,MAAO,GAAO,CAC5B,EAAc,GAA+B,CAcnD,OAbA,EAAS,CAAE,KAAM,UAAW,CAAC,CAE7B,GAAc,CACX,UAAe,EAAQ,QAAQ,EAAY,MAAM,OAAO,CAAgB,IAAA,GAAW,IAAA,GAAW,EAAY,MAAM,CAChH,KAAM,GAAY,CACb,EAAU,QACV,EAAQ,MAAM,CAChB,EAAS,CAAE,KAAM,UAAW,MAAO,EAAQ,MAAY,CAAC,CAExD,EAAS,CAAE,KAAM,UAAW,MAAO,EAAQ,MAAoB,CAAC,GAElE,KAES,CACX,EAAU,MAAQ,GAClB,EAAY,QAAQ,GAErB,CAAC,GAAG,EAAM,EAAY,CAAC,CAE1B,IAAM,EAAU,MAAkB,GAAc,CAAE,EAAE,CAAC,CAErD,MAAO,CACL,GAAG,EACH,OAAQ,EAAM,OAAS,OACvB,UAAW,EAAM,OAAS,UAC1B,UAAW,EAAM,OAAS,UAC1B,UAAW,EAAM,OAAS,UAC1B,UACD"}
1
+ {"version":3,"file":"useTask.js","names":[],"sources":["../../src/async/useTask.ts"],"sourcesContent":["\"use client\"\n\nimport { createCancellationTokenSource, Task, type Throwable } from \"functype\"\nimport { useCallback, useEffect, useReducer, useRef } from \"react\"\n\nimport type { TaskState } from \"./TaskState\"\n\ntype Action<E, A> =\n | { readonly type: \"PENDING\" }\n | { readonly type: \"SUCCESS\"; readonly value: A }\n | { readonly type: \"FAILURE\"; readonly error: E }\n\nfunction reducer<E, A>(_state: TaskState<E, A>, action: Action<E, A>): TaskState<E, A> {\n if (action.type === \"PENDING\") return { _tag: \"Pending\" }\n if (action.type === \"SUCCESS\") return { _tag: \"Success\", value: action.value }\n return { _tag: \"Failure\", error: action.error }\n}\n\nexport type UseTaskResult<E, A> = TaskState<E, A> & {\n readonly isIdle: boolean\n readonly isPending: boolean\n readonly isSuccess: boolean\n readonly isFailure: boolean\n refetch: () => void\n}\n\n/**\n * Run an async operation tied to a React component's lifecycle.\n *\n * - Returns a discriminated `TaskState<Throwable, A>` plus boolean flags and\n * a `refetch` trigger.\n * - The `task` callback receives an `AbortSignal` wired to a cancellation\n * token that fires when the component unmounts or `deps` change. Pass the\n * signal to `fetch` (or any abortable API) to cancel in-flight work.\n * - StrictMode-safe: the cleanup fn cancels the token and discards any\n * late-arriving result.\n */\nexport function useTask<A>(\n task: (signal: AbortSignal) => Promise<A> | A,\n deps: ReadonlyArray<unknown>,\n): UseTaskResult<Throwable, A> {\n const [state, dispatch] = useReducer(reducer<Throwable, A>, { _tag: \"Idle\" })\n const [refetchTick, forceRefetch] = useReducer((n: number) => n + 1, 0)\n const taskRef = useRef(task)\n taskRef.current = task\n\n useEffect(() => {\n const cancelled = { value: false }\n const tokenSource = createCancellationTokenSource()\n dispatch({ type: \"PENDING\" })\n\n void Task<A>()\n .Async<A>(() => taskRef.current(tokenSource.token.signal) as Promise<A>, undefined, undefined, tokenSource.token)\n .then((outcome) => {\n if (cancelled.value) return\n if (outcome.isOk()) {\n dispatch({ type: \"SUCCESS\", value: outcome.value as A })\n } else {\n dispatch({ type: \"FAILURE\", error: outcome.error as Throwable })\n }\n })\n\n return () => {\n cancelled.value = true\n tokenSource.cancel()\n }\n }, [...deps, refetchTick])\n\n const refetch = useCallback(() => forceRefetch(), [])\n\n return {\n ...state,\n isIdle: state._tag === \"Idle\",\n isPending: state._tag === \"Pending\",\n isSuccess: state._tag === \"Success\",\n isFailure: state._tag === \"Failure\",\n refetch,\n }\n}\n"],"mappings":"+JAYA,SAAS,EAAc,EAAyB,EAAuC,CAGrF,OAFI,EAAO,OAAS,UAAkB,CAAE,KAAM,SAAU,EACpD,EAAO,OAAS,UAAkB,CAAE,KAAM,UAAW,MAAO,EAAO,KAAM,EACtE,CAAE,KAAM,UAAW,MAAO,EAAO,KAAM,CAChD,CAqBA,SAAgB,EACd,EACA,EAC6B,CAC7B,GAAM,CAAC,EAAO,GAAY,EAAW,EAAuB,CAAE,KAAM,MAAO,CAAC,EACtE,CAAC,EAAa,GAAgB,EAAY,GAAc,EAAI,EAAG,CAAC,EAChE,EAAU,EAAO,CAAI,EAC3B,EAAQ,QAAU,EAElB,MAAgB,CACd,IAAM,EAAY,CAAE,MAAO,EAAM,EAC3B,EAAc,EAA8B,EAclD,OAbA,EAAS,CAAE,KAAM,SAAU,CAAC,EAE5B,EAAa,EACV,UAAe,EAAQ,QAAQ,EAAY,MAAM,MAAM,EAAiB,IAAA,GAAW,IAAA,GAAW,EAAY,KAAK,EAC/G,KAAM,GAAY,CACb,EAAU,QACV,EAAQ,KAAK,EACf,EAAS,CAAE,KAAM,UAAW,MAAO,EAAQ,KAAW,CAAC,EAEvD,EAAS,CAAE,KAAM,UAAW,MAAO,EAAQ,KAAmB,CAAC,EAEnE,CAAC,MAEU,CACX,EAAU,MAAQ,GAClB,EAAY,OAAO,CACrB,CACF,EAAG,CAAC,GAAG,EAAM,CAAW,CAAC,EAEzB,IAAM,EAAU,MAAkB,EAAa,EAAG,CAAC,CAAC,EAEpD,MAAO,CACL,GAAG,EACH,OAAQ,EAAM,OAAS,OACvB,UAAW,EAAM,OAAS,UAC1B,UAAW,EAAM,OAAS,UAC1B,UAAW,EAAM,OAAS,UAC1B,SACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useTaskPromise.js","names":[],"sources":["../../src/async/useTaskPromise.ts"],"sourcesContent":["\"use client\"\n\nimport { createCancellationTokenSource, Task, type TaskOutcome } from \"functype\"\nimport { useMemo, useRef } from \"react\"\n\n/**\n * Returns a `Promise<TaskOutcome<A>>` that is stable across renders with the\n * same `deps`. Intended for consumers of React 19's `use()` hook — providing\n * a new promise reference on each render would infinite-suspend.\n *\n * The `task` callback is read through a ref, so the latest closure is invoked\n * even if it isn't included in `deps`. Encode in `deps` whatever semantically\n * changes the task's result.\n */\nexport function useTaskPromise<A>(\n task: (signal: AbortSignal) => Promise<A> | A,\n deps: ReadonlyArray<unknown>,\n): Promise<TaskOutcome<A>> {\n const taskRef = useRef(task)\n taskRef.current = task\n\n return useMemo(() => {\n const tokenSource = createCancellationTokenSource()\n return Task<A>().Async<A>(\n () => taskRef.current(tokenSource.token.signal) as Promise<A>,\n undefined,\n undefined,\n tokenSource.token,\n )\n }, deps)\n}\n"],"mappings":"4HAcA,SAAgB,EACd,EACA,EACyB,CACzB,IAAM,EAAU,EAAO,EAAK,CAG5B,MAFA,GAAQ,QAAU,EAEX,MAAc,CACnB,IAAM,EAAc,GAA+B,CACnD,OAAO,GAAS,CAAC,UACT,EAAQ,QAAQ,EAAY,MAAM,OAAO,CAC/C,IAAA,GACA,IAAA,GACA,EAAY,MACb,EACA,EAAK"}
1
+ {"version":3,"file":"useTaskPromise.js","names":[],"sources":["../../src/async/useTaskPromise.ts"],"sourcesContent":["\"use client\"\n\nimport { createCancellationTokenSource, Task, type TaskOutcome } from \"functype\"\nimport { useMemo, useRef } from \"react\"\n\n/**\n * Returns a `Promise<TaskOutcome<A>>` that is stable across renders with the\n * same `deps`. Intended for consumers of React 19's `use()` hook — providing\n * a new promise reference on each render would infinite-suspend.\n *\n * The `task` callback is read through a ref, so the latest closure is invoked\n * even if it isn't included in `deps`. Encode in `deps` whatever semantically\n * changes the task's result.\n */\nexport function useTaskPromise<A>(\n task: (signal: AbortSignal) => Promise<A> | A,\n deps: ReadonlyArray<unknown>,\n): Promise<TaskOutcome<A>> {\n const taskRef = useRef(task)\n taskRef.current = task\n\n return useMemo(() => {\n const tokenSource = createCancellationTokenSource()\n return Task<A>().Async<A>(\n () => taskRef.current(tokenSource.token.signal) as Promise<A>,\n undefined,\n undefined,\n tokenSource.token,\n )\n }, deps)\n}\n"],"mappings":"4HAcA,SAAgB,EACd,EACA,EACyB,CACzB,IAAM,EAAU,EAAO,CAAI,EAG3B,MAFA,GAAQ,QAAU,EAEX,MAAc,CACnB,IAAM,EAAc,EAA8B,EAClD,OAAO,EAAQ,EAAE,UACT,EAAQ,QAAQ,EAAY,MAAM,MAAM,EAC9C,IAAA,GACA,IAAA,GACA,EAAY,KACd,CACF,EAAG,CAAI,CACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"useTaskValue.js","names":[],"sources":["../../src/async/useTaskValue.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-either -- the throw is the contract: React's `use()` semantics require errors to propagate to the nearest ErrorBoundary via throw, not via an Either return. */\n\nimport { use } from \"react\"\n\nimport { useTaskPromise } from \"./useTaskPromise\"\n\n/**\n * Suspense-aware variant of `useTask` for React 19+. Suspends the component\n * while the task is pending and throws on failure — both behaviors integrate\n * with `<TaskBoundary>` (or a hand-rolled Suspense + ErrorBoundary pair).\n *\n * Invariants enforced by React 19:\n * 1. The promise must be stable across renders. `useTaskPromise` memoizes by\n * `deps`, so passing the same deps yields the same promise.\n * 2. An ErrorBoundary must wrap the Suspense, never the reverse — otherwise\n * Suspense will catch the thrown error instead of the boundary.\n * 3. Do not call this on the server. Pass the underlying `Task` (or its\n * promise) into a Client Component and call `useTaskValue` there.\n *\n * Testing note: React 19's `use()` does not unsuspend reliably under jsdom +\n * @testing-library/react. End-to-end tests of `useTaskValue` + `<TaskBoundary>`\n * require a real browser scheduler (Playwright); unit tests of `useTask` and\n * `useTaskPromise` cover the Task plumbing.\n */\nexport function useTaskValue<A>(task: (signal: AbortSignal) => Promise<A> | A, deps: ReadonlyArray<unknown>): A {\n const outcome = use(useTaskPromise(task, deps))\n if (outcome.isErr()) {\n throw outcome.error\n }\n return outcome.value as A\n}\n"],"mappings":"8FAyBA,SAAgB,EAAgB,EAA+C,EAAiC,CAC9G,IAAM,EAAU,EAAI,EAAe,EAAM,EAAK,CAAC,CAC/C,GAAI,EAAQ,OAAO,CACjB,MAAM,EAAQ,MAEhB,OAAO,EAAQ"}
1
+ {"version":3,"file":"useTaskValue.js","names":[],"sources":["../../src/async/useTaskValue.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-either -- the throw is the contract: React's `use()` semantics require errors to propagate to the nearest ErrorBoundary via throw, not via an Either return. */\n\nimport { use } from \"react\"\n\nimport { useTaskPromise } from \"./useTaskPromise\"\n\n/**\n * Suspense-aware variant of `useTask` for React 19+. Suspends the component\n * while the task is pending and throws on failure — both behaviors integrate\n * with `<TaskBoundary>` (or a hand-rolled Suspense + ErrorBoundary pair).\n *\n * Invariants enforced by React 19:\n * 1. The promise must be stable across renders. `useTaskPromise` memoizes by\n * `deps`, so passing the same deps yields the same promise.\n * 2. An ErrorBoundary must wrap the Suspense, never the reverse — otherwise\n * Suspense will catch the thrown error instead of the boundary.\n * 3. Do not call this on the server. Pass the underlying `Task` (or its\n * promise) into a Client Component and call `useTaskValue` there.\n *\n * Testing note: React 19's `use()` does not unsuspend reliably under jsdom +\n * @testing-library/react. End-to-end tests of `useTaskValue` + `<TaskBoundary>`\n * require a real browser scheduler (Playwright); unit tests of `useTask` and\n * `useTaskPromise` cover the Task plumbing.\n */\nexport function useTaskValue<A>(task: (signal: AbortSignal) => Promise<A> | A, deps: ReadonlyArray<unknown>): A {\n const outcome = use(useTaskPromise(task, deps))\n if (outcome.isErr()) {\n throw outcome.error\n }\n return outcome.value as A\n}\n"],"mappings":"8FAyBA,SAAgB,EAAgB,EAA+C,EAAiC,CAC9G,IAAM,EAAU,EAAI,EAAe,EAAM,CAAI,CAAC,EAC9C,GAAI,EAAQ,MAAM,EAChB,MAAM,EAAQ,MAEhB,OAAO,EAAQ,KACjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"Validated.js","names":[],"sources":["../../src/forms/Validated.ts"],"sourcesContent":["import { type Either, Left, List, type List as ListT, Right } from \"functype\"\n\n/**\n * Applicative-error-accumulating result for form validation.\n *\n * Encoded as `Either<List<E>, A>` — the same shape Scala's cats uses for\n * `ValidatedNel`. Multiple field errors accumulate via `List.concat`; on\n * success, the validated value rides in the `Right` channel.\n *\n * functype core already exposes this pattern via `FormValidation<T>` in\n * `packages/functype/src/error/typed/Validation.ts`; this alias just gives\n * the React-facing surface an ergonomic name.\n */\nexport type Validated<E, A> = Either<ListT<E>, A>\n\n/** Construct a `Valid` (Right) result. */\nexport function valid<A>(value: A): Validated<never, A> {\n return Right<ListT<never>, A>(value)\n}\n\n/** Construct an `Invalid` (Left) result from an iterable of errors. */\nexport function invalid<E>(errors: ListT<E> | ReadonlyArray<E>): Validated<E, never> {\n const list = errors instanceof Array ? List<E>(errors) : (errors as ListT<E>)\n return Left<ListT<E>, never>(list)\n}\n"],"mappings":"qDAgBA,SAAgB,EAAS,EAA+B,CACtD,OAAO,EAAuB,EAAM,CAItC,SAAgB,EAAW,EAA0D,CAEnF,OAAO,EADM,aAAkB,MAAQ,EAAQ,EAAO,CAAI,EACxB"}
1
+ {"version":3,"file":"Validated.js","names":[],"sources":["../../src/forms/Validated.ts"],"sourcesContent":["import { type Either, Left, List, type List as ListT, Right } from \"functype\"\n\n/**\n * Applicative-error-accumulating result for form validation.\n *\n * Encoded as `Either<List<E>, A>` — the same shape Scala's cats uses for\n * `ValidatedNel`. Multiple field errors accumulate via `List.concat`; on\n * success, the validated value rides in the `Right` channel.\n *\n * functype core already exposes this pattern via `FormValidation<T>` in\n * `packages/functype/src/error/typed/Validation.ts`; this alias just gives\n * the React-facing surface an ergonomic name.\n */\nexport type Validated<E, A> = Either<ListT<E>, A>\n\n/** Construct a `Valid` (Right) result. */\nexport function valid<A>(value: A): Validated<never, A> {\n return Right<ListT<never>, A>(value)\n}\n\n/** Construct an `Invalid` (Left) result from an iterable of errors. */\nexport function invalid<E>(errors: ListT<E> | ReadonlyArray<E>): Validated<E, never> {\n const list = errors instanceof Array ? List<E>(errors) : (errors as ListT<E>)\n return Left<ListT<E>, never>(list)\n}\n"],"mappings":"qDAgBA,SAAgB,EAAS,EAA+B,CACtD,OAAO,EAAuB,CAAK,CACrC,CAGA,SAAgB,EAAW,EAA0D,CAEnF,OAAO,EADM,aAAkB,MAAQ,EAAQ,CAAM,EAAK,CACzB,CACnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useValidatedField.js","names":[],"sources":["../../src/forms/useValidatedField.ts"],"sourcesContent":["\"use client\"\n\nimport { List, type List as ListT } from \"functype\"\nimport { type ChangeEvent, useCallback, useMemo, useState } from \"react\"\n\nimport type { Validated } from \"./Validated\"\n\n/**\n * Single-field state container with continuously-derived validation.\n *\n * `validation` is re-derived from `validate(value)` on every render — keep\n * `validate` cheap or memoize the callback.\n *\n * `bind()` returns props ready to spread onto a native `<input>`. The\n * generic `A` may be any value type; `bind` is most useful when `A` is\n * string-shaped. For non-string fields, wire `value`/`setValue` directly.\n */\nexport function useValidatedField<A, E = string>(opts: {\n readonly initial: A\n readonly validate: (a: A) => Validated<E, A>\n}): {\n readonly value: A\n setValue: (a: A) => void\n readonly validation: Validated<E, A>\n readonly errors: ListT<E>\n readonly isValid: boolean\n bind: () => { value: A; onChange: (e: ChangeEvent<HTMLInputElement>) => void }\n} {\n const [value, setValue] = useState<A>(opts.initial)\n\n const validation = useMemo(() => opts.validate(value), [opts, value])\n const errors = useMemo(\n () =>\n validation.fold(\n (es) => es,\n () => List.empty<E>(),\n ),\n [validation],\n )\n const isValid = validation.isRight()\n\n const bind = useCallback(\n () => ({\n value,\n onChange: (e: ChangeEvent<HTMLInputElement>) => setValue(e.target.value as unknown as A),\n }),\n [value],\n )\n\n return { value, setValue, validation, errors, isValid, bind }\n}\n"],"mappings":"4GAiBA,SAAgB,EAAiC,EAU/C,CACA,GAAM,CAAC,EAAO,GAAY,EAAY,EAAK,QAAQ,CAE7C,EAAa,MAAc,EAAK,SAAS,EAAM,CAAE,CAAC,EAAM,EAAM,CAAC,CAmBrE,MAAO,CAAE,QAAO,WAAU,aAAY,OAlBvB,MAEX,EAAW,KACR,GAAO,MACF,EAAK,OAAU,CACtB,CACH,CAAC,EAAW,CAY8B,CAAE,QAV9B,EAAW,SAU0B,CAAE,KAR1C,OACJ,CACL,QACA,SAAW,GAAqC,EAAS,EAAE,OAAO,MAAsB,CACzF,EACD,CAAC,EAAM,CAGkD,CAAE"}
1
+ {"version":3,"file":"useValidatedField.js","names":[],"sources":["../../src/forms/useValidatedField.ts"],"sourcesContent":["\"use client\"\n\nimport { List, type List as ListT } from \"functype\"\nimport { type ChangeEvent, useCallback, useMemo, useState } from \"react\"\n\nimport type { Validated } from \"./Validated\"\n\n/**\n * Single-field state container with continuously-derived validation.\n *\n * `validation` is re-derived from `validate(value)` on every render — keep\n * `validate` cheap or memoize the callback.\n *\n * `bind()` returns props ready to spread onto a native `<input>`. The\n * generic `A` may be any value type; `bind` is most useful when `A` is\n * string-shaped. For non-string fields, wire `value`/`setValue` directly.\n */\nexport function useValidatedField<A, E = string>(opts: {\n readonly initial: A\n readonly validate: (a: A) => Validated<E, A>\n}): {\n readonly value: A\n setValue: (a: A) => void\n readonly validation: Validated<E, A>\n readonly errors: ListT<E>\n readonly isValid: boolean\n bind: () => { value: A; onChange: (e: ChangeEvent<HTMLInputElement>) => void }\n} {\n const [value, setValue] = useState<A>(opts.initial)\n\n const validation = useMemo(() => opts.validate(value), [opts, value])\n const errors = useMemo(\n () =>\n validation.fold(\n (es) => es,\n () => List.empty<E>(),\n ),\n [validation],\n )\n const isValid = validation.isRight()\n\n const bind = useCallback(\n () => ({\n value,\n onChange: (e: ChangeEvent<HTMLInputElement>) => setValue(e.target.value as unknown as A),\n }),\n [value],\n )\n\n return { value, setValue, validation, errors, isValid, bind }\n}\n"],"mappings":"4GAiBA,SAAgB,EAAiC,EAU/C,CACA,GAAM,CAAC,EAAO,GAAY,EAAY,EAAK,OAAO,EAE5C,EAAa,MAAc,EAAK,SAAS,CAAK,EAAG,CAAC,EAAM,CAAK,CAAC,EAmBpE,MAAO,CAAE,QAAO,WAAU,aAAY,OAlBvB,MAEX,EAAW,KACR,GAAO,MACF,EAAK,MAAS,CACtB,EACF,CAAC,CAAU,CAY8B,EAAG,QAV9B,EAAW,QAUyB,EAAG,KAR1C,OACJ,CACL,QACA,SAAW,GAAqC,EAAS,EAAE,OAAO,KAAqB,CACzF,GACA,CAAC,CAAK,CAGkD,CAAE,CAC9D"}
@@ -1 +1 @@
1
- {"version":3,"file":"useValidatedForm.js","names":[],"sources":["../../src/forms/useValidatedForm.ts"],"sourcesContent":["\"use client\"\n\nimport { List, type List as ListT } from \"functype\"\nimport { type FormEvent, useCallback, useMemo, useState } from \"react\"\n\nimport type { Validated } from \"./Validated\"\n\n/**\n * Form-level state container with applicative error accumulation.\n *\n * The consumer-provided `validate` is the canonical place to accumulate\n * field-level errors. A common pattern:\n *\n * ```ts\n * const validate = (s: Form): Validated<string, Form> => {\n * const errs = List<string>([])\n * .concat(s.email.includes(\"@\") ? List([]) : List([\"email must contain @\"]))\n * .concat(s.age >= 18 ? List([]) : List([\"age must be 18+\"]))\n * return errs.isEmpty ? valid(s) : invalid(errs)\n * }\n * ```\n *\n * `handleSubmit(onValid)` returns an event handler that calls `e.preventDefault()`\n * and only invokes `onValid` when the current form value passes validation.\n */\nexport function useValidatedForm<S extends Record<string, unknown>, E = string>(opts: {\n readonly initial: S\n readonly validate: (s: S) => Validated<E, S>\n}): {\n readonly values: S\n setField: <K extends keyof S>(key: K, value: S[K]) => void\n readonly validation: Validated<E, S>\n readonly errors: ListT<E>\n readonly isValid: boolean\n reset: () => void\n handleSubmit: (onValid: (s: S) => void | Promise<void>) => (e: FormEvent) => void\n} {\n const [values, setValues] = useState<S>(opts.initial)\n\n const setField = useCallback(<K extends keyof S>(key: K, value: S[K]) => {\n setValues((prev) => ({ ...prev, [key]: value }))\n }, [])\n\n const reset = useCallback(() => setValues(opts.initial), [opts.initial])\n\n const validation = useMemo(() => opts.validate(values), [opts, values])\n const errors = useMemo(\n () =>\n validation.fold(\n (es) => es,\n () => List.empty<E>(),\n ),\n [validation],\n )\n const isValid = validation.isRight()\n\n const handleSubmit = useCallback(\n (onValid: (s: S) => void | Promise<void>) => (e: FormEvent) => {\n e.preventDefault()\n validation.fold(\n () => undefined,\n (s) => {\n void onValid(s)\n },\n )\n },\n [validation],\n )\n\n return { values, setField, validation, errors, isValid, reset, handleSubmit }\n}\n"],"mappings":"4GAyBA,SAAgB,EAAgE,EAW9E,CACA,GAAM,CAAC,EAAQ,GAAa,EAAY,EAAK,QAAQ,CAE/C,EAAW,GAAgC,EAAQ,IAAgB,CACvE,EAAW,IAAU,CAAE,GAAG,GAAO,GAAM,EAAO,EAAE,EAC/C,EAAE,CAAC,CAEA,EAAQ,MAAkB,EAAU,EAAK,QAAQ,CAAE,CAAC,EAAK,QAAQ,CAAC,CAElE,EAAa,MAAc,EAAK,SAAS,EAAO,CAAE,CAAC,EAAM,EAAO,CAAC,CAwBvE,MAAO,CAAE,SAAQ,WAAU,aAAY,OAvBxB,MAEX,EAAW,KACR,GAAO,MACF,EAAK,OAAU,CACtB,CACH,CAAC,EAAW,CAiB+B,CAAE,QAf/B,EAAW,SAe2B,CAAE,QAAO,aAb1C,EAClB,GAA6C,GAAiB,CAC7D,EAAE,gBAAgB,CAClB,EAAW,SACH,IAAA,GACL,GAAM,CACL,EAAa,EAAE,EAElB,EAEH,CAAC,EAAW,CAG6D,CAAE"}
1
+ {"version":3,"file":"useValidatedForm.js","names":[],"sources":["../../src/forms/useValidatedForm.ts"],"sourcesContent":["\"use client\"\n\nimport { List, type List as ListT } from \"functype\"\nimport { type FormEvent, useCallback, useMemo, useState } from \"react\"\n\nimport type { Validated } from \"./Validated\"\n\n/**\n * Form-level state container with applicative error accumulation.\n *\n * The consumer-provided `validate` is the canonical place to accumulate\n * field-level errors. A common pattern:\n *\n * ```ts\n * const validate = (s: Form): Validated<string, Form> => {\n * const errs = List<string>([])\n * .concat(s.email.includes(\"@\") ? List([]) : List([\"email must contain @\"]))\n * .concat(s.age >= 18 ? List([]) : List([\"age must be 18+\"]))\n * return errs.isEmpty ? valid(s) : invalid(errs)\n * }\n * ```\n *\n * `handleSubmit(onValid)` returns an event handler that calls `e.preventDefault()`\n * and only invokes `onValid` when the current form value passes validation.\n */\nexport function useValidatedForm<S extends Record<string, unknown>, E = string>(opts: {\n readonly initial: S\n readonly validate: (s: S) => Validated<E, S>\n}): {\n readonly values: S\n setField: <K extends keyof S>(key: K, value: S[K]) => void\n readonly validation: Validated<E, S>\n readonly errors: ListT<E>\n readonly isValid: boolean\n reset: () => void\n handleSubmit: (onValid: (s: S) => void | Promise<void>) => (e: FormEvent) => void\n} {\n const [values, setValues] = useState<S>(opts.initial)\n\n const setField = useCallback(<K extends keyof S>(key: K, value: S[K]) => {\n setValues((prev) => ({ ...prev, [key]: value }))\n }, [])\n\n const reset = useCallback(() => setValues(opts.initial), [opts.initial])\n\n const validation = useMemo(() => opts.validate(values), [opts, values])\n const errors = useMemo(\n () =>\n validation.fold(\n (es) => es,\n () => List.empty<E>(),\n ),\n [validation],\n )\n const isValid = validation.isRight()\n\n const handleSubmit = useCallback(\n (onValid: (s: S) => void | Promise<void>) => (e: FormEvent) => {\n e.preventDefault()\n validation.fold(\n () => undefined,\n (s) => {\n void onValid(s)\n },\n )\n },\n [validation],\n )\n\n return { values, setField, validation, errors, isValid, reset, handleSubmit }\n}\n"],"mappings":"4GAyBA,SAAgB,EAAgE,EAW9E,CACA,GAAM,CAAC,EAAQ,GAAa,EAAY,EAAK,OAAO,EAE9C,EAAW,GAAgC,EAAQ,IAAgB,CACvE,EAAW,IAAU,CAAE,GAAG,GAAO,GAAM,CAAM,EAAE,CACjD,EAAG,CAAC,CAAC,EAEC,EAAQ,MAAkB,EAAU,EAAK,OAAO,EAAG,CAAC,EAAK,OAAO,CAAC,EAEjE,EAAa,MAAc,EAAK,SAAS,CAAM,EAAG,CAAC,EAAM,CAAM,CAAC,EAwBtE,MAAO,CAAE,SAAQ,WAAU,aAAY,OAvBxB,MAEX,EAAW,KACR,GAAO,MACF,EAAK,MAAS,CACtB,EACF,CAAC,CAAU,CAiB+B,EAAG,QAf/B,EAAW,QAe0B,EAAG,QAAO,aAb1C,EAClB,GAA6C,GAAiB,CAC7D,EAAE,eAAe,EACjB,EAAW,SACH,IAAA,GACL,GAAM,CACL,EAAa,CAAC,CAChB,CACF,CACF,EACA,CAAC,CAAU,CAG6D,CAAE,CAC9E"}
@@ -1 +1 @@
1
- {"version":3,"file":"eq.js","names":[],"sources":["../../src/hooks/eq.ts"],"sourcesContent":["/**\n * Equality comparators for use with `useStable*` hooks.\n *\n * functype core doesn't ship an Eq typeclass; instead, comparators are passed\n * explicitly. The default everywhere is `referenceEq` (Object.is), matching\n * React's built-in dependency comparison.\n */\n\nexport type Eq<A> = (a: A, b: A) => boolean\n\n/**\n * Reference equality, identical to React's default `Object.is` for hook deps.\n */\nexport const referenceEq: Eq<unknown> = Object.is\n\n/**\n * Compares two functype ADTs by their `_tag` literal only.\n *\n * Useful when you only care about variant changes (e.g., re-render when an\n * `Option` flips between Some and None, but not on payload changes). Returns\n * false for non-tagged values.\n */\nexport const tagEq: Eq<unknown> = (a, b) => {\n if (Object.is(a, b)) return true\n if (typeof a !== \"object\" || a === null) return false\n if (typeof b !== \"object\" || b === null) return false\n const ta = (a as { _tag?: unknown })._tag\n const tb = (b as { _tag?: unknown })._tag\n return ta !== undefined && ta === tb\n}\n\n/**\n * Recursive structural equality. Handles primitives, arrays, plain objects, and\n * tagged ADTs. Cycles are not detected — passing cyclic structures is undefined\n * behavior. Functions compare by reference.\n */\nexport const structuralEq: Eq<unknown> = (a, b) => {\n if (Object.is(a, b)) return true\n if (typeof a !== typeof b) return false\n if (a === null || b === null) return false\n if (typeof a !== \"object\") return false\n\n if (Array.isArray(a)) {\n if (!Array.isArray(b) || a.length !== b.length) return false\n return a.every((item, i) => structuralEq(item, b[i]))\n }\n if (Array.isArray(b)) return false\n\n const ka = Object.keys(a as object)\n const kb = Object.keys(b as object)\n if (ka.length !== kb.length) return false\n return ka.every(\n (k) =>\n Object.prototype.hasOwnProperty.call(b, k) &&\n structuralEq((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]),\n )\n}\n"],"mappings":"AAaA,MAAa,EAA2B,OAAO,GASlC,GAAsB,EAAG,IAAM,CAC1C,GAAI,OAAO,GAAG,EAAG,EAAE,CAAE,MAAO,GAE5B,GADI,OAAO,GAAM,WAAY,GACzB,OAAO,GAAM,WAAY,EAAY,MAAO,GAChD,IAAM,EAAM,EAAyB,KAC/B,EAAM,EAAyB,KACrC,OAAO,IAAO,IAAA,IAAa,IAAO,GAQvB,GAA6B,EAAG,IAAM,CACjD,GAAI,OAAO,GAAG,EAAG,EAAE,CAAE,MAAO,GAG5B,GAFI,OAAO,GAAM,OAAO,GACpB,IAAM,MAAQ,IAAM,MACpB,OAAO,GAAM,SAAU,MAAO,GAElC,GAAI,MAAM,QAAQ,EAAE,CAElB,MADI,CAAC,MAAM,QAAQ,EAAE,EAAI,EAAE,SAAW,EAAE,OAAe,GAChD,EAAE,OAAO,EAAM,IAAM,EAAa,EAAM,EAAE,GAAG,CAAC,CAEvD,GAAI,MAAM,QAAQ,EAAE,CAAE,MAAO,GAE7B,IAAM,EAAK,OAAO,KAAK,EAAY,CAC7B,EAAK,OAAO,KAAK,EAAY,CAEnC,OADI,EAAG,SAAW,EAAG,OACd,EAAG,MACP,GACC,OAAO,UAAU,eAAe,KAAK,EAAG,EAAE,EAC1C,EAAc,EAA8B,GAAK,EAA8B,GAAG,CACrF,CALmC"}
1
+ {"version":3,"file":"eq.js","names":[],"sources":["../../src/hooks/eq.ts"],"sourcesContent":["/**\n * Equality comparators for use with `useStable*` hooks.\n *\n * functype core doesn't ship an Eq typeclass; instead, comparators are passed\n * explicitly. The default everywhere is `referenceEq` (Object.is), matching\n * React's built-in dependency comparison.\n */\n\nexport type Eq<A> = (a: A, b: A) => boolean\n\n/**\n * Reference equality, identical to React's default `Object.is` for hook deps.\n */\nexport const referenceEq: Eq<unknown> = Object.is\n\n/**\n * Compares two functype ADTs by their `_tag` literal only.\n *\n * Useful when you only care about variant changes (e.g., re-render when an\n * `Option` flips between Some and None, but not on payload changes). Returns\n * false for non-tagged values.\n */\nexport const tagEq: Eq<unknown> = (a, b) => {\n if (Object.is(a, b)) return true\n if (typeof a !== \"object\" || a === null) return false\n if (typeof b !== \"object\" || b === null) return false\n const ta = (a as { _tag?: unknown })._tag\n const tb = (b as { _tag?: unknown })._tag\n return ta !== undefined && ta === tb\n}\n\n/**\n * Recursive structural equality. Handles primitives, arrays, plain objects, and\n * tagged ADTs. Cycles are not detected — passing cyclic structures is undefined\n * behavior. Functions compare by reference.\n */\nexport const structuralEq: Eq<unknown> = (a, b) => {\n if (Object.is(a, b)) return true\n if (typeof a !== typeof b) return false\n if (a === null || b === null) return false\n if (typeof a !== \"object\") return false\n\n if (Array.isArray(a)) {\n if (!Array.isArray(b) || a.length !== b.length) return false\n return a.every((item, i) => structuralEq(item, b[i]))\n }\n if (Array.isArray(b)) return false\n\n const ka = Object.keys(a as object)\n const kb = Object.keys(b as object)\n if (ka.length !== kb.length) return false\n return ka.every(\n (k) =>\n Object.prototype.hasOwnProperty.call(b, k) &&\n structuralEq((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]),\n )\n}\n"],"mappings":"AAaA,MAAa,EAA2B,OAAO,GASlC,GAAsB,EAAG,IAAM,CAC1C,GAAI,OAAO,GAAG,EAAG,CAAC,EAAG,MAAO,GAE5B,GADI,OAAO,GAAM,WAAY,GACzB,OAAO,GAAM,WAAY,EAAY,MAAO,GAChD,IAAM,EAAM,EAAyB,KAC/B,EAAM,EAAyB,KACrC,OAAO,IAAO,IAAA,IAAa,IAAO,CACpC,EAOa,GAA6B,EAAG,IAAM,CACjD,GAAI,OAAO,GAAG,EAAG,CAAC,EAAG,MAAO,GAG5B,GAFI,OAAO,GAAM,OAAO,GACpB,IAAM,MAAQ,IAAM,MACpB,OAAO,GAAM,SAAU,MAAO,GAElC,GAAI,MAAM,QAAQ,CAAC,EAEjB,MADI,CAAC,MAAM,QAAQ,CAAC,GAAK,EAAE,SAAW,EAAE,OAAe,GAChD,EAAE,OAAO,EAAM,IAAM,EAAa,EAAM,EAAE,EAAE,CAAC,EAEtD,GAAI,MAAM,QAAQ,CAAC,EAAG,MAAO,GAE7B,IAAM,EAAK,OAAO,KAAK,CAAW,EAC5B,EAAK,OAAO,KAAK,CAAW,EAElC,OADI,EAAG,SAAW,EAAG,OACd,EAAG,MACP,GACC,OAAO,UAAU,eAAe,KAAK,EAAG,CAAC,GACzC,EAAc,EAA8B,GAAK,EAA8B,EAAE,CACrF,EALoC,EAMtC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useEither.js","names":[],"sources":["../../src/hooks/useEither.ts"],"sourcesContent":["\"use client\"\n\nimport { type Either, Left, Right } from \"functype/either\"\nimport { useCallback, useMemo, useState } from \"react\"\n\n/**\n * Stateful Either container. Defaults to Left(undefined) when no initial.\n */\nexport function useEither<E, A>(\n initial?: Either<E, A>,\n): {\n readonly value: Either<E, A>\n setRight: (a: A) => void\n setLeft: (e: E) => void\n fold: <R>(onLeft: (e: E) => R, onRight: (a: A) => R) => R\n} {\n const [value, setValue] = useState<Either<E, A>>(() => initial ?? Left<E, A>(undefined as unknown as E))\n\n const setRight = useCallback((a: A) => setValue(Right<E, A>(a)), [])\n const setLeft = useCallback((e: E) => setValue(Left<E, A>(e)), [])\n\n const fold = useCallback(<R>(onLeft: (e: E) => R, onRight: (a: A) => R) => value.fold(onLeft, onRight), [value])\n\n return useMemo(() => ({ value, setRight, setLeft, fold }), [value, setRight, setLeft, fold])\n}\n"],"mappings":"8HAQA,SAAgB,EACd,EAMA,CACA,GAAM,CAAC,EAAO,GAAY,MAA6B,GAAW,EAAW,IAAA,GAA0B,CAAC,CAElG,EAAW,EAAa,GAAS,EAAS,EAAY,EAAE,CAAC,CAAE,EAAE,CAAC,CAC9D,EAAU,EAAa,GAAS,EAAS,EAAW,EAAE,CAAC,CAAE,EAAE,CAAC,CAE5D,EAAO,GAAgB,EAAqB,IAAyB,EAAM,KAAK,EAAQ,EAAQ,CAAE,CAAC,EAAM,CAAC,CAEhH,OAAO,OAAe,CAAE,QAAO,WAAU,UAAS,OAAM,EAAG,CAAC,EAAO,EAAU,EAAS,EAAK,CAAC"}
1
+ {"version":3,"file":"useEither.js","names":[],"sources":["../../src/hooks/useEither.ts"],"sourcesContent":["\"use client\"\n\nimport { type Either, Left, Right } from \"functype/either\"\nimport { useCallback, useMemo, useState } from \"react\"\n\n/**\n * Stateful Either container. Defaults to Left(undefined) when no initial.\n */\nexport function useEither<E, A>(\n initial?: Either<E, A>,\n): {\n readonly value: Either<E, A>\n setRight: (a: A) => void\n setLeft: (e: E) => void\n fold: <R>(onLeft: (e: E) => R, onRight: (a: A) => R) => R\n} {\n const [value, setValue] = useState<Either<E, A>>(() => initial ?? Left<E, A>(undefined as unknown as E))\n\n const setRight = useCallback((a: A) => setValue(Right<E, A>(a)), [])\n const setLeft = useCallback((e: E) => setValue(Left<E, A>(e)), [])\n\n const fold = useCallback(<R>(onLeft: (e: E) => R, onRight: (a: A) => R) => value.fold(onLeft, onRight), [value])\n\n return useMemo(() => ({ value, setRight, setLeft, fold }), [value, setRight, setLeft, fold])\n}\n"],"mappings":"8HAQA,SAAgB,EACd,EAMA,CACA,GAAM,CAAC,EAAO,GAAY,MAA6B,GAAW,EAAW,IAAA,EAAyB,CAAC,EAEjG,EAAW,EAAa,GAAS,EAAS,EAAY,CAAC,CAAC,EAAG,CAAC,CAAC,EAC7D,EAAU,EAAa,GAAS,EAAS,EAAW,CAAC,CAAC,EAAG,CAAC,CAAC,EAE3D,EAAO,GAAgB,EAAqB,IAAyB,EAAM,KAAK,EAAQ,CAAO,EAAG,CAAC,CAAK,CAAC,EAE/G,OAAO,OAAe,CAAE,QAAO,WAAU,UAAS,MAAK,GAAI,CAAC,EAAO,EAAU,EAAS,CAAI,CAAC,CAC7F"}
@@ -1 +1 @@
1
- {"version":3,"file":"useList.js","names":[],"sources":["../../src/hooks/useList.ts"],"sourcesContent":["\"use client\"\n\nimport { List, type List as ListT } from \"functype/list\"\nimport { useCallback, useMemo, useState } from \"react\"\n\n/**\n * Stateful immutable List container. All mutators return new state.\n */\nexport function useList<A>(initial?: readonly A[]): {\n readonly value: ListT<A>\n add: (a: A) => void\n remove: (a: A) => void\n removeAt: (index: number) => void\n clear: () => void\n map: <B>(f: (a: A) => B) => ListT<B>\n filter: (p: (a: A) => boolean) => ListT<A>\n} {\n const [value, setValue] = useState<ListT<A>>(() => List<A>(initial ?? []))\n\n const add = useCallback((a: A) => setValue((prev) => prev.add(a) as ListT<A>), [])\n const remove = useCallback((a: A) => setValue((prev) => prev.remove(a)), [])\n const removeAt = useCallback((index: number) => setValue((prev) => prev.removeAt(index)), [])\n const clear = useCallback(() => setValue(List.empty<A>()), [])\n\n const map = useCallback(<B>(f: (a: A) => B) => value.map(f), [value])\n const filter = useCallback((p: (a: A) => boolean) => value.filter(p), [value])\n\n return useMemo(\n () => ({ value, add, remove, removeAt, clear, map, filter }),\n [value, add, remove, removeAt, clear, map, filter],\n )\n}\n"],"mappings":"iHAQA,SAAgB,EAAW,EAQzB,CACA,GAAM,CAAC,EAAO,GAAY,MAAyB,EAAQ,GAAW,EAAE,CAAC,CAAC,CAEpE,EAAM,EAAa,GAAS,EAAU,GAAS,EAAK,IAAI,EAAE,CAAa,CAAE,EAAE,CAAC,CAC5E,EAAS,EAAa,GAAS,EAAU,GAAS,EAAK,OAAO,EAAE,CAAC,CAAE,EAAE,CAAC,CACtE,EAAW,EAAa,GAAkB,EAAU,GAAS,EAAK,SAAS,EAAM,CAAC,CAAE,EAAE,CAAC,CACvF,EAAQ,MAAkB,EAAS,EAAK,OAAU,CAAC,CAAE,EAAE,CAAC,CAExD,EAAM,EAAgB,GAAmB,EAAM,IAAI,EAAE,CAAE,CAAC,EAAM,CAAC,CAC/D,EAAS,EAAa,GAAyB,EAAM,OAAO,EAAE,CAAE,CAAC,EAAM,CAAC,CAE9E,OAAO,OACE,CAAE,QAAO,MAAK,SAAQ,WAAU,QAAO,MAAK,SAAQ,EAC3D,CAAC,EAAO,EAAK,EAAQ,EAAU,EAAO,EAAK,EAAO,CACnD"}
1
+ {"version":3,"file":"useList.js","names":[],"sources":["../../src/hooks/useList.ts"],"sourcesContent":["\"use client\"\n\nimport { List, type List as ListT } from \"functype/list\"\nimport { useCallback, useMemo, useState } from \"react\"\n\n/**\n * Stateful immutable List container. All mutators return new state.\n */\nexport function useList<A>(initial?: readonly A[]): {\n readonly value: ListT<A>\n add: (a: A) => void\n remove: (a: A) => void\n removeAt: (index: number) => void\n clear: () => void\n map: <B>(f: (a: A) => B) => ListT<B>\n filter: (p: (a: A) => boolean) => ListT<A>\n} {\n const [value, setValue] = useState<ListT<A>>(() => List<A>(initial ?? []))\n\n const add = useCallback((a: A) => setValue((prev) => prev.add(a) as ListT<A>), [])\n const remove = useCallback((a: A) => setValue((prev) => prev.remove(a)), [])\n const removeAt = useCallback((index: number) => setValue((prev) => prev.removeAt(index)), [])\n const clear = useCallback(() => setValue(List.empty<A>()), [])\n\n const map = useCallback(<B>(f: (a: A) => B) => value.map(f), [value])\n const filter = useCallback((p: (a: A) => boolean) => value.filter(p), [value])\n\n return useMemo(\n () => ({ value, add, remove, removeAt, clear, map, filter }),\n [value, add, remove, removeAt, clear, map, filter],\n )\n}\n"],"mappings":"iHAQA,SAAgB,EAAW,EAQzB,CACA,GAAM,CAAC,EAAO,GAAY,MAAyB,EAAQ,GAAW,CAAC,CAAC,CAAC,EAEnE,EAAM,EAAa,GAAS,EAAU,GAAS,EAAK,IAAI,CAAC,CAAa,EAAG,CAAC,CAAC,EAC3E,EAAS,EAAa,GAAS,EAAU,GAAS,EAAK,OAAO,CAAC,CAAC,EAAG,CAAC,CAAC,EACrE,EAAW,EAAa,GAAkB,EAAU,GAAS,EAAK,SAAS,CAAK,CAAC,EAAG,CAAC,CAAC,EACtF,EAAQ,MAAkB,EAAS,EAAK,MAAS,CAAC,EAAG,CAAC,CAAC,EAEvD,EAAM,EAAgB,GAAmB,EAAM,IAAI,CAAC,EAAG,CAAC,CAAK,CAAC,EAC9D,EAAS,EAAa,GAAyB,EAAM,OAAO,CAAC,EAAG,CAAC,CAAK,CAAC,EAE7E,OAAO,OACE,CAAE,QAAO,MAAK,SAAQ,WAAU,QAAO,MAAK,QAAO,GAC1D,CAAC,EAAO,EAAK,EAAQ,EAAU,EAAO,EAAK,CAAM,CACnD,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useOption.js","names":[],"sources":["../../src/hooks/useOption.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-option -- this hook's job is to convert nullable JS values into Option, so its inputs must be nullable. */\n\nimport { Option, type Option as OptionT } from \"functype/option\"\nimport { useCallback, useMemo, useState } from \"react\"\n\n/**\n * Stateful Option container. `set(null | undefined)` clears to None.\n */\nexport function useOption<A>(initial?: A): {\n readonly value: OptionT<A>\n set: (a: A | null | undefined) => void\n clear: () => void\n map: <B>(f: (a: A) => B) => OptionT<B>\n fold: <R>(onNone: () => R, onSome: (a: A) => R) => R\n} {\n const [value, setValue] = useState<OptionT<A>>(() => Option<A>(initial))\n\n const set = useCallback((a: A | null | undefined) => setValue(Option<A>(a)), [])\n const clear = useCallback(() => setValue(Option.none<A>()), [])\n\n const map = useCallback(<B>(f: (a: A) => B) => value.map(f), [value])\n const fold = useCallback(<R>(onNone: () => R, onSome: (a: A) => R) => value.fold(onNone, onSome), [value])\n\n return useMemo(() => ({ value, set, clear, map, fold }), [value, set, clear, map, fold])\n}\n"],"mappings":"qHASA,SAAgB,EAAa,EAM3B,CACA,GAAM,CAAC,EAAO,GAAY,MAA2B,EAAU,EAAQ,CAAC,CAElE,EAAM,EAAa,GAA4B,EAAS,EAAU,EAAE,CAAC,CAAE,EAAE,CAAC,CAC1E,EAAQ,MAAkB,EAAS,EAAO,MAAS,CAAC,CAAE,EAAE,CAAC,CAEzD,EAAM,EAAgB,GAAmB,EAAM,IAAI,EAAE,CAAE,CAAC,EAAM,CAAC,CAC/D,EAAO,GAAgB,EAAiB,IAAwB,EAAM,KAAK,EAAQ,EAAO,CAAE,CAAC,EAAM,CAAC,CAE1G,OAAO,OAAe,CAAE,QAAO,MAAK,QAAO,MAAK,OAAM,EAAG,CAAC,EAAO,EAAK,EAAO,EAAK,EAAK,CAAC"}
1
+ {"version":3,"file":"useOption.js","names":[],"sources":["../../src/hooks/useOption.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-option -- this hook's job is to convert nullable JS values into Option, so its inputs must be nullable. */\n\nimport { Option, type Option as OptionT } from \"functype/option\"\nimport { useCallback, useMemo, useState } from \"react\"\n\n/**\n * Stateful Option container. `set(null | undefined)` clears to None.\n */\nexport function useOption<A>(initial?: A): {\n readonly value: OptionT<A>\n set: (a: A | null | undefined) => void\n clear: () => void\n map: <B>(f: (a: A) => B) => OptionT<B>\n fold: <R>(onNone: () => R, onSome: (a: A) => R) => R\n} {\n const [value, setValue] = useState<OptionT<A>>(() => Option<A>(initial))\n\n const set = useCallback((a: A | null | undefined) => setValue(Option<A>(a)), [])\n const clear = useCallback(() => setValue(Option.none<A>()), [])\n\n const map = useCallback(<B>(f: (a: A) => B) => value.map(f), [value])\n const fold = useCallback(<R>(onNone: () => R, onSome: (a: A) => R) => value.fold(onNone, onSome), [value])\n\n return useMemo(() => ({ value, set, clear, map, fold }), [value, set, clear, map, fold])\n}\n"],"mappings":"qHASA,SAAgB,EAAa,EAM3B,CACA,GAAM,CAAC,EAAO,GAAY,MAA2B,EAAU,CAAO,CAAC,EAEjE,EAAM,EAAa,GAA4B,EAAS,EAAU,CAAC,CAAC,EAAG,CAAC,CAAC,EACzE,EAAQ,MAAkB,EAAS,EAAO,KAAQ,CAAC,EAAG,CAAC,CAAC,EAExD,EAAM,EAAgB,GAAmB,EAAM,IAAI,CAAC,EAAG,CAAC,CAAK,CAAC,EAC9D,EAAO,GAAgB,EAAiB,IAAwB,EAAM,KAAK,EAAQ,CAAM,EAAG,CAAC,CAAK,CAAC,EAEzG,OAAO,OAAe,CAAE,QAAO,MAAK,QAAO,MAAK,MAAK,GAAI,CAAC,EAAO,EAAK,EAAO,EAAK,CAAI,CAAC,CACzF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useStableCallback.js","names":[],"sources":["../../src/hooks/useStableCallback.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-option, functype/prefer-fold -- React hooks must accept idiomatic optional params and use ref-init sentinels; wrapping in Option would change the public API shape consumers expect. */\n\nimport { type DependencyList, useCallback, useRef } from \"react\"\n\nimport { type Eq, referenceEq } from \"./eq\"\n\n/**\n * Like `useCallback`, but only returns a new function when *some* dep has\n * changed under the supplied (or default) comparator.\n */\nexport function useStableCallback<F extends (...args: never[]) => unknown>(\n callback: F,\n deps: DependencyList,\n eqs?: ReadonlyArray<Eq<unknown> | undefined>,\n): F {\n const prev = useRef<DependencyList | null>(null)\n const tick = useRef(0)\n\n if (prev.current === null) {\n prev.current = deps\n tick.current += 1\n } else {\n const stale = prev.current\n const changed = deps.some((d, i) => {\n const cmp = eqs?.[i] ?? referenceEq\n return !cmp(stale[i], d)\n })\n if (changed) {\n prev.current = deps\n tick.current += 1\n }\n }\n\n return useCallback(callback, [tick.current]) as F\n}\n"],"mappings":"mGAWA,SAAgB,EACd,EACA,EACA,EACG,CACH,IAAM,EAAO,EAA8B,KAAK,CAC1C,EAAO,EAAO,EAAE,CAEtB,GAAI,EAAK,UAAY,KACnB,EAAK,QAAU,EACf,EAAK,SAAW,MACX,CACL,IAAM,EAAQ,EAAK,QACH,EAAK,MAAM,EAAG,IAErB,EADK,IAAM,IAAM,GACZ,EAAM,GAAI,EAAE,CAEf,GACT,EAAK,QAAU,EACf,EAAK,SAAW,GAIpB,OAAO,EAAY,EAAU,CAAC,EAAK,QAAQ,CAAC"}
1
+ {"version":3,"file":"useStableCallback.js","names":[],"sources":["../../src/hooks/useStableCallback.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-option, functype/prefer-fold -- React hooks must accept idiomatic optional params and use ref-init sentinels; wrapping in Option would change the public API shape consumers expect. */\n\nimport { type DependencyList, useCallback, useRef } from \"react\"\n\nimport { type Eq, referenceEq } from \"./eq\"\n\n/**\n * Like `useCallback`, but only returns a new function when *some* dep has\n * changed under the supplied (or default) comparator.\n */\nexport function useStableCallback<F extends (...args: never[]) => unknown>(\n callback: F,\n deps: DependencyList,\n eqs?: ReadonlyArray<Eq<unknown> | undefined>,\n): F {\n const prev = useRef<DependencyList | null>(null)\n const tick = useRef(0)\n\n if (prev.current === null) {\n prev.current = deps\n tick.current += 1\n } else {\n const stale = prev.current\n const changed = deps.some((d, i) => {\n const cmp = eqs?.[i] ?? referenceEq\n return !cmp(stale[i], d)\n })\n if (changed) {\n prev.current = deps\n tick.current += 1\n }\n }\n\n return useCallback(callback, [tick.current]) as F\n}\n"],"mappings":"mGAWA,SAAgB,EACd,EACA,EACA,EACG,CACH,IAAM,EAAO,EAA8B,IAAI,EACzC,EAAO,EAAO,CAAC,EAErB,GAAI,EAAK,UAAY,KACnB,EAAK,QAAU,EACf,EAAK,SAAW,MACX,CACL,IAAM,EAAQ,EAAK,QACH,EAAK,MAAM,EAAG,IAErB,EADK,IAAM,IAAM,GACZ,EAAM,GAAI,CAAC,CAEf,IACR,EAAK,QAAU,EACf,EAAK,SAAW,EAEpB,CAEA,OAAO,EAAY,EAAU,CAAC,EAAK,OAAO,CAAC,CAC7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"useStableEffect.js","names":[],"sources":["../../src/hooks/useStableEffect.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-option, functype/prefer-fold -- React hooks must accept idiomatic optional params and use ref-init sentinels; wrapping in Option would change the public API shape consumers expect. */\n\nimport { type DependencyList, type EffectCallback, useEffect, useRef } from \"react\"\n\nimport { type Eq, referenceEq } from \"./eq\"\n\n/**\n * Like `useEffect`, but re-runs only when *some* dep has changed under the\n * supplied (or default) comparator. The `eqs` array is aligned positionally\n * with `deps`; missing entries fall back to `referenceEq`.\n *\n * Useful when deps include functype ADTs (`Option`, `Either`, etc.) whose\n * structural identity is what matters, but whose references churn every render.\n */\nexport function useStableEffect(\n effect: EffectCallback,\n deps: DependencyList,\n eqs?: ReadonlyArray<Eq<unknown> | undefined>,\n): void {\n const prev = useRef<DependencyList | null>(null)\n const tick = useRef(0)\n\n if (prev.current === null) {\n prev.current = deps\n tick.current += 1\n } else {\n const stale = prev.current\n const changed = deps.some((d, i) => {\n const cmp = eqs?.[i] ?? referenceEq\n return !cmp(stale[i], d)\n })\n if (changed) {\n prev.current = deps\n tick.current += 1\n }\n }\n\n useEffect(effect, [tick.current])\n}\n"],"mappings":"iGAeA,SAAgB,EACd,EACA,EACA,EACM,CACN,IAAM,EAAO,EAA8B,KAAK,CAC1C,EAAO,EAAO,EAAE,CAEtB,GAAI,EAAK,UAAY,KACnB,EAAK,QAAU,EACf,EAAK,SAAW,MACX,CACL,IAAM,EAAQ,EAAK,QACH,EAAK,MAAM,EAAG,IAErB,EADK,IAAM,IAAM,GACZ,EAAM,GAAI,EAAE,CAEf,GACT,EAAK,QAAU,EACf,EAAK,SAAW,GAIpB,EAAU,EAAQ,CAAC,EAAK,QAAQ,CAAC"}
1
+ {"version":3,"file":"useStableEffect.js","names":[],"sources":["../../src/hooks/useStableEffect.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-option, functype/prefer-fold -- React hooks must accept idiomatic optional params and use ref-init sentinels; wrapping in Option would change the public API shape consumers expect. */\n\nimport { type DependencyList, type EffectCallback, useEffect, useRef } from \"react\"\n\nimport { type Eq, referenceEq } from \"./eq\"\n\n/**\n * Like `useEffect`, but re-runs only when *some* dep has changed under the\n * supplied (or default) comparator. The `eqs` array is aligned positionally\n * with `deps`; missing entries fall back to `referenceEq`.\n *\n * Useful when deps include functype ADTs (`Option`, `Either`, etc.) whose\n * structural identity is what matters, but whose references churn every render.\n */\nexport function useStableEffect(\n effect: EffectCallback,\n deps: DependencyList,\n eqs?: ReadonlyArray<Eq<unknown> | undefined>,\n): void {\n const prev = useRef<DependencyList | null>(null)\n const tick = useRef(0)\n\n if (prev.current === null) {\n prev.current = deps\n tick.current += 1\n } else {\n const stale = prev.current\n const changed = deps.some((d, i) => {\n const cmp = eqs?.[i] ?? referenceEq\n return !cmp(stale[i], d)\n })\n if (changed) {\n prev.current = deps\n tick.current += 1\n }\n }\n\n useEffect(effect, [tick.current])\n}\n"],"mappings":"iGAeA,SAAgB,EACd,EACA,EACA,EACM,CACN,IAAM,EAAO,EAA8B,IAAI,EACzC,EAAO,EAAO,CAAC,EAErB,GAAI,EAAK,UAAY,KACnB,EAAK,QAAU,EACf,EAAK,SAAW,MACX,CACL,IAAM,EAAQ,EAAK,QACH,EAAK,MAAM,EAAG,IAErB,EADK,IAAM,IAAM,GACZ,EAAM,GAAI,CAAC,CAEf,IACR,EAAK,QAAU,EACf,EAAK,SAAW,EAEpB,CAEA,EAAU,EAAQ,CAAC,EAAK,OAAO,CAAC,CAClC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useStableMemo.js","names":[],"sources":["../../src/hooks/useStableMemo.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-option, functype/prefer-fold -- React hooks must accept idiomatic optional params and use ref-init sentinels; wrapping in Option would change the public API shape consumers expect. */\n\nimport { type DependencyList, useMemo, useRef } from \"react\"\n\nimport { type Eq, referenceEq } from \"./eq\"\n\n/**\n * Like `useMemo`, but recomputes only when *some* dep has changed under the\n * supplied (or default) comparator. The `eqs` array is aligned positionally\n * with `deps`; missing entries fall back to `referenceEq`.\n */\nexport function useStableMemo<A>(\n factory: () => A,\n deps: DependencyList,\n eqs?: ReadonlyArray<Eq<unknown> | undefined>,\n): A {\n const prev = useRef<DependencyList | null>(null)\n const tick = useRef(0)\n\n if (prev.current === null) {\n prev.current = deps\n tick.current += 1\n } else {\n const stale = prev.current\n const changed = deps.some((d, i) => {\n const cmp = eqs?.[i] ?? referenceEq\n return !cmp(stale[i], d)\n })\n if (changed) {\n prev.current = deps\n tick.current += 1\n }\n }\n\n return useMemo(factory, [tick.current])\n}\n"],"mappings":"+FAYA,SAAgB,EACd,EACA,EACA,EACG,CACH,IAAM,EAAO,EAA8B,KAAK,CAC1C,EAAO,EAAO,EAAE,CAEtB,GAAI,EAAK,UAAY,KACnB,EAAK,QAAU,EACf,EAAK,SAAW,MACX,CACL,IAAM,EAAQ,EAAK,QACH,EAAK,MAAM,EAAG,IAErB,EADK,IAAM,IAAM,GACZ,EAAM,GAAI,EAAE,CAEf,GACT,EAAK,QAAU,EACf,EAAK,SAAW,GAIpB,OAAO,EAAQ,EAAS,CAAC,EAAK,QAAQ,CAAC"}
1
+ {"version":3,"file":"useStableMemo.js","names":[],"sources":["../../src/hooks/useStableMemo.ts"],"sourcesContent":["\"use client\"\n/* eslint-disable functype/prefer-option, functype/prefer-fold -- React hooks must accept idiomatic optional params and use ref-init sentinels; wrapping in Option would change the public API shape consumers expect. */\n\nimport { type DependencyList, useMemo, useRef } from \"react\"\n\nimport { type Eq, referenceEq } from \"./eq\"\n\n/**\n * Like `useMemo`, but recomputes only when *some* dep has changed under the\n * supplied (or default) comparator. The `eqs` array is aligned positionally\n * with `deps`; missing entries fall back to `referenceEq`.\n */\nexport function useStableMemo<A>(\n factory: () => A,\n deps: DependencyList,\n eqs?: ReadonlyArray<Eq<unknown> | undefined>,\n): A {\n const prev = useRef<DependencyList | null>(null)\n const tick = useRef(0)\n\n if (prev.current === null) {\n prev.current = deps\n tick.current += 1\n } else {\n const stale = prev.current\n const changed = deps.some((d, i) => {\n const cmp = eqs?.[i] ?? referenceEq\n return !cmp(stale[i], d)\n })\n if (changed) {\n prev.current = deps\n tick.current += 1\n }\n }\n\n return useMemo(factory, [tick.current])\n}\n"],"mappings":"+FAYA,SAAgB,EACd,EACA,EACA,EACG,CACH,IAAM,EAAO,EAA8B,IAAI,EACzC,EAAO,EAAO,CAAC,EAErB,GAAI,EAAK,UAAY,KACnB,EAAK,QAAU,EACf,EAAK,SAAW,MACX,CACL,IAAM,EAAQ,EAAK,QACH,EAAK,MAAM,EAAG,IAErB,EADK,IAAM,IAAM,GACZ,EAAM,GAAI,CAAC,CAEf,IACR,EAAK,QAAU,EACf,EAAK,SAAW,EAEpB,CAEA,OAAO,EAAQ,EAAS,CAAC,EAAK,OAAO,CAAC,CACxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useStableState.js","names":[],"sources":["../../src/hooks/useStableState.ts"],"sourcesContent":["\"use client\"\n\nimport { useCallback, useRef, useState } from \"react\"\n\nimport { type Eq, referenceEq } from \"./eq\"\n\n/**\n * Drop-in replacement for `useState` that no-ops when the next value is equal\n * to the current under `eq`. Defaults to `Object.is` (React's built-in), so the\n * behavior is identical to `useState` unless an `eq` is supplied.\n *\n * @param initial - Initial value or a lazy producer (called once)\n * @param eq - Comparator used to decide whether to skip the update\n */\nexport function useStableState<A>(\n initial: A | (() => A),\n eq: Eq<A> = referenceEq as Eq<A>,\n): readonly [A, (next: A | ((prev: A) => A)) => void] {\n const [value, setValue] = useState<A>(initial)\n const ref = useRef<A>(value)\n ref.current = value\n\n const setStable = useCallback(\n (next: A | ((prev: A) => A)) => {\n const resolved = typeof next === \"function\" ? (next as (prev: A) => A)(ref.current) : next\n if (!eq(ref.current, resolved)) {\n setValue(resolved)\n }\n },\n [eq],\n )\n\n return [value, setStable] as const\n}\n"],"mappings":"iHAcA,SAAgB,EACd,EACA,EAAY,EACwC,CACpD,GAAM,CAAC,EAAO,GAAY,EAAY,EAAQ,CACxC,EAAM,EAAU,EAAM,CAa5B,MAZA,GAAI,QAAU,EAYP,CAAC,EAVU,EACf,GAA+B,CAC9B,IAAM,EAAW,OAAO,GAAS,WAAc,EAAwB,EAAI,QAAQ,CAAG,EACjF,EAAG,EAAI,QAAS,EAAS,EAC5B,EAAS,EAAS,EAGtB,CAAC,EAAG,CAGkB,CAAC"}
1
+ {"version":3,"file":"useStableState.js","names":[],"sources":["../../src/hooks/useStableState.ts"],"sourcesContent":["\"use client\"\n\nimport { useCallback, useRef, useState } from \"react\"\n\nimport { type Eq, referenceEq } from \"./eq\"\n\n/**\n * Drop-in replacement for `useState` that no-ops when the next value is equal\n * to the current under `eq`. Defaults to `Object.is` (React's built-in), so the\n * behavior is identical to `useState` unless an `eq` is supplied.\n *\n * @param initial - Initial value or a lazy producer (called once)\n * @param eq - Comparator used to decide whether to skip the update\n */\nexport function useStableState<A>(\n initial: A | (() => A),\n eq: Eq<A> = referenceEq as Eq<A>,\n): readonly [A, (next: A | ((prev: A) => A)) => void] {\n const [value, setValue] = useState<A>(initial)\n const ref = useRef<A>(value)\n ref.current = value\n\n const setStable = useCallback(\n (next: A | ((prev: A) => A)) => {\n const resolved = typeof next === \"function\" ? (next as (prev: A) => A)(ref.current) : next\n if (!eq(ref.current, resolved)) {\n setValue(resolved)\n }\n },\n [eq],\n )\n\n return [value, setStable] as const\n}\n"],"mappings":"iHAcA,SAAgB,EACd,EACA,EAAY,EACwC,CACpD,GAAM,CAAC,EAAO,GAAY,EAAY,CAAO,EACvC,EAAM,EAAU,CAAK,EAa3B,MAZA,GAAI,QAAU,EAYP,CAAC,EAVU,EACf,GAA+B,CAC9B,IAAM,EAAW,OAAO,GAAS,WAAc,EAAwB,EAAI,OAAO,EAAI,EACjF,EAAG,EAAI,QAAS,CAAQ,GAC3B,EAAS,CAAQ,CAErB,EACA,CAAC,CAAE,CAGkB,CAAC,CAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"useTry.js","names":[],"sources":["../../src/hooks/useTry.ts"],"sourcesContent":["\"use client\"\n\nimport { Try, type Try as TryT } from \"functype/try\"\nimport { useCallback, useMemo, useState } from \"react\"\n\n/**\n * Stateful Try container. Defaults to Failure(new Error(\"uninitialized\")) when no initial.\n */\nexport function useTry<A>(initial?: TryT<A>): {\n readonly value: TryT<A>\n setSuccess: (a: A) => void\n setFailure: (e: Error) => void\n fold: <R>(onFailure: (e: Error) => R, onSuccess: (a: A) => R) => R\n} {\n const [value, setValue] = useState<TryT<A>>(() => initial ?? Try.failure<A>(new Error(\"uninitialized\")))\n\n const setSuccess = useCallback((a: A) => setValue(Try.success(a)), [])\n const setFailure = useCallback((e: Error) => setValue(Try.failure<A>(e)), [])\n\n const fold = useCallback(\n <R>(onFailure: (e: Error) => R, onSuccess: (a: A) => R) => value.fold(onFailure, onSuccess),\n [value],\n )\n\n return useMemo(() => ({ value, setSuccess, setFailure, fold }), [value, setSuccess, setFailure, fold])\n}\n"],"mappings":"+GAQA,SAAgB,EAAU,EAKxB,CACA,GAAM,CAAC,EAAO,GAAY,MAAwB,GAAW,EAAI,QAAe,MAAM,gBAAgB,CAAC,CAAC,CAElG,EAAa,EAAa,GAAS,EAAS,EAAI,QAAQ,EAAE,CAAC,CAAE,EAAE,CAAC,CAChE,EAAa,EAAa,GAAa,EAAS,EAAI,QAAW,EAAE,CAAC,CAAE,EAAE,CAAC,CAEvE,EAAO,GACP,EAA4B,IAA2B,EAAM,KAAK,EAAW,EAAU,CAC3F,CAAC,EAAM,CACR,CAED,OAAO,OAAe,CAAE,QAAO,aAAY,aAAY,OAAM,EAAG,CAAC,EAAO,EAAY,EAAY,EAAK,CAAC"}
1
+ {"version":3,"file":"useTry.js","names":[],"sources":["../../src/hooks/useTry.ts"],"sourcesContent":["\"use client\"\n\nimport { Try, type Try as TryT } from \"functype/try\"\nimport { useCallback, useMemo, useState } from \"react\"\n\n/**\n * Stateful Try container. Defaults to Failure(new Error(\"uninitialized\")) when no initial.\n */\nexport function useTry<A>(initial?: TryT<A>): {\n readonly value: TryT<A>\n setSuccess: (a: A) => void\n setFailure: (e: Error) => void\n fold: <R>(onFailure: (e: Error) => R, onSuccess: (a: A) => R) => R\n} {\n const [value, setValue] = useState<TryT<A>>(() => initial ?? Try.failure<A>(new Error(\"uninitialized\")))\n\n const setSuccess = useCallback((a: A) => setValue(Try.success(a)), [])\n const setFailure = useCallback((e: Error) => setValue(Try.failure<A>(e)), [])\n\n const fold = useCallback(\n <R>(onFailure: (e: Error) => R, onSuccess: (a: A) => R) => value.fold(onFailure, onSuccess),\n [value],\n )\n\n return useMemo(() => ({ value, setSuccess, setFailure, fold }), [value, setSuccess, setFailure, fold])\n}\n"],"mappings":"+GAQA,SAAgB,EAAU,EAKxB,CACA,GAAM,CAAC,EAAO,GAAY,MAAwB,GAAW,EAAI,QAAe,MAAM,eAAe,CAAC,CAAC,EAEjG,EAAa,EAAa,GAAS,EAAS,EAAI,QAAQ,CAAC,CAAC,EAAG,CAAC,CAAC,EAC/D,EAAa,EAAa,GAAa,EAAS,EAAI,QAAW,CAAC,CAAC,EAAG,CAAC,CAAC,EAEtE,EAAO,GACP,EAA4B,IAA2B,EAAM,KAAK,EAAW,CAAS,EAC1F,CAAC,CAAK,CACR,EAEA,OAAO,OAAe,CAAE,QAAO,aAAY,aAAY,MAAK,GAAI,CAAC,EAAO,EAAY,EAAY,CAAI,CAAC,CACvG"}
package/dist/index.d.ts CHANGED
@@ -8,4 +8,4 @@ import { t as useStableMemo } from "./useStableMemo-2dH3cBpb.js";
8
8
  import { t as useStableState } from "./useStableState-yar7Urk-.js";
9
9
  import { t as useTry } from "./useTry-CQb5RL6r.js";
10
10
  import { a as MatchCases, i as Match, n as MatchOption, r as MatchEither, t as MatchTry } from "./index-zHf9iM1m.js";
11
- export { Eq, Match, MatchCases, MatchEither, MatchOption, MatchTry, referenceEq, structuralEq, tagEq, useEither, useList, useOption, useStableCallback, useStableEffect, useStableMemo, useStableState, useTry };
11
+ export { type Eq, Match, type MatchCases, MatchEither, MatchOption, MatchTry, referenceEq, structuralEq, tagEq, useEither, useList, useOption, useStableCallback, useStableEffect, useStableMemo, useStableState, useTry };
@@ -1,2 +1,2 @@
1
1
  import { a as MatchCases, i as Match, n as MatchOption, r as MatchEither, t as MatchTry } from "../index-zHf9iM1m.js";
2
- export { Match, MatchCases, MatchEither, MatchOption, MatchTry };
2
+ export { Match, type MatchCases, MatchEither, MatchOption, MatchTry };
@@ -1 +1 @@
1
- {"version":3,"file":"match-imuIisms.js","names":[],"sources":["../src/match/Match.tsx","../src/match/MatchEither.tsx","../src/match/MatchOption.tsx","../src/match/MatchTry.tsx"],"sourcesContent":["import type { ReactElement, ReactNode } from \"react\"\n\n/**\n * Maps each `_tag` literal to a handler that receives the narrowed variant.\n *\n * TypeScript enforces exhaustiveness: omitting a case from `children` is a\n * compile error.\n */\nexport type MatchCases<U extends { _tag: string }> = {\n readonly [K in U[\"_tag\"]]: (value: Extract<U, { readonly _tag: K }>) => ReactNode\n}\n\n/**\n * Generic discriminated-union matcher for ADTs that follow functype's `_tag`\n * convention. Pass a value and a record of handlers keyed on each tag; the\n * matching handler is invoked with the narrowed variant.\n *\n * ```tsx\n * <Match value={state}>\n * {{\n * Loading: () => <Spinner />,\n * Success: ({ data }) => <Result data={data} />,\n * Failure: ({ error }) => <Err err={error} />,\n * }}\n * </Match>\n * ```\n */\nexport function Match<U extends { _tag: string }>(props: {\n readonly value: U\n readonly children: MatchCases<U>\n}): ReactElement {\n const handler = props.children[props.value._tag as U[\"_tag\"]] as (value: U) => ReactNode\n return <>{handler(props.value)}</>\n}\n","import type { Either } from \"functype/either\"\nimport type { ReactElement, ReactNode } from \"react\"\n\n/**\n * Tag-narrowed sugar over `Match` for `Either<L, R>`.\n */\nexport function MatchEither<L, R>(props: {\n readonly value: Either<L, R>\n readonly Left: (l: L) => ReactNode\n readonly Right: (r: R) => ReactNode\n}): ReactElement {\n return <>{props.value.fold(props.Left, props.Right)}</>\n}\n","import type { Option } from \"functype/option\"\nimport type { ReactElement, ReactNode } from \"react\"\n\n/**\n * Tag-narrowed sugar over `Match` for `Option<A>`. Renders `Some(a)` to the\n * `Some` handler and `None` to the `None` handler.\n */\nexport function MatchOption<A>(props: {\n readonly value: Option<A>\n readonly Some: (a: A) => ReactNode\n readonly None: () => ReactNode\n}): ReactElement {\n return <>{props.value.fold(props.None, props.Some)}</>\n}\n","import type { Try } from \"functype/try\"\nimport type { ReactElement, ReactNode } from \"react\"\n\n/**\n * Tag-narrowed sugar over `Match` for `Try<A>`. `Failure` receives the\n * underlying `Error`; `Success` receives the value.\n */\nexport function MatchTry<A>(props: {\n readonly value: Try<A>\n readonly Success: (a: A) => ReactNode\n readonly Failure: (e: Error) => ReactNode\n}): ReactElement {\n return <>{props.value.fold(props.Failure, props.Success)}</>\n}\n"],"mappings":"sDA2BA,SAAgB,EAAkC,EAGjC,CACf,IAAM,EAAU,EAAM,SAAS,EAAM,MAAM,MAC3C,OAAO,EAAA,EAAA,CAAA,SAAG,EAAQ,EAAM,MAAM,CAAI,CAAA,CC1BpC,SAAgB,EAAkB,EAIjB,CACf,OAAO,EAAA,EAAA,CAAA,SAAG,EAAM,MAAM,KAAK,EAAM,KAAM,EAAM,MAAM,CAAI,CAAA,CCJzD,SAAgB,EAAe,EAId,CACf,OAAO,EAAA,EAAA,CAAA,SAAG,EAAM,MAAM,KAAK,EAAM,KAAM,EAAM,KAAK,CAAI,CAAA,CCLxD,SAAgB,EAAY,EAIX,CACf,OAAO,EAAA,EAAA,CAAA,SAAG,EAAM,MAAM,KAAK,EAAM,QAAS,EAAM,QAAQ,CAAI,CAAA"}
1
+ {"version":3,"file":"match-imuIisms.js","names":[],"sources":["../src/match/Match.tsx","../src/match/MatchEither.tsx","../src/match/MatchOption.tsx","../src/match/MatchTry.tsx"],"sourcesContent":["import type { ReactElement, ReactNode } from \"react\"\n\n/**\n * Maps each `_tag` literal to a handler that receives the narrowed variant.\n *\n * TypeScript enforces exhaustiveness: omitting a case from `children` is a\n * compile error.\n */\nexport type MatchCases<U extends { _tag: string }> = {\n readonly [K in U[\"_tag\"]]: (value: Extract<U, { readonly _tag: K }>) => ReactNode\n}\n\n/**\n * Generic discriminated-union matcher for ADTs that follow functype's `_tag`\n * convention. Pass a value and a record of handlers keyed on each tag; the\n * matching handler is invoked with the narrowed variant.\n *\n * ```tsx\n * <Match value={state}>\n * {{\n * Loading: () => <Spinner />,\n * Success: ({ data }) => <Result data={data} />,\n * Failure: ({ error }) => <Err err={error} />,\n * }}\n * </Match>\n * ```\n */\nexport function Match<U extends { _tag: string }>(props: {\n readonly value: U\n readonly children: MatchCases<U>\n}): ReactElement {\n const handler = props.children[props.value._tag as U[\"_tag\"]] as (value: U) => ReactNode\n return <>{handler(props.value)}</>\n}\n","import type { Either } from \"functype/either\"\nimport type { ReactElement, ReactNode } from \"react\"\n\n/**\n * Tag-narrowed sugar over `Match` for `Either<L, R>`.\n */\nexport function MatchEither<L, R>(props: {\n readonly value: Either<L, R>\n readonly Left: (l: L) => ReactNode\n readonly Right: (r: R) => ReactNode\n}): ReactElement {\n return <>{props.value.fold(props.Left, props.Right)}</>\n}\n","import type { Option } from \"functype/option\"\nimport type { ReactElement, ReactNode } from \"react\"\n\n/**\n * Tag-narrowed sugar over `Match` for `Option<A>`. Renders `Some(a)` to the\n * `Some` handler and `None` to the `None` handler.\n */\nexport function MatchOption<A>(props: {\n readonly value: Option<A>\n readonly Some: (a: A) => ReactNode\n readonly None: () => ReactNode\n}): ReactElement {\n return <>{props.value.fold(props.None, props.Some)}</>\n}\n","import type { Try } from \"functype/try\"\nimport type { ReactElement, ReactNode } from \"react\"\n\n/**\n * Tag-narrowed sugar over `Match` for `Try<A>`. `Failure` receives the\n * underlying `Error`; `Success` receives the value.\n */\nexport function MatchTry<A>(props: {\n readonly value: Try<A>\n readonly Success: (a: A) => ReactNode\n readonly Failure: (e: Error) => ReactNode\n}): ReactElement {\n return <>{props.value.fold(props.Failure, props.Success)}</>\n}\n"],"mappings":"sDA2BA,SAAgB,EAAkC,EAGjC,CACf,IAAM,EAAU,EAAM,SAAS,EAAM,MAAM,MAC3C,OAAO,EAAA,EAAA,CAAA,SAAG,EAAQ,EAAM,KAAK,CAAI,CAAA,CACnC,CC3BA,SAAgB,EAAkB,EAIjB,CACf,OAAO,EAAA,EAAA,CAAA,SAAG,EAAM,MAAM,KAAK,EAAM,KAAM,EAAM,KAAK,CAAI,CAAA,CACxD,CCLA,SAAgB,EAAe,EAId,CACf,OAAO,EAAA,EAAA,CAAA,SAAG,EAAM,MAAM,KAAK,EAAM,KAAM,EAAM,IAAI,CAAI,CAAA,CACvD,CCNA,SAAgB,EAAY,EAIX,CACf,OAAO,EAAA,EAAA,CAAA,SAAG,EAAM,MAAM,KAAK,EAAM,QAAS,EAAM,OAAO,CAAI,CAAA,CAC7D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functype-react",
3
- "version": "0.1.0",
3
+ "version": "0.60.6",
4
4
  "description": "React bindings for functype — ADT-aware hooks and exhaustive pattern matching components",
5
5
  "keywords": [
6
6
  "functype",
@@ -25,7 +25,7 @@
25
25
  "peerDependencies": {
26
26
  "react": ">=18.0.0 <20.0.0",
27
27
  "react-dom": ">=18.0.0 <20.0.0",
28
- "functype": "^0.60.5"
28
+ "functype": "^0.60.6"
29
29
  },
30
30
  "peerDependenciesMeta": {
31
31
  "react-dom": {
@@ -34,18 +34,18 @@
34
34
  },
35
35
  "devDependencies": {
36
36
  "@testing-library/dom": "^10.4.1",
37
- "@testing-library/jest-dom": "^6.6.4",
38
- "@testing-library/react": "^16.3.0",
39
- "@types/node": "^24.12.2",
40
- "@types/react": "^19.2.0",
41
- "@types/react-dom": "^19.2.0",
42
- "fast-check": "^4.7.0",
43
- "jsdom": "^26.0.0",
44
- "react": "^19.2.0",
45
- "react-dom": "^19.2.0",
37
+ "@testing-library/jest-dom": "^6.9.1",
38
+ "@testing-library/react": "^16.3.2",
39
+ "@types/node": "^24.12.4",
40
+ "@types/react": "^19.2.14",
41
+ "@types/react-dom": "^19.2.3",
42
+ "fast-check": "^4.8.0",
43
+ "jsdom": "^26.1.0",
44
+ "react": "^19.2.6",
45
+ "react-dom": "^19.2.6",
46
46
  "ts-builds": "^2.8.0",
47
47
  "tsdown": "^0.22.0",
48
- "functype": "^0.60.5"
48
+ "functype": "^0.60.6"
49
49
  },
50
50
  "type": "module",
51
51
  "main": "./dist/index.js",