UNPKG

optic-react-native

Version:

React Native library for Optic

1,259 lines (1,238 loc) 82.4 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // node_modules/use-latest-callback/lib/src/useIsomorphicLayoutEffect.js var require_useIsomorphicLayoutEffect = __commonJS({ "node_modules/use-latest-callback/lib/src/useIsomorphicLayoutEffect.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); var react_1 = require("react"); var useIsomorphicLayoutEffect2 = typeof document !== "undefined" ? react_1.useLayoutEffect : react_1.useEffect; exports2.default = useIsomorphicLayoutEffect2; } }); // node_modules/use-latest-callback/lib/src/index.js var require_src = __commonJS({ "node_modules/use-latest-callback/lib/src/index.js"(exports2, module2) { "use strict"; var React75 = require("react"); var useIsomorphicLayoutEffect_1 = require_useIsomorphicLayoutEffect(); function useLatestCallback2(callback) { var ref = React75.useRef(callback); var latestCallback = React75.useRef(function latestCallback2() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return ref.current.apply(this, args); }).current; (0, useIsomorphicLayoutEffect_1.default)(function() { ref.current = callback; }); return latestCallback; } module2.exports = useLatestCallback2; } }); // node_modules/strict-uri-encode/index.js var require_strict_uri_encode = __commonJS({ "node_modules/strict-uri-encode/index.js"(exports2, module2) { "use strict"; module2.exports = (str) => encodeURIComponent(str).replace(/[!'()*]/g, (x) => `%${x.charCodeAt(0).toString(16).toUpperCase()}`); } }); // node_modules/decode-uri-component/index.js var require_decode_uri_component = __commonJS({ "node_modules/decode-uri-component/index.js"(exports2, module2) { "use strict"; var token = "%[a-f0-9]{2}"; var singleMatcher = new RegExp("(" + token + ")|([^%]+?)", "gi"); var multiMatcher = new RegExp("(" + token + ")+", "gi"); function decodeComponents(components, split) { try { return [decodeURIComponent(components.join(""))]; } catch (err) { } if (components.length === 1) { return components; } split = split || 1; var left = components.slice(0, split); var right = components.slice(split); return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right)); } function decode(input) { try { return decodeURIComponent(input); } catch (err) { var tokens = input.match(singleMatcher) || []; for (var i = 1; i < tokens.length; i++) { input = decodeComponents(tokens, i).join(""); tokens = input.match(singleMatcher) || []; } return input; } } function customDecodeURIComponent(input) { var replaceMap = { "%FE%FF": "\uFFFD\uFFFD", "%FF%FE": "\uFFFD\uFFFD" }; var match = multiMatcher.exec(input); while (match) { try { replaceMap[match[0]] = decodeURIComponent(match[0]); } catch (err) { var result = decode(match[0]); if (result !== match[0]) { replaceMap[match[0]] = result; } } match = multiMatcher.exec(input); } replaceMap["%C2"] = "\uFFFD"; var entries = Object.keys(replaceMap); for (var i = 0; i < entries.length; i++) { var key = entries[i]; input = input.replace(new RegExp(key, "g"), replaceMap[key]); } return input; } module2.exports = function(encodedURI) { if (typeof encodedURI !== "string") { throw new TypeError("Expected `encodedURI` to be of type `string`, got `" + typeof encodedURI + "`"); } try { encodedURI = encodedURI.replace(/\+/g, " "); return decodeURIComponent(encodedURI); } catch (err) { return customDecodeURIComponent(encodedURI); } }; } }); // node_modules/split-on-first/index.js var require_split_on_first = __commonJS({ "node_modules/split-on-first/index.js"(exports2, module2) { "use strict"; module2.exports = (string, separator) => { if (!(typeof string === "string" && typeof separator === "string")) { throw new TypeError("Expected the arguments to be of type `string`"); } if (separator === "") { return [string]; } const separatorIndex = string.indexOf(separator); if (separatorIndex === -1) { return [string]; } return [ string.slice(0, separatorIndex), string.slice(separatorIndex + separator.length) ]; }; } }); // node_modules/filter-obj/index.js var require_filter_obj = __commonJS({ "node_modules/filter-obj/index.js"(exports2, module2) { "use strict"; module2.exports = function(obj, predicate) { var ret = {}; var keys = Object.keys(obj); var isArr = Array.isArray(predicate); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var val = obj[key]; if (isArr ? predicate.indexOf(key) !== -1 : predicate(key, val, obj)) { ret[key] = val; } } return ret; }; } }); // node_modules/query-string/index.js var require_query_string = __commonJS({ "node_modules/query-string/index.js"(exports2) { "use strict"; var strictUriEncode = require_strict_uri_encode(); var decodeComponent = require_decode_uri_component(); var splitOnFirst = require_split_on_first(); var filterObject = require_filter_obj(); var isNullOrUndefined = (value) => value === null || value === void 0; var encodeFragmentIdentifier = Symbol("encodeFragmentIdentifier"); function encoderForArrayFormat(options) { switch (options.arrayFormat) { case "index": return (key) => (result, value) => { const index = result.length; if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") { return result; } if (value === null) { return [...result, [encode(key, options), "[", index, "]"].join("")]; } return [ ...result, [encode(key, options), "[", encode(index, options), "]=", encode(value, options)].join("") ]; }; case "bracket": return (key) => (result, value) => { if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") { return result; } if (value === null) { return [...result, [encode(key, options), "[]"].join("")]; } return [...result, [encode(key, options), "[]=", encode(value, options)].join("")]; }; case "colon-list-separator": return (key) => (result, value) => { if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") { return result; } if (value === null) { return [...result, [encode(key, options), ":list="].join("")]; } return [...result, [encode(key, options), ":list=", encode(value, options)].join("")]; }; case "comma": case "separator": case "bracket-separator": { const keyValueSep = options.arrayFormat === "bracket-separator" ? "[]=" : "="; return (key) => (result, value) => { if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") { return result; } value = value === null ? "" : value; if (result.length === 0) { return [[encode(key, options), keyValueSep, encode(value, options)].join("")]; } return [[result, encode(value, options)].join(options.arrayFormatSeparator)]; }; } default: return (key) => (result, value) => { if (value === void 0 || options.skipNull && value === null || options.skipEmptyString && value === "") { return result; } if (value === null) { return [...result, encode(key, options)]; } return [...result, [encode(key, options), "=", encode(value, options)].join("")]; }; } } function parserForArrayFormat(options) { let result; switch (options.arrayFormat) { case "index": return (key, value, accumulator) => { result = /\[(\d*)\]$/.exec(key); key = key.replace(/\[\d*\]$/, ""); if (!result) { accumulator[key] = value; return; } if (accumulator[key] === void 0) { accumulator[key] = {}; } accumulator[key][result[1]] = value; }; case "bracket": return (key, value, accumulator) => { result = /(\[\])$/.exec(key); key = key.replace(/\[\]$/, ""); if (!result) { accumulator[key] = value; return; } if (accumulator[key] === void 0) { accumulator[key] = [value]; return; } accumulator[key] = [].concat(accumulator[key], value); }; case "colon-list-separator": return (key, value, accumulator) => { result = /(:list)$/.exec(key); key = key.replace(/:list$/, ""); if (!result) { accumulator[key] = value; return; } if (accumulator[key] === void 0) { accumulator[key] = [value]; return; } accumulator[key] = [].concat(accumulator[key], value); }; case "comma": case "separator": return (key, value, accumulator) => { const isArray = typeof value === "string" && value.includes(options.arrayFormatSeparator); const isEncodedArray = typeof value === "string" && !isArray && decode(value, options).includes(options.arrayFormatSeparator); value = isEncodedArray ? decode(value, options) : value; const newValue = isArray || isEncodedArray ? value.split(options.arrayFormatSeparator).map((item) => decode(item, options)) : value === null ? value : decode(value, options); accumulator[key] = newValue; }; case "bracket-separator": return (key, value, accumulator) => { const isArray = /(\[\])$/.test(key); key = key.replace(/\[\]$/, ""); if (!isArray) { accumulator[key] = value ? decode(value, options) : value; return; } const arrayValue = value === null ? [] : value.split(options.arrayFormatSeparator).map((item) => decode(item, options)); if (accumulator[key] === void 0) { accumulator[key] = arrayValue; return; } accumulator[key] = [].concat(accumulator[key], arrayValue); }; default: return (key, value, accumulator) => { if (accumulator[key] === void 0) { accumulator[key] = value; return; } accumulator[key] = [].concat(accumulator[key], value); }; } } function validateArrayFormatSeparator(value) { if (typeof value !== "string" || value.length !== 1) { throw new TypeError("arrayFormatSeparator must be single character string"); } } function encode(value, options) { if (options.encode) { return options.strict ? strictUriEncode(value) : encodeURIComponent(value); } return value; } function decode(value, options) { if (options.decode) { return decodeComponent(value); } return value; } function keysSorter(input) { if (Array.isArray(input)) { return input.sort(); } if (typeof input === "object") { return keysSorter(Object.keys(input)).sort((a, b) => Number(a) - Number(b)).map((key) => input[key]); } return input; } function removeHash(input) { const hashStart = input.indexOf("#"); if (hashStart !== -1) { input = input.slice(0, hashStart); } return input; } function getHash(url) { let hash = ""; const hashStart = url.indexOf("#"); if (hashStart !== -1) { hash = url.slice(hashStart); } return hash; } function extract(input) { input = removeHash(input); const queryStart = input.indexOf("?"); if (queryStart === -1) { return ""; } return input.slice(queryStart + 1); } function parseValue(value, options) { if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === "string" && value.trim() !== "")) { value = Number(value); } else if (options.parseBooleans && value !== null && (value.toLowerCase() === "true" || value.toLowerCase() === "false")) { value = value.toLowerCase() === "true"; } return value; } function parse2(query, options) { options = Object.assign({ decode: true, sort: true, arrayFormat: "none", arrayFormatSeparator: ",", parseNumbers: false, parseBooleans: false }, options); validateArrayFormatSeparator(options.arrayFormatSeparator); const formatter = parserForArrayFormat(options); const ret = /* @__PURE__ */ Object.create(null); if (typeof query !== "string") { return ret; } query = query.trim().replace(/^[?#&]/, ""); if (!query) { return ret; } for (const param of query.split("&")) { if (param === "") { continue; } let [key, value] = splitOnFirst(options.decode ? param.replace(/\+/g, " ") : param, "="); value = value === void 0 ? null : ["comma", "separator", "bracket-separator"].includes(options.arrayFormat) ? value : decode(value, options); formatter(decode(key, options), value, ret); } for (const key of Object.keys(ret)) { const value = ret[key]; if (typeof value === "object" && value !== null) { for (const k of Object.keys(value)) { value[k] = parseValue(value[k], options); } } else { ret[key] = parseValue(value, options); } } if (options.sort === false) { return ret; } return (options.sort === true ? Object.keys(ret).sort() : Object.keys(ret).sort(options.sort)).reduce((result, key) => { const value = ret[key]; if (Boolean(value) && typeof value === "object" && !Array.isArray(value)) { result[key] = keysSorter(value); } else { result[key] = value; } return result; }, /* @__PURE__ */ Object.create(null)); } exports2.extract = extract; exports2.parse = parse2; exports2.stringify = (object, options) => { if (!object) { return ""; } options = Object.assign({ encode: true, strict: true, arrayFormat: "none", arrayFormatSeparator: "," }, options); validateArrayFormatSeparator(options.arrayFormatSeparator); const shouldFilter = (key) => options.skipNull && isNullOrUndefined(object[key]) || options.skipEmptyString && object[key] === ""; const formatter = encoderForArrayFormat(options); const objectCopy = {}; for (const key of Object.keys(object)) { if (!shouldFilter(key)) { objectCopy[key] = object[key]; } } const keys = Object.keys(objectCopy); if (options.sort !== false) { keys.sort(options.sort); } return keys.map((key) => { const value = object[key]; if (value === void 0) { return ""; } if (value === null) { return encode(key, options); } if (Array.isArray(value)) { if (value.length === 0 && options.arrayFormat === "bracket-separator") { return encode(key, options) + "[]"; } return value.reduce(formatter(key), []).join("&"); } return encode(key, options) + "=" + encode(value, options); }).filter((x) => x.length > 0).join("&"); }; exports2.parseUrl = (url, options) => { options = Object.assign({ decode: true }, options); const [url_, hash] = splitOnFirst(url, "#"); return Object.assign( { url: url_.split("?")[0] || "", query: parse2(extract(url), options) }, options && options.parseFragmentIdentifier && hash ? { fragmentIdentifier: decode(hash, options) } : {} ); }; exports2.stringifyUrl = (object, options) => { options = Object.assign({ encode: true, strict: true, [encodeFragmentIdentifier]: true }, options); const url = removeHash(object.url).split("?")[0] || ""; const queryFromUrl = exports2.extract(object.url); const parsedQueryFromUrl = exports2.parse(queryFromUrl, { sort: false }); const query = Object.assign(parsedQueryFromUrl, object.query); let queryString3 = exports2.stringify(query, options); if (queryString3) { queryString3 = `?${queryString3}`; } let hash = getHash(object.url); if (object.fragmentIdentifier) { hash = `#${options[encodeFragmentIdentifier] ? encode(object.fragmentIdentifier, options) : object.fragmentIdentifier}`; } return `${url}${queryString3}${hash}`; }; exports2.pick = (input, filter, options) => { options = Object.assign({ parseFragmentIdentifier: true, [encodeFragmentIdentifier]: false }, options); const { url, query, fragmentIdentifier } = exports2.parseUrl(input, options); return exports2.stringifyUrl({ url, query: filterObject(query, filter), fragmentIdentifier }, options); }; exports2.exclude = (input, filter, options) => { const exclusionFilter = Array.isArray(filter) ? (key) => !filter.includes(key) : (key, value) => !filter(key, value); return exports2.pick(input, exclusionFilter, options); }; } }); // node_modules/escape-string-regexp/index.js var require_escape_string_regexp = __commonJS({ "node_modules/escape-string-regexp/index.js"(exports2, module2) { "use strict"; module2.exports = (string) => { if (typeof string !== "string") { throw new TypeError("Expected a string"); } return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d"); }; } }); // node_modules/use-sync-external-store/cjs/use-sync-external-store-with-selector.production.js var require_use_sync_external_store_with_selector_production = __commonJS({ "node_modules/use-sync-external-store/cjs/use-sync-external-store-with-selector.production.js"(exports2) { "use strict"; var React75 = require("react"); function is(x, y) { return x === y && (0 !== x || 1 / x === 1 / y) || x !== x && y !== y; } var objectIs = "function" === typeof Object.is ? Object.is : is; var useSyncExternalStore3 = React75.useSyncExternalStore; var useRef20 = React75.useRef; var useEffect26 = React75.useEffect; var useMemo13 = React75.useMemo; var useDebugValue2 = React75.useDebugValue; exports2.useSyncExternalStoreWithSelector = function(subscribe, getSnapshot, getServerSnapshot, selector, isEqual2) { var instRef = useRef20(null); if (null === instRef.current) { var inst = { hasValue: false, value: null }; instRef.current = inst; } else inst = instRef.current; instRef = useMemo13( function() { function memoizedSelector(nextSnapshot) { if (!hasMemo) { hasMemo = true; memoizedSnapshot = nextSnapshot; nextSnapshot = selector(nextSnapshot); if (void 0 !== isEqual2 && inst.hasValue) { var currentSelection = inst.value; if (isEqual2(currentSelection, nextSnapshot)) return memoizedSelection = currentSelection; } return memoizedSelection = nextSnapshot; } currentSelection = memoizedSelection; if (objectIs(memoizedSnapshot, nextSnapshot)) return currentSelection; var nextSelection = selector(nextSnapshot); if (void 0 !== isEqual2 && isEqual2(currentSelection, nextSelection)) return memoizedSnapshot = nextSnapshot, currentSelection; memoizedSnapshot = nextSnapshot; return memoizedSelection = nextSelection; } var hasMemo = false, memoizedSnapshot, memoizedSelection, maybeGetServerSnapshot = void 0 === getServerSnapshot ? null : getServerSnapshot; return [ function() { return memoizedSelector(getSnapshot()); }, null === maybeGetServerSnapshot ? void 0 : function() { return memoizedSelector(maybeGetServerSnapshot()); } ]; }, [getSnapshot, getServerSnapshot, selector, isEqual2] ); var value = useSyncExternalStore3(subscribe, instRef[0], instRef[1]); useEffect26( function() { inst.hasValue = true; inst.value = value; }, [value] ); useDebugValue2(value); return value; }; } }); // node_modules/use-sync-external-store/cjs/use-sync-external-store-with-selector.development.js var require_use_sync_external_store_with_selector_development = __commonJS({ "node_modules/use-sync-external-store/cjs/use-sync-external-store-with-selector.development.js"(exports2) { "use strict"; "production" !== process.env.NODE_ENV && function() { function is(x, y) { return x === y && (0 !== x || 1 / x === 1 / y) || x !== x && y !== y; } "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error()); var React75 = require("react"), objectIs = "function" === typeof Object.is ? Object.is : is, useSyncExternalStore3 = React75.useSyncExternalStore, useRef20 = React75.useRef, useEffect26 = React75.useEffect, useMemo13 = React75.useMemo, useDebugValue2 = React75.useDebugValue; exports2.useSyncExternalStoreWithSelector = function(subscribe, getSnapshot, getServerSnapshot, selector, isEqual2) { var instRef = useRef20(null); if (null === instRef.current) { var inst = { hasValue: false, value: null }; instRef.current = inst; } else inst = instRef.current; instRef = useMemo13( function() { function memoizedSelector(nextSnapshot) { if (!hasMemo) { hasMemo = true; memoizedSnapshot = nextSnapshot; nextSnapshot = selector(nextSnapshot); if (void 0 !== isEqual2 && inst.hasValue) { var currentSelection = inst.value; if (isEqual2(currentSelection, nextSnapshot)) return memoizedSelection = currentSelection; } return memoizedSelection = nextSnapshot; } currentSelection = memoizedSelection; if (objectIs(memoizedSnapshot, nextSnapshot)) return currentSelection; var nextSelection = selector(nextSnapshot); if (void 0 !== isEqual2 && isEqual2(currentSelection, nextSelection)) return memoizedSnapshot = nextSnapshot, currentSelection; memoizedSnapshot = nextSnapshot; return memoizedSelection = nextSelection; } var hasMemo = false, memoizedSnapshot, memoizedSelection, maybeGetServerSnapshot = void 0 === getServerSnapshot ? null : getServerSnapshot; return [ function() { return memoizedSelector(getSnapshot()); }, null === maybeGetServerSnapshot ? void 0 : function() { return memoizedSelector(maybeGetServerSnapshot()); } ]; }, [getSnapshot, getServerSnapshot, selector, isEqual2] ); var value = useSyncExternalStore3(subscribe, instRef[0], instRef[1]); useEffect26( function() { inst.hasValue = true; inst.value = value; }, [value] ); useDebugValue2(value); return value; }; "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error()); }(); } }); // node_modules/use-sync-external-store/with-selector.js var require_with_selector = __commonJS({ "node_modules/use-sync-external-store/with-selector.js"(exports2, module2) { "use strict"; if (process.env.NODE_ENV === "production") { module2.exports = require_use_sync_external_store_with_selector_production(); } else { module2.exports = require_use_sync_external_store_with_selector_development(); } } }); // node_modules/fast-deep-equal/index.js var require_fast_deep_equal = __commonJS({ "node_modules/fast-deep-equal/index.js"(exports2, module2) { "use strict"; module2.exports = function equal(a, b) { if (a === b) return true; if (a && b && typeof a == "object" && typeof b == "object") { if (a.constructor !== b.constructor) return false; var length, i, keys; if (Array.isArray(a)) { length = a.length; if (length != b.length) return false; for (i = length; i-- !== 0; ) if (!equal(a[i], b[i])) return false; return true; } if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); keys = Object.keys(a); length = keys.length; if (length !== Object.keys(b).length) return false; for (i = length; i-- !== 0; ) if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; for (i = length; i-- !== 0; ) { var key = keys[i]; if (!equal(a[key], b[key])) return false; } return true; } return a !== a && b !== b; }; } }); // src/index.ts var index_exports = {}; __export(index_exports, { OpticProvider: () => OpticProvider, endTrace: () => endTrace, initOptic: () => initOptic, startTrace: () => startTrace, useMetricsStore: () => useMetricsStore, useRenderMonitor: () => useRenderMonitor }); module.exports = __toCommonJS(index_exports); // src/metrics/globalRenderTracking.ts var React = __toESM(require("react")); // src/store/metricsStore.ts var import_zustand = require("zustand"); var useMetricsStore = (0, import_zustand.create)((set, get) => ({ currentScreen: null, screens: {}, networkRequests: [], traces: [], startupTime: null, setCurrentScreen: (screenName) => { set((state) => { if (screenName && !state.screens[screenName]) { return { currentScreen: screenName, screens: __spreadProps(__spreadValues({}, state.screens), { [screenName]: { reRenderCounts: {}, fps: null } }) }; } return { currentScreen: screenName }; }); }, incrementReRender: (componentName) => { const state = get(); if (!state.currentScreen) return; const currentScreen = state.screens[state.currentScreen]; const currentCount = currentScreen.reRenderCounts[componentName] || 0; set((state2) => ({ screens: __spreadProps(__spreadValues({}, state2.screens), { [state2.currentScreen]: __spreadProps(__spreadValues({}, currentScreen), { reRenderCounts: __spreadProps(__spreadValues({}, currentScreen.reRenderCounts), { [componentName]: currentCount + 1 }) }) }) })); }, setStartupTime: (time) => { set({ startupTime: time }); }, setFPS: (fps, screenName) => { set((state) => ({ screens: __spreadProps(__spreadValues({}, state.screens), { [screenName]: __spreadProps(__spreadValues({}, state.screens[screenName]), { fps }) }) })); }, addNetworkRequest: (request) => { set((state) => ({ networkRequests: [...state.networkRequests, request].slice(-50) // Keep last 50 requests })); }, setTrace: (trace) => { set((state) => ({ traces: [...state.traces, trace].slice(-10) // Keep last 10 traces })); } })); var opticEnabled = true; function setOpticEnabled(value) { opticEnabled = value; } // src/metrics/globalRenderTracking.ts var renderCounts = {}; var withRenderTracking = (WrappedComponent) => { const RenderTrackingWrapper = (props) => { const componentName = WrappedComponent.displayName || WrappedComponent.name || "Unknown"; const incrementReRender = useMetricsStore((state) => state.incrementReRender); React.useEffect(() => { if (global.__OPTIC_RENDER_TRACKING_ENABLED__) { const reRenderInfo = { componentName, timestamp: Date.now(), changedProps: props, renderCount: (renderCounts[componentName] || 0) + 1 }; incrementReRender(componentName, reRenderInfo); renderCounts[componentName] = (renderCounts[componentName] || 0) + 1; } }); return React.createElement(WrappedComponent, props); }; return RenderTrackingWrapper; }; function wrapWithRenderTracking(component) { if (!component) return component; if (component.__OPTIC_WRAPPED__) return component; const wrapped = withRenderTracking(component); wrapped.__OPTIC_WRAPPED__ = true; return wrapped; } function setupGlobalRenderTracking() { const rootComponent = global.__OPTIC_ROOT_COMPONENT__; if (!rootComponent) { return; } const wrappedRoot = wrapWithRenderTracking(rootComponent); global.__OPTIC_ROOT_COMPONENT__ = wrappedRoot; } function initRenderTracking() { global.__OPTIC_RENDER_TRACKING_ENABLED__ = true; if (global.__OPTIC_ROOT_COMPONENT__) { setupGlobalRenderTracking(); } } // src/metrics/network.ts var NETWORK_THRESHOLDS = { GOOD: 200, WARNING: 500, CRITICAL: 1e3 }; var originalFetch = null; var pendingRequests = /* @__PURE__ */ new Map(); var initNetworkTracking = () => { if (originalFetch !== null) return; try { originalFetch = global.fetch; global.fetch = async function(input, init) { const startTime = Date.now(); const url = input instanceof Request ? input.url : input.toString(); const method = input instanceof Request ? input.method : (init == null ? void 0 : init.method) || "GET"; pendingRequests.set(url, { startTime, url, method }); try { const response = await originalFetch(input, init); const responseTime = Date.now(); const responseDuration = responseTime - startTime; const clonedResponse = response.clone(); const newResponse = new Response(response.body, { status: response.status, statusText: response.statusText, headers: response.headers }); const originalJson = newResponse.json; const originalText = newResponse.text; newResponse.json = async function() { try { await clonedResponse.json(); const data = await originalJson.call(this); const endTime = Date.now(); const totalDuration = endTime - startTime; const metricsStore = useMetricsStore.getState(); const currentScreen = metricsStore.currentScreen; const networkRequest = { url, method, duration: totalDuration, responseDuration, status: response.status, screen: currentScreen, timestamp: endTime, startTime, endTime }; metricsStore.addNetworkRequest(networkRequest); pendingRequests.delete(url); return data; } catch (error) { const endTime = Date.now(); const totalDuration = endTime - startTime; const metricsStore = useMetricsStore.getState(); const currentScreen = metricsStore.currentScreen; const networkRequest = { url, method, duration: totalDuration, responseDuration, status: response.status, screen: currentScreen, timestamp: endTime, startTime, endTime, error: error instanceof Error ? error.message : "Unknown error" }; metricsStore.addNetworkRequest(networkRequest); pendingRequests.delete(url); throw error; } }; newResponse.text = async function() { try { const data = await originalText.call(this); const endTime = Date.now(); const totalDuration = endTime - startTime; const metricsStore = useMetricsStore.getState(); const currentScreen = metricsStore.currentScreen; const networkRequest = { url, method, duration: totalDuration, responseDuration, status: response.status, screen: currentScreen, timestamp: endTime, startTime, endTime }; metricsStore.addNetworkRequest(networkRequest); pendingRequests.delete(url); return data; } catch (error) { const endTime = Date.now(); const totalDuration = endTime - startTime; const metricsStore = useMetricsStore.getState(); const currentScreen = metricsStore.currentScreen; const networkRequest = { url, method, duration: totalDuration, responseDuration, status: response.status, screen: currentScreen, timestamp: endTime, startTime, endTime, error: error instanceof Error ? error.message : "Unknown error" }; metricsStore.addNetworkRequest(networkRequest); pendingRequests.delete(url); throw error; } }; return newResponse; } catch (error) { const endTime = Date.now(); const totalDuration = endTime - startTime; const metricsStore = useMetricsStore.getState(); const currentScreen = metricsStore.currentScreen; const networkRequest = { url, method, duration: totalDuration, status: 0, screen: currentScreen, timestamp: endTime, startTime, endTime, error: error instanceof Error ? error.message : "Unknown error" }; metricsStore.addNetworkRequest(networkRequest); pendingRequests.delete(url); throw error; } }; } catch (error) { if (originalFetch) { global.fetch = originalFetch; originalFetch = null; } } }; var getNetworkColor = (duration) => { if (duration === null || duration === void 0) return "#666666"; if (duration <= NETWORK_THRESHOLDS.GOOD) return "#4CAF50"; if (duration <= NETWORK_THRESHOLDS.WARNING) return "#FFC107"; return "#F44336"; }; var getLatestNetworkRequest = () => { const metricsStore = useMetricsStore.getState(); const currentScreen = metricsStore.currentScreen; const networkRequests = metricsStore.networkRequests; const screenNetworkRequests = networkRequests.filter((req) => req.screen === currentScreen); return screenNetworkRequests[screenNetworkRequests.length - 1]; }; // src/metrics/startup.ts if (global.__OPTIC_APP_START_TIME__ === void 0) { global.__OPTIC_APP_START_TIME__ = Date.now(); } if (global.__OPTIC_STARTUP_CAPTURED__ === void 0) { global.__OPTIC_STARTUP_CAPTURED__ = false; } function trackStartupTime() { if (global.__OPTIC_STARTUP_CAPTURED__) { return; } const start = global.__OPTIC_APP_START_TIME__ || Date.now(); requestAnimationFrame(() => { if (!global.__OPTIC_STARTUP_CAPTURED__) { const duration = Date.now() - start; global.__OPTIC_STARTUP_CAPTURED__ = true; useMetricsStore.getState().setStartupTime(duration); } }); } // src/core/initOptic.ts var import_react = __toESM(require("react")); function initOptic(options = {}) { const { enabled = true, onMetricsLogged, network = true, startup = true, reRenders = true, traces = true } = options; const config = { enabled, onMetricsLogged, network, startup, reRenders, traces }; setOpticEnabled(enabled); if (!enabled) { return; } if (reRenders) { initRenderTracking(); } if (network) { initNetworkTracking(); } if (startup) { trackStartupTime(); } useMetricsStore.getState(); if (onMetricsLogged) { const unsubscribe = useMetricsStore.subscribe((metrics) => { onMetricsLogged(metrics); }); return { config, unsubscribe }; } return config; } // src/providers/OpticProvider.tsx var import_react4 = __toESM(require("react")); // src/overlay/Overlay.tsx var import_react2 = __toESM(require("react")); var import_react_native = require("react-native"); var import_react_native_safe_area_context = require("react-native-safe-area-context"); var minimizeImageUrl = "https://img.icons8.com/material-rounded/24/ffffff/minus.png"; var maximizeImageUrl = "https://img.icons8.com/ios-filled/50/ffffff/full-screen.png"; var { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = import_react_native.Dimensions.get("window"); var METRICS_THRESHOLDS = { STARTUP: { good: 1e3, // 1 second warning: 2e3 // 2 seconds }, TRACE: { good: 50, // 50ms warning: 200 // 200ms }, FPS: { good: 55, // 55+ FPS is good warning: 30 // 30+ FPS is acceptable } }; var getMetricColor = (metric, value) => { const thresholds = METRICS_THRESHOLDS[metric]; if (metric === "FPS") { if (value >= thresholds.good) return "#4CAF50"; if (value >= thresholds.warning) return "#FFC107"; return "#F44336"; } if (value <= thresholds.good) return "#4CAF50"; if (value <= thresholds.warning) return "#FFC107"; return "#F44336"; }; var getStatusColor = (status) => { if (status >= 200 && status < 300) return "#4CAF50"; if (status >= 400) return "#F44336"; return "#FFC107"; }; var Overlay = () => { if (!opticEnabled) return null; const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)(); const currentScreen = useMetricsStore((state) => state.currentScreen); const screens = useMetricsStore((state) => state.screens); const startupTime = useMetricsStore((state) => state.startupTime); const networkRequests = useMetricsStore((state) => state.networkRequests); const traces = useMetricsStore((state) => state.traces); const [isMinimized, setIsMinimized] = (0, import_react2.useState)(false); const [isNetworkExpanded, setIsNetworkExpanded] = (0, import_react2.useState)(false); const [isTracesExpanded, setIsTracesExpanded] = (0, import_react2.useState)(false); const [isCollapsed, setIsCollapsed] = (0, import_react2.useState)(false); const [expanded, setExpanded] = (0, import_react2.useState)(false); const [expandedTrace, setExpandedTrace] = (0, import_react2.useState)(false); const pan = (0, import_react2.useRef)(new import_react_native.Animated.ValueXY()).current; const [position, setPosition] = (0, import_react2.useState)({ x: (SCREEN_WIDTH - 300) / 2, y: insets.top + 20 }); const panResponder = (0, import_react2.useRef)( import_react_native.PanResponder.create({ onStartShouldSetPanResponder: () => true, onPanResponderMove: (_, gesture) => { const newX = position.x + gesture.dx; const newY = position.y + gesture.dy; const boundedX = Math.max(10, Math.min(newX, SCREEN_WIDTH - 290)); const boundedY = Math.max(insets.top + 10, Math.min(newY, SCREEN_HEIGHT - 200)); setPosition({ x: boundedX, y: boundedY }); }, onPanResponderRelease: () => { pan.setValue({ x: 0, y: 0 }); } }) ).current; const currentScreenMetrics = currentScreen ? screens[currentScreen] : null; const latestRequest = getLatestNetworkRequest(); const latestTrace = traces[traces.length - 1]; const handleCopyMetrics = () => { try { const metrics = { currentScreen: currentScreen || "No Screen", startupTime: startupTime ? `${startupTime.toFixed(2)}ms` : "N/A", fps: (currentScreenMetrics == null ? void 0 : currentScreenMetrics.fps) ? `${currentScreenMetrics.fps.toFixed(1)} FPS` : "N/A", latestNetworkRequest: latestRequest ? { url: latestRequest.url, duration: `${latestRequest.duration.toFixed(2)}ms`, status: latestRequest.status } : "N/A", latestTrace: latestTrace ? { interactionName: latestTrace.interactionName, componentName: latestTrace.componentName, duration: `${latestTrace.duration.toFixed(2)}ms` } : "N/A" }; if (import_react_native.Platform.OS === "ios" || import_react_native.Platform.OS === "android") { import_react_native.Clipboard.setString(JSON.stringify(metrics, null, 2)); } } catch (error) { console.error("Error copying metrics:", error); } }; const handleOpenWebsite = () => { import_react_native.Linking.openURL("https://useoptic.dev"); }; const renderCollapsedView = () => /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.collapsedContainer }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.collapsedMetrics }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: styles.collapsedMetric }, "\u{1F680} ", startupTime !== null ? `${startupTime.toFixed(1)}ms` : "..."), /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: styles.collapsedMetric }, "\u{1F3AE} ", (currentScreenMetrics == null ? void 0 : currentScreenMetrics.fps) !== null && (currentScreenMetrics == null ? void 0 : currentScreenMetrics.fps) !== void 0 ? `${currentScreenMetrics.fps.toFixed(1)}` : "..."))); if (!currentScreen) return null; return /* @__PURE__ */ import_react2.default.createElement(import_react_native_safe_area_context.SafeAreaView, { style: styles.safeArea, pointerEvents: "box-none" }, /* @__PURE__ */ import_react2.default.createElement( import_react_native.Animated.View, __spreadValues({ style: [ styles.overlay, isCollapsed ? styles.collapsedOverlay : null, { left: position.x, top: position.y } ] }, panResponder.panHandlers), /* @__PURE__ */ import_react2.default.createElement( import_react_native.TouchableOpacity, { style: styles.dragHandle, onPress: () => setIsCollapsed(!isCollapsed) } ), isCollapsed ? renderCollapsedView() : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.header }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.headerTop }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: styles.text }, "Performance Metrics"), /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.headerButtons }, /* @__PURE__ */ import_react2.default.createElement( import_react_native.TouchableOpacity, { style: [styles.iconButton], onPress: () => setIsMinimized(!isMinimized) }, /* @__PURE__ */ import_react2.default.createElement( import_react_native.Image, { source: { uri: isMinimized ? maximizeImageUrl : minimizeImageUrl }, style: styles.icon } ) ))), /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.screenNameContainer }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: styles.screenName }, currentScreen || "No Screen"))), !isMinimized && /* @__PURE__ */ import_react2.default.createElement(import_react_native.ScrollView, { style: styles.content }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.section }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: styles.sectionTitle }, "Performance Metrics"), startupTime && /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: [styles.metric, { color: getMetricColor("STARTUP", startupTime) }] }, "Startup: ", startupTime.toFixed(2), "ms"), (currentScreenMetrics == null ? void 0 : currentScreenMetrics.fps) && /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: [styles.metric, { color: getMetricColor("FPS", currentScreenMetrics.fps) }] }, "FPS: ", currentScreenMetrics.fps.toFixed(1))), latestRequest && /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.section }, /* @__PURE__ */ import_react2.default.createElement( import_react_native.TouchableOpacity, { style: styles.sectionHeader, onPress: () => setIsNetworkExpanded(!isNetworkExpanded) }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: styles.sectionTitle }, "Network Request"), /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: styles.expandIcon }, isNetworkExpanded ? "\u25BC" : "\u25B6") ), /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.networkInfo }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: [styles.metric, { color: getNetworkColor(latestRequest.duration) }] }, "\u2192 ", Math.round(latestRequest.duration).toFixed(1), "ms"), isNetworkExpanded && /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.expandedNetworkInfo }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: styles.statusContainer }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.Text, { style: [styles.statusCode, { color: getStatusColor(latestRequest.status)