@faasjs/react 8.0.0-beta.16 → 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
+ * })
1025
1334
  *
1026
- * getClient()
1027
- * getClient('http://localhost:8080/api/')
1335
+ * const client = getClient('https://service-b.example.com/api/')
1336
+ *
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 ?? "");
@@ -1080,43 +1444,41 @@ var ErrorBoundary = class extends react.Component {
1080
1444
  }
1081
1445
  };
1082
1446
  //#endregion
1083
- //#region src/useStateRef.ts
1447
+ //#region src/OptionalWrapper.tsx
1084
1448
  /**
1085
- * Custom hook that returns a stateful value and a ref to that value.
1449
+ * Conditionally wrap children with another component.
1086
1450
  *
1087
- * @template T - The type of the value.
1088
- * @param initialValue - Initial state value. When omitted, state starts as `null`.
1089
- * @returns Tuple containing the current state, the state setter, and a ref that always points at the latest state.
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.
1090
1457
  *
1091
1458
  * @example
1092
1459
  * ```tsx
1093
- * import { useStateRef } from '@faasjs/react'
1460
+ * import { OptionalWrapper } from '@faasjs/react'
1094
1461
  *
1095
- * function MyComponent() {
1096
- * const [value, setValue, ref] = useStateRef(0)
1462
+ * const Wrapper = ({ children }: { children: React.ReactNode }) => (
1463
+ * <div className='wrapper'>{children}</div>
1464
+ * )
1097
1465
  *
1098
- * return (
1099
- * <div>
1100
- * <p>Value: {value}</p>
1101
- * <button onClick={() => setValue(value + 1)}>Increment</button>
1102
- * <button onClick={() => console.log(ref.current)}>Submit</button>
1103
- * </div>
1104
- * )
1105
- * }
1466
+ * const App = () => (
1467
+ * <OptionalWrapper condition={true} Wrapper={Wrapper}>
1468
+ * <span>Test</span>
1469
+ * </OptionalWrapper>
1470
+ * )
1106
1471
  * ```
1107
1472
  */
1108
- function useStateRef(initialValue) {
1109
- const [state, setState] = (0, react.useState)(initialValue ?? null);
1110
- const ref = (0, react.useRef)(state);
1111
- (0, react.useEffect)(() => {
1112
- ref.current = state;
1113
- }, [state]);
1114
- return [
1115
- state,
1116
- setState,
1117
- ref
1118
- ];
1473
+ function OptionalWrapper(props) {
1474
+ const { condition, Wrapper, wrapperProps, children } = props;
1475
+ if (condition) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Wrapper, {
1476
+ ...wrapperProps,
1477
+ children
1478
+ });
1479
+ return children;
1119
1480
  }
1481
+ OptionalWrapper.displayName = "OptionalWrapper";
1120
1482
  //#endregion
1121
1483
  //#region src/splittingState.tsx
1122
1484
  /**
@@ -1149,9 +1511,15 @@ function useSplittingState(initialStates) {
1149
1511
  //#endregion
1150
1512
  //#region src/splittingContext.tsx
1151
1513
  /**
1152
- * 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.
1153
1519
  *
1154
- * @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.
1155
1523
  *
1156
1524
  * @example
1157
1525
  * ```tsx
@@ -1229,306 +1597,46 @@ function createSplittingContext(defaultValue) {
1229
1597
  };
1230
1598
  }
1231
1599
  //#endregion
1232
- //#region src/Form/context.tsx
1233
- const FormContext = createSplittingContext([
1234
- "items",
1235
- "onSubmit",
1236
- "Elements",
1237
- "lang",
1238
- "rules",
1239
- "submitting",
1240
- "setSubmitting",
1241
- "values",
1242
- "setValues",
1243
- "errors",
1244
- "setErrors",
1245
- "valuesRef"
1246
- ]);
1247
- const FormContextProvider = FormContext.Provider;
1248
- const useFormContext = FormContext.use;
1249
- //#endregion
1250
- //#region src/Form/Input.tsx
1251
- function processValue(input, rules) {
1252
- switch (rules?.type) {
1253
- case "number": return Number(input);
1254
- case "string": return String(input);
1255
- default: return input;
1256
- }
1257
- }
1258
- function FormInput({ name, rules, ...rest }) {
1259
- const { Elements, values, setValues } = useFormContext();
1260
- const value = values?.[name];
1261
- if (rest.Input) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(rest.Input, {
1262
- name,
1263
- value,
1264
- onChange: (v) => setValues((prev) => ({
1265
- ...prev,
1266
- [name]: processValue(v, rules)
1267
- })),
1268
- ...rest.props
1269
- });
1270
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Elements.Input, {
1271
- name,
1272
- value,
1273
- onChange: (v) => setValues((prev) => ({
1274
- ...prev,
1275
- [name]: processValue(v, rules)
1276
- })),
1277
- ...rest.props
1278
- });
1279
- }
1280
- FormInput.displayName = "FormInput";
1281
- //#endregion
1282
- //#region src/Form/Item.tsx
1283
- function FormItem(props) {
1284
- const { Elements, errors } = useFormContext();
1285
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(props.label?.Label ?? Elements.Label, {
1286
- name: props.name,
1287
- ...props.label,
1288
- error: errors[props.name],
1289
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormInput, {
1290
- name: props.name,
1291
- ...props.input,
1292
- ...props.rules ? { rules: props.rules } : {}
1293
- })
1294
- });
1295
- }
1296
- FormItem.displayName = "FormItem";
1297
- //#endregion
1298
- //#region src/Form/Body.tsx
1299
- function FormBody() {
1300
- const { items } = useFormContext();
1301
- return items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormItem, { ...item }, item.name));
1302
- }
1303
- FormBody.displayName = "FormBody";
1304
- //#endregion
1305
- //#region src/Form/elements/Button.tsx
1306
- const FormButtonElement = (0, react.forwardRef)(({ children, submit, submitting, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1307
- type: "button",
1308
- disabled: submitting,
1309
- onClick: submit,
1310
- ...props,
1311
- ref,
1312
- children
1313
- }));
1314
- FormButtonElement.displayName = "FormButtonElement";
1315
- //#endregion
1316
- //#region src/Form/elements/Input.tsx
1317
- const FormInputElement = (0, react.forwardRef)(({ onChange, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1318
- ...props,
1319
- onChange: (e) => onChange(e.target.value),
1320
- ref
1321
- }));
1322
- FormInputElement.displayName = "FormInputElement";
1323
- //#endregion
1324
- //#region src/Form/elements/Label.tsx
1325
- const FormLabelElement = ({ name, title, description, error, children }) => {
1326
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
1327
- title ?? name,
1328
- children,
1329
- description,
1330
- error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1331
- style: { color: "red" },
1332
- children: error.message
1333
- })
1334
- ] });
1335
- };
1336
- FormLabelElement.displayName = "FormLabelElement";
1337
- //#endregion
1338
- //#region src/Form/elements/index.ts
1339
- const FormDefaultElements = {
1340
- Label: FormLabelElement,
1341
- Input: FormInputElement,
1342
- Button: FormButtonElement
1343
- };
1344
- //#endregion
1345
- //#region src/Form/rules.ts
1346
- /**
1347
- * Default validation rules for a form.
1348
- */
1349
- const FormDefaultRules = {
1350
- required: async (value, _, lang) => {
1351
- if (value === null || value === void 0 || value === "" || Number.isNaN(value)) throw Error(lang?.required);
1352
- },
1353
- type: async (value, options, lang) => {
1354
- switch (options) {
1355
- case "string":
1356
- if (typeof value !== "string") throw Error(lang?.string);
1357
- break;
1358
- case "number":
1359
- if (Number.isNaN(Number(value))) throw Error(lang?.number);
1360
- break;
1361
- }
1362
- },
1363
- custom: async (value, options) => {
1364
- return options(value);
1365
- }
1366
- };
1367
- async function validValues(rules, items, values, lang) {
1368
- const errors = {};
1369
- for (const item of items) {
1370
- const value = values[item.name];
1371
- const rulesOptions = item.rules;
1372
- if (rulesOptions) for (const [name, options] of Object.entries(rulesOptions)) try {
1373
- await rules[name](value, options, lang);
1374
- } catch (error) {
1375
- errors[item.name] = error;
1376
- break;
1377
- }
1378
- }
1379
- return errors;
1380
- }
1381
- //#endregion
1382
- //#region src/Form/Footer.tsx
1383
- function FormFooter() {
1384
- const { submitting, setSubmitting, onSubmit, valuesRef, Elements, items, setErrors, lang, rules } = useFormContext();
1385
- const Button = Elements.Button;
1386
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1387
- submitting,
1388
- submit: (0, react.useCallback)(async () => {
1389
- setSubmitting(true);
1390
- setErrors({});
1391
- const errors = await validValues(rules, items, valuesRef.current, lang);
1392
- if (Object.keys(errors).length) {
1393
- setErrors(errors);
1394
- setSubmitting(false);
1395
- return;
1396
- }
1397
- onSubmit(valuesRef.current).finally(() => setSubmitting(false));
1398
- }, [
1399
- setSubmitting,
1400
- setErrors,
1401
- rules,
1402
- items,
1403
- valuesRef,
1404
- lang,
1405
- onSubmit
1406
- ]),
1407
- children: lang.submit
1408
- });
1409
- }
1410
- FormFooter.displayName = "FormFooter";
1411
- //#endregion
1412
- //#region src/Form/lang.ts
1413
- const FormDefaultLang = {
1414
- submit: "Submit",
1415
- required: "This field is required",
1416
- string: "This field must be a string",
1417
- number: "This field must be a number"
1418
- };
1419
- //#endregion
1420
- //#region src/Form/Container.tsx
1421
- function mergeValues(items, defaultValues = {}) {
1422
- const values = {};
1423
- for (const item of items) values[item.name] = defaultValues[item.name] ?? "";
1424
- return values;
1425
- }
1426
- /**
1427
- * Render a form with context, default elements, and validation state.
1428
- *
1429
- * `FormContainer` merges provided elements, language strings, and rules with
1430
- * the package defaults, then exposes them through form context.
1431
- *
1432
- * @template Values - The type of form values, defaults to Record<string, any>.
1433
- * @template FormElements - The type of form elements, defaults to FormElementTypes.
1434
- * @template Rules - The type of form rules, defaults to FormDefaultRules.
1435
- * @param props - Form items and optional overrides for defaults, language, rules, and submit behavior.
1436
- * @returns React form container with shared form context.
1437
- *
1438
- * @example
1439
- * ```tsx
1440
- * import { Form } from '@faasjs/react'
1441
- *
1442
- * function MyForm() {
1443
- * return (
1444
- * <Form
1445
- * items={[
1446
- * { name: 'name' },
1447
- * ]}
1448
- * />
1449
- * )
1450
- * }
1451
- * ```
1452
- */
1453
- function FormContainer({ defaultValues, Elements, rules, lang, items, ...props }) {
1454
- const [values, setValues, valuesRef] = useStateRef(mergeValues(items, defaultValues));
1455
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(FormContextProvider, {
1456
- initializeStates: {
1457
- errors: {},
1458
- submitting: false
1459
- },
1460
- value: {
1461
- Elements: Object.assign(FormDefaultElements, Elements),
1462
- lang: Object.assign(FormDefaultLang, lang),
1463
- rules: Object.assign(FormDefaultRules, rules),
1464
- items,
1465
- values,
1466
- setValues,
1467
- valuesRef,
1468
- ...props
1469
- },
1470
- memo: true,
1471
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormBody, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormFooter, {})]
1472
- });
1473
- }
1474
- FormContainer.displayName = "FormContainer";
1475
- //#endregion
1476
- //#region src/OptionalWrapper.tsx
1477
- /**
1478
- * A wrapper component that conditionally wraps its children with a provided wrapper component.
1479
- *
1480
- * @example
1481
- * ```tsx
1482
- * import { OptionalWrapper } from '@faasjs/react'
1483
- *
1484
- * const Wrapper = ({ children }: { children: React.ReactNode }) => (
1485
- * <div className='wrapper'>{children}</div>
1486
- * )
1487
- *
1488
- * const App = () => (
1489
- * <OptionalWrapper condition={true} Wrapper={Wrapper}>
1490
- * <span>Test</span>
1491
- * </OptionalWrapper>
1492
- * )
1493
- * ```
1494
- */
1495
- function OptionalWrapper({ condition, Wrapper, wrapperProps, children }) {
1496
- if (condition) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Wrapper, {
1497
- ...wrapperProps,
1498
- children
1499
- });
1500
- return children;
1501
- }
1502
- OptionalWrapper.displayName = "OptionalWrapper";
1503
- //#endregion
1504
1600
  //#region src/useFaasStream.tsx
1505
1601
  /**
1506
1602
  * Stream a FaasJS response into React state.
1507
1603
  *
1508
- * The hook sends a streaming request, appends decoded text chunks to `data`,
1509
- * 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.
1510
1607
  *
1511
1608
  * @param action - Action path to invoke.
1512
1609
  * @param defaultParams - Params used for the initial request and future reloads.
1513
1610
  * @param options - Optional hook configuration such as controlled data, debounce, and skip logic.
1514
- * @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}.
1515
1618
  *
1516
1619
  * @example
1517
1620
  * ```tsx
1518
- * import { useState } from 'react'
1519
1621
  * import { useFaasStream } from '@faasjs/react'
1520
1622
  *
1521
- * function Chat() {
1522
- * const [prompt, setPrompt] = useState('')
1523
- * const { data, loading, reload } = useFaasStream('chat', { prompt })
1623
+ * function Chat({ prompt }: { prompt: string }) {
1624
+ * const { data, error, loading, reload } = useFaasStream('/pages/chat/stream', { prompt })
1524
1625
  *
1525
- * return (
1526
- * <div>
1527
- * <textarea value={prompt} onChange={e => setPrompt(e.target.value)} />
1528
- * <button onClick={reload} disabled={loading}>Send</button>
1529
- * <div>{data}</div>
1530
- * </div>
1531
- * )
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>
1532
1640
  * }
1533
1641
  * ```
1534
1642
  */
@@ -1660,6 +1768,17 @@ function useFaasStream(action, defaultParams, options = {}) {
1660
1768
  * @template T - The type of the value.
1661
1769
  * @param value - The current value to track.
1662
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
+ * ```
1663
1782
  */
1664
1783
  function usePrevious(value) {
1665
1784
  const ref = (0, react.useRef)(void 0);
@@ -1669,17 +1788,48 @@ function usePrevious(value) {
1669
1788
  return ref.current;
1670
1789
  }
1671
1790
  //#endregion
1791
+ //#region src/useStateRef.ts
1792
+ /**
1793
+ * Custom hook that returns a stateful value and a ref to that value.
1794
+ *
1795
+ * @template T - The type of the value.
1796
+ * @param initialValue - Initial state value. When omitted, state starts as `null`.
1797
+ * @returns Tuple containing the current state, the state setter, and a ref that always points at the latest state.
1798
+ *
1799
+ * @example
1800
+ * ```tsx
1801
+ * import { useStateRef } from '@faasjs/react'
1802
+ *
1803
+ * function MyComponent() {
1804
+ * const [value, setValue, ref] = useStateRef(0)
1805
+ *
1806
+ * return (
1807
+ * <div>
1808
+ * <p>Value: {value}</p>
1809
+ * <button onClick={() => setValue(value + 1)}>Increment</button>
1810
+ * <button onClick={() => console.log(ref.current)}>Submit</button>
1811
+ * </div>
1812
+ * )
1813
+ * }
1814
+ * ```
1815
+ */
1816
+ function useStateRef(initialValue) {
1817
+ const [state, setState] = (0, react.useState)(initialValue ?? null);
1818
+ const ref = (0, react.useRef)(state);
1819
+ (0, react.useEffect)(() => {
1820
+ ref.current = state;
1821
+ }, [state]);
1822
+ return [
1823
+ state,
1824
+ setState,
1825
+ ref
1826
+ ];
1827
+ }
1828
+ //#endregion
1672
1829
  exports.ErrorBoundary = ErrorBoundary;
1673
1830
  exports.FaasBrowserClient = FaasBrowserClient;
1674
1831
  exports.FaasDataWrapper = FaasDataWrapper;
1675
1832
  exports.FaasReactClient = FaasReactClient;
1676
- exports.Form = FormContainer;
1677
- exports.FormContextProvider = FormContextProvider;
1678
- exports.FormDefaultElements = FormDefaultElements;
1679
- exports.FormDefaultLang = FormDefaultLang;
1680
- exports.FormDefaultRules = FormDefaultRules;
1681
- exports.FormInput = FormInput;
1682
- exports.FormItem = FormItem;
1683
1833
  exports.OptionalWrapper = OptionalWrapper;
1684
1834
  exports.Response = Response;
1685
1835
  exports.ResponseError = ResponseError;
@@ -1696,9 +1846,7 @@ exports.useEqualMemo = useEqualMemo;
1696
1846
  exports.useEqualMemoize = useEqualMemoize;
1697
1847
  exports.useFaas = useFaas;
1698
1848
  exports.useFaasStream = useFaasStream;
1699
- exports.useFormContext = useFormContext;
1700
1849
  exports.usePrevious = usePrevious;
1701
1850
  exports.useSplittingState = useSplittingState;
1702
1851
  exports.useStateRef = useStateRef;
1703
- exports.validValues = validValues;
1704
1852
  exports.withFaasData = withFaasData;