xantiagoma 0.1.1 → 0.1.2

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.
package/README.md CHANGED
@@ -34,7 +34,7 @@ npm install xantiagoma
34
34
  | `xantiagoma/unstorage` | Cache helpers with unstorage | `unstorage`, `ohash` |
35
35
  | `xantiagoma/valibot` | TimeZone validation schema | `valibot` |
36
36
  | `xantiagoma/sonner` | Toast streaming for iterables | `sonner`, `react` |
37
- | `xantiagoma/react` | StreamRenderer + useStream hook | `react`, `@tanstack/react-query` |
37
+ | `xantiagoma/react` | React hooks + components | `react`, `@tanstack/react-query` |
38
38
 
39
39
  Sub-entry dependencies are **optional peer deps** — only install what you use.
40
40
 
@@ -42,75 +42,84 @@ Sub-entry dependencies are **optional peer deps** — only install what you use.
42
42
 
43
43
  ### Error Handling
44
44
 
45
- | Export | Description |
46
- | ---------------------------- | ------------------------------------------------- |
47
- | `tryCatch(promise)` | Async `[data, error]` tuple — no try/catch needed |
48
- | `tryCatchSync(fn)` | Sync `[data, error]` tuple |
49
- | `assertNotNull(value)` | Throws if null/undefined, narrows type |
50
- | `valueOrThrow(value, error)` | Returns value or throws |
51
- | `AssertError` | Custom error class for assertions |
45
+ | Export | Description | Source | Tests |
46
+ | --------------------------- | -------------------------------------- | ------------------------------- | --------------------------------------- |
47
+ | `tryCatch` / `tryCatchSync` | `[data, error]` tuples — no try/catch | [src](./src/try-catch.ts) | [tests](./test/try-catch.test.ts) |
48
+ | `assertNotNull` | Throws if null/undefined, narrows type | [src](./src/assert-not-null.ts) | [tests](./test/assert-not-null.test.ts) |
49
+ | `valueOrThrow` | Returns value or throws | [src](./src/error.ts) | [tests](./test/error.test.ts) |
50
+ | `AssertError` | Custom error class | [src](./src/errors.ts) | [tests](./test/errors.test.ts) |
52
51
 
53
52
  ### Async
54
53
 
55
- | Export | Description |
56
- | ---------------------------- | ----------------------------------------- |
57
- | `wait(ms)` | Typed setTimeout delay |
58
- | `Completer` | Externally resolvable Promise (like Dart) |
59
- | `collect(iterable)` | Drain `AsyncIterable<T>` into `T[]` |
60
- | `asyncOf(...values)` | Create `AsyncGenerator` from values |
61
- | `AsyncChannel` | Push-based `AsyncIterable` with modes |
62
- | `resolveMaybePromise(value)` | Resolve `T \| Promise<T>` → `Promise<T>` |
54
+ | Export | Description | Source | Tests |
55
+ | --------------------- | ---------------------------------------- | ------------------------------------- | --------------------------------------------- |
56
+ | `wait` | Typed setTimeout delay | [src](./src/wait.ts) | [tests](./test/wait.test.ts) |
57
+ | `Completer` | Externally resolvable Promise | [src](./src/completer.ts) | [tests](./test/completer.test.ts) |
58
+ | `collect` | Drain `AsyncIterable<T>` into `T[]` | [src](./src/collect.ts) | [tests](./test/collect.test.ts) |
59
+ | `asyncOf` | Create `AsyncGenerator` from values | [src](./src/async-of.ts) | [tests](./test/async-of.test.ts) |
60
+ | `AsyncChannel` | Push-based `AsyncIterable` with modes | [src](./src/async-channel.ts) | [tests](./test/async-channel.test.ts) |
61
+ | `resolveMaybePromise` | Resolve `T \| Promise<T>` → `Promise<T>` | [src](./src/resolve-maybe-promise.ts) | [tests](./test/resolve-maybe-promise.test.ts) |
63
62
 
64
63
  ### Iterables & Generators
65
64
 
66
- | Export | Description |
67
- | ------------------------------ | ------------------------------------------ |
68
- | `range(start, end, step?)` | Numeric range as array |
69
- | `rangeLazy(start, end, step?)` | Numeric range as generator |
70
- | `enumerate(iterable)` | `[index, value]` tuples (sync) |
71
- | `enumerateAsync(iterable)` | `[index, value]` tuples (async) |
72
- | `toIterator(iterable)` | Normalize to `Iterator` |
73
- | `toAsyncIterable(source)` | Normalize any iterable to `AsyncGenerator` |
65
+ | Export | Description | Source | Tests |
66
+ | ------------------------------ | --------------------------------- | --------------------------------- | ----------------------------------------- |
67
+ | `range` / `rangeLazy` | Numeric range (array / generator) | [src](./src/range.ts) | [tests](./test/range.test.ts) |
68
+ | `enumerate` / `enumerateAsync` | `[index, value]` tuples | [src](./src/enumerate.ts) | [tests](./test/enumerate.test.ts) |
69
+ | `toIterator` | Normalize to `Iterator` | [src](./src/to-iterator.ts) | [tests](./test/to-iterator.test.ts) |
70
+ | `toAsyncIterable` | Normalize to `AsyncGenerator` | [src](./src/to-async-iterable.ts) | [tests](./test/to-async-iterable.test.ts) |
74
71
 
75
72
  ### Type Guards
76
73
 
77
- | Export | Description |
78
- | -------------------------- | ------------------------------------------- |
79
- | `isPromise(value)` | Check for promise-like |
80
- | `isIterable(value)` | Check for `Iterable` |
81
- | `isAsyncIterable(value)` | Check for `AsyncIterable` |
82
- | `isIterator(value)` | Check for `Iterator` |
83
- | `isGenerator(value)` | Check for `Generator` |
84
- | `isAsyncGenerator(value)` | Check for `AsyncGenerator` |
85
- | `isDisposable(value)` | Check for `Disposable` or `AsyncDisposable` |
86
- | `isAsyncDisposable(value)` | Check for `AsyncDisposable` |
87
- | `isSyncDisposable(value)` | Check for `Disposable` |
74
+ | Export | Description | Source | Tests |
75
+ | --------------------------------------------------------- | ---------------------- | ---------------------------------- | ------------------------------------------ |
76
+ | `isPromise` | Promise-like check | [src](./src/is-promise.ts) | [tests](./test/is-promise.test.ts) |
77
+ | `isIterable` | `Iterable` check | [src](./src/is-iterable.ts) | [tests](./test/is-iterable.test.ts) |
78
+ | `isAsyncIterable` | `AsyncIterable` check | [src](./src/is-async-iterable.ts) | [tests](./test/is-async-iterable.test.ts) |
79
+ | `isIterator` | `Iterator` check | [src](./src/is-iterator.ts) | [tests](./test/is-iterator.test.ts) |
80
+ | `isGenerator` | `Generator` check | [src](./src/is-generator.ts) | [tests](./test/is-generator.test.ts) |
81
+ | `isAsyncGenerator` | `AsyncGenerator` check | [src](./src/is-async-generator.ts) | [tests](./test/is-async-generator.test.ts) |
82
+ | `isDisposable` / `isAsyncDisposable` / `isSyncDisposable` | Disposable checks | [src](./src/is-disposable.ts) | [tests](./test/is-disposable.test.ts) |
88
83
 
89
84
  ### Disposable Utilities
90
85
 
91
- | Export | Description |
92
- | --------------------- | --------------------------------------- |
93
- | `defer(fn)` | Cancellable `await using` disposable |
94
- | `deferSync(fn)` | Cancellable `using` disposable |
95
- | `makeDisposable(obj)` | Add `Symbol.asyncDispose` to any object |
86
+ | Export | Description | Source | Tests |
87
+ | --------------------- | --------------------------------------- | ------------------------------- | --------------------------------------- |
88
+ | `defer` / `deferSync` | Cancellable `using`/`await using` | [src](./src/defer.ts) | [tests](./test/defer.test.ts) |
89
+ | `makeDisposable` | Add `Symbol.asyncDispose` to any object | [src](./src/make-disposable.ts) | [tests](./test/make-disposable.test.ts) |
96
90
 
97
91
  ### Strings
98
92
 
99
- | Export | Description |
100
- | --------------------------- | ----------------------------- |
101
- | `ensureString(value)` | Coerce to string |
102
- | `naturalSortCompare(a, b)` | Natural sort comparator |
103
- | `jaroWinklerDistance(a, b)` | Fuzzy string similarity (0-1) |
93
+ | Export | Description | Source | Tests |
94
+ | ------------------------------------------------------------- | ---------------- | ---------------------- | ------------------------------ |
95
+ | `ensureString` / `naturalSortCompare` / `jaroWinklerDistance` | String utilities | [src](./src/string.ts) | [tests](./test/string.test.ts) |
104
96
 
105
97
  ### Misc
106
98
 
107
- | Export | Description |
108
- | ------------------------------------------- | --------------------------------------- |
109
- | `cast<T>(value)` | Unsafe `as T` type cast |
110
- | `log(value)` | `console.log` that returns its argument |
111
- | `prepareLoaderResult(rows, keys)` | Map DB rows to DataLoader key order |
112
- | `resolveStreamSource(source)` | Resolve `StreamSource<T>` |
113
- | `secondsToMs` / `minutesToMs` / `hoursToMs` | Time unit converters |
99
+ | Export | Description | Source | Tests |
100
+ | ------------------------------------------- | --------------------------------------- | ------------------------------------- | --------------------------------------------- |
101
+ | `cast<T>` | Unsafe `as T` type cast | [src](./src/cast.ts) | [tests](./test/cast.test.ts) |
102
+ | `log` | `console.log` that returns its argument | [src](./src/log.ts) | [tests](./test/log.test.ts) |
103
+ | `prepareLoaderResult` | Map DB rows to DataLoader key order | [src](./src/prepare-loader-result.ts) | [tests](./test/prepare-loader-result.test.ts) |
104
+ | `resolveStreamSource` | Resolve `StreamSource<T>` | [src](./src/stream-source.ts) | [tests](./test/stream-source.test.ts) |
105
+ | `secondsToMs` / `minutesToMs` / `hoursToMs` | Time unit converters | [src](./src/time-convert.ts) | [tests](./test/time-convert.test.ts) |
106
+
107
+ ## Web Utilities (`xantiagoma/web`)
108
+
109
+ | Export | Description | Source | Tests |
110
+ | ----------------------- | ----------------------------------- | ----------------------------------------- | ------------------------------------------- |
111
+ | `formDataToObject` | `FormData` → plain object | [src](./src/form-data-to-object-utils.ts) | [tests](./test/form-data-to-object.test.ts) |
112
+ | `fetchWithProgress` | Fetch with upload/download progress | [src](./src/fetch-with-progress.ts) | [tests](./test/fetch-with-progress.test.ts) |
113
+ | `createHttpInterceptor` | Intercept fetch + XHR with rules | [src](./src/intercept-http.ts) | [tests](./test/intercept-http.test.tsx) |
114
+
115
+ ## React Utilities (`xantiagoma/react`)
116
+
117
+ | Export | Description | Source | Tests |
118
+ | ------------------------------ | ----------------------------------- | -------------------------------------- | ----------------------------------------------- |
119
+ | `Providers` / `provider` | Compose providers without nesting | [src](./src/providers.tsx) | [tests](./test/providers.test.tsx) |
120
+ | `usePreventAutoFocus` | Prevent auto-focus in modals | [src](./src/use-prevent-auto-focus.ts) | [tests](./test/use-prevent-auto-focus.test.tsx) |
121
+ | `useDynamicRefs` | Dynamic ref registry by key | [src](./src/use-dynamic-refs.ts) | [tests](./test/use-dynamic-refs.test.tsx) |
122
+ | `useStream` / `StreamRenderer` | Stream consumption hook + component | [src](./src/stream-renderer.tsx) | [tests](./test/stream-renderer.test.tsx) |
114
123
 
115
124
  ## Recommended Libraries
116
125
 
@@ -452,9 +452,106 @@ const Providers = ({ providers, children }) => providers.reduceRight((acc, [Prov
452
452
  children: acc
453
453
  }, getProviderKey(Provider, index)), children ?? null);
454
454
  //#endregion
455
+ //#region src/use-prevent-auto-focus.ts
456
+ /**
457
+ * Hook that prevents auto-focus behavior while still maintaining focus management.
458
+ * Useful for modal dialogs (e.g. Radix Dialog) where you want to control the initial focus.
459
+ *
460
+ * @template E - Element type, defaults to HTMLDivElement
461
+ * @returns Object containing:
462
+ * - `ref` — React ref to attach to the element
463
+ * - `onOpenAutoFocus` — Event handler to prevent default focus behavior
464
+ * - `tabIndex` — `-1` to make the element programmatically focusable
465
+ *
466
+ * @example
467
+ * ```tsx
468
+ * import { usePreventAutoFocus } from "xantiagoma/react";
469
+ *
470
+ * function MyDialog() {
471
+ * const preventAutoFocus = usePreventAutoFocus();
472
+ *
473
+ * return (
474
+ * <DialogContent {...preventAutoFocus}>
475
+ * <p>Dialog content here</p>
476
+ * </DialogContent>
477
+ * );
478
+ * }
479
+ * ```
480
+ */
481
+ function usePreventAutoFocus() {
482
+ const ref = (0, react.useRef)(null);
483
+ return {
484
+ ref,
485
+ onOpenAutoFocus: (0, react.useCallback)((event) => {
486
+ event.preventDefault();
487
+ ref.current?.focus({ preventScroll: true });
488
+ }, []),
489
+ tabIndex: -1
490
+ };
491
+ }
492
+ //#endregion
493
+ //#region src/use-dynamic-refs.ts
494
+ function createRefGetter(refMap) {
495
+ function getRef(key) {
496
+ if (!key) return;
497
+ const existing = refMap.get(key);
498
+ if (existing) return existing;
499
+ const ref = (0, react.createRef)();
500
+ refMap.set(key, ref);
501
+ return ref;
502
+ }
503
+ return getRef;
504
+ }
505
+ /**
506
+ * React hook that returns a ref "getter" function.
507
+ * Call the getter with a string key to get (or create) a `RefObject` for that key.
508
+ * Useful for dynamic lists, tabs, or any UI where the number of refs isn't known at compile time.
509
+ *
510
+ * @template T - The element type for the refs (e.g. `HTMLDivElement`)
511
+ * @param options - Optional configuration
512
+ * @returns A {@link DynamicRefGetter} function
513
+ *
514
+ * @example
515
+ * ```tsx
516
+ * import { useDynamicRefs } from "xantiagoma/react";
517
+ *
518
+ * function TabList({ tabs }: { tabs: string[] }) {
519
+ * const getRef = useDynamicRefs<HTMLButtonElement>();
520
+ *
521
+ * return (
522
+ * <div>
523
+ * {tabs.map((tab) => (
524
+ * <button key={tab} ref={getRef(tab)}>
525
+ * {tab}
526
+ * </button>
527
+ * ))}
528
+ * </div>
529
+ * );
530
+ * }
531
+ * ```
532
+ *
533
+ * @example
534
+ * ```tsx
535
+ * // Scroll to a specific item
536
+ * const getRef = useDynamicRefs<HTMLDivElement>();
537
+ *
538
+ * function scrollTo(id: string) {
539
+ * getRef(id)?.current?.scrollIntoView({ behavior: "smooth" });
540
+ * }
541
+ * ```
542
+ */
543
+ function useDynamicRefs(options) {
544
+ const prefix = options?.prefix;
545
+ return (0, react.useMemo)(() => {
546
+ return createRefGetter(/* @__PURE__ */ new Map());
547
+ }, [prefix]);
548
+ }
549
+ //#endregion
455
550
  exports.Providers = Providers;
456
551
  exports.StreamRenderer = StreamRenderer;
457
552
  exports.provider = provider;
553
+ exports.useDynamicRefs = useDynamicRefs;
554
+ exports.usePreventAutoFocus = usePreventAutoFocus;
458
555
  exports.useStream = useStream;
459
556
 
460
557
  //# sourceMappingURL=entry-react.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry-react.cjs","names":["resolveStreamSource","wait","isGenerator","isAsyncGenerator","AsyncChannel","isAsyncIterable","isIterable","toAsyncIterable","Component"],"sources":["../src/stream-renderer.tsx","../src/providers.tsx"],"sourcesContent":["import {\n AsyncChannel,\n isAsyncGenerator,\n isAsyncIterable,\n isGenerator,\n isIterable,\n resolveStreamSource,\n type StreamSource,\n toAsyncIterable,\n wait,\n} from \"./index.ts\";\nimport {\n experimental_streamedQuery as streamedQuery,\n type UseQueryResult,\n useQuery,\n} from \"@tanstack/react-query\";\nimport { type ReactNode, useEffect, useMemo, useRef } from \"react\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Render prop: can be ReactNode or function returning ReactNode */\ntype RenderProp<TData> = ReactNode | ((data: TData) => ReactNode);\n\n/** Render prop without data (for loading) */\ntype RenderPropNoData = ReactNode | (() => ReactNode);\n\n/** Stream state with accumulated items */\nexport type StreamState<TYield, TReturn = void> = {\n items: TYield[];\n count: number;\n latest: TYield | null;\n returnValue: TReturn | undefined;\n status: \"streaming\" | \"complete\";\n};\n\n/** Granular delay configuration */\nexport type DelayConfig = {\n items?: number;\n first?: number;\n result?: number;\n};\n\n/** Streaming data passed to streaming render prop */\nexport type StreamingData<TYield> = {\n count: number;\n latest: TYield;\n items: TYield[];\n};\n\n/** Success data passed to success render prop */\nexport type SuccessData<TYield, TReturn> = {\n count: number;\n items: TYield[];\n returnValue: TReturn | undefined;\n};\n\n/** Error data passed to error render prop */\nexport type ErrorData<TYield> = {\n error: Error;\n count: number;\n items: TYield[];\n};\n\n/** Data passed to onItem callback */\nexport type OnItemData<TYield> = {\n item: TYield;\n count: number;\n items: TYield[];\n};\n\n/** Data passed to onSuccess callback */\nexport type OnSuccessData<TYield, TReturn = void> = {\n count: number;\n items: TYield[];\n returnValue: TReturn | undefined;\n};\n\n/** Data passed to onError callback */\nexport type OnErrorData<TYield> = {\n error: Error;\n count: number;\n items: TYield[];\n};\n\n/** Children render prop data - includes status and all relevant data */\nexport type ChildrenData<TYield, TReturn = void> =\n | {\n status: \"loading\";\n count: 0;\n items: [];\n latest: null;\n returnValue: undefined;\n error: null;\n }\n | {\n status: \"streaming\";\n count: number;\n items: TYield[];\n latest: TYield;\n returnValue: undefined;\n error: null;\n }\n | {\n status: \"success\";\n count: number;\n items: TYield[];\n latest: TYield | null;\n returnValue: TReturn | undefined;\n error: null;\n }\n | {\n status: \"error\";\n count: number;\n items: TYield[];\n latest: TYield | null;\n returnValue: undefined;\n error: Error;\n };\n\n/** Base props for StreamRenderer */\nexport type StreamRendererBaseProps<TYield, TReturn = void> = {\n /** The stream source - can be instance or factory */\n source: StreamSource<TYield> | null | undefined;\n\n /** Unique key for the query cache */\n queryKey?: unknown[];\n /** Delay configuration for visualizing sync streams */\n delay?: number | DelayConfig;\n /** Called for each item received */\n onItem?: (data: OnItemData<TYield>) => void;\n /** Called when stream completes successfully */\n onSuccess?: (data: OnSuccessData<TYield, TReturn>) => void;\n /** Called when stream errors */\n onError?: (data: OnErrorData<TYield>) => void;\n /** Whether to start streaming immediately (default: true) */\n enabled?: boolean;\n /**\n * Retry configuration on error (default: false = no retries)\n * - `false`: no retries\n * - `true`: use TanStack Query default (3 retries)\n * - `number`: retry that many times\n * - `(data) => boolean`: custom retry logic with access to partial data\n */\n retry?: boolean | number | ((data: RetryData<TYield>) => boolean);\n};\n\n/** Props for StreamRenderer - supports all accumulation modes */\nexport type StreamRendererProps<TYield, TReturn = void> = StreamRendererBaseProps<\n TYield,\n TReturn\n> & {\n /**\n * Accumulation mode (mutually exclusive with maxItems)\n * - `\"accumulate\"` (default): keep all items\n * - `\"latest\"`: only keep the most recent item\n */\n mode?: StreamMode;\n /** Keep only the last N items (mutually exclusive with mode) */\n maxItems?: number;\n /** Render while loading - ReactNode or () => ReactNode */\n loading?: RenderPropNoData;\n /** Render while streaming - ReactNode or (data) => ReactNode */\n streaming?: RenderProp<StreamingData<TYield>>;\n /** Render on success - ReactNode or (data) => ReactNode */\n success?: RenderProp<SuccessData<TYield, TReturn>>;\n /** Render on error - ReactNode or (data) => ReactNode */\n error?: RenderProp<ErrorData<TYield>>;\n /** Fallback/universal render prop - ReactNode or (data) => ReactNode */\n children?: RenderProp<ChildrenData<TYield, TReturn>>;\n};\n\n/** Data passed to retry callback */\nexport type RetryData<TYield> = {\n /** Number of times the query has failed (starts at 0) */\n failureCount: number;\n /** The error that caused the failure */\n error: Error;\n /** Items collected before the error */\n items: TYield[];\n /** Number of items collected before the error */\n count: number;\n /** Last item received before the error (null if none) */\n latest: TYield | null;\n};\n\n/**\n * Accumulation mode for stream items.\n * - `\"accumulate\"` (default): keep all items in memory\n * - `\"latest\"`: only keep the most recent item\n */\nexport type StreamMode = \"accumulate\" | \"latest\";\n\n/**\n * Custom reducer for stream state.\n * Receives current state and new item, returns new state.\n */\nexport type StreamReducer<TYield, TState> = (state: TState, item: TYield, count: number) => TState;\n\n/** Base options shared by all useStream configurations */\ntype UseStreamBaseOptions<TYield, TReturn = void> = {\n /** The stream source - can be instance or factory */\n source: StreamSource<TYield> | null | undefined;\n /** Unique key for the query cache */\n queryKey?: unknown[];\n /** Delay configuration for visualizing sync streams */\n delay?: number | DelayConfig;\n /** Called for each item received */\n onItem?: (data: OnItemData<TYield>) => void;\n /** Called when stream completes successfully */\n onSuccess?: (data: OnSuccessData<TYield, TReturn>) => void;\n /** Called when stream errors */\n onError?: (data: OnErrorData<TYield>) => void;\n /** Whether to start streaming immediately (default: true) */\n enabled?: boolean;\n /**\n * Retry configuration on error (default: false = no retries)\n * - `false`: no retries\n * - `true`: use TanStack Query default (3 retries)\n * - `number`: retry that many times\n * - `(data) => boolean`: custom retry logic with access to partial data\n */\n retry?: boolean | number | ((data: RetryData<TYield>) => boolean);\n};\n\n/** Options with default accumulation (all items) */\ntype UseStreamAccumulateOptions<TYield, TReturn = void> = UseStreamBaseOptions<TYield, TReturn> & {\n mode?: \"accumulate\";\n maxItems?: never;\n reducer?: never;\n};\n\n/** Options with \"latest\" mode (only keep last item) */\ntype UseStreamLatestOptions<TYield, TReturn = void> = UseStreamBaseOptions<TYield, TReturn> & {\n mode: \"latest\";\n maxItems?: never;\n reducer?: never;\n};\n\n/** Options with maxItems (sliding window) */\ntype UseStreamMaxItemsOptions<TYield, TReturn = void> = UseStreamBaseOptions<TYield, TReturn> & {\n mode?: never;\n /** Keep only the last N items (sliding window) */\n maxItems: number;\n reducer?: never;\n};\n\n/** Options with custom reducer */\ntype UseStreamReducerOptions<TYield, TState, TReturn = void> = UseStreamBaseOptions<\n TYield,\n TReturn\n> & {\n mode?: never;\n maxItems?: never;\n /** Custom reducer function for complete control over state */\n reducer: StreamReducer<TYield, TState>;\n /** Initial state for custom reducer */\n initialState: TState;\n};\n\n/** Props for useStream hook - mutually exclusive accumulation options */\nexport type UseStreamOptions<TYield, TReturn = void, TState = unknown> =\n | UseStreamAccumulateOptions<TYield, TReturn>\n | UseStreamLatestOptions<TYield, TReturn>\n | UseStreamMaxItemsOptions<TYield, TReturn>\n | UseStreamReducerOptions<TYield, TState, TReturn>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction normalizeDelay(delay: number | DelayConfig | undefined): DelayConfig {\n if (delay === undefined) {\n return {};\n }\n if (typeof delay === \"number\") {\n return { first: delay, items: delay, result: delay };\n }\n return delay;\n}\n\n/** Resolve a render prop (ReactNode or function) */\nfunction resolveRenderProp<TData>(\n prop: RenderProp<TData> | undefined,\n data: TData,\n fallback: ReactNode,\n): ReactNode {\n if (prop === undefined) {\n return fallback;\n }\n if (typeof prop === \"function\") {\n return prop(data);\n }\n return prop;\n}\n\n/** Resolve a render prop without data (for loading) */\nfunction resolveRenderPropNoData(\n prop: RenderPropNoData | undefined,\n fallback: ReactNode,\n): ReactNode {\n if (prop === undefined) {\n return fallback;\n }\n if (typeof prop === \"function\") {\n return prop();\n }\n return prop;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// useStream Hook\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Result type for standard useStream (accumulate, latest, maxItems modes) */\nexport type UseStreamResult<TYield, TReturn = void> = {\n query: UseQueryResult<StreamState<TYield, TReturn>, Error>;\n /** Items received (all, last N, or just latest depending on mode) */\n items: TYield[];\n /** Total number of items received (even if not all kept in memory) */\n count: number;\n /** Most recent item */\n latest: TYield | null;\n /** Return value from generator (if complete) */\n returnValue: TReturn | undefined;\n /** Restart the stream */\n refetch: () => void;\n // Status flags\n isLoading: boolean;\n isStreaming: boolean;\n isSuccess: boolean;\n isError: boolean;\n};\n\n/** Result type for useStream with custom reducer */\nexport type UseStreamReducerResult<_TYield, TState> = {\n query: UseQueryResult<{ state: TState; count: number; status: \"streaming\" | \"complete\" }, Error>;\n /** Custom state from reducer */\n state: TState;\n /** Total number of items received */\n count: number;\n /** Restart the stream */\n refetch: () => void;\n // Status flags\n isLoading: boolean;\n isStreaming: boolean;\n isSuccess: boolean;\n isError: boolean;\n};\n\n/**\n * Hook for consuming streams using TanStack Query's streamedQuery.\n *\n * @example\n * ```tsx\n * // Default: accumulate all items\n * const { items, isStreaming, count } = useStream({\n * source: fetchItems,\n * queryKey: ['my-stream'],\n * });\n *\n * // Latest only: keep just the most recent item\n * const { items, latest } = useStream({\n * source: sseEvents,\n * mode: \"latest\",\n * });\n *\n * // Sliding window: keep last N items\n * const { items } = useStream({\n * source: logStream,\n * maxItems: 100,\n * });\n *\n * // Custom reducer: full control over state\n * const { state } = useStream({\n * source: events,\n * reducer: (state, event) => ({ ...state, [event.id]: event }),\n * initialState: {},\n * });\n * ```\n */\n// Overload: custom reducer\nexport function useStream<TYield, TState, TReturn = void>(\n options: UseStreamReducerOptions<TYield, TState, TReturn>,\n): UseStreamReducerResult<TYield, TState>;\n\n// Overload: standard modes (accumulate, latest, maxItems)\nexport function useStream<TYield, TReturn = void>(\n options:\n | UseStreamAccumulateOptions<TYield, TReturn>\n | UseStreamLatestOptions<TYield, TReturn>\n | UseStreamMaxItemsOptions<TYield, TReturn>,\n): UseStreamResult<TYield, TReturn>;\n\n// Implementation\nexport function useStream<TYield, TReturn = void, TState = unknown>(\n options: UseStreamOptions<TYield, TReturn, TState>,\n): UseStreamResult<TYield, TReturn> | UseStreamReducerResult<TYield, TState> {\n const {\n source,\n queryKey,\n delay,\n onItem,\n onSuccess,\n onError,\n enabled = true,\n retry = false,\n } = options;\n\n // Extract mode options\n const mode = \"mode\" in options ? options.mode : undefined;\n const maxItems = \"maxItems\" in options ? options.maxItems : undefined;\n const customReducer = \"reducer\" in options ? options.reducer : undefined;\n const initialState = \"initialState\" in options ? options.initialState : undefined;\n\n const delayConfig = useMemo(() => normalizeDelay(delay), [delay]);\n\n // Track partial state for retry callback\n const partialStateRef = useRef<{\n items: TYield[];\n count: number;\n latest: TYield | null;\n }>({ items: [], count: 0, latest: null });\n\n // Create a stable query key\n const stableQueryKey = useMemo(() => queryKey ?? [\"stream\", source], [queryKey, source]);\n\n const query = useQuery({\n queryKey: stableQueryKey,\n enabled: enabled && source != null,\n queryFn: streamedQuery({\n async *streamFn() {\n // Reset partial state on new fetch\n partialStateRef.current = { items: [], count: 0, latest: null };\n\n const resolved = resolveStreamSource(source as StreamSource<TYield>);\n let isFirst = true;\n let itemCount = 0;\n\n const processDelay = async () => {\n if (isFirst && delayConfig.first) {\n await wait(delayConfig.first);\n isFirst = false;\n } else if (!isFirst && delayConfig.items) {\n await wait(delayConfig.items);\n } else {\n isFirst = false;\n }\n };\n\n const trackItem = (item: TYield) => {\n partialStateRef.current = {\n items: [...partialStateRef.current.items, item],\n count: partialStateRef.current.count + 1,\n latest: item,\n };\n };\n\n const emitItem = (item: TYield) => {\n onItem?.({\n item,\n count: partialStateRef.current.count,\n items: partialStateRef.current.items,\n });\n };\n\n // Handle sync Generator (captures return value)\n if (isGenerator<TYield, TReturn>(resolved)) {\n let result = resolved.next();\n while (!result.done) {\n await processDelay();\n itemCount += 1;\n trackItem(result.value);\n emitItem(result.value);\n yield {\n type: \"item\" as const,\n value: result.value,\n count: itemCount,\n };\n result = resolved.next();\n }\n if (delayConfig.result) {\n await wait(delayConfig.result);\n }\n yield { type: \"return\" as const, value: result.value };\n return;\n }\n\n // Handle AsyncGenerator (captures return value)\n if (isAsyncGenerator<TYield, TReturn>(resolved)) {\n let result = await resolved.next();\n while (!result.done) {\n await processDelay();\n itemCount += 1;\n trackItem(result.value);\n emitItem(result.value);\n yield {\n type: \"item\" as const,\n value: result.value,\n count: itemCount,\n };\n result = await resolved.next();\n }\n if (delayConfig.result) {\n await wait(delayConfig.result);\n }\n yield { type: \"return\" as const, value: result.value };\n return;\n }\n\n // Handle AsyncChannel (captures return value)\n if (resolved instanceof AsyncChannel) {\n const iterator = resolved[Symbol.asyncIterator]();\n let result = await iterator.next();\n while (!result.done) {\n await processDelay();\n itemCount += 1;\n trackItem(result.value);\n emitItem(result.value);\n yield {\n type: \"item\" as const,\n value: result.value,\n count: itemCount,\n };\n result = await iterator.next();\n }\n if (delayConfig.result) {\n await wait(delayConfig.result);\n }\n yield { type: \"return\" as const, value: result.value };\n return;\n }\n\n // Handle plain iterables/iterators (no return value)\n const iterable =\n isAsyncIterable(resolved) || isIterable(resolved)\n ? toAsyncIterable(resolved)\n : toAsyncIterable(resolved as AsyncIterator<TYield>);\n\n for await (const item of iterable) {\n await processDelay();\n itemCount += 1;\n trackItem(item);\n emitItem(item);\n yield { type: \"item\" as const, value: item, count: itemCount };\n }\n\n if (delayConfig.result) {\n await wait(delayConfig.result);\n }\n yield { type: \"complete\" as const };\n },\n\n reducer: (\n state:\n | StreamState<TYield, TReturn>\n | { state: TState; count: number; status: \"streaming\" | \"complete\" },\n chunk:\n | { type: \"item\"; value: TYield; count: number }\n | { type: \"return\"; value: TReturn }\n | { type: \"complete\" },\n ) => {\n // Custom reducer mode\n if (customReducer) {\n const s = state as {\n state: TState;\n count: number;\n status: \"streaming\" | \"complete\";\n };\n if (chunk.type === \"item\") {\n return {\n state: customReducer(s.state, chunk.value, chunk.count),\n count: chunk.count,\n status: \"streaming\" as const,\n };\n }\n return { ...s, status: \"complete\" as const };\n }\n\n // Standard modes\n const s = state as StreamState<TYield, TReturn>;\n if (chunk.type === \"item\") {\n let newItems: TYield[];\n if (mode === \"latest\") {\n // Only keep the latest item\n newItems = [chunk.value];\n } else if (maxItems !== undefined) {\n // Sliding window: keep last N items\n newItems = [...s.items, chunk.value].slice(-maxItems);\n } else {\n // Default: accumulate all\n newItems = [...s.items, chunk.value];\n }\n return {\n ...s,\n items: newItems,\n count: chunk.count,\n latest: chunk.value,\n status: \"streaming\" as const,\n };\n }\n if (chunk.type === \"return\") {\n return {\n ...s,\n returnValue: chunk.value,\n status: \"complete\" as const,\n };\n }\n // complete\n return { ...s, status: \"complete\" as const };\n },\n\n initialValue: customReducer\n ? {\n state: initialState as TState,\n count: 0,\n status: \"streaming\" as const,\n }\n : ({\n items: [] as TYield[],\n count: 0,\n latest: null,\n returnValue: undefined,\n status: \"streaming\" as const,\n } as StreamState<TYield, TReturn>),\n\n refetchMode: \"reset\",\n }),\n // Wrap our callback to include partial state data\n retry:\n typeof retry === \"function\"\n ? (failureCount: number, error: Error) =>\n retry({\n failureCount,\n error,\n ...partialStateRef.current,\n })\n : retry,\n staleTime: Number.POSITIVE_INFINITY,\n refetchOnWindowFocus: false,\n });\n\n // Track callback invocations to prevent duplicates\n const callbackFiredRef = useRef<\"success\" | \"error\" | null>(null);\n\n // Compute derived state for both paths\n const reducerData = customReducer\n ? ((query.data as\n | { state: TState; count: number; status: \"streaming\" | \"complete\" }\n | undefined) ?? {\n state: initialState as TState,\n count: 0,\n status: \"streaming\" as const,\n })\n : null;\n\n const standardData = customReducer\n ? null\n : ((query.data as StreamState<TYield, TReturn> | undefined) ?? {\n items: [] as TYield[],\n count: 0,\n latest: null,\n returnValue: undefined,\n status: \"streaming\" as const,\n });\n\n const isComplete = customReducer\n ? query.isSuccess && reducerData?.status === \"complete\"\n : query.isSuccess && standardData?.status === \"complete\";\n\n const isErrorState = query.isError;\n\n // Fire onSuccess/onError callbacks\n useEffect(() => {\n if (isComplete && callbackFiredRef.current !== \"success\") {\n callbackFiredRef.current = \"success\";\n if (standardData) {\n onSuccess?.({\n count: standardData.count,\n items: standardData.items,\n returnValue: standardData.returnValue,\n });\n } else if (reducerData) {\n // For reducer mode, we don't have items/returnValue in the same way\n // but we can still call onSuccess with partial data from ref\n onSuccess?.({\n count: reducerData.count,\n items: partialStateRef.current.items,\n returnValue: undefined as TReturn | undefined,\n });\n }\n }\n if (isErrorState && callbackFiredRef.current !== \"error\" && query.error) {\n callbackFiredRef.current = \"error\";\n onError?.({\n error: query.error,\n count: partialStateRef.current.count,\n items: partialStateRef.current.items,\n });\n }\n }, [isComplete, isErrorState, onSuccess, onError, standardData, reducerData, query.error]);\n\n // Reset callback tracking on refetch\n useEffect(() => {\n if (query.fetchStatus === \"fetching\" && callbackFiredRef.current !== null) {\n callbackFiredRef.current = null;\n }\n }, [query.fetchStatus]);\n\n // Handle custom reducer result\n if (customReducer && reducerData) {\n const isReducerLoading =\n query.fetchStatus === \"fetching\" && reducerData.count === 0 && !query.isError;\n const isReducerStreaming =\n query.fetchStatus === \"fetching\" &&\n reducerData.count > 0 &&\n reducerData.status === \"streaming\";\n\n return {\n query: query as UseQueryResult<\n { state: TState; count: number; status: \"streaming\" | \"complete\" },\n Error\n >,\n state: reducerData.state,\n count: reducerData.count,\n refetch: query.refetch,\n isLoading: isReducerLoading,\n isStreaming: isReducerStreaming,\n isSuccess: isComplete,\n isError: query.isError,\n } as UseStreamReducerResult<TYield, TState>;\n }\n\n // Standard result (standardData is guaranteed non-null after the reducer early return)\n const data = standardData ?? {\n items: [] as TYield[],\n count: 0,\n latest: null,\n returnValue: undefined as TReturn | undefined,\n status: \"streaming\" as const,\n };\n\n // isLoading: fetching but no data yet (not just isPending which is true when disabled)\n const isLoading = query.fetchStatus === \"fetching\" && data.count === 0 && !query.isError;\n const isStreaming =\n query.fetchStatus === \"fetching\" && data.count > 0 && data.status === \"streaming\";\n\n return {\n query: query as UseQueryResult<StreamState<TYield, TReturn>, Error>,\n items: data.items,\n count: data.count,\n latest: data.latest,\n returnValue: data.returnValue,\n refetch: query.refetch,\n isLoading,\n isStreaming,\n isSuccess: isComplete,\n isError: query.isError,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// StreamRenderer Component\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Renders different content based on stream state using TanStack Query.\n *\n * @example\n * ```tsx\n * <StreamRenderer\n * source={fetchItems}\n * queryKey={['items']}\n * loading={() => <Spinner />}\n * streaming={({ count, latest }) => <p>Received {count}: {latest.name}</p>}\n * success={({ items }) => <List items={items} />}\n * error={({ error }) => <Error message={error.message} />}\n * />\n * ```\n */\nexport function StreamRenderer<TYield, TReturn = void>({\n source,\n queryKey,\n delay,\n onItem,\n onSuccess,\n onError,\n loading,\n streaming,\n success,\n error,\n children,\n mode,\n maxItems,\n}: StreamRendererProps<TYield, TReturn>) {\n // Build useStream options based on mode/maxItems\n const streamOptions = useMemo(() => {\n const base = { source, queryKey, delay, onItem, onSuccess, onError };\n if (maxItems !== undefined) {\n return { ...base, maxItems };\n }\n if (mode === \"latest\") {\n return { ...base, mode: \"latest\" as const };\n }\n return base;\n }, [source, queryKey, delay, onItem, onSuccess, onError, mode, maxItems]);\n\n const stream = useStream<TYield, TReturn>(streamOptions);\n\n // Build children data based on current status\n const getChildrenData = (): ChildrenData<TYield, TReturn> => {\n if (stream.isError && stream.query.error) {\n return {\n status: \"error\",\n count: stream.count,\n items: stream.items,\n latest: stream.latest,\n returnValue: undefined,\n error: stream.query.error,\n };\n }\n if (stream.isSuccess) {\n return {\n status: \"success\",\n count: stream.count,\n items: stream.items,\n latest: stream.latest,\n returnValue: stream.returnValue,\n error: null,\n };\n }\n if (stream.isStreaming && stream.latest !== null) {\n return {\n status: \"streaming\",\n count: stream.count,\n items: stream.items,\n latest: stream.latest,\n returnValue: undefined,\n error: null,\n };\n }\n // Loading state\n return {\n status: \"loading\",\n count: 0,\n items: [] as TYield[],\n latest: null,\n returnValue: undefined,\n error: null,\n } as ChildrenData<TYield, TReturn>;\n };\n\n const childrenData = getChildrenData();\n const fallback = resolveRenderProp(children, childrenData, null);\n\n // Loading (before any data)\n if (stream.isLoading) {\n return resolveRenderPropNoData(loading, fallback);\n }\n\n // Error\n if (stream.isError && stream.query.error) {\n return resolveRenderProp(\n error,\n {\n error: stream.query.error,\n count: stream.count,\n items: stream.items,\n },\n fallback,\n );\n }\n\n // Success (stream complete)\n if (stream.isSuccess) {\n return resolveRenderProp(\n success,\n {\n count: stream.count,\n items: stream.items,\n returnValue: stream.returnValue,\n },\n fallback,\n );\n }\n\n // Streaming\n if (stream.isStreaming && stream.latest !== null) {\n return resolveRenderProp(\n streaming,\n {\n count: stream.count,\n latest: stream.latest,\n items: stream.items,\n },\n fallback,\n );\n }\n\n // Fallback (initial state before streaming starts)\n return resolveRenderPropNoData(loading, fallback);\n}\n","import type { ComponentType, ReactNode } from \"react\";\n\ntype AnyProps = Record<string, unknown>;\n\n/**\n * A provider entry for {@link Providers}.\n *\n * This is intentionally \"erased\" (non-generic) so you can compose a heterogeneous list of\n * providers (each with different props) in one array.\n *\n * Use {@link provider} to create a `ProviderSpec` with correct prop inference.\n */\nexport type ProviderSpec = readonly [Provider: ComponentType<unknown>, props: AnyProps];\n\ntype RequiredKeys<T> = {\n // biome-ignore lint/complexity/noBannedTypes: Intentional optional-key detection pattern.\n [K in keyof T]-?: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\ntype ProviderInputProps<TProps extends AnyProps> = Omit<TProps, \"children\">;\n\ntype ProviderArgProps<TProps extends AnyProps> = keyof ProviderInputProps<TProps> extends never\n ? Record<string, never>\n : ProviderInputProps<TProps>;\n\ntype ProviderPropsArg<TProps extends AnyProps> =\n RequiredKeys<ProviderInputProps<TProps>> extends never\n ? [props?: ProviderArgProps<TProps>]\n : [props: ProviderInputProps<TProps>];\n\n/**\n * Create a typed `ProviderSpec` tuple with prop inference and required-prop enforcement.\n *\n * - If a provider has required props (excluding `children`), the props argument is required.\n * - If a provider has no props (excluding `children`), the props argument is optional and\n * rejects extra keys.\n *\n * @param Provider - The React component to use as a provider.\n * @param args - Props to pass to the provider (excluding `children`).\n * @returns A `ProviderSpec` tuple.\n *\n * @example\n * ```tsx\n * import { provider, Providers } from \"xantiagoma/react\";\n *\n * // Provider with required props\n * provider(ThemeProvider, { theme: \"dark\" })\n *\n * // Provider with no props (optional)\n * provider(PermissionsProvider)\n * ```\n */\nexport const provider = <TProps extends AnyProps>(\n Provider: ComponentType<TProps>,\n ...args: ProviderPropsArg<TProps>\n): ProviderSpec => {\n const props = (args[0] ?? {}) as AnyProps;\n return [Provider as ComponentType<unknown>, props] as const;\n};\n\n/**\n * Props for the {@link Providers} component.\n */\nexport type ProvidersProps = {\n /** List of providers (outer → inner), created with {@link provider}. */\n providers: readonly ProviderSpec[];\n children?: ReactNode;\n};\n\nconst getProviderKey = (providerComponent: unknown, index: number): string => {\n const providerName =\n (providerComponent as { displayName?: string }).displayName ??\n (providerComponent as { name?: string }).name ??\n \"Provider\";\n return `${providerName}:${index}`;\n};\n\nfunction ProviderComponent({\n Provider,\n props,\n children,\n}: {\n Provider: ComponentType<unknown>;\n props: AnyProps;\n children: ReactNode;\n}): ReactNode {\n const Component = Provider as ComponentType<AnyProps & { children?: ReactNode }>;\n return <Component {...props}>{children}</Component>;\n}\n\n/**\n * Compose multiple React providers without deeply nesting JSX.\n * Eliminates \"provider hell\" by flattening the tree into an array.\n *\n * @example\n * ```tsx\n * import { Providers, provider } from \"xantiagoma/react\";\n *\n * <Providers\n * providers={[\n * provider(QueryClientProvider, { client: queryClient }),\n * provider(ThemeProvider, { theme: \"dark\" }),\n * provider(AuthProvider),\n * ]}\n * >\n * <App />\n * </Providers>\n * ```\n */\nexport const Providers = ({ providers, children }: ProvidersProps): ReactNode =>\n providers.reduceRight<ReactNode>(\n (acc, [Provider, props], index) => (\n <ProviderComponent key={getProviderKey(Provider, index)} Provider={Provider} props={props}>\n {acc}\n </ProviderComponent>\n ),\n children ?? null,\n );\n"],"mappings":"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"}
1
+ {"version":3,"file":"entry-react.cjs","names":["resolveStreamSource","wait","isGenerator","isAsyncGenerator","AsyncChannel","isAsyncIterable","isIterable","toAsyncIterable","Component"],"sources":["../src/stream-renderer.tsx","../src/providers.tsx","../src/use-prevent-auto-focus.ts","../src/use-dynamic-refs.ts"],"sourcesContent":["import {\n AsyncChannel,\n isAsyncGenerator,\n isAsyncIterable,\n isGenerator,\n isIterable,\n resolveStreamSource,\n type StreamSource,\n toAsyncIterable,\n wait,\n} from \"./index.ts\";\nimport {\n experimental_streamedQuery as streamedQuery,\n type UseQueryResult,\n useQuery,\n} from \"@tanstack/react-query\";\nimport { type ReactNode, useEffect, useMemo, useRef } from \"react\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Render prop: can be ReactNode or function returning ReactNode */\ntype RenderProp<TData> = ReactNode | ((data: TData) => ReactNode);\n\n/** Render prop without data (for loading) */\ntype RenderPropNoData = ReactNode | (() => ReactNode);\n\n/** Stream state with accumulated items */\nexport type StreamState<TYield, TReturn = void> = {\n items: TYield[];\n count: number;\n latest: TYield | null;\n returnValue: TReturn | undefined;\n status: \"streaming\" | \"complete\";\n};\n\n/** Granular delay configuration */\nexport type DelayConfig = {\n items?: number;\n first?: number;\n result?: number;\n};\n\n/** Streaming data passed to streaming render prop */\nexport type StreamingData<TYield> = {\n count: number;\n latest: TYield;\n items: TYield[];\n};\n\n/** Success data passed to success render prop */\nexport type SuccessData<TYield, TReturn> = {\n count: number;\n items: TYield[];\n returnValue: TReturn | undefined;\n};\n\n/** Error data passed to error render prop */\nexport type ErrorData<TYield> = {\n error: Error;\n count: number;\n items: TYield[];\n};\n\n/** Data passed to onItem callback */\nexport type OnItemData<TYield> = {\n item: TYield;\n count: number;\n items: TYield[];\n};\n\n/** Data passed to onSuccess callback */\nexport type OnSuccessData<TYield, TReturn = void> = {\n count: number;\n items: TYield[];\n returnValue: TReturn | undefined;\n};\n\n/** Data passed to onError callback */\nexport type OnErrorData<TYield> = {\n error: Error;\n count: number;\n items: TYield[];\n};\n\n/** Children render prop data - includes status and all relevant data */\nexport type ChildrenData<TYield, TReturn = void> =\n | {\n status: \"loading\";\n count: 0;\n items: [];\n latest: null;\n returnValue: undefined;\n error: null;\n }\n | {\n status: \"streaming\";\n count: number;\n items: TYield[];\n latest: TYield;\n returnValue: undefined;\n error: null;\n }\n | {\n status: \"success\";\n count: number;\n items: TYield[];\n latest: TYield | null;\n returnValue: TReturn | undefined;\n error: null;\n }\n | {\n status: \"error\";\n count: number;\n items: TYield[];\n latest: TYield | null;\n returnValue: undefined;\n error: Error;\n };\n\n/** Base props for StreamRenderer */\nexport type StreamRendererBaseProps<TYield, TReturn = void> = {\n /** The stream source - can be instance or factory */\n source: StreamSource<TYield> | null | undefined;\n\n /** Unique key for the query cache */\n queryKey?: unknown[];\n /** Delay configuration for visualizing sync streams */\n delay?: number | DelayConfig;\n /** Called for each item received */\n onItem?: (data: OnItemData<TYield>) => void;\n /** Called when stream completes successfully */\n onSuccess?: (data: OnSuccessData<TYield, TReturn>) => void;\n /** Called when stream errors */\n onError?: (data: OnErrorData<TYield>) => void;\n /** Whether to start streaming immediately (default: true) */\n enabled?: boolean;\n /**\n * Retry configuration on error (default: false = no retries)\n * - `false`: no retries\n * - `true`: use TanStack Query default (3 retries)\n * - `number`: retry that many times\n * - `(data) => boolean`: custom retry logic with access to partial data\n */\n retry?: boolean | number | ((data: RetryData<TYield>) => boolean);\n};\n\n/** Props for StreamRenderer - supports all accumulation modes */\nexport type StreamRendererProps<TYield, TReturn = void> = StreamRendererBaseProps<\n TYield,\n TReturn\n> & {\n /**\n * Accumulation mode (mutually exclusive with maxItems)\n * - `\"accumulate\"` (default): keep all items\n * - `\"latest\"`: only keep the most recent item\n */\n mode?: StreamMode;\n /** Keep only the last N items (mutually exclusive with mode) */\n maxItems?: number;\n /** Render while loading - ReactNode or () => ReactNode */\n loading?: RenderPropNoData;\n /** Render while streaming - ReactNode or (data) => ReactNode */\n streaming?: RenderProp<StreamingData<TYield>>;\n /** Render on success - ReactNode or (data) => ReactNode */\n success?: RenderProp<SuccessData<TYield, TReturn>>;\n /** Render on error - ReactNode or (data) => ReactNode */\n error?: RenderProp<ErrorData<TYield>>;\n /** Fallback/universal render prop - ReactNode or (data) => ReactNode */\n children?: RenderProp<ChildrenData<TYield, TReturn>>;\n};\n\n/** Data passed to retry callback */\nexport type RetryData<TYield> = {\n /** Number of times the query has failed (starts at 0) */\n failureCount: number;\n /** The error that caused the failure */\n error: Error;\n /** Items collected before the error */\n items: TYield[];\n /** Number of items collected before the error */\n count: number;\n /** Last item received before the error (null if none) */\n latest: TYield | null;\n};\n\n/**\n * Accumulation mode for stream items.\n * - `\"accumulate\"` (default): keep all items in memory\n * - `\"latest\"`: only keep the most recent item\n */\nexport type StreamMode = \"accumulate\" | \"latest\";\n\n/**\n * Custom reducer for stream state.\n * Receives current state and new item, returns new state.\n */\nexport type StreamReducer<TYield, TState> = (state: TState, item: TYield, count: number) => TState;\n\n/** Base options shared by all useStream configurations */\ntype UseStreamBaseOptions<TYield, TReturn = void> = {\n /** The stream source - can be instance or factory */\n source: StreamSource<TYield> | null | undefined;\n /** Unique key for the query cache */\n queryKey?: unknown[];\n /** Delay configuration for visualizing sync streams */\n delay?: number | DelayConfig;\n /** Called for each item received */\n onItem?: (data: OnItemData<TYield>) => void;\n /** Called when stream completes successfully */\n onSuccess?: (data: OnSuccessData<TYield, TReturn>) => void;\n /** Called when stream errors */\n onError?: (data: OnErrorData<TYield>) => void;\n /** Whether to start streaming immediately (default: true) */\n enabled?: boolean;\n /**\n * Retry configuration on error (default: false = no retries)\n * - `false`: no retries\n * - `true`: use TanStack Query default (3 retries)\n * - `number`: retry that many times\n * - `(data) => boolean`: custom retry logic with access to partial data\n */\n retry?: boolean | number | ((data: RetryData<TYield>) => boolean);\n};\n\n/** Options with default accumulation (all items) */\ntype UseStreamAccumulateOptions<TYield, TReturn = void> = UseStreamBaseOptions<TYield, TReturn> & {\n mode?: \"accumulate\";\n maxItems?: never;\n reducer?: never;\n};\n\n/** Options with \"latest\" mode (only keep last item) */\ntype UseStreamLatestOptions<TYield, TReturn = void> = UseStreamBaseOptions<TYield, TReturn> & {\n mode: \"latest\";\n maxItems?: never;\n reducer?: never;\n};\n\n/** Options with maxItems (sliding window) */\ntype UseStreamMaxItemsOptions<TYield, TReturn = void> = UseStreamBaseOptions<TYield, TReturn> & {\n mode?: never;\n /** Keep only the last N items (sliding window) */\n maxItems: number;\n reducer?: never;\n};\n\n/** Options with custom reducer */\ntype UseStreamReducerOptions<TYield, TState, TReturn = void> = UseStreamBaseOptions<\n TYield,\n TReturn\n> & {\n mode?: never;\n maxItems?: never;\n /** Custom reducer function for complete control over state */\n reducer: StreamReducer<TYield, TState>;\n /** Initial state for custom reducer */\n initialState: TState;\n};\n\n/** Props for useStream hook - mutually exclusive accumulation options */\nexport type UseStreamOptions<TYield, TReturn = void, TState = unknown> =\n | UseStreamAccumulateOptions<TYield, TReturn>\n | UseStreamLatestOptions<TYield, TReturn>\n | UseStreamMaxItemsOptions<TYield, TReturn>\n | UseStreamReducerOptions<TYield, TState, TReturn>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction normalizeDelay(delay: number | DelayConfig | undefined): DelayConfig {\n if (delay === undefined) {\n return {};\n }\n if (typeof delay === \"number\") {\n return { first: delay, items: delay, result: delay };\n }\n return delay;\n}\n\n/** Resolve a render prop (ReactNode or function) */\nfunction resolveRenderProp<TData>(\n prop: RenderProp<TData> | undefined,\n data: TData,\n fallback: ReactNode,\n): ReactNode {\n if (prop === undefined) {\n return fallback;\n }\n if (typeof prop === \"function\") {\n return prop(data);\n }\n return prop;\n}\n\n/** Resolve a render prop without data (for loading) */\nfunction resolveRenderPropNoData(\n prop: RenderPropNoData | undefined,\n fallback: ReactNode,\n): ReactNode {\n if (prop === undefined) {\n return fallback;\n }\n if (typeof prop === \"function\") {\n return prop();\n }\n return prop;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// useStream Hook\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Result type for standard useStream (accumulate, latest, maxItems modes) */\nexport type UseStreamResult<TYield, TReturn = void> = {\n query: UseQueryResult<StreamState<TYield, TReturn>, Error>;\n /** Items received (all, last N, or just latest depending on mode) */\n items: TYield[];\n /** Total number of items received (even if not all kept in memory) */\n count: number;\n /** Most recent item */\n latest: TYield | null;\n /** Return value from generator (if complete) */\n returnValue: TReturn | undefined;\n /** Restart the stream */\n refetch: () => void;\n // Status flags\n isLoading: boolean;\n isStreaming: boolean;\n isSuccess: boolean;\n isError: boolean;\n};\n\n/** Result type for useStream with custom reducer */\nexport type UseStreamReducerResult<_TYield, TState> = {\n query: UseQueryResult<{ state: TState; count: number; status: \"streaming\" | \"complete\" }, Error>;\n /** Custom state from reducer */\n state: TState;\n /** Total number of items received */\n count: number;\n /** Restart the stream */\n refetch: () => void;\n // Status flags\n isLoading: boolean;\n isStreaming: boolean;\n isSuccess: boolean;\n isError: boolean;\n};\n\n/**\n * Hook for consuming streams using TanStack Query's streamedQuery.\n *\n * @example\n * ```tsx\n * // Default: accumulate all items\n * const { items, isStreaming, count } = useStream({\n * source: fetchItems,\n * queryKey: ['my-stream'],\n * });\n *\n * // Latest only: keep just the most recent item\n * const { items, latest } = useStream({\n * source: sseEvents,\n * mode: \"latest\",\n * });\n *\n * // Sliding window: keep last N items\n * const { items } = useStream({\n * source: logStream,\n * maxItems: 100,\n * });\n *\n * // Custom reducer: full control over state\n * const { state } = useStream({\n * source: events,\n * reducer: (state, event) => ({ ...state, [event.id]: event }),\n * initialState: {},\n * });\n * ```\n */\n// Overload: custom reducer\nexport function useStream<TYield, TState, TReturn = void>(\n options: UseStreamReducerOptions<TYield, TState, TReturn>,\n): UseStreamReducerResult<TYield, TState>;\n\n// Overload: standard modes (accumulate, latest, maxItems)\nexport function useStream<TYield, TReturn = void>(\n options:\n | UseStreamAccumulateOptions<TYield, TReturn>\n | UseStreamLatestOptions<TYield, TReturn>\n | UseStreamMaxItemsOptions<TYield, TReturn>,\n): UseStreamResult<TYield, TReturn>;\n\n// Implementation\nexport function useStream<TYield, TReturn = void, TState = unknown>(\n options: UseStreamOptions<TYield, TReturn, TState>,\n): UseStreamResult<TYield, TReturn> | UseStreamReducerResult<TYield, TState> {\n const {\n source,\n queryKey,\n delay,\n onItem,\n onSuccess,\n onError,\n enabled = true,\n retry = false,\n } = options;\n\n // Extract mode options\n const mode = \"mode\" in options ? options.mode : undefined;\n const maxItems = \"maxItems\" in options ? options.maxItems : undefined;\n const customReducer = \"reducer\" in options ? options.reducer : undefined;\n const initialState = \"initialState\" in options ? options.initialState : undefined;\n\n const delayConfig = useMemo(() => normalizeDelay(delay), [delay]);\n\n // Track partial state for retry callback\n const partialStateRef = useRef<{\n items: TYield[];\n count: number;\n latest: TYield | null;\n }>({ items: [], count: 0, latest: null });\n\n // Create a stable query key\n const stableQueryKey = useMemo(() => queryKey ?? [\"stream\", source], [queryKey, source]);\n\n const query = useQuery({\n queryKey: stableQueryKey,\n enabled: enabled && source != null,\n queryFn: streamedQuery({\n async *streamFn() {\n // Reset partial state on new fetch\n partialStateRef.current = { items: [], count: 0, latest: null };\n\n const resolved = resolveStreamSource(source as StreamSource<TYield>);\n let isFirst = true;\n let itemCount = 0;\n\n const processDelay = async () => {\n if (isFirst && delayConfig.first) {\n await wait(delayConfig.first);\n isFirst = false;\n } else if (!isFirst && delayConfig.items) {\n await wait(delayConfig.items);\n } else {\n isFirst = false;\n }\n };\n\n const trackItem = (item: TYield) => {\n partialStateRef.current = {\n items: [...partialStateRef.current.items, item],\n count: partialStateRef.current.count + 1,\n latest: item,\n };\n };\n\n const emitItem = (item: TYield) => {\n onItem?.({\n item,\n count: partialStateRef.current.count,\n items: partialStateRef.current.items,\n });\n };\n\n // Handle sync Generator (captures return value)\n if (isGenerator<TYield, TReturn>(resolved)) {\n let result = resolved.next();\n while (!result.done) {\n await processDelay();\n itemCount += 1;\n trackItem(result.value);\n emitItem(result.value);\n yield {\n type: \"item\" as const,\n value: result.value,\n count: itemCount,\n };\n result = resolved.next();\n }\n if (delayConfig.result) {\n await wait(delayConfig.result);\n }\n yield { type: \"return\" as const, value: result.value };\n return;\n }\n\n // Handle AsyncGenerator (captures return value)\n if (isAsyncGenerator<TYield, TReturn>(resolved)) {\n let result = await resolved.next();\n while (!result.done) {\n await processDelay();\n itemCount += 1;\n trackItem(result.value);\n emitItem(result.value);\n yield {\n type: \"item\" as const,\n value: result.value,\n count: itemCount,\n };\n result = await resolved.next();\n }\n if (delayConfig.result) {\n await wait(delayConfig.result);\n }\n yield { type: \"return\" as const, value: result.value };\n return;\n }\n\n // Handle AsyncChannel (captures return value)\n if (resolved instanceof AsyncChannel) {\n const iterator = resolved[Symbol.asyncIterator]();\n let result = await iterator.next();\n while (!result.done) {\n await processDelay();\n itemCount += 1;\n trackItem(result.value);\n emitItem(result.value);\n yield {\n type: \"item\" as const,\n value: result.value,\n count: itemCount,\n };\n result = await iterator.next();\n }\n if (delayConfig.result) {\n await wait(delayConfig.result);\n }\n yield { type: \"return\" as const, value: result.value };\n return;\n }\n\n // Handle plain iterables/iterators (no return value)\n const iterable =\n isAsyncIterable(resolved) || isIterable(resolved)\n ? toAsyncIterable(resolved)\n : toAsyncIterable(resolved as AsyncIterator<TYield>);\n\n for await (const item of iterable) {\n await processDelay();\n itemCount += 1;\n trackItem(item);\n emitItem(item);\n yield { type: \"item\" as const, value: item, count: itemCount };\n }\n\n if (delayConfig.result) {\n await wait(delayConfig.result);\n }\n yield { type: \"complete\" as const };\n },\n\n reducer: (\n state:\n | StreamState<TYield, TReturn>\n | { state: TState; count: number; status: \"streaming\" | \"complete\" },\n chunk:\n | { type: \"item\"; value: TYield; count: number }\n | { type: \"return\"; value: TReturn }\n | { type: \"complete\" },\n ) => {\n // Custom reducer mode\n if (customReducer) {\n const s = state as {\n state: TState;\n count: number;\n status: \"streaming\" | \"complete\";\n };\n if (chunk.type === \"item\") {\n return {\n state: customReducer(s.state, chunk.value, chunk.count),\n count: chunk.count,\n status: \"streaming\" as const,\n };\n }\n return { ...s, status: \"complete\" as const };\n }\n\n // Standard modes\n const s = state as StreamState<TYield, TReturn>;\n if (chunk.type === \"item\") {\n let newItems: TYield[];\n if (mode === \"latest\") {\n // Only keep the latest item\n newItems = [chunk.value];\n } else if (maxItems !== undefined) {\n // Sliding window: keep last N items\n newItems = [...s.items, chunk.value].slice(-maxItems);\n } else {\n // Default: accumulate all\n newItems = [...s.items, chunk.value];\n }\n return {\n ...s,\n items: newItems,\n count: chunk.count,\n latest: chunk.value,\n status: \"streaming\" as const,\n };\n }\n if (chunk.type === \"return\") {\n return {\n ...s,\n returnValue: chunk.value,\n status: \"complete\" as const,\n };\n }\n // complete\n return { ...s, status: \"complete\" as const };\n },\n\n initialValue: customReducer\n ? {\n state: initialState as TState,\n count: 0,\n status: \"streaming\" as const,\n }\n : ({\n items: [] as TYield[],\n count: 0,\n latest: null,\n returnValue: undefined,\n status: \"streaming\" as const,\n } as StreamState<TYield, TReturn>),\n\n refetchMode: \"reset\",\n }),\n // Wrap our callback to include partial state data\n retry:\n typeof retry === \"function\"\n ? (failureCount: number, error: Error) =>\n retry({\n failureCount,\n error,\n ...partialStateRef.current,\n })\n : retry,\n staleTime: Number.POSITIVE_INFINITY,\n refetchOnWindowFocus: false,\n });\n\n // Track callback invocations to prevent duplicates\n const callbackFiredRef = useRef<\"success\" | \"error\" | null>(null);\n\n // Compute derived state for both paths\n const reducerData = customReducer\n ? ((query.data as\n | { state: TState; count: number; status: \"streaming\" | \"complete\" }\n | undefined) ?? {\n state: initialState as TState,\n count: 0,\n status: \"streaming\" as const,\n })\n : null;\n\n const standardData = customReducer\n ? null\n : ((query.data as StreamState<TYield, TReturn> | undefined) ?? {\n items: [] as TYield[],\n count: 0,\n latest: null,\n returnValue: undefined,\n status: \"streaming\" as const,\n });\n\n const isComplete = customReducer\n ? query.isSuccess && reducerData?.status === \"complete\"\n : query.isSuccess && standardData?.status === \"complete\";\n\n const isErrorState = query.isError;\n\n // Fire onSuccess/onError callbacks\n useEffect(() => {\n if (isComplete && callbackFiredRef.current !== \"success\") {\n callbackFiredRef.current = \"success\";\n if (standardData) {\n onSuccess?.({\n count: standardData.count,\n items: standardData.items,\n returnValue: standardData.returnValue,\n });\n } else if (reducerData) {\n // For reducer mode, we don't have items/returnValue in the same way\n // but we can still call onSuccess with partial data from ref\n onSuccess?.({\n count: reducerData.count,\n items: partialStateRef.current.items,\n returnValue: undefined as TReturn | undefined,\n });\n }\n }\n if (isErrorState && callbackFiredRef.current !== \"error\" && query.error) {\n callbackFiredRef.current = \"error\";\n onError?.({\n error: query.error,\n count: partialStateRef.current.count,\n items: partialStateRef.current.items,\n });\n }\n }, [isComplete, isErrorState, onSuccess, onError, standardData, reducerData, query.error]);\n\n // Reset callback tracking on refetch\n useEffect(() => {\n if (query.fetchStatus === \"fetching\" && callbackFiredRef.current !== null) {\n callbackFiredRef.current = null;\n }\n }, [query.fetchStatus]);\n\n // Handle custom reducer result\n if (customReducer && reducerData) {\n const isReducerLoading =\n query.fetchStatus === \"fetching\" && reducerData.count === 0 && !query.isError;\n const isReducerStreaming =\n query.fetchStatus === \"fetching\" &&\n reducerData.count > 0 &&\n reducerData.status === \"streaming\";\n\n return {\n query: query as UseQueryResult<\n { state: TState; count: number; status: \"streaming\" | \"complete\" },\n Error\n >,\n state: reducerData.state,\n count: reducerData.count,\n refetch: query.refetch,\n isLoading: isReducerLoading,\n isStreaming: isReducerStreaming,\n isSuccess: isComplete,\n isError: query.isError,\n } as UseStreamReducerResult<TYield, TState>;\n }\n\n // Standard result (standardData is guaranteed non-null after the reducer early return)\n const data = standardData ?? {\n items: [] as TYield[],\n count: 0,\n latest: null,\n returnValue: undefined as TReturn | undefined,\n status: \"streaming\" as const,\n };\n\n // isLoading: fetching but no data yet (not just isPending which is true when disabled)\n const isLoading = query.fetchStatus === \"fetching\" && data.count === 0 && !query.isError;\n const isStreaming =\n query.fetchStatus === \"fetching\" && data.count > 0 && data.status === \"streaming\";\n\n return {\n query: query as UseQueryResult<StreamState<TYield, TReturn>, Error>,\n items: data.items,\n count: data.count,\n latest: data.latest,\n returnValue: data.returnValue,\n refetch: query.refetch,\n isLoading,\n isStreaming,\n isSuccess: isComplete,\n isError: query.isError,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// StreamRenderer Component\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Renders different content based on stream state using TanStack Query.\n *\n * @example\n * ```tsx\n * <StreamRenderer\n * source={fetchItems}\n * queryKey={['items']}\n * loading={() => <Spinner />}\n * streaming={({ count, latest }) => <p>Received {count}: {latest.name}</p>}\n * success={({ items }) => <List items={items} />}\n * error={({ error }) => <Error message={error.message} />}\n * />\n * ```\n */\nexport function StreamRenderer<TYield, TReturn = void>({\n source,\n queryKey,\n delay,\n onItem,\n onSuccess,\n onError,\n loading,\n streaming,\n success,\n error,\n children,\n mode,\n maxItems,\n}: StreamRendererProps<TYield, TReturn>) {\n // Build useStream options based on mode/maxItems\n const streamOptions = useMemo(() => {\n const base = { source, queryKey, delay, onItem, onSuccess, onError };\n if (maxItems !== undefined) {\n return { ...base, maxItems };\n }\n if (mode === \"latest\") {\n return { ...base, mode: \"latest\" as const };\n }\n return base;\n }, [source, queryKey, delay, onItem, onSuccess, onError, mode, maxItems]);\n\n const stream = useStream<TYield, TReturn>(streamOptions);\n\n // Build children data based on current status\n const getChildrenData = (): ChildrenData<TYield, TReturn> => {\n if (stream.isError && stream.query.error) {\n return {\n status: \"error\",\n count: stream.count,\n items: stream.items,\n latest: stream.latest,\n returnValue: undefined,\n error: stream.query.error,\n };\n }\n if (stream.isSuccess) {\n return {\n status: \"success\",\n count: stream.count,\n items: stream.items,\n latest: stream.latest,\n returnValue: stream.returnValue,\n error: null,\n };\n }\n if (stream.isStreaming && stream.latest !== null) {\n return {\n status: \"streaming\",\n count: stream.count,\n items: stream.items,\n latest: stream.latest,\n returnValue: undefined,\n error: null,\n };\n }\n // Loading state\n return {\n status: \"loading\",\n count: 0,\n items: [] as TYield[],\n latest: null,\n returnValue: undefined,\n error: null,\n } as ChildrenData<TYield, TReturn>;\n };\n\n const childrenData = getChildrenData();\n const fallback = resolveRenderProp(children, childrenData, null);\n\n // Loading (before any data)\n if (stream.isLoading) {\n return resolveRenderPropNoData(loading, fallback);\n }\n\n // Error\n if (stream.isError && stream.query.error) {\n return resolveRenderProp(\n error,\n {\n error: stream.query.error,\n count: stream.count,\n items: stream.items,\n },\n fallback,\n );\n }\n\n // Success (stream complete)\n if (stream.isSuccess) {\n return resolveRenderProp(\n success,\n {\n count: stream.count,\n items: stream.items,\n returnValue: stream.returnValue,\n },\n fallback,\n );\n }\n\n // Streaming\n if (stream.isStreaming && stream.latest !== null) {\n return resolveRenderProp(\n streaming,\n {\n count: stream.count,\n latest: stream.latest,\n items: stream.items,\n },\n fallback,\n );\n }\n\n // Fallback (initial state before streaming starts)\n return resolveRenderPropNoData(loading, fallback);\n}\n","import type { ComponentType, ReactNode } from \"react\";\n\ntype AnyProps = Record<string, unknown>;\n\n/**\n * A provider entry for {@link Providers}.\n *\n * This is intentionally \"erased\" (non-generic) so you can compose a heterogeneous list of\n * providers (each with different props) in one array.\n *\n * Use {@link provider} to create a `ProviderSpec` with correct prop inference.\n */\nexport type ProviderSpec = readonly [Provider: ComponentType<unknown>, props: AnyProps];\n\ntype RequiredKeys<T> = {\n // biome-ignore lint/complexity/noBannedTypes: Intentional optional-key detection pattern.\n [K in keyof T]-?: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\ntype ProviderInputProps<TProps extends AnyProps> = Omit<TProps, \"children\">;\n\ntype ProviderArgProps<TProps extends AnyProps> = keyof ProviderInputProps<TProps> extends never\n ? Record<string, never>\n : ProviderInputProps<TProps>;\n\ntype ProviderPropsArg<TProps extends AnyProps> =\n RequiredKeys<ProviderInputProps<TProps>> extends never\n ? [props?: ProviderArgProps<TProps>]\n : [props: ProviderInputProps<TProps>];\n\n/**\n * Create a typed `ProviderSpec` tuple with prop inference and required-prop enforcement.\n *\n * - If a provider has required props (excluding `children`), the props argument is required.\n * - If a provider has no props (excluding `children`), the props argument is optional and\n * rejects extra keys.\n *\n * @param Provider - The React component to use as a provider.\n * @param args - Props to pass to the provider (excluding `children`).\n * @returns A `ProviderSpec` tuple.\n *\n * @example\n * ```tsx\n * import { provider, Providers } from \"xantiagoma/react\";\n *\n * // Provider with required props\n * provider(ThemeProvider, { theme: \"dark\" })\n *\n * // Provider with no props (optional)\n * provider(PermissionsProvider)\n * ```\n */\nexport const provider = <TProps extends AnyProps>(\n Provider: ComponentType<TProps>,\n ...args: ProviderPropsArg<TProps>\n): ProviderSpec => {\n const props = (args[0] ?? {}) as AnyProps;\n return [Provider as ComponentType<unknown>, props] as const;\n};\n\n/**\n * Props for the {@link Providers} component.\n */\nexport type ProvidersProps = {\n /** List of providers (outer → inner), created with {@link provider}. */\n providers: readonly ProviderSpec[];\n children?: ReactNode;\n};\n\nconst getProviderKey = (providerComponent: unknown, index: number): string => {\n const providerName =\n (providerComponent as { displayName?: string }).displayName ??\n (providerComponent as { name?: string }).name ??\n \"Provider\";\n return `${providerName}:${index}`;\n};\n\nfunction ProviderComponent({\n Provider,\n props,\n children,\n}: {\n Provider: ComponentType<unknown>;\n props: AnyProps;\n children: ReactNode;\n}): ReactNode {\n const Component = Provider as ComponentType<AnyProps & { children?: ReactNode }>;\n return <Component {...props}>{children}</Component>;\n}\n\n/**\n * Compose multiple React providers without deeply nesting JSX.\n * Eliminates \"provider hell\" by flattening the tree into an array.\n *\n * @example\n * ```tsx\n * import { Providers, provider } from \"xantiagoma/react\";\n *\n * <Providers\n * providers={[\n * provider(QueryClientProvider, { client: queryClient }),\n * provider(ThemeProvider, { theme: \"dark\" }),\n * provider(AuthProvider),\n * ]}\n * >\n * <App />\n * </Providers>\n * ```\n */\nexport const Providers = ({ providers, children }: ProvidersProps): ReactNode =>\n providers.reduceRight<ReactNode>(\n (acc, [Provider, props], index) => (\n <ProviderComponent key={getProviderKey(Provider, index)} Provider={Provider} props={props}>\n {acc}\n </ProviderComponent>\n ),\n children ?? null,\n );\n","import { useCallback, useRef } from \"react\";\n\n/**\n * Hook that prevents auto-focus behavior while still maintaining focus management.\n * Useful for modal dialogs (e.g. Radix Dialog) where you want to control the initial focus.\n *\n * @template E - Element type, defaults to HTMLDivElement\n * @returns Object containing:\n * - `ref` — React ref to attach to the element\n * - `onOpenAutoFocus` — Event handler to prevent default focus behavior\n * - `tabIndex` — `-1` to make the element programmatically focusable\n *\n * @example\n * ```tsx\n * import { usePreventAutoFocus } from \"xantiagoma/react\";\n *\n * function MyDialog() {\n * const preventAutoFocus = usePreventAutoFocus();\n *\n * return (\n * <DialogContent {...preventAutoFocus}>\n * <p>Dialog content here</p>\n * </DialogContent>\n * );\n * }\n * ```\n */\nexport function usePreventAutoFocus<E extends HTMLElement = HTMLDivElement>() {\n const ref = useRef<E>(null);\n\n const onOpenAutoFocus = useCallback((event: Event) => {\n event.preventDefault();\n ref.current?.focus({ preventScroll: true });\n }, []);\n\n return { ref, onOpenAutoFocus, tabIndex: -1 as const };\n}\n","import { createRef, useMemo, type RefObject } from \"react\";\n\n/**\n * Options for {@link useDynamicRefs}.\n */\nexport interface UseDynamicRefsOptions {\n /** Optional namespace prefix. Changing the prefix creates a new ref map. */\n prefix?: string;\n}\n\n/** The internal Map type for a given ref element type. */\nexport type RefMap<T> = Map<string, RefObject<T | null>>;\n\n/**\n * Returns a ref for the given key, creating one if it doesn't exist yet.\n * Overloads ensure that calling without a key returns `undefined`.\n */\nexport interface DynamicRefGetter<T> {\n (): undefined;\n (key: string): RefObject<T | null>;\n}\n\nfunction createRefGetter<T>(refMap: RefMap<T>): DynamicRefGetter<T> {\n function getRef(): undefined;\n function getRef(key: string): RefObject<T | null>;\n function getRef(key?: string): RefObject<T | null> | undefined {\n if (!key) {\n return undefined;\n }\n const existing = refMap.get(key);\n if (existing) return existing;\n\n const ref = createRef<T>();\n refMap.set(key, ref);\n return ref;\n }\n return getRef as DynamicRefGetter<T>;\n}\n\n/**\n * React hook that returns a ref \"getter\" function.\n * Call the getter with a string key to get (or create) a `RefObject` for that key.\n * Useful for dynamic lists, tabs, or any UI where the number of refs isn't known at compile time.\n *\n * @template T - The element type for the refs (e.g. `HTMLDivElement`)\n * @param options - Optional configuration\n * @returns A {@link DynamicRefGetter} function\n *\n * @example\n * ```tsx\n * import { useDynamicRefs } from \"xantiagoma/react\";\n *\n * function TabList({ tabs }: { tabs: string[] }) {\n * const getRef = useDynamicRefs<HTMLButtonElement>();\n *\n * return (\n * <div>\n * {tabs.map((tab) => (\n * <button key={tab} ref={getRef(tab)}>\n * {tab}\n * </button>\n * ))}\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Scroll to a specific item\n * const getRef = useDynamicRefs<HTMLDivElement>();\n *\n * function scrollTo(id: string) {\n * getRef(id)?.current?.scrollIntoView({ behavior: \"smooth\" });\n * }\n * ```\n */\nexport function useDynamicRefs<T>(options?: UseDynamicRefsOptions): DynamicRefGetter<T> {\n const prefix = options?.prefix;\n\n const getRef = useMemo(() => {\n const refMap: RefMap<T> = new Map();\n return createRefGetter<T>(refMap);\n }, [prefix]);\n\n return getRef;\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { o as StreamSource } from "./index-CECp5YtT.cjs";
2
2
  import { UseQueryResult } from "@tanstack/react-query";
3
- import { ComponentType, ReactNode } from "react";
3
+ import * as _$react from "react";
4
+ import { ComponentType, ReactNode, RefObject } from "react";
4
5
 
5
6
  //#region src/stream-renderer.d.ts
6
7
  /** Render prop: can be ReactNode or function returning ReactNode */
@@ -345,5 +346,95 @@ declare const Providers: ({
345
346
  children
346
347
  }: ProvidersProps) => ReactNode;
347
348
  //#endregion
348
- export { type ChildrenData, type DelayConfig, type ErrorData, type OnErrorData, type OnItemData, type OnSuccessData, type ProviderSpec, Providers, type ProvidersProps, type RetryData, type StreamMode, type StreamReducer, StreamRenderer, type StreamRendererBaseProps, type StreamRendererProps, type StreamState, type StreamingData, type SuccessData, type UseStreamOptions, type UseStreamReducerResult, type UseStreamResult, provider, useStream };
349
+ //#region src/use-prevent-auto-focus.d.ts
350
+ /**
351
+ * Hook that prevents auto-focus behavior while still maintaining focus management.
352
+ * Useful for modal dialogs (e.g. Radix Dialog) where you want to control the initial focus.
353
+ *
354
+ * @template E - Element type, defaults to HTMLDivElement
355
+ * @returns Object containing:
356
+ * - `ref` — React ref to attach to the element
357
+ * - `onOpenAutoFocus` — Event handler to prevent default focus behavior
358
+ * - `tabIndex` — `-1` to make the element programmatically focusable
359
+ *
360
+ * @example
361
+ * ```tsx
362
+ * import { usePreventAutoFocus } from "xantiagoma/react";
363
+ *
364
+ * function MyDialog() {
365
+ * const preventAutoFocus = usePreventAutoFocus();
366
+ *
367
+ * return (
368
+ * <DialogContent {...preventAutoFocus}>
369
+ * <p>Dialog content here</p>
370
+ * </DialogContent>
371
+ * );
372
+ * }
373
+ * ```
374
+ */
375
+ declare function usePreventAutoFocus<E extends HTMLElement = HTMLDivElement>(): {
376
+ ref: _$react.RefObject<E | null>;
377
+ onOpenAutoFocus: (event: Event) => void;
378
+ tabIndex: -1;
379
+ };
380
+ //#endregion
381
+ //#region src/use-dynamic-refs.d.ts
382
+ /**
383
+ * Options for {@link useDynamicRefs}.
384
+ */
385
+ interface UseDynamicRefsOptions {
386
+ /** Optional namespace prefix. Changing the prefix creates a new ref map. */
387
+ prefix?: string;
388
+ }
389
+ /** The internal Map type for a given ref element type. */
390
+ type RefMap<T> = Map<string, RefObject<T | null>>;
391
+ /**
392
+ * Returns a ref for the given key, creating one if it doesn't exist yet.
393
+ * Overloads ensure that calling without a key returns `undefined`.
394
+ */
395
+ interface DynamicRefGetter<T> {
396
+ (): undefined;
397
+ (key: string): RefObject<T | null>;
398
+ }
399
+ /**
400
+ * React hook that returns a ref "getter" function.
401
+ * Call the getter with a string key to get (or create) a `RefObject` for that key.
402
+ * Useful for dynamic lists, tabs, or any UI where the number of refs isn't known at compile time.
403
+ *
404
+ * @template T - The element type for the refs (e.g. `HTMLDivElement`)
405
+ * @param options - Optional configuration
406
+ * @returns A {@link DynamicRefGetter} function
407
+ *
408
+ * @example
409
+ * ```tsx
410
+ * import { useDynamicRefs } from "xantiagoma/react";
411
+ *
412
+ * function TabList({ tabs }: { tabs: string[] }) {
413
+ * const getRef = useDynamicRefs<HTMLButtonElement>();
414
+ *
415
+ * return (
416
+ * <div>
417
+ * {tabs.map((tab) => (
418
+ * <button key={tab} ref={getRef(tab)}>
419
+ * {tab}
420
+ * </button>
421
+ * ))}
422
+ * </div>
423
+ * );
424
+ * }
425
+ * ```
426
+ *
427
+ * @example
428
+ * ```tsx
429
+ * // Scroll to a specific item
430
+ * const getRef = useDynamicRefs<HTMLDivElement>();
431
+ *
432
+ * function scrollTo(id: string) {
433
+ * getRef(id)?.current?.scrollIntoView({ behavior: "smooth" });
434
+ * }
435
+ * ```
436
+ */
437
+ declare function useDynamicRefs<T>(options?: UseDynamicRefsOptions): DynamicRefGetter<T>;
438
+ //#endregion
439
+ export { type ChildrenData, type DelayConfig, type DynamicRefGetter, type ErrorData, type OnErrorData, type OnItemData, type OnSuccessData, type ProviderSpec, Providers, type ProvidersProps, type RefMap, type RetryData, type StreamMode, type StreamReducer, StreamRenderer, type StreamRendererBaseProps, type StreamRendererProps, type StreamState, type StreamingData, type SuccessData, type UseDynamicRefsOptions, type UseStreamOptions, type UseStreamReducerResult, type UseStreamResult, provider, useDynamicRefs, usePreventAutoFocus, useStream };
349
440
  //# sourceMappingURL=entry-react.d.cts.map