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