optic-react-native
Version:
React Native library for Optic
1,259 lines (1,239 loc) • 79.4 kB
JavaScript
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 __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __commonJS = (cb, mod) => function __require2() {
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
));
// node_modules/use-latest-callback/lib/src/useIsomorphicLayoutEffect.js
var require_useIsomorphicLayoutEffect = __commonJS({
"node_modules/use-latest-callback/lib/src/useIsomorphicLayoutEffect.js"(exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __require("react");
var useIsomorphicLayoutEffect2 = typeof document !== "undefined" ? react_1.useLayoutEffect : react_1.useEffect;
exports.default = useIsomorphicLayoutEffect2;
}
});
// node_modules/use-latest-callback/lib/src/index.js
var require_src = __commonJS({
"node_modules/use-latest-callback/lib/src/index.js"(exports, module) {
"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;
}
module.exports = useLatestCallback2;
}
});
// node_modules/strict-uri-encode/index.js
var require_strict_uri_encode = __commonJS({
"node_modules/strict-uri-encode/index.js"(exports, module) {
"use strict";
module.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"(exports, module) {
"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;
}
module.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"(exports, module) {
"use strict";
module.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"(exports, module) {
"use strict";
module.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"(exports) {
"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));
}
exports.extract = extract;
exports.parse = parse2;
exports.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("&");
};
exports.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) } : {}
);
};
exports.stringifyUrl = (object, options) => {
options = Object.assign({
encode: true,
strict: true,
[encodeFragmentIdentifier]: true
}, options);
const url = removeHash(object.url).split("?")[0] || "";
const queryFromUrl = exports.extract(object.url);
const parsedQueryFromUrl = exports.parse(queryFromUrl, { sort: false });
const query = Object.assign(parsedQueryFromUrl, object.query);
let queryString3 = exports.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}`;
};
exports.pick = (input, filter, options) => {
options = Object.assign({
parseFragmentIdentifier: true,
[encodeFragmentIdentifier]: false
}, options);
const { url, query, fragmentIdentifier } = exports.parseUrl(input, options);
return exports.stringifyUrl({
url,
query: filterObject(query, filter),
fragmentIdentifier
}, options);
};
exports.exclude = (input, filter, options) => {
const exclusionFilter = Array.isArray(filter) ? (key) => !filter.includes(key) : (key, value) => !filter(key, value);
return exports.pick(input, exclusionFilter, options);
};
}
});
// node_modules/escape-string-regexp/index.js
var require_escape_string_regexp = __commonJS({
"node_modules/escape-string-regexp/index.js"(exports, module) {
"use strict";
module.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"(exports) {
"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;
exports.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"(exports) {
"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;
exports.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"(exports, module) {
"use strict";
if (process.env.NODE_ENV === "production") {
module.exports = require_use_sync_external_store_with_selector_production();
} else {
module.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"(exports, module) {
"use strict";
module.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/metrics/globalRenderTracking.ts
import * as React from "react";
// src/store/metricsStore.ts
import { create } from "zustand";
var useMetricsStore = 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
import React2 from "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
import React73, { useEffect as useEffect24 } from "react";
// src/overlay/Overlay.tsx
import React3, { useRef, useState } from "react";
import { View, Text, StyleSheet, PanResponder, Animated, Dimensions, TouchableOpacity, Clipboard, Image, Platform, Linking, ScrollView } from "react-native";
import { SafeAreaView, useSafeAreaInsets } from "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 } = 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 = 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] = useState(false);
const [isNetworkExpanded, setIsNetworkExpanded] = useState(false);
const [isTracesExpanded, setIsTracesExpanded] = useState(false);
const [isCollapsed, setIsCollapsed] = useState(false);
const [expanded, setExpanded] = useState(false);
const [expandedTrace, setExpandedTrace] = useState(false);
const pan = useRef(new Animated.ValueXY()).current;
const [position, setPosition] = useState({
x: (SCREEN_WIDTH - 300) / 2,
y: insets.top + 20
});
const panResponder = useRef(
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 (Platform.OS === "ios" || Platform.OS === "android") {
Clipboard.setString(JSON.stringify(metrics, null, 2));
}
} catch (error) {
console.error("Error copying metrics:", error);
}
};
const handleOpenWebsite = () => {
Linking.openURL("https://useoptic.dev");
};
const renderCollapsedView = () => /* @__PURE__ */ React3.createElement(View, { style: styles.collapsedContainer }, /* @__PURE__ */ React3.createElement(View, { style: styles.collapsedMetrics }, /* @__PURE__ */ React3.createElement(Text, { style: styles.collapsedMetric }, "\u{1F680} ", startupTime !== null ? `${startupTime.toFixed(1)}ms` : "..."), /* @__PURE__ */ React3.createElement(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__ */ React3.createElement(SafeAreaView, { style: styles.safeArea, pointerEvents: "box-none" }, /* @__PURE__ */ React3.createElement(
Animated.View,
__spreadValues({
style: [
styles.overlay,
isCollapsed ? styles.collapsedOverlay : null,
{
left: position.x,
top: position.y
}
]
}, panResponder.panHandlers),
/* @__PURE__ */ React3.createElement(
TouchableOpacity,
{
style: styles.dragHandle,
onPress: () => setIsCollapsed(!isCollapsed)
}
),
isCollapsed ? renderCollapsedView() : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(View, { style: styles.header }, /* @__PURE__ */ React3.createElement(View, { style: styles.headerTop }, /* @__PURE__ */ React3.createElement(Text, { style: styles.text }, "Performance Metrics"), /* @__PURE__ */ React3.createElement(View, { style: styles.headerButtons }, /* @__PURE__ */ React3.createElement(
TouchableOpacity,
{
style: [styles.iconButton],
onPress: () => setIsMinimized(!isMinimized)
},
/* @__PURE__ */ React3.createElement(
Image,
{
source: { uri: isMinimized ? maximizeImageUrl : minimizeImageUrl },
style: styles.icon
}
)
))), /* @__PURE__ */ React3.createElement(View, { style: styles.screenNameContainer }, /* @__PURE__ */ React3.createElement(Text, { style: styles.screenName }, currentScreen || "No Screen"))), !isMinimized && /* @__PURE__ */ React3.createElement(ScrollView, { style: styles.content }, /* @__PURE__ */ React3.createElement(View, { style: styles.section }, /* @__PURE__ */ React3.createElement(Text, { style: styles.sectionTitle }, "Performance Metrics"), startupTime && /* @__PURE__ */ React3.createElement(Text, { style: [styles.metric, { color: getMetricColor("STARTUP", startupTime) }] }, "Startup: ", startupTime.toFixed(2), "ms"), (currentScreenMetrics == null ? void 0 : currentScreenMetrics.fps) && /* @__PURE__ */ React3.createElement(Text, { style: [styles.metric, { color: getMetricColor("FPS", currentScreenMetrics.fps) }] }, "FPS: ", currentScreenMetrics.fps.toFixed(1))), latestRequest && /* @__PURE__ */ React3.createElement(View, { style: styles.section }, /* @__PURE__ */ React3.createElement(
TouchableOpacity,
{
style: styles.sectionHeader,
onPress: () => setIsNetworkExpanded(!isNetworkExpanded)
},
/* @__PURE__ */ React3.createElement(Text, { style: styles.sectionTitle }, "Network Request"),
/* @__PURE__ */ React3.createElement(Text, { style: styles.expandIcon }, isNetworkExpanded ? "\u25BC" : "\u25B6")
), /* @__PURE__ */ React3.createElement(View, { style: styles.networkInfo }, /* @__PURE__ */ React3.createElement(Text, { style: [styles.metric, { color: getNetworkColor(latestRequest.duration) }] }, "\u2192 ", Math.round(latestRequest.duration).toFixed(1), "ms"), isNetworkExpanded && /* @__PURE__ */ React3.createElement(View, { style: styles.expandedNetworkInfo }, /* @__PURE__ */ React3.createElement(View, { style: styles.statusContainer }, /* @__PURE__ */ React3.createElement(Text, { style: [styles.statusCode, { color: getStatusColor(latestRequest.status) }] }, latestRequest.status, " ", latestRequest.status >= 500 ? "\u{1F534}" : latestRequest.status >= 400 ? "\u{1F7E0}" : "\u{1F7E2}")), /* @__PURE__ */ React3.createElement(View, { style: styles.urlContainer }, /* @__PURE__ */ React3.createElement(Text, { style: styles.networkUrl, numberOfLines: 1, ellipsizeMode: "middle" }, latestRequest.url))))), traces.length > 0 && /* @__PURE__ */ React3.createElement(View, { style: styles.section }, /* @__PURE__ */ React3.createElement(
TouchableOpacity,
{
style: styles.sectionHeader,
onPress: () => setIsTracesExpanded(!isTracesExpanded)
},
/* @__PURE__ */ React3.createElement(Text, { style: styles.sectionTitle }, "Recent Traces"),
/* @__PURE__ */ React3.createElement(Text, { style: styles.expandIcon }, isTracesExpanded ? "\u25BC" : "\u25B6")
), isTracesExpanded && traces.slice(-3).reverse().map((trace, idx) => /* @__PURE__ */ React3.createElement(View, { key: idx, style: styles.traceRow }, /* @__PURE__ */ React3.createElement(Text, { style: styles.traceScreen }, trace.interactionName, " \u2192 ", trace.componentName), /* @__PURE__ */ React3.createElement(Text, { style: [styles.traceDuration, { color: getMetricColor("TRACE", trace.duration) }] }, trace.duration.toFixed(1), "ms")))), /* @__PURE__ */ React3.createElement(TouchableOpacity, { style: styles.copyButton, onPress: handleCopyMetrics }, /* @__PURE__ */ React3.createElement(Text, { style: sty