UNPKG

@fingerprintjs/fingerprintjs-pro-react

Version:
506 lines (479 loc) 20.8 kB
'use client'; /** * FingerprintJS Pro React v2.7.0 - Copyright (c) FingerprintJS, Inc, 2025 (https://fingerprint.com) * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. */ import { createContext, cloneElement, Component, useRef, useMemo, useCallback, useEffect, useContext, useState } from 'react'; import { jsx } from 'react/jsx-runtime'; import { FpjsClient } from '@fingerprintjs/fingerprintjs-pro-spa'; export { CacheLocation, CacheStub, FingerprintJSPro, FpjsClient, InMemoryCache, LocalStorageCache, SessionStorageCache, defaultEndpoint, defaultScriptUrlPattern, defaultTlsEndpoint } from '@fingerprintjs/fingerprintjs-pro-spa'; import deepEquals from 'fast-deep-equal'; var stub = function () { throw new Error('You forgot to wrap your component in <FpjsProvider>.'); }; var initialContext = { getVisitorData: stub, clearCache: stub, }; var FpjsContext = createContext(initialContext); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var name = "@fingerprintjs/fingerprintjs-pro-react"; var version = "2.7.0"; var isSSR = function () { return typeof window === 'undefined' || typeof window.document === 'undefined'; }; function waitUntil(_a) { var checkCondition = _a.checkCondition, _b = _a.intervalMs, intervalMs = _b === void 0 ? 250 : _b, _c = _a.timeoutMs, timeoutMs = _c === void 0 ? 2000 : _c; return new Promise(function (resolve, reject) { var timeoutId = setTimeout(function () { clearInterval(interval); reject(new Error('Timeout')); }, timeoutMs); var interval = setInterval(function () { if (checkCondition()) { clearTimeout(timeoutId); clearInterval(interval); resolve(); } }, intervalMs); }); } var env = '%DETECTED_ENV%'; var Env; (function (Env) { Env["React"] = "react"; Env["Preact"] = "preact"; Env["Next"] = "next"; Env["Unknown"] = "unknown"; })(Env || (Env = {})); function runEnvChecks() { var strategies = []; for (var _i = 0; _i < arguments.length; _i++) { strategies[_i] = arguments[_i]; } if (typeof window === 'undefined') { return false; } for (var _a = 0, strategies_1 = strategies; _a < strategies_1.length; _a++) { var strategy = strategies_1[_a]; var result = strategy(); if (result) { return true; } } return false; } /** * Runs checks that determine if user is using preact. * So far they are not ideal, as there is no consistent way to detect preact. * */ function isPreact(context) { return context.classRenderReceivesAnyArguments; } /** * Checks if user is using react. * */ function isReact(context) { return !context.classRenderReceivesAnyArguments; } /** * Runs checks that determine if user is using next. * Those checks should have almost 100% accuracy. * */ function isNext() { return runEnvChecks(function () { return 'next' in window && Boolean(window.next); }, function () { return document.querySelector('script[id=__NEXT_DATA__]'); }); } /** * Returns next version currently used by user. * */ function getNextVersion() { var _a; return (_a = window === null || window === void 0 ? void 0 : window.next) === null || _a === void 0 ? void 0 : _a.version; } /** * Attempts to determine user environment. * */ function detectEnvironment(_a) { var context = _a.context; if (isNext()) { return { name: Env.Next, version: getNextVersion(), }; } if (isPreact(context)) { return { name: Env.Preact, }; } if (isReact(context)) { return { name: Env.React, }; } return { name: Env.Unknown, }; } function getEnvironment(params) { try { var parsedEnv = JSON.parse(env); if (typeof parsedEnv === 'object') { return parsedEnv; } } catch (_a) { // Nothing here... } return detectEnvironment(params); } /** * Utility component that synchronously detects the current environment (React/Preact etc.) and * provides it as a prop to the child element. * * @example * ```jsx * const App = ({ env }: { env: EnvDetails }) => `I'm running in ${env.name}!` * * <WithEnvironment> * <App /> * </WithEnvironment> * ``` */ var WithEnvironment = /** @class */ (function (_super) { __extends(WithEnvironment, _super); function WithEnvironment(props) { return _super.call(this, props) || this; } WithEnvironment.prototype.render = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (!this.detectedEnv) { // unlike React, class components in Preact always receive `props` and `state` in render() // this is true for both Preact 8.x and 10.x var hasAnyArguments = args.length > 0; var detectParams = { context: { classRenderReceivesAnyArguments: hasAnyArguments }, }; this.detectedEnv = getEnvironment(detectParams); } // passes the `env` down as a prop return cloneElement(this.props.children, { env: this.detectedEnv }); }; return WithEnvironment; }(Component)); var pkgName = name.split('/')[1]; /** * @example * ```jsx * <FpjsProvider * loadOptions = {{ * apiKey: "<your-fpjs-public-api-key>" * }} * cacheTime={60 * 10} * cacheLocation={CacheLocation.LocalStorage} * > * <MyApp /> * </FpjsProvider> * ``` * * Provides the FpjsContext to its child components. * * @privateRemarks * This is just a wrapper around the actual provider. * For the implementation, see `ProviderWithEnv` component. */ function FpjsProvider(props) { var propsWithEnv = props; return (jsx(WithEnvironment, { children: jsx(ProviderWithEnv, __assign({}, propsWithEnv)) })); } function ProviderWithEnv(_a) { var _this = this; var children = _a.children, forceRebuild = _a.forceRebuild, cache = _a.cache, cacheTimeInSeconds = _a.cacheTimeInSeconds, cachePrefix = _a.cachePrefix, cacheLocation = _a.cacheLocation, customAgent = _a.customAgent, loadOptions = _a.loadOptions, env = _a.env; var clientRef = useRef(); var clientInitPromiseRef = useRef(); var clientOptions = useMemo(function () { return { cache: cache, cacheTimeInSeconds: cacheTimeInSeconds, cachePrefix: cachePrefix, cacheLocation: cacheLocation, customAgent: customAgent, loadOptions: loadOptions, }; }, [loadOptions, cache, cacheTimeInSeconds, cachePrefix, cacheLocation, customAgent]); var createClient = useCallback(function () { var integrationInfo = "".concat(pkgName, "/").concat(version); if (env) { var envInfo = env.version ? "".concat(env.name, "/").concat(env.version) : env.name; integrationInfo += "/".concat(envInfo); } var parsedClientOptions = __assign(__assign({}, clientOptions), { loadOptions: __assign(__assign({}, loadOptions), { integrationInfo: __spreadArray(__spreadArray([], (loadOptions.integrationInfo || []), true), [integrationInfo], false) }) }); var createdClient = new FpjsClient(parsedClientOptions); clientInitPromiseRef.current = createdClient.init(); return createdClient; }, [clientOptions, env, loadOptions]); var getClient = useCallback(function () { return __awaiter(_this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: if (isSSR()) { throw new Error('FpjsProvider client cannot be used in SSR'); } if (!!clientRef.current) return [3 /*break*/, 2]; return [4 /*yield*/, waitUntil({ checkCondition: function () { return Boolean(clientRef.current); }, }).catch(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { /** * We did timeout waiting for ideal condition to create client (eg: env detection) * Attempt to create client now, potentially without some additional information that might be useful but are not required. * */ createClient(); return [2 /*return*/]; }); }); })]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/, clientRef.current]; } }); }); }, [createClient]); var getVisitorData = useCallback(function (options, ignoreCache) { return __awaiter(_this, void 0, void 0, function () { var client; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, getClient()]; case 1: client = _a.sent(); return [4 /*yield*/, clientInitPromiseRef.current]; case 2: _a.sent(); return [2 /*return*/, client.getVisitorData(options, ignoreCache)]; } }); }); }, [getClient]); var clearCache = useCallback(function () { return __awaiter(_this, void 0, void 0, function () { var client; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, getClient()]; case 1: client = _a.sent(); return [4 /*yield*/, client.clearCache()]; case 2: _a.sent(); return [2 /*return*/]; } }); }); }, [getClient]); var contextValue = useMemo(function () { return { clearCache: clearCache, getVisitorData: getVisitorData, }; }, [clearCache, getVisitorData]); useEffect(function () { // By default, the client is always initialized once during the first render and won't be updated // if the configuration changes. Use `forceRebuilt` flag to disable this behaviour. if (!clientRef.current || forceRebuild) { clientRef.current = createClient(); } }, [forceRebuild, clientOptions, createClient]); return jsx(FpjsContext.Provider, { value: contextValue, children: children }); } function toError(error) { if (error instanceof Error) { return error; } return new Error(String(error)); } function assertIsTruthy(value, name) { if (!value) { throw new TypeError("".concat(name, " must not be null or undefined")); } } /** * @example * ```js * const { * // Request state * data, * isLoading, * error, * // A method to be called manually when the `immediate` field in the config is set to `false`: * getData, * } = useVisitorData({ extended: true }, { immediate: false }); * ``` * Use the `useVisitorData` hook in your components to perform identification requests with the FingerprintJS API. The returned object contains information about loading status, errors, and visitor. * * @param getOptions options for the `fp.get()` request * @param config config for the hook */ function useVisitorData(getOptions, config) { var _this = this; if (getOptions === void 0) { getOptions = {}; } if (config === void 0) { config = defaultUseVisitorDataConfig; } assertIsTruthy(getOptions, 'getOptions'); var immediate = config.immediate; var getVisitorData = useContext(FpjsContext).getVisitorData; var _a = useState(getOptions), currentGetOptions = _a[0], setCurrentGetOptions = _a[1]; var _b = useState({ isLoading: immediate, }), queryState = _b[0], setQueryState = _b[1]; var getData = useCallback(function () { var args_1 = []; for (var _i = 0; _i < arguments.length; _i++) { args_1[_i] = arguments[_i]; } return __awaiter(_this, __spreadArray([], args_1, true), void 0, function (params) { var ignoreCache, getDataPassedOptions, defaultIgnoreCache, getVisitorDataOptions, getDataOptions, result_1, unknownError_1, error_1; if (params === void 0) { params = {}; } return __generator(this, function (_a) { switch (_a.label) { case 0: assertIsTruthy(params, 'getDataParams'); ignoreCache = params.ignoreCache, getDataPassedOptions = __rest(params, ["ignoreCache"]); _a.label = 1; case 1: _a.trys.push([1, 3, 4, 5]); setQueryState(function (state) { return (__assign(__assign({}, state), { isLoading: true })); }); defaultIgnoreCache = currentGetOptions.ignoreCache, getVisitorDataOptions = __rest(currentGetOptions, ["ignoreCache"]); getDataOptions = __assign(__assign({}, getVisitorDataOptions), getDataPassedOptions); return [4 /*yield*/, getVisitorData(getDataOptions, typeof ignoreCache === 'boolean' ? ignoreCache : defaultIgnoreCache)]; case 2: result_1 = _a.sent(); setQueryState(function (state) { return (__assign(__assign({}, state), { data: result_1, isLoading: false, error: undefined })); }); return [2 /*return*/, result_1]; case 3: unknownError_1 = _a.sent(); error_1 = toError(unknownError_1); error_1.name = 'FPJSAgentError'; setQueryState(function (state) { return (__assign(__assign({}, state), { data: undefined, error: error_1 })); }); throw error_1; case 4: setQueryState(function (state) { return (state.isLoading ? __assign(__assign({}, state), { isLoading: false }) : state); }); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }, [currentGetOptions, getVisitorData]); useEffect(function () { if (immediate) { getData().catch(function (error) { console.error("Failed to fetch visitor data on mount: ".concat(error)); }); } }, [immediate, getData]); if (!Object.is(currentGetOptions, getOptions) && !deepEquals(currentGetOptions, getOptions)) { setCurrentGetOptions(getOptions); } var isLoading = queryState.isLoading, data = queryState.data, error = queryState.error; return { getData: getData, isLoading: isLoading, data: data, error: error, }; } var defaultUseVisitorDataConfig = { immediate: true }; export { FpjsContext, FpjsProvider, useVisitorData }; //# sourceMappingURL=fp-pro-react.esm.js.map