@faasjs/react 8.0.0-beta.17 → 8.0.0-beta.18

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/dist/index.cjs CHANGED
@@ -3,14 +3,18 @@ let react = require("react");
3
3
  let react_jsx_runtime = require("react/jsx-runtime");
4
4
  //#region src/generateId.ts
5
5
  /**
6
- * Generate random id with prefix
6
+ * Generate a random identifier with an optional prefix.
7
7
  *
8
- * @param prefix prefix of id
9
- * @param length length of id without prefix, range is 8 ~ 18, default is 18
8
+ * @param prefix - Prefix prepended to the generated identifier.
9
+ * @param length - Length of the generated identifier excluding `prefix`. Must be between `8` and `18`.
10
+ * @returns Generated identifier string.
11
+ * @throws {Error} When `length` is outside the supported `8` to `18` range.
10
12
  *
11
13
  * @example
12
14
  * ```ts
13
- * generateId('prefix-') // prefix-1z3b4c5d6e
15
+ * const id = generateId('prefix-')
16
+ *
17
+ * id.startsWith('prefix-') // true
14
18
  * ```
15
19
  */
16
20
  function generateId(prefix = "", length = 18) {
@@ -135,14 +139,30 @@ function generateId(prefix = "", length = 18) {
135
139
  * @see FaasBrowserClient.action for method returning Response
136
140
  */
137
141
  var Response = class {
142
+ /**
143
+ * HTTP status code exposed to callers.
144
+ */
138
145
  status;
146
+ /**
147
+ * Response headers keyed by header name.
148
+ */
139
149
  headers;
150
+ /**
151
+ * Raw response body.
152
+ */
140
153
  body;
154
+ /**
155
+ * Parsed response payload when JSON data is available.
156
+ */
141
157
  data;
142
158
  /**
143
159
  * Create a wrapped response object.
144
160
  *
145
161
  * @param props - Response properties including status, headers, body, and data.
162
+ * @param props.status - HTTP status code. Defaults to `200` when `data` or `body` exists, otherwise `204`.
163
+ * @param props.headers - Response headers keyed by header name.
164
+ * @param props.body - Raw response body to expose without additional parsing.
165
+ * @param props.data - Parsed response payload to expose on `response.data`.
146
166
  * @returns Wrapped response instance.
147
167
  */
148
168
  constructor(props = {}) {
@@ -250,17 +270,22 @@ var Response = class {
250
270
  * @see setMock for mocking errors in tests
251
271
  */
252
272
  var ResponseError = class extends Error {
273
+ /**
274
+ * HTTP status code reported for the failed request.
275
+ */
253
276
  status;
277
+ /**
278
+ * Response headers returned with the error.
279
+ */
254
280
  headers;
281
+ /**
282
+ * Raw error body or fallback error payload.
283
+ */
255
284
  body;
256
- originalError;
257
285
  /**
258
- * Create a ResponseError from a message, Error, or structured response error payload.
259
- *
260
- * @param data - Error message, Error object, or structured response error props.
261
- * @param options - Additional options such as status, headers, and body.
262
- * @returns ResponseError instance.
286
+ * Original error used to construct this instance, when available.
263
287
  */
288
+ originalError;
264
289
  constructor(data, options) {
265
290
  let props;
266
291
  if (typeof data === "string") props = {
@@ -277,11 +302,12 @@ var ResponseError = class extends Error {
277
302
  this.status = props.status || 500;
278
303
  this.headers = props.headers || {};
279
304
  this.body = props.body || props.originalError || { error: { message: props.message } };
305
+ if (props.originalError) this.originalError = props.originalError;
280
306
  }
281
307
  };
282
308
  let mock = null;
283
309
  /**
284
- * Set global mock handler for testing. Mock affects all FaasBrowserClient instances.
310
+ * Set the global mock handler used by all {@link FaasBrowserClient} instances.
285
311
  *
286
312
  * @param handler - Mock handler, can be:
287
313
  * - MockHandler function: receives (action, params, options) and returns response data
@@ -289,24 +315,52 @@ let mock = null;
289
315
  * - Response instance: pre-configured Response object
290
316
  * - null or undefined: clear mock
291
317
  *
318
+ * @example Reset in Vitest shared setup
319
+ * ```ts
320
+ * import { afterEach } from 'vitest'
321
+ *
322
+ * afterEach(() => {
323
+ * setMock(null)
324
+ * })
325
+ * ```
326
+ *
327
+ * @example Use ResponseProps object
328
+ * ```ts
329
+ * setMock({
330
+ * data: { name: 'FaasJS' },
331
+ * })
332
+ *
333
+ * setMock({
334
+ * status: 500,
335
+ * data: { message: 'Internal Server Error' },
336
+ * })
337
+ * ```
338
+ *
292
339
  * @example Use MockHandler function
293
340
  * ```ts
294
- * setMock(async (action, params, options) => {
295
- * if (action === 'user') {
296
- * return { data: { name: 'John' } }
341
+ * setMock(async (action) => {
342
+ * if (action === '/pages/users/get') {
343
+ * return { data: { id: 1, name: 'FaasJS' } }
297
344
  * }
298
- * return { status: 404, data: { error: 'Not found' } }
345
+ *
346
+ * return { status: 404, data: { message: 'Not Found' } }
299
347
  * })
300
348
  *
301
- * const response = await client.action('user')
349
+ * const response = await client.action('/pages/users/get')
302
350
  * ```
303
351
  *
304
- * @example Use ResponseProps object
352
+ * @example Branch by action and params
305
353
  * ```ts
306
- * setMock({
307
- * status: 200,
308
- * data: { result: 'success' },
309
- * headers: { 'X-Custom': 'value' }
354
+ * setMock(async (action, params) => {
355
+ * if (action === '/pages/users/get' && params?.id === 1) {
356
+ * return { data: { id: 1, name: 'Admin' } }
357
+ * }
358
+ *
359
+ * if (action === '/pages/users/get' && params?.id === 2) {
360
+ * return { data: { id: 2, name: 'Editor' } }
361
+ * }
362
+ *
363
+ * return { status: 404, data: { message: 'User not found' } }
310
364
  * })
311
365
  * ```
312
366
  *
@@ -318,11 +372,22 @@ let mock = null;
318
372
  * }))
319
373
  * ```
320
374
  *
375
+ * @example Streaming response
376
+ * ```ts
377
+ * setMock({
378
+ * body: new ReadableStream({
379
+ * start(controller) {
380
+ * controller.enqueue(new TextEncoder().encode('hello'))
381
+ * controller.enqueue(new TextEncoder().encode(' world'))
382
+ * controller.close()
383
+ * },
384
+ * }),
385
+ * })
386
+ * ```
387
+ *
321
388
  * @example Clear mock
322
389
  * ```ts
323
390
  * setMock(null)
324
- * // or
325
- * setMock(undefined)
326
391
  * ```
327
392
  *
328
393
  * @example Handle errors
@@ -406,14 +471,25 @@ function setMock(handler) {
406
471
  * @see ResponseError for error handling
407
472
  */
408
473
  var FaasBrowserClient = class {
474
+ /**
475
+ * Unique identifier for this client instance.
476
+ */
409
477
  id;
478
+ /**
479
+ * Base URL used to build action request URLs.
480
+ */
410
481
  baseUrl;
482
+ /**
483
+ * Default request options merged into every request.
484
+ */
411
485
  defaultOptions;
412
486
  /**
413
487
  * Creates a new FaasBrowserClient instance.
414
488
  *
415
489
  * @param baseUrl - Base URL for all API requests. Must end with `/`. Defaults to `/` for relative requests.
416
490
  * @param options - Default request options such as headers, hooks, request override, or stream mode.
491
+ * See {@link Options} for supported request fields such as `headers`, `beforeRequest`,
492
+ * `request`, `baseUrl`, and `stream`.
417
493
  *
418
494
  * @example Basic initialization
419
495
  * ```ts
@@ -483,9 +559,11 @@ var FaasBrowserClient = class {
483
559
  * Optional if the function accepts no parameters.
484
560
  * @param options - Optional request options that override client defaults.
485
561
  * Supports headers, beforeRequest hook, custom request function, baseUrl override, and streaming mode.
562
+ * See {@link Options} for supported request fields such as `headers`, `beforeRequest`,
563
+ * `request`, `baseUrl`, and `stream`.
486
564
  *
487
- * @returns A Promise that resolves to a Response object containing status, headers, body, and data.
488
- * The data property is typed based on the PathOrData generic parameter.
565
+ * @returns A promise resolving to the wrapped FaasJS response. When `options.stream`
566
+ * is `true`, the runtime returns the native fetch response so callers can read the stream.
489
567
  *
490
568
  * @throws {Error} When action is not provided or is empty
491
569
  * @throws {ResponseError} When the server returns an error response (status >= 400 or body.error exists)
@@ -663,16 +741,24 @@ var FaasBrowserClient = class {
663
741
  /**
664
742
  * Call the currently configured FaasReactClient.
665
743
  *
744
+ * This helper forwards the request to `getClient`. When the registered
745
+ * client defines `onError`, the hook is invoked before the promise rejects.
746
+ *
747
+ * @template PathOrData - Action path or response data type used for inference.
748
+ *
666
749
  * @param action - Action path to invoke.
667
750
  * @param params - Parameters sent to the action.
668
751
  * @param options - Optional per-request overrides such as headers or base URL.
752
+ * See the request `Options` type for supported fields such as `headers`, `beforeRequest`,
753
+ * `request`, `baseUrl`, and `stream`.
669
754
  * @returns Response returned by the active browser client.
755
+ * @throws {ResponseError} When the request fails and the active client does not recover inside `onError`.
670
756
  *
671
757
  * @example
672
758
  * ```ts
673
759
  * import { faas } from '@faasjs/react'
674
760
  *
675
- * const response = await faas<{ title: string }>('post/get', { id: 1 })
761
+ * const response = await faas('posts/get', { id: 1 })
676
762
  *
677
763
  * console.log(response.data.title)
678
764
  * ```
@@ -699,6 +785,14 @@ const AsyncFunction = (async () => {}).constructor;
699
785
  * @param a - The first value to compare.
700
786
  * @param b - The second value to compare.
701
787
  * @returns `true` if the values are deeply equal, `false` otherwise.
788
+ *
789
+ * @example
790
+ * ```ts
791
+ * import { equal } from '@faasjs/react'
792
+ *
793
+ * equal({ page: 1, filters: ['a'] }, { page: 1, filters: ['a'] }) // true
794
+ * equal({ page: 1 }, { page: 2 }) // false
795
+ * ```
702
796
  */
703
797
  function equal(a, b) {
704
798
  if (a === b) return true;
@@ -733,6 +827,17 @@ function equal(a, b) {
733
827
  *
734
828
  * @param value - The value to be memoized.
735
829
  * @returns The memoized value.
830
+ *
831
+ * @example
832
+ * ```tsx
833
+ * import { useEqualMemoize } from '@faasjs/react'
834
+ *
835
+ * function Filters({ filters }: { filters: Record<string, any> }) {
836
+ * const memoizedFilters = useEqualMemoize(filters)
837
+ *
838
+ * return <pre>{JSON.stringify(memoizedFilters)}</pre>
839
+ * }
840
+ * ```
736
841
  */
737
842
  function useEqualMemoize(value) {
738
843
  const ref = (0, react.useRef)(value);
@@ -754,6 +859,19 @@ function useEqualSignal(value) {
754
859
  * @param callback - The effect callback function to run.
755
860
  * @param dependencies - The list of dependencies for the effect.
756
861
  * @returns The result of the `useEffect` hook with memoized dependencies.
862
+ *
863
+ * @example
864
+ * ```tsx
865
+ * import { useEqualEffect } from '@faasjs/react'
866
+ *
867
+ * function Page({ filters }: { filters: Record<string, any> }) {
868
+ * useEqualEffect(() => {
869
+ * console.log('filters changed', filters)
870
+ * }, [filters])
871
+ *
872
+ * return null
873
+ * }
874
+ * ```
757
875
  */
758
876
  function useEqualEffect(callback, dependencies) {
759
877
  return (0, react.useEffect)(callback, [useEqualSignal(dependencies)]);
@@ -761,9 +879,22 @@ function useEqualEffect(callback, dependencies) {
761
879
  /**
762
880
  * Custom hook that works like `useMemo` but uses deep comparison on dependencies.
763
881
  *
882
+ * @template T - Memoized value type returned by the callback.
883
+ *
764
884
  * @param callback - The callback function to run.
765
885
  * @param dependencies - The list of dependencies.
766
886
  * @returns The result of the `useMemo` hook with memoized dependencies.
887
+ *
888
+ * @example
889
+ * ```tsx
890
+ * import { useEqualMemo } from '@faasjs/react'
891
+ *
892
+ * function Page({ filters }: { filters: Record<string, any> }) {
893
+ * const queryString = useEqualMemo(() => JSON.stringify(filters), [filters])
894
+ *
895
+ * return <span>{queryString}</span>
896
+ * }
897
+ * ```
767
898
  */
768
899
  function useEqualMemo(callback, dependencies) {
769
900
  const signal = useEqualSignal(dependencies);
@@ -776,9 +907,24 @@ function useEqualMemo(callback, dependencies) {
776
907
  /**
777
908
  * Custom hook that works like `useCallback` but uses deep comparison on dependencies.
778
909
  *
910
+ * @template T - Callback signature to memoize.
911
+ *
779
912
  * @param callback - The callback function to run.
780
913
  * @param dependencies - The list of dependencies.
781
914
  * @returns The result of the `useCallback` hook with memoized dependencies.
915
+ *
916
+ * @example
917
+ * ```tsx
918
+ * import { useEqualCallback } from '@faasjs/react'
919
+ *
920
+ * function Search({ filters }: { filters: Record<string, any> }) {
921
+ * const handleSubmit = useEqualCallback(() => {
922
+ * console.log(filters)
923
+ * }, [filters])
924
+ *
925
+ * return <button onClick={handleSubmit}>Search</button>
926
+ * }
927
+ * ```
782
928
  */
783
929
  function useEqualCallback(callback, dependencies) {
784
930
  return (0, react.useCallback)((...args) => callback(...args), [useEqualSignal(dependencies)]);
@@ -786,6 +932,91 @@ function useEqualCallback(callback, dependencies) {
786
932
  //#endregion
787
933
  //#region src/FaasDataWrapper.tsx
788
934
  const fixedForwardRef = react.forwardRef;
935
+ /**
936
+ * Fetch FaasJS data and inject the result into a render prop or child element.
937
+ *
938
+ * The wrapper defers rendering `children` or `render` until the first request
939
+ * completes, then keeps passing the latest request state to the rendered output.
940
+ *
941
+ * @param props - Wrapper props controlling the request and rendered fallback.
942
+ * @param props.render - Render prop that receives the resolved Faas request state.
943
+ * @param props.children - Child element cloned with injected Faas request state.
944
+ * @param props.fallback - Element rendered before the first successful load.
945
+ * @param props.action - Action path to request.
946
+ * @param props.params - Params sent to the action.
947
+ * @param props.onDataChange - Callback invoked when the resolved data value changes.
948
+ * @param props.data - Controlled data value used instead of internal state.
949
+ * @param props.setData - Controlled setter used instead of internal state.
950
+ * @param props.baseUrl - Base URL override used for this wrapper instance.
951
+ *
952
+ * @example
953
+ * ```tsx
954
+ * import { FaasDataWrapper } from '@faasjs/react'
955
+ *
956
+ * type User = {
957
+ * name: string
958
+ * }
959
+ *
960
+ * function UserView(props: {
961
+ * data?: User
962
+ * error?: Error
963
+ * reload?: () => void
964
+ * }) {
965
+ * if (props.error) {
966
+ * return (
967
+ * <div>
968
+ * <p>Failed to load user: {props.error.message}</p>
969
+ * <button type="button" onClick={() => props.reload?.()}>
970
+ * Retry
971
+ * </button>
972
+ * </div>
973
+ * )
974
+ * }
975
+ *
976
+ * return <div>Hello, {props.data?.name}</div>
977
+ * }
978
+ *
979
+ * // Render-prop mode
980
+ * export function UserProfile(props: { id: number }) {
981
+ * return (
982
+ * <FaasDataWrapper<User>
983
+ * action="/pages/users/get"
984
+ * params={{ id: props.id }}
985
+ * fallback={<div>Loading user...</div>}
986
+ * render={({ data, error, reload }) => {
987
+ * if (error) {
988
+ * return (
989
+ * <div>
990
+ * <p>Failed to load user: {error.message}</p>
991
+ * <button type="button" onClick={() => reload()}>
992
+ * Retry
993
+ * </button>
994
+ * </div>
995
+ * )
996
+ * }
997
+ *
998
+ * return <div>Hello, {data.name}</div>
999
+ * }}
1000
+ * />
1001
+ * )
1002
+ * }
1003
+ *
1004
+ * // Children injection mode
1005
+ * export function UserProfileWithChildren(props: { id: number }) {
1006
+ * return (
1007
+ * <FaasDataWrapper<User>
1008
+ * action="/pages/users/get"
1009
+ * params={{ id: props.id }}
1010
+ * fallback={<div>Loading user...</div>}
1011
+ * >
1012
+ * <UserView />
1013
+ * </FaasDataWrapper>
1014
+ * )
1015
+ * }
1016
+ * ```
1017
+ *
1018
+ * When a ref is provided, it exposes the current Faas request state imperatively.
1019
+ */
789
1020
  const FaasDataWrapper = fixedForwardRef((props, ref) => {
790
1021
  const requestOptions = {
791
1022
  ...props.data !== void 0 ? { data: props.data } : {},
@@ -817,11 +1048,36 @@ const FaasDataWrapper = fixedForwardRef((props, ref) => {
817
1048
  });
818
1049
  Object.assign(FaasDataWrapper, { displayName: "FaasDataWrapper" });
819
1050
  /**
820
- * HOC to wrap a component with FaasDataWrapper
1051
+ * Wrap a component with {@link FaasDataWrapper} and inject Faas request state as props.
1052
+ *
1053
+ * `withFaasData` is most useful for wrapper-style exports or compatibility with
1054
+ * an existing component boundary. For new code, prefer `useFaas` or
1055
+ * `FaasDataWrapper` when they express the request ownership more directly.
1056
+ *
1057
+ * @template PathOrData - Action path or response data type used for inference.
1058
+ * @template TComponentProps - Component props including injected Faas data fields.
1059
+ * @param Component - Component that consumes injected Faas data props.
1060
+ * @param faasProps - Request configuration forwarded to `FaasDataWrapper`.
1061
+ * @returns Component that accepts the original props minus the injected Faas data fields.
821
1062
  *
822
1063
  * @example
823
1064
  * ```tsx
824
- * const MyComponent = withFaasData(({ data }) => <div>{data.name}</div>, { action: 'test', params: { a: 1 } })
1065
+ * import { withFaasData } from '@faasjs/react'
1066
+ *
1067
+ * const MyComponent = withFaasData(
1068
+ * ({ data, error, reload }) => {
1069
+ * if (error) {
1070
+ * return (
1071
+ * <button type="button" onClick={() => reload()}>
1072
+ * Retry
1073
+ * </button>
1074
+ * )
1075
+ * }
1076
+ *
1077
+ * return <div>{data.name}</div>
1078
+ * },
1079
+ * { action: '/pages/users/get', params: { id: 1 } },
1080
+ * )
825
1081
  * ```
826
1082
  */
827
1083
  function withFaasData(Component, faasProps) {
@@ -835,22 +1091,51 @@ function withFaasData(Component, faasProps) {
835
1091
  /**
836
1092
  * Request FaasJS data and keep request state in React state.
837
1093
  *
838
- * `useFaas` sends an initial request unless `skip` is enabled, and returns
839
- * request state plus helpers for reloading, updating data, and handling errors.
1094
+ * `useFaas` is the default hook for standard FaasJS request-response flows in React.
1095
+ * It sends an initial request unless `skip` is enabled, and returns request state
1096
+ * plus helpers for reloading, updating data, and handling errors.
1097
+ *
1098
+ * @template PathOrData - Action path or response data type used for inference.
840
1099
  *
841
1100
  * @param action - Action path to invoke.
842
1101
  * @param defaultParams - Params used for the initial request and future reloads.
843
1102
  * @param options - Optional hook configuration such as controlled data, debounce, and skip logic.
844
- * @returns Request state and helper methods for the action.
1103
+ * @param options.params - Request params override used without mutating the hook's stored params state.
1104
+ * @param options.data - Controlled data value used instead of the hook's internal state.
1105
+ * @param options.setData - Controlled setter used instead of the hook's internal `setData`.
1106
+ * @param options.skip - Boolean or predicate that suppresses the automatic request until `reload()` runs.
1107
+ * @param options.debounce - Milliseconds to wait before sending the latest request.
1108
+ * @param options.baseUrl - Base URL override used for this hook instance.
1109
+ * @returns Request state and helper methods described by {@link FaasDataInjection}.
845
1110
  *
846
1111
  * @example
847
1112
  * ```tsx
848
1113
  * import { useFaas } from '@faasjs/react'
849
1114
  *
850
- * function Post({ id }: { id: number }) {
851
- * const { data } = useFaas<{ title: string }>('post/get', { id })
1115
+ * function Profile({ id }: { id: number }) {
1116
+ * const { data, error, loading, reload } = useFaas('/pages/users/get', { id })
1117
+ *
1118
+ * if (loading) return <div>Loading...</div>
1119
+ *
1120
+ * if (error) {
1121
+ * return (
1122
+ * <div>
1123
+ * <div>Load failed: {error.message}</div>
1124
+ * <button type="button" onClick={() => reload()}>
1125
+ * Retry
1126
+ * </button>
1127
+ * </div>
1128
+ * )
1129
+ * }
852
1130
  *
853
- * return <h1>{data.title}</h1>
1131
+ * return (
1132
+ * <div>
1133
+ * <span>{data.name}</span>
1134
+ * <button type="button" onClick={() => reload()}>
1135
+ * Refresh
1136
+ * </button>
1137
+ * </div>
1138
+ * )
854
1139
  * }
855
1140
  * ```
856
1141
  */
@@ -937,7 +1222,6 @@ function useFaas(action, defaultParams, options = {}) {
937
1222
  if (skip) setSkip(false);
938
1223
  if (params) setParams(params);
939
1224
  const reloadCounter = ++reloadCounterRef.current;
940
- setReloadTimes((prev) => prev + 1);
941
1225
  return new Promise((resolve, reject) => {
942
1226
  pendingReloadsRef.current.set(reloadCounter, {
943
1227
  resolve,
@@ -975,18 +1259,32 @@ const clients = {};
975
1259
  * used by helpers such as {@link faas} and {@link useFaas}.
976
1260
  *
977
1261
  * @param options - Client configuration including base URL, default request options, and error hooks.
1262
+ * @param options.baseUrl - Base URL used to register and route the client instance.
1263
+ * @param options.options - Default browser-client request options forwarded to `FaasBrowserClient`.
1264
+ * @param options.onError - Hook factory used to handle failed `faas` and `useFaas` requests.
1265
+ * See {@link Options} for supported browser-client request fields such as `headers`,
1266
+ * `beforeRequest`, `request`, `baseUrl`, and `stream`.
978
1267
  * @returns Registered FaasReactClient instance.
979
1268
  *
980
1269
  * @example
981
1270
  * ```ts
982
- * import { FaasReactClient } from '@faasjs/react'
1271
+ * import { FaasReactClient, ResponseError } from '@faasjs/react'
983
1272
  *
984
1273
  * const client = FaasReactClient({
985
1274
  * baseUrl: 'http://localhost:8080/api/',
1275
+ * onError: (action, params) => async (res) => {
1276
+ * if (res instanceof ResponseError) {
1277
+ * reportErrorToSentry(res, {
1278
+ * tags: { action },
1279
+ * extra: { params },
1280
+ * })
1281
+ * }
1282
+ * },
986
1283
  * })
987
1284
  * ```
988
1285
  */
989
- function FaasReactClient({ baseUrl, options: clientOptions, onError } = { baseUrl: "/" }) {
1286
+ function FaasReactClient(options = { baseUrl: "/" }) {
1287
+ const { baseUrl, options: clientOptions, onError } = options;
990
1288
  const resolvedBaseUrl = baseUrl ?? "/";
991
1289
  const client = new FaasBrowserClient(resolvedBaseUrl, clientOptions);
992
1290
  function withBaseUrl(options) {
@@ -1015,16 +1313,28 @@ function FaasReactClient({ baseUrl, options: clientOptions, onError } = { baseUr
1015
1313
  *
1016
1314
  * When `host` is omitted, the first registered client is returned. If no client
1017
1315
  * has been created yet, a default client is initialized automatically.
1316
+ * Use `getClient` only for special cases such as multiple Faas clients with
1317
+ * different base URLs. In normal single-client app code, prefer the default
1318
+ * `faas`, `useFaas`, or `FaasReactClient` setup directly.
1018
1319
  *
1019
1320
  * @param host - Registered base URL to look up. Omit it to use the default client.
1020
1321
  * @returns Registered or newly created FaasReactClient instance.
1021
1322
  *
1022
1323
  * @example
1023
1324
  * ```ts
1024
- * import { getClient } from '@faasjs/react'
1325
+ * import { FaasReactClient, getClient } from '@faasjs/react'
1326
+ *
1327
+ * FaasReactClient({
1328
+ * baseUrl: 'https://service-a.example.com/api/',
1329
+ * })
1330
+ *
1331
+ * FaasReactClient({
1332
+ * baseUrl: 'https://service-b.example.com/api/',
1333
+ * })
1334
+ *
1335
+ * const client = getClient('https://service-b.example.com/api/')
1025
1336
  *
1026
- * getClient()
1027
- * getClient('http://localhost:8080/api/')
1337
+ * await client.faas('/pages/posts/get', { id: 1 })
1028
1338
  * ```
1029
1339
  */
1030
1340
  function getClient(host) {
@@ -1039,6 +1349,20 @@ function getClient(host) {
1039
1349
  //#region src/constant.ts
1040
1350
  /**
1041
1351
  * Returns a constant value that is created by the given function.
1352
+ *
1353
+ * @template T - Constant value type returned by the initializer.
1354
+ * @param fn - Initializer that runs only once for the current component instance.
1355
+ *
1356
+ * @example
1357
+ * ```tsx
1358
+ * import { useConstant } from '@faasjs/react'
1359
+ *
1360
+ * function Page() {
1361
+ * const requestId = useConstant(() => crypto.randomUUID())
1362
+ *
1363
+ * return <span>{requestId}</span>
1364
+ * }
1365
+ * ```
1042
1366
  */
1043
1367
  function useConstant(fn) {
1044
1368
  const ref = (0, react.useRef)(null);
@@ -1047,8 +1371,39 @@ function useConstant(fn) {
1047
1371
  }
1048
1372
  //#endregion
1049
1373
  //#region src/ErrorBoundary.tsx
1374
+ /**
1375
+ * React error boundary with an optional custom fallback element.
1376
+ *
1377
+ * The boundary renders its children until a descendant throws. After that it
1378
+ * either clones `errorChildren` with injected error details or renders a simple
1379
+ * built-in fallback.
1380
+ *
1381
+ * @example
1382
+ * ```tsx
1383
+ * import { ErrorBoundary } from '@faasjs/react'
1384
+ *
1385
+ * function Fallback({ errorMessage }: { errorMessage?: string }) {
1386
+ * return <div>{errorMessage}</div>
1387
+ * }
1388
+ *
1389
+ * <ErrorBoundary errorChildren={<Fallback />}>
1390
+ * <DangerousWidget />
1391
+ * </ErrorBoundary>
1392
+ * ```
1393
+ */
1050
1394
  var ErrorBoundary = class extends react.Component {
1395
+ /**
1396
+ * Stable display name used by React DevTools.
1397
+ */
1051
1398
  static displayName = "ErrorBoundary";
1399
+ /**
1400
+ * Create an error boundary with empty error state.
1401
+ *
1402
+ * @param props - Boundary props.
1403
+ * @param props.children - Descendant elements protected by the boundary.
1404
+ * @param props.onError - Callback invoked after a render error is captured.
1405
+ * @param props.errorChildren - Custom fallback element that receives error details.
1406
+ */
1052
1407
  constructor(props) {
1053
1408
  super(props);
1054
1409
  this.state = {
@@ -1056,12 +1411,21 @@ var ErrorBoundary = class extends react.Component {
1056
1411
  info: { componentStack: "" }
1057
1412
  };
1058
1413
  }
1414
+ /**
1415
+ * Capture rendering errors from descendant components.
1416
+ *
1417
+ * @param error - Caught render error.
1418
+ * @param info - React component stack metadata.
1419
+ */
1059
1420
  componentDidCatch(error, info) {
1060
1421
  this.setState({
1061
1422
  error,
1062
1423
  info
1063
1424
  });
1064
1425
  }
1426
+ /**
1427
+ * Render children or the configured fallback for the captured error.
1428
+ */
1065
1429
  render() {
1066
1430
  const { error, info } = this.state;
1067
1431
  const errorMessage = String(error ?? "");
@@ -1082,7 +1446,14 @@ var ErrorBoundary = class extends react.Component {
1082
1446
  //#endregion
1083
1447
  //#region src/OptionalWrapper.tsx
1084
1448
  /**
1085
- * A wrapper component that conditionally wraps its children with a provided wrapper component.
1449
+ * Conditionally wrap children with another component.
1450
+ *
1451
+ * @param props - Wrapper condition, wrapper component, and child content.
1452
+ * @param props.condition - When `true`, wrap children with `Wrapper`.
1453
+ * @param props.Wrapper - Component used as the wrapper when the condition passes.
1454
+ * @param props.wrapperProps - Props forwarded to the wrapper component.
1455
+ * @param props.children - Content rendered directly or inside the wrapper.
1456
+ * @returns Wrapped children or the original children when `condition` is false.
1086
1457
  *
1087
1458
  * @example
1088
1459
  * ```tsx
@@ -1099,7 +1470,8 @@ var ErrorBoundary = class extends react.Component {
1099
1470
  * )
1100
1471
  * ```
1101
1472
  */
1102
- function OptionalWrapper({ condition, Wrapper, wrapperProps, children }) {
1473
+ function OptionalWrapper(props) {
1474
+ const { condition, Wrapper, wrapperProps, children } = props;
1103
1475
  if (condition) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Wrapper, {
1104
1476
  ...wrapperProps,
1105
1477
  children
@@ -1139,9 +1511,15 @@ function useSplittingState(initialStates) {
1139
1511
  //#endregion
1140
1512
  //#region src/splittingContext.tsx
1141
1513
  /**
1142
- * Creates a splitting context with the given default value.
1514
+ * Create a context whose keys can be consumed independently.
1515
+ *
1516
+ * `createSplittingContext` returns a `Provider` and a `use` hook. Each key in
1517
+ * the provided shape is backed by a separate React context so readers only
1518
+ * subscribe to the values they access.
1143
1519
  *
1144
- * @param defaultValue The default value of the splitting context.
1520
+ * @template T - Context value shape exposed by the provider and hook.
1521
+ * @param defaultValue - Default value map or key list used to create split contexts.
1522
+ * @returns Provider and hook helpers for the split context.
1145
1523
  *
1146
1524
  * @example
1147
1525
  * ```tsx
@@ -1223,30 +1601,42 @@ function createSplittingContext(defaultValue) {
1223
1601
  /**
1224
1602
  * Stream a FaasJS response into React state.
1225
1603
  *
1226
- * The hook sends a streaming request, appends decoded text chunks to `data`,
1227
- * and exposes reload helpers for retrying the same action.
1604
+ * `useFaasStream` is the default hook for streaming FaasJS responses in React.
1605
+ * It sends a streaming request, appends decoded text chunks to `data`, and
1606
+ * exposes reload helpers for retrying the same action.
1228
1607
  *
1229
1608
  * @param action - Action path to invoke.
1230
1609
  * @param defaultParams - Params used for the initial request and future reloads.
1231
1610
  * @param options - Optional hook configuration such as controlled data, debounce, and skip logic.
1232
- * @returns Streaming request state and helper methods.
1611
+ * @param options.params - Request params override used without mutating the hook's stored params state.
1612
+ * @param options.data - Controlled stream text used instead of the hook's internal state.
1613
+ * @param options.setData - Controlled setter used instead of the hook's internal `setData`.
1614
+ * @param options.skip - Boolean or predicate that suppresses the automatic request until `reload()` runs.
1615
+ * @param options.debounce - Milliseconds to wait before sending the latest request.
1616
+ * @param options.baseUrl - Base URL override used for this hook instance.
1617
+ * @returns Streaming request state and helper methods described by {@link UseFaasStreamResult}.
1233
1618
  *
1234
1619
  * @example
1235
1620
  * ```tsx
1236
- * import { useState } from 'react'
1237
1621
  * import { useFaasStream } from '@faasjs/react'
1238
1622
  *
1239
- * function Chat() {
1240
- * const [prompt, setPrompt] = useState('')
1241
- * const { data, loading, reload } = useFaasStream('chat', { prompt })
1623
+ * function Chat({ prompt }: { prompt: string }) {
1624
+ * const { data, error, loading, reload } = useFaasStream('/pages/chat/stream', { prompt })
1242
1625
  *
1243
- * return (
1244
- * <div>
1245
- * <textarea value={prompt} onChange={e => setPrompt(e.target.value)} />
1246
- * <button onClick={reload} disabled={loading}>Send</button>
1247
- * <div>{data}</div>
1248
- * </div>
1249
- * )
1626
+ * if (loading) return <div>Streaming...</div>
1627
+ *
1628
+ * if (error) {
1629
+ * return (
1630
+ * <div>
1631
+ * <div>Stream failed: {error.message}</div>
1632
+ * <button type="button" onClick={() => reload()}>
1633
+ * Retry
1634
+ * </button>
1635
+ * </div>
1636
+ * )
1637
+ * }
1638
+ *
1639
+ * return <pre>{data}</pre>
1250
1640
  * }
1251
1641
  * ```
1252
1642
  */
@@ -1378,6 +1768,17 @@ function useFaasStream(action, defaultParams, options = {}) {
1378
1768
  * @template T - The type of the value.
1379
1769
  * @param value - The current value to track.
1380
1770
  * @returns Previous value from the prior render, or `undefined` on the first render.
1771
+ *
1772
+ * @example
1773
+ * ```tsx
1774
+ * import { usePrevious } from '@faasjs/react'
1775
+ *
1776
+ * function Counter({ count }: { count: number }) {
1777
+ * const previous = usePrevious(count)
1778
+ *
1779
+ * return <span>{previous} -> {count}</span>
1780
+ * }
1781
+ * ```
1381
1782
  */
1382
1783
  function usePrevious(value) {
1383
1784
  const ref = (0, react.useRef)(void 0);