@faasjs/react 8.0.0-beta.5 → 8.0.0-beta.7

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
@@ -1,745 +1,1009 @@
1
- 'use strict';
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let _faasjs_browser = require("@faasjs/browser");
3
+ let react = require("react");
4
+ let react_jsx_runtime = require("react/jsx-runtime");
2
5
 
3
- var browser = require('@faasjs/browser');
4
- var react = require('react');
5
- var jsxRuntime = require('react/jsx-runtime');
6
-
7
- // src/client.tsx
8
- var AsyncFunction = (async () => {
9
- }).constructor;
6
+ //#region src/equal.ts
7
+ const AsyncFunction = (async () => {}).constructor;
8
+ /**
9
+ * Compares two values for deep equality.
10
+ *
11
+ * This function checks if two values are deeply equal by comparing their types and contents.
12
+ * It handles various data types including primitives, arrays, dates, regular expressions, functions,
13
+ * maps, sets, and promises.
14
+ *
15
+ * @param a - The first value to compare.
16
+ * @param b - The second value to compare.
17
+ * @returns `true` if the values are deeply equal, `false` otherwise.
18
+ */
10
19
  function equal(a, b) {
11
- if (a === b) return true;
12
- if ((a === null || a === void 0) && (b === null || b === void 0))
13
- return true;
14
- if (typeof a !== typeof b) return false;
15
- if (a === null || a === void 0 || b === null || b === void 0)
16
- return false;
17
- const ctor = a.constructor;
18
- if (ctor !== b.constructor) return false;
19
- switch (ctor) {
20
- case String:
21
- case Boolean:
22
- return a === b;
23
- case Number:
24
- return Number.isNaN(a) && Number.isNaN(b) || a === b;
25
- case Array: {
26
- if (a.length !== b.length) return false;
27
- for (let i = 0; i < a.length; i++) {
28
- if (!equal(a[i], b[i])) return false;
29
- }
30
- return true;
31
- }
32
- case Date:
33
- return a.getTime() === b.getTime();
34
- case RegExp:
35
- case Function:
36
- case AsyncFunction:
37
- return a.toString() === b.toString();
38
- case Map:
39
- case Set:
40
- return equal(Array.from(a), Array.from(b));
41
- case Promise:
42
- return a === b;
43
- case Object: {
44
- for (const key of /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]))
45
- if (!equal(a[key], b[key])) return false;
46
- return true;
47
- }
48
- default:
49
- throw Error(`Unsupported type: ${ctor}`);
50
- }
20
+ if (a === b) return true;
21
+ if ((a === null || a === void 0) && (b === null || b === void 0)) return true;
22
+ if (typeof a !== typeof b) return false;
23
+ if (a === null || a === void 0 || b === null || b === void 0) return false;
24
+ const ctor = a.constructor;
25
+ if (ctor !== b.constructor) return false;
26
+ switch (ctor) {
27
+ case String:
28
+ case Boolean: return a === b;
29
+ case Number: return Number.isNaN(a) && Number.isNaN(b) || a === b;
30
+ case Array:
31
+ if (a.length !== b.length) return false;
32
+ for (let i = 0; i < a.length; i++) if (!equal(a[i], b[i])) return false;
33
+ return true;
34
+ case Date: return a.getTime() === b.getTime();
35
+ case RegExp:
36
+ case Function:
37
+ case AsyncFunction: return a.toString() === b.toString();
38
+ case Map:
39
+ case Set: return equal(Array.from(a), Array.from(b));
40
+ case Promise: return a === b;
41
+ case Object:
42
+ for (const key of new Set([...Object.keys(a), ...Object.keys(b)])) if (!equal(a[key], b[key])) return false;
43
+ return true;
44
+ default: throw Error(`Unsupported type: ${ctor}`);
45
+ }
51
46
  }
47
+ /**
48
+ * Custom hook that memoizes a value using deep equality comparison.
49
+ *
50
+ * @param value - The value to be memoized.
51
+ * @returns The memoized value.
52
+ */
52
53
  function useEqualMemoize(value) {
53
- const ref = react.useRef(value);
54
- const signalRef = react.useRef(0);
55
- if (!equal(value, ref.current)) {
56
- ref.current = value;
57
- signalRef.current += 1;
58
- }
59
- return react.useMemo(() => ref.current, [signalRef.current]);
54
+ const ref = (0, react.useRef)(value);
55
+ const signalRef = (0, react.useRef)(0);
56
+ if (!equal(value, ref.current)) {
57
+ ref.current = value;
58
+ signalRef.current += 1;
59
+ }
60
+ return (0, react.useMemo)(() => ref.current, [signalRef.current]);
60
61
  }
62
+ /**
63
+ * Custom hook that works like `useEffect` but uses deep comparison on dependencies.
64
+ *
65
+ * @param callback - The effect callback function to run.
66
+ * @param dependencies - The list of dependencies for the effect.
67
+ * @returns The result of the `useEffect` hook with memoized dependencies.
68
+ */
61
69
  function useEqualEffect(callback, dependencies) {
62
- return react.useEffect(callback, useEqualMemoize(dependencies));
70
+ return (0, react.useEffect)(callback, useEqualMemoize(dependencies));
63
71
  }
72
+ /**
73
+ * Custom hook that works like `useMemo` but uses deep comparison on dependencies.
74
+ *
75
+ * @param callback - The callback function to run.
76
+ * @param dependencies - The list of dependencies.
77
+ * @returns The result of the `useMemo` hook with memoized dependencies.
78
+ */
64
79
  function useEqualMemo(callback, dependencies) {
65
- return react.useMemo(callback, useEqualMemoize(dependencies));
80
+ return (0, react.useMemo)(callback, useEqualMemoize(dependencies));
66
81
  }
82
+ /**
83
+ * Custom hook that works like `useCallback` but uses deep comparison on dependencies.
84
+ *
85
+ * @param callback - The callback function to run.
86
+ * @param dependencies - The list of dependencies.
87
+ * @returns The result of the `useCallback` hook with memoized dependencies.
88
+ */
67
89
  function useEqualCallback(callback, dependencies) {
68
- return react.useCallback(
69
- (...args) => callback(...args),
70
- useEqualMemoize(dependencies)
71
- );
90
+ return (0, react.useCallback)((...args) => callback(...args), useEqualMemoize(dependencies));
72
91
  }
73
- var fixedForwardRef = react.forwardRef;
74
- var FaasDataWrapper = fixedForwardRef(
75
- (props, ref) => {
76
- const request = getClient(props.baseUrl).useFaas(
77
- props.action,
78
- props.params,
79
- {
80
- data: props.data,
81
- setData: props.setData
82
- }
83
- );
84
- const [loaded, setLoaded] = react.useState(false);
85
- react.useImperativeHandle(ref, () => request, [request]);
86
- useEqualEffect(() => {
87
- if (!request.loading) setLoaded((prev) => prev === false ? true : prev);
88
- }, [request.loading]);
89
- useEqualEffect(() => {
90
- if (props.onDataChange) props.onDataChange(request);
91
- }, [request.data]);
92
- const child = useEqualMemo(() => {
93
- if (loaded) {
94
- if (props.children) return react.cloneElement(props.children, request);
95
- if (props.render) return props.render(request);
96
- }
97
- return props.fallback || null;
98
- }, [
99
- loaded,
100
- request.action,
101
- request.params,
102
- request.data,
103
- request.error,
104
- request.loading
105
- ]);
106
- return child;
107
- }
108
- );
109
- Object.assign(FaasDataWrapper, {
110
- displayName: "FaasDataWrapper"
92
+
93
+ //#endregion
94
+ //#region src/FaasDataWrapper.tsx
95
+ const fixedForwardRef = react.forwardRef;
96
+ const FaasDataWrapper = fixedForwardRef((props, ref) => {
97
+ const requestOptions = {
98
+ ...props.data !== void 0 ? { data: props.data } : {},
99
+ ...props.setData ? { setData: props.setData } : {}
100
+ };
101
+ const request = getClient(props.baseUrl).useFaas(props.action, props.params ?? {}, requestOptions);
102
+ const [loaded, setLoaded] = (0, react.useState)(false);
103
+ (0, react.useImperativeHandle)(ref, () => request, [request]);
104
+ useEqualEffect(() => {
105
+ if (!request.loading) setLoaded((prev) => prev === false ? true : prev);
106
+ }, [request.loading]);
107
+ useEqualEffect(() => {
108
+ if (props.onDataChange) props.onDataChange(request);
109
+ }, [request.data]);
110
+ return useEqualMemo(() => {
111
+ if (loaded) {
112
+ if (props.children) return (0, react.cloneElement)(props.children, request);
113
+ if (props.render) return props.render(request);
114
+ }
115
+ return props.fallback || null;
116
+ }, [
117
+ loaded,
118
+ request.action,
119
+ request.params,
120
+ request.data,
121
+ request.error,
122
+ request.loading
123
+ ]);
111
124
  });
112
- function withFaasData(Component2, faasProps) {
113
- return (props) => /* @__PURE__ */ jsxRuntime.jsx(FaasDataWrapper, { ...faasProps, children: /* @__PURE__ */ jsxRuntime.jsx(Component2, { ...props }) });
125
+ Object.assign(FaasDataWrapper, { displayName: "FaasDataWrapper" });
126
+ /**
127
+ * HOC to wrap a component with FaasDataWrapper
128
+ *
129
+ * @example
130
+ * ```tsx
131
+ * const MyComponent = withFaasData(({ data }) => <div>{data.name}</div>, { action: 'test', params: { a: 1 } })
132
+ * ```
133
+ */
134
+ function withFaasData(Component, faasProps) {
135
+ return (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FaasDataWrapper, {
136
+ ...faasProps,
137
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...props })
138
+ });
114
139
  }
115
140
 
116
- // src/faas.ts
141
+ //#endregion
142
+ //#region src/faas.ts
143
+ /**
144
+ * Request faas server
145
+ *
146
+ * @param action {string} action name
147
+ * @param params {object} action params
148
+ * @returns {Promise<Response<any>>}
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * faas<{ title: string }>('post/get', { id: 1 }).then(res => {
153
+ * console.log(res.data.title)
154
+ * })
155
+ * ```
156
+ */
117
157
  async function faas(action, params, options) {
118
- const client = getClient(options?.baseUrl);
119
- if (client.onError)
120
- return client.browserClient.action(action, params, options).catch(async (res) => {
121
- await client.onError(action, params)(res);
122
- return Promise.reject(res);
123
- });
124
- return client.browserClient.action(action, params, options);
158
+ const client = getClient(options?.baseUrl);
159
+ const onError = client.onError;
160
+ if (onError) return client.browserClient.action(action, params, options).catch(async (res) => {
161
+ await onError(action, params)(res);
162
+ return Promise.reject(res);
163
+ });
164
+ return client.browserClient.action(action, params, options);
125
165
  }
166
+
167
+ //#endregion
168
+ //#region src/useFaas.tsx
169
+ /**
170
+ * Request faas server with React hook
171
+ *
172
+ * @param action {string} action name
173
+ * @param defaultParams {object} initial action params
174
+ * @returns {FaasDataInjection<any>}
175
+ *
176
+ * @example
177
+ * ```tsx
178
+ * function Post ({ id }) {
179
+ * const { data } = useFaas<{ title: string }>('post/get', { id })
180
+ * return <h1>{data.title}</h1>
181
+ * }
182
+ * ```
183
+ */
126
184
  function useFaas(action, defaultParams, options = {}) {
127
- const [loading, setLoading] = react.useState(true);
128
- const [data, setData] = react.useState();
129
- const [error, setError] = react.useState();
130
- const [params, setParams] = react.useState(defaultParams);
131
- const [reloadTimes, setReloadTimes] = react.useState(0);
132
- const [fails, setFails] = react.useState(0);
133
- const [skip, setSkip] = react.useState(
134
- typeof options.skip === "function" ? options.skip(defaultParams) : options.skip
135
- );
136
- const promiseRef = react.useRef(null);
137
- const controllerRef = react.useRef(null);
138
- const pendingReloadsRef = react.useRef(/* @__PURE__ */ new Map());
139
- const reloadCounterRef = react.useRef(0);
140
- useEqualEffect(() => {
141
- setSkip(
142
- typeof options.skip === "function" ? options.skip(params) : options.skip
143
- );
144
- }, [typeof options.skip === "function" ? params : options.skip]);
145
- useEqualEffect(() => {
146
- if (!equal(defaultParams, params)) {
147
- setParams(defaultParams);
148
- }
149
- }, [defaultParams]);
150
- useEqualEffect(() => {
151
- if (!action || skip) {
152
- setLoading(false);
153
- return;
154
- }
155
- setLoading(true);
156
- controllerRef.current = new AbortController();
157
- const client = getClient(options.baseUrl);
158
- function send() {
159
- const request = client.faas(
160
- action,
161
- options.params || params,
162
- { signal: controllerRef.current.signal }
163
- );
164
- promiseRef.current = request;
165
- request.then((r) => {
166
- setFails(0);
167
- setError(null);
168
- options.setData ? options.setData(r.data) : setData(r.data);
169
- setLoading(false);
170
- for (const { resolve } of pendingReloadsRef.current.values())
171
- resolve(r.data);
172
- pendingReloadsRef.current.clear();
173
- }).catch(async (e) => {
174
- if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0)
175
- return;
176
- if (!fails && typeof e?.message === "string" && e.message.indexOf("Failed to fetch") >= 0) {
177
- console.warn(`FaasReactClient: ${e.message} retry...`);
178
- setFails(1);
179
- return send();
180
- }
181
- let error2 = e;
182
- if (client.onError)
183
- try {
184
- await client.onError(action, params)(e);
185
- } catch (newError) {
186
- error2 = newError;
187
- }
188
- setError(error2);
189
- setLoading(false);
190
- for (const { reject } of pendingReloadsRef.current.values())
191
- reject(error2);
192
- pendingReloadsRef.current.clear();
193
- return;
194
- });
195
- }
196
- if (options.debounce) {
197
- const timeout = setTimeout(send, options.debounce);
198
- return () => {
199
- clearTimeout(timeout);
200
- controllerRef.current?.abort();
201
- setLoading(false);
202
- };
203
- }
204
- send();
205
- return () => {
206
- controllerRef.current?.abort();
207
- setLoading(false);
208
- };
209
- }, [action, options.params || params, reloadTimes, skip]);
210
- const reload = useEqualCallback(
211
- (params2) => {
212
- if (skip) setSkip(false);
213
- if (params2) setParams(params2);
214
- const reloadCounter = ++reloadCounterRef.current;
215
- setReloadTimes((prev) => prev + 1);
216
- return new Promise((resolve, reject) => {
217
- pendingReloadsRef.current.set(reloadCounter, { resolve, reject });
218
- setReloadTimes((prev) => prev + 1);
219
- });
220
- },
221
- [params, skip]
222
- );
223
- return {
224
- action,
225
- params,
226
- loading,
227
- data: options.data || data,
228
- reloadTimes,
229
- error,
230
- promise: promiseRef.current,
231
- reload,
232
- setData: options.setData || setData,
233
- setLoading,
234
- setPromise: (newPromise) => typeof newPromise === "function" ? newPromise(promiseRef.current) : promiseRef.current = newPromise,
235
- setError
236
- };
185
+ const [loading, setLoading] = (0, react.useState)(true);
186
+ const [data, setData] = (0, react.useState)();
187
+ const [error, setError] = (0, react.useState)();
188
+ const [params, setParams] = (0, react.useState)(defaultParams);
189
+ const [reloadTimes, setReloadTimes] = (0, react.useState)(0);
190
+ const [fails, setFails] = (0, react.useState)(0);
191
+ const [skip, setSkip] = (0, react.useState)(typeof options.skip === "function" ? options.skip(defaultParams) : options.skip);
192
+ const promiseRef = (0, react.useRef)(null);
193
+ const controllerRef = (0, react.useRef)(null);
194
+ const localSetData = setData;
195
+ const pendingReloadsRef = (0, react.useRef)(/* @__PURE__ */ new Map());
196
+ const reloadCounterRef = (0, react.useRef)(0);
197
+ useEqualEffect(() => {
198
+ setSkip(typeof options.skip === "function" ? options.skip(params) : options.skip);
199
+ }, [typeof options.skip === "function" ? params : options.skip]);
200
+ useEqualEffect(() => {
201
+ if (!equal(defaultParams, params)) setParams(defaultParams);
202
+ }, [defaultParams]);
203
+ useEqualEffect(() => {
204
+ if (!action || skip) {
205
+ setLoading(false);
206
+ return;
207
+ }
208
+ setLoading(true);
209
+ const controller = new AbortController();
210
+ controllerRef.current = controller;
211
+ const client = getClient(options.baseUrl);
212
+ const requestParams = options.params ?? params;
213
+ function send() {
214
+ const request = client.faas(action, requestParams, { signal: controller.signal });
215
+ promiseRef.current = request;
216
+ request.then((r) => {
217
+ const nextData = r.data;
218
+ setFails(0);
219
+ setError(null);
220
+ options.setData ? options.setData(nextData) : localSetData(nextData);
221
+ setLoading(false);
222
+ for (const { resolve } of pendingReloadsRef.current.values()) resolve(nextData);
223
+ pendingReloadsRef.current.clear();
224
+ }).catch(async (e) => {
225
+ if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0) return;
226
+ if (!fails && typeof e?.message === "string" && e.message.indexOf("Failed to fetch") >= 0) {
227
+ console.warn(`FaasReactClient: ${e.message} retry...`);
228
+ setFails(1);
229
+ return send();
230
+ }
231
+ let error = e;
232
+ if (client.onError) try {
233
+ await client.onError(action, requestParams)(e);
234
+ } catch (newError) {
235
+ error = newError;
236
+ }
237
+ setError(error);
238
+ setLoading(false);
239
+ for (const { reject } of pendingReloadsRef.current.values()) reject(error);
240
+ pendingReloadsRef.current.clear();
241
+ });
242
+ }
243
+ if (options.debounce) {
244
+ const timeout = setTimeout(send, options.debounce);
245
+ return () => {
246
+ clearTimeout(timeout);
247
+ controllerRef.current?.abort();
248
+ setLoading(false);
249
+ };
250
+ }
251
+ send();
252
+ return () => {
253
+ controllerRef.current?.abort();
254
+ setLoading(false);
255
+ };
256
+ }, [
257
+ action,
258
+ options.params || params,
259
+ reloadTimes,
260
+ skip
261
+ ]);
262
+ const reload = useEqualCallback((params) => {
263
+ if (skip) setSkip(false);
264
+ if (params) setParams(params);
265
+ const reloadCounter = ++reloadCounterRef.current;
266
+ setReloadTimes((prev) => prev + 1);
267
+ return new Promise((resolve, reject) => {
268
+ pendingReloadsRef.current.set(reloadCounter, {
269
+ resolve,
270
+ reject
271
+ });
272
+ setReloadTimes((prev) => prev + 1);
273
+ });
274
+ }, [params, skip]);
275
+ const currentData = options.data ?? data;
276
+ const currentPromise = promiseRef.current ?? Promise.resolve({});
277
+ return {
278
+ action,
279
+ params,
280
+ loading,
281
+ data: currentData,
282
+ reloadTimes,
283
+ error,
284
+ promise: currentPromise,
285
+ reload,
286
+ setData: options.setData ?? localSetData,
287
+ setLoading,
288
+ setPromise: (newPromise) => {
289
+ promiseRef.current = typeof newPromise === "function" ? newPromise(currentPromise) : newPromise;
290
+ },
291
+ setError
292
+ };
237
293
  }
238
- var clients = {};
239
- function FaasReactClient({ baseUrl, options, onError } = {
240
- baseUrl: "/"
241
- }) {
242
- const client = new browser.FaasBrowserClient(baseUrl, options);
243
- const reactClient = {
244
- id: client.id,
245
- faas: async (action, params, options2) => faas(action, params, { baseUrl, ...options2 }),
246
- useFaas: (action, defaultParams, options2) => useFaas(action, defaultParams, { baseUrl, ...options2 }),
247
- FaasDataWrapper: (props) => /* @__PURE__ */ jsxRuntime.jsx(FaasDataWrapper, { baseUrl, ...props }),
248
- onError,
249
- browserClient: client
250
- };
251
- clients[baseUrl] = reactClient;
252
- return reactClient;
294
+
295
+ //#endregion
296
+ //#region src/client.tsx
297
+ const clients = {};
298
+ /**
299
+ * Before use faas, you should initialize a FaasReactClient.
300
+ *
301
+ * @returns FaasReactClient instance.
302
+ *
303
+ * @example
304
+ * ```ts
305
+ * const client = FaasReactClient({
306
+ * baseUrl: 'localhost:8080/api/'
307
+ * })
308
+ * ```
309
+ */
310
+ function FaasReactClient({ baseUrl, options: clientOptions, onError } = { baseUrl: "/" }) {
311
+ const resolvedBaseUrl = baseUrl ?? "/";
312
+ const client = new _faasjs_browser.FaasBrowserClient(resolvedBaseUrl, clientOptions);
313
+ function withBaseUrl(options) {
314
+ if (options?.baseUrl) return options;
315
+ return {
316
+ ...options ?? {},
317
+ baseUrl: resolvedBaseUrl
318
+ };
319
+ }
320
+ const reactClient = {
321
+ id: client.id,
322
+ faas: async (action, params, requestOptions) => faas(action, params, withBaseUrl(requestOptions)),
323
+ useFaas: (action, defaultParams, requestOptions) => useFaas(action, defaultParams, withBaseUrl(requestOptions)),
324
+ FaasDataWrapper: (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FaasDataWrapper, {
325
+ ...props,
326
+ baseUrl: resolvedBaseUrl
327
+ }),
328
+ ...onError ? { onError } : {},
329
+ browserClient: client
330
+ };
331
+ clients[resolvedBaseUrl] = reactClient;
332
+ return reactClient;
253
333
  }
334
+ /**
335
+ * Get FaasReactClient instance
336
+ *
337
+ * @param host {string} empty string for default host
338
+ * @returns {FaasReactClientInstance}
339
+ *
340
+ * @example
341
+ * ```ts
342
+ * getClient()
343
+ * // or
344
+ * getClient('another-host')
345
+ * ```
346
+ */
254
347
  function getClient(host) {
255
- const client = clients[host || Object.keys(clients)[0]];
256
- if (!client) {
257
- console.warn("FaasReactClient is not initialized manually, use default.");
258
- return FaasReactClient();
259
- }
260
- return client;
348
+ const client = clients[host || Object.keys(clients)[0]];
349
+ if (!client) {
350
+ console.warn("FaasReactClient is not initialized manually, use default.");
351
+ return FaasReactClient();
352
+ }
353
+ return client;
261
354
  }
355
+
356
+ //#endregion
357
+ //#region src/constant.ts
358
+ /**
359
+ * Returns a constant value that is created by the given function.
360
+ */
262
361
  function useConstant(fn) {
263
- const ref = react.useRef(null);
264
- if (!ref.current) {
265
- ref.current = { v: fn() };
266
- }
267
- return ref.current.v;
362
+ const ref = (0, react.useRef)(null);
363
+ if (!ref.current) ref.current = { v: fn() };
364
+ return ref.current.v;
268
365
  }
366
+
367
+ //#endregion
368
+ //#region src/ErrorBoundary.tsx
269
369
  var ErrorBoundary = class extends react.Component {
270
- static displayName = "ErrorBoundary";
271
- constructor(props) {
272
- super(props);
273
- this.state = {
274
- error: void 0,
275
- info: { componentStack: "" }
276
- };
277
- }
278
- componentDidCatch(error, info) {
279
- this.setState({
280
- error,
281
- info
282
- });
283
- }
284
- render() {
285
- const errorMessage = (this.state.error || "").toString();
286
- const errorDescription = this.state.info?.componentStack ? this.state.info.componentStack : null;
287
- if (this.state.error) {
288
- if (this.props.onError)
289
- this.props.onError(this.state.error, this.state.info);
290
- if (this.props.errorChildren)
291
- return react.cloneElement(this.props.errorChildren, {
292
- error: this.state.error,
293
- info: this.state.info,
294
- errorMessage,
295
- errorDescription
296
- });
297
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
298
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: errorMessage }),
299
- /* @__PURE__ */ jsxRuntime.jsx("pre", { children: errorDescription })
300
- ] });
301
- }
302
- return this.props.children;
303
- }
370
+ static displayName = "ErrorBoundary";
371
+ constructor(props) {
372
+ super(props);
373
+ this.state = {
374
+ error: null,
375
+ info: { componentStack: "" }
376
+ };
377
+ }
378
+ componentDidCatch(error, info) {
379
+ this.setState({
380
+ error,
381
+ info
382
+ });
383
+ }
384
+ render() {
385
+ const { error, info } = this.state;
386
+ const errorMessage = String(error ?? "");
387
+ const errorDescription = info.componentStack || void 0;
388
+ if (error) {
389
+ if (this.props.onError) this.props.onError(error, info);
390
+ if (this.props.errorChildren) return (0, react.cloneElement)(this.props.errorChildren, {
391
+ error,
392
+ info,
393
+ errorMessage,
394
+ ...errorDescription ? { errorDescription } : {}
395
+ });
396
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: errorMessage }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: errorDescription })] });
397
+ }
398
+ return this.props.children ?? null;
399
+ }
304
400
  };
401
+
402
+ //#endregion
403
+ //#region src/useStateRef.ts
404
+ /**
405
+ * Custom hook that returns a stateful value and a ref to that value.
406
+ *
407
+ * @template T - The type of the value.
408
+ * @param {T} initialValue - The initial value of the state.
409
+ * @returns {[T, (value: T) => void, RefObject<T>]} - The stateful value, a function to set the value, and a ref to the value.
410
+ *
411
+ * @example
412
+ * ```tsx
413
+ * import { useStateRef } from '@faasjs/react'
414
+ *
415
+ * function MyComponent() {
416
+ * const [value, setValue, ref] = useStateRef(0)
417
+ *
418
+ * return (
419
+ * <div>
420
+ * <p>Value: {value}</p>
421
+ * <button onClick={() => setValue(value + 1)}>Increment</button>
422
+ * <button onClick={() => console.log(ref.current)}>Submit</button>
423
+ * </div>
424
+ * )
425
+ */
305
426
  function useStateRef(initialValue) {
306
- const [state, setState] = react.useState(initialValue ?? null);
307
- const ref = react.useRef(state);
308
- react.useEffect(() => {
309
- ref.current = state;
310
- }, [state]);
311
- return [state, setState, ref];
427
+ const [state, setState] = (0, react.useState)(initialValue ?? null);
428
+ const ref = (0, react.useRef)(state);
429
+ (0, react.useEffect)(() => {
430
+ ref.current = state;
431
+ }, [state]);
432
+ return [
433
+ state,
434
+ setState,
435
+ ref
436
+ ];
312
437
  }
438
+
439
+ //#endregion
440
+ //#region src/splittingState.tsx
441
+ /**
442
+ * A hook that initializes and splits state variables and their corresponding setters.
443
+ *
444
+ * @template T - A generic type that extends a record with string keys and any values.
445
+ * @param {T} initialStates - An object containing the initial states.
446
+ *
447
+ * @example
448
+ * ```tsx
449
+ * function Counter() {
450
+ * const { count, setCount, name, setName } = useSplittingState({ count: 0, name: 'John' });
451
+ *
452
+ * return <>{name}: {count}</>
453
+ * }
454
+ * ```
455
+ */
313
456
  function useSplittingState(initialStates) {
314
- const states = {};
315
- for (const key of Object.keys(initialStates)) {
316
- const state = react.useState(initialStates[key]);
317
- Object.assign(states, {
318
- [key]: state[0],
319
- [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
320
- });
321
- }
322
- return states;
457
+ const states = {};
458
+ for (const key of Object.keys(initialStates)) {
459
+ const state = (0, react.useState)(initialStates[key]);
460
+ Object.assign(states, {
461
+ [key]: state[0],
462
+ [`set${String(key).charAt(0).toUpperCase()}${String(key).slice(1)}`]: state[1]
463
+ });
464
+ }
465
+ return states;
323
466
  }
467
+
468
+ //#endregion
469
+ //#region src/splittingContext.tsx
470
+ /**
471
+ * Creates a splitting context with the given default value.
472
+ *
473
+ * @param defaultValue The default value of the splitting context.
474
+ *
475
+ * @example
476
+ * ```tsx
477
+ * const { Provider, use } = createSplittingContext<{
478
+ * value: number
479
+ * setValue: React.Dispatch<React.SetStateAction<number>>
480
+ * }>({
481
+ * value: 0,
482
+ * setValue: null,
483
+ * })
484
+ *
485
+ * function ReaderComponent() {
486
+ * const { value } = use()
487
+ *
488
+ * return <div>{value}</div>
489
+ * }
490
+ *
491
+ * function WriterComponent() {
492
+ * const { setValue } = use()
493
+ *
494
+ * return (
495
+ * <button type='button' onClick={() => setValue((p: number) => p + 1)}>
496
+ * Change
497
+ * </button>
498
+ * )
499
+ * }
500
+ *
501
+ * function App() {
502
+ * const [value, setValue] = useState(0)
503
+ *
504
+ * return (
505
+ * <Provider value={{ value, setValue }}>
506
+ * <ReaderComponent />
507
+ * <WriterComponent />
508
+ * </Provider>
509
+ * )
510
+ * }
511
+ * ```
512
+ */
324
513
  function createSplittingContext(defaultValue) {
325
- const keys = Array.isArray(defaultValue) ? defaultValue : Object.keys(defaultValue);
326
- const defaultValues = Array.isArray(defaultValue) ? keys.reduce(
327
- (prev, cur) => {
328
- prev[cur] = null;
329
- return prev;
330
- },
331
- {}
332
- ) : defaultValue;
333
- const contexts = {};
334
- for (const key of keys) contexts[key] = react.createContext(defaultValues[key]);
335
- function Provider(props) {
336
- const states = props.initializeStates ? useSplittingState(props.initializeStates) : {};
337
- let children = props.memo ? useEqualMemo(
338
- () => props.children,
339
- props.memo === true ? [] : props.memo
340
- ) : props.children;
341
- for (const key of keys) {
342
- const Context = contexts[key];
343
- const value = props.value?.[key] ?? states[key] ?? defaultValues[key];
344
- children = /* @__PURE__ */ jsxRuntime.jsx(Context.Provider, { value, children });
345
- }
346
- return children;
347
- }
348
- Provider.displayName = "SplittingContextProvider";
349
- function use() {
350
- return useConstant(() => {
351
- const obj = /* @__PURE__ */ Object.create(null);
352
- for (const key of Object.keys(contexts)) {
353
- Object.defineProperty(obj, key, {
354
- get: () => {
355
- if (!contexts[key]) {
356
- throw new Error(`Context for key "${key}" is undefined`);
357
- }
358
- return react.useContext(contexts[key]);
359
- }
360
- });
361
- }
362
- return Object.freeze(obj);
363
- });
364
- }
365
- return {
366
- Provider,
367
- use
368
- };
514
+ const keys = Array.isArray(defaultValue) ? defaultValue : Object.keys(defaultValue);
515
+ const defaultValues = Array.isArray(defaultValue) ? keys.reduce((prev, cur) => {
516
+ prev[cur] = null;
517
+ return prev;
518
+ }, {}) : defaultValue;
519
+ const contexts = {};
520
+ for (const key of keys) contexts[key] = (0, react.createContext)(defaultValues[key]);
521
+ function Provider(props) {
522
+ const states = props.initializeStates ? useSplittingState(props.initializeStates) : {};
523
+ let children = props.memo ? useEqualMemo(() => props.children, props.memo === true ? [] : props.memo) : props.children;
524
+ for (const key of keys) {
525
+ const Context = contexts[key];
526
+ const value = props.value?.[key] ?? states[key] ?? defaultValues[key];
527
+ children = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Context.Provider, {
528
+ value,
529
+ children
530
+ });
531
+ }
532
+ return children;
533
+ }
534
+ Provider.displayName = "SplittingContextProvider";
535
+ function use() {
536
+ return useConstant(() => {
537
+ const obj = Object.create(null);
538
+ for (const key of Object.keys(contexts)) Object.defineProperty(obj, key, { get: () => {
539
+ if (!contexts[key]) throw new Error(`Context for key "${key}" is undefined`);
540
+ return (0, react.useContext)(contexts[key]);
541
+ } });
542
+ return Object.freeze(obj);
543
+ });
544
+ }
545
+ return {
546
+ Provider,
547
+ use
548
+ };
369
549
  }
370
550
 
371
- // src/Form/context.tsx
372
- var FormContext = createSplittingContext([
373
- "items",
374
- "onSubmit",
375
- "Elements",
376
- "lang",
377
- "rules",
378
- "submitting",
379
- "setSubmitting",
380
- "values",
381
- "setValues",
382
- "errors",
383
- "setErrors",
384
- "valuesRef"
551
+ //#endregion
552
+ //#region src/Form/context.tsx
553
+ const FormContext = createSplittingContext([
554
+ "items",
555
+ "onSubmit",
556
+ "Elements",
557
+ "lang",
558
+ "rules",
559
+ "submitting",
560
+ "setSubmitting",
561
+ "values",
562
+ "setValues",
563
+ "errors",
564
+ "setErrors",
565
+ "valuesRef"
385
566
  ]);
386
- var FormContextProvider = FormContext.Provider;
387
- var useFormContext = FormContext.use;
567
+ const FormContextProvider = FormContext.Provider;
568
+ const useFormContext = FormContext.use;
569
+
570
+ //#endregion
571
+ //#region src/Form/Input.tsx
388
572
  function processValue(input, rules) {
389
- switch (rules?.type) {
390
- case "number":
391
- return Number(input);
392
- case "string":
393
- return String(input);
394
- default:
395
- return input;
396
- }
573
+ switch (rules?.type) {
574
+ case "number": return Number(input);
575
+ case "string": return String(input);
576
+ default: return input;
577
+ }
397
578
  }
398
- function FormInput({
399
- name,
400
- rules,
401
- ...rest
402
- }) {
403
- const { Elements, values, setValues } = useFormContext();
404
- const value = values?.[name];
405
- if (rest.Input) {
406
- return /* @__PURE__ */ jsxRuntime.jsx(
407
- rest.Input,
408
- {
409
- name,
410
- value,
411
- onChange: (v) => setValues((prev) => ({
412
- ...prev,
413
- [name]: processValue(v, rules)
414
- })),
415
- ...rest.props
416
- }
417
- );
418
- }
419
- return /* @__PURE__ */ jsxRuntime.jsx(
420
- Elements.Input,
421
- {
422
- name,
423
- value,
424
- onChange: (v) => setValues((prev) => ({
425
- ...prev,
426
- [name]: processValue(v, rules)
427
- })),
428
- ...rest.props
429
- }
430
- );
579
+ function FormInput({ name, rules, ...rest }) {
580
+ const { Elements, values, setValues } = useFormContext();
581
+ const value = values?.[name];
582
+ if (rest.Input) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(rest.Input, {
583
+ name,
584
+ value,
585
+ onChange: (v) => setValues((prev) => ({
586
+ ...prev,
587
+ [name]: processValue(v, rules)
588
+ })),
589
+ ...rest.props
590
+ });
591
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Elements.Input, {
592
+ name,
593
+ value,
594
+ onChange: (v) => setValues((prev) => ({
595
+ ...prev,
596
+ [name]: processValue(v, rules)
597
+ })),
598
+ ...rest.props
599
+ });
431
600
  }
432
601
  FormInput.displayName = "FormInput";
602
+
603
+ //#endregion
604
+ //#region src/Form/Item.tsx
433
605
  function FormItem(props) {
434
- const { Elements, errors } = useFormContext();
435
- const Label = props.label?.Label ?? Elements.Label;
436
- return /* @__PURE__ */ jsxRuntime.jsx(Label, { name: props.name, ...props.label, error: errors[props.name], children: /* @__PURE__ */ jsxRuntime.jsx(FormInput, { name: props.name, rules: props.rules, ...props.input }) });
606
+ const { Elements, errors } = useFormContext();
607
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(props.label?.Label ?? Elements.Label, {
608
+ name: props.name,
609
+ ...props.label,
610
+ error: errors[props.name],
611
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormInput, {
612
+ name: props.name,
613
+ ...props.input,
614
+ ...props.rules ? { rules: props.rules } : {}
615
+ })
616
+ });
437
617
  }
438
618
  FormItem.displayName = "FormItem";
619
+
620
+ //#endregion
621
+ //#region src/Form/Body.tsx
439
622
  function FormBody() {
440
- const { items } = useFormContext();
441
- return items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(FormItem, { ...item }, item.name));
623
+ const { items } = useFormContext();
624
+ return items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormItem, { ...item }, item.name));
442
625
  }
443
626
  FormBody.displayName = "FormBody";
444
- var FormButtonElement = react.forwardRef(({ children, submit, submitting, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
445
- "button",
446
- {
447
- type: "button",
448
- disabled: submitting,
449
- onClick: submit,
450
- ...props,
451
- ref,
452
- children
453
- }
454
- ));
627
+
628
+ //#endregion
629
+ //#region src/Form/elements/Button.tsx
630
+ const FormButtonElement = (0, react.forwardRef)(({ children, submit, submitting, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
631
+ type: "button",
632
+ disabled: submitting,
633
+ onClick: submit,
634
+ ...props,
635
+ ref,
636
+ children
637
+ }));
455
638
  FormButtonElement.displayName = "FormButtonElement";
456
- var FormInputElement = react.forwardRef(({ onChange, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("input", { ...props, onChange: (e) => onChange(e.target.value), ref }));
639
+
640
+ //#endregion
641
+ //#region src/Form/elements/Input.tsx
642
+ const FormInputElement = (0, react.forwardRef)(({ onChange, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
643
+ ...props,
644
+ onChange: (e) => onChange(e.target.value),
645
+ ref
646
+ }));
457
647
  FormInputElement.displayName = "FormInputElement";
458
- var FormLabelElement = ({
459
- name,
460
- title,
461
- description,
462
- error,
463
- children
464
- }) => {
465
- return /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
466
- title ?? name,
467
- children,
468
- description,
469
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "red" }, children: error.message })
470
- ] });
648
+
649
+ //#endregion
650
+ //#region src/Form/elements/Label.tsx
651
+ const FormLabelElement = ({ name, title, description, error, children }) => {
652
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
653
+ title ?? name,
654
+ children,
655
+ description,
656
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
657
+ style: { color: "red" },
658
+ children: error.message
659
+ })
660
+ ] });
471
661
  };
472
662
  FormLabelElement.displayName = "FormLabelElement";
473
663
 
474
- // src/Form/elements/index.ts
475
- var FormDefaultElements = {
476
- Label: FormLabelElement,
477
- Input: FormInputElement,
478
- Button: FormButtonElement
664
+ //#endregion
665
+ //#region src/Form/elements/index.ts
666
+ const FormDefaultElements = {
667
+ Label: FormLabelElement,
668
+ Input: FormInputElement,
669
+ Button: FormButtonElement
479
670
  };
480
671
 
481
- // src/Form/rules.ts
482
- var FormDefaultRules = {
483
- required: async (value, _, lang) => {
484
- if (value === null || value === void 0 || value === "" || Number.isNaN(value)) {
485
- throw Error(lang?.required);
486
- }
487
- },
488
- type: async (value, options, lang) => {
489
- switch (options) {
490
- case "string":
491
- if (typeof value !== "string") throw Error(lang?.string);
492
- break;
493
- case "number":
494
- if (Number.isNaN(Number(value))) throw Error(lang?.number);
495
- break;
496
- }
497
- },
498
- custom: async (value, options) => {
499
- return options(value);
500
- }
672
+ //#endregion
673
+ //#region src/Form/rules.ts
674
+ /**
675
+ * Default validation rules for a form.
676
+ */
677
+ const FormDefaultRules = {
678
+ required: async (value, _, lang) => {
679
+ if (value === null || value === void 0 || value === "" || Number.isNaN(value)) throw Error(lang?.required);
680
+ },
681
+ type: async (value, options, lang) => {
682
+ switch (options) {
683
+ case "string":
684
+ if (typeof value !== "string") throw Error(lang?.string);
685
+ break;
686
+ case "number":
687
+ if (Number.isNaN(Number(value))) throw Error(lang?.number);
688
+ break;
689
+ }
690
+ },
691
+ custom: async (value, options) => {
692
+ return options(value);
693
+ }
501
694
  };
502
695
  async function validValues(rules, items, values, lang) {
503
- const errors = {};
504
- for (const item of items) {
505
- const value = values[item.name];
506
- const rulesOptions = item.rules;
507
- if (rulesOptions) {
508
- for (const [name, options] of Object.entries(rulesOptions)) {
509
- try {
510
- await rules[name](value, options, lang);
511
- } catch (error) {
512
- errors[item.name] = error;
513
- break;
514
- }
515
- }
516
- }
517
- }
518
- return errors;
696
+ const errors = {};
697
+ for (const item of items) {
698
+ const value = values[item.name];
699
+ const rulesOptions = item.rules;
700
+ if (rulesOptions) for (const [name, options] of Object.entries(rulesOptions)) try {
701
+ await rules[name](value, options, lang);
702
+ } catch (error) {
703
+ errors[item.name] = error;
704
+ break;
705
+ }
706
+ }
707
+ return errors;
519
708
  }
709
+
710
+ //#endregion
711
+ //#region src/Form/Footer.tsx
520
712
  function FormFooter() {
521
- const {
522
- submitting,
523
- setSubmitting,
524
- onSubmit,
525
- valuesRef,
526
- Elements,
527
- items,
528
- setErrors,
529
- lang,
530
- rules
531
- } = useFormContext();
532
- const handleSubmit = react.useCallback(async () => {
533
- setSubmitting(true);
534
- setErrors({});
535
- const errors = await validValues(rules, items, valuesRef.current, lang);
536
- if (Object.keys(errors).length) {
537
- setErrors(errors);
538
- setSubmitting(false);
539
- return;
540
- }
541
- onSubmit(valuesRef.current).finally(() => setSubmitting(false));
542
- }, [setSubmitting, setErrors, rules, items, lang, onSubmit]);
543
- const MemoizedButton = react.useMemo(
544
- () => /* @__PURE__ */ jsxRuntime.jsx(Elements.Button, { submitting, submit: handleSubmit, children: lang.submit }),
545
- [submitting, handleSubmit, lang.submit, Elements.Button]
546
- );
547
- return MemoizedButton;
713
+ const { submitting, setSubmitting, onSubmit, valuesRef, Elements, items, setErrors, lang, rules } = useFormContext();
714
+ const handleSubmit = (0, react.useCallback)(async () => {
715
+ setSubmitting(true);
716
+ setErrors({});
717
+ const errors = await validValues(rules, items, valuesRef.current, lang);
718
+ if (Object.keys(errors).length) {
719
+ setErrors(errors);
720
+ setSubmitting(false);
721
+ return;
722
+ }
723
+ onSubmit(valuesRef.current).finally(() => setSubmitting(false));
724
+ }, [
725
+ setSubmitting,
726
+ setErrors,
727
+ rules,
728
+ items,
729
+ lang,
730
+ onSubmit
731
+ ]);
732
+ return (0, react.useMemo)(() => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Elements.Button, {
733
+ submitting,
734
+ submit: handleSubmit,
735
+ children: lang.submit
736
+ }), [
737
+ submitting,
738
+ handleSubmit,
739
+ lang.submit,
740
+ Elements.Button
741
+ ]);
548
742
  }
549
743
  FormFooter.displayName = "FormFooter";
550
744
 
551
- // src/Form/lang.ts
552
- var FormDefaultLang = {
553
- submit: "Submit",
554
- required: "This field is required",
555
- string: "This field must be a string",
556
- number: "This field must be a number"
745
+ //#endregion
746
+ //#region src/Form/lang.ts
747
+ const FormDefaultLang = {
748
+ submit: "Submit",
749
+ required: "This field is required",
750
+ string: "This field must be a string",
751
+ number: "This field must be a number"
557
752
  };
753
+
754
+ //#endregion
755
+ //#region src/Form/Container.tsx
558
756
  function mergeValues(items, defaultValues = {}) {
559
- const values = {};
560
- for (const item of items)
561
- values[item.name] = defaultValues[item.name] ?? "";
562
- return values;
757
+ const values = {};
758
+ for (const item of items) values[item.name] = defaultValues[item.name] ?? "";
759
+ return values;
563
760
  }
564
- function FormContainer({
565
- defaultValues,
566
- Elements,
567
- rules,
568
- lang,
569
- items,
570
- ...props
571
- }) {
572
- const [values, setValues, valuesRef] = useStateRef(
573
- mergeValues(items, defaultValues)
574
- );
575
- return /* @__PURE__ */ jsxRuntime.jsxs(
576
- FormContextProvider,
577
- {
578
- initializeStates: {
579
- errors: {},
580
- submitting: false
581
- },
582
- value: {
583
- Elements: Object.assign(FormDefaultElements, Elements),
584
- lang: Object.assign(FormDefaultLang, lang),
585
- rules: Object.assign(FormDefaultRules, rules),
586
- items,
587
- values,
588
- setValues,
589
- valuesRef,
590
- ...props
591
- },
592
- memo: true,
593
- children: [
594
- /* @__PURE__ */ jsxRuntime.jsx(FormBody, {}),
595
- /* @__PURE__ */ jsxRuntime.jsx(FormFooter, {})
596
- ]
597
- }
598
- );
761
+ /**
762
+ * FormContainer component is a wrapper that provides context and state management for form elements.
763
+ * It initializes form states such as values, errors, submitting status, elements, language, and rules.
764
+ *
765
+ * @template Values - The type of form values, defaults to Record<string, any>.
766
+ * @template FormElements - The type of form elements, defaults to FormElementTypes.
767
+ * @template Rules - The type of form rules, defaults to FormDefaultRules.
768
+ *
769
+ * @param {FormProps<Values, FormElements, Rules>} props - The properties for the FormContainer component.
770
+ * @param {Values} props.defaultValues - The default values for the form fields.
771
+ * @param {FormElements} props.Elements - The form elements to be used in the form.
772
+ * @param {Rules} props.rules - The validation rules for the form fields.
773
+ * @param {FormLang} props.lang - The language settings for the form.
774
+ * @param {Partial<FormContextProps>} props - Additional properties for the form context.
775
+ *
776
+ * @returns {JSX.Element} The FormContainer component.
777
+ *
778
+ * @example
779
+ * ```tsx
780
+ * import { Form } from '@faasjs/react'
781
+ *
782
+ * function MyForm() {
783
+ * return <Form
784
+ * items={[
785
+ * { name: 'name' },
786
+ * ]}
787
+ * />
788
+ * }
789
+ * ```
790
+ */
791
+ function FormContainer({ defaultValues, Elements, rules, lang, items, ...props }) {
792
+ const [values, setValues, valuesRef] = useStateRef(mergeValues(items, defaultValues));
793
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(FormContextProvider, {
794
+ initializeStates: {
795
+ errors: {},
796
+ submitting: false
797
+ },
798
+ value: {
799
+ Elements: Object.assign(FormDefaultElements, Elements),
800
+ lang: Object.assign(FormDefaultLang, lang),
801
+ rules: Object.assign(FormDefaultRules, rules),
802
+ items,
803
+ values,
804
+ setValues,
805
+ valuesRef,
806
+ ...props
807
+ },
808
+ memo: true,
809
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormBody, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormFooter, {})]
810
+ });
599
811
  }
600
812
  FormContainer.displayName = "FormContainer";
601
- function OptionalWrapper({
602
- condition,
603
- Wrapper,
604
- wrapperProps,
605
- children
606
- }) {
607
- if (condition) return /* @__PURE__ */ jsxRuntime.jsx(Wrapper, { ...wrapperProps, children });
608
- return children;
813
+
814
+ //#endregion
815
+ //#region src/OptionalWrapper.tsx
816
+ /**
817
+ * A wrapper component that conditionally wraps its children with a provided wrapper component.
818
+ *
819
+ * @example
820
+ * ```tsx
821
+ * import { OptionalWrapper } from '@faasjs/react'
822
+ *
823
+ * const Wrapper = ({ children }: { children: React.ReactNode }) => (
824
+ * <div className='wrapper'>{children}</div>
825
+ * )
826
+ *
827
+ * const App = () => (
828
+ * <OptionalWrapper condition={true} Wrapper={Wrapper}>
829
+ * <span>Test</span>
830
+ * </OptionalWrapper>
831
+ * )
832
+ * ```
833
+ */
834
+ function OptionalWrapper({ condition, Wrapper, wrapperProps, children }) {
835
+ if (condition) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Wrapper, {
836
+ ...wrapperProps,
837
+ children
838
+ });
839
+ return children;
609
840
  }
610
841
  OptionalWrapper.displayName = "OptionalWrapper";
842
+
843
+ //#endregion
844
+ //#region src/useFaasStream.tsx
845
+ /**
846
+ * Stream faas server response with React hook
847
+ *
848
+ * @param action {string} action name
849
+ * @param defaultParams {object} initial action params
850
+ * @returns {UseFaasStreamResult}
851
+ *
852
+ * @example
853
+ * ```tsx
854
+ * function Chat() {
855
+ * const [prompt, setPrompt] = useState('')
856
+ * const { data, loading, reload } = useFaasStream('chat', { prompt })
857
+ *
858
+ * return (
859
+ * <div>
860
+ * <textarea value={prompt} onChange={e => setPrompt(e.target.value)} />
861
+ * <button onClick={reload} disabled={loading}>Send</button>
862
+ * <div>{data}</div>
863
+ * </div>
864
+ * )
865
+ * }
866
+ * ```
867
+ */
611
868
  function useFaasStream(action, defaultParams, options = {}) {
612
- const [loading, setLoading] = react.useState(true);
613
- const [data, setData] = react.useState(options.data || "");
614
- const [error, setError] = react.useState();
615
- const [params, setParams] = react.useState(defaultParams);
616
- const [reloadTimes, setReloadTimes] = react.useState(0);
617
- const [fails, setFails] = react.useState(0);
618
- const [skip, setSkip] = react.useState(
619
- typeof options.skip === "function" ? options.skip(defaultParams) : options.skip
620
- );
621
- const controllerRef = react.useRef(null);
622
- const pendingReloadsRef = react.useRef(/* @__PURE__ */ new Map());
623
- const reloadCounterRef = react.useRef(0);
624
- useEqualEffect(() => {
625
- setSkip(
626
- typeof options.skip === "function" ? options.skip(params) : options.skip
627
- );
628
- }, [typeof options.skip === "function" ? params : options.skip]);
629
- useEqualEffect(() => {
630
- if (!equal(defaultParams, params)) {
631
- setParams(defaultParams);
632
- }
633
- }, [defaultParams]);
634
- useEqualEffect(() => {
635
- if (!action || skip) {
636
- setLoading(false);
637
- return;
638
- }
639
- setLoading(true);
640
- setData("");
641
- controllerRef.current = new AbortController();
642
- const client = getClient(options.baseUrl);
643
- function send() {
644
- client.browserClient.action(action, options.params || params, {
645
- signal: controllerRef.current.signal,
646
- stream: true
647
- }).then(async (response) => {
648
- if (!response.body) {
649
- setError(new Error("Response body is null"));
650
- setLoading(false);
651
- return;
652
- }
653
- const reader = response.body.getReader();
654
- const decoder = new TextDecoder();
655
- let accumulatedText = "";
656
- try {
657
- while (true) {
658
- const { done, value } = await reader.read();
659
- if (done) break;
660
- accumulatedText += decoder.decode(value, { stream: true });
661
- setData(accumulatedText);
662
- }
663
- setFails(0);
664
- setError(null);
665
- setLoading(false);
666
- for (const { resolve } of pendingReloadsRef.current.values())
667
- resolve(accumulatedText);
668
- pendingReloadsRef.current.clear();
669
- } catch (readError) {
670
- reader.releaseLock();
671
- throw readError;
672
- }
673
- }).catch(async (e) => {
674
- if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0)
675
- return;
676
- if (!fails && typeof e?.message === "string" && e.message.indexOf("Failed to fetch") >= 0) {
677
- console.warn(`FaasReactClient: ${e.message} retry...`);
678
- setFails(1);
679
- return send();
680
- }
681
- let error2 = e;
682
- if (client.onError)
683
- try {
684
- await client.onError(action, params)(e);
685
- } catch (newError) {
686
- error2 = newError;
687
- }
688
- setError(error2);
689
- setLoading(false);
690
- for (const { reject } of pendingReloadsRef.current.values())
691
- reject(error2);
692
- pendingReloadsRef.current.clear();
693
- return;
694
- });
695
- }
696
- if (options.debounce) {
697
- const timeout = setTimeout(send, options.debounce);
698
- return () => {
699
- clearTimeout(timeout);
700
- controllerRef.current?.abort();
701
- setLoading(false);
702
- };
703
- }
704
- send();
705
- return () => {
706
- controllerRef.current?.abort();
707
- setLoading(false);
708
- };
709
- }, [action, options.params || params, reloadTimes, skip]);
710
- const reload = useEqualCallback(
711
- (params2) => {
712
- if (skip) setSkip(false);
713
- if (params2) setParams(params2);
714
- const reloadCounter = ++reloadCounterRef.current;
715
- return new Promise((resolve, reject) => {
716
- pendingReloadsRef.current.set(reloadCounter, { resolve, reject });
717
- setReloadTimes((prev) => prev + 1);
718
- });
719
- },
720
- [params, skip]
721
- );
722
- return {
723
- action,
724
- params,
725
- loading,
726
- data: options.data || data,
727
- reloadTimes,
728
- error,
729
- reload,
730
- setData: options.setData || setData,
731
- setLoading,
732
- setError
733
- };
869
+ const [loading, setLoading] = (0, react.useState)(true);
870
+ const [data, setData] = (0, react.useState)(options.data || "");
871
+ const [error, setError] = (0, react.useState)();
872
+ const [params, setParams] = (0, react.useState)(defaultParams);
873
+ const [reloadTimes, setReloadTimes] = (0, react.useState)(0);
874
+ const [fails, setFails] = (0, react.useState)(0);
875
+ const [skip, setSkip] = (0, react.useState)(typeof options.skip === "function" ? options.skip(defaultParams) : options.skip);
876
+ const controllerRef = (0, react.useRef)(null);
877
+ const pendingReloadsRef = (0, react.useRef)(/* @__PURE__ */ new Map());
878
+ const reloadCounterRef = (0, react.useRef)(0);
879
+ useEqualEffect(() => {
880
+ setSkip(typeof options.skip === "function" ? options.skip(params) : options.skip);
881
+ }, [typeof options.skip === "function" ? params : options.skip]);
882
+ useEqualEffect(() => {
883
+ if (!equal(defaultParams, params)) setParams(defaultParams);
884
+ }, [defaultParams]);
885
+ useEqualEffect(() => {
886
+ if (!action || skip) {
887
+ setLoading(false);
888
+ return;
889
+ }
890
+ setLoading(true);
891
+ setData("");
892
+ const controller = new AbortController();
893
+ controllerRef.current = controller;
894
+ const client = getClient(options.baseUrl);
895
+ const requestParams = options.params ?? params;
896
+ function send() {
897
+ client.browserClient.action(action, requestParams, {
898
+ signal: controller.signal,
899
+ stream: true
900
+ }).then(async (response) => {
901
+ if (!response.body) {
902
+ setError(/* @__PURE__ */ new Error("Response body is null"));
903
+ setLoading(false);
904
+ return;
905
+ }
906
+ const reader = response.body.getReader();
907
+ const decoder = new TextDecoder();
908
+ let accumulatedText = "";
909
+ try {
910
+ while (true) {
911
+ const { done, value } = await reader.read();
912
+ if (done) break;
913
+ accumulatedText += decoder.decode(value, { stream: true });
914
+ setData(accumulatedText);
915
+ }
916
+ setFails(0);
917
+ setError(null);
918
+ setLoading(false);
919
+ for (const { resolve } of pendingReloadsRef.current.values()) resolve(accumulatedText);
920
+ pendingReloadsRef.current.clear();
921
+ } catch (readError) {
922
+ reader.releaseLock();
923
+ throw readError;
924
+ }
925
+ }).catch(async (e) => {
926
+ if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0) return;
927
+ if (!fails && typeof e?.message === "string" && e.message.indexOf("Failed to fetch") >= 0) {
928
+ console.warn(`FaasReactClient: ${e.message} retry...`);
929
+ setFails(1);
930
+ return send();
931
+ }
932
+ let error = e;
933
+ if (client.onError) try {
934
+ await client.onError(action, requestParams)(e);
935
+ } catch (newError) {
936
+ error = newError;
937
+ }
938
+ setError(error);
939
+ setLoading(false);
940
+ for (const { reject } of pendingReloadsRef.current.values()) reject(error);
941
+ pendingReloadsRef.current.clear();
942
+ });
943
+ }
944
+ if (options.debounce) {
945
+ const timeout = setTimeout(send, options.debounce);
946
+ return () => {
947
+ clearTimeout(timeout);
948
+ controllerRef.current?.abort();
949
+ setLoading(false);
950
+ };
951
+ }
952
+ send();
953
+ return () => {
954
+ controllerRef.current?.abort();
955
+ setLoading(false);
956
+ };
957
+ }, [
958
+ action,
959
+ options.params || params,
960
+ reloadTimes,
961
+ skip
962
+ ]);
963
+ const reload = useEqualCallback((params) => {
964
+ if (skip) setSkip(false);
965
+ if (params) setParams(params);
966
+ const reloadCounter = ++reloadCounterRef.current;
967
+ return new Promise((resolve, reject) => {
968
+ pendingReloadsRef.current.set(reloadCounter, {
969
+ resolve,
970
+ reject
971
+ });
972
+ setReloadTimes((prev) => prev + 1);
973
+ });
974
+ }, [params, skip]);
975
+ return {
976
+ action,
977
+ params,
978
+ loading,
979
+ data: options.data || data,
980
+ reloadTimes,
981
+ error,
982
+ reload,
983
+ setData: options.setData || setData,
984
+ setLoading,
985
+ setError
986
+ };
734
987
  }
988
+
989
+ //#endregion
990
+ //#region src/usePrevious.ts
991
+ /**
992
+ * Hook to store the previous value of a state or prop.
993
+ *
994
+ * @template T - The type of the value.
995
+ * @param {T} value - The current value to be stored.
996
+ * @returns {T | undefined} - The previous value, or undefined if there is no previous value.
997
+ */
735
998
  function usePrevious(value) {
736
- const ref = react.useRef(void 0);
737
- react.useEffect(() => {
738
- ref.current = value;
739
- });
740
- return ref.current;
999
+ const ref = (0, react.useRef)(void 0);
1000
+ (0, react.useEffect)(() => {
1001
+ ref.current = value;
1002
+ });
1003
+ return ref.current;
741
1004
  }
742
1005
 
1006
+ //#endregion
743
1007
  exports.ErrorBoundary = ErrorBoundary;
744
1008
  exports.FaasDataWrapper = FaasDataWrapper;
745
1009
  exports.FaasReactClient = FaasReactClient;
@@ -748,6 +1012,7 @@ exports.FormContextProvider = FormContextProvider;
748
1012
  exports.FormDefaultElements = FormDefaultElements;
749
1013
  exports.FormDefaultLang = FormDefaultLang;
750
1014
  exports.FormDefaultRules = FormDefaultRules;
1015
+ exports.FormInput = FormInput;
751
1016
  exports.FormItem = FormItem;
752
1017
  exports.OptionalWrapper = OptionalWrapper;
753
1018
  exports.createSplittingContext = createSplittingContext;
@@ -766,4 +1031,4 @@ exports.usePrevious = usePrevious;
766
1031
  exports.useSplittingState = useSplittingState;
767
1032
  exports.useStateRef = useStateRef;
768
1033
  exports.validValues = validValues;
769
- exports.withFaasData = withFaasData;
1034
+ exports.withFaasData = withFaasData;