@faasjs/react 8.0.0-beta.34 → 8.0.0-beta.35

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.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- import * as _$react from "react";
2
1
  import { Component, ComponentProps, ComponentType, Dispatch, ErrorInfo, JSX, ReactElement, ReactNode, RefObject, SetStateAction } from "react";
3
- import * as _$react_jsx_runtime0 from "react/jsx-runtime";
4
2
  import { FaasActionPaths, FaasData, FaasParams } from "@faasjs/types";
5
3
 
6
4
  //#region src/generate-id/index.d.ts
@@ -119,7 +117,13 @@ type ResponseHeaders = {
119
117
  [key: string]: string;
120
118
  };
121
119
  /**
122
- * Type definition for the FaasBrowserClient.action method.
120
+ * Type signature for the {@link FaasBrowserClient.action} method.
121
+ *
122
+ * @template Path - Action path used to infer the request params and response data types.
123
+ * @param {Path} action - Action path to invoke.
124
+ * @param {FaasParams<Path>} [params] - Params sent to the action.
125
+ * @param {Options} [options] - Per-request overrides on top of client defaults.
126
+ * @returns {Promise<Response<FaasData<Path>> | Response>} FaasJS response or native fetch response when streaming.
123
127
  */
124
128
  type FaasBrowserClientAction = <Path extends FaasActionPaths>(action: Path, params?: FaasParams<Path>, options?: Options) => Promise<Response<FaasData<Path>> | Response>;
125
129
  /**
@@ -149,12 +153,55 @@ type MockHandler = (action: string, params: Record<string, any> | undefined, opt
149
153
  //#region src/browser/mock.d.ts
150
154
  /**
151
155
  * Set the global mock handler used by all {@link FaasBrowserClient} instances.
156
+ *
157
+ * When a mock handler is set, every {@link FaasBrowserClient.action} call will
158
+ * route through the mock instead of making a real network request, which is
159
+ * useful for testing and local development.
160
+ *
161
+ * @param {MockHandler | ResponseProps | Response | null | undefined} handler -
162
+ * A mock function that receives `(action, params, options)` and returns a
163
+ * response shape, or a static response/value, or `null`/`undefined` to
164
+ * disable mocking.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * import { setMock, Response } from '@faasjs/react'
169
+ *
170
+ * // Mock all requests with a static response
171
+ * setMock({ data: { name: 'test' } })
172
+ *
173
+ * // Mock with a handler function
174
+ * setMock(async (action, params) => {
175
+ * if (action === 'posts/get') {
176
+ * return { data: { title: 'Hello' } }
177
+ * }
178
+ * return new Error('Not found')
179
+ * })
180
+ *
181
+ * // Disable mocking
182
+ * setMock(null)
183
+ * ```
152
184
  */
153
185
  declare function setMock(handler: MockHandler | ResponseProps | Response | null | undefined): void;
154
186
  //#endregion
155
187
  //#region src/browser/client.d.ts
156
188
  /**
157
189
  * Browser client for FaasJS - provides HTTP client functionality for making API requests from web applications.
190
+ *
191
+ * Handles request URL construction, default and per-request option merging,
192
+ * before-request hooks, mock resolution for testing, and native fetch dispatching.
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * import { FaasBrowserClient } from '@faasjs/react'
197
+ *
198
+ * const client = new FaasBrowserClient('https://api.example.com/', {
199
+ * headers: { 'X-Custom-Header': 'value' },
200
+ * })
201
+ *
202
+ * const response = await client.action('posts/get', { id: 1 })
203
+ * console.log(response.data)
204
+ * ```
158
205
  */
159
206
  declare class FaasBrowserClient {
160
207
  /**
@@ -171,10 +218,26 @@ declare class FaasBrowserClient {
171
218
  defaultOptions: Options;
172
219
  /**
173
220
  * Creates a new FaasBrowserClient instance.
221
+ *
222
+ * @param {BaseUrl} [baseUrl='/'] - Base URL used to build action request URLs. Must end with `/`.
223
+ * @param {Options} [options={}] - Default request options merged into every request.
224
+ * @throws {Error} When `baseUrl` does not end with a forward slash.
174
225
  */
175
226
  constructor(baseUrl?: BaseUrl, options?: Options);
176
227
  /**
177
228
  * Makes a request to a FaasJS function.
229
+ *
230
+ * Builds the request URL and resolved options, runs `beforeRequest` hooks,
231
+ * checks for mock handlers, and dispatches via native `fetch` or custom `request`.
232
+ * When `stream` is enabled the raw fetch response is returned so callers can
233
+ * consume the body stream themselves.
234
+ *
235
+ * @template Path - Action path used to infer the request params and response data types.
236
+ * @param {Path} action - Action path to invoke. Must be non-empty.
237
+ * @param {FaasParams<Path>} [params] - Params sent to the action. Defaults to an empty object.
238
+ * @param {Options} [options] - Per-request overrides on top of client defaults.
239
+ * @returns {Promise<Response<FaasData<Path>>>} FaasJS response containing the parsed data, or native fetch response when streaming.
240
+ * @throws {Error} When `action` is empty or falsy.
178
241
  */
179
242
  action<Path extends FaasActionPaths>(action: Path, params: FaasParams<Path>, options?: Options): Promise<Response<FaasData<Path>>>;
180
243
  }
@@ -346,7 +409,7 @@ type FaasDataWrapperRef<Path extends FaasActionPaths> = FaasDataInjection<Path>;
346
409
  *
347
410
  * When a ref is provided, it exposes the current Faas request state imperatively.
348
411
  */
349
- declare const FaasDataWrapper: <Path extends FaasActionPaths>(props: FaasDataWrapperProps<Path> & _$react.RefAttributes<FaasDataWrapperRef<Path>>) => React.ReactElement | null;
412
+ declare const FaasDataWrapper: <Path extends FaasActionPaths>(props: FaasDataWrapperProps<Path> & import("react").RefAttributes<FaasDataWrapperRef<Path>>) => React.ReactElement | null;
350
413
  /**
351
414
  * Wrap a component with {@link FaasDataWrapper} and inject Faas request state as props.
352
415
  *
@@ -656,7 +719,7 @@ declare class ErrorBoundary extends Component<ErrorBoundaryProps, {
656
719
  /**
657
720
  * Render children or the configured fallback for the captured error.
658
721
  */
659
- render(): string | number | bigint | boolean | _$react_jsx_runtime0.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | _$react.ReactPortal | ReactElement<unknown, string | _$react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null;
722
+ render(): string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null;
660
723
  }
661
724
  //#endregion
662
725
  //#region src/equal/index.d.ts
@@ -803,7 +866,7 @@ type OptionalWrapperProps<TWrapper extends ComponentType<{
803
866
  * )
804
867
  * ```
805
868
  */
806
- declare function OptionalWrapper(props: OptionalWrapperProps): string | number | bigint | boolean | _$react_jsx_runtime0.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | _$react.ReactPortal | _$react.ReactElement<unknown, string | _$react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
869
+ declare function OptionalWrapper(props: OptionalWrapperProps): string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
807
870
  declare namespace OptionalWrapper {
808
871
  var displayName: string;
809
872
  }
@@ -958,6 +1021,12 @@ declare function useSplittingState<T extends Record<string, unknown>>(initialSta
958
1021
  //#region src/useFaasStream/index.d.ts
959
1022
  /**
960
1023
  * Options that customize the {@link useFaasStream} request lifecycle.
1024
+ *
1025
+ * Extends the shared request options so stream consumers can control params,
1026
+ * skip logic, debounce timing, polling, and base URL overrides the same way
1027
+ * {@link useFaas} does.
1028
+ *
1029
+ * @see {@link SharedUseFaasOptions} for a full description of each field.
961
1030
  */
962
1031
  type UseFaasStreamOptions = SharedUseFaasOptions<Record<string, any>, string>;
963
1032
  /**
package/dist/index.mjs CHANGED
@@ -107,6 +107,34 @@ var ResponseError = class extends Error {
107
107
  let mock = null;
108
108
  /**
109
109
  * Set the global mock handler used by all {@link FaasBrowserClient} instances.
110
+ *
111
+ * When a mock handler is set, every {@link FaasBrowserClient.action} call will
112
+ * route through the mock instead of making a real network request, which is
113
+ * useful for testing and local development.
114
+ *
115
+ * @param {MockHandler | ResponseProps | Response | null | undefined} handler -
116
+ * A mock function that receives `(action, params, options)` and returns a
117
+ * response shape, or a static response/value, or `null`/`undefined` to
118
+ * disable mocking.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * import { setMock, Response } from '@faasjs/react'
123
+ *
124
+ * // Mock all requests with a static response
125
+ * setMock({ data: { name: 'test' } })
126
+ *
127
+ * // Mock with a handler function
128
+ * setMock(async (action, params) => {
129
+ * if (action === 'posts/get') {
130
+ * return { data: { title: 'Hello' } }
131
+ * }
132
+ * return new Error('Not found')
133
+ * })
134
+ *
135
+ * // Disable mocking
136
+ * setMock(null)
137
+ * ```
110
138
  */
111
139
  function setMock(handler) {
112
140
  mock = handler;
@@ -137,15 +165,50 @@ function normalizeMockResponse(response) {
137
165
  }
138
166
  return new Response(response || {});
139
167
  }
168
+ /**
169
+ * Resolve a mock response for the current request.
170
+ *
171
+ * If the global mock is a function it is invoked with the action, params, and
172
+ * resolved options. Otherwise the static mock value is normalized into a
173
+ * {@link Response} object.
174
+ *
175
+ * @template Path - Action path used for response data inference.
176
+ * @param {string} action - Action path being requested.
177
+ * @param {Record<string, any>} params - Params sent with the request.
178
+ * @param {ResolvedActionOptions} options - Fully resolved request options.
179
+ * @returns {Promise<Response<FaasData<Path>>>} Normalized mock response.
180
+ */
140
181
  async function resolveMockResponse(action, params, options) {
141
182
  if (typeof mock === "function") return normalizeMockResponse(await mock(action, params, options));
142
183
  return normalizeMockResponse(mock);
143
184
  }
144
185
  //#endregion
145
186
  //#region src/browser/helpers.ts
187
+ /**
188
+ * Build the full request URL for an action, with optional request-id query parameter.
189
+ *
190
+ * @param {string} action - Action path to append to the base URL.
191
+ * @param {BaseUrl} baseUrl - Default base URL used when the request options do not override it.
192
+ * @param {Options} [options] - Per-request options whose `baseUrl` field overrides `baseUrl`.
193
+ * @param {string} [requestId] - Unique request identifier appended as the `_` query parameter.
194
+ * @returns {string} Fully assembled request URL.
195
+ */
146
196
  function buildActionUrl(action, baseUrl, options, requestId) {
147
197
  return `${(options?.baseUrl || baseUrl) + action}?_=${requestId}`;
148
198
  }
199
+ /**
200
+ * Merge default and per-request options into a fully resolved request configuration.
201
+ *
202
+ * Sets method to `POST`, adds JSON content-type and CORS credentials, serializes params
203
+ * as JSON body, and injects the `X-FaasJS-Request-Id` header unless already present.
204
+ *
205
+ * @template Path - Action path used for params inference.
206
+ * @param {Options} defaultOptions - Client-wide default options merged into every request.
207
+ * @param {Options | undefined} options - Per-request overrides applied on top of defaults.
208
+ * @param {FaasParams<Path>} params - Params to serialize as the JSON request body.
209
+ * @param {string} requestId - Unique request identifier injected into headers.
210
+ * @returns {ResolvedActionOptions} Fully resolved request options ready for `fetch`.
211
+ */
149
212
  function buildActionOptions(defaultOptions, options, params, requestId) {
150
213
  const resolvedOptions = {
151
214
  method: "POST",
@@ -159,6 +222,15 @@ function buildActionOptions(defaultOptions, options, params, requestId) {
159
222
  if (!resolvedOptions.headers["X-FaasJS-Request-Id"] && !resolvedOptions.headers["x-faasjs-request-id"]) resolvedOptions.headers["X-FaasJS-Request-Id"] = requestId;
160
223
  return resolvedOptions;
161
224
  }
225
+ /**
226
+ * Invoke the `beforeRequest` hook if it is configured in the resolved options.
227
+ *
228
+ * @template Path - Action path used for params inference.
229
+ * @param {Path} action - Action path being requested.
230
+ * @param {FaasParams<Path>} params - Params being sent with the request.
231
+ * @param {ResolvedActionOptions} options - Resolved options that may carry a `beforeRequest` callback.
232
+ * @returns {Promise<void>} Resolves after the hook (if any) completes.
233
+ */
162
234
  async function runBeforeRequest(action, params, options) {
163
235
  if (!options.beforeRequest) return;
164
236
  await options.beforeRequest({
@@ -168,11 +240,30 @@ async function runBeforeRequest(action, params, options) {
168
240
  headers: options.headers
169
241
  });
170
242
  }
243
+ /**
244
+ * Convert fetch `Headers` iterable to a plain key-value object.
245
+ *
246
+ * @param {Iterable<[string, string]>} headers - Iterable of header name/value pairs.
247
+ * @returns {ResponseHeaders} Plain object keyed by lower-cased header names.
248
+ */
171
249
  function toResponseHeaders(headers) {
172
250
  const responseHeaders = {};
173
251
  for (const [key, value] of headers) responseHeaders[key] = value;
174
252
  return responseHeaders;
175
253
  }
254
+ /**
255
+ * Parse a successful (2xx) HTTP response body into a {@link Response} object.
256
+ *
257
+ * If the parsed body contains an `error.message` property the response is treated
258
+ * as a logical error and a {@link ResponseError} is thrown instead.
259
+ *
260
+ * @template Path - Action path used for response data inference.
261
+ * @param {number} status - HTTP status code.
262
+ * @param {ResponseHeaders} headers - Response headers.
263
+ * @param {string} text - Raw response body text.
264
+ * @returns {Response<FaasData<Path>>} Wrapped response containing the parsed data.
265
+ * @throws {ResponseError} When the body contains a logical `error.message`.
266
+ */
176
267
  function parseSuccessfulResponse(status, headers, text) {
177
268
  if (!text) return new Response({
178
269
  status,
@@ -192,6 +283,15 @@ function parseSuccessfulResponse(status, headers, text) {
192
283
  data: body.data
193
284
  });
194
285
  }
286
+ /**
287
+ * Parse a failed (non-2xx) HTTP response and always throw a {@link ResponseError}.
288
+ *
289
+ * @param {number} status - HTTP status code.
290
+ * @param {ResponseHeaders} headers - Response headers.
291
+ * @param {string} text - Raw response body text.
292
+ * @returns {never} Always throws – never returns normally.
293
+ * @throws {ResponseError} Wrapped error containing the HTTP status, headers, and body.
294
+ */
195
295
  function parseFailedResponse(status, headers, text) {
196
296
  try {
197
297
  const body = JSON.parse(text);
@@ -217,6 +317,17 @@ function parseFailedResponse(status, headers, text) {
217
317
  });
218
318
  }
219
319
  }
320
+ /**
321
+ * Read and parse a native fetch response into a FaasJS {@link Response} or throw on failure.
322
+ *
323
+ * Routes to {@link parseSuccessfulResponse} for 2xx status codes and
324
+ * {@link parseFailedResponse} for all others.
325
+ *
326
+ * @template Path - Action path used for response data inference.
327
+ * @param {ParsedFetchResponse} response - Parsed fetch response with headers, status, and text body.
328
+ * @returns {Promise<Response<FaasData<Path>>>} Wrapped FaasJS response on success.
329
+ * @throws {ResponseError} When the HTTP status indicates a failure.
330
+ */
220
331
  async function parseFetchResponse(response) {
221
332
  const headers = toResponseHeaders(response.headers);
222
333
  const text = await response.text();
@@ -227,6 +338,21 @@ async function parseFetchResponse(response) {
227
338
  //#region src/browser/client.ts
228
339
  /**
229
340
  * Browser client for FaasJS - provides HTTP client functionality for making API requests from web applications.
341
+ *
342
+ * Handles request URL construction, default and per-request option merging,
343
+ * before-request hooks, mock resolution for testing, and native fetch dispatching.
344
+ *
345
+ * @example
346
+ * ```ts
347
+ * import { FaasBrowserClient } from '@faasjs/react'
348
+ *
349
+ * const client = new FaasBrowserClient('https://api.example.com/', {
350
+ * headers: { 'X-Custom-Header': 'value' },
351
+ * })
352
+ *
353
+ * const response = await client.action('posts/get', { id: 1 })
354
+ * console.log(response.data)
355
+ * ```
230
356
  */
231
357
  var FaasBrowserClient = class {
232
358
  /**
@@ -243,6 +369,10 @@ var FaasBrowserClient = class {
243
369
  defaultOptions;
244
370
  /**
245
371
  * Creates a new FaasBrowserClient instance.
372
+ *
373
+ * @param {BaseUrl} [baseUrl='/'] - Base URL used to build action request URLs. Must end with `/`.
374
+ * @param {Options} [options={}] - Default request options merged into every request.
375
+ * @throws {Error} When `baseUrl` does not end with a forward slash.
246
376
  */
247
377
  constructor(baseUrl = "/", options = Object.create(null)) {
248
378
  if (baseUrl && !baseUrl.endsWith("/")) throw Error("[FaasJS] baseUrl should end with /");
@@ -252,6 +382,18 @@ var FaasBrowserClient = class {
252
382
  }
253
383
  /**
254
384
  * Makes a request to a FaasJS function.
385
+ *
386
+ * Builds the request URL and resolved options, runs `beforeRequest` hooks,
387
+ * checks for mock handlers, and dispatches via native `fetch` or custom `request`.
388
+ * When `stream` is enabled the raw fetch response is returned so callers can
389
+ * consume the body stream themselves.
390
+ *
391
+ * @template Path - Action path used to infer the request params and response data types.
392
+ * @param {Path} action - Action path to invoke. Must be non-empty.
393
+ * @param {FaasParams<Path>} [params] - Params sent to the action. Defaults to an empty object.
394
+ * @param {Options} [options] - Per-request overrides on top of client defaults.
395
+ * @returns {Promise<Response<FaasData<Path>>>} FaasJS response containing the parsed data, or native fetch response when streaming.
396
+ * @throws {Error} When `action` is empty or falsy.
255
397
  */
256
398
  async action(action, params, options) {
257
399
  if (!action) throw Error("[FaasJS] action required");
@@ -672,6 +814,7 @@ function useFaasRequest({ action, defaultParams, options, beforeSend, onSuccess,
672
814
  const handledRequestTriggerTimesRef = useRef(-1);
673
815
  const pollingTimerRef = useRef(null);
674
816
  const hasLoadedRef = useRef(false);
817
+ const isOnlineRef = useRef(typeof navigator !== "undefined" ? navigator.onLine : true);
675
818
  const beforeSendRef = useRef(beforeSend);
676
819
  const onSuccessRef = useRef(onSuccess);
677
820
  const sendRef = useRef(send);
@@ -709,14 +852,34 @@ function useFaasRequest({ action, defaultParams, options, beforeSend, onSuccess,
709
852
  const schedulePolling = () => {
710
853
  clearPollingTimer();
711
854
  if (!options.polling || options.polling <= 0 || !isCurrentRequest()) return;
855
+ if (!isOnlineRef.current) return;
712
856
  pollingTimerRef.current = setTimeout(() => {
713
857
  if (!isCurrentRequest()) return;
858
+ if (!isOnlineRef.current) return;
714
859
  setRequestTrigger((prev) => ({
715
860
  times: prev.times + 1,
716
861
  silent: true
717
862
  }));
718
863
  }, options.polling);
719
864
  };
865
+ const handleOnline = () => {
866
+ isOnlineRef.current = true;
867
+ if (options.polling && options.polling > 0 && !skip) setRequestTrigger((prev) => ({
868
+ times: prev.times + 1,
869
+ silent: hasLoadedRef.current
870
+ }));
871
+ };
872
+ const handleOffline = () => {
873
+ isOnlineRef.current = false;
874
+ if (pollingTimerRef.current) {
875
+ clearTimeout(pollingTimerRef.current);
876
+ pollingTimerRef.current = null;
877
+ }
878
+ };
879
+ if (typeof window !== "undefined") {
880
+ window.addEventListener("online", handleOnline);
881
+ window.addEventListener("offline", handleOffline);
882
+ }
720
883
  const rejectPending = (reason) => {
721
884
  for (const { reject } of pendingReloadsRef.current.values()) reject(reason);
722
885
  pendingReloadsRef.current.clear();
@@ -773,6 +936,10 @@ function useFaasRequest({ action, defaultParams, options, beforeSend, onSuccess,
773
936
  return () => {
774
937
  clearTimeout(timeout);
775
938
  clearPollingTimer();
939
+ if (typeof window !== "undefined") {
940
+ window.removeEventListener("online", handleOnline);
941
+ window.removeEventListener("offline", handleOffline);
942
+ }
776
943
  if (controllerRef.current === controller) controllerRef.current = null;
777
944
  controller.abort();
778
945
  setLoading(false);
@@ -782,6 +949,10 @@ function useFaasRequest({ action, defaultParams, options, beforeSend, onSuccess,
782
949
  run();
783
950
  return () => {
784
951
  clearPollingTimer();
952
+ if (typeof window !== "undefined") {
953
+ window.removeEventListener("online", handleOnline);
954
+ window.removeEventListener("offline", handleOffline);
955
+ }
785
956
  if (controllerRef.current === controller) controllerRef.current = null;
786
957
  controller.abort();
787
958
  setLoading(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/react",
3
- "version": "8.0.0-beta.34",
3
+ "version": "8.0.0-beta.35",
4
4
  "homepage": "https://faasjs.com/doc/react/",
5
5
  "bugs": {
6
6
  "url": "https://github.com/faasjs/faasjs/issues"
@@ -26,12 +26,12 @@
26
26
  }
27
27
  },
28
28
  "devDependencies": {
29
- "@faasjs/types": ">=8.0.0-beta.34",
29
+ "@faasjs/types": ">=8.0.0-beta.35",
30
30
  "@types/react": "^19.0.0",
31
31
  "react": "^19.0.0"
32
32
  },
33
33
  "peerDependencies": {
34
- "@faasjs/types": ">=8.0.0-beta.34"
34
+ "@faasjs/types": ">=8.0.0-beta.35"
35
35
  },
36
36
  "engines": {
37
37
  "node": ">=26.0.0",