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

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
@@ -36,9 +36,6 @@ function generateId(prefix = "", length = 18) {
36
36
  * @property {T} [data] - The parsed JSON data from the response.
37
37
  * Optional property that contains the response payload when JSON is provided.
38
38
  *
39
- * @param {ResponseProps<T>} [props] - Response properties including status, headers, body, and data.
40
- * All properties are optional with sensible defaults.
41
- *
42
39
  * Notes:
43
40
  * - status defaults to 200 if data or body is present, 204 otherwise
44
41
  * - body is automatically populated from data if not explicitly provided
@@ -142,6 +139,12 @@ var Response = class {
142
139
  headers;
143
140
  body;
144
141
  data;
142
+ /**
143
+ * Create a wrapped response object.
144
+ *
145
+ * @param props - Response properties including status, headers, body, and data.
146
+ * @returns Wrapped response instance.
147
+ */
145
148
  constructor(props = {}) {
146
149
  this.status = props.status || (props.data || props.body ? 200 : 204);
147
150
  this.headers = props.headers || {};
@@ -156,7 +159,6 @@ var Response = class {
156
159
  * Extends the built-in Error class to provide additional information about failed requests,
157
160
  * including HTTP status code, response headers, response body, and the original error.
158
161
  *
159
- * @class ResponseError
160
162
  * @augments Error
161
163
  *
162
164
  * @property {number} status - The HTTP status code of the failed response. Defaults to 500 if not provided.
@@ -164,9 +166,6 @@ var Response = class {
164
166
  * @property {any} body - The response body containing error details or the original error if available.
165
167
  * @property {Error} [originalError] - The original Error object if this ResponseError was created from another Error.
166
168
  *
167
- * @param {string | Error | ResponseErrorProps} data - The error message, an Error object, or a ResponseErrorProps object.
168
- * @param {Omit<ResponseErrorProps, 'message' | 'originalError'>} [options] - Additional options for the error (status, headers, body).
169
- *
170
169
  * @example Basic error with message
171
170
  * ```ts
172
171
  * throw new ResponseError('User not found')
@@ -255,6 +254,13 @@ var ResponseError = class extends Error {
255
254
  headers;
256
255
  body;
257
256
  originalError;
257
+ /**
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.
263
+ */
258
264
  constructor(data, options) {
259
265
  let props;
260
266
  if (typeof data === "string") props = {
@@ -406,11 +412,8 @@ var FaasBrowserClient = class {
406
412
  /**
407
413
  * Creates a new FaasBrowserClient instance.
408
414
  *
409
- * @param baseUrl - Base URL for all API requests. Must end with '/'. Defaults to '/' for relative requests.
410
- * @throws {Error} If baseUrl does not end with '/'
411
- * @param options - Configuration options for the client.
412
- * Supports default headers, beforeRequest hook, custom request function,
413
- * baseUrl override, and streaming mode.
415
+ * @param baseUrl - Base URL for all API requests. Must end with `/`. Defaults to `/` for relative requests.
416
+ * @param options - Default request options such as headers, hooks, request override, or stream mode.
414
417
  *
415
418
  * @example Basic initialization
416
419
  * ```ts
@@ -461,7 +464,7 @@ var FaasBrowserClient = class {
461
464
  * })
462
465
  * ```
463
466
  *
464
- * @throws {Error} When baseUrl does not end with '/'
467
+ * @throws {Error} When `baseUrl` does not end with `/`
465
468
  */
466
469
  constructor(baseUrl = "/", options = Object.create(null)) {
467
470
  if (baseUrl && !baseUrl.endsWith("/")) throw Error("[FaasJS] baseUrl should end with /");
@@ -486,7 +489,7 @@ var FaasBrowserClient = class {
486
489
  *
487
490
  * @throws {Error} When action is not provided or is empty
488
491
  * @throws {ResponseError} When the server returns an error response (status >= 400 or body.error exists)
489
- * @throws {NetworkError} When network request fails
492
+ * @throws {Error} When the request fails before a response is received
490
493
  *
491
494
  * Notes:
492
495
  * - All requests are POST requests by default
@@ -540,11 +543,7 @@ var FaasBrowserClient = class {
540
543
  * email: string
541
544
  * }
542
545
  *
543
- * const response = await client.action<{
544
- * action: 'user'
545
- * params: { id: number }
546
- * data: UserData
547
- * }>('user', { id: 123 })
546
+ * const response = await client.action<UserData>('user', { id: 123 })
548
547
  * console.log(response.data.name) // TypeScript knows it's a string
549
548
  * ```
550
549
  *
@@ -556,7 +555,7 @@ var FaasBrowserClient = class {
556
555
  * } catch (error) {
557
556
  * if (error instanceof ResponseError) {
558
557
  * console.error(`Server error: ${error.message}`, error.status)
559
- * if (error.data) console.error('Error details:', error.data)
558
+ * if (error.body) console.error('Error details:', error.body)
560
559
  * } else {
561
560
  * console.error('Network error:', error)
562
561
  * }
@@ -662,17 +661,20 @@ var FaasBrowserClient = class {
662
661
  //#endregion
663
662
  //#region src/faas.ts
664
663
  /**
665
- * Request faas server
664
+ * Call the currently configured FaasReactClient.
666
665
  *
667
- * @param action {string} action name
668
- * @param params {object} action params
669
- * @returns {Promise<Response<any>>}
666
+ * @param action - Action path to invoke.
667
+ * @param params - Parameters sent to the action.
668
+ * @param options - Optional per-request overrides such as headers or base URL.
669
+ * @returns Response returned by the active browser client.
670
670
  *
671
671
  * @example
672
672
  * ```ts
673
- * faas<{ title: string }>('post/get', { id: 1 }).then(res => {
674
- * console.log(res.data.title)
675
- * })
673
+ * import { faas } from '@faasjs/react'
674
+ *
675
+ * const response = await faas<{ title: string }>('post/get', { id: 1 })
676
+ *
677
+ * console.log(response.data.title)
676
678
  * ```
677
679
  */
678
680
  async function faas(action, params, options) {
@@ -831,16 +833,23 @@ function withFaasData(Component, faasProps) {
831
833
  //#endregion
832
834
  //#region src/useFaas.tsx
833
835
  /**
834
- * Request faas server with React hook
836
+ * Request FaasJS data and keep request state in React state.
835
837
  *
836
- * @param action {string} action name
837
- * @param defaultParams {object} initial action params
838
- * @returns {FaasDataInjection<any>}
838
+ * `useFaas` sends an initial request unless `skip` is enabled, and returns
839
+ * request state plus helpers for reloading, updating data, and handling errors.
840
+ *
841
+ * @param action - Action path to invoke.
842
+ * @param defaultParams - Params used for the initial request and future reloads.
843
+ * @param options - Optional hook configuration such as controlled data, debounce, and skip logic.
844
+ * @returns Request state and helper methods for the action.
839
845
  *
840
846
  * @example
841
847
  * ```tsx
842
- * function Post ({ id }) {
848
+ * import { useFaas } from '@faasjs/react'
849
+ *
850
+ * function Post({ id }: { id: number }) {
843
851
  * const { data } = useFaas<{ title: string }>('post/get', { id })
852
+ *
844
853
  * return <h1>{data.title}</h1>
845
854
  * }
846
855
  * ```
@@ -960,14 +969,20 @@ function useFaas(action, defaultParams, options = {}) {
960
969
  //#region src/client.tsx
961
970
  const clients = {};
962
971
  /**
963
- * Before use faas, you should initialize a FaasReactClient.
972
+ * Create and register a FaasReactClient instance.
964
973
  *
965
- * @returns FaasReactClient instance.
974
+ * The returned client is stored by `baseUrl` and becomes the default client
975
+ * used by helpers such as {@link faas} and {@link useFaas}.
976
+ *
977
+ * @param options - Client configuration including base URL, default request options, and error hooks.
978
+ * @returns Registered FaasReactClient instance.
966
979
  *
967
980
  * @example
968
981
  * ```ts
982
+ * import { FaasReactClient } from '@faasjs/react'
983
+ *
969
984
  * const client = FaasReactClient({
970
- * baseUrl: 'localhost:8080/api/'
985
+ * baseUrl: 'http://localhost:8080/api/',
971
986
  * })
972
987
  * ```
973
988
  */
@@ -996,16 +1011,20 @@ function FaasReactClient({ baseUrl, options: clientOptions, onError } = { baseUr
996
1011
  return reactClient;
997
1012
  }
998
1013
  /**
999
- * Get FaasReactClient instance
1014
+ * Get a registered FaasReactClient instance.
1000
1015
  *
1001
- * @param host {string} empty string for default host
1002
- * @returns {FaasReactClientInstance}
1016
+ * When `host` is omitted, the first registered client is returned. If no client
1017
+ * has been created yet, a default client is initialized automatically.
1018
+ *
1019
+ * @param host - Registered base URL to look up. Omit it to use the default client.
1020
+ * @returns Registered or newly created FaasReactClient instance.
1003
1021
  *
1004
1022
  * @example
1005
1023
  * ```ts
1024
+ * import { getClient } from '@faasjs/react'
1025
+ *
1006
1026
  * getClient()
1007
- * // or
1008
- * getClient('another-host')
1027
+ * getClient('http://localhost:8080/api/')
1009
1028
  * ```
1010
1029
  */
1011
1030
  function getClient(host) {
@@ -1061,53 +1080,46 @@ var ErrorBoundary = class extends react.Component {
1061
1080
  }
1062
1081
  };
1063
1082
  //#endregion
1064
- //#region src/useStateRef.ts
1083
+ //#region src/OptionalWrapper.tsx
1065
1084
  /**
1066
- * Custom hook that returns a stateful value and a ref to that value.
1067
- *
1068
- * @template T - The type of the value.
1069
- * @param {T} initialValue - The initial value of the state.
1070
- * @returns {[T, (value: T) => void, RefObject<T>]} - The stateful value, a function to set the value, and a ref to the value.
1085
+ * A wrapper component that conditionally wraps its children with a provided wrapper component.
1071
1086
  *
1072
1087
  * @example
1073
1088
  * ```tsx
1074
- * import { useStateRef } from '@faasjs/react'
1089
+ * import { OptionalWrapper } from '@faasjs/react'
1075
1090
  *
1076
- * function MyComponent() {
1077
- * const [value, setValue, ref] = useStateRef(0)
1078
- *
1079
- * return (
1080
- * <div>
1081
- * <p>Value: {value}</p>
1082
- * <button onClick={() => setValue(value + 1)}>Increment</button>
1083
- * <button onClick={() => console.log(ref.current)}>Submit</button>
1084
- * </div>
1091
+ * const Wrapper = ({ children }: { children: React.ReactNode }) => (
1092
+ * <div className='wrapper'>{children}</div>
1093
+ * )
1094
+ *
1095
+ * const App = () => (
1096
+ * <OptionalWrapper condition={true} Wrapper={Wrapper}>
1097
+ * <span>Test</span>
1098
+ * </OptionalWrapper>
1085
1099
  * )
1100
+ * ```
1086
1101
  */
1087
- function useStateRef(initialValue) {
1088
- const [state, setState] = (0, react.useState)(initialValue ?? null);
1089
- const ref = (0, react.useRef)(state);
1090
- (0, react.useEffect)(() => {
1091
- ref.current = state;
1092
- }, [state]);
1093
- return [
1094
- state,
1095
- setState,
1096
- ref
1097
- ];
1102
+ function OptionalWrapper({ condition, Wrapper, wrapperProps, children }) {
1103
+ if (condition) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Wrapper, {
1104
+ ...wrapperProps,
1105
+ children
1106
+ });
1107
+ return children;
1098
1108
  }
1109
+ OptionalWrapper.displayName = "OptionalWrapper";
1099
1110
  //#endregion
1100
1111
  //#region src/splittingState.tsx
1101
1112
  /**
1102
- * A hook that initializes and splits state variables and their corresponding setters.
1113
+ * Create local state entries and matching setters for each key in an object.
1103
1114
  *
1104
1115
  * @template T - A generic type that extends a record with string keys and any values.
1105
- * @param {T} initialStates - An object containing the initial states.
1116
+ * @param initialStates - Object whose keys become state values and `setXxx` setters.
1117
+ * @returns Object containing the original keys plus generated setter functions.
1106
1118
  *
1107
1119
  * @example
1108
1120
  * ```tsx
1109
1121
  * function Counter() {
1110
- * const { count, setCount, name, setName } = useSplittingState({ count: 0, name: 'John' });
1122
+ * const { count, setCount, name, setName } = useSplittingState({ count: 0, name: 'John' })
1111
1123
  *
1112
1124
  * return <>{name}: {count}</>
1113
1125
  * }
@@ -1207,291 +1219,23 @@ function createSplittingContext(defaultValue) {
1207
1219
  };
1208
1220
  }
1209
1221
  //#endregion
1210
- //#region src/Form/context.tsx
1211
- const FormContext = createSplittingContext([
1212
- "items",
1213
- "onSubmit",
1214
- "Elements",
1215
- "lang",
1216
- "rules",
1217
- "submitting",
1218
- "setSubmitting",
1219
- "values",
1220
- "setValues",
1221
- "errors",
1222
- "setErrors",
1223
- "valuesRef"
1224
- ]);
1225
- const FormContextProvider = FormContext.Provider;
1226
- const useFormContext = FormContext.use;
1227
- //#endregion
1228
- //#region src/Form/Input.tsx
1229
- function processValue(input, rules) {
1230
- switch (rules?.type) {
1231
- case "number": return Number(input);
1232
- case "string": return String(input);
1233
- default: return input;
1234
- }
1235
- }
1236
- function FormInput({ name, rules, ...rest }) {
1237
- const { Elements, values, setValues } = useFormContext();
1238
- const value = values?.[name];
1239
- if (rest.Input) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(rest.Input, {
1240
- name,
1241
- value,
1242
- onChange: (v) => setValues((prev) => ({
1243
- ...prev,
1244
- [name]: processValue(v, rules)
1245
- })),
1246
- ...rest.props
1247
- });
1248
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Elements.Input, {
1249
- name,
1250
- value,
1251
- onChange: (v) => setValues((prev) => ({
1252
- ...prev,
1253
- [name]: processValue(v, rules)
1254
- })),
1255
- ...rest.props
1256
- });
1257
- }
1258
- FormInput.displayName = "FormInput";
1259
- //#endregion
1260
- //#region src/Form/Item.tsx
1261
- function FormItem(props) {
1262
- const { Elements, errors } = useFormContext();
1263
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(props.label?.Label ?? Elements.Label, {
1264
- name: props.name,
1265
- ...props.label,
1266
- error: errors[props.name],
1267
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormInput, {
1268
- name: props.name,
1269
- ...props.input,
1270
- ...props.rules ? { rules: props.rules } : {}
1271
- })
1272
- });
1273
- }
1274
- FormItem.displayName = "FormItem";
1275
- //#endregion
1276
- //#region src/Form/Body.tsx
1277
- function FormBody() {
1278
- const { items } = useFormContext();
1279
- return items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormItem, { ...item }, item.name));
1280
- }
1281
- FormBody.displayName = "FormBody";
1282
- //#endregion
1283
- //#region src/Form/elements/Button.tsx
1284
- const FormButtonElement = (0, react.forwardRef)(({ children, submit, submitting, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1285
- type: "button",
1286
- disabled: submitting,
1287
- onClick: submit,
1288
- ...props,
1289
- ref,
1290
- children
1291
- }));
1292
- FormButtonElement.displayName = "FormButtonElement";
1293
- //#endregion
1294
- //#region src/Form/elements/Input.tsx
1295
- const FormInputElement = (0, react.forwardRef)(({ onChange, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1296
- ...props,
1297
- onChange: (e) => onChange(e.target.value),
1298
- ref
1299
- }));
1300
- FormInputElement.displayName = "FormInputElement";
1301
- //#endregion
1302
- //#region src/Form/elements/Label.tsx
1303
- const FormLabelElement = ({ name, title, description, error, children }) => {
1304
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
1305
- title ?? name,
1306
- children,
1307
- description,
1308
- error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1309
- style: { color: "red" },
1310
- children: error.message
1311
- })
1312
- ] });
1313
- };
1314
- FormLabelElement.displayName = "FormLabelElement";
1315
- //#endregion
1316
- //#region src/Form/elements/index.ts
1317
- const FormDefaultElements = {
1318
- Label: FormLabelElement,
1319
- Input: FormInputElement,
1320
- Button: FormButtonElement
1321
- };
1322
- //#endregion
1323
- //#region src/Form/rules.ts
1324
- /**
1325
- * Default validation rules for a form.
1326
- */
1327
- const FormDefaultRules = {
1328
- required: async (value, _, lang) => {
1329
- if (value === null || value === void 0 || value === "" || Number.isNaN(value)) throw Error(lang?.required);
1330
- },
1331
- type: async (value, options, lang) => {
1332
- switch (options) {
1333
- case "string":
1334
- if (typeof value !== "string") throw Error(lang?.string);
1335
- break;
1336
- case "number":
1337
- if (Number.isNaN(Number(value))) throw Error(lang?.number);
1338
- break;
1339
- }
1340
- },
1341
- custom: async (value, options) => {
1342
- return options(value);
1343
- }
1344
- };
1345
- async function validValues(rules, items, values, lang) {
1346
- const errors = {};
1347
- for (const item of items) {
1348
- const value = values[item.name];
1349
- const rulesOptions = item.rules;
1350
- if (rulesOptions) for (const [name, options] of Object.entries(rulesOptions)) try {
1351
- await rules[name](value, options, lang);
1352
- } catch (error) {
1353
- errors[item.name] = error;
1354
- break;
1355
- }
1356
- }
1357
- return errors;
1358
- }
1359
- //#endregion
1360
- //#region src/Form/Footer.tsx
1361
- function FormFooter() {
1362
- const { submitting, setSubmitting, onSubmit, valuesRef, Elements, items, setErrors, lang, rules } = useFormContext();
1363
- const Button = Elements.Button;
1364
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1365
- submitting,
1366
- submit: (0, react.useCallback)(async () => {
1367
- setSubmitting(true);
1368
- setErrors({});
1369
- const errors = await validValues(rules, items, valuesRef.current, lang);
1370
- if (Object.keys(errors).length) {
1371
- setErrors(errors);
1372
- setSubmitting(false);
1373
- return;
1374
- }
1375
- onSubmit(valuesRef.current).finally(() => setSubmitting(false));
1376
- }, [
1377
- setSubmitting,
1378
- setErrors,
1379
- rules,
1380
- items,
1381
- valuesRef,
1382
- lang,
1383
- onSubmit
1384
- ]),
1385
- children: lang.submit
1386
- });
1387
- }
1388
- FormFooter.displayName = "FormFooter";
1389
- //#endregion
1390
- //#region src/Form/lang.ts
1391
- const FormDefaultLang = {
1392
- submit: "Submit",
1393
- required: "This field is required",
1394
- string: "This field must be a string",
1395
- number: "This field must be a number"
1396
- };
1397
- //#endregion
1398
- //#region src/Form/Container.tsx
1399
- function mergeValues(items, defaultValues = {}) {
1400
- const values = {};
1401
- for (const item of items) values[item.name] = defaultValues[item.name] ?? "";
1402
- return values;
1403
- }
1222
+ //#region src/useFaasStream.tsx
1404
1223
  /**
1405
- * FormContainer component is a wrapper that provides context and state management for form elements.
1406
- * It initializes form states such as values, errors, submitting status, elements, language, and rules.
1407
- *
1408
- * @template Values - The type of form values, defaults to Record<string, any>.
1409
- * @template FormElements - The type of form elements, defaults to FormElementTypes.
1410
- * @template Rules - The type of form rules, defaults to FormDefaultRules.
1224
+ * Stream a FaasJS response into React state.
1411
1225
  *
1412
- * @param {FormProps<Values, FormElements, Rules>} props - The properties for the FormContainer component.
1413
- * @param {Values} props.defaultValues - The default values for the form fields.
1414
- * @param {FormElements} props.Elements - The form elements to be used in the form.
1415
- * @param {Rules} props.rules - The validation rules for the form fields.
1416
- * @param {FormLang} props.lang - The language settings for the form.
1417
- * @param {Partial<FormContextProps>} props - Additional properties for the form context.
1226
+ * The hook sends a streaming request, appends decoded text chunks to `data`,
1227
+ * and exposes reload helpers for retrying the same action.
1418
1228
  *
1419
- * @returns {JSX.Element} The FormContainer component.
1229
+ * @param action - Action path to invoke.
1230
+ * @param defaultParams - Params used for the initial request and future reloads.
1231
+ * @param options - Optional hook configuration such as controlled data, debounce, and skip logic.
1232
+ * @returns Streaming request state and helper methods.
1420
1233
  *
1421
1234
  * @example
1422
1235
  * ```tsx
1423
- * import { Form } from '@faasjs/react'
1424
- *
1425
- * function MyForm() {
1426
- * return <Form
1427
- * items={[
1428
- * { name: 'name' },
1429
- * ]}
1430
- * />
1431
- * }
1432
- * ```
1433
- */
1434
- function FormContainer({ defaultValues, Elements, rules, lang, items, ...props }) {
1435
- const [values, setValues, valuesRef] = useStateRef(mergeValues(items, defaultValues));
1436
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(FormContextProvider, {
1437
- initializeStates: {
1438
- errors: {},
1439
- submitting: false
1440
- },
1441
- value: {
1442
- Elements: Object.assign(FormDefaultElements, Elements),
1443
- lang: Object.assign(FormDefaultLang, lang),
1444
- rules: Object.assign(FormDefaultRules, rules),
1445
- items,
1446
- values,
1447
- setValues,
1448
- valuesRef,
1449
- ...props
1450
- },
1451
- memo: true,
1452
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormBody, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormFooter, {})]
1453
- });
1454
- }
1455
- FormContainer.displayName = "FormContainer";
1456
- //#endregion
1457
- //#region src/OptionalWrapper.tsx
1458
- /**
1459
- * A wrapper component that conditionally wraps its children with a provided wrapper component.
1460
- *
1461
- * @example
1462
- * ```tsx
1463
- * import { OptionalWrapper } from '@faasjs/react'
1464
- *
1465
- * const Wrapper = ({ children }: { children: React.ReactNode }) => (
1466
- * <div className='wrapper'>{children}</div>
1467
- * )
1468
- *
1469
- * const App = () => (
1470
- * <OptionalWrapper condition={true} Wrapper={Wrapper}>
1471
- * <span>Test</span>
1472
- * </OptionalWrapper>
1473
- * )
1474
- * ```
1475
- */
1476
- function OptionalWrapper({ condition, Wrapper, wrapperProps, children }) {
1477
- if (condition) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Wrapper, {
1478
- ...wrapperProps,
1479
- children
1480
- });
1481
- return children;
1482
- }
1483
- OptionalWrapper.displayName = "OptionalWrapper";
1484
- //#endregion
1485
- //#region src/useFaasStream.tsx
1486
- /**
1487
- * Stream faas server response with React hook
1236
+ * import { useState } from 'react'
1237
+ * import { useFaasStream } from '@faasjs/react'
1488
1238
  *
1489
- * @param action {string} action name
1490
- * @param defaultParams {object} initial action params
1491
- * @returns {UseFaasStreamResult}
1492
- *
1493
- * @example
1494
- * ```tsx
1495
1239
  * function Chat() {
1496
1240
  * const [prompt, setPrompt] = useState('')
1497
1241
  * const { data, loading, reload } = useFaasStream('chat', { prompt })
@@ -1632,8 +1376,8 @@ function useFaasStream(action, defaultParams, options = {}) {
1632
1376
  * Hook to store the previous value of a state or prop.
1633
1377
  *
1634
1378
  * @template T - The type of the value.
1635
- * @param {T} value - The current value to be stored.
1636
- * @returns {T | undefined} - The previous value, or undefined if there is no previous value.
1379
+ * @param value - The current value to track.
1380
+ * @returns Previous value from the prior render, or `undefined` on the first render.
1637
1381
  */
1638
1382
  function usePrevious(value) {
1639
1383
  const ref = (0, react.useRef)(void 0);
@@ -1643,17 +1387,48 @@ function usePrevious(value) {
1643
1387
  return ref.current;
1644
1388
  }
1645
1389
  //#endregion
1390
+ //#region src/useStateRef.ts
1391
+ /**
1392
+ * Custom hook that returns a stateful value and a ref to that value.
1393
+ *
1394
+ * @template T - The type of the value.
1395
+ * @param initialValue - Initial state value. When omitted, state starts as `null`.
1396
+ * @returns Tuple containing the current state, the state setter, and a ref that always points at the latest state.
1397
+ *
1398
+ * @example
1399
+ * ```tsx
1400
+ * import { useStateRef } from '@faasjs/react'
1401
+ *
1402
+ * function MyComponent() {
1403
+ * const [value, setValue, ref] = useStateRef(0)
1404
+ *
1405
+ * return (
1406
+ * <div>
1407
+ * <p>Value: {value}</p>
1408
+ * <button onClick={() => setValue(value + 1)}>Increment</button>
1409
+ * <button onClick={() => console.log(ref.current)}>Submit</button>
1410
+ * </div>
1411
+ * )
1412
+ * }
1413
+ * ```
1414
+ */
1415
+ function useStateRef(initialValue) {
1416
+ const [state, setState] = (0, react.useState)(initialValue ?? null);
1417
+ const ref = (0, react.useRef)(state);
1418
+ (0, react.useEffect)(() => {
1419
+ ref.current = state;
1420
+ }, [state]);
1421
+ return [
1422
+ state,
1423
+ setState,
1424
+ ref
1425
+ ];
1426
+ }
1427
+ //#endregion
1646
1428
  exports.ErrorBoundary = ErrorBoundary;
1647
1429
  exports.FaasBrowserClient = FaasBrowserClient;
1648
1430
  exports.FaasDataWrapper = FaasDataWrapper;
1649
1431
  exports.FaasReactClient = FaasReactClient;
1650
- exports.Form = FormContainer;
1651
- exports.FormContextProvider = FormContextProvider;
1652
- exports.FormDefaultElements = FormDefaultElements;
1653
- exports.FormDefaultLang = FormDefaultLang;
1654
- exports.FormDefaultRules = FormDefaultRules;
1655
- exports.FormInput = FormInput;
1656
- exports.FormItem = FormItem;
1657
1432
  exports.OptionalWrapper = OptionalWrapper;
1658
1433
  exports.Response = Response;
1659
1434
  exports.ResponseError = ResponseError;
@@ -1670,9 +1445,7 @@ exports.useEqualMemo = useEqualMemo;
1670
1445
  exports.useEqualMemoize = useEqualMemoize;
1671
1446
  exports.useFaas = useFaas;
1672
1447
  exports.useFaasStream = useFaasStream;
1673
- exports.useFormContext = useFormContext;
1674
1448
  exports.usePrevious = usePrevious;
1675
1449
  exports.useSplittingState = useSplittingState;
1676
1450
  exports.useStateRef = useStateRef;
1677
- exports.validValues = validValues;
1678
1451
  exports.withFaasData = withFaasData;