import {
ElementTypeClass,
ElementTypeFunction,
ElementTypeRoot,
ElementTypeHostComponent,
ElementTypeOtherOrUnknown,
} from 'react-devtools-shared/src/frontend/types';
import {getUID, utfEncodeString, printOperationsArray} from '../../utils';
import {
cleanForBridge,
copyWithDelete,
copyWithRename,
copyWithSet,
serializeToString,
} from '../utils';
import {
deletePathInObject,
getDisplayName,
getInObject,
renamePathInObject,
setInObject,
} from 'react-devtools-shared/src/utils';
import {
__DEBUG__,
TREE_OPERATION_ADD,
TREE_OPERATION_REMOVE,
TREE_OPERATION_REORDER_CHILDREN,
} from '../../constants';
import {decorateMany, forceUpdate, restoreMany} from './utils';
import type {
DevToolsHook,
GetElementIDForHostInstance,
InspectedElementPayload,
InstanceAndStyle,
HostInstance,
PathFrame,
PathMatch,
RendererInterface,
} from '../types';
import type {
ComponentFilter,
ElementType,
} from 'react-devtools-shared/src/frontend/types';
import type {InspectedElement, SerializedElement} from '../types';
export type InternalInstance = Object;
type LegacyRenderer = Object;
function getData(internalInstance: InternalInstance) {
let displayName = null;
let key = null;
if (internalInstance._currentElement != null) {
if (internalInstance._currentElement.key) {
key = String(internalInstance._currentElement.key);
}
const elementType = internalInstance._currentElement.type;
if (typeof elementType === 'string') {
displayName = elementType;
} else if (typeof elementType === 'function') {
displayName = getDisplayName(elementType);
}
}
return {
displayName,
key,
};
}
function getElementType(internalInstance: InternalInstance): ElementType {
if (internalInstance._currentElement != null) {
const elementType = internalInstance._currentElement.type;
if (typeof elementType === 'function') {
const publicInstance = internalInstance.getPublicInstance();
if (publicInstance !== null) {
return ElementTypeClass;
} else {
return ElementTypeFunction;
}
} else if (typeof elementType === 'string') {
return ElementTypeHostComponent;
}
}
return ElementTypeOtherOrUnknown;
}
function getChildren(internalInstance: Object): Array<any> {
const children = [];
if (typeof internalInstance !== 'object') {
} else if (
internalInstance._currentElement === null ||
internalInstance._currentElement === false
) {
} else if (internalInstance._renderedComponent) {
const child = internalInstance._renderedComponent;
if (getElementType(child) !== ElementTypeOtherOrUnknown) {
children.push(child);
}
} else if (internalInstance._renderedChildren) {
const renderedChildren = internalInstance._renderedChildren;
for (const name in renderedChildren) {
const child = renderedChildren[name];
if (getElementType(child) !== ElementTypeOtherOrUnknown) {
children.push(child);
}
}
}
return children;
}
export function attach(
hook: DevToolsHook,
rendererID: number,
renderer: LegacyRenderer,
global: Object,
): RendererInterface {
const idToInternalInstanceMap: Map<number, InternalInstance> = new Map();
const internalInstanceToIDMap: WeakMap<InternalInstance, number> =
new WeakMap();
const internalInstanceToRootIDMap: WeakMap<InternalInstance, number> =
new WeakMap();
let getElementIDForHostInstance: GetElementIDForHostInstance =
((null: any): GetElementIDForHostInstance);
let findHostInstanceForInternalID: (id: number) => ?HostInstance;
let getNearestMountedDOMNode = (node: Element): null | Element => {
return null;
};
if (renderer.ComponentTree) {
getElementIDForHostInstance = node => {
const internalInstance =
renderer.ComponentTree.getClosestInstanceFromNode(node);
return internalInstanceToIDMap.get(internalInstance) || null;
};
findHostInstanceForInternalID = (id: number) => {
const internalInstance = idToInternalInstanceMap.get(id);
return renderer.ComponentTree.getNodeFromInstance(internalInstance);
};
getNearestMountedDOMNode = (node: Element): null | Element => {
const internalInstance =
renderer.ComponentTree.getClosestInstanceFromNode(node);
if (internalInstance != null) {
return renderer.ComponentTree.getNodeFromInstance(internalInstance);
}
return null;
};
} else if (renderer.Mount.getID && renderer.Mount.getNode) {
getElementIDForHostInstance = node => {
return null;
};
findHostInstanceForInternalID = (id: number) => {
return null;
};
}
function getDisplayNameForElementID(id: number): string | null {
const internalInstance = idToInternalInstanceMap.get(id);
return internalInstance ? getData(internalInstance).displayName : null;
}
function getID(internalInstance: InternalInstance): number {
if (typeof internalInstance !== 'object' || internalInstance === null) {
throw new Error('Invalid internal instance: ' + internalInstance);
}
if (!internalInstanceToIDMap.has(internalInstance)) {
const id = getUID();
internalInstanceToIDMap.set(internalInstance, id);
idToInternalInstanceMap.set(id, internalInstance);
}
return ((internalInstanceToIDMap.get(internalInstance): any): number);
}
function areEqualArrays(a: Array<any>, b: Array<any>) {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
let parentIDStack = [];
let oldReconcilerMethods = null;
if (renderer.Reconciler) {
oldReconcilerMethods = decorateMany(renderer.Reconciler, {
mountComponent(fn, args) {
const internalInstance = args[0];
const hostContainerInfo = args[3];
if (getElementType(internalInstance) === ElementTypeOtherOrUnknown) {
return fn.apply(this, args);
}
if (hostContainerInfo._topLevelWrapper === undefined) {
return fn.apply(this, args);
}
const id = getID(internalInstance);
const parentID =
parentIDStack.length > 0
? parentIDStack[parentIDStack.length - 1]
: 0;
recordMount(internalInstance, id, parentID);
parentIDStack.push(id);
internalInstanceToRootIDMap.set(
internalInstance,
getID(hostContainerInfo._topLevelWrapper),
);
try {
const result = fn.apply(this, args);
parentIDStack.pop();
return result;
} catch (err) {
parentIDStack = [];
throw err;
} finally {
if (parentIDStack.length === 0) {
const rootID = internalInstanceToRootIDMap.get(internalInstance);
if (rootID === undefined) {
throw new Error('Expected to find root ID.');
}
flushPendingEvents(rootID);
}
}
},
performUpdateIfNecessary(fn, args) {
const internalInstance = args[0];
if (getElementType(internalInstance) === ElementTypeOtherOrUnknown) {
return fn.apply(this, args);
}
const id = getID(internalInstance);
parentIDStack.push(id);
const prevChildren = getChildren(internalInstance);
try {
const result = fn.apply(this, args);
const nextChildren = getChildren(internalInstance);
if (!areEqualArrays(prevChildren, nextChildren)) {
recordReorder(internalInstance, id, nextChildren);
}
parentIDStack.pop();
return result;
} catch (err) {
parentIDStack = [];
throw err;
} finally {
if (parentIDStack.length === 0) {
const rootID = internalInstanceToRootIDMap.get(internalInstance);
if (rootID === undefined) {
throw new Error('Expected to find root ID.');
}
flushPendingEvents(rootID);
}
}
},
receiveComponent(fn, args) {
const internalInstance = args[0];
if (getElementType(internalInstance) === ElementTypeOtherOrUnknown) {
return fn.apply(this, args);
}
const id = getID(internalInstance);
parentIDStack.push(id);
const prevChildren = getChildren(internalInstance);
try {
const result = fn.apply(this, args);
const nextChildren = getChildren(internalInstance);
if (!areEqualArrays(prevChildren, nextChildren)) {
recordReorder(internalInstance, id, nextChildren);
}
parentIDStack.pop();
return result;
} catch (err) {
parentIDStack = [];
throw err;
} finally {
if (parentIDStack.length === 0) {
const rootID = internalInstanceToRootIDMap.get(internalInstance);
if (rootID === undefined) {
throw new Error('Expected to find root ID.');
}
flushPendingEvents(rootID);
}
}
},
unmountComponent(fn, args) {
const internalInstance = args[0];
if (getElementType(internalInstance) === ElementTypeOtherOrUnknown) {
return fn.apply(this, args);
}
const id = getID(internalInstance);
parentIDStack.push(id);
try {
const result = fn.apply(this, args);
parentIDStack.pop();
recordUnmount(internalInstance, id);
return result;
} catch (err) {
parentIDStack = [];
throw err;
} finally {
if (parentIDStack.length === 0) {
const rootID = internalInstanceToRootIDMap.get(internalInstance);
if (rootID === undefined) {
throw new Error('Expected to find root ID.');
}
flushPendingEvents(rootID);
}
}
},
});
}
function cleanup() {
if (oldReconcilerMethods !== null) {
if (renderer.Component) {
restoreMany(renderer.Component.Mixin, oldReconcilerMethods);
} else {
restoreMany(renderer.Reconciler, oldReconcilerMethods);
}
}
oldReconcilerMethods = null;
}
function recordMount(
internalInstance: InternalInstance,
id: number,
parentID: number,
) {
const isRoot = parentID === 0;
if (__DEBUG__) {
console.log(
'%crecordMount()',
'color: green; font-weight: bold;',
id,
getData(internalInstance).displayName,
);
}
if (isRoot) {
const hasOwnerMetadata =
internalInstance._currentElement != null &&
internalInstance._currentElement._owner != null;
pushOperation(TREE_OPERATION_ADD);
pushOperation(id);
pushOperation(ElementTypeRoot);
pushOperation(0);
pushOperation(0);
pushOperation(0);
pushOperation(hasOwnerMetadata ? 1 : 0);
} else {
const type = getElementType(internalInstance);
const {displayName, key} = getData(internalInstance);
const ownerID =
internalInstance._currentElement != null &&
internalInstance._currentElement._owner != null
? getID(internalInstance._currentElement._owner)
: 0;
const displayNameStringID = getStringID(displayName);
const keyStringID = getStringID(key);
pushOperation(TREE_OPERATION_ADD);
pushOperation(id);
pushOperation(type);
pushOperation(parentID);
pushOperation(ownerID);
pushOperation(displayNameStringID);
pushOperation(keyStringID);
}
}
function recordReorder(
internalInstance: InternalInstance,
id: number,
nextChildren: Array<InternalInstance>,
) {
pushOperation(TREE_OPERATION_REORDER_CHILDREN);
pushOperation(id);
const nextChildIDs = nextChildren.map(getID);
pushOperation(nextChildIDs.length);
for (let i = 0; i < nextChildIDs.length; i++) {
pushOperation(nextChildIDs[i]);
}
}
function recordUnmount(internalInstance: InternalInstance, id: number) {
pendingUnmountedIDs.push(id);
idToInternalInstanceMap.delete(id);
}
function crawlAndRecordInitialMounts(
id: number,
parentID: number,
rootID: number,
) {
if (__DEBUG__) {
console.group('crawlAndRecordInitialMounts() id:', id);
}
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance != null) {
internalInstanceToRootIDMap.set(internalInstance, rootID);
recordMount(internalInstance, id, parentID);
getChildren(internalInstance).forEach(child =>
crawlAndRecordInitialMounts(getID(child), id, rootID),
);
}
if (__DEBUG__) {
console.groupEnd();
}
}
function flushInitialOperations() {
const roots =
renderer.Mount._instancesByReactRootID ||
renderer.Mount._instancesByContainerID;
for (const key in roots) {
const internalInstance = roots[key];
const id = getID(internalInstance);
crawlAndRecordInitialMounts(id, 0, id);
flushPendingEvents(id);
}
}
const pendingOperations: Array<number> = [];
const pendingStringTable: Map<string, number> = new Map();
let pendingUnmountedIDs: Array<number> = [];
let pendingStringTableLength: number = 0;
let pendingUnmountedRootID: number | null = null;
function flushPendingEvents(rootID: number) {
if (
pendingOperations.length === 0 &&
pendingUnmountedIDs.length === 0 &&
pendingUnmountedRootID === null
) {
return;
}
const numUnmountIDs =
pendingUnmountedIDs.length + (pendingUnmountedRootID === null ? 0 : 1);
const operations = new Array<number>(
2 +
1 +
pendingStringTableLength +
(numUnmountIDs > 0 ? 2 + numUnmountIDs : 0) +
pendingOperations.length,
);
let i = 0;
operations[i++] = rendererID;
operations[i++] = rootID;
operations[i++] = pendingStringTableLength;
pendingStringTable.forEach((value, key) => {
operations[i++] = key.length;
const encodedKey = utfEncodeString(key);
for (let j = 0; j < encodedKey.length; j++) {
operations[i + j] = encodedKey[j];
}
i += key.length;
});
if (numUnmountIDs > 0) {
operations[i++] = TREE_OPERATION_REMOVE;
operations[i++] = numUnmountIDs;
for (let j = 0; j < pendingUnmountedIDs.length; j++) {
operations[i++] = pendingUnmountedIDs[j];
}
if (pendingUnmountedRootID !== null) {
operations[i] = pendingUnmountedRootID;
i++;
}
}
for (let j = 0; j < pendingOperations.length; j++) {
operations[i + j] = pendingOperations[j];
}
i += pendingOperations.length;
if (__DEBUG__) {
printOperationsArray(operations);
}
hook.emit('operations', operations);
pendingOperations.length = 0;
pendingUnmountedIDs = [];
pendingUnmountedRootID = null;
pendingStringTable.clear();
pendingStringTableLength = 0;
}
function pushOperation(op: number): void {
if (__DEV__) {
if (!Number.isInteger(op)) {
console.error(
'pushOperation() was called but the value is not an integer.',
op,
);
}
}
pendingOperations.push(op);
}
function getStringID(str: string | null): number {
if (str === null) {
return 0;
}
const existingID = pendingStringTable.get(str);
if (existingID !== undefined) {
return existingID;
}
const stringID = pendingStringTable.size + 1;
pendingStringTable.set(str, stringID);
pendingStringTableLength += str.length + 1;
return stringID;
}
let currentlyInspectedElementID: number | null = null;
let currentlyInspectedPaths: Object = {};
function mergeInspectedPaths(path: Array<string | number>) {
let current = currentlyInspectedPaths;
path.forEach(key => {
if (!current[key]) {
current[key] = {};
}
current = current[key];
});
}
function createIsPathAllowed(key: string) {
return function isPathAllowed(path: Array<string | number>): boolean {
let current = currentlyInspectedPaths[key];
if (!current) {
return false;
}
for (let i = 0; i < path.length; i++) {
current = current[path[i]];
if (!current) {
return false;
}
}
return true;
};
}
function getInstanceAndStyle(id: number): InstanceAndStyle {
let instance = null;
let style = null;
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance != null) {
instance = internalInstance._instance || null;
const element = internalInstance._currentElement;
if (element != null && element.props != null) {
style = element.props.style || null;
}
}
return {
instance,
style,
};
}
function updateSelectedElement(id: number): void {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance == null) {
console.warn(`Could not find instance with id "${id}"`);
return;
}
switch (getElementType(internalInstance)) {
case ElementTypeClass:
global.$r = internalInstance._instance;
break;
case ElementTypeFunction:
const element = internalInstance._currentElement;
if (element == null) {
console.warn(`Could not find element with id "${id}"`);
return;
}
global.$r = {
props: element.props,
type: element.type,
};
break;
default:
global.$r = null;
break;
}
}
function storeAsGlobal(
id: number,
path: Array<string | number>,
count: number,
): void {
const inspectedElement = inspectElementRaw(id);
if (inspectedElement !== null) {
const value = getInObject(inspectedElement, path);
const key = `$reactTemp${count}`;
window[key] = value;
console.log(key);
console.log(value);
}
}
function getSerializedElementValueByPath(
id: number,
path: Array<string | number>,
): ?string {
const inspectedElement = inspectElementRaw(id);
if (inspectedElement !== null) {
const valueToCopy = getInObject(inspectedElement, path);
return serializeToString(valueToCopy);
}
}
function inspectElement(
requestID: number,
id: number,
path: Array<string | number> | null,
forceFullData: boolean,
): InspectedElementPayload {
if (forceFullData || currentlyInspectedElementID !== id) {
currentlyInspectedElementID = id;
currentlyInspectedPaths = {};
}
const inspectedElement = inspectElementRaw(id);
if (inspectedElement === null) {
return {
id,
responseID: requestID,
type: 'not-found',
};
}
if (path !== null) {
mergeInspectedPaths(path);
}
updateSelectedElement(id);
inspectedElement.context = cleanForBridge(
inspectedElement.context,
createIsPathAllowed('context'),
);
inspectedElement.props = cleanForBridge(
inspectedElement.props,
createIsPathAllowed('props'),
);
inspectedElement.state = cleanForBridge(
inspectedElement.state,
createIsPathAllowed('state'),
);
return {
id,
responseID: requestID,
type: 'full-data',
value: inspectedElement,
};
}
function inspectElementRaw(id: number): InspectedElement | null {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance == null) {
return null;
}
const {key} = getData(internalInstance);
const type = getElementType(internalInstance);
let context = null;
let owners = null;
let props = null;
let state = null;
const element = internalInstance._currentElement;
if (element !== null) {
props = element.props;
let owner = element._owner;
if (owner) {
owners = ([]: Array<SerializedElement>);
while (owner != null) {
owners.push({
displayName: getData(owner).displayName || 'Unknown',
id: getID(owner),
key: element.key,
type: getElementType(owner),
});
if (owner._currentElement) {
owner = owner._currentElement._owner;
}
}
}
}
const publicInstance = internalInstance._instance;
if (publicInstance != null) {
context = publicInstance.context || null;
state = publicInstance.state || null;
}
const errors: Array<[string, number]> = [];
const warnings: Array<[string, number]> = [];
return {
id,
canEditHooks: false,
canEditFunctionProps: false,
canEditHooksAndDeletePaths: false,
canEditHooksAndRenamePaths: false,
canEditFunctionPropsDeletePaths: false,
canEditFunctionPropsRenamePaths: false,
canToggleError: false,
isErrored: false,
canToggleSuspense: false,
canViewSource: type === ElementTypeClass || type === ElementTypeFunction,
source: null,
hasLegacyContext: true,
type: type,
key: key != null ? key : null,
context,
hooks: null,
props,
state,
errors,
warnings,
owners,
rootType: null,
rendererPackageName: null,
rendererVersion: null,
plugins: {
stylex: null,
},
};
}
function logElementToConsole(id: number): void {
const result = inspectElementRaw(id);
if (result === null) {
console.warn(`Could not find element with id "${id}"`);
return;
}
const displayName = getDisplayNameForElementID(id);
const supportsGroup = typeof console.groupCollapsed === 'function';
if (supportsGroup) {
console.groupCollapsed(
`[Click to expand] %c<${displayName || 'Component'} />`,
'color: var(--dom-tag-name-color); font-weight: normal;',
);
}
if (result.props !== null) {
console.log('Props:', result.props);
}
if (result.state !== null) {
console.log('State:', result.state);
}
if (result.context !== null) {
console.log('Context:', result.context);
}
const hostInstance = findHostInstanceForInternalID(id);
if (hostInstance !== null) {
console.log('Node:', hostInstance);
}
if (window.chrome || /firefox/i.test(navigator.userAgent)) {
console.log(
'Right-click any value to save it as a global variable for further inspection.',
);
}
if (supportsGroup) {
console.groupEnd();
}
}
function getElementAttributeByPath(
id: number,
path: Array<string | number>,
): mixed {
const inspectedElement = inspectElementRaw(id);
if (inspectedElement !== null) {
return getInObject(inspectedElement, path);
}
return undefined;
}
function getElementSourceFunctionById(id: number): null | Function {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance == null) {
console.warn(`Could not find instance with id "${id}"`);
return null;
}
const element = internalInstance._currentElement;
if (element == null) {
console.warn(`Could not find element with id "${id}"`);
return null;
}
return element.type;
}
function deletePath(
type: 'context' | 'hooks' | 'props' | 'state',
id: number,
hookID: ?number,
path: Array<string | number>,
): void {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance != null) {
const publicInstance = internalInstance._instance;
if (publicInstance != null) {
switch (type) {
case 'context':
deletePathInObject(publicInstance.context, path);
forceUpdate(publicInstance);
break;
case 'hooks':
throw new Error('Hooks not supported by this renderer');
case 'props':
const element = internalInstance._currentElement;
internalInstance._currentElement = {
...element,
props: copyWithDelete(element.props, path),
};
forceUpdate(publicInstance);
break;
case 'state':
deletePathInObject(publicInstance.state, path);
forceUpdate(publicInstance);
break;
}
}
}
}
function renamePath(
type: 'context' | 'hooks' | 'props' | 'state',
id: number,
hookID: ?number,
oldPath: Array<string | number>,
newPath: Array<string | number>,
): void {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance != null) {
const publicInstance = internalInstance._instance;
if (publicInstance != null) {
switch (type) {
case 'context':
renamePathInObject(publicInstance.context, oldPath, newPath);
forceUpdate(publicInstance);
break;
case 'hooks':
throw new Error('Hooks not supported by this renderer');
case 'props':
const element = internalInstance._currentElement;
internalInstance._currentElement = {
...element,
props: copyWithRename(element.props, oldPath, newPath),
};
forceUpdate(publicInstance);
break;
case 'state':
renamePathInObject(publicInstance.state, oldPath, newPath);
forceUpdate(publicInstance);
break;
}
}
}
}
function overrideValueAtPath(
type: 'context' | 'hooks' | 'props' | 'state',
id: number,
hookID: ?number,
path: Array<string | number>,
value: any,
): void {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance != null) {
const publicInstance = internalInstance._instance;
if (publicInstance != null) {
switch (type) {
case 'context':
setInObject(publicInstance.context, path, value);
forceUpdate(publicInstance);
break;
case 'hooks':
throw new Error('Hooks not supported by this renderer');
case 'props':
const element = internalInstance._currentElement;
internalInstance._currentElement = {
...element,
props: copyWithSet(element.props, path, value),
};
forceUpdate(publicInstance);
break;
case 'state':
setInObject(publicInstance.state, path, value);
forceUpdate(publicInstance);
break;
}
}
}
}
const getProfilingData = () => {
throw new Error('getProfilingData not supported by this renderer');
};
const handleCommitFiberRoot = () => {
throw new Error('handleCommitFiberRoot not supported by this renderer');
};
const handleCommitFiberUnmount = () => {
throw new Error('handleCommitFiberUnmount not supported by this renderer');
};
const handlePostCommitFiberRoot = () => {
throw new Error('handlePostCommitFiberRoot not supported by this renderer');
};
const overrideError = () => {
throw new Error('overrideError not supported by this renderer');
};
const overrideSuspense = () => {
throw new Error('overrideSuspense not supported by this renderer');
};
const startProfiling = () => {
};
const stopProfiling = () => {
};
function getBestMatchForTrackedPath(): PathMatch | null {
return null;
}
function getPathForElement(id: number): Array<PathFrame> | null {
return null;
}
function updateComponentFilters(componentFilters: Array<ComponentFilter>) {
}
function getEnvironmentNames(): Array<string> {
return [];
}
function setTraceUpdatesEnabled(enabled: boolean) {
}
function setTrackedPath(path: Array<PathFrame> | null) {
}
function getOwnersList(id: number): Array<SerializedElement> | null {
return null;
}
function clearErrorsAndWarnings() {
}
function clearErrorsForElementID(id: number) {
}
function clearWarningsForElementID(id: number) {
}
function hasElementWithId(id: number): boolean {
return idToInternalInstanceMap.has(id);
}
return {
clearErrorsAndWarnings,
clearErrorsForElementID,
clearWarningsForElementID,
cleanup,
getSerializedElementValueByPath,
deletePath,
flushInitialOperations,
getBestMatchForTrackedPath,
getDisplayNameForElementID,
getNearestMountedDOMNode,
getElementIDForHostInstance,
getInstanceAndStyle,
findHostInstancesForElementID: (id: number) => {
const hostInstance = findHostInstanceForInternalID(id);
return hostInstance == null ? null : [hostInstance];
},
getOwnersList,
getPathForElement,
getProfilingData,
handleCommitFiberRoot,
handleCommitFiberUnmount,
handlePostCommitFiberRoot,
hasElementWithId,
inspectElement,
logElementToConsole,
overrideError,
overrideSuspense,
overrideValueAtPath,
renamePath,
getElementAttributeByPath,
getElementSourceFunctionById,
renderer,
setTraceUpdatesEnabled,
setTrackedPath,
startProfiling,
stopProfiling,
storeAsGlobal,
updateComponentFilters,
getEnvironmentNames,
};
}