@termwright/probe-ink 0.2.0 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/instrument.ts"],"sourcesContent":["/** The render wrapper imported by the replacement Ink entry module. */\n\nimport { Stream } from 'node:stream';\nimport { createElement, Fragment, type ComponentType, type ReactNode } from 'react';\nimport type { DOMElement, Instance, RenderOptions } from 'ink';\nimport type { AdapterCapability } from '@termwright/protocol';\nimport { ENV_ENDPOINT, ENV_PROTOCOL, ENV_TOKEN, PROTOCOL_V2_ID } from '@termwright/protocol';\nimport { connectProbe, type ProbeChannel } from '@termwright/probe-runtime';\nimport { canPublishInkGeometry } from './geometry.js';\nimport type { InkDomElement, MeasureElement } from './observe.js';\nimport { createInkSession, probeInfo, type InkProbeSession } from './session.js';\nimport { isInstrumented } from './runtime.js';\nimport type { EnvSource } from './runtime.js';\nimport { PACKAGE_VERSION } from './version.js';\nimport { onInkAnnotationChange } from './annotations.js';\n\nconst ADAPTER_NAME = '@termwright/probe-ink';\nconst ADAPTER_VERSION = PACKAGE_VERSION;\n\ntype InkRender = (\n node: ReactNode,\n options?: NodeJS.WriteStream | RenderOptions,\n) => Instance;\n\n/** Runtime Ink surface forwarded by the shim, kept structural to avoid cycles. */\nexport interface InkModule {\n readonly render: InkRender;\n readonly Box: ComponentType<Record<string, unknown>>;\n readonly measureElement: MeasureElement;\n}\n\n/** @internal Used only by the in-process component harness. */\nexport interface InstrumentInkOptions {\n readonly env?: EnvSource;\n}\n\n/**\n * Wrap the original `ink.render` while preserving every other Ink export.\n * With no complete driver environment this function calls the original render\n * directly, without even creating the hidden ref used by an active session.\n */\nexport function wrapInkRender(ink: InkModule, options: InstrumentInkOptions = {}): InkRender {\n const env = options.env ?? process.env;\n const wrapped: InkRender = (node, suppliedOptions) => {\n if (!isInstrumented(env)) return ink.render(node, suppliedOptions);\n\n try {\n return instrumentedRender(ink, node, suppliedOptions, env);\n } catch {\n // Setup failures are probe failures. The application still gets its\n // ordinary render rather than inheriting our exception.\n return ink.render(node, suppliedOptions);\n }\n };\n Object.defineProperty(wrapped, '__termwright__', { value: true });\n return wrapped;\n}\n\nfunction instrumentedRender(\n ink: InkModule,\n node: ReactNode,\n suppliedOptions: NodeJS.WriteStream | RenderOptions | undefined,\n env: EnvSource,\n): Instance {\n const options = normalizeOptions(suppliedOptions);\n const stdout = options.stdout ?? process.stdout;\n const probeRef: { current: DOMElement | null } = { current: null };\n const state: { channel: ProbeChannel | null; session: InkProbeSession | null } = {\n channel: null,\n session: null,\n };\n let disposed = false;\n const releaseAnnotations = onInkAnnotationChange(() => state.session?.notifyRender());\n\n const wrap = (child: ReactNode): ReactNode => createElement(\n Fragment,\n null,\n createElement(ink.Box, { ref: probeRef, display: 'none' }),\n child,\n );\n\n const userOnRender = options.onRender;\n const instance = ink.render(wrap(node), {\n ...options,\n onRender(metrics) {\n try {\n // Freeze the committed host tree before an application callback can\n // synchronously schedule or flush another update.\n state.session?.notifyRender();\n } catch {\n state.session?.stop();\n }\n userOnRender?.(metrics);\n },\n });\n\n const includeGeometry = canPublishInkGeometry({\n alternateScreen: options.alternateScreen === true,\n ...(options.interactive === undefined ? {} : { interactive: options.interactive }),\n stdoutIsTTY: stdout.isTTY === true,\n });\n const baseCapabilities: readonly AdapterCapability[] = includeGeometry\n ? ['tree', 'bounds', 'absolute-bounds', 'states', 'actions', 'render-revisions']\n : ['tree', 'states', 'actions', 'render-revisions'];\n const qualified = env[ENV_PROTOCOL] === PROTOCOL_V2_ID;\n const capabilities: readonly AdapterCapability[] = qualified\n ? [...baseCapabilities, 'qualified-observations']\n : baseCapabilities;\n\n const connection = connectProbe({\n endpoint: env[ENV_ENDPOINT] as string,\n token: env[ENV_TOKEN] as string,\n probe: probeInfo(),\n capabilities,\n adapterName: ADAPTER_NAME,\n adapterVersion: ADAPTER_VERSION,\n ...(qualified ? { protocol: PROTOCOL_V2_ID } : {}),\n })\n .then(async (channel) => {\n if (channel === null || disposed) {\n channel?.close();\n return;\n }\n state.channel = channel;\n state.session = createInkSession({\n channel,\n resolveRoot: () => (probeRef.current?.parentNode as InkDomElement | undefined) ?? null,\n resolveExcluded: () => probeRef.current as InkDomElement | null,\n measureElement: ink.measureElement,\n stdout,\n includeGeometry,\n });\n // The first commit may have beaten the handshake, but the live host tree\n // can already contain a throttled commit whose bytes are not on screen.\n // Flush Ink first. If that emits onRender, the newly-installed session\n // captures it there; otherwise the stable current tree is a safe catch-up.\n try {\n await instance.waitUntilRenderFlush();\n } catch {\n state.session.stop();\n return;\n }\n if (!disposed && state.session.frames === 0) state.session.notifyRender();\n })\n .catch(() => undefined);\n\n const stop = (): void => {\n if (disposed) return;\n disposed = true;\n releaseAnnotations();\n state.session?.stop();\n state.channel?.close();\n };\n\n // Natural `useApp().exit()` does not call our wrapped cleanup. Await the\n // attach attempt and the exact publication queue instead of guessing a\n // teardown delay; a slow stdout must not lose its final marker.\n void instance.waitUntilExit()\n .catch(() => undefined)\n .then(async () => {\n await connection;\n await state.session?.flush();\n stop();\n })\n .catch(stop);\n\n return {\n ...instance,\n rerender(next) {\n instance.rerender(wrap(next));\n },\n unmount(error?: unknown) {\n return instance.unmount(error as Parameters<Instance['unmount']>[0]);\n },\n cleanup() {\n stop();\n instance.cleanup();\n },\n };\n}\n\nfunction normalizeOptions(\n supplied: NodeJS.WriteStream | RenderOptions | undefined,\n): RenderOptions {\n if (supplied === undefined) return {};\n // Match Ink's own `getOptions` test exactly. A merely stream-shaped options\n // object must not gain different semantics only because the probe is active.\n if (supplied instanceof Stream) {\n return { stdout: supplied as NodeJS.WriteStream };\n }\n return supplied as RenderOptions;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/instrument.ts","../src/frame-capture.ts","../src/terminal-tracker.ts","../src/render-boundary.ts"],"sourcesContent":["/** The render wrapper imported by the replacement Ink entry module. */\n\nimport { Stream } from 'node:stream';\nimport { createElement, Fragment, type ComponentType, type ReactNode } from 'react';\nimport type { DOMElement, Instance, RenderOptions } from 'ink';\nimport type { AdapterCapability } from '@termwright/protocol';\nimport { ENV_ENDPOINT, ENV_TOKEN } from '@termwright/protocol';\nimport { connectProbe, type ProbeChannel } from '@termwright/probe-runtime';\nimport type { InkDomElement, MeasureElement } from './observe.js';\nimport {\n createInkMarkerWriter,\n createInkSession,\n probeInfo,\n type InkProbeSession,\n} from './session.js';\nimport { isInstrumented } from './runtime.js';\nimport type { EnvSource } from './runtime.js';\nimport { PACKAGE_VERSION } from './version.js';\nimport {\n captureInkLayout,\n capturedInkFrame,\n installInkCaptureHook,\n retainInkFrame,\n} from './frame-capture.js';\nimport { instrumentationSentinel } from './instrumentation.js';\nimport { trackTerminal, type InkTerminalTracker } from './terminal-tracker.js';\nimport { onInkAnnotationChange } from './annotations.js';\nimport { RenderBoundaryQueue } from './render-boundary.js';\nimport {\n acquireReactCommitBridge,\n activateInkRendererObservation,\n type ReactCommitBridge,\n type InkReconcilerInstrumentation,\n} from './react-commit-bridge.js';\n\n/**\n * Shadowing starts when this module is evaluated, not when render() runs.\n *\n * Import evaluation precedes the importing module's body, so cursor movement,\n * alternate-buffer changes and positioning written before the first render are\n * included in the same terminal shadow used to place the captured Ink frame.\n * Starting at render() would lose that prefix and derive incorrect bounds.\n * Pointer and focus modes are intentionally not inferred here: fd/native and\n * descendant writes bypass this JavaScript stream wrapper, so such evidence\n * would not be authoritative for an opaque child.\n */\nconst processTracker = trackTerminal(process.stdout, process.stderr);\n\nconst ADAPTER_NAME = '@termwright/probe-ink';\nconst ADAPTER_VERSION = PACKAGE_VERSION;\nconst INK_CAPABILITIES: readonly AdapterCapability[] = [\n 'tree',\n 'intended-geometry',\n 'clipped-geometry',\n 'states',\n 'actions',\n 'render-revisions',\n];\n\n/** Private cross-package hook used by the shipped fixture runner. */\nexport const INK_FLUSH_NEXT_RENDER = Symbol.for('@termwright/probe-ink/flush-next-render');\nconst COMMIT_GENERATION_ATTRIBUTE = '__termwrightCommitGeneration';\n\ntype InkRender = (node: ReactNode, options?: NodeJS.WriteStream | RenderOptions) => Instance;\n\n/** Runtime Ink surface forwarded by the shim, kept structural to avoid cycles. */\nexport interface InkModule {\n readonly render: InkRender;\n readonly Box: ComponentType<Record<string, unknown>>;\n readonly measureElement: MeasureElement;\n}\n\n/** @internal Used only by the in-process component harness. */\nexport interface InstrumentInkOptions {\n readonly env?: EnvSource;\n /** Exact pinned in-process harness; never enabled by the application shim. */\n readonly certifiedHarness?: boolean;\n /** Ink's existing React reconciler instrumentation seam. */\n readonly reconciler?: InkReconcilerInstrumentation;\n /** @internal Deterministic transport seam for setup-failure tests. */\n readonly connect?: typeof connectProbe;\n}\n\n/**\n * Wrap the original `ink.render` while preserving every other Ink export.\n * With no complete driver environment this function calls the original render\n * directly, without even creating the hidden ref used by an active session.\n */\nexport function wrapInkRender(ink: InkModule, options: InstrumentInkOptions = {}): InkRender {\n const env = options.env ?? process.env;\n const wrapped: InkRender = (node, suppliedOptions) => {\n if (!isInstrumented(env)) return ink.render(node, suppliedOptions);\n return instrumentedRender(\n ink,\n node,\n suppliedOptions,\n env,\n options.certifiedHarness === true,\n options.reconciler,\n options.connect ?? connectProbe,\n );\n };\n Object.defineProperty(wrapped, '__termwright__', { value: true });\n return wrapped;\n}\n\nfunction instrumentedRender(\n ink: InkModule,\n node: ReactNode,\n suppliedOptions: NodeJS.WriteStream | RenderOptions | undefined,\n env: EnvSource,\n certifiedHarness: boolean,\n reconciler: InkReconcilerInstrumentation | undefined,\n connector: typeof connectProbe,\n): Instance {\n // A modified or unsupported Ink artifact is never observed through a weaker\n // path. The driver sees no adapter and required semantics fail negotiation.\n const certifiedRuntime = instrumentationSentinel() !== undefined;\n if (!certifiedRuntime && !certifiedHarness) return ink.render(node, suppliedOptions);\n let options: RenderOptions;\n try {\n options = normalizeOptions(suppliedOptions);\n } catch (error) {\n return renderAfterSetupFailure(ink, node, suppliedOptions, connector, env, error);\n }\n let currentNode = node;\n let commitGeneration = 0;\n const stdout = options.stdout ?? process.stdout;\n const stderr = options.stderr ?? process.stderr;\n // Reuse the shadow that has been running since import when render() writes\n // to the streams it already watches, which is the default and the only case\n // where earlier bytes exist to have been missed.\n const ownsTracker = stdout !== process.stdout || stderr !== process.stderr;\n let tracker: InkTerminalTracker = processTracker;\n const probeRef: { current: DOMElement | null } = { current: null };\n const state: { channel: ProbeChannel | null; session: InkProbeSession | null } = {\n channel: null,\n session: null,\n };\n let disposed = false;\n const renderBoundaries = new RenderBoundaryQueue();\n let reactRoot: InkDomElement | null = null;\n let reactBridge: ReactCommitBridge | undefined;\n let releaseCapture: (() => void) | undefined;\n let releaseReactBridge: (() => void) | undefined;\n let releaseReactBridgeHook: (() => void) | undefined;\n let releaseAnnotations: (() => void) | undefined;\n\n const stop = (): void => {\n if (disposed) return;\n disposed = true;\n renderBoundaries.stop();\n releaseCapture?.();\n releaseReactBridge?.();\n releaseReactBridgeHook?.();\n releaseAnnotations?.();\n if (ownsTracker) tracker.stop();\n state.session?.stop();\n state.channel?.close();\n };\n\n try {\n releaseCapture = installInkCaptureHook();\n tracker = ownsTracker ? trackTerminal(stdout, stderr) : processTracker;\n if (reconciler !== undefined) {\n const bridgeLease = acquireReactCommitBridge();\n reactBridge = bridgeLease.bridge;\n releaseReactBridgeHook = bridgeLease.release;\n // Ink's DEV constructor invokes this exact reconciler seam itself. A\n // direct call here would double-inject into an existing user hook.\n if (env['DEV'] !== 'true') {\n reactBridge = activateInkRendererObservation(reconciler);\n }\n releaseReactBridge = reactBridge.subscribe((event) => {\n // Several ink.render() roots can coexist. Correlate through the\n // sentinel host instead of treating the latest global commit as ours.\n if (event.type === 'commit' && probeRef.current?.parentNode === event.root) {\n reactRoot = event.root;\n }\n });\n }\n releaseAnnotations = onInkAnnotationChange(() => {\n // React layout-effect cleanup/registration can run while Ink is still\n // committing. The renderer's onRender precedes this deterministic\n // annotation-only catch-up publication.\n setImmediate(() => {\n if (!disposed) state.session?.notifyRender({ allowUnsettled: true });\n });\n });\n } catch (error) {\n stop();\n return renderAfterSetupFailure(ink, node, suppliedOptions, connector, env, error);\n }\n\n const wrap = (child: ReactNode): ReactNode =>\n createElement(\n Fragment,\n null,\n createElement(ink.Box, {\n ref: probeRef,\n display: 'none',\n [COMMIT_GENERATION_ATTRIBUTE]: commitGeneration,\n }),\n child,\n );\n\n const userOnRender = options.onRender;\n let instrumentedNode: ReactNode;\n let instrumentedOptions: RenderOptions;\n try {\n instrumentedNode = wrap(node);\n instrumentedOptions = {\n ...options,\n onRender(metrics) {\n // Box forwards non-layout metadata through the host style object. The\n // hidden sentinel is excluded from semantic observation, but its host\n // commit still gives this callback a synchronous causal generation.\n const generation = (probeRef.current as InkDomElement | null)?.style?.[\n COMMIT_GENERATION_ATTRIBUTE\n ];\n const boundary =\n typeof generation === 'number' ? renderBoundaries.take(generation) : undefined;\n try {\n if (!certifiedRuntime) {\n const root = (probeRef.current?.parentNode as InkDomElement | undefined) ?? null;\n if (root !== null) {\n const measured = ink.measureElement(root);\n const staticNode = root.staticNode;\n const staticRows =\n staticNode === undefined ? 0 : ink.measureElement(staticNode).height;\n retainInkFrame(\n captureInkLayout(\n root,\n {\n output: '',\n outputHeight: measured.height,\n staticOutput: '\\n'.repeat(staticRows),\n },\n {\n interactive: options.interactive === true,\n alternateScreen: options.alternateScreen === true,\n debug: options.debug === true,\n stdoutIsTTY: stdout.isTTY === true,\n rows: stdout.rows ?? 24,\n },\n ),\n );\n }\n }\n // Freeze the committed host tree before an application callback can\n // synchronously schedule or flush another update.\n const publication = state.session?.notifyRender({\n awaitPublication: boundary !== undefined,\n });\n if (boundary !== undefined) {\n if (publication === undefined) {\n boundary.reject(new Error('Ink semantic session is not attached'));\n } else {\n void publication.then(\n (revision) => {\n if (revision === null) boundary.reject(new Error('Ink render was not published'));\n else boundary.resolve(revision);\n },\n (error) =>\n boundary.reject(error instanceof Error ? error : new Error(String(error))),\n );\n }\n }\n } catch (error) {\n boundary?.reject(error instanceof Error ? error : new Error(String(error)));\n state.session?.stop();\n }\n userOnRender?.(metrics);\n },\n };\n } catch (error) {\n stop();\n return renderAfterSetupFailure(ink, node, suppliedOptions, connector, env, error);\n }\n let instance: Instance;\n try {\n instance = ink.render(instrumentedNode, instrumentedOptions);\n } catch (error) {\n // The application render is never retried. Preserve the exact thrown\n // object and release every probe resource acquired before the call.\n stop();\n throw error;\n }\n\n if (reconciler !== undefined && reactBridge?.hasInkRenderer() !== true) {\n const error = new Error(\n 'Ink semantic probe unavailable: React renderer instrumentation did not register Ink.',\n );\n stop();\n reportSetupFailure(connector, env, error);\n return instance;\n }\n\n let connection: Promise<void>;\n try {\n connection = connector({\n endpoint: env[ENV_ENDPOINT] as string,\n token: env[ENV_TOKEN] as string,\n probe: probeInfo(),\n capabilities: INK_CAPABILITIES,\n adapterName: ADAPTER_NAME,\n adapterVersion: ADAPTER_VERSION,\n })\n .then(async (channel) => {\n if (channel === null || disposed) {\n channel?.close();\n return;\n }\n state.channel = channel;\n state.session = createInkSession({\n channel,\n // The React bridge observes the same committed Ink host root through\n // FiberRoot.containerInfo. Keep the hidden ref as the certified\n // control while differential conformance is still in progress.\n resolveRoot: () =>\n reactRoot ?? (probeRef.current?.parentNode as InkDomElement | undefined) ?? null,\n resolveExcluded: () => probeRef.current as InkDomElement | null,\n resolveCapture: (root) => capturedInkFrame(root),\n waitForRenderFlush: () => instance.waitUntilRenderFlush(),\n stdout,\n writeMarker: createInkMarkerWriter(stdout, { certifiedHarness }),\n tracker,\n onGuaranteeViolation: (error) => {\n state.session?.stop();\n channel.fail('adapter-guarantee-violation', error.message);\n // `fail()` owns transport termination. Keep the guard explicit so a\n // future channel implementation cannot leave a failed producer\n // attached and silently downgrade semantic coverage.\n if (channel.isOpen) channel.close();\n },\n });\n // The first commit may have beaten the handshake, but the live host tree\n // can already contain a throttled commit whose bytes are not on screen.\n // Drain that work, then bind a fresh hidden-host generation to one real\n // rerender. A bare `rerender(); waitUntilRenderFlush()` is not causal:\n // Ink may satisfy the wait with the render that was already pending,\n // leaving the new commit without an onRender publication. That race was\n // observable on Windows under Node 22 as a negotiated adapter with no\n // authoritative first tree.\n try {\n await renderBoundaries.afterCurrentRender(\n () => instance.waitUntilRenderFlush(),\n (generation) => {\n commitGeneration = generation;\n instance.rerender(wrap(currentNode));\n },\n );\n } catch {\n state.session.stop();\n return;\n }\n })\n .catch(() => undefined);\n } catch (error) {\n stop();\n reportSetupFailure(connector, env, error);\n return instance;\n }\n\n // Natural `useApp().exit()` does not call our wrapped cleanup. Await the\n // attach attempt and the exact publication queue instead of guessing a\n // teardown delay; a slow stdout must not lose its final marker.\n void instance\n .waitUntilExit()\n .catch(() => undefined)\n .then(async () => {\n // An exited renderer cannot complete an armed commit. Reject its causal\n // boundary before awaiting the attach promise, otherwise teardown can\n // wait on a render which Ink can no longer produce.\n renderBoundaries.stop();\n await connection;\n await state.session?.flush();\n stop();\n })\n .catch(stop);\n\n const wrappedInstance: Instance & {\n [INK_FLUSH_NEXT_RENDER](mutate: () => void): Promise<number>;\n } = {\n ...instance,\n rerender(next) {\n currentNode = next;\n instance.rerender(wrap(next));\n },\n unmount(error?: unknown) {\n return instance.unmount(error as Parameters<Instance['unmount']>[0]);\n },\n cleanup() {\n stop();\n instance.cleanup();\n },\n [INK_FLUSH_NEXT_RENDER](mutate: () => void): Promise<number> {\n return renderBoundaries.afterCurrentRender(\n () => instance.waitUntilRenderFlush(),\n (generation) => {\n commitGeneration = generation;\n mutate();\n },\n );\n },\n };\n return wrappedInstance;\n}\n\nfunction normalizeOptions(supplied: NodeJS.WriteStream | RenderOptions | undefined): RenderOptions {\n if (supplied === undefined) return {};\n // Match Ink's own `getOptions` test exactly. A merely stream-shaped options\n // object must not gain different semantics only because the probe is active.\n if (supplied instanceof Stream) {\n return { stdout: supplied as NodeJS.WriteStream };\n }\n return supplied as RenderOptions;\n}\n\nfunction reportSetupFailure(\n connector: typeof connectProbe,\n env: EnvSource,\n failure: unknown,\n): void {\n const error = failure instanceof Error ? failure : new Error(String(failure));\n try {\n void connector({\n endpoint: env[ENV_ENDPOINT] as string,\n token: env[ENV_TOKEN] as string,\n probe: probeInfo(),\n capabilities: INK_CAPABILITIES,\n adapterName: ADAPTER_NAME,\n adapterVersion: ADAPTER_VERSION,\n })\n .then((channel) => {\n if (channel === null) return;\n channel.fail('adapter-guarantee-violation', error.message);\n // ProbeChannel.fail owns termination. Keep this guard explicit so a\n // future transport cannot leave a failed semantic producer connected.\n if (channel.isOpen) channel.close();\n })\n .catch(() => undefined);\n } catch {\n // Diagnostics must never replace the application's own render result.\n }\n}\n\nfunction renderAfterSetupFailure(\n ink: InkModule,\n node: ReactNode,\n suppliedOptions: NodeJS.WriteStream | RenderOptions | undefined,\n connector: typeof connectProbe,\n env: EnvSource,\n setupFailure: unknown,\n): Instance {\n try {\n const instance = ink.render(node, suppliedOptions);\n reportSetupFailure(connector, env, setupFailure);\n return instance;\n } catch (applicationError) {\n reportSetupFailure(connector, env, setupFailure);\n // Preserve the application's exact thrown object. The setup failure is\n // already reported through the typed adapter channel and never replaces it.\n throw applicationError;\n }\n}\n","/** Frame-local Ink layout facts captured at the exact renderer boundary. */\n\nimport type { ProbeRect } from '@termwright/protocol';\nimport {\n INK_RENDER_CAPTURE,\n INK_FRAME_CONTEXT,\n type InkRenderCaptureHook,\n type InkRenderedOutput,\n} from './instrumentation.js';\nimport type { InkDomElement, InkDomNode } from './observe.js';\n\ninterface YogaNodeLike {\n getDisplay(): number;\n getComputedLeft(): number;\n getComputedTop(): number;\n getComputedWidth(): number;\n getComputedHeight(): number;\n getComputedBorder(edge: number): number;\n}\n\ninterface RenderableInkElement extends InkDomElement {\n readonly yogaNode?: YogaNodeLike;\n readonly style?: InkDomElement['style'] & {\n readonly overflow?: string;\n readonly overflowX?: string;\n readonly overflowY?: string;\n };\n}\n\nexport interface RelativeGeometry {\n readonly intended: ProbeRect;\n readonly visible: ProbeRect;\n readonly region: 'live' | 'static';\n}\n\nexport interface InkFrameCapture {\n readonly root: InkDomElement;\n /** Static host subtrees retained at the renderer boundary, before Ink detaches them. */\n readonly staticRoots: readonly InkDomElement[];\n /** Immutable child lists for static hosts that Ink mutates after output commit. */\n readonly staticChildren: ReadonlyMap<InkDomElement, readonly InkDomNode[]>;\n readonly rendered: InkRenderedOutput;\n readonly screenReader: boolean;\n readonly geometry: ReadonlyMap<InkDomElement, RelativeGeometry>;\n readonly liveRows: number;\n readonly staticRows: number;\n readonly context?: InkFrameContext;\n}\n\nexport interface InkFrameContext {\n readonly interactive: boolean;\n readonly alternateScreen: boolean;\n readonly debug: boolean;\n readonly stdoutIsTTY: boolean;\n readonly rows: number;\n}\n\nconst latest = new WeakMap<object, InkFrameCapture>();\n\n/** Install one process-wide receiver used by every certified Ink renderer. */\nexport function installInkCaptureHook(): () => void {\n const globals = globalThis as Record<PropertyKey, unknown>;\n const previous = globals[INK_RENDER_CAPTURE];\n const previousContext = globals[INK_FRAME_CONTEXT];\n const hook: InkRenderCaptureHook = (root, rendered, screenReader) => {\n if (screenReader) {\n retainInkFrame({\n root: root as InkDomElement,\n staticRoots:\n (root as InkDomElement).staticNode === undefined\n ? []\n : [(root as InkDomElement).staticNode as InkDomElement],\n staticChildren: snapshotStaticChildren((root as InkDomElement).staticNode),\n rendered,\n screenReader,\n geometry: new Map(),\n liveRows: visibleRows(rendered.output),\n staticRows: visibleRows(rendered.staticOutput),\n });\n return;\n }\n retainInkFrame(captureInkLayout(root as InkDomElement, rendered));\n };\n globals[INK_RENDER_CAPTURE] = hook;\n const contextHook = (root: object, context: InkFrameContext): void => {\n const frame = latest.get(root);\n if (frame !== undefined) latest.set(root, { ...frame, context });\n };\n globals[INK_FRAME_CONTEXT] = contextHook;\n return () => {\n if (globals[INK_RENDER_CAPTURE] === hook) {\n if (previous === undefined) delete globals[INK_RENDER_CAPTURE];\n else globals[INK_RENDER_CAPTURE] = previous;\n }\n if (globals[INK_FRAME_CONTEXT] === contextHook) {\n if (previousContext === undefined) delete globals[INK_FRAME_CONTEXT];\n else globals[INK_FRAME_CONTEXT] = previousContext;\n }\n };\n}\n\n/** Exact traversal used by the checksummed hook and the pinned in-process harness. */\nexport function captureInkLayout(\n root: InkDomElement,\n rendered: InkRenderedOutput,\n context?: InkFrameContext,\n): InkFrameCapture {\n const inkRoot = root as RenderableInkElement;\n const geometry = new Map<InkDomElement, RelativeGeometry>();\n const width = integer(inkRoot.yogaNode?.getComputedWidth());\n const height = integer(inkRoot.yogaNode?.getComputedHeight());\n walk(inkRoot, 'live', 0, 0, rect(0, 0, width, height), geometry, true);\n if (inkRoot.staticNode !== undefined) {\n const staticRoot = inkRoot.staticNode as RenderableInkElement;\n walk(\n staticRoot,\n 'static',\n 0,\n 0,\n rect(\n 0,\n 0,\n integer(staticRoot.yogaNode?.getComputedWidth()),\n integer(staticRoot.yogaNode?.getComputedHeight()),\n ),\n geometry,\n false,\n );\n }\n return {\n root: inkRoot,\n staticRoots: inkRoot.staticNode === undefined ? [] : [inkRoot.staticNode],\n staticChildren: snapshotStaticChildren(inkRoot.staticNode),\n rendered,\n screenReader: false,\n geometry,\n liveRows: visibleRows(rendered.output),\n staticRows: visibleRows(rendered.staticOutput),\n ...(context === undefined ? {} : { context }),\n };\n}\n\nexport function capturedInkFrame(root: object): InkFrameCapture | undefined {\n return latest.get(root);\n}\n\nexport function retainInkFrame(capture: InkFrameCapture): void {\n const previous = latest.get(capture.root);\n if (previous === undefined || capture.screenReader) {\n latest.set(capture.root, capture);\n return;\n }\n const previousRoots = new Set(previous.staticRoots);\n const addedRoots = capture.staticRoots.filter((root) => !previousRoots.has(root));\n const newStaticNodes = new Set(\n [...capture.staticChildren.keys()].filter((node) => !previous.staticChildren.has(node)),\n );\n const hasNewStaticOutput = capture.staticRows > 0 && newStaticNodes.size > 0;\n if (!hasNewStaticOutput) {\n const retainedGeometry = new Map(capture.geometry);\n for (const [node, geometry] of previous.geometry) {\n if (geometry.region === 'static') retainedGeometry.set(node, geometry);\n }\n latest.set(capture.root, {\n ...capture,\n staticRoots: previous.staticRoots,\n staticChildren: previous.staticChildren,\n staticRows: previous.staticRows,\n geometry: retainedGeometry,\n });\n return;\n }\n\n const geometry = new Map(capture.geometry);\n for (const [node, retained] of previous.geometry) {\n if (retained.region === 'static') geometry.set(node, retained);\n }\n for (const [node, current] of capture.geometry) {\n if (current.region === 'static') {\n if (newStaticNodes.has(node)) {\n geometry.set(node, {\n ...current,\n intended: shiftRows(current.intended, previous.staticRows),\n visible: shiftRows(current.visible, previous.staticRows),\n });\n } else if (capture.staticRoots.includes(node)) {\n const retained = previous.geometry.get(node);\n if (retained !== undefined) {\n geometry.set(node, {\n ...retained,\n intended: { ...retained.intended, height: previous.staticRows + capture.staticRows },\n visible: { ...retained.visible, height: previous.staticRows + capture.staticRows },\n });\n }\n }\n }\n }\n const staticChildren = new Map(previous.staticChildren);\n for (const [parent, currentChildren] of capture.staticChildren) {\n const retained = staticChildren.get(parent) ?? [];\n staticChildren.set(parent, [\n ...retained,\n ...currentChildren.filter((child) => !retained.includes(child)),\n ]);\n }\n latest.set(capture.root, {\n ...capture,\n staticRoots: [...previous.staticRoots, ...addedRoots],\n staticChildren,\n staticRows: previous.staticRows + capture.staticRows,\n geometry,\n });\n}\n\nfunction snapshotStaticChildren(\n root: InkDomElement | undefined,\n): ReadonlyMap<InkDomElement, readonly InkDomNode[]> {\n const result = new Map<InkDomElement, readonly InkDomNode[]>();\n if (root === undefined) return result;\n const stack = [root];\n while (stack.length > 0) {\n const node = stack.pop() as InkDomElement;\n const children = [...node.childNodes];\n result.set(node, children);\n for (const child of children) if (child.nodeName !== '#text') stack.push(child);\n }\n return result;\n}\n\nfunction shiftRows(value: ProbeRect, rows: number): ProbeRect {\n return { ...value, row: value.row + rows };\n}\n\nfunction walk(\n node: RenderableInkElement,\n region: 'live' | 'static',\n offsetX: number,\n offsetY: number,\n ancestorClip: ProbeRect,\n output: Map<InkDomElement, RelativeGeometry>,\n skipStatic: boolean,\n): void {\n if (skipStatic && node.internal_static === true) return;\n const yoga = node.yogaNode;\n // Yoga.DISPLAY_NONE is 1 in the pinned 7.1.1 yoga build. Hidden subtrees are\n // represented by displayed=false in the tree and deliberately have no box.\n if (yoga === undefined || yoga.getDisplay() === 1) return;\n\n const x = offsetX + integer(yoga.getComputedLeft());\n const y = offsetY + integer(yoga.getComputedTop());\n const intended = rect(x, y, integer(yoga.getComputedWidth()), integer(yoga.getComputedHeight()));\n const visible = intersection(intended, ancestorClip);\n output.set(node, { intended, visible, region });\n\n let childClip = ancestorClip;\n if (node.nodeName === 'ink-box') {\n const horizontal = node.style?.overflowX === 'hidden' || node.style?.overflow === 'hidden';\n const vertical = node.style?.overflowY === 'hidden' || node.style?.overflow === 'hidden';\n if (horizontal || vertical) {\n const left = horizontal ? x + integer(yoga.getComputedBorder(0)) : ancestorClip.column;\n const right = horizontal\n ? x + intended.width - integer(yoga.getComputedBorder(2))\n : ancestorClip.column + ancestorClip.width;\n const top = vertical ? y + integer(yoga.getComputedBorder(1)) : ancestorClip.row;\n const bottom = vertical\n ? y + intended.height - integer(yoga.getComputedBorder(3))\n : ancestorClip.row + ancestorClip.height;\n childClip = intersection(ancestorClip, rect(left, top, right - left, bottom - top));\n }\n }\n\n for (const child of node.childNodes) {\n if (child.nodeName !== '#text') {\n walk(child as RenderableInkElement, region, x, y, childClip, output, skipStatic);\n }\n }\n}\n\nfunction integer(value: number | undefined): number {\n return Number.isFinite(value) ? Math.trunc(value as number) : 0;\n}\n\nfunction rect(column: number, row: number, width: number, height: number): ProbeRect {\n return {\n row,\n column,\n width: Math.max(0, width),\n height: Math.max(0, height),\n };\n}\n\nfunction intersection(a: ProbeRect, b: ProbeRect): ProbeRect {\n const column = Math.max(a.column, b.column);\n const row = Math.max(a.row, b.row);\n const right = Math.max(column, Math.min(a.column + a.width, b.column + b.width));\n const bottom = Math.max(row, Math.min(a.row + a.height, b.row + b.height));\n return rect(column, row, right - column, bottom - row);\n}\n\nfunction visibleRows(output: string): number {\n if (output === '') return 0;\n const lines = output.split('\\n');\n return output.endsWith('\\n') ? lines.length - 1 : lines.length;\n}\n","/**\n * Shadow of bytes written through the supplied JavaScript stdout/stderr stream methods.\n * Direct fd, native and descendant output is deliberately outside this boundary.\n */\n\nimport { createTerminal, type Terminal } from '@termwright/vt';\n\nexport interface TerminalPosition {\n readonly row: number;\n readonly column: number;\n readonly buffer: 'normal' | 'alternate';\n}\n\nexport interface InkTerminalTracker {\n drain(): Promise<void>;\n position(): TerminalPosition;\n resize(columns: number, rows: number): void;\n stop(): void;\n}\n\n/** @internal Serial shadow writes that fail closed after the first parser error. */\nexport class ShadowWriteQueue {\n #queue: Promise<void> = Promise.resolve();\n #failure: Error | undefined;\n\n enqueue(operation: () => Promise<void>): void {\n this.#queue = this.#queue.then(async () => {\n if (this.#failure !== undefined) return;\n try {\n await operation();\n } catch (error) {\n this.#failure = error instanceof Error ? error : new Error(String(error));\n }\n });\n }\n\n async drain(): Promise<void> {\n await this.#queue;\n if (this.#failure !== undefined) throw this.#failure;\n }\n}\n\nexport function trackTerminal(\n stdout: NodeJS.WriteStream,\n stderr: NodeJS.WriteStream,\n): InkTerminalTracker {\n const built = createTerminal({\n columns: positive(stdout.columns, 80),\n rows: positive(stdout.rows, 24),\n scrollback: 100_000,\n });\n const terminal = built.terminal;\n const queue = new ShadowWriteQueue();\n let stopped = false;\n const restorers: (() => void)[] = [];\n const observe = (chunk: unknown, encoding?: unknown): void => {\n if (stopped) return;\n const bytes =\n Buffer.isBuffer(chunk) || chunk instanceof Uint8Array\n ? chunk\n : Buffer.from(\n String(chunk),\n typeof encoding === 'string' ? (encoding as BufferEncoding) : 'utf8',\n );\n // A real Unix PTY has ONLCR enabled for the child output stream by default.\n // Ink writes LF, while both the host terminal and Termwright's driver see\n // CRLF after the line discipline. Shadow those committed bytes, not the\n // pre-PTY JavaScript payload. Pipes are deliberately left byte-exact.\n const committed = stdout.isTTY ? withOnlcr(bytes) : bytes;\n queue.enqueue(() => writeTerminal(terminal, committed));\n };\n\n for (const stream of new Set([stdout, stderr])) restorers.push(intercept(stream, observe));\n const onResize = (): void =>\n terminal.resize(positive(stdout.columns, terminal.cols), positive(stdout.rows, terminal.rows));\n stdout.on('resize', onResize);\n restorers.push(() => stdout.off('resize', onResize));\n\n return {\n drain: () => queue.drain(),\n position() {\n const buffer = terminal.buffer.active;\n return { row: buffer.cursorY, column: buffer.cursorX, buffer: buffer.type };\n },\n resize(columns, rows) {\n terminal.resize(columns, rows);\n },\n stop() {\n if (stopped) return;\n stopped = true;\n for (const restore of restorers.reverse()) restore();\n terminal.dispose();\n },\n };\n}\n\nfunction withOnlcr(bytes: Uint8Array): Uint8Array {\n let newlines = 0;\n for (const byte of bytes) if (byte === 0x0a) newlines += 1;\n if (newlines === 0) return bytes;\n const output = new Uint8Array(bytes.length + newlines);\n let index = 0;\n for (const byte of bytes) {\n if (byte === 0x0a) output[index++] = 0x0d;\n output[index++] = byte;\n }\n return output;\n}\n\nfunction intercept(\n stream: NodeJS.WriteStream,\n observe: (chunk: unknown, encoding?: unknown) => void,\n): () => void {\n const target = stream as NodeJS.WriteStream & { write: (...args: unknown[]) => boolean };\n const original = target.write;\n const wrapped = function (this: NodeJS.WriteStream, ...args: unknown[]): boolean {\n observe(args[0], args[1]);\n return Reflect.apply(original, this, args) as boolean;\n };\n try {\n target.write = wrapped;\n } catch (error) {\n throw new Error('Ink terminal stream cannot be instrumented exactly', { cause: error });\n }\n return () => {\n if (target.write === wrapped) target.write = original;\n };\n}\n\nfunction writeTerminal(terminal: Terminal, bytes: Uint8Array): Promise<void> {\n return new Promise((resolve) => terminal.write(bytes, resolve));\n}\n\nfunction positive(value: number | undefined, fallback: number): number {\n return Number.isSafeInteger(value) && (value as number) > 0 ? (value as number) : fallback;\n}\n","export interface RenderBoundary {\n readonly generation: number;\n readonly resolve: (revision: number) => void;\n readonly reject: (error: Error) => void;\n}\n\n/**\n * Associates an explicit mutation with the renderer commit it causes.\n *\n * Ink can still have an onRender callback queued after a caller has observed\n * the first semantic frame. Drain that existing work before arming the next\n * boundary, otherwise the trailing callback can acknowledge the new mutation.\n */\nexport class RenderBoundaryQueue {\n readonly #pending: RenderBoundary[] = [];\n readonly #preparing = new Set<RenderBoundary>();\n #nextGeneration = 1;\n #stopped = false;\n\n take(committedGeneration: number): RenderBoundary | undefined {\n const boundary = this.#pending[0];\n if (boundary === undefined || boundary.generation !== committedGeneration) return undefined;\n return this.#pending.shift();\n }\n\n afterCurrentRender(\n waitForCurrentRender: () => Promise<void>,\n mutate: (generation: number) => void,\n ): Promise<number> {\n if (this.#stopped) return Promise.reject(stoppedError());\n return new Promise<number>((resolve, reject) => {\n const boundary = { generation: this.#nextGeneration, resolve, reject };\n this.#nextGeneration += 1;\n this.#preparing.add(boundary);\n let flush: Promise<void>;\n try {\n flush = waitForCurrentRender();\n } catch (error) {\n this.#preparing.delete(boundary);\n reject(asError(error));\n return;\n }\n // Ink exposes no cancellation API. Keep the uncancellable flush owned\n // and observed until it settles, while `stop()` rejects the public\n // operation immediately so teardown cannot hang on an upstream flush.\n void flush.then(\n () => {\n this.#preparing.delete(boundary);\n if (this.#stopped) return;\n this.#pending.push(boundary);\n try {\n mutate(boundary.generation);\n } catch (error) {\n const index = this.#pending.indexOf(boundary);\n if (index !== -1) this.#pending.splice(index, 1);\n reject(asError(error));\n }\n },\n (error: unknown) => {\n this.#preparing.delete(boundary);\n reject(asError(error));\n },\n );\n });\n }\n\n stop(): void {\n if (this.#stopped) return;\n this.#stopped = true;\n for (const boundary of this.#preparing) boundary.reject(stoppedError());\n // Do not erase ownership of an upstream flush that has not settled. Ink's\n // certified waitUntilRenderFlush() settles when the instance renders or\n // exits; retaining the record until its reaction runs makes a regression\n // in that contract visible to --detectAsyncLeaks instead of hiding it.\n for (const boundary of this.#pending.splice(0)) {\n boundary.reject(stoppedError());\n }\n }\n}\n\nfunction stoppedError(): Error {\n return new Error('Ink probe stopped before the render boundary');\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n"],"mappings":"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","names":["geometry"]}
@@ -0,0 +1,26 @@
1
+ /** Exact, content-addressed instrumentation for Ink 7.1.1's renderer. */
2
+ declare const INK_VERSION: string;
3
+ declare const INK_RENDER_CAPTURE: unique symbol;
4
+ declare const INK_FRAME_CONTEXT: unique symbol;
5
+ declare const INK_INSTRUMENTATION_SENTINEL: unique symbol;
6
+ declare const INK_RENDERER_PATTERN: RegExp;
7
+ declare const INK_CORE_PATTERN: RegExp;
8
+ interface InkInstrumentationSentinel {
9
+ readonly version: 1;
10
+ readonly frameworkVersion: string;
11
+ readonly rendererChecksum: string;
12
+ readonly coreChecksum: string;
13
+ }
14
+ interface InkRenderedOutput {
15
+ readonly output: string;
16
+ readonly outputHeight: number;
17
+ readonly staticOutput: string;
18
+ }
19
+ type InkRenderCaptureHook = (root: object, result: InkRenderedOutput, screenReader: boolean) => void;
20
+ declare function instrumentationSentinel(): InkInstrumentationSentinel | undefined;
21
+ /** Transform the matching Ink class so every capture includes render-mode facts. */
22
+ declare function instrumentInkCore(path: string, source: string): string | undefined;
23
+ /** Transform only the byte-exact renderer shipped by Ink 7.1.1. */
24
+ declare function instrumentInkRenderer(path: string, source: string): string | undefined;
25
+
26
+ export { INK_CORE_PATTERN, INK_FRAME_CONTEXT, INK_INSTRUMENTATION_SENTINEL, INK_RENDERER_PATTERN, INK_RENDER_CAPTURE, INK_VERSION, type InkInstrumentationSentinel, type InkRenderCaptureHook, type InkRenderedOutput, instrumentInkCore, instrumentInkRenderer, instrumentationSentinel };
@@ -0,0 +1,23 @@
1
+ import {
2
+ INK_CORE_PATTERN,
3
+ INK_FRAME_CONTEXT,
4
+ INK_INSTRUMENTATION_SENTINEL,
5
+ INK_RENDERER_PATTERN,
6
+ INK_RENDER_CAPTURE,
7
+ INK_VERSION,
8
+ instrumentInkCore,
9
+ instrumentInkRenderer,
10
+ instrumentationSentinel
11
+ } from "./chunk-SLKX554P.js";
12
+ export {
13
+ INK_CORE_PATTERN,
14
+ INK_FRAME_CONTEXT,
15
+ INK_INSTRUMENTATION_SENTINEL,
16
+ INK_RENDERER_PATTERN,
17
+ INK_RENDER_CAPTURE,
18
+ INK_VERSION,
19
+ instrumentInkCore,
20
+ instrumentInkRenderer,
21
+ instrumentationSentinel
22
+ };
23
+ //# sourceMappingURL=instrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/node-hook.js CHANGED
@@ -1,31 +1,54 @@
1
1
  import {
2
2
  buildShimSource,
3
3
  shouldShim
4
- } from "./chunk-IUFXTMZ7.js";
4
+ } from "./chunk-CLY2SLYH.js";
5
5
  import {
6
6
  isInstrumented
7
- } from "./chunk-LO7YF74P.js";
7
+ } from "./chunk-67M2GX5S.js";
8
+ import {
9
+ INK_CORE_PATTERN,
10
+ INK_RENDERER_PATTERN,
11
+ instrumentInkCore,
12
+ instrumentInkRenderer
13
+ } from "./chunk-SLKX554P.js";
8
14
 
9
15
  // src/node-hook.ts
10
16
  import * as nodeModule from "module";
11
17
  import { fileURLToPath } from "url";
12
- function syncLoad(url, context, nextLoad) {
13
- if (!shouldShim(url)) return nextLoad(url, context);
14
- return { format: "module", shortCircuit: true, source: buildShimSource(url) };
18
+ function loadWithInstrumentation(url, context, nextLoad) {
19
+ if (shouldShim(url))
20
+ return { format: "module", shortCircuit: true, source: buildShimSource(url) };
21
+ const loaded = nextLoad(url, context);
22
+ const path = url.split("?")[0] ?? "";
23
+ if (!INK_RENDERER_PATTERN.test(path) && !INK_CORE_PATTERN.test(path)) return loaded;
24
+ const source = sourceText(loaded.source);
25
+ const instrumented = source === void 0 ? void 0 : INK_RENDERER_PATTERN.test(path) ? instrumentInkRenderer(url, source) : instrumentInkCore(url, source);
26
+ return instrumented === void 0 ? loaded : { ...loaded, format: "module", shortCircuit: true, source: instrumented };
27
+ }
28
+ function sourceText(source) {
29
+ if (typeof source === "string") return source;
30
+ if (source === void 0) return void 0;
31
+ return Buffer.from(source.buffer, source.byteOffset, source.byteLength).toString("utf8");
15
32
  }
16
33
  function installNodeHook(env = process.env) {
17
34
  if (!isInstrumented(env)) return null;
18
35
  const registerHooks2 = nodeModule.registerHooks;
19
36
  if (typeof registerHooks2 === "function") {
20
- registerHooks2({ load: syncLoad });
37
+ registerHooks2({ load: loadWithInstrumentation });
21
38
  return "sync";
22
39
  }
23
40
  nodeModule.register(new URL(import.meta.url), { parentURL: new URL(import.meta.url) });
24
41
  return "thread";
25
42
  }
26
43
  async function loadHook(url, context, nextLoad) {
27
- if (!shouldShim(url)) return nextLoad(url, context);
28
- return { format: "module", shortCircuit: true, source: buildShimSource(url) };
44
+ if (shouldShim(url))
45
+ return { format: "module", shortCircuit: true, source: buildShimSource(url) };
46
+ const loaded = await nextLoad(url, context);
47
+ const path = url.split("?")[0] ?? "";
48
+ if (!INK_RENDERER_PATTERN.test(path) && !INK_CORE_PATTERN.test(path)) return loaded;
49
+ const source = sourceText(loaded.source);
50
+ const instrumented = source === void 0 ? void 0 : INK_RENDERER_PATTERN.test(path) ? instrumentInkRenderer(url, source) : instrumentInkCore(url, source);
51
+ return instrumented === void 0 ? loaded : { ...loaded, format: "module", shortCircuit: true, source: instrumented };
29
52
  }
30
53
  var NODE_HOOK_PATH = fileURLToPath(import.meta.url);
31
54
  installNodeHook();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/node-hook.ts"],"sourcesContent":["/** Node entry: `node --import @termwright/probe-ink/node-hook app.js`. */\n\nimport * as nodeModule from 'node:module';\nimport { fileURLToPath } from 'node:url';\nimport { buildShimSource, shouldShim } from './shim.js';\nimport { isInstrumented } from './runtime.js';\n\ntype LoadResult = {\n format?: string | null;\n source?: string | ArrayBufferView | undefined;\n shortCircuit?: boolean;\n};\ntype NextLoad = (url: string, context: unknown) => LoadResult;\n\nfunction syncLoad(url: string, context: unknown, nextLoad: NextLoad): LoadResult {\n if (!shouldShim(url)) return nextLoad(url, context);\n return { format: 'module', shortCircuit: true, source: buildShimSource(url) };\n}\n\n/** Install the best loader API available on the current Node 22+ release. */\nexport function installNodeHook(env: NodeJS.ProcessEnv = process.env): 'sync' | 'thread' | null {\n if (!isInstrumented(env)) return null;\n // A named ESM import of `registerHooks` makes early Node 22 fail while\n // instantiating this module, before a feature check can run. Namespace access\n // is the compatibility boundary: 22.9 simply yields `undefined`.\n const registerHooks = (nodeModule as typeof nodeModule & {\n readonly registerHooks?: (hooks: { load: never }) => unknown;\n }).registerHooks;\n if (typeof registerHooks === 'function') {\n registerHooks({ load: syncLoad as never });\n return 'sync';\n }\n nodeModule.register(new URL(import.meta.url), { parentURL: new URL(import.meta.url) });\n return 'thread';\n}\n\n/** Off-thread loader export for early Node 22 releases. */\nexport async function loadHook(\n url: string,\n context: unknown,\n nextLoad: (url: string, context: unknown) => Promise<LoadResult>,\n): Promise<LoadResult> {\n if (!shouldShim(url)) return nextLoad(url, context);\n return { format: 'module', shortCircuit: true, source: buildShimSource(url) };\n}\n\nexport { loadHook as load };\nexport const NODE_HOOK_PATH = fileURLToPath(import.meta.url);\n\ninstallNodeHook();\n"],"mappings":";;;;;;;;;AAEA,YAAY,gBAAgB;AAC5B,SAAS,qBAAqB;AAW9B,SAAS,SAAS,KAAa,SAAkB,UAAgC;AAC/E,MAAI,CAAC,WAAW,GAAG,EAAG,QAAO,SAAS,KAAK,OAAO;AAClD,SAAO,EAAE,QAAQ,UAAU,cAAc,MAAM,QAAQ,gBAAgB,GAAG,EAAE;AAC9E;AAGO,SAAS,gBAAgB,MAAyB,QAAQ,KAA+B;AAC9F,MAAI,CAAC,eAAe,GAAG,EAAG,QAAO;AAIjC,QAAMA,iBAEH;AACH,MAAI,OAAOA,mBAAkB,YAAY;AACvC,IAAAA,eAAc,EAAE,MAAM,SAAkB,CAAC;AACzC,WAAO;AAAA,EACT;AACA,EAAW,oBAAS,IAAI,IAAI,YAAY,GAAG,GAAG,EAAE,WAAW,IAAI,IAAI,YAAY,GAAG,EAAE,CAAC;AACrF,SAAO;AACT;AAGA,eAAsB,SACpB,KACA,SACA,UACqB;AACrB,MAAI,CAAC,WAAW,GAAG,EAAG,QAAO,SAAS,KAAK,OAAO;AAClD,SAAO,EAAE,QAAQ,UAAU,cAAc,MAAM,QAAQ,gBAAgB,GAAG,EAAE;AAC9E;AAGO,IAAM,iBAAiB,cAAc,YAAY,GAAG;AAE3D,gBAAgB;","names":["registerHooks"]}
1
+ {"version":3,"sources":["../src/node-hook.ts"],"sourcesContent":["/** Node entry: `node --import @termwright/probe-ink/node-hook app.js`. */\n\nimport * as nodeModule from 'node:module';\nimport { fileURLToPath } from 'node:url';\nimport { buildShimSource, shouldShim } from './shim.js';\nimport {\n instrumentInkCore,\n instrumentInkRenderer,\n INK_CORE_PATTERN,\n INK_RENDERER_PATTERN,\n} from './instrumentation.js';\nimport { isInstrumented } from './runtime.js';\n\ntype LoadResult = {\n format?: string | null;\n source?: string | ArrayBufferView | undefined;\n shortCircuit?: boolean;\n};\ntype NextLoad = (url: string, context: unknown) => LoadResult;\n\nfunction loadWithInstrumentation(url: string, context: unknown, nextLoad: NextLoad): LoadResult {\n if (shouldShim(url))\n return { format: 'module', shortCircuit: true, source: buildShimSource(url) };\n const loaded = nextLoad(url, context);\n const path = url.split('?')[0] ?? '';\n if (!INK_RENDERER_PATTERN.test(path) && !INK_CORE_PATTERN.test(path)) return loaded;\n const source = sourceText(loaded.source);\n const instrumented =\n source === undefined\n ? undefined\n : INK_RENDERER_PATTERN.test(path)\n ? instrumentInkRenderer(url, source)\n : instrumentInkCore(url, source);\n return instrumented === undefined\n ? loaded\n : { ...loaded, format: 'module', shortCircuit: true, source: instrumented };\n}\n\nfunction sourceText(source: LoadResult['source']): string | undefined {\n if (typeof source === 'string') return source;\n if (source === undefined) return undefined;\n return Buffer.from(source.buffer, source.byteOffset, source.byteLength).toString('utf8');\n}\n\n/** Install the best loader API available on the current Node 22+ release. */\nexport function installNodeHook(env: NodeJS.ProcessEnv = process.env): 'sync' | 'thread' | null {\n if (!isInstrumented(env)) return null;\n // A named ESM import of `registerHooks` makes early Node 22 fail while\n // instantiating this module, before a feature check can run. Namespace access\n // is the compatibility boundary: 22.9 simply yields `undefined`.\n const registerHooks = (\n nodeModule as typeof nodeModule & {\n readonly registerHooks?: (hooks: { load: never }) => unknown;\n }\n ).registerHooks;\n if (typeof registerHooks === 'function') {\n registerHooks({ load: loadWithInstrumentation as never });\n return 'sync';\n }\n nodeModule.register(new URL(import.meta.url), { parentURL: new URL(import.meta.url) });\n return 'thread';\n}\n\n/** Off-thread loader export for early Node 22 releases. */\nexport async function loadHook(\n url: string,\n context: unknown,\n nextLoad: (url: string, context: unknown) => Promise<LoadResult>,\n): Promise<LoadResult> {\n if (shouldShim(url))\n return { format: 'module', shortCircuit: true, source: buildShimSource(url) };\n const loaded = await nextLoad(url, context);\n const path = url.split('?')[0] ?? '';\n if (!INK_RENDERER_PATTERN.test(path) && !INK_CORE_PATTERN.test(path)) return loaded;\n const source = sourceText(loaded.source);\n const instrumented =\n source === undefined\n ? undefined\n : INK_RENDERER_PATTERN.test(path)\n ? instrumentInkRenderer(url, source)\n : instrumentInkCore(url, source);\n return instrumented === undefined\n ? loaded\n : { ...loaded, format: 'module', shortCircuit: true, source: instrumented };\n}\n\nexport { loadHook as load };\nexport const NODE_HOOK_PATH = fileURLToPath(import.meta.url);\n\ninstallNodeHook();\n"],"mappings":";;;;;;;;;;;;;;;AAEA,YAAY,gBAAgB;AAC5B,SAAS,qBAAqB;AAiB9B,SAAS,wBAAwB,KAAa,SAAkB,UAAgC;AAC9F,MAAI,WAAW,GAAG;AAChB,WAAO,EAAE,QAAQ,UAAU,cAAc,MAAM,QAAQ,gBAAgB,GAAG,EAAE;AAC9E,QAAM,SAAS,SAAS,KAAK,OAAO;AACpC,QAAM,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC,KAAK;AAClC,MAAI,CAAC,qBAAqB,KAAK,IAAI,KAAK,CAAC,iBAAiB,KAAK,IAAI,EAAG,QAAO;AAC7E,QAAM,SAAS,WAAW,OAAO,MAAM;AACvC,QAAM,eACJ,WAAW,SACP,SACA,qBAAqB,KAAK,IAAI,IAC5B,sBAAsB,KAAK,MAAM,IACjC,kBAAkB,KAAK,MAAM;AACrC,SAAO,iBAAiB,SACpB,SACA,EAAE,GAAG,QAAQ,QAAQ,UAAU,cAAc,MAAM,QAAQ,aAAa;AAC9E;AAEA,SAAS,WAAW,QAAkD;AACpE,MAAI,OAAO,WAAW,SAAU,QAAO;AACvC,MAAI,WAAW,OAAW,QAAO;AACjC,SAAO,OAAO,KAAK,OAAO,QAAQ,OAAO,YAAY,OAAO,UAAU,EAAE,SAAS,MAAM;AACzF;AAGO,SAAS,gBAAgB,MAAyB,QAAQ,KAA+B;AAC9F,MAAI,CAAC,eAAe,GAAG,EAAG,QAAO;AAIjC,QAAMA,iBAIJ;AACF,MAAI,OAAOA,mBAAkB,YAAY;AACvC,IAAAA,eAAc,EAAE,MAAM,wBAAiC,CAAC;AACxD,WAAO;AAAA,EACT;AACA,EAAW,oBAAS,IAAI,IAAI,YAAY,GAAG,GAAG,EAAE,WAAW,IAAI,IAAI,YAAY,GAAG,EAAE,CAAC;AACrF,SAAO;AACT;AAGA,eAAsB,SACpB,KACA,SACA,UACqB;AACrB,MAAI,WAAW,GAAG;AAChB,WAAO,EAAE,QAAQ,UAAU,cAAc,MAAM,QAAQ,gBAAgB,GAAG,EAAE;AAC9E,QAAM,SAAS,MAAM,SAAS,KAAK,OAAO;AAC1C,QAAM,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC,KAAK;AAClC,MAAI,CAAC,qBAAqB,KAAK,IAAI,KAAK,CAAC,iBAAiB,KAAK,IAAI,EAAG,QAAO;AAC7E,QAAM,SAAS,WAAW,OAAO,MAAM;AACvC,QAAM,eACJ,WAAW,SACP,SACA,qBAAqB,KAAK,IAAI,IAC5B,sBAAsB,KAAK,MAAM,IACjC,kBAAkB,KAAK,MAAM;AACrC,SAAO,iBAAiB,SACpB,SACA,EAAE,GAAG,QAAQ,QAAQ,UAAU,cAAc,MAAM,QAAQ,aAAa;AAC9E;AAGO,IAAM,iBAAiB,cAAc,YAAY,GAAG;AAE3D,gBAAgB;","names":["registerHooks"]}
@@ -0,0 +1,169 @@
1
+ import { ProbeRect, ProbeFrame, ProtocolLimits } from '@termwright/protocol';
2
+ import { InkRenderedOutput } from './instrumentation.js';
3
+
4
+ /** Runtime activation shared by the two preload entry points. */
5
+ /** Runtimes into which the Ink probe can be injected. */
6
+ type ProbeRuntime = 'bun' | 'node';
7
+ /** Read-only environment view, so activation is testable without mutation. */
8
+ type EnvSource = Readonly<Record<string, string | undefined>>;
9
+ /** Both secrets are required. A partial environment remains fully dormant. */
10
+ declare function isInstrumented(env: EnvSource): boolean;
11
+
12
+ /** Frame-local Ink layout facts captured at the exact renderer boundary. */
13
+
14
+ interface RelativeGeometry {
15
+ readonly intended: ProbeRect;
16
+ readonly visible: ProbeRect;
17
+ readonly region: 'live' | 'static';
18
+ }
19
+ interface InkFrameCapture {
20
+ readonly root: InkDomElement;
21
+ /** Static host subtrees retained at the renderer boundary, before Ink detaches them. */
22
+ readonly staticRoots: readonly InkDomElement[];
23
+ /** Immutable child lists for static hosts that Ink mutates after output commit. */
24
+ readonly staticChildren: ReadonlyMap<InkDomElement, readonly InkDomNode[]>;
25
+ readonly rendered: InkRenderedOutput;
26
+ readonly screenReader: boolean;
27
+ readonly geometry: ReadonlyMap<InkDomElement, RelativeGeometry>;
28
+ readonly liveRows: number;
29
+ readonly staticRows: number;
30
+ readonly context?: InkFrameContext;
31
+ }
32
+ interface InkFrameContext {
33
+ readonly interactive: boolean;
34
+ readonly alternateScreen: boolean;
35
+ readonly debug: boolean;
36
+ readonly stdoutIsTTY: boolean;
37
+ readonly rows: number;
38
+ }
39
+
40
+ /** Ink's retained host tree to framework-neutral Probe IR. */
41
+
42
+ /** Structural subset of Ink's DOM node. No runtime import from `ink`. */
43
+ interface InkDomElement {
44
+ readonly nodeName: 'ink-root' | 'ink-box' | 'ink-text' | 'ink-virtual-text';
45
+ readonly childNodes: readonly InkDomNode[];
46
+ readonly parentNode?: InkDomElement;
47
+ readonly style?: Readonly<Record<string, unknown>> & {
48
+ readonly display?: string;
49
+ };
50
+ readonly internal_static?: boolean;
51
+ readonly staticNode?: InkDomElement;
52
+ readonly internal_accessibility?: {
53
+ readonly role?: string;
54
+ readonly state?: {
55
+ readonly checked?: boolean;
56
+ readonly disabled?: boolean;
57
+ readonly expanded?: boolean;
58
+ readonly readonly?: boolean;
59
+ readonly selected?: boolean;
60
+ readonly busy?: boolean;
61
+ readonly multiline?: boolean;
62
+ readonly required?: boolean;
63
+ readonly multiselectable?: boolean;
64
+ };
65
+ };
66
+ }
67
+ interface InkTextNode {
68
+ readonly nodeName: '#text';
69
+ readonly nodeValue: string;
70
+ readonly parentNode?: InkDomElement;
71
+ }
72
+ type InkDomNode = InkDomElement | InkTextNode;
73
+ /** Public Ink measurement function, kept injectable for tests and isolation. */
74
+ type MeasureElement = (node: InkDomElement) => {
75
+ readonly x: number;
76
+ readonly y: number;
77
+ readonly width: number;
78
+ readonly height: number;
79
+ };
80
+ interface ObserveInkOptions {
81
+ readonly frame: number;
82
+ readonly limits: ProtocolLimits;
83
+ /** The probe's own hidden Box. It is the sole injected node and is omitted. */
84
+ readonly excluded?: InkDomElement | null;
85
+ /** Renderer-retained roots (notably committed <Static>) detached by Ink later. */
86
+ readonly retainedRoots?: readonly InkDomElement[];
87
+ readonly retainedChildren?: ReadonlyMap<InkDomElement, readonly InkDomNode[]>;
88
+ readonly measureElement?: MeasureElement;
89
+ /** Geometry frozen by the certified 7.1.1 renderer instrumentation. */
90
+ readonly geometry?: ReadonlyMap<InkDomElement, RelativeGeometry>;
91
+ }
92
+ interface InkObservation {
93
+ readonly frame: ProbeFrame;
94
+ readonly truncated: boolean;
95
+ readonly geometryRegions: ReadonlyMap<string, 'live' | 'static'>;
96
+ }
97
+ /**
98
+ * Observe every Ink host element, including plain unannotated layout boxes.
99
+ *
100
+ * Source component names do not survive Ink's reconciler. `frameworkType` is
101
+ * therefore deliberately one of Ink's four host kinds; inventing `Button` or
102
+ * a component stack here would be false provenance.
103
+ */
104
+ declare function observeInkTree(root: InkDomElement, options: ObserveInkOptions): InkObservation;
105
+
106
+ /** Minimal React renderer instrumentation observer used by the Ink probe spike. */
107
+
108
+ interface RendererMetadata {
109
+ readonly rendererPackageName?: unknown;
110
+ readonly rendererVersion?: unknown;
111
+ }
112
+ interface FiberRootLike {
113
+ readonly containerInfo?: unknown;
114
+ readonly current?: FiberLike;
115
+ }
116
+ interface FiberLike {
117
+ readonly stateNode?: unknown;
118
+ readonly memoizedProps?: unknown;
119
+ readonly child?: FiberLike | null;
120
+ readonly sibling?: FiberLike | null;
121
+ }
122
+ interface InkRendererRegistration {
123
+ readonly rendererId: unknown;
124
+ readonly packageName: 'ink';
125
+ readonly version?: string;
126
+ }
127
+ type InkCommitEvent = {
128
+ readonly type: 'commit';
129
+ readonly renderer: InkRendererRegistration;
130
+ readonly fiberRoot: FiberRootLike;
131
+ readonly root: InkDomElement;
132
+ } | {
133
+ readonly type: 'unmount';
134
+ readonly renderer: InkRendererRegistration;
135
+ readonly fiber: unknown;
136
+ } | {
137
+ readonly type: 'invalid-root';
138
+ readonly renderer: InkRendererRegistration;
139
+ readonly fiberRoot: FiberRootLike;
140
+ readonly containerInfo: unknown;
141
+ };
142
+ interface InkReconcilerInstrumentation {
143
+ injectIntoDevTools(): unknown;
144
+ }
145
+ type Listener = (event: InkCommitEvent) => void;
146
+ /**
147
+ * A process-global observer which composes with an already-installed hook.
148
+ * Renderer ids are always the ids returned to React by that hook.
149
+ */
150
+ declare class ReactCommitBridge {
151
+ #private;
152
+ register(renderer: RendererMetadata, delegatedId?: unknown): unknown;
153
+ commit(rendererId: unknown, fiberRoot: FiberRootLike): void;
154
+ unmount(rendererId: unknown, fiber: unknown): void;
155
+ subscribe(listener: Listener): () => void;
156
+ roots(): readonly InkDomElement[];
157
+ hasInkRenderer(): boolean;
158
+ }
159
+ /** Install or reuse the bridge without replacing the user's hook behavior. */
160
+ declare function installReactCommitBridge(target?: typeof globalThis): ReactCommitBridge;
161
+ /**
162
+ * Enable Ink's existing reconciler seam directly. This intentionally does not
163
+ * set DEV and therefore cannot load the DevTools UI/backend or open a socket.
164
+ */
165
+ declare function activateInkRendererObservation(reconciler: InkReconcilerInstrumentation, target?: typeof globalThis): ReactCommitBridge;
166
+ /** Fail closed instead of accepting a foreign or incomplete committed root. */
167
+ declare function requireCommittedInkRoot(event: InkCommitEvent): InkDomElement;
168
+
169
+ export { type EnvSource as E, type InkDomElement as I, type MeasureElement as M, type ProbeRuntime as P, ReactCommitBridge as R, type InkFrameCapture as a, type InkCommitEvent as b, type InkDomNode as c, type InkObservation as d, type InkReconcilerInstrumentation as e, type InkRendererRegistration as f, activateInkRendererObservation as g, isInstrumented as h, installReactCommitBridge as i, observeInkTree as o, requireCommittedInkRoot as r };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@termwright/probe-ink",
3
- "version": "0.2.0",
4
- "description": "zero-config Ink 7 probe: render interception and host-tree observation",
3
+ "version": "0.3.0",
4
+ "description": "Ink application instrumentation for Termwright end-to-end semantic tests",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "type": "module",
12
12
  "engines": {
13
- "node": ">=22"
13
+ "node": "^22.0.0 || ^24.0.0"
14
14
  },
15
15
  "exports": {
16
16
  ".": {
@@ -34,23 +34,27 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@termwright/protocol": "0.2.0",
38
- "@termwright/probe-runtime": "0.2.0",
39
- "@termwright/recognizers": "^0.2.0"
37
+ "@termwright/evidence-provider": "0.3.0",
38
+ "@termwright/protocol": "0.3.0",
39
+ "@termwright/probe-runtime": "0.3.0",
40
+ "@termwright/pty": "0.3.0",
41
+ "@termwright/vt": "0.3.0",
42
+ "@termwright/recognizers": "^0.3.0"
40
43
  },
41
44
  "peerDependencies": {
42
- "ink": ">=7.1.0 <8",
45
+ "ink": "7.1.1",
43
46
  "react": ">=19.2.0"
44
47
  },
45
48
  "devDependencies": {
46
49
  "@types/react": "^19.2.18",
47
- "ink": "^7.1.1",
50
+ "ink": "7.1.1",
48
51
  "react": "^19.2.8",
49
- "@termwright/ink": "0.2.0"
52
+ "@termwright/driver": "0.3.0",
53
+ "@termwright/resource-broker": "0.3.0"
50
54
  },
51
55
  "scripts": {
52
- "build": "tsup src/index.ts src/node-hook.ts src/bun-preload.ts src/instrument.ts --format esm --dts --sourcemap --clean",
56
+ "build": "tsup src/index.ts src/node-hook.ts src/bun-preload.ts src/instrument.ts src/instrumentation.ts --format esm --dts --sourcemap --clean",
53
57
  "typecheck": "tsc --noEmit",
54
- "test": "vitest run"
58
+ "test": "pnpm --dir ../.. test -- -- --run packages/probe-ink"
55
59
  }
56
60
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shim.ts"],"sourcesContent":["/** Replacement source for Ink's public entry module. */\n\n/** Marker on the shim's re-import of the untouched module. */\nexport const ORIGINAL_MARKER = 'termwright-original=1';\n\n/**\n * Ink's ESM entry under ordinary node_modules and Bun's versioned cache.\n * Matching the resolved path, rather than the bare specifier, is required by\n * both loader APIs used here.\n */\nexport const INK_ENTRY_PATTERN = /[\\\\/](?:ink|ink@[^\\\\/]+)[\\\\/]build[\\\\/]index\\.js$/u;\n\n/** The separately-built runtime imported by replacement module source. */\nexport const INSTRUMENT_URL = new URL('./instrument.js', import.meta.url).href;\n\nexport function shouldShim(urlOrPath: string): boolean {\n if (urlOrPath.includes(ORIGINAL_MARKER)) return false;\n return INK_ENTRY_PATTERN.test(urlOrPath.split('?')[0] ?? '');\n}\n\nexport function originalUrl(urlOrPath: string): string {\n return `${urlOrPath}${urlOrPath.includes('?') ? '&' : '?'}${ORIGINAL_MARKER}`;\n}\n\n/**\n * Forward the complete Ink namespace and shadow only `render`.\n *\n * The wrapper receives the already-marked original namespace, so the runtime\n * never imports `ink` itself and cannot recurse through the loader hook.\n */\nexport function buildShimSource(target: string, instrumentUrl = INSTRUMENT_URL): string {\n const original = JSON.stringify(originalUrl(target));\n const instrument = JSON.stringify(instrumentUrl);\n return `import * as __termwright_original from ${original};\nimport {wrapInkRender as __termwright_wrap} from ${instrument};\nexport * from ${original};\n\nexport const render = __termwright_wrap(__termwright_original);\n`;\n}\n"],"mappings":";AAGO,IAAM,kBAAkB;AAOxB,IAAM,oBAAoB;AAG1B,IAAM,iBAAiB,IAAI,IAAI,mBAAmB,YAAY,GAAG,EAAE;AAEnE,SAAS,WAAW,WAA4B;AACrD,MAAI,UAAU,SAAS,eAAe,EAAG,QAAO;AAChD,SAAO,kBAAkB,KAAK,UAAU,MAAM,GAAG,EAAE,CAAC,KAAK,EAAE;AAC7D;AAEO,SAAS,YAAY,WAA2B;AACrD,SAAO,GAAG,SAAS,GAAG,UAAU,SAAS,GAAG,IAAI,MAAM,GAAG,GAAG,eAAe;AAC7E;AAQO,SAAS,gBAAgB,QAAgB,gBAAgB,gBAAwB;AACtF,QAAM,WAAW,KAAK,UAAU,YAAY,MAAM,CAAC;AACnD,QAAM,aAAa,KAAK,UAAU,aAAa;AAC/C,SAAO,0CAA0C,QAAQ;AAAA,mDACR,UAAU;AAAA,gBAC7C,QAAQ;AAAA;AAAA;AAAA;AAIxB;","names":[]}