@dev-fastn-ai/react-core
Version:
React hooks and components for integrating Fastn AI connector marketplace into your applications. Built on top of @fastn-ai/core with React Query for optimal performance.
929 lines (825 loc) • 29.3 kB
JavaScript
import { jsx } from 'react/jsx-runtime';
import { createContext, useMemo, useContext, useEffect, useState, useCallback } from 'react';
import { QueryClientProvider, QueryClient, useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
import { Fastn } from '@fastn-ai/core';
export * from '@fastn-ai/core';
const FastnContext = createContext(null);
const FastnProvider = ({ children, config, queryClient, }) => {
const fastn = useMemo(() => {
return new Fastn(config);
}, [config]);
return (jsx(QueryClientProvider, { client: queryClient || new QueryClient(), children: jsx(FastnContext.Provider, { value: fastn, children: children }) }));
};
const useFastn = () => {
const fastn = useContext(FastnContext);
if (!fastn) {
throw new Error("Initialize Fastn with FastnProvider first");
}
return fastn;
};
const useConfigurations = (input) => {
const fastn = useFastn();
const query = useQuery({
queryKey: ["configurations", input],
queryFn: () => fastn.getConfigurations(input),
staleTime: 1000 * 60 * 5, // 5 minutes
});
useEffect(() => {
const invalidate = () => {
query.refetch();
};
fastn.onEvent("REFETCH_CONFIGURATIONS", invalidate);
return () => {
fastn.offEvent("REFETCH_CONFIGURATIONS", invalidate);
};
}, [query.data]);
return query;
};
const useConfigurationForm = (input) => {
var _a;
const fastn = useFastn();
const configurations = useConfigurations({
configurationId: input.configurationId,
});
const query = useQuery({
queryKey: ["configuration-form", input],
queryFn: () => {
var _a;
return fastn.getConfigurationForm(Object.assign(Object.assign({}, input), { configuration: (_a = configurations === null || configurations === void 0 ? void 0 : configurations.data) === null || _a === void 0 ? void 0 : _a.find((c) => c.connectorId === input.connectorId) }));
},
enabled: !!((_a = configurations === null || configurations === void 0 ? void 0 : configurations.data) === null || _a === void 0 ? void 0 : _a.find((c) => c.connectorId === input.connectorId)),
});
useEffect(() => {
const invalidate = () => {
query.refetch();
};
fastn.onEvent("REFRESH_CONFIGURATION_FORM", invalidate);
return () => {
fastn.offEvent("REFRESH_CONFIGURATION_FORM", invalidate);
};
}, [query.data]);
return query;
};
const useConnectors = () => {
const fastn = useFastn();
const query = useQuery({
queryKey: ["connectors"],
queryFn: () => fastn.getConnectors(),
staleTime: 1000 * 60 * 5, // 5 minutes,
});
useEffect(() => {
const invalidate = () => {
query.refetch();
};
fastn.onEvent("REFETCH_CONNECTORS", invalidate);
return () => {
fastn.offEvent("REFETCH_CONNECTORS", invalidate);
};
}, [query.data]);
return query;
};
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* _.isObject({});
* // => true
*
* _.isObject([1, 2, 3]);
* // => true
*
* _.isObject(_.noop);
* // => true
*
* _.isObject(null);
* // => false
*/
var isObject_1;
var hasRequiredIsObject;
function requireIsObject () {
if (hasRequiredIsObject) return isObject_1;
hasRequiredIsObject = 1;
function isObject(value) {
var type = typeof value;
return value != null && (type == 'object' || type == 'function');
}
isObject_1 = isObject;
return isObject_1;
}
/** Detect free variable `global` from Node.js. */
var _freeGlobal;
var hasRequired_freeGlobal;
function require_freeGlobal () {
if (hasRequired_freeGlobal) return _freeGlobal;
hasRequired_freeGlobal = 1;
var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
_freeGlobal = freeGlobal;
return _freeGlobal;
}
var _root;
var hasRequired_root;
function require_root () {
if (hasRequired_root) return _root;
hasRequired_root = 1;
var freeGlobal = require_freeGlobal();
/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();
_root = root;
return _root;
}
var now_1;
var hasRequiredNow;
function requireNow () {
if (hasRequiredNow) return now_1;
hasRequiredNow = 1;
var root = require_root();
/**
* Gets the timestamp of the number of milliseconds that have elapsed since
* the Unix epoch (1 January 1970 00:00:00 UTC).
*
* @static
* @memberOf _
* @since 2.4.0
* @category Date
* @returns {number} Returns the timestamp.
* @example
*
* _.defer(function(stamp) {
* console.log(_.now() - stamp);
* }, _.now());
* // => Logs the number of milliseconds it took for the deferred invocation.
*/
var now = function() {
return root.Date.now();
};
now_1 = now;
return now_1;
}
/** Used to match a single whitespace character. */
var _trimmedEndIndex;
var hasRequired_trimmedEndIndex;
function require_trimmedEndIndex () {
if (hasRequired_trimmedEndIndex) return _trimmedEndIndex;
hasRequired_trimmedEndIndex = 1;
var reWhitespace = /\s/;
/**
* Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
* character of `string`.
*
* @private
* @param {string} string The string to inspect.
* @returns {number} Returns the index of the last non-whitespace character.
*/
function trimmedEndIndex(string) {
var index = string.length;
while (index-- && reWhitespace.test(string.charAt(index))) {}
return index;
}
_trimmedEndIndex = trimmedEndIndex;
return _trimmedEndIndex;
}
var _baseTrim;
var hasRequired_baseTrim;
function require_baseTrim () {
if (hasRequired_baseTrim) return _baseTrim;
hasRequired_baseTrim = 1;
var trimmedEndIndex = require_trimmedEndIndex();
/** Used to match leading whitespace. */
var reTrimStart = /^\s+/;
/**
* The base implementation of `_.trim`.
*
* @private
* @param {string} string The string to trim.
* @returns {string} Returns the trimmed string.
*/
function baseTrim(string) {
return string
? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '')
: string;
}
_baseTrim = baseTrim;
return _baseTrim;
}
var _Symbol;
var hasRequired_Symbol;
function require_Symbol () {
if (hasRequired_Symbol) return _Symbol;
hasRequired_Symbol = 1;
var root = require_root();
/** Built-in value references. */
var Symbol = root.Symbol;
_Symbol = Symbol;
return _Symbol;
}
var _getRawTag;
var hasRequired_getRawTag;
function require_getRawTag () {
if (hasRequired_getRawTag) return _getRawTag;
hasRequired_getRawTag = 1;
var Symbol = require_Symbol();
/** Used for built-in method references. */
var objectProto = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
/**
* Used to resolve the
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
* of values.
*/
var nativeObjectToString = objectProto.toString;
/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
/**
* A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the raw `toStringTag`.
*/
function getRawTag(value) {
var isOwn = hasOwnProperty.call(value, symToStringTag),
tag = value[symToStringTag];
try {
value[symToStringTag] = undefined;
var unmasked = true;
} catch (e) {}
var result = nativeObjectToString.call(value);
if (unmasked) {
if (isOwn) {
value[symToStringTag] = tag;
} else {
delete value[symToStringTag];
}
}
return result;
}
_getRawTag = getRawTag;
return _getRawTag;
}
/** Used for built-in method references. */
var _objectToString;
var hasRequired_objectToString;
function require_objectToString () {
if (hasRequired_objectToString) return _objectToString;
hasRequired_objectToString = 1;
var objectProto = Object.prototype;
/**
* Used to resolve the
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
* of values.
*/
var nativeObjectToString = objectProto.toString;
/**
* Converts `value` to a string using `Object.prototype.toString`.
*
* @private
* @param {*} value The value to convert.
* @returns {string} Returns the converted string.
*/
function objectToString(value) {
return nativeObjectToString.call(value);
}
_objectToString = objectToString;
return _objectToString;
}
var _baseGetTag;
var hasRequired_baseGetTag;
function require_baseGetTag () {
if (hasRequired_baseGetTag) return _baseGetTag;
hasRequired_baseGetTag = 1;
var Symbol = require_Symbol(),
getRawTag = require_getRawTag(),
objectToString = require_objectToString();
/** `Object#toString` result references. */
var nullTag = '[object Null]',
undefinedTag = '[object Undefined]';
/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
/**
* The base implementation of `getTag` without fallbacks for buggy environments.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the `toStringTag`.
*/
function baseGetTag(value) {
if (value == null) {
return value === undefined ? undefinedTag : nullTag;
}
return (symToStringTag && symToStringTag in Object(value))
? getRawTag(value)
: objectToString(value);
}
_baseGetTag = baseGetTag;
return _baseGetTag;
}
/**
* Checks if `value` is object-like. A value is object-like if it's not `null`
* and has a `typeof` result of "object".
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
* @example
*
* _.isObjectLike({});
* // => true
*
* _.isObjectLike([1, 2, 3]);
* // => true
*
* _.isObjectLike(_.noop);
* // => false
*
* _.isObjectLike(null);
* // => false
*/
var isObjectLike_1;
var hasRequiredIsObjectLike;
function requireIsObjectLike () {
if (hasRequiredIsObjectLike) return isObjectLike_1;
hasRequiredIsObjectLike = 1;
function isObjectLike(value) {
return value != null && typeof value == 'object';
}
isObjectLike_1 = isObjectLike;
return isObjectLike_1;
}
var isSymbol_1;
var hasRequiredIsSymbol;
function requireIsSymbol () {
if (hasRequiredIsSymbol) return isSymbol_1;
hasRequiredIsSymbol = 1;
var baseGetTag = require_baseGetTag(),
isObjectLike = requireIsObjectLike();
/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';
/**
* Checks if `value` is classified as a `Symbol` primitive or object.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
* @example
*
* _.isSymbol(Symbol.iterator);
* // => true
*
* _.isSymbol('abc');
* // => false
*/
function isSymbol(value) {
return typeof value == 'symbol' ||
(isObjectLike(value) && baseGetTag(value) == symbolTag);
}
isSymbol_1 = isSymbol;
return isSymbol_1;
}
var toNumber_1;
var hasRequiredToNumber;
function requireToNumber () {
if (hasRequiredToNumber) return toNumber_1;
hasRequiredToNumber = 1;
var baseTrim = require_baseTrim(),
isObject = requireIsObject(),
isSymbol = requireIsSymbol();
/** Used as references for various `Number` constants. */
var NAN = 0 / 0;
/** Used to detect bad signed hexadecimal string values. */
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
/** Used to detect binary string values. */
var reIsBinary = /^0b[01]+$/i;
/** Used to detect octal string values. */
var reIsOctal = /^0o[0-7]+$/i;
/** Built-in method references without a dependency on `root`. */
var freeParseInt = parseInt;
/**
* Converts `value` to a number.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to process.
* @returns {number} Returns the number.
* @example
*
* _.toNumber(3.2);
* // => 3.2
*
* _.toNumber(Number.MIN_VALUE);
* // => 5e-324
*
* _.toNumber(Infinity);
* // => Infinity
*
* _.toNumber('3.2');
* // => 3.2
*/
function toNumber(value) {
if (typeof value == 'number') {
return value;
}
if (isSymbol(value)) {
return NAN;
}
if (isObject(value)) {
var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
value = isObject(other) ? (other + '') : other;
}
if (typeof value != 'string') {
return value === 0 ? value : +value;
}
value = baseTrim(value);
var isBinary = reIsBinary.test(value);
return (isBinary || reIsOctal.test(value))
? freeParseInt(value.slice(2), isBinary ? 2 : 8)
: (reIsBadHex.test(value) ? NAN : +value);
}
toNumber_1 = toNumber;
return toNumber_1;
}
var debounce_1;
var hasRequiredDebounce;
function requireDebounce () {
if (hasRequiredDebounce) return debounce_1;
hasRequiredDebounce = 1;
var isObject = requireIsObject(),
now = requireNow(),
toNumber = requireToNumber();
/** Error message constants. */
var FUNC_ERROR_TEXT = 'Expected a function';
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max,
nativeMin = Math.min;
/**
* Creates a debounced function that delays invoking `func` until after `wait`
* milliseconds have elapsed since the last time the debounced function was
* invoked. The debounced function comes with a `cancel` method to cancel
* delayed `func` invocations and a `flush` method to immediately invoke them.
* Provide `options` to indicate whether `func` should be invoked on the
* leading and/or trailing edge of the `wait` timeout. The `func` is invoked
* with the last arguments provided to the debounced function. Subsequent
* calls to the debounced function return the result of the last `func`
* invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is
* invoked on the trailing edge of the timeout only if the debounced function
* is invoked more than once during the `wait` timeout.
*
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until to the next tick, similar to `setTimeout` with a timeout of `0`.
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `_.debounce` and `_.throttle`.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Function
* @param {Function} func The function to debounce.
* @param {number} [wait=0] The number of milliseconds to delay.
* @param {Object} [options={}] The options object.
* @param {boolean} [options.leading=false]
* Specify invoking on the leading edge of the timeout.
* @param {number} [options.maxWait]
* The maximum time `func` is allowed to be delayed before it's invoked.
* @param {boolean} [options.trailing=true]
* Specify invoking on the trailing edge of the timeout.
* @returns {Function} Returns the new debounced function.
* @example
*
* // Avoid costly calculations while the window size is in flux.
* jQuery(window).on('resize', _.debounce(calculateLayout, 150));
*
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
* jQuery(element).on('click', _.debounce(sendMail, 300, {
* 'leading': true,
* 'trailing': false
* }));
*
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
* var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
* var source = new EventSource('/stream');
* jQuery(source).on('message', debounced);
*
* // Cancel the trailing debounced invocation.
* jQuery(window).on('popstate', debounced.cancel);
*/
function debounce(func, wait, options) {
var lastArgs,
lastThis,
maxWait,
result,
timerId,
lastCallTime,
lastInvokeTime = 0,
leading = false,
maxing = false,
trailing = true;
if (typeof func != 'function') {
throw new TypeError(FUNC_ERROR_TEXT);
}
wait = toNumber(wait) || 0;
if (isObject(options)) {
leading = !!options.leading;
maxing = 'maxWait' in options;
maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
trailing = 'trailing' in options ? !!options.trailing : trailing;
}
function invokeFunc(time) {
var args = lastArgs,
thisArg = lastThis;
lastArgs = lastThis = undefined;
lastInvokeTime = time;
result = func.apply(thisArg, args);
return result;
}
function leadingEdge(time) {
// Reset any `maxWait` timer.
lastInvokeTime = time;
// Start the timer for the trailing edge.
timerId = setTimeout(timerExpired, wait);
// Invoke the leading edge.
return leading ? invokeFunc(time) : result;
}
function remainingWait(time) {
var timeSinceLastCall = time - lastCallTime,
timeSinceLastInvoke = time - lastInvokeTime,
timeWaiting = wait - timeSinceLastCall;
return maxing
? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
: timeWaiting;
}
function shouldInvoke(time) {
var timeSinceLastCall = time - lastCallTime,
timeSinceLastInvoke = time - lastInvokeTime;
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
// it as the trailing edge, or we've hit the `maxWait` limit.
return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
}
function timerExpired() {
var time = now();
if (shouldInvoke(time)) {
return trailingEdge(time);
}
// Restart the timer.
timerId = setTimeout(timerExpired, remainingWait(time));
}
function trailingEdge(time) {
timerId = undefined;
// Only invoke if we have `lastArgs` which means `func` has been
// debounced at least once.
if (trailing && lastArgs) {
return invokeFunc(time);
}
lastArgs = lastThis = undefined;
return result;
}
function cancel() {
if (timerId !== undefined) {
clearTimeout(timerId);
}
lastInvokeTime = 0;
lastArgs = lastCallTime = lastThis = timerId = undefined;
}
function flush() {
return timerId === undefined ? result : trailingEdge(now());
}
function debounced() {
var time = now(),
isInvoking = shouldInvoke(time);
lastArgs = arguments;
lastThis = this;
lastCallTime = time;
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime);
}
if (maxing) {
// Handle invocations in a tight loop.
clearTimeout(timerId);
timerId = setTimeout(timerExpired, wait);
return invokeFunc(lastCallTime);
}
}
if (timerId === undefined) {
timerId = setTimeout(timerExpired, wait);
}
return result;
}
debounced.cancel = cancel;
debounced.flush = flush;
return debounced;
}
debounce_1 = debounce;
return debounce_1;
}
var debounceExports = requireDebounce();
var debounce = /*@__PURE__*/getDefaultExportFromCjs(debounceExports);
/**
* Custom hook to manage async select field options with search, pagination, and error handling using React Query.
*
* Benefits of React Query caching:
* - Automatic caching with configurable stale time (5 minutes)
* - Background refetching when data becomes stale
* - Deduplication of requests
* - Optimistic updates
* - Automatic retry on failure
* - Cache invalidation and garbage collection
*
* @param field ConnectorField - The field definition containing optionsSource
* @param context FormData - Optional context data to pass to getOptions and loadMore methods
* @returns { options, loading, loadingMore, hasNext, query, refresh, search, loadMore, error }
*
* @example
* ```tsx
* const { options, loading, loadingMore, hasNext, search, loadMore } = useFieldOptions(field, context);
*
* // Options are automatically cached and shared across components
* // Loading states are handled automatically
* // Pagination is managed with infinite query
* ```
*/
function useFieldOptions(field, context) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
const [searchQuery, setSearchQuery] = useState("");
const queryClient = useQueryClient();
// Check if field is independent - if so, don't pass context
const isIndependent = (_b = (_a = field === null || field === void 0 ? void 0 : field.configs) === null || _a === void 0 ? void 0 : _a.selection) === null || _b === void 0 ? void 0 : _b.isIndependent;
const effectiveContext = isIndependent ? undefined : context;
// Generate a unique query key for this field
const fieldSourceId = ((_d = (_c = field === null || field === void 0 ? void 0 : field.optionsSource) === null || _c === void 0 ? void 0 : _c.pagination) === null || _d === void 0 ? void 0 : _d.sourceId) || "";
const queryKey = useMemo(() => {
return ["field-options", field.key, field.name, fieldSourceId];
}, [field.key, field.name, fieldSourceId]);
// Initial fetch query for static options or first page
const initialQuery = useQuery({
queryKey: [...queryKey, "initial", searchQuery, effectiveContext],
queryFn: async () => {
var _a, _b;
if (!((_a = field.optionsSource) === null || _a === void 0 ? void 0 : _a.getOptions))
return null;
const pagination = (_b = field.optionsSource
.pagination) !== null && _b !== void 0 ? _b : {
sourceId: "",
sourceProject: "",
limit: 10,
offset: 0,
type: "OFFSET",
hasNextPage: false,
};
return await field.optionsSource.getOptions(pagination, effectiveContext, searchQuery);
},
enabled: !!((_e = field.optionsSource) === null || _e === void 0 ? void 0 : _e.getOptions) && !((_f = field.optionsSource) === null || _f === void 0 ? void 0 : _f.loadMore), // Only run if loadMore is not available
staleTime: 1000 * 60 * 5, // 5 minutes
gcTime: 1000 * 60 * 10, // 10 minutes (formerly cacheTime)
});
// Create debounced search function for server-side search
const debouncedSetSearch = useMemo(() => debounce((query) => {
setSearchQuery(query);
}, 800), []);
// Infinite query for pagination
const infiniteQuery = useInfiniteQuery({
queryKey: [...queryKey, "infinite", searchQuery, effectiveContext],
queryFn: async ({ pageParam, }) => {
var _a, _b;
// For search queries, use getOptions if available, otherwise use loadMore
if (searchQuery && ((_a = field.optionsSource) === null || _a === void 0 ? void 0 : _a.getOptions)) {
return await field.optionsSource.getOptions(pageParam, effectiveContext, searchQuery);
}
if (!((_b = field.optionsSource) === null || _b === void 0 ? void 0 : _b.loadMore)) {
throw new Error("loadMore function not available");
}
return await field.optionsSource.loadMore(pageParam, effectiveContext);
},
initialPageParam: ((_h = (_g = field.optionsSource) === null || _g === void 0 ? void 0 : _g.pagination) !== null && _h !== void 0 ? _h : {
sourceId: "",
sourceProject: "",
limit: 10,
offset: 0,
type: "OFFSET",
hasNextPage: false,
}),
getNextPageParam: (lastPage) => {
return lastPage.pagination.hasNextPage ? lastPage.pagination : undefined;
},
enabled: !!(((_j = field.optionsSource) === null || _j === void 0 ? void 0 : _j.loadMore) || ((_k = field.optionsSource) === null || _k === void 0 ? void 0 : _k.getOptions)), // Run if either function is available
staleTime: 1000 * 60 * 5, // 5 minutes
gcTime: 1000 * 60 * 10, // 10 minutes
});
// Combine all options from initial query and infinite query with deduplication
const allOptions = useMemo(() => {
var _a, _b;
const optionsMap = new Map();
// Add initial options
if ((_a = initialQuery.data) === null || _a === void 0 ? void 0 : _a.options) {
initialQuery.data.options.forEach((option) => {
optionsMap.set(option.value, option);
});
}
// Add paginated options
if ((_b = infiniteQuery.data) === null || _b === void 0 ? void 0 : _b.pages) {
infiniteQuery.data.pages.forEach((page) => {
if (page.options) {
page.options.forEach((option) => {
optionsMap.set(option.value, option);
});
}
});
}
return Array.from(optionsMap.values());
}, [initialQuery.data, infiniteQuery.data]);
// Filter options by query (client-side filtering only when not searchable)
const filteredOptions = useMemo(() => {
var _a;
if (!((_a = initialQuery.data) === null || _a === void 0 ? void 0 : _a.searchable) && searchQuery) {
// Only do client-side filtering when not searchable
return allOptions.filter((option) => option.label.toLowerCase().includes(searchQuery.toLowerCase()));
}
// When searchable is true, use the server-filtered results directly
return allOptions;
}, [allOptions, searchQuery, (_l = initialQuery.data) === null || _l === void 0 ? void 0 : _l.searchable]);
// Search handler
const search = useCallback((query) => {
// Always use debounced search to prevent rapid state updates
debouncedSetSearch(query);
}, [debouncedSetSearch]);
// Load more handler
const loadMore = useCallback(async () => {
if (infiniteQuery.hasNextPage && !infiniteQuery.isFetchingNextPage) {
await infiniteQuery.fetchNextPage();
}
}, [infiniteQuery]);
// Refresh handler - resets to initial state and invalidates cache
const refresh = useCallback(async () => {
var _a, _b, _c;
try {
// Reset search query
setSearchQuery("");
// Invalidate and refetch initial query
if ((_a = field.optionsSource) === null || _a === void 0 ? void 0 : _a.getOptions) {
await queryClient.invalidateQueries({
queryKey: [...queryKey, "initial"],
});
await initialQuery.refetch();
}
// Invalidate and refetch infinite query
if (((_b = field.optionsSource) === null || _b === void 0 ? void 0 : _b.loadMore) || ((_c = field.optionsSource) === null || _c === void 0 ? void 0 : _c.getOptions)) {
await queryClient.invalidateQueries({
queryKey: [...queryKey, "infinite"],
});
await infiniteQuery.refetch();
}
}
catch (error) {
console.error("Error in refresh:", error);
}
}, [
queryClient,
queryKey,
initialQuery,
infiniteQuery,
field.optionsSource,
field,
effectiveContext,
]);
return {
options: filteredOptions,
loading: initialQuery.isLoading || infiniteQuery.isLoading,
loadingMore: infiniteQuery.isFetchingNextPage,
hasNext: infiniteQuery.hasNextPage,
query: searchQuery,
refresh,
search,
loadMore,
totalLoadedOptions: allOptions.length,
error: ((_m = initialQuery.error) === null || _m === void 0 ? void 0 : _m.message) || ((_o = infiniteQuery.error) === null || _o === void 0 ? void 0 : _o.message) || null,
};
}
export { FastnProvider, useConfigurationForm, useConfigurations, useConnectors, useFieldOptions };
//# sourceMappingURL=index.esm.js.map