;
+ s: boolean;
+ u?: (() => void) | undefined;
+ er?: Array<{
+ error: unknown;
+ stack: string;
+ }> | undefined;
+ eb?: boolean;
+ sp?: boolean;
+ re?: RecoverableErrorHandler | undefined;
+ ce?: RecoverableErrorHandler | undefined;
+ ue?: RecoverableErrorHandler | undefined;
+ i: string | undefined;
+ ic: number;
+ h: boolean;
+ a?: boolean;
+ fr?: boolean;
}
-
export function createFiber(tag: FiberTag, type: any, key: string | null): Fiber {
- return {
- tag,
- type,
- key,
- ref: null,
- pp: null,
- mp: null,
- ms: null,
- sn: null,
- dom: null,
- parent: null,
- child: null,
- sibling: null,
- hooks: null,
- cu: null,
- dy: false,
- um: false,
- root: null,
- }
+ return {
+ t: tag,
+ type,
+ key,
+ ref: null,
+ v: null,
+ m: null,
+ b: null,
+ i: null,
+ n: null,
+ p: null,
+ c: null,
+ s: null,
+ h: null,
+ e: null,
+ g: false,
+ u: false,
+ r: null,
+ };
}
-
-export type ChildNode = ReactElement | string | number | boolean | null | undefined | ChildNode[]
-export type { ReactElement, ReactNode }
+export type ChildNode = ReactElement | string | number | boolean | null | undefined | ChildNode[];
+export type { ReactElement, ReactNode };
diff --git a/packages/redact/src/core/resource-hints.ts b/packages/redact/src/core/resource-hints.ts
index 82bf80e..53984da 100644
--- a/packages/redact/src/core/resource-hints.ts
+++ b/packages/redact/src/core/resource-hints.ts
@@ -1,3 +1,4 @@
+import { isFunction } from './functions'
export type ResourceHintKind =
| 'dns-prefetch'
| 'preconnect'
@@ -22,15 +23,17 @@ export const resourceHintDispatcher: {
// out of sharing, but event ownership, disabled sheets and itemProp do.
export function resourceKind(type: string, props: any): 'style' | 'stylesheet' | 'script' | undefined {
if (props.itemProp != null) return
- if (type === 'style') {
- if (typeof props.precedence === 'string' && typeof props.href === 'string' && props.href) return 'style'
- } else if (type === 'link') {
- if (props.rel === 'stylesheet' && typeof props.href === 'string' && props.href && typeof props.precedence === 'string' && props.disabled == null && !props.onLoad && !props.onError) return 'stylesheet'
- } else if (type === 'script' && props.async && typeof props.async !== 'function' && typeof props.async !== 'symbol' && typeof props.src === 'string' && props.src && !props.onLoad && !props.onError) return 'script'
+ if (type === 'style' || type === 'link') {
+ if (isString(props.precedence) && isString(props.href) && props.href &&
+ (type === 'style' || props.rel === 'stylesheet' && props.disabled == null && !props.onLoad && !props.onError)) {
+ return type === 'style' ? 'style' : 'stylesheet'
+ }
+ } else if (type === 'script' && props.async && !isFunction(props.async) && typeof props.async !== 'symbol' && isString(props.src) && props.src && !props.onLoad && !props.onError) return 'script'
+
}
-export function resourceURL(value: string): string {
- return /^[\u0000-\u0020]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i.test(value)
+export const resourceURL = (value: string): string => (/^[\u0000-\u0020]*javascript:/i.test(String(value).replace(/[\r\n\t]/g, ''))
? "javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')"
- : value
-}
+ : value)
+
+import { isString } from "./functions"
diff --git a/packages/redact/src/core/types.ts b/packages/redact/src/core/types.ts
index 4d1f92b..6b7fd1e 100644
--- a/packages/redact/src/core/types.ts
+++ b/packages/redact/src/core/types.ts
@@ -15,7 +15,7 @@ export interface ReactElement {
props: P
}
-export type ReactNode =
+type ReactNodeValue =
| ReactElement
| string
| number
@@ -24,6 +24,8 @@ export type ReactNode =
| undefined
| Iterable
+export type ReactNode = ReactNodeValue | Promise
+
export type RefObject = { current: T | null }
export type RefCallback = (instance: T | null) => void | (() => void)
export type Ref = RefObject | RefCallback | null
diff --git a/packages/redact/src/dom/_all.ts b/packages/redact/src/dom/_all.ts
index ef8f5c2..7204589 100644
--- a/packages/redact/src/dom/_all.ts
+++ b/packages/redact/src/dom/_all.ts
@@ -5,44 +5,42 @@
// state for things like the hydration CLAIMED WeakSet, scheduler queue,
// dispatcher H slot, etc. is preserved because every public entry funnels
// through this module.
-
// Side-effect import: registers all opt-in features (renderers, type matchers,
// element markers) with the reconciler. A vite plugin may alias individual
// feature modules to ./features//stub to strip them from the bundle.
-import './features'
-
+import './features';
// Top-level public API (mirrors the surface of /, /dom, /dom-client).
-export { flushSync, batchedUpdates as unstable_batchedUpdates } from './root'
-export { createRoot, hydrateRoot } from './root'
-export type { Root, RootOptions } from './root'
-export { createPortal } from './portal'
-export { act } from './test-utils'
-
-export { preconnect, prefetchDNS, preload, preinit, preloadModule, preinitModule } from './resource-hints'
-
-export const version = '19.2.3'
-
+export { flushSync, batchedUpdates as unstable_batchedUpdates } from './root';
+export { createRoot, hydrateRoot } from './root';
+export type { Root, RootOptions } from './root';
+export { createPortal } from './portal';
+export { act } from './test-utils';
+export { preconnect, prefetchDNS, preload, preinit, preloadModule, preinitModule } from './resource-hints';
+export const version = '19.2.3';
// Registration primitives — needed by custom features
-// (`registerRenderer`, `registerTypeMatcher`, `registerElementMarker`) and by
+// (`installRenderer`, `registerTypeMatcher`, `registerElementMarker`) and by
// anything that wants to override a cross-cutting concern like Suspense's
// `handleSuspended` or Context's `readContext` (`installCapability`). The
// built-in features in `./features/*/{full,stub}.ts` use these via relative
// imports; external authors reach them through this entry. Order-of-
// registration matters: later calls overwrite earlier ones.
-export {
- registerRenderer,
- registerTypeMatcher,
- registerElementMarker,
- installCapability,
- reconcileChildren,
- childrenToArray,
-} from './reconcile'
-export type {
- RenderFn,
- TypeMatcher,
- Capabilities,
-} from './reconcile'
-
+export { registerTypeMatcher, registerElementMarker, reconcileChildren, childrenToArray, } from './reconcile';
+export type { TypeMatcher, } from './reconcile';
+import { installCapability as install, type Capabilities as InternalCapabilities } from './reconcile';
+const capabilityKeys = {
+ handleSuspended: 'h', readContext: 'c', isActivityHidden: 'i',
+ activityIsDisconnected: 'd', prepareActivityFiber: 'p', syncActivity: 's',
+} as const;
+export type Capabilities = {
+ [K in keyof typeof capabilityKeys]: InternalCapabilities[(typeof capabilityKeys)[K]];
+};
+// Keep extension names stable while ordinary bundles use compact private keys.
+export function installCapability(name: K, fn: Capabilities[K]): void {
+ install(capabilityKeys[name], fn as InternalCapabilities[(typeof capabilityKeys)[K]]);
+}
// Core types most custom features need.
-export { FiberTag } from '../core'
-export type { Fiber, FiberRoot, ReactNode, ReactElement } from '../core'
+export { FiberTag } from '../core';
+export type { Fiber, FiberRoot, ReactNode, ReactElement } from '../core';
+import { installRenderer, getHostParent, getAnchor } from "./reconcile";
+export type RenderFn = (fiber: import("../core").Fiber, parent: Node, anchor: Node | null) => void;
+export function registerRenderer(tag: import("../core").FiberTag, fn: RenderFn): void { installRenderer(tag, fiber => { const parent = getHostParent(fiber); fn(fiber, parent, getAnchor(fiber, parent)); }); }
diff --git a/packages/redact/src/dom/client.ts b/packages/redact/src/dom/client.ts
index 5959401..2422562 100644
--- a/packages/redact/src/dom/client.ts
+++ b/packages/redact/src/dom/client.ts
@@ -1,13 +1,16 @@
-import './features'
+export type { Root, RootOptions } from './root'
export { createRoot, hydrateRoot } from './root'
-export type { Root, RootOptions } from './root'
+
+import './features'
+
+import { createRoot, hydrateRoot } from './root'
// Default export — real React's `react-dom/client` is named-only too, but
// bundlers synthesize a default from the CJS `module.exports` object.
// Redact ships pure ESM, so without an explicit default, strict bundlers
// (rolldown, modern Vite) reject `import ReactDOM from 'react-dom/client'`.
-import { createRoot, hydrateRoot } from './root'
+
export default {
createRoot,
hydrateRoot,
diff --git a/packages/redact/src/dom/commit.ts b/packages/redact/src/dom/commit.ts
index 7f4f908..3da3f7d 100644
--- a/packages/redact/src/dom/commit.ts
+++ b/packages/redact/src/dom/commit.ts
@@ -1,186 +1,223 @@
-import { FiberTag, type Fiber, type Hook } from '../core'
-import { setProp } from './dom'
-
+import { walkRetained } from './walk';
+import { isString } from "../core/functions";
+import { isFunction } from '../core/functions';
+import { setProp } from './dom';
export interface CommitPlan {
- q: any[][] // Before-mutation, mutation, effect, rollback and failure queues.
- c: number[][] // Queue lengths saved at each rollback checkpoint.
- b: number // Next before-mutation callback, including reentrant commits.
- m: boolean // Mutation phase has started, preventing duplicate commits.
-}
-
-export let currentCommit: CommitPlan | null = null
-let activeCommit: CommitPlan | null = null
-let reusableCommit: CommitPlan | undefined
-let checkpointHook: (() => void) | undefined
-
-function createCommit(): CommitPlan {
- return { q: [[], [], [], [], []], c: [], b: 0, m: false }
-}
-
-export function prepareCommit(render: () => void, plan = createCommit()): CommitPlan {
- const previous = currentCommit
- currentCommit = plan
- try { render() } catch (error) {
- currentCommit = null
- const failed = plan.q[4]!
- for (let i = 0; i < failed.length; i++) {
- const work = failed[i]
- if (typeof work === 'function') work()
- else {
- const children = failed[++i] as Fiber[] | undefined
- work.child = children?.[0] ?? null
- if (children) for (let j = 0; j < children.length; j++) children[j]!.sibling = children[j + 1] ?? null
- }
- }
- throw error
- } finally { currentCommit = previous }
- return plan
+ q: any[][]; // Before-mutation, mutation, effect, rollback, failure, and placement queues.
+ c: number[][]; // Queue lengths saved at each rollback checkpoint.
+ b: number; // Next before-mutation callback, including reentrant commits.
+ m: boolean; // Mutation phase has started, preventing duplicate commits.
+}
+export let currentCommit: CommitPlan | null = null;
+let activeCommit: CommitPlan | null = null;
+let reusableCommit: CommitPlan | undefined;
+let checkpointHook: (() => void) | undefined;
+let placementHook: (parents: Iterable) => void;
+const createCommit = (): CommitPlan => ({ q: [[], [], [], [], [], []], c: [], b: 0, m: false });
+function clearCommit(plan: CommitPlan): void {
+ for (const queue of plan.q)
+ queue.length = 0;
+ plan.c.length = 0;
+}
+export function setCommitPlacementHook(hook: typeof placementHook): void { placementHook = hook; }
+export function checkpointCommit(fiber?: Fiber): number {
+ if (!currentCommit)
+ return -1;
+ const checkpoint = currentCommit.c.length;
+ currentCommit.c.push(currentCommit.q.map(queue => queue.length));
+ if (fiber)
+ walkRetained(fiber, rememberFiber);
+ checkpointHook?.();
+ return checkpoint;
}
-
-export function queueMutation(work: () => void): void {
- if (!currentCommit) { work(); return }
- currentCommit.q[1]!.push(work)
+export function commitBeforeMutation(plan: CommitPlan): void {
+ const queue = plan.q[0]!;
+ while (plan.b < queue.length)
+ queue[plan.b++]!();
+}
+export function queueBeforeMutation(work: () => void): void {
+ if (currentCommit)
+ currentCommit.q[0]!.push(work);
+ else
+ work();
+}
+export function onCommitFailure(work: () => void): void {
+ if (currentCommit)
+ currentCommit.q[4]!.push(work);
+ else
+ work();
+}
+export function setCommitCheckpointHook(hook: () => void): void {
+ checkpointHook = hook;
}
-
export function queueText(node: Text, value: string): void {
- if (currentCommit) currentCommit.q[1]!.push(value, node)
- else node.data = value
+ if (currentCommit)
+ currentCommit.q[1]!.push(value, node);
+ else
+ node.data = value;
}
-
export function queueProp(node: Element, key: string, value: any, previous: any, svg: boolean): void {
- if (currentCommit) currentCommit.q[1]!.push(node, key, value, previous, svg)
- else setProp(node, key, value, previous, svg)
+ if (currentCommit)
+ currentCommit.q[1]!.push(node, key, value, previous, svg);
+ else
+ setProp(node, key, value, previous, svg);
}
-
-export function queueBeforeMutation(work: () => void): void {
- if (currentCommit) currentCommit.q[0]!.push(work)
- else work()
+export function queueMutation(work: () => void): void {
+ if (!currentCommit) {
+ work();
+ return;
+ }
+ currentCommit.q[1]!.push(work);
}
-
-export function queueCommitEffects(work: () => void): void {
- const plan = currentCommit || activeCommit
- if (plan) plan.q[2]!.push(work)
- else work()
+export function rewindCommit(checkpoint: number): void {
+ if (!currentCommit || checkpoint < 0)
+ return;
+ const lengths = currentCommit.c[checkpoint]!;
+ const undo = currentCommit.q[3]!;
+ for (let index = undo.length - 1; index >= lengths[3]!; index--)
+ undo[index]!();
+ currentCommit.q.forEach((queue, index) => { queue.length = lengths[index]!; });
+ currentCommit.c.length = checkpoint;
+}
+export function queuePlacement(parent: Fiber): void {
+ if (!currentCommit) {
+ placementHook([parent]);
+ return;
+ }
+ currentCommit.q[5]!.push(parent);
}
-
export function onCommitRollback(work: () => void): void {
- currentCommit?.q[3]!.push(work)
+ currentCommit?.q[3]!.push(work);
}
-
-export function onCommitFailure(work: () => void): void {
- if (currentCommit) currentCommit.q[4]!.push(work)
- else work()
-}
-
-// Structural reconciliation already builds the previous child list. Keep
-// that list for a failed preparation without allocating per-fiber closures.
-export function rememberChildList(parent: Fiber, children?: Fiber[]): void {
- currentCommit?.q[4]!.push(parent, children)
+export function commitMutation(plan: CommitPlan): void {
+ if (plan.m)
+ return;
+ commitBeforeMutation(plan);
+ if (plan.m)
+ return;
+ plan.m = true;
+ const previous = activeCommit;
+ activeCommit = plan;
+ try {
+ const mutations = plan.q[1]!;
+ for (let index = 0; index < mutations.length; index++) {
+ const work = mutations[index];
+ if (isFunction(work))
+ work();
+ else if (isString(work))
+ mutations[++index].data = work;
+ else
+ setProp(work, mutations[++index], mutations[++index], mutations[++index], mutations[++index]);
+ }
+ placementHook?.(plan.q[5]!);
+ for (const work of plan.q[2]!)
+ work();
+ }
+ finally {
+ activeCommit = previous;
+ clearCommit(plan);
+ }
}
-
-export function setCommitCheckpointHook(hook: () => void): void {
- checkpointHook = hook
+export function queueCommitEffects(work: () => void): void {
+ const plan = currentCommit || activeCommit;
+ if (plan)
+ plan.q[2]!.push(work);
+ else
+ work();
}
-
-export function checkpointCommit(fiber?: Fiber): number {
- if (!currentCommit) return -1
- const checkpoint = currentCommit.c.length
- currentCommit.c.push(currentCommit.q.map(queue => queue.length))
- if (fiber) rememberSubtree(fiber)
- checkpointHook?.()
- return checkpoint
-}
-
-function rememberSubtree(fiber: Fiber): void {
- const saved = { ...fiber }
- // Memo and lazy preserve their wrapper tag around the same class instance.
- const instance = fiber.sn?._fiber === fiber ? fiber.sn : null
- // Hook has four fields. One flat snapshot preserves slot and dispatch
- // identity without a tuple and object clone for every hook.
- const hooks: any[] | null = fiber.hooks && []
- if (hooks) for (const hook of fiber.hooks!) {
- const queue = hook.q
- hooks.push(hook, hook.s, queue, hook.d, hook.c, queue && typeof queue === 'object' ? { ...queue } : null)
- }
- const memoized = fiber.ms && (instance || fiber.tag === FiberTag.Suspense || fiber.tag === FiberTag.Fragment)
- ? { ...fiber.ms } : null
- if (fiber.cx) saved.cx = new Map(fiber.cx)
- if (fiber.cu) saved.cu = fiber.cu.slice()
- const props = instance?.props, state = instance?.state, context = instance?.context
- onCommitRollback(() => {
- Object.assign(fiber, saved)
- if (memoized) {
- for (const key in fiber.ms) if (!(key in memoized)) delete fiber.ms[key]
- Object.assign(fiber.ms, memoized)
+export function commitSynchronously(render: () => void): void {
+ if (currentCommit) {
+ render();
+ return;
+ }
+ const plan = reusableCommit || createCommit();
+ reusableCommit = undefined;
+ plan.b = 0;
+ plan.m = false;
+ try {
+ commitMutation(prepareCommit(render, plan));
}
- if (hooks) {
- fiber.hooks!.length = hooks.length / 6
- for (let index = 0; index < hooks.length; index += 6) {
- const hook: Hook = fiber.hooks![index / 6] = hooks[index]
- hook.s = hooks[index + 1]
- hook.q = hooks[index + 2]
- hook.d = hooks[index + 3]
- hook.c = hooks[index + 4]
- const queue = hooks[index + 5]
- if (queue) Object.assign(hook.q, queue)
- }
+ finally {
+ // Failed preparation must not leave work for a later synchronous root.
+ if (!plan.m) {
+ clearCommit(plan);
+ }
+ reusableCommit = plan;
}
- if (instance) { instance.props = props; instance.state = state; instance.context = context }
- })
- for (let child = fiber.child; child; child = child.sibling) rememberSubtree(child)
- if (fiber.tag === FiberTag.Suspense && fiber.ms?.f) rememberSubtree(fiber.ms.f)
}
-
-export function rewindCommit(checkpoint: number): void {
- if (!currentCommit || checkpoint < 0) return
- const lengths = currentCommit.c[checkpoint]!
- const undo = currentCommit.q[3]!
- for (let index = undo.length - 1; index >= lengths[3]!; index--) undo[index]!()
- currentCommit.q.forEach((queue, index) => { queue.length = lengths[index]! })
- currentCommit.c.length = checkpoint
-}
-
-export function commitBeforeMutation(plan: CommitPlan): void {
- const queue = plan.q[0]!
- while (plan.b < queue.length) queue[plan.b++]!()
+import { FiberTag, type Fiber, type Hook } from '../core';
+function rememberFiber(fiber: Fiber): void {
+ const saved = { ...fiber };
+ // Memo and lazy preserve their wrapper tag around the same class instance.
+ const instance = fiber.i?._f === fiber ? fiber.i : null;
+ const hooks = fiber.h?.map(hook => [hook, { ...hook },
+ hook.q && isObject(hook.q) ? { ...hook.q } : null]);
+ const memoized = fiber.b && (instance || fiber.t === FiberTag.Suspense || fiber.t === FiberTag.Fragment)
+ ? { ...fiber.b } : null;
+ if (fiber.cx)
+ saved.cx = new Map(fiber.cx);
+ if (fiber.e)
+ saved.e = fiber.e.slice();
+ const props = instance?.props, state = instance?.state, context = instance?.context;
+ onCommitRollback(() => {
+ Object.assign(fiber, saved);
+ if (memoized) {
+ for (const key in fiber.b)
+ if (!(key in memoized))
+ delete fiber.b[key];
+ Object.assign(fiber.b, memoized);
+ }
+ if (hooks) {
+ fiber.h!.length = hooks.length;
+ for (let index = 0; index < hooks.length; index++) {
+ const [hook, saved, queue] = hooks[index]!;
+ fiber.h![index] = Object.assign(hook, saved);
+ if (queue)
+ Object.assign(hook.q, queue);
+ }
+ }
+ if (instance) {
+ instance.props = props;
+ instance.state = state;
+ instance.context = context;
+ }
+ });
+}
+import { isObject } from '../core/functions';
+// Structural reconciliation already builds the previous child list. Keep
+// that list for a failed preparation without allocating per-fiber closures.
+export function rememberChildList(parent: Fiber, children?: Fiber[]): void {
+ currentCommit?.q[4]!.push(parent, children);
}
-
-export function commitMutation(plan: CommitPlan): void {
- if (plan.m) return
- commitBeforeMutation(plan)
- if (plan.m) return
- plan.m = true
- const previous = activeCommit
- activeCommit = plan
- try {
- const mutations = plan.q[1]!
- for (let index = 0; index < mutations.length; index++) {
- const work = mutations[index]
- if (typeof work === 'function') work()
- else if (typeof work === 'string') mutations[++index].data = work
- else setProp(work, mutations[++index], mutations[++index], mutations[++index], mutations[++index])
+export function prepareCommit(render: () => void, plan = createCommit()): CommitPlan {
+ const previous = currentCommit;
+ currentCommit = plan;
+ try {
+ render();
}
- for (const work of plan.q[2]!) work()
- } finally {
- activeCommit = previous
- for (const queue of plan.q) queue.length = 0
- plan.c.length = 0
- }
-}
-
-export function commitSynchronously(render: () => void): void {
- if (currentCommit) { render(); return }
- const plan = reusableCommit || createCommit()
- reusableCommit = undefined
- plan.b = 0
- plan.m = false
- try { commitMutation(prepareCommit(render, plan)) }
- finally {
- // Failed preparation must not leave work for a later synchronous root.
- if (!plan.m) {
- for (const queue of plan.q) queue.length = 0
- plan.c.length = 0
+ catch (error) {
+ currentCommit = null;
+ const failed = plan.q[4]!;
+ for (let i = 0; i < failed.length; i++) {
+ const work = failed[i];
+ if (isFunction(work))
+ work();
+ else {
+ const children = failed[++i] as Fiber[] | undefined;
+ for (let child = work.c; child; child = child.s) {
+ if (!children?.includes(child))
+ walkRetained(child, fiber => { fiber.u = true; });
+ }
+ work.c = children?.[0] ?? null;
+ if (children)
+ for (let j = 0; j < children.length; j++)
+ children[j]!.s = children[j + 1] ?? null;
+ }
+ }
+ throw error;
+ }
+ finally {
+ currentCommit = previous;
}
- reusableCommit = plan
- }
+ return plan;
}
diff --git a/packages/redact/src/dom/devtools-effects.ts b/packages/redact/src/dom/devtools-effects.ts
index 8dcd67c..a00226d 100644
--- a/packages/redact/src/dom/devtools-effects.ts
+++ b/packages/redact/src/dom/devtools-effects.ts
@@ -71,3 +71,8 @@ export function notifyDevToolsCommit(root: FiberRoot, notify: (layout: number, p
function drain(): void {
while (commits.length && commits[0]!.pending === 0) commits.shift()!.notify!()
}
+
+export function measureDevToolsLayout(root: FiberRoot, work: () => void): void {
+ const start = startDevToolsEffect()
+ try { work() } finally { finishDevToolsLayout(root, start) }
+}
diff --git a/packages/redact/src/dom/devtools.ts b/packages/redact/src/dom/devtools.ts
index 11c97e8..f5a91b0 100644
--- a/packages/redact/src/dom/devtools.ts
+++ b/packages/redact/src/dom/devtools.ts
@@ -1,14 +1,21 @@
import { FiberTag, type Fiber, type FiberRoot } from '../core'
-import { ReactSharedInternals } from '../react/shared-internals'
-import { REACT_VIEW_TRANSITION_TYPE } from '../react/view-transition'
-import { REACT_PROFILER_TYPE, REACT_STRICT_MODE_TYPE } from '../react/suspense'
-import { scheduleUpdate, flushSyncWork } from './reconcile'
-import { onCommitRollback } from './commit'
import { devToolsMetadata } from '../core/devtools'
import { notifyDevToolsCommit, hasPendingDevToolsEffects } from './devtools-effects'
// React DevTools consumes React's private fiber layout. Keep that layout in a
// separate, development-only tree instead of expanding Redact's runtime fibers.
+// The renderer supplies its development hooks at attachment time. Keeping
+// this module independent avoids pulling renderer cycles into production builds.
+interface DevToolsRuntime {
+ shared: typeof import('../react/shared-internals').ReactSharedInternals
+ scheduleUpdate: typeof import('./reconcile').scheduleUpdate
+ flushSyncWork: typeof import('./reconcile').flushSyncWork
+ onCommitRollback: typeof import('./commit').onCommitRollback
+}
+let runtime: DevToolsRuntime
+const REACT_VIEW_TRANSITION_TYPE = /* @__PURE__ */ Symbol.for('react.view_transition')
+const REACT_PROFILER_TYPE = /* @__PURE__ */ Symbol.for('react.profiler')
+const REACT_STRICT_MODE_TYPE = /* @__PURE__ */ Symbol.for('react.strict_mode')
type DebugFiber = Record
type HookDescription = { name: string; index: number; value: any; args: any[] }
const fibers = /* @__PURE__ */ new WeakMap