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