UNPKG

@idiosync/react-observable

Version:

State management control layer for React projects

175 lines (174 loc) 6.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.shallowEqualArrays = exports.isPlainObject = exports.isObject = exports.isFunction = exports.identity = exports.tryCatchSync = exports.tryCatch = void 0; exports.uuid = uuid; exports.isDevEnv = isDevEnv; exports.getModuleFunctionName = getModuleFunctionName; exports.getCallsiteName = getCallsiteName; const tryCatch = async (fn, errorMessage) => { try { const result = await fn(); return [result, undefined]; } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); if (errorMessage) { err.message = `${errorMessage}\n${err.message}`; } return [undefined, err]; } }; exports.tryCatch = tryCatch; const tryCatchSync = (fn, errorMessage) => { try { const result = fn(); return [result, undefined]; } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); if (errorMessage) { err.message = `${errorMessage}\n${err.message}`; } return [undefined, err]; } }; exports.tryCatchSync = tryCatchSync; const identity = (value) => value; exports.identity = identity; const isFunction = (value) => typeof value === 'function'; exports.isFunction = isFunction; const isObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value); exports.isObject = isObject; const isPlainObject = (value) => (0, exports.isObject)(value) && value.constructor === Object; exports.isPlainObject = isPlainObject; const shallowEqualArrays = (a, b) => { if (!Array.isArray(a) || !Array.isArray(b)) return false; if (a.length !== b.length) return false; return a.every((item, index) => item === b[index]); }; exports.shallowEqualArrays = shallowEqualArrays; let uuidCounter = 0; function uuid() { if (uuidCounter >= Number.MAX_SAFE_INTEGER) { uuidCounter = 0; } return `${Date.now()}-${++uuidCounter}`; } function isDevEnv() { const g = typeof globalThis !== 'undefined' ? globalThis : {}; return ((typeof g.__DEV__ !== 'undefined' && !!g.__DEV__) || (g.process && g.process.env && g.process.env.NODE_ENV !== 'production')); } // Specialized version for command streams. Filters out Metro module loading frames // and just returns the top-level function name where the stream was created. function getModuleFunctionName(options) { var _a, _b; const libHint = (_a = options === null || options === void 0 ? void 0 : options.libHint) !== null && _a !== void 0 ? _a : /(react-observable|node_modules\/react-observable|src\/utils\/|src\/factories\/|src\/store\/|src\/hooks\/|metroRequire|guardedLoadModule|loadModuleImplementation)/; const fallback = (_b = options === null || options === void 0 ? void 0 : options.fallback) !== null && _b !== void 0 ? _b : 'command-stream'; if (!isDevEnv()) return fallback; const error = new Error(); const raw = String(error.stack || ''); const lines = raw.split(/\r?\n/); if (!lines.length) return fallback; const metroNames = new Set([ 'metroRequire', 'guardedLoadModule', 'loadModuleImplementation', 'anonymous', ]); for (const line of lines.map((l) => l.trim())) { if (!line) continue; const v8 = line.match(/at\s+([^\s(]+)\s*\(([^)]+)\)/); if (v8) { const fn = v8[1]; if (!libHint.test(line) && !metroNames.has(fn) && fn !== 'getModuleFunctionName') { return fn; } } const fnAtLoc = line.match(/at\s+([^\s(]+)@(.+)/); if (fnAtLoc) { const fn = fnAtLoc[1]; if (!libHint.test(line) && !metroNames.has(fn) && fn !== 'getModuleFunctionName') { return fn; } } } return fallback; } // Dev-only helper that returns a human-friendly callsite label based on the // first user-land stack frame. It avoids library frames using libHint. // Returns fallback when not in dev or when a stack cannot be parsed. function getCallsiteName(options) { var _a, _b; const libHint = (_a = options === null || options === void 0 ? void 0 : options.libHint) !== null && _a !== void 0 ? _a : /(react-observable|node_modules\/react-observable|src\/utils\/|src\/factories\/|src\/store\/|src\/hooks\/)/; const fallback = (_b = options === null || options === void 0 ? void 0 : options.fallback) !== null && _b !== void 0 ? _b : 'observable'; // Only attempt stack parsing in dev to avoid runtime overhead and engine variance if (!isDevEnv()) return fallback; // Capture stack const error = new Error(); const raw = String(error.stack || ''); const lines = raw.split(/\r?\n/); if (!lines.length) return fallback; const reactInternalNames = new Set([ 'renderWithHooks', 'updateFunctionComponent', 'beginWork', 'performUnitOfWork', 'workLoopSync', 'performSyncWorkOnRoot', 'flushSyncCallbacks', ]); const parsed = lines .map((l) => l.trim()) .filter((l) => !!l) .map((l) => { const v8 = l.match(/at\s+([^\s(]+)\s+\(([^)]+)\)/); if (v8) return { raw: l, functionName: v8[1], location: v8[2] }; const fnAtLoc = l.match(/at\s+([^\s(]+)@(.+)/); if (fnAtLoc) return { raw: l, functionName: fnAtLoc[1], location: fnAtLoc[2] }; const plain = l.match(/at\s+(.+):(\d+):(\d+)/); if (plain) return { raw: l, location: `${plain[1]}:${plain[2]}:${plain[3]}` }; return { raw: l }; }) .filter((f) => !libHint.test(f.raw)); const getIdx = parsed.findIndex((f) => f.functionName === 'getCallsiteName'); const afterGet = getIdx >= 0 ? parsed.slice(getIdx + 1) : parsed; const names = []; for (const f of afterGet) { const name = f.functionName; if (!name) continue; if (reactInternalNames.has(name)) break; names.push(name); if (/^[A-Z][A-Za-z0-9_$]*$/.test(name)) break; } if (names.length > 0) { return names.reverse().join('->'); } const first = afterGet.find((f) => f.location || f.functionName); if (first) { if (first.functionName && first.location) return `${first.functionName} @ ${first.location}`; if (first.functionName) return first.functionName; if (first.location) return first.location; } return fallback; }