@anscribe/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 msmps
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # @anscribe/react
2
+
3
+ > Cross-framework React DevTools enrichment for Anscribe Captures.
4
+
5
+ Installs a React DevTools hook that captures React's `currentDispatcherRef` and observes commits. Anscribe's host adapters (`@anscribe/opentui` today; `@anscribe/ink` and friends are the intended future) consume it to enrich Captures with `componentName`, `componentPath`, and source-frame references.
6
+
7
+ This package is the cross-framework substrate. Most users should reach for the host adapter's re-export instead — for OpenTUI that's [`@anscribe/opentui/react/preload`](https://www.npmjs.com/package/@anscribe/opentui).
8
+
9
+ ## When to use this package directly
10
+
11
+ Import `@anscribe/react/preload` directly when:
12
+
13
+ - You're building a non-OpenTUI Anscribe host adapter and want to share the enricher.
14
+ - You're embedding the enricher into a custom React TUI bootstrapper where adapter subpaths don't fit your build pipeline.
15
+
16
+ Otherwise, prefer your host adapter's preload subpath.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ bun add @anscribe/react
22
+ ```
23
+
24
+ `react@>=19.2.0` is a peer dependency.
25
+
26
+ ## Use
27
+
28
+ ```ts
29
+ // MUST be imported before "react" / "@opentui/react" / your React renderer
30
+ import "@anscribe/react/preload";
31
+ ```
32
+
33
+ The preload installs `globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__` (or patches an existing one) before React reads it. It must run **before** the first React render, otherwise the renderer is created without the hook attached and metadata enrichment silently no-ops.
34
+
35
+ Adapter-internal API (consumed by host adapters, not end users):
36
+
37
+ ```ts
38
+ import { reactMetadataEnricher, isReactRuntimeEnrichmentAvailable } from "@anscribe/react";
39
+ ```
40
+
41
+ ## License
42
+
43
+ MIT © [msmps](https://github.com/msmps)
@@ -0,0 +1,382 @@
1
+ import { SourceReference } from "@anscribe/core";
2
+ import { Effect } from "effect";
3
+ //#region src/source-frames.ts
4
+ const V8_FRAME = /^\s*at\s+(?:(.+?)\s+)?\(?(.+?):(\d+):(\d+)\)?\s*$/;
5
+ const WEBKIT_FRAME = /^(?:(.+?)@)?(.+?):(\d+):(\d+)$/;
6
+ const REJECT_PATH = /(?:node_modules\/|@anscribe\/[a-z-]+\/(?:dist|src|bin)\/|\/packages\/(?:core|react|opentui|mcp|ink)\/(?:src|bin)\/)/;
7
+ const REJECT_FN = /^(?:react-stack-bottom-frame|runWithFiberInDEV|commitMount|commitWork)$/;
8
+ const REACT_PACKAGE_BUNDLE_TAIL = /^(?:cjs|umd|esm|dist|build|lib)\/|^index\.[mc]?js(?:$|\/)/;
9
+ const PREFIX_REGEX = /^(?:webpack-internal:\/{2,3}|webpack:\/{2,3}|turbopack:\/{2,3}|bun:\/{2,3}|node:)/;
10
+ const knownReactPackageNames = new Set([
11
+ "react",
12
+ "react-dom",
13
+ "react-reconciler",
14
+ "scheduler"
15
+ ]);
16
+ function recordReactRendererPackageName(name) {
17
+ if (typeof name !== "string" || name.length === 0) return;
18
+ knownReactPackageNames.add(name);
19
+ }
20
+ function parseStackFrame(frame) {
21
+ const trimmed = frame.trim();
22
+ if (trimmed.length === 0) return void 0;
23
+ const v8 = V8_FRAME.exec(trimmed);
24
+ if (v8 !== null) return makeParsedFrame(v8[1], v8[2], v8[3], v8[4]);
25
+ const webkit = WEBKIT_FRAME.exec(trimmed);
26
+ if (webkit !== null) return makeParsedFrame(webkit[1], webkit[2], webkit[3], webkit[4]);
27
+ }
28
+ function cleanSourcePath(path) {
29
+ let cleaned = path;
30
+ cleaned = cleaned.replace(PREFIX_REGEX, "");
31
+ if (cleaned.startsWith("file://")) {
32
+ cleaned = cleaned.slice(7);
33
+ try {
34
+ cleaned = decodeURIComponent(cleaned);
35
+ } catch {}
36
+ }
37
+ if (cleaned.startsWith("./")) cleaned = cleaned.slice(2);
38
+ else if (cleaned.startsWith("~/")) cleaned = cleaned.slice(2);
39
+ const queryIndex = cleaned.search(/[?#]/);
40
+ if (queryIndex !== -1) cleaned = cleaned.slice(0, queryIndex);
41
+ return cleaned;
42
+ }
43
+ function isApplicationFrame(path, functionName) {
44
+ if (path.length === 0) return false;
45
+ if (!path.includes("/") && !path.includes("\\")) return false;
46
+ if (containsKnownReactPackageSegment(path)) return false;
47
+ if (REJECT_PATH.test(path)) return false;
48
+ if (functionName !== void 0 && REJECT_FN.test(functionName)) return false;
49
+ return true;
50
+ }
51
+ function makeParsedFrame(rawFunctionName, rawFile, rawLine, rawColumn) {
52
+ if (rawFile === void 0 || rawFile.length === 0) return void 0;
53
+ const file = cleanSourcePath(rawFile);
54
+ if (file.length === 0) return void 0;
55
+ const line = rawLine === void 0 ? void 0 : safeParseInt(rawLine);
56
+ const column = rawColumn === void 0 ? void 0 : safeParseInt(rawColumn);
57
+ const functionName = rawFunctionName !== void 0 && rawFunctionName.length > 0 ? rawFunctionName : void 0;
58
+ return {
59
+ file,
60
+ ...line !== void 0 && { line },
61
+ ...column !== void 0 && { column },
62
+ ...functionName !== void 0 && { functionName }
63
+ };
64
+ }
65
+ function safeParseInt(value) {
66
+ const parsed = Number.parseInt(value, 10);
67
+ return Number.isFinite(parsed) ? parsed : void 0;
68
+ }
69
+ function containsKnownReactPackageSegment(path) {
70
+ const normalised = path.replace(/\\/g, "/");
71
+ for (const name of knownReactPackageNames) {
72
+ const marker = `/${name}/`;
73
+ const idx = normalised.indexOf(marker);
74
+ if (idx === -1) continue;
75
+ if (name.includes("-")) return true;
76
+ const tail = normalised.slice(idx + marker.length);
77
+ if (REACT_PACKAGE_BUNDLE_TAIL.test(tail)) return true;
78
+ }
79
+ return false;
80
+ }
81
+ //#endregion
82
+ //#region src/fiber-pipeline.ts
83
+ const REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref");
84
+ const REACT_MEMO_TYPE = Symbol.for("react.memo");
85
+ const REACT_LAZY_TYPE = Symbol.for("react.lazy");
86
+ const ABORT_MESSAGE = "anscribe.dispatcher-probe.abort";
87
+ const DISPATCHER_PROXY = new Proxy(Object.create(null), { get() {
88
+ throw new Error(ABORT_MESSAGE);
89
+ } });
90
+ const probeCache = /* @__PURE__ */ new WeakMap();
91
+ const renderableEnrichment = /* @__PURE__ */ new WeakMap();
92
+ let currentDispatcherRef;
93
+ let reactMetadataEnricherRegistered = false;
94
+ let reactRendererInjected = false;
95
+ function markReactMetadataEnricherRegistered() {
96
+ reactMetadataEnricherRegistered = true;
97
+ }
98
+ function markReactRendererInjected() {
99
+ reactRendererInjected = true;
100
+ }
101
+ function isReactRuntimeEnrichmentAvailable() {
102
+ return reactMetadataEnricherRegistered && reactRendererInjected;
103
+ }
104
+ function reactMetadataEnricher(input) {
105
+ return Effect.sync(() => {
106
+ const enrichment = isObject(input.renderable) ? renderableEnrichment.get(input.renderable) : void 0;
107
+ if (enrichment === void 0) return void 0;
108
+ return enrichment.sourceReferences.length > 0 ? {
109
+ metadata: enrichment.metadata,
110
+ sourceReferences: enrichment.sourceReferences
111
+ } : { metadata: enrichment.metadata };
112
+ });
113
+ }
114
+ function recordReactCommitRoot(root) {
115
+ const current = isObject(root) ? root.current : void 0;
116
+ if (current === void 0 || current === null) return;
117
+ walkFiber(current);
118
+ }
119
+ function walkFiber(fiber) {
120
+ recordFiber(fiber);
121
+ if (fiber.child !== void 0 && fiber.child !== null) walkFiber(fiber.child);
122
+ if (fiber.sibling !== void 0 && fiber.sibling !== null) walkFiber(fiber.sibling);
123
+ }
124
+ function recordFiber(fiber) {
125
+ const renderable = fiber.stateNode;
126
+ if (!isObject(renderable)) return;
127
+ const componentPath = readComponentPath(fiber);
128
+ const componentName = componentPath.at(-1);
129
+ if (componentName === void 0) return;
130
+ const sourceReferences = extractSourceReferences(fiber);
131
+ renderableEnrichment.set(renderable, {
132
+ metadata: {
133
+ componentName,
134
+ componentPath: componentPath.join(" > ")
135
+ },
136
+ sourceReferences
137
+ });
138
+ }
139
+ function readComponentPath(fiber) {
140
+ const path = [];
141
+ for (let current = fiber.return; current !== void 0 && current !== null; current = current.return) {
142
+ const name = readComponentName(current);
143
+ if (name !== void 0 && !isInternalComponentName(name)) path.push(name);
144
+ }
145
+ return path.reverse();
146
+ }
147
+ function readComponentName(fiber) {
148
+ const type = fiber.elementType ?? fiber.type;
149
+ if (typeof type === "string") return;
150
+ if (typeof type === "function") return readNamedFunction(type);
151
+ if (!isObject(type)) return;
152
+ const displayName = readString(type.displayName);
153
+ if (displayName !== void 0) return displayName;
154
+ const nestedType = type.type;
155
+ if (typeof nestedType === "function") return readNamedFunction(nestedType);
156
+ const render = type.render;
157
+ if (typeof render === "function") return readNamedFunction(render);
158
+ const contextDisplayName = isObject(type._context) ? readString(type._context.displayName) : void 0;
159
+ return contextDisplayName === void 0 ? void 0 : `${contextDisplayName}.Provider`;
160
+ }
161
+ function readNamedFunction(fn) {
162
+ return readString(fn.displayName) ?? readString(fn.name);
163
+ }
164
+ function isInternalComponentName(name) {
165
+ return name === "ErrorBoundary" || name === "AppContext.Provider" || name === "Context.Provider" || name === "Provider";
166
+ }
167
+ function extractSourceReferences(fiber) {
168
+ const direct = readFromDebugStack(fiber) ?? readFromOwnerDebugStack(fiber) ?? readFromOwnerDebugSource(fiber) ?? readFromJsxSource(fiber) ?? readFromDebugInfo(fiber);
169
+ if (direct !== void 0) return [direct];
170
+ const componentFunction = unwrapReactComponentFunction(fiber.type);
171
+ if (componentFunction === void 0) return [];
172
+ const cached = probeCache.get(componentFunction);
173
+ if (cached !== void 0) return cached.ref === null ? [] : [cached.ref];
174
+ const probed = probeComponentSource(componentFunction) ?? null;
175
+ probeCache.set(componentFunction, { ref: probed });
176
+ return probed === null ? [] : [probed];
177
+ }
178
+ function readFromDebugStack(fiber) {
179
+ const raw = fiber._debugStack;
180
+ const stack = stackToString(raw);
181
+ if (stack === void 0) return void 0;
182
+ const parsed = pickApplicationFrame(stack);
183
+ if (parsed === void 0) return void 0;
184
+ return makeSourceReference(parsed, "react-debug-stack");
185
+ }
186
+ function readFromOwnerDebugStack(fiber) {
187
+ let owner = fiber._debugOwner;
188
+ while (owner !== void 0 && owner !== null) {
189
+ const stack = stackToString(owner._debugStack);
190
+ if (stack !== void 0) {
191
+ const parsed = pickApplicationFrame(stack);
192
+ if (parsed !== void 0) return makeSourceReference(parsed, "react-debug-owner-stack", readComponentDisplayName(owner.type));
193
+ }
194
+ owner = owner._debugOwner;
195
+ }
196
+ }
197
+ function readFromOwnerDebugSource(fiber) {
198
+ let owner = fiber._debugOwner;
199
+ while (owner !== void 0 && owner !== null) {
200
+ const source = readSourceFields(owner._debugSource);
201
+ if (source !== void 0) return makeSourceReference(source, "react-debug-owner", readComponentDisplayName(owner.type));
202
+ owner = owner._debugOwner;
203
+ }
204
+ }
205
+ function readFromJsxSource(fiber) {
206
+ const props = fiber.memoizedProps;
207
+ if (!isObject(props)) return void 0;
208
+ const parsed = readSourceFields(props.__source);
209
+ if (parsed === void 0) return void 0;
210
+ return makeSourceReference(parsed, "jsx-runtime-source", readSelfConstructorName(props.__self));
211
+ }
212
+ function readFromDebugInfo(fiber) {
213
+ const info = fiber._debugInfo;
214
+ if (!Array.isArray(info)) return void 0;
215
+ for (const entry of info) {
216
+ if (!isObject(entry)) continue;
217
+ const stack = entry.stack;
218
+ if (typeof stack === "string" && stack.length > 0) {
219
+ const parsed = pickApplicationFrame(stack);
220
+ if (parsed !== void 0) return makeSourceReference(parsed, "react-debug-info", readComponentDisplayName(entry.name ?? void 0));
221
+ }
222
+ const owner = entry.owner;
223
+ if (isObject(owner)) {
224
+ const source = readSourceFields(owner._debugSource);
225
+ if (source !== void 0) return makeSourceReference(source, "react-debug-info", readComponentDisplayName(owner.type ?? entry.name));
226
+ }
227
+ }
228
+ }
229
+ function stackToString(value) {
230
+ if (typeof value === "string" && value.length > 0) return value;
231
+ if (value instanceof Error && typeof value.stack === "string") return value.stack;
232
+ if (isObject(value) && typeof value.stack === "string") return value.stack;
233
+ }
234
+ function pickApplicationFrame(stack) {
235
+ for (const line of stack.split("\n")) {
236
+ const parsed = parseStackFrame(line);
237
+ if (parsed === void 0) continue;
238
+ if (parsed.file === void 0) continue;
239
+ if (!isApplicationFrame(parsed.file, parsed.functionName)) continue;
240
+ return parsed;
241
+ }
242
+ }
243
+ function readSourceFields(raw) {
244
+ if (!isObject(raw)) return void 0;
245
+ const fileName = raw.fileName;
246
+ if (typeof fileName !== "string" || fileName.length === 0) return void 0;
247
+ const cleaned = cleanSourcePath(fileName);
248
+ if (cleaned.length === 0) return void 0;
249
+ const lineNumber = raw.lineNumber;
250
+ const columnNumber = raw.columnNumber;
251
+ return {
252
+ file: cleaned,
253
+ ...typeof lineNumber === "number" && Number.isFinite(lineNumber) && { line: lineNumber },
254
+ ...typeof columnNumber === "number" && Number.isFinite(columnNumber) && { column: columnNumber }
255
+ };
256
+ }
257
+ function readSelfConstructorName(value) {
258
+ if (!isObject(value)) return void 0;
259
+ const ctor = value.constructor;
260
+ if (typeof ctor !== "function") return void 0;
261
+ const name = ctor.name;
262
+ return typeof name === "string" && name.length > 0 ? name : void 0;
263
+ }
264
+ function makeSourceReference(parsed, origin, componentName) {
265
+ return new SourceReference({
266
+ ...parsed.file !== void 0 && { file: parsed.file },
267
+ ...parsed.line !== void 0 && { line: parsed.line },
268
+ ...parsed.column !== void 0 && { column: parsed.column },
269
+ ...parsed.functionName !== void 0 && { functionName: parsed.functionName },
270
+ ...componentName !== void 0 && { componentName },
271
+ origin
272
+ });
273
+ }
274
+ function unwrapReactComponentFunction(value) {
275
+ let current = value;
276
+ const seen = /* @__PURE__ */ new Set();
277
+ while (current !== null && current !== void 0 && !seen.has(current)) {
278
+ seen.add(current);
279
+ if (typeof current === "function") return current;
280
+ if (typeof current !== "object") return;
281
+ const wrapper = current;
282
+ if (wrapper.$$typeof === REACT_FORWARD_REF_TYPE) {
283
+ current = wrapper.render;
284
+ continue;
285
+ }
286
+ if (wrapper.$$typeof === REACT_MEMO_TYPE) {
287
+ current = wrapper.type;
288
+ continue;
289
+ }
290
+ if (wrapper.$$typeof === REACT_LAZY_TYPE) {
291
+ const init = wrapper._init;
292
+ if (typeof init !== "function") return;
293
+ try {
294
+ current = init(wrapper._payload);
295
+ continue;
296
+ } catch {
297
+ return;
298
+ }
299
+ }
300
+ return;
301
+ }
302
+ }
303
+ /**
304
+ * Record the `currentDispatcherRef` that the React reconciler hands to
305
+ * `__REACT_DEVTOOLS_GLOBAL_HOOK__.inject`. That object is the live
306
+ * `ReactSharedInternals` of the React instance the application renders with,
307
+ * so reading the dispatcher slot from it always targets the right copy of
308
+ * React even when more than one is resolvable on disk.
309
+ *
310
+ * @internal
311
+ */
312
+ function setReactCurrentDispatcherRef(ref) {
313
+ if (isObject(ref)) currentDispatcherRef = ref;
314
+ }
315
+ function probeComponentSource(componentFunction) {
316
+ if (isClassComponent(componentFunction)) return void 0;
317
+ const inner = unwrapReactComponentFunction(componentFunction);
318
+ if (inner === void 0) return void 0;
319
+ const slot = locateDispatcherSlot();
320
+ if (slot === void 0) return void 0;
321
+ const original = slot.root[slot.key];
322
+ slot.root[slot.key] = DISPATCHER_PROXY;
323
+ let stack;
324
+ try {
325
+ inner({});
326
+ } catch (error) {
327
+ stack = error instanceof Error && typeof error.stack === "string" ? error.stack : void 0;
328
+ } finally {
329
+ slot.root[slot.key] = original;
330
+ }
331
+ if (stack === void 0) return void 0;
332
+ for (const line of stack.split("\n")) {
333
+ const parsed = parseStackFrame(line);
334
+ if (parsed === void 0 || parsed.file === void 0) continue;
335
+ if (!isApplicationFrame(parsed.file, parsed.functionName)) continue;
336
+ return new SourceReference({
337
+ file: parsed.file,
338
+ ...parsed.line !== void 0 && { line: parsed.line },
339
+ ...parsed.column !== void 0 && { column: parsed.column },
340
+ ...parsed.functionName !== void 0 && { functionName: parsed.functionName },
341
+ origin: "dispatcher-probe"
342
+ });
343
+ }
344
+ }
345
+ function isClassComponent(value) {
346
+ const prototype = value.prototype;
347
+ if (prototype === null || typeof prototype !== "object") return false;
348
+ return prototype.isReactComponent !== void 0;
349
+ }
350
+ function locateDispatcherSlot() {
351
+ const ref = currentDispatcherRef;
352
+ if (ref === void 0) return void 0;
353
+ if ("H" in ref) return {
354
+ root: ref,
355
+ key: "H"
356
+ };
357
+ const legacyDispatcher = ref.ReactCurrentDispatcher;
358
+ if (isObject(legacyDispatcher) && "current" in legacyDispatcher) return {
359
+ root: legacyDispatcher,
360
+ key: "current"
361
+ };
362
+ }
363
+ function readComponentDisplayName(type) {
364
+ if (typeof type === "string" && type.length > 0) return type;
365
+ if (typeof type === "function") {
366
+ const fn = type;
367
+ if (typeof fn.displayName === "string" && fn.displayName.length > 0) return fn.displayName;
368
+ if (typeof fn.name === "string" && fn.name.length > 0) return fn.name;
369
+ }
370
+ if (isObject(type)) {
371
+ const obj = type;
372
+ if (typeof obj.displayName === "string" && obj.displayName.length > 0) return obj.displayName;
373
+ }
374
+ }
375
+ function isObject(value) {
376
+ return value !== null && typeof value === "object";
377
+ }
378
+ function readString(value) {
379
+ return typeof value === "string" && value.length > 0 ? value : void 0;
380
+ }
381
+ //#endregion
382
+ export { recordReactCommitRoot as a, reactMetadataEnricher as i, markReactMetadataEnricherRegistered as n, setReactCurrentDispatcherRef as o, markReactRendererInjected as r, recordReactRendererPackageName as s, isReactRuntimeEnrichmentAvailable as t };
@@ -0,0 +1,11 @@
1
+ import { CaptureEnrichmentOutput, CapturedTarget, SourceReference } from "@anscribe/core";
2
+ import { Effect } from "effect";
3
+
4
+ //#region src/fiber-pipeline.d.ts
5
+ declare function isReactRuntimeEnrichmentAvailable(): boolean;
6
+ declare function reactMetadataEnricher(input: {
7
+ renderable: unknown;
8
+ target: CapturedTarget;
9
+ }): Effect.Effect<CaptureEnrichmentOutput | undefined>;
10
+ //#endregion
11
+ export { isReactRuntimeEnrichmentAvailable, reactMetadataEnricher };
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { i as reactMetadataEnricher, t as isReactRuntimeEnrichmentAvailable } from "./fiber-pipeline-OQRkNJFF.mjs";
2
+ export { isReactRuntimeEnrichmentAvailable, reactMetadataEnricher };
@@ -0,0 +1,23 @@
1
+ //#region src/preload.d.ts
2
+ type ReactDevToolsRenderer = Record<string, unknown>;
3
+ type ReactDevToolsHook = {
4
+ supportsFiber?: boolean;
5
+ hasUnsupportedRendererAttached?: boolean;
6
+ renderers?: Map<number, ReactDevToolsRenderer>;
7
+ inject?: (renderer: ReactDevToolsRenderer) => number;
8
+ onCommitFiberRoot?: (rendererId: number, root: unknown, priorityLevel?: unknown, didError?: unknown) => void;
9
+ onScheduleFiberRoot?: (rendererId: number, root: unknown, children: unknown) => void;
10
+ onPostCommitFiberRoot?: (rendererId: number, root: unknown) => void;
11
+ onCommitFiberUnmount?: (rendererId: number, fiber: unknown) => void;
12
+ checkDCE?: (fn: unknown) => void;
13
+ on?: (...args: unknown[]) => void;
14
+ off?: (...args: unknown[]) => void;
15
+ sub?: (...args: unknown[]) => () => void;
16
+ [key: symbol]: true | undefined;
17
+ };
18
+ declare global {
19
+ var __REACT_DEVTOOLS_GLOBAL_HOOK__: ReactDevToolsHook | undefined;
20
+ }
21
+ declare function installReactPreloadHook(): ReactDevToolsHook;
22
+ //#endregion
23
+ export { installReactPreloadHook };
@@ -0,0 +1,76 @@
1
+ import { a as recordReactCommitRoot, n as markReactMetadataEnricherRegistered, o as setReactCurrentDispatcherRef, r as markReactRendererInjected, s as recordReactRendererPackageName } from "./fiber-pipeline-OQRkNJFF.mjs";
2
+ //#region src/preload.ts
3
+ const PATCHED = Symbol.for("anscribe.react.preload.patched");
4
+ const ENRICHER_REGISTERED = Symbol.for("anscribe.react.preload.enricherRegistered");
5
+ const noop = () => {};
6
+ installReactPreloadHook();
7
+ function installReactPreloadHook() {
8
+ const hook = getOrCreateHook();
9
+ if (hook[PATCHED] === true) {
10
+ registerReactMetadataEnricher(hook);
11
+ return hook;
12
+ }
13
+ hook[PATCHED] = true;
14
+ registerReactMetadataEnricher(hook);
15
+ hook.supportsFiber = true;
16
+ hook.hasUnsupportedRendererAttached = false;
17
+ hook.renderers ??= /* @__PURE__ */ new Map();
18
+ for (const renderer of hook.renderers.values()) {
19
+ markReactRendererInjected();
20
+ captureDispatcherRef(renderer);
21
+ }
22
+ hook.on ??= noop;
23
+ hook.off ??= noop;
24
+ hook.sub ??= () => noop;
25
+ hook.checkDCE ??= noop;
26
+ hook.onCommitFiberUnmount ??= noop;
27
+ hook.onPostCommitFiberRoot ??= noop;
28
+ hook.onScheduleFiberRoot ??= noop;
29
+ const originalInject = hook.inject;
30
+ let nextRendererId = Math.max(0, ...hook.renderers.keys());
31
+ hook.inject = (renderer) => {
32
+ const rendererId = originalInject?.call(hook, renderer) ?? ++nextRendererId;
33
+ hook.renderers?.set(rendererId, renderer);
34
+ markReactRendererInjected();
35
+ captureDispatcherRef(renderer);
36
+ return rendererId;
37
+ };
38
+ const originalOnCommitFiberRoot = hook.onCommitFiberRoot;
39
+ hook.onCommitFiberRoot = (rendererId, root, priorityLevel, didError) => {
40
+ recordReactCommitRoot(root);
41
+ originalOnCommitFiberRoot?.call(hook, rendererId, root, priorityLevel, didError);
42
+ };
43
+ return hook;
44
+ }
45
+ function captureDispatcherRef(renderer) {
46
+ const ref = renderer.currentDispatcherRef;
47
+ setReactCurrentDispatcherRef(ref);
48
+ const name = renderer.rendererPackageName;
49
+ recordReactRendererPackageName(name);
50
+ }
51
+ function registerReactMetadataEnricher(hook) {
52
+ if (hook[ENRICHER_REGISTERED] === true) return;
53
+ hook[ENRICHER_REGISTERED] = true;
54
+ markReactMetadataEnricherRegistered();
55
+ }
56
+ function getOrCreateHook() {
57
+ const existingHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
58
+ if (existingHook !== void 0) return existingHook;
59
+ const hook = {
60
+ supportsFiber: true,
61
+ hasUnsupportedRendererAttached: false,
62
+ renderers: /* @__PURE__ */ new Map(),
63
+ on: noop,
64
+ off: noop,
65
+ sub: () => noop,
66
+ onCommitFiberRoot: noop,
67
+ onScheduleFiberRoot: noop,
68
+ onCommitFiberUnmount: noop,
69
+ onPostCommitFiberRoot: noop,
70
+ checkDCE: noop
71
+ };
72
+ globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ = hook;
73
+ return hook;
74
+ }
75
+ //#endregion
76
+ export { installReactPreloadHook };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@anscribe/react",
3
+ "version": "0.1.0",
4
+ "description": "Anscribe React adapter: drop-in <Anscribe /> component and hooks for capture mode in React TUIs.",
5
+ "keywords": [
6
+ "agent",
7
+ "anscribe",
8
+ "capture",
9
+ "mcp",
10
+ "opentui",
11
+ "react",
12
+ "tui"
13
+ ],
14
+ "homepage": "https://github.com/msmps/anscribe",
15
+ "bugs": {
16
+ "url": "https://github.com/msmps/anscribe/issues"
17
+ },
18
+ "license": "MIT",
19
+ "author": "msmps",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/msmps/anscribe.git",
23
+ "directory": "packages/react"
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "type": "module",
29
+ "exports": {
30
+ "./package.json": "./package.json",
31
+ ".": {
32
+ "types": "./dist/index.d.mts",
33
+ "import": "./dist/index.mjs"
34
+ },
35
+ "./preload": {
36
+ "types": "./dist/preload.d.mts",
37
+ "import": "./dist/preload.mjs"
38
+ }
39
+ },
40
+ "publishConfig": {
41
+ "access": "public",
42
+ "provenance": true,
43
+ "registry": "https://registry.npmjs.org"
44
+ },
45
+ "dependencies": {
46
+ "effect": "^4.0.0-beta.65",
47
+ "@anscribe/core": "0.1.0"
48
+ },
49
+ "peerDependencies": {
50
+ "react": ">=19.2.0"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "react": {
54
+ "optional": true
55
+ }
56
+ },
57
+ "scripts": {
58
+ "build": "tsdown",
59
+ "test": "vitest run"
60
+ }
61
+ }