UNPKG

@fingerprintjs/fingerprintjs-pro-react-native

Version:

Official React Native client for Fingerprint PRO. Best identification solution for React Native.

543 lines (534 loc) 16.6 kB
/** * FingerprintJS Pro React Native v3.3.1 - Copyright (c) FingerprintJS, Inc, 2025 (https://fingerprint.com) * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. */ import { NativeModules } from 'react-native'; import React, { createContext, useState, useCallback, useRef, useEffect, useMemo, useContext } from 'react'; /** * Something was wrong while building URL for identification request * * @group Errors */ class InvalidUrlError extends Error { constructor(message) { super(message); this.name = 'InvalidURL'; } } /** * Something was wrong with params while building URL for identification request * * @group Errors */ class InvalidURLParamsError extends Error { constructor(message) { super(message); this.name = 'InvalidURLParams'; } } /** * Unknown API error * * @group Errors */ class ApiError extends Error { constructor(message) { super(message); this.name = 'ApiError'; } } /** * Token is missing in the request * * @group Errors */ class ApiKeyRequiredError extends Error { constructor(message) { super(message); this.name = 'ApiKeyRequiredError'; } } /** * Wrong token * * @group Errors */ class ApiKeyNotFoundError extends Error { constructor(message) { super(message); this.name = 'ApiKeyNotFoundError'; } } /** * API token is outdated * * @group Errors */ class ApiKeyExpiredError extends Error { constructor(message) { super(message); this.name = 'ApiKeyExpiredError'; } } /** * Wrong request format (content, parameters) * * @group Errors */ class RequestCannotBeParsedError extends Error { constructor(message) { super(message); this.name = 'RequestCannotBeParsedError'; } } /** * Request failed * * @group Errors */ class FailedError extends Error { constructor(message) { super(message); this.name = 'FailedError'; } } /** * Server timeout * * @group Errors */ class RequestTimeoutError extends Error { constructor(message) { super(message); this.name = 'RequestTimeoutError'; } } /** * Request limit for token reached * * @group Errors */ class TooManyRequestError extends Error { constructor(message) { super(message); this.name = 'TooManyRequestError'; } } /** * Request Filtering deny origin * * @group Errors */ class OriginNotAvailableError extends Error { constructor(message) { super(message); this.name = 'OriginNotAvailableError'; } } /** * Request Filtering deny some headers * * @group Errors */ class HeaderRestrictedError extends Error { constructor(message) { super(message); this.name = 'HeaderRestrictedError'; } } /** * Request Filtering deny this hostname * * @group Errors */ class HostnameRestrictedError extends Error { constructor(message) { super(message); this.name = 'HostnameRestrictedError'; } } /** * Request Filtering deny crawlers * * @group Errors */ class NotAvailableForCrawlBotsError extends Error { constructor(message) { super(message); this.name = 'NotAvailableForCrawlBotsError'; } } /** * Request Filtering deny empty UA * * @group Errors */ class NotAvailableWithoutUAError extends Error { constructor(message) { super(message); this.name = 'NotAvailableWithoutUAError'; } } /** * API key does not match the selected region * * @group Errors */ class WrongRegionError extends Error { constructor(message) { super(message); this.name = 'WrongRegionError'; } } /** * Subscription is not active for the provided API key * * @group Errors */ class SubscriptionNotActiveError extends Error { constructor(message) { super(message); this.name = 'SubscriptionNotActiveError'; } } /** * Something wrong with used API version * * @group Errors */ class UnsupportedVersionError extends Error { constructor(message) { super(message); this.name = 'UnsupportedVersionError'; } } /** * * * @group Errors */ class InstallationMethodRestrictedError extends Error { constructor(message) { super(message); this.name = 'InstallationMethodRestrictedError'; } } /** * Error while parsing the response * * @group Errors */ class ResponseCannotBeParsedError extends Error { constructor(message) { super(message); this.name = 'ResponseCannotBeParsedError'; } } /** * Something wrong with network * * @group Errors */ class NetworkError extends Error { constructor(message) { super(message); this.name = 'NetworkError'; } } /** * Error while parsing JSON response * * @group Errors */ class JsonParsingError extends Error { constructor(message) { super(message); this.name = 'JsonParsingError'; } } /** * Bad response type * * @group Errors */ class InvalidResponseTypeError extends Error { constructor(message) { super(message); this.name = 'InvalidResponseType'; } } /** * Client-side timeout error * * @group Errors */ class ClientTimeoutError extends Error { constructor(message) { super(message); this.name = 'ClientTimeoutError'; } } /** * Other error * * @group Errors */ class UnknownError extends Error { constructor(message) { super(message); this.name = 'UnknownError'; } } function unwrapError(error) { const [errorType, ...errorMessageParts] = error.message.split(':'); const errorMessage = errorMessageParts.join(':'); switch (errorType) { case 'InvalidURL': return new InvalidUrlError(errorMessage); case 'InvalidURLParams': return new InvalidURLParamsError(errorMessage); case 'ApiError': return new ApiError(errorMessage); // Api Errors block case 'ApiKeyRequired': case 'TokenRequired': return new ApiKeyRequiredError(errorMessage); case 'ApiKeyNotFound': case 'TokenNotFound': return new ApiKeyNotFoundError(errorMessage); case 'ApiKeyExpired': case 'TokenExpired': return new ApiKeyExpiredError(errorMessage); case 'RequestCannotBeParsed': return new RequestCannotBeParsedError(errorMessage); case 'Failed': return new FailedError(errorMessage); case 'RequestTimeout': return new RequestTimeoutError(errorMessage); case 'TooManyRequest': return new TooManyRequestError(errorMessage); case 'OriginNotAvailable': return new OriginNotAvailableError(errorMessage); case 'HeaderRestricted': return new HeaderRestrictedError(errorMessage); case 'HostnameRestricted': return new HostnameRestrictedError(errorMessage); case 'NotAvailableForCrawlBots': return new NotAvailableForCrawlBotsError(errorMessage); case 'NotAvailableWithoutUA': return new NotAvailableWithoutUAError(errorMessage); case 'WrongRegion': return new WrongRegionError(errorMessage); case 'SubscriptionNotActive': return new SubscriptionNotActiveError(errorMessage); case 'UnsupportedVersion': return new UnsupportedVersionError(errorMessage); case 'InstallationMethodRestricted': return new InstallationMethodRestrictedError(errorMessage); case 'ResponseCannotBeParsed': return new ResponseCannotBeParsedError(errorMessage); // end of API Errors block case 'ClientTimeout': return new ClientTimeoutError(errorMessage); case 'NetworkError': return new NetworkError(errorMessage); case 'JsonParsingError': return new JsonParsingError(errorMessage); case 'InvalidResponseType': return new InvalidResponseTypeError(errorMessage); default: return new UnknownError(error.message); } } var version = "3.3.1"; /** * * @group API Client approach */ class FingerprintJsProAgent { constructor({ apiKey, region, endpointUrl, fallbackEndpointUrls = [], extendedResponseFormat = false, requestOptions = {}, }) { /** * Initialises FingerprintJS Pro Agent with certain settings * * @param params */ this.requestOptions = {}; try { NativeModules.RNFingerprintjsPro.configure(apiKey, region, endpointUrl, fallbackEndpointUrls, extendedResponseFormat, version); this.requestOptions = requestOptions; } catch (e) { console.error('RNFingerprintjsPro configure error: ', e); } } /** * Returns visitor identifier based on the request options {@link https://dev.fingerprint.com/docs/native-android-integration#get-the-visitor-identifier | more info in the documentation page} * * @param tags is a customer-provided value or an object that will be saved together with the analysis event and will be returned back to you in a webhook message or when you search for the visit in the server API. {@link https://dev.fingerprint.com/docs/js-agent#tag | more info in the documentation page} * @param linkedId is a way of linking current analysis event with a custom identifier. This will allow you to filter visit information when using the Server API {@link https://dev.fingerprint.com/docs/js-agent#linkedid | more info in the documentation page} * @param options is used to configure requests with different settings */ async getVisitorId(tags, linkedId, options) { var _a; try { const timeout = (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : this.requestOptions.timeout; if (timeout != null) { return await NativeModules.RNFingerprintjsPro.getVisitorIdWithTimeout(tags, linkedId, timeout); } return await NativeModules.RNFingerprintjsPro.getVisitorId(tags, linkedId); } catch (error) { if (error instanceof Error) { throw unwrapError(error); } else { throw new UnknownError(String(error)); } } } /** * Returns visitor identification data based on the request options {@link https://dev.fingerprint.com/docs/native-android-integration#get-the-visitor-identifier | more info in the documentation page} * * Provide `extendedResponseFormat` option in the {@link constructor} to get response in the {@link https://dev.fingerprint.com/docs/native-android-integration#response-format | extended format} * * @param tags is a customer-provided value or an object that will be saved together with the analysis event and will be returned back to you in a webhook message or when you search for the visit in the server API. {@link https://dev.fingerprint.com/docs/js-agent#tag | more info in the documentation page} * @param linkedId is a way of linking current analysis event with a custom identifier. This will allow you to filter visit information when using the Server API {@link https://dev.fingerprint.com/docs/js-agent#linkedid | more info in the documentation page} * @param options is used to configure requests with different settings */ async getVisitorData(tags, linkedId, options) { var _a; try { const timeout = (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : this.requestOptions.timeout; let visitorData; if (timeout != null) { visitorData = await NativeModules.RNFingerprintjsPro.getVisitorDataWithTimeout(tags, linkedId, timeout); } else { visitorData = await NativeModules.RNFingerprintjsPro.getVisitorData(tags, linkedId); } const [requestId, confidenceScore, visitorDataJsonString, sealedResult] = visitorData; const result = { ...JSON.parse(visitorDataJsonString), requestId, confidence: { score: confidenceScore, }, }; if (sealedResult) { result['sealedResult'] = sealedResult; } return result; } catch (error) { if (error instanceof Error) { throw unwrapError(error); } else { throw new UnknownError(String(error)); } } } } const stub = () => { throw new Error('You forgot to wrap your component in <FingerprintJsProProvider>.'); }; const initialContext = { visitorId: '', getVisitorData: stub, }; const FingerprintJsProContext = createContext(initialContext); /** * Provides the FingerprintJsProContext to its child components. * * @example * ```jsx * <FingerprintJsProProvider * apiKey: 'your-fpjs-public-api-key' * requestOptions: { timeout: 5000 } // Optional: Set a custom timeout in milliseconds * > * <MyApp /> * </FingerprintJsProProvider> * ``` * @group Hooks approach */ function FingerprintJsProProvider({ children, ...fingerprintJsProAgentParams }) { const [client, setClient] = useState(() => new FingerprintJsProAgent(fingerprintJsProAgentParams)); const [visitorId, updateVisitorId] = useState(''); const getVisitorData = useCallback(async (tags, linkedId, requestOptions) => { const result = await client.getVisitorData(tags, linkedId, requestOptions); updateVisitorId(result.visitorId); return result; }, [client]); const firstRender = useRef(true); useEffect(() => { if (firstRender) { firstRender.current = false; } else { setClient(new FingerprintJsProAgent(fingerprintJsProAgentParams)); } }, [fingerprintJsProAgentParams]); const contextValue = useMemo(() => { return { visitorId, getVisitorData, }; }, [visitorId, getVisitorData]); return React.createElement(FingerprintJsProContext.Provider, { value: contextValue }, children); } /** * Use the `useVisitorData` hook in your components to perform identification requests with the FingerprintJS API. * * @example * ```jsx * const { * // Request state * isLoading, * // Error info * error, * // Visitor info * data, * // A method to be called to initiate request * getData, * } = useVisitorData(); * ``` * * @group Hooks approach */ function useVisitorData() { const { getVisitorData } = useContext(FingerprintJsProContext); const [state, setState] = useState({}); const getData = useCallback(async (tags, linkedId, options) => { let result = null; try { setState((state) => ({ ...state, isLoading: true })); result = await getVisitorData(tags, linkedId, options); setState((state) => ({ ...state, data: result, isLoading: false, error: undefined, })); } catch (error) { setState((state) => ({ ...state, data: undefined, error: error, })); } finally { setState((state) => (state.isLoading ? { ...state, isLoading: false } : state)); } return result; }, [getVisitorData]); const { isLoading, data, error } = state; return { isLoading, data, error, getData, }; } export { ApiError, ApiKeyExpiredError, ApiKeyNotFoundError, ApiKeyRequiredError, ClientTimeoutError, FailedError, FingerprintJsProAgent, FingerprintJsProProvider, HeaderRestrictedError, InstallationMethodRestrictedError, InvalidResponseTypeError, InvalidURLParamsError, InvalidUrlError, JsonParsingError, NetworkError, NotAvailableForCrawlBotsError, NotAvailableWithoutUAError, OriginNotAvailableError, RequestCannotBeParsedError, RequestTimeoutError, ResponseCannotBeParsedError, SubscriptionNotActiveError, TooManyRequestError, UnknownError, UnsupportedVersionError, WrongRegionError, useVisitorData }; //# sourceMappingURL=fpjs-pro-react-native.esm.js.map