@termwright/probe-ink 0.2.0 → 0.3.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react-commit-bridge.ts","../src/annotations.ts","../src/observe.ts","../src/version.ts","../src/probe-info.ts","../src/session.ts"],"sourcesContent":["/** Minimal React renderer instrumentation observer used by the Ink probe spike. */\n\nimport type { InkDomElement } from './observe.js';\n\nconst BRIDGE = Symbol.for('@termwright/probe-ink/react-commit-bridge.v1');\n\ninterface RendererMetadata {\n readonly rendererPackageName?: unknown;\n readonly rendererVersion?: unknown;\n}\n\ninterface FiberRootLike {\n readonly containerInfo?: unknown;\n readonly current?: FiberLike;\n}\n\ninterface FiberLike {\n readonly stateNode?: unknown;\n readonly memoizedProps?: unknown;\n readonly child?: FiberLike | null;\n readonly sibling?: FiberLike | null;\n}\n\ninterface DevToolsHookLike {\n readonly supportsFiber?: boolean;\n inject?(renderer: RendererMetadata): unknown;\n onCommitFiberRoot?(rendererId: unknown, root: FiberRootLike, ...rest: readonly unknown[]): void;\n onCommitFiberUnmount?(rendererId: unknown, fiber: unknown): void;\n [BRIDGE]?: ReactCommitBridge;\n [key: PropertyKey]: unknown;\n}\n\nexport interface InkRendererRegistration {\n readonly rendererId: unknown;\n readonly packageName: 'ink';\n readonly version?: string;\n}\n\nexport type InkCommitEvent =\n | {\n readonly type: 'commit';\n readonly renderer: InkRendererRegistration;\n readonly fiberRoot: FiberRootLike;\n readonly root: InkDomElement;\n }\n | {\n readonly type: 'unmount';\n readonly renderer: InkRendererRegistration;\n readonly fiber: unknown;\n }\n | {\n readonly type: 'invalid-root';\n readonly renderer: InkRendererRegistration;\n readonly fiberRoot: FiberRootLike;\n readonly containerInfo: unknown;\n };\n\nexport interface InkReconcilerInstrumentation {\n injectIntoDevTools(): unknown;\n}\n\nexport interface ReactCommitBridgeLease {\n readonly bridge: ReactCommitBridge;\n release(): void;\n}\n\n/**\n * Experimental, deliberately Fiber-dependent correlation used to measure\n * which source accessibility props Ink drops from its committed host DOM.\n * It is not used by the production observer or accepted as a stable seam.\n */\nexport interface InkHostPropCorrelation {\n readonly hostProps?: Readonly<Record<string, unknown>>;\n readonly sourceProps?: Readonly<Record<string, unknown>>;\n readonly accessibleName?: string;\n readonly ariaHidden?: boolean;\n}\n\ntype Listener = (event: InkCommitEvent) => void;\n\n/**\n * A process-global observer which composes with an already-installed hook.\n * Renderer ids are always the ids returned to React by that hook.\n */\nexport class ReactCommitBridge {\n readonly #renderers = new Map<unknown, InkRendererRegistration>();\n readonly #roots = new Map<object, InkDomElement>();\n readonly #listeners = new Set<Listener>();\n #nextRendererId = 1;\n\n register(renderer: RendererMetadata, delegatedId?: unknown): unknown {\n const rendererId = delegatedId === undefined ? this.#nextRendererId++ : delegatedId;\n if (typeof rendererId === 'number' && Number.isInteger(rendererId)) {\n this.#nextRendererId = Math.max(this.#nextRendererId, rendererId + 1);\n }\n if (renderer.rendererPackageName === 'ink') {\n this.#renderers.set(rendererId, {\n rendererId,\n packageName: 'ink',\n ...(typeof renderer.rendererVersion === 'string'\n ? { version: renderer.rendererVersion }\n : {}),\n });\n }\n return rendererId;\n }\n\n commit(rendererId: unknown, fiberRoot: FiberRootLike): void {\n const renderer = this.#renderers.get(rendererId);\n if (renderer === undefined) return;\n const containerInfo = fiberRoot.containerInfo;\n if (!isInkRoot(containerInfo)) {\n this.#emit({ type: 'invalid-root', renderer, fiberRoot, containerInfo });\n return;\n }\n this.#roots.set(fiberRoot as object, containerInfo);\n this.#emit({ type: 'commit', renderer, fiberRoot, root: containerInfo });\n }\n\n unmount(rendererId: unknown, fiber: unknown): void {\n const renderer = this.#renderers.get(rendererId);\n if (renderer !== undefined) this.#emit({ type: 'unmount', renderer, fiber });\n }\n\n subscribe(listener: Listener): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n roots(): readonly InkDomElement[] {\n return [...this.#roots.values()];\n }\n\n hasInkRenderer(): boolean {\n return this.#renderers.size > 0;\n }\n\n #emit(event: InkCommitEvent): void {\n for (const listener of this.#listeners) {\n try {\n listener(event);\n } catch {\n // Instrumentation observers must never break React's commit callback.\n }\n }\n }\n}\n\n/** Install or reuse the bridge without replacing the user's hook behavior. */\nexport function installReactCommitBridge(\n target: typeof globalThis = globalThis,\n): ReactCommitBridge {\n const holder = target as typeof globalThis & {\n __REACT_DEVTOOLS_GLOBAL_HOOK__?: DevToolsHookLike;\n };\n const existing = holder.__REACT_DEVTOOLS_GLOBAL_HOOK__;\n const installed = existing?.[BRIDGE];\n if (installed !== undefined) return installed;\n\n const bridge = new ReactCommitBridge();\n const hook = Object.create(existing ?? null) as DevToolsHookLike;\n Object.defineProperties(hook, {\n supportsFiber: { value: true, enumerable: true, configurable: true },\n inject: {\n configurable: true,\n value(renderer: RendererMetadata): unknown {\n const delegatedId = existing?.inject?.call(existing, renderer);\n return bridge.register(renderer, delegatedId);\n },\n },\n onCommitFiberRoot: {\n configurable: true,\n value(rendererId: unknown, root: FiberRootLike, ...rest: readonly unknown[]): void {\n try {\n existing?.onCommitFiberRoot?.call(existing, rendererId, root, ...rest);\n } finally {\n bridge.commit(rendererId, root);\n }\n },\n },\n onCommitFiberUnmount: {\n configurable: true,\n value(rendererId: unknown, fiber: unknown): void {\n try {\n existing?.onCommitFiberUnmount?.call(existing, rendererId, fiber);\n } finally {\n bridge.unmount(rendererId, fiber);\n }\n },\n },\n [BRIDGE]: { value: bridge },\n });\n try {\n const descriptor = Object.getOwnPropertyDescriptor(holder, '__REACT_DEVTOOLS_GLOBAL_HOOK__');\n if (\n descriptor?.configurable === true &&\n (('writable' in descriptor && descriptor.writable === false) ||\n (!('writable' in descriptor) && descriptor.set === undefined))\n ) {\n Object.defineProperty(holder, '__REACT_DEVTOOLS_GLOBAL_HOOK__', {\n value: hook,\n writable: true,\n enumerable: descriptor.enumerable ?? false,\n configurable: true,\n });\n } else {\n holder.__REACT_DEVTOOLS_GLOBAL_HOOK__ = hook;\n }\n } catch (cause) {\n throw new Error(\n 'Ink semantic probe unavailable: the existing React renderer instrumentation hook cannot be composed.',\n { cause },\n );\n }\n return bridge;\n}\n\ninterface BridgeLeaseRecord {\n readonly bridge: ReactCommitBridge;\n readonly hook: DevToolsHookLike;\n readonly priorDescriptor?: PropertyDescriptor;\n references: number;\n}\n\nconst bridgeLeases = new WeakMap<object, BridgeLeaseRecord>();\n\n/**\n * Acquire a process-hook lease for transactional adapter setup. The final\n * release restores the exact prior property descriptor, but only while our\n * hook is still current. A bridge installed independently is never removed.\n */\nexport function acquireReactCommitBridge(\n target: typeof globalThis = globalThis,\n): ReactCommitBridgeLease {\n const holder = target as typeof globalThis & {\n __REACT_DEVTOOLS_GLOBAL_HOOK__?: DevToolsHookLike;\n };\n const currentRecord = bridgeLeases.get(target);\n if (currentRecord !== undefined && holder.__REACT_DEVTOOLS_GLOBAL_HOOK__ === currentRecord.hook) {\n currentRecord.references += 1;\n return leaseFor(target, currentRecord);\n }\n\n const existingBridge = holder.__REACT_DEVTOOLS_GLOBAL_HOOK__?.[BRIDGE];\n const priorDescriptor = Object.getOwnPropertyDescriptor(holder, '__REACT_DEVTOOLS_GLOBAL_HOOK__');\n const bridge = installReactCommitBridge(target);\n const hook = holder.__REACT_DEVTOOLS_GLOBAL_HOOK__;\n if (hook === undefined) {\n throw new Error(\n 'Ink semantic probe unavailable: React renderer instrumentation hook installation disappeared.',\n );\n }\n // If another subsystem installed this bridge, this adapter may subscribe to\n // it but must not claim ownership of the process-global hook.\n if (existingBridge !== undefined) return { bridge, release() {} };\n const record: BridgeLeaseRecord = {\n bridge,\n hook,\n ...(priorDescriptor === undefined ? {} : { priorDescriptor }),\n references: 1,\n };\n bridgeLeases.set(target, record);\n return leaseFor(target, record);\n}\n\nfunction leaseFor(target: typeof globalThis, record: BridgeLeaseRecord): ReactCommitBridgeLease {\n let released = false;\n return {\n bridge: record.bridge,\n release() {\n if (released) return;\n released = true;\n record.references -= 1;\n if (record.references > 0) return;\n bridgeLeases.delete(target);\n const holder = target as typeof globalThis & {\n __REACT_DEVTOOLS_GLOBAL_HOOK__?: DevToolsHookLike;\n };\n if (holder.__REACT_DEVTOOLS_GLOBAL_HOOK__ !== record.hook) return;\n if (record.priorDescriptor === undefined) {\n delete holder.__REACT_DEVTOOLS_GLOBAL_HOOK__;\n } else {\n Object.defineProperty(holder, '__REACT_DEVTOOLS_GLOBAL_HOOK__', record.priorDescriptor);\n }\n },\n };\n}\n\nconst activatedReconcilers = new WeakMap<object, WeakSet<object>>();\n\n/**\n * Enable Ink's existing reconciler seam directly. This intentionally does not\n * set DEV and therefore cannot load the DevTools UI/backend or open a socket.\n */\nexport function activateInkRendererObservation(\n reconciler: InkReconcilerInstrumentation,\n target: typeof globalThis = globalThis,\n): ReactCommitBridge {\n const bridge = installReactCommitBridge(target);\n let bridges = activatedReconcilers.get(reconciler);\n if (bridges === undefined) {\n bridges = new WeakSet<object>();\n activatedReconcilers.set(reconciler, bridges);\n }\n if (!bridges.has(bridge)) {\n // React 19's reconciler currently returns false even after synchronously\n // calling hook.inject(). Registration, not that implementation-detail\n // return value, is the capability proof.\n reconciler.injectIntoDevTools();\n if (!bridge.hasInkRenderer())\n throw new Error(\n 'Ink semantic probe unavailable: React renderer instrumentation did not register Ink.',\n );\n bridges.add(bridge);\n }\n return bridge;\n}\n\n/**\n * Correlate committed Ink host objects with the nearest source component\n * props. This POC proves that `aria-label`/`aria-hidden`, which Ink omits from\n * normal-mode host DOM, remain recoverable through Fiber. The returned map is\n * a measurement aid, not a production contract: every field is structural\n * React internals and must fail absent rather than fabricate data.\n */\nexport function correlateInkHostProps(\n fiberRoot: FiberRootLike,\n options: { readonly maxFibers?: number } = {},\n): ReadonlyMap<InkDomElement, InkHostPropCorrelation> {\n const correlations = new Map<InkDomElement, InkHostPropCorrelation>();\n const maxFibers = options.maxFibers ?? 100_000;\n let visitedFibers = 0;\n const walk = (\n fiber: FiberLike | null | undefined,\n candidateSourceProps?: Readonly<Record<string, unknown>>,\n ): void => {\n for (\n let current = fiber;\n current !== null && current !== undefined;\n current = current.sibling\n ) {\n visitedFibers += 1;\n if (visitedFibers > maxFibers) {\n throw new Error(\n 'Ink Fiber accessibility correlation exceeded its bounded traversal limit.',\n );\n }\n const props = record(current.memoizedProps);\n const sourceProps = hasAccessibilitySourceProps(props) ? props : candidateSourceProps;\n if (isInkElement(current.stateNode)) {\n correlations.set(current.stateNode, {\n ...(props === undefined ? {} : { hostProps: props }),\n ...(sourceProps === undefined ? {} : { sourceProps }),\n ...(typeof sourceProps?.['aria-label'] === 'string'\n ? { accessibleName: sourceProps['aria-label'] }\n : {}),\n ...(typeof sourceProps?.['aria-hidden'] === 'boolean'\n ? { ariaHidden: sourceProps['aria-hidden'] }\n : {}),\n });\n walk(current.child, undefined);\n } else {\n walk(current.child, sourceProps);\n }\n }\n };\n walk(fiberRoot.current?.child);\n return correlations;\n}\n\n/** Fail closed instead of accepting a foreign or incomplete committed root. */\nexport function requireCommittedInkRoot(event: InkCommitEvent): InkDomElement {\n if (event.type !== 'commit') {\n throw new Error(\n 'Ink semantic probe unavailable: React renderer instrumentation did not expose expected committed Ink root.',\n );\n }\n return event.root;\n}\n\nfunction isInkRoot(value: unknown): value is InkDomElement {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as {\n readonly nodeName?: unknown;\n readonly childNodes?: unknown;\n };\n return candidate.nodeName === 'ink-root' && Array.isArray(candidate.childNodes);\n}\n\nfunction isInkElement(value: unknown): value is InkDomElement {\n if (typeof value !== 'object' || value === null) return false;\n const nodeName = (value as { readonly nodeName?: unknown }).nodeName;\n return (\n nodeName === 'ink-root' ||\n nodeName === 'ink-box' ||\n nodeName === 'ink-text' ||\n nodeName === 'ink-virtual-text'\n );\n}\n\nfunction record(value: unknown): Readonly<Record<string, unknown>> | undefined {\n return typeof value === 'object' && value !== null\n ? (value as Readonly<Record<string, unknown>>)\n : undefined;\n}\n\nfunction hasAccessibilitySourceProps(\n props: Readonly<Record<string, unknown>> | undefined,\n): boolean {\n return (\n props !== undefined &&\n (Object.hasOwn(props, 'aria-label') ||\n Object.hasOwn(props, 'aria-hidden') ||\n Object.hasOwn(props, 'aria-role') ||\n Object.hasOwn(props, 'aria-state'))\n );\n}\n","import type { ProbeAnnotations, ProtocolLimits } from '@termwright/protocol';\nimport { validateProbeAnnotations } from '@termwright/protocol';\n\nconst REGISTRY = Symbol.for('termwright.annotation.ink.v1');\n\ninterface StoredAnnotation {\n readonly role?: unknown;\n readonly name?: unknown;\n readonly description?: unknown;\n readonly testId?: unknown;\n readonly extended?: unknown;\n readonly actions?: unknown;\n readonly labelledBy?: readonly WeakRef<object>[];\n readonly describedBy?: readonly WeakRef<object>[];\n}\n\ninterface AnnotationSlot {\n readonly current?: StoredAnnotation;\n}\n\ninterface AnnotationChannel {\n readonly entries: WeakMap<object, AnnotationSlot>;\n readonly listeners: Set<() => void>;\n}\n\nfunction channel(): AnnotationChannel {\n const scope = globalThis as Record<PropertyKey, unknown>;\n const present = scope[REGISTRY] as Partial<AnnotationChannel> | undefined;\n if (present?.entries instanceof WeakMap && present.listeners instanceof Set) {\n return present as AnnotationChannel;\n }\n const created: AnnotationChannel = {\n entries: new WeakMap<object, AnnotationSlot>(),\n listeners: new Set<() => void>(),\n };\n Object.defineProperty(scope, REGISTRY, { configurable: true, value: created });\n return created;\n}\n\n/** Re-capture after an annotation attaches to a newly reconciled host. */\nexport function onInkAnnotationChange(handler: () => void): () => void {\n const listeners = channel().listeners;\n listeners.add(handler);\n return () => listeners.delete(handler);\n}\n\nfunction strings(\n refs: unknown,\n idFor: (node: object) => string,\n maxTargets: number,\n): string[] | null | undefined {\n if (refs === undefined) return undefined;\n if (!Array.isArray(refs)) return null;\n const length = Object.getOwnPropertyDescriptor(refs, 'length')?.value;\n if (!Number.isSafeInteger(length) || length < 0 || length > maxTargets) return null;\n const ids: string[] = [];\n for (let index = 0; index < length; index += 1) {\n try {\n const descriptor = Object.getOwnPropertyDescriptor(refs, String(index));\n if (descriptor === undefined || !('value' in descriptor)) return null;\n const ref = descriptor.value;\n if (!(ref instanceof WeakRef)) return null;\n const target = WeakRef.prototype.deref.call(ref) as object | undefined;\n if (target !== undefined) ids.push(idFor(target));\n } catch {\n return null;\n }\n }\n return ids.length === 0 ? undefined : ids;\n}\n\nfunction ownData(value: object, key: keyof StoredAnnotation): unknown {\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n return descriptor !== undefined && 'value' in descriptor ? descriptor.value : undefined;\n}\n\n/** Read author intent without taking a runtime dependency on the optional SDK. */\nexport function annotationForInkNode(\n node: object,\n idFor: (node: object) => string,\n limits: ProtocolLimits,\n): ProbeAnnotations | undefined {\n try {\n const slot = channel().entries.get(node);\n const value = slot?.current;\n if (value === undefined) return undefined;\n const role = ownData(value, 'role');\n const name = ownData(value, 'name');\n const description = ownData(value, 'description');\n const testId = ownData(value, 'testId');\n const extended = ownData(value, 'extended');\n const actions = ownData(value, 'actions');\n const labelledBy = strings(ownData(value, 'labelledBy'), idFor, limits.maxRelationTargets);\n const describedBy = strings(ownData(value, 'describedBy'), idFor, limits.maxRelationTargets);\n const candidate = {\n ...(role === undefined ? {} : { role }),\n ...(name === undefined ? {} : { name }),\n ...(description === undefined ? {} : { description }),\n ...(testId === undefined ? {} : { testId }),\n ...(extended === undefined ? {} : { extended }),\n ...(actions === undefined ? {} : { actions }),\n ...(labelledBy === undefined ? {} : { labelledBy }),\n ...(describedBy === undefined ? {} : { describedBy }),\n };\n if (Object.keys(candidate).length === 0) return undefined;\n const validated = validateProbeAnnotations(candidate, limits);\n return validated.ok ? validated.annotations : undefined;\n } catch {\n return undefined;\n }\n}\n","/** Ink's retained host tree to framework-neutral Probe IR. */\n\nimport type {\n ProbeAccessibilityHints,\n ProbeFrame,\n ProbeObject,\n ProbeObservedState,\n ProbeRect,\n ProbeUnobservableField,\n ProtocolLimits,\n} from '@termwright/protocol';\nimport { annotationForInkNode } from './annotations.js';\nimport type { RelativeGeometry } from './frame-capture.js';\n\n/** Structural subset of Ink's DOM node. No runtime import from `ink`. */\nexport interface InkDomElement {\n readonly nodeName: 'ink-root' | 'ink-box' | 'ink-text' | 'ink-virtual-text';\n readonly childNodes: readonly InkDomNode[];\n readonly parentNode?: InkDomElement;\n readonly style?: Readonly<Record<string, unknown>> & { readonly display?: string };\n readonly internal_static?: boolean;\n readonly staticNode?: InkDomElement;\n readonly internal_accessibility?: {\n readonly role?: string;\n readonly state?: {\n readonly checked?: boolean;\n readonly disabled?: boolean;\n readonly expanded?: boolean;\n readonly readonly?: boolean;\n readonly selected?: boolean;\n readonly busy?: boolean;\n readonly multiline?: boolean;\n readonly required?: boolean;\n readonly multiselectable?: boolean;\n };\n };\n}\n\nexport interface InkTextNode {\n readonly nodeName: '#text';\n readonly nodeValue: string;\n readonly parentNode?: InkDomElement;\n}\n\nexport type InkDomNode = InkDomElement | InkTextNode;\n\n/** Public Ink measurement function, kept injectable for tests and isolation. */\nexport type MeasureElement = (node: InkDomElement) => {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n};\n\nexport interface ObserveInkOptions {\n readonly frame: number;\n readonly limits: ProtocolLimits;\n /** The probe's own hidden Box. It is the sole injected node and is omitted. */\n readonly excluded?: InkDomElement | null;\n /** Renderer-retained roots (notably committed <Static>) detached by Ink later. */\n readonly retainedRoots?: readonly InkDomElement[];\n readonly retainedChildren?: ReadonlyMap<InkDomElement, readonly InkDomNode[]>;\n readonly measureElement?: MeasureElement;\n /** Geometry frozen by the certified 7.1.1 renderer instrumentation. */\n readonly geometry?: ReadonlyMap<InkDomElement, RelativeGeometry>;\n}\n\nexport interface InkObservation {\n readonly frame: ProbeFrame;\n readonly truncated: boolean;\n readonly geometryRegions: ReadonlyMap<string, 'live' | 'static'>;\n}\n\nconst isElement = (node: InkDomNode): node is InkDomElement => node.nodeName !== '#text';\n\n/**\n * Observe every Ink host element, including plain unannotated layout boxes.\n *\n * Source component names do not survive Ink's reconciler. `frameworkType` is\n * therefore deliberately one of Ink's four host kinds; inventing `Button` or\n * a component stack here would be false provenance.\n */\nexport function observeInkTree(root: InkDomElement, options: ObserveInkOptions): InkObservation {\n const objects: ProbeObject[] = [];\n const ids = identityStore(root);\n let truncated = false;\n const geometryRegions = new Map<string, 'live' | 'static'>();\n const visited = new Set<InkDomElement>();\n\n const visit = (\n node: InkDomElement,\n parent: InkDomElement | undefined,\n depth: number,\n ancestorHidden: boolean,\n ): void => {\n if (node === options.excluded || visited.has(node)) return;\n visited.add(node);\n if (depth > options.limits.maxDepth || objects.length >= options.limits.maxNodes) {\n truncated = true;\n return;\n }\n\n const hidden = ancestorHidden || node.style?.display === 'none';\n const state = observedState(node, !hidden);\n const annotations = annotationForInkNode(\n node,\n (target) => ids.idFor(target as InkDomElement),\n options.limits,\n );\n const accessibility = observedAccessibility(node);\n const geometry = geometryOf(node, options);\n const identity = ids.idFor(node);\n const region = options.geometry?.get(node)?.region;\n if (region !== undefined) geometryRegions.set(identity, region);\n // Probe IR's `text` is the object's own text, never a descendant-derived\n // accessible name. The recognizer applies name-from-content over the tree.\n const children = options.retainedChildren?.get(node) ?? node.childNodes;\n const text = isTextHost(node) ? textOf(children, options.limits.maxStringBytes) : undefined;\n const unobservable = unobservableFor(\n node,\n geometry?.intendedRect !== undefined,\n text !== undefined,\n );\n\n objects.push({\n identity: { kind: 'stable', value: identity },\n frameworkType: node.nodeName,\n ...(parent === undefined ? {} : { parent: ids.idFor(parent) }),\n ...(geometry === undefined ? {} : { geometry }),\n ...(state === undefined ? {} : { state }),\n ...(text === undefined ? {} : { text }),\n ...(accessibility === undefined ? {} : { accessibility }),\n ...(annotations === undefined ? {} : { annotations }),\n unobservable,\n });\n\n for (const child of children) {\n // Raw `#text` values are payload owned by their `ink-text` host, not a\n // fifth host kind. The text is retained on that host above.\n if (isElement(child)) visit(child, node, depth + 1, hidden);\n }\n };\n\n visit(root, undefined, 0, false);\n // Ink removes committed <Static> children from the live root but retains the\n // exact host subtree in root.staticNode for the separately rendered static\n // output. Observe that retained subtree as a root child when it is no longer\n // reachable through childNodes; the identity and captured layout stay exact.\n if (root.staticNode !== undefined) visit(root.staticNode, root, 1, false);\n for (const retained of options.retainedRoots ?? []) visit(retained, root, 1, false);\n return { frame: { frame: options.frame, objects }, truncated, geometryRegions };\n}\n\n/** Weak identity is stable for exactly the lifetime of Ink's host object. */\nconst stores = new WeakMap<InkDomElement, IdentityStore>();\n\ninterface IdentityStore {\n idFor(node: InkDomElement): string;\n}\n\nfunction identityStore(root: InkDomElement): IdentityStore {\n let store = stores.get(root);\n if (store !== undefined) return store;\n const ids = new WeakMap<InkDomElement, string>();\n let nextId = 0;\n store = {\n idFor(node) {\n const existing = ids.get(node);\n if (existing !== undefined) return existing;\n nextId += 1;\n const id = String(nextId);\n ids.set(node, id);\n return id;\n },\n };\n stores.set(root, store);\n return store;\n}\n\nfunction isTextHost(node: InkDomElement): boolean {\n return node.nodeName === 'ink-text' || node.nodeName === 'ink-virtual-text';\n}\n\nfunction observedAccessibility(node: InkDomElement): ProbeAccessibilityHints | undefined {\n const role = node.internal_accessibility?.role;\n return role === undefined ? undefined : { role };\n}\n\nfunction observedState(node: InkDomElement, displayed: boolean): ProbeObservedState | undefined {\n const accessibility = node.internal_accessibility?.state;\n const state: ProbeObservedState = {\n displayed,\n ...(accessibility?.checked === undefined ? {} : { checked: accessibility.checked }),\n ...(accessibility?.disabled === undefined ? {} : { disabled: accessibility.disabled }),\n ...(accessibility?.expanded === undefined ? {} : { expanded: accessibility.expanded }),\n ...(accessibility?.readonly === undefined ? {} : { readonly: accessibility.readonly }),\n ...(accessibility?.selected === undefined ? {} : { selected: accessibility.selected }),\n ...(accessibility?.busy === undefined ? {} : { busy: accessibility.busy }),\n ...(accessibility?.multiline === undefined ? {} : { multiline: accessibility.multiline }),\n ...(accessibility?.required === undefined ? {} : { required: accessibility.required }),\n ...(accessibility?.multiselectable === undefined\n ? {}\n : { multiselectable: accessibility.multiselectable }),\n };\n return state;\n}\n\nfunction geometryOf(\n node: InkDomElement,\n options: ObserveInkOptions,\n): { readonly intendedRect: ProbeRect; readonly visibleRect: ProbeRect } | undefined {\n const geometry = options.geometry?.get(node);\n return geometry === undefined\n ? undefined\n : { intendedRect: geometry.intended, visibleRect: geometry.visible };\n}\n\nfunction textOf(children: readonly InkDomNode[], maxBytes: number): string | undefined {\n const parts: string[] = [];\n let bytes = 0;\n\n const append = (value: string): void => {\n for (const codePoint of value) {\n const size = Buffer.byteLength(codePoint, 'utf8');\n if (bytes + size > maxBytes) return;\n parts.push(codePoint);\n bytes += size;\n }\n };\n\n // Raw #text children are this host's payload. Nested host elements retain\n // their own ProbeObjects, so folding them in here would violate the IR's\n // own-text contract and duplicate them during name-from-content inference.\n for (const child of children) {\n if (bytes >= maxBytes) break;\n if (!isElement(child)) append(child.nodeValue);\n }\n const text = parts.join('').replace(/\\s+/gu, ' ').trim();\n return text.length === 0 ? undefined : text;\n}\n\nfunction unobservableFor(\n node: InkDomElement,\n hasGeometry: boolean,\n hasText: boolean,\n): readonly ProbeUnobservableField[] {\n const result: ProbeUnobservableField[] = [\n 'focused',\n 'value',\n 'selectedIndex',\n 'textSelection',\n 'scroll',\n 'scrollExtent',\n 'paintOrder',\n ];\n const state = node.internal_accessibility?.state;\n if (state?.disabled === undefined) result.push('disabled');\n if (state?.checked === undefined) result.push('checked');\n if (state?.expanded === undefined) result.push('expanded');\n if (state?.readonly === undefined) result.push('readonly');\n if (state?.selected === undefined) result.push('selected');\n if (state?.busy === undefined) result.push('busy');\n if (state?.multiline === undefined) result.push('multiline');\n if (!hasGeometry) result.push('intendedRect', 'visibleRect');\n if (!hasText && isTextHost(node)) result.push('text');\n return result;\n}\n","/** Synchronized from package.json by scripts/sync-protocol-version.mjs. */\nexport const PACKAGE_VERSION = '0.3.1';\n","import type { ProbeInfo } from '@termwright/protocol';\nimport { instrumentationSentinel, INK_VERSION } from './instrumentation.js';\nimport { PACKAGE_VERSION } from './version.js';\n\n/** Static probe identity, kept independent from the render-session runtime. */\nexport function probeInfo(\n frameworkVersion = instrumentationSentinel()?.frameworkVersion ?? INK_VERSION,\n): ProbeInfo {\n return {\n framework: 'ink',\n frameworkVersion,\n probeVersion: PACKAGE_VERSION,\n identityKind: 'stable',\n capabilities: ['stable-identity', 'intended-rect', 'visible-rect', 'annotations'],\n instrumentation: {\n highestTier: 'T3',\n semanticClass: 'A',\n degradedCapabilities: [],\n },\n };\n}\n","/** Certified Ink render capture to revision-paired semantic snapshots. */\n\nimport type { ProbeFrame, ProtocolLimits, SemanticSnapshot } from '@termwright/protocol';\nimport { writeWindowsConsoleMarker } from '@termwright/pty';\nimport { recognize } from '@termwright/recognizers';\nimport type { ProbeChannel } from '@termwright/probe-runtime';\nimport { observeInkTree, type InkDomElement } from './observe.js';\nimport type { InkFrameCapture } from './frame-capture.js';\nimport type { InkTerminalTracker, TerminalPosition } from './terminal-tracker.js';\nexport { probeInfo } from './probe-info.js';\n\nexport interface InkSessionOptions {\n readonly channel: ProbeChannel;\n readonly resolveRoot: () => InkDomElement | null;\n readonly resolveExcluded?: () => InkDomElement | null;\n readonly resolveCapture: (root: InkDomElement) => InkFrameCapture | undefined;\n /** Resolves after Ink has enqueued and flushed every stdout write for the captured render. */\n readonly waitForRenderFlush: () => Promise<void>;\n readonly stdout: NodeJS.WriteStream;\n /** Writes the authenticated marker through the same ordered transport as the frame. */\n readonly writeMarker: (marker: string) => Promise<void>;\n readonly tracker: InkTerminalTracker;\n readonly onGuaranteeViolation?: (error: Error) => void;\n}\n\nexport interface InkProbeSession {\n readonly revision: number;\n readonly frames: number;\n /** Freeze a renderer commit; refresh-only calls wait when the host tree is ahead of its capture. */\n notifyRender(options?: {\n readonly allowUnsettled?: boolean;\n /** Resolve with the first publication at or causally after this frame. */\n readonly awaitPublication?: boolean;\n }): Promise<number | null>;\n flush(): Promise<void>;\n stop(): void;\n}\n\ninterface FrozenFrame {\n readonly number: number;\n readonly capture: InkFrameCapture;\n readonly observation: ReturnType<typeof observeInkTree>;\n}\n\nexport function createInkSession(options: InkSessionOptions): InkProbeSession {\n let revision = 0;\n let frames = 0;\n let latestFrame = 0;\n let stopped = false;\n let queue: Promise<void> = Promise.resolve();\n const publicationWaiters: Array<{\n readonly targetFrame: number;\n readonly resolve: (revision: number) => void;\n readonly reject: (error: Error) => void;\n }> = [];\n\n const fail = (error: unknown): void => {\n if (stopped) return;\n stopped = true;\n const failure = error instanceof Error ? error : new Error(String(error));\n for (const waiter of publicationWaiters.splice(0)) waiter.reject(failure);\n options.onGuaranteeViolation?.(failure);\n options.channel.close();\n };\n\n const stop = (): void => {\n if (stopped) return;\n stopped = true;\n const failure = new Error('Ink probe stopped');\n for (const waiter of publicationWaiters.splice(0)) waiter.reject(failure);\n // A normal application exit or explicit cleanup is not a semantic\n // guarantee violation. Keep the typed failure callback exclusively for\n // capture/publication/marker faults, while graceful teardown simply\n // closes the producer after rejecting its owned causal waiters.\n options.channel.close();\n };\n\n const resolvePublications = (frame: number, publishedRevision: number): void => {\n for (let index = publicationWaiters.length - 1; index >= 0; index -= 1) {\n const waiter = publicationWaiters[index];\n if (waiter === undefined || waiter.targetFrame > frame) continue;\n publicationWaiters.splice(index, 1);\n waiter.resolve(publishedRevision);\n }\n };\n\n const publish = async (frozen: FrozenFrame): Promise<number | null> => {\n await nextMacrotask();\n // A marker authenticates the terminal bytes for this render, so it must\n // follow Ink's own stdout flush boundary, not just the probe's shadow drain.\n await options.waitForRenderFlush();\n await options.tracker.drain();\n if (stopped) return null;\n if (!options.channel.isOpen) {\n fail(new Error('Ink semantic channel closed before publication'));\n return null;\n }\n if (frozen.number !== latestFrame) {\n options.channel.recordCoalescedEvent();\n return null;\n }\n const context = frozen.capture.context;\n if (context === undefined) throw new Error('certified Ink frame context is unavailable');\n if (frozen.capture.screenReader) {\n throw new Error('Ink screen-reader output has no authoritative per-node cell geometry');\n }\n const position = options.tracker.position();\n if ((context.alternateScreen ? 'alternate' : 'normal') !== position.buffer) {\n throw new Error('Ink render mode and committed VT buffer disagree');\n }\n const columns = options.stdout.columns ?? 80;\n const rows = options.stdout.rows ?? 24;\n const qualified = qualifyFrame(frozen, position, columns, rows);\n revision += 1;\n const snapshot: SemanticSnapshot = recognize(qualified, {\n sessionId: options.channel.session.sessionId,\n revision,\n columns,\n rows,\n framework: 'ink',\n paintOrderKnown: false,\n maxStringBytes: options.channel.session.limits.maxStringBytes,\n });\n const marker = options.channel.publish(snapshot, {\n probeEvents: qualified.objects.length + (qualified.operations?.length ?? 0),\n });\n if (marker === undefined) throw new Error('Ink semantic publication was refused');\n // There must be no async gap between the final frame check and enqueueing\n // its marker: a newer Ink render could otherwise write in between them.\n // The selected transport establishes FRAME -> MARKER; awaiting it makes\n // `flush()` an actual publication boundary for teardown.\n await options.writeMarker(marker);\n resolvePublications(frozen.number, revision);\n return revision;\n };\n\n return {\n get revision() {\n return revision;\n },\n get frames() {\n return frames;\n },\n notifyRender(notifyOptions = {}) {\n if (stopped) return Promise.resolve(null);\n try {\n const root = options.resolveRoot();\n if (root === null) throw new Error('Ink committed frame has no retained root');\n const capture = options.resolveCapture(root);\n if (capture === undefined || capture.root !== root) {\n throw new Error('Ink committed frame has no matching certified renderer capture');\n }\n const excluded = options.resolveExcluded?.();\n const observation = observeInkTree(root, {\n frame: frames,\n limits: options.channel.session.limits as ProtocolLimits,\n ...(excluded === undefined ? {} : { excluded }),\n ...(capture.staticRoots.length === 0 ? {} : { retainedRoots: capture.staticRoots }),\n ...(capture.staticChildren.size === 0\n ? {}\n : { retainedChildren: capture.staticChildren }),\n geometry: capture.geometry,\n });\n // Layout effects can register annotations after React mutates the host\n // tree but before Ink's throttled renderer has produced the matching\n // capture. That is a transient refresh state, not a committed frame\n // whose guaranteed geometry may be downgraded. The subsequent real\n // onRender call freezes it. Renderer-originated calls remain strict.\n if (hasDisplayedNodeWithoutGeometry(observation.frame)) {\n if (notifyOptions.allowUnsettled === true) return Promise.resolve(null);\n throw new Error(\n 'certified Ink renderer capture is missing geometry for a displayed host node',\n );\n }\n frames += 1;\n latestFrame = frames;\n const frozen = { number: frames, capture, observation };\n const boundary =\n notifyOptions.awaitPublication === true\n ? new Promise<number>((resolve, reject) => {\n publicationWaiters.push({ targetFrame: frozen.number, resolve, reject });\n })\n : null;\n const publication = queue\n .then(() => publish(frozen))\n .catch((error) => {\n fail(error);\n return null;\n });\n queue = publication.then(() => undefined);\n return boundary ?? publication;\n } catch (error) {\n fail(error);\n return Promise.resolve(null);\n }\n },\n async flush() {\n await queue.catch(() => undefined);\n },\n stop,\n };\n}\n\nfunction hasDisplayedNodeWithoutGeometry(frame: ProbeFrame): boolean {\n return frame.objects.some(\n (object) => object.state?.displayed !== false && object.geometry?.intendedRect === undefined,\n );\n}\n\nfunction qualifyFrame(\n frozen: FrozenFrame,\n position: TerminalPosition,\n columns: number,\n rows: number,\n): ProbeFrame {\n const { capture, observation } = frozen;\n const context = capture.context as NonNullable<InkFrameCapture['context']>;\n const fullscreen = context.stdoutIsTTY && capture.liveRows >= context.rows;\n const liveOrigin = context.alternateScreen\n ? 0\n : !context.interactive\n ? position.row\n : context.debug || fullscreen\n ? position.row - Math.max(0, capture.liveRows - 1)\n : position.row - capture.liveRows;\n const staticOrigin = liveOrigin - capture.staticRows;\n\n return {\n ...observation.frame,\n objects: observation.frame.objects.map((object) => {\n const region = observation.geometryRegions.get(object.identity.value);\n const geometry = object.geometry;\n if (\n geometry?.intendedRect === undefined ||\n geometry.visibleRect === undefined ||\n region === undefined\n )\n return object;\n const origin = region === 'live' ? liveOrigin : staticOrigin;\n const intendedRect = shift(geometry.intendedRect, origin);\n const visibleRect =\n context.interactive || region === 'static' || context.debug\n ? viewportIntersection(shift(geometry.visibleRect, origin), columns, rows)\n : { row: Math.min(Math.max(origin, 0), rows), column: 0, width: 0, height: 0 };\n return { ...object, geometry: { intendedRect, visibleRect } };\n }),\n };\n}\n\nfunction shift(\n rect: import('@termwright/protocol').ProbeRect,\n rows: number,\n): import('@termwright/protocol').ProbeRect {\n return { ...rect, row: rect.row + rows };\n}\n\nfunction viewportIntersection(\n rect: import('@termwright/protocol').ProbeRect,\n columns: number,\n rows: number,\n): import('@termwright/protocol').ProbeRect {\n const column = Math.max(0, rect.column);\n const row = Math.max(0, rect.row);\n const right = Math.max(column, Math.min(columns, rect.column + rect.width));\n const bottom = Math.max(row, Math.min(rows, rect.row + rect.height));\n return { row, column, width: right - column, height: bottom - row };\n}\n\nfunction nextMacrotask(): Promise<void> {\n return new Promise((resolve) => setImmediate(resolve));\n}\n\nexport function createInkMarkerWriter(\n stream: NodeJS.WriteStream,\n options: {\n readonly certifiedHarness: boolean;\n readonly platform?: NodeJS.Platform;\n readonly writeWindowsMarker?: (fd: number, marker: string) => void;\n },\n): (marker: string) => Promise<void> {\n const platform = options.platform ?? process.platform;\n if (!options.certifiedHarness && platform === 'win32' && stream.isTTY === true) {\n const fd = (stream as NodeJS.WriteStream & { readonly fd?: unknown }).fd;\n if (typeof fd !== 'number' || !Number.isInteger(fd) || fd < 0) {\n return () =>\n Promise.reject(new Error('Ink stdout has no certifiable Windows console handle'));\n }\n const writeNative = options.writeWindowsMarker ?? writeWindowsConsoleMarker;\n return (marker) => {\n try {\n writeNative(fd, marker);\n return Promise.resolve();\n } catch (error) {\n return Promise.reject(error instanceof Error ? error : new Error(String(error)));\n }\n };\n }\n return (marker) =>\n new Promise((resolve, reject) => {\n if (stream.writableEnded || stream.destroyed) {\n reject(new Error('Ink stdout closed before the semantic render marker could be written'));\n return;\n }\n try {\n stream.write(marker, (error?: Error | null) => {\n if (error instanceof Error) reject(error);\n else resolve();\n });\n } catch (error) {\n reject(error instanceof Error ? error : new Error(String(error)));\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,152 @@
1
+ // src/instrumentation.ts
2
+ import { createHash } from "crypto";
3
+
4
+ // src/certified-instrumentation.json
5
+ var certified_instrumentation_default = {
6
+ framework: "ink",
7
+ profiles: [
8
+ {
9
+ coreSha256: "f632f6176e593183f0c0bb6e4a6e8a28d65f1c3899a33a84d4a95d26e1a82a58",
10
+ rendererSha256: "9e72b27731c38daac7e9f978e24f7bf1210c5cc26bf973e30f08c3ad4a9fe374",
11
+ version: "7.1.1"
12
+ }
13
+ ],
14
+ schemaVersion: 1
15
+ };
16
+
17
+ // src/instrumentation.ts
18
+ var BUILTIN_PROFILES = certified_instrumentation_default.profiles;
19
+ var INK_VERSION = BUILTIN_PROFILES.at(-1)?.version ?? "unsupported";
20
+ var INK_RENDER_CAPTURE = /* @__PURE__ */ Symbol.for("termwright.ink.render-capture.v1");
21
+ var INK_FRAME_CONTEXT = /* @__PURE__ */ Symbol.for("termwright.ink.frame-context.v1");
22
+ var INK_INSTRUMENTATION_SENTINEL = /* @__PURE__ */ Symbol.for("termwright.ink.instrumentation.v1");
23
+ var INK_RENDERER_PATTERN = /[\\/](?:ink|ink@[^\\/]+)[\\/]build[\\/]renderer\.js$/u;
24
+ var INK_CORE_PATTERN = /[\\/](?:ink|ink@[^\\/]+)[\\/]build[\\/]ink\.js$/u;
25
+ function instrumentationSentinel() {
26
+ const value = globalThis[INK_INSTRUMENTATION_SENTINEL];
27
+ if (value === null || typeof value !== "object") return void 0;
28
+ const candidate = value;
29
+ const profile = instrumentationProfiles().find(
30
+ (entry) => entry.version === candidate.frameworkVersion
31
+ );
32
+ return profile !== void 0 && candidate.version === 1 && candidate.rendererChecksum === profile.rendererSha256 && candidate.coreChecksum === profile.coreSha256 ? candidate : void 0;
33
+ }
34
+ function instrumentInkCore(path, source) {
35
+ if (!INK_CORE_PATTERN.test(path.split("?")[0] ?? "")) return void 0;
36
+ const checksum = createHash("sha256").update(source).digest("hex");
37
+ const profile = instrumentationProfiles().find((entry) => entry.coreSha256 === checksum);
38
+ if (profile === void 0) return void 0;
39
+ const needle = ` const { output, outputHeight, staticOutput } = render(this.rootNode, this.isScreenReaderEnabled);
40
+ this.options.onRender?.({ renderTime: performance.now() - startTime });`;
41
+ const replacement = ` const { output, outputHeight, staticOutput } = render(this.rootNode, this.isScreenReaderEnabled);
42
+ globalThis[Symbol.for("termwright.ink.frame-context.v1")]?.(this.rootNode, Object.freeze({ interactive: this.interactive, alternateScreen: this.alternateScreen, debug: this.options.debug === true, stdoutIsTTY: this.options.stdout.isTTY === true, rows: getWindowSize(this.options.stdout).rows }));
43
+ this.options.onRender?.({ renderTime: performance.now() - startTime });`;
44
+ if (source.split(needle).length !== 2) return void 0;
45
+ const sentinelNeedle = `const noop = () => { };`;
46
+ if (source.split(sentinelNeedle).length !== 2) return void 0;
47
+ const sentinel = `const __termwrightInkSentinelSymbol = Symbol.for("termwright.ink.instrumentation.v1");
48
+ const __termwrightInkPriorSentinel = globalThis[__termwrightInkSentinelSymbol] ?? {};
49
+ globalThis[__termwrightInkSentinelSymbol] = Object.freeze({ ...__termwrightInkPriorSentinel, version: 1, frameworkVersion: "${profile.version}", coreChecksum: "${checksum}" });`;
50
+ return source.replace(sentinelNeedle, `${sentinel}
51
+ ${sentinelNeedle}`).replace(needle, replacement);
52
+ }
53
+ function instrumentInkRenderer(path, source) {
54
+ if (!INK_RENDERER_PATTERN.test(path.split("?")[0] ?? "")) return void 0;
55
+ const checksum = createHash("sha256").update(source).digest("hex");
56
+ const profile = instrumentationProfiles().find((entry) => entry.rendererSha256 === checksum);
57
+ if (profile === void 0) return void 0;
58
+ const insertion = "import Output from './output.js';";
59
+ if (source.split(insertion).length !== 2) return void 0;
60
+ let output = source.replace(insertion, `${insertion}
61
+ ${runtime(profile.version, checksum)}`);
62
+ const screenReaderReturn = ` return {
63
+ output,
64
+ outputHeight,
65
+ staticOutput: staticOutput ? \`${"${staticOutput}"}\\n\` : '',
66
+ };`;
67
+ const screenReaderReplacement = ` return __termwrightCaptureInkRenderer(node, {
68
+ output,
69
+ outputHeight,
70
+ staticOutput: staticOutput ? \`${"${staticOutput}"}\\n\` : '',
71
+ }, true);`;
72
+ const normalReturn = ` return {
73
+ output: generatedOutput,
74
+ outputHeight,
75
+ // Newline at the end is needed, because static output doesn't have one, so
76
+ // interactive output will override last line of static output
77
+ staticOutput: staticOutput ? \`${"${staticOutput.get().output}"}\\n\` : '',
78
+ };`;
79
+ const normalReplacement = ` return __termwrightCaptureInkRenderer(node, {
80
+ output: generatedOutput,
81
+ outputHeight,
82
+ // Newline at the end is needed, because static output doesn't have one, so
83
+ // interactive output will override last line of static output
84
+ staticOutput: staticOutput ? \`${"${staticOutput.get().output}"}\\n\` : '',
85
+ }, false);`;
86
+ const emptyReturn = ` return {
87
+ output: '',
88
+ outputHeight: 0,
89
+ staticOutput: '',
90
+ };`;
91
+ const emptyReplacement = ` return __termwrightCaptureInkRenderer(node, {
92
+ output: '',
93
+ outputHeight: 0,
94
+ staticOutput: '',
95
+ }, isScreenReaderEnabled);`;
96
+ for (const [needle, replacement] of [
97
+ [screenReaderReturn, screenReaderReplacement],
98
+ [normalReturn, normalReplacement],
99
+ [emptyReturn, emptyReplacement]
100
+ ]) {
101
+ if (output.split(needle).length !== 2) return void 0;
102
+ output = output.replace(needle, replacement);
103
+ }
104
+ return output;
105
+ }
106
+ function runtime(frameworkVersion, checksum) {
107
+ return `const __termwrightInkCaptureSymbol = Symbol.for("termwright.ink.render-capture.v1");
108
+ const __termwrightInkSentinelSymbol = Symbol.for("termwright.ink.instrumentation.v1");
109
+ const __termwrightInkPriorSentinel = globalThis[__termwrightInkSentinelSymbol] ?? {};
110
+ globalThis[__termwrightInkSentinelSymbol] = Object.freeze({ ...__termwrightInkPriorSentinel, version: 1, frameworkVersion: "${frameworkVersion}", rendererChecksum: "${checksum}" });
111
+ const __termwrightCaptureInkRenderer = (root, result, screenReader) => {
112
+ const capture = globalThis[__termwrightInkCaptureSymbol];
113
+ if (typeof capture === "function") capture(root, result, screenReader);
114
+ return result;
115
+ };`;
116
+ }
117
+ function instrumentationProfiles() {
118
+ const override = certificationOverride();
119
+ return override === void 0 ? BUILTIN_PROFILES : [override, ...BUILTIN_PROFILES];
120
+ }
121
+ function certificationOverride() {
122
+ const raw = process.env["TERMWRIGHT_CERTIFICATION_HOOK_PROFILE"];
123
+ if (raw === void 0) return void 0;
124
+ if (process.env["GITHUB_ACTIONS"] !== "true") return void 0;
125
+ try {
126
+ const value = JSON.parse(raw);
127
+ const digest = process.env["TERMWRIGHT_CERTIFICATION_CANDIDATE_DIGEST"];
128
+ const revision = process.env["TERMWRIGHT_CERTIFICATION_SOURCE_REVISION"];
129
+ if (value["framework"] !== "ink" || !/^sha256:[a-f0-9]{64}$/u.test(digest ?? "") || revision !== process.env["GITHUB_SHA"] || value["sourceRevision"] !== revision || value["candidateDigest"] !== digest || typeof value["version"] !== "string" || !/^[a-f0-9]{64}$/u.test(String(value["rendererSha256"])) || !/^[a-f0-9]{64}$/u.test(String(value["coreSha256"])))
130
+ return void 0;
131
+ return {
132
+ version: value["version"],
133
+ rendererSha256: String(value["rendererSha256"]),
134
+ coreSha256: String(value["coreSha256"])
135
+ };
136
+ } catch {
137
+ return void 0;
138
+ }
139
+ }
140
+
141
+ export {
142
+ INK_VERSION,
143
+ INK_RENDER_CAPTURE,
144
+ INK_FRAME_CONTEXT,
145
+ INK_INSTRUMENTATION_SENTINEL,
146
+ INK_RENDERER_PATTERN,
147
+ INK_CORE_PATTERN,
148
+ instrumentationSentinel,
149
+ instrumentInkCore,
150
+ instrumentInkRenderer
151
+ };
152
+ //# sourceMappingURL=chunk-SLKX554P.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/instrumentation.ts","../src/certified-instrumentation.json"],"sourcesContent":["/** Exact, content-addressed instrumentation for Ink 7.1.1's renderer. */\n\nimport { createHash } from 'node:crypto';\nimport certified from './certified-instrumentation.json' with { type: 'json' };\n\ninterface InkInstrumentationProfile {\n readonly version: string;\n readonly rendererSha256: string;\n readonly coreSha256: string;\n}\n\nconst BUILTIN_PROFILES: readonly InkInstrumentationProfile[] = certified.profiles;\nexport const INK_VERSION = BUILTIN_PROFILES.at(-1)?.version ?? 'unsupported';\nexport const INK_RENDER_CAPTURE = Symbol.for('termwright.ink.render-capture.v1');\nexport const INK_FRAME_CONTEXT = Symbol.for('termwright.ink.frame-context.v1');\nexport const INK_INSTRUMENTATION_SENTINEL = Symbol.for('termwright.ink.instrumentation.v1');\n\nexport const INK_RENDERER_PATTERN = /[\\\\/](?:ink|ink@[^\\\\/]+)[\\\\/]build[\\\\/]renderer\\.js$/u;\nexport const INK_CORE_PATTERN = /[\\\\/](?:ink|ink@[^\\\\/]+)[\\\\/]build[\\\\/]ink\\.js$/u;\n\nexport interface InkInstrumentationSentinel {\n readonly version: 1;\n readonly frameworkVersion: string;\n readonly rendererChecksum: string;\n readonly coreChecksum: string;\n}\n\nexport interface InkRenderedOutput {\n readonly output: string;\n readonly outputHeight: number;\n readonly staticOutput: string;\n}\n\nexport type InkRenderCaptureHook = (\n root: object,\n result: InkRenderedOutput,\n screenReader: boolean,\n) => void;\n\nexport function instrumentationSentinel(): InkInstrumentationSentinel | undefined {\n const value = (globalThis as Record<PropertyKey, unknown>)[INK_INSTRUMENTATION_SENTINEL];\n if (value === null || typeof value !== 'object') return undefined;\n const candidate = value as Partial<InkInstrumentationSentinel>;\n const profile = instrumentationProfiles().find(\n (entry) => entry.version === candidate.frameworkVersion,\n );\n return profile !== undefined &&\n candidate.version === 1 &&\n candidate.rendererChecksum === profile.rendererSha256 &&\n candidate.coreChecksum === profile.coreSha256\n ? (candidate as InkInstrumentationSentinel)\n : undefined;\n}\n\n/** Transform the matching Ink class so every capture includes render-mode facts. */\nexport function instrumentInkCore(path: string, source: string): string | undefined {\n if (!INK_CORE_PATTERN.test(path.split('?')[0] ?? '')) return undefined;\n const checksum = createHash('sha256').update(source).digest('hex');\n const profile = instrumentationProfiles().find((entry) => entry.coreSha256 === checksum);\n if (profile === undefined) return undefined;\n const needle = ` const { output, outputHeight, staticOutput } = render(this.rootNode, this.isScreenReaderEnabled);\\n this.options.onRender?.({ renderTime: performance.now() - startTime });`;\n const replacement = ` const { output, outputHeight, staticOutput } = render(this.rootNode, this.isScreenReaderEnabled);\\n globalThis[Symbol.for(\"termwright.ink.frame-context.v1\")]?.(this.rootNode, Object.freeze({ interactive: this.interactive, alternateScreen: this.alternateScreen, debug: this.options.debug === true, stdoutIsTTY: this.options.stdout.isTTY === true, rows: getWindowSize(this.options.stdout).rows }));\\n this.options.onRender?.({ renderTime: performance.now() - startTime });`;\n if (source.split(needle).length !== 2) return undefined;\n const sentinelNeedle = `const noop = () => { };`;\n if (source.split(sentinelNeedle).length !== 2) return undefined;\n const sentinel = `const __termwrightInkSentinelSymbol = Symbol.for(\"termwright.ink.instrumentation.v1\");\\nconst __termwrightInkPriorSentinel = globalThis[__termwrightInkSentinelSymbol] ?? {};\\nglobalThis[__termwrightInkSentinelSymbol] = Object.freeze({ ...__termwrightInkPriorSentinel, version: 1, frameworkVersion: \"${profile.version}\", coreChecksum: \"${checksum}\" });`;\n return source\n .replace(sentinelNeedle, `${sentinel}\\n${sentinelNeedle}`)\n .replace(needle, replacement);\n}\n\n/** Transform only the byte-exact renderer shipped by Ink 7.1.1. */\nexport function instrumentInkRenderer(path: string, source: string): string | undefined {\n if (!INK_RENDERER_PATTERN.test(path.split('?')[0] ?? '')) return undefined;\n const checksum = createHash('sha256').update(source).digest('hex');\n const profile = instrumentationProfiles().find((entry) => entry.rendererSha256 === checksum);\n if (profile === undefined) return undefined;\n\n const insertion = \"import Output from './output.js';\";\n if (source.split(insertion).length !== 2) return undefined;\n let output = source.replace(insertion, `${insertion}\\n${runtime(profile.version, checksum)}`);\n\n const screenReaderReturn = ` return {\n output,\n outputHeight,\n staticOutput: staticOutput ? \\`${'${staticOutput}'}\\\\n\\` : '',\n };`;\n const screenReaderReplacement = ` return __termwrightCaptureInkRenderer(node, {\n output,\n outputHeight,\n staticOutput: staticOutput ? \\`${'${staticOutput}'}\\\\n\\` : '',\n }, true);`;\n const normalReturn = ` return {\n output: generatedOutput,\n outputHeight,\n // Newline at the end is needed, because static output doesn't have one, so\n // interactive output will override last line of static output\n staticOutput: staticOutput ? \\`${'${staticOutput.get().output}'}\\\\n\\` : '',\n };`;\n const normalReplacement = ` return __termwrightCaptureInkRenderer(node, {\n output: generatedOutput,\n outputHeight,\n // Newline at the end is needed, because static output doesn't have one, so\n // interactive output will override last line of static output\n staticOutput: staticOutput ? \\`${'${staticOutput.get().output}'}\\\\n\\` : '',\n }, false);`;\n const emptyReturn = ` return {\n output: '',\n outputHeight: 0,\n staticOutput: '',\n };`;\n const emptyReplacement = ` return __termwrightCaptureInkRenderer(node, {\n output: '',\n outputHeight: 0,\n staticOutput: '',\n }, isScreenReaderEnabled);`;\n\n for (const [needle, replacement] of [\n [screenReaderReturn, screenReaderReplacement],\n [normalReturn, normalReplacement],\n [emptyReturn, emptyReplacement],\n ] as const) {\n if (output.split(needle).length !== 2) return undefined;\n output = output.replace(needle, replacement);\n }\n return output;\n}\n\nfunction runtime(frameworkVersion: string, checksum: string): string {\n return `const __termwrightInkCaptureSymbol = Symbol.for(\"termwright.ink.render-capture.v1\");\nconst __termwrightInkSentinelSymbol = Symbol.for(\"termwright.ink.instrumentation.v1\");\nconst __termwrightInkPriorSentinel = globalThis[__termwrightInkSentinelSymbol] ?? {};\nglobalThis[__termwrightInkSentinelSymbol] = Object.freeze({ ...__termwrightInkPriorSentinel, version: 1, frameworkVersion: \"${frameworkVersion}\", rendererChecksum: \"${checksum}\" });\nconst __termwrightCaptureInkRenderer = (root, result, screenReader) => {\n const capture = globalThis[__termwrightInkCaptureSymbol];\n if (typeof capture === \"function\") capture(root, result, screenReader);\n return result;\n};`;\n}\n\nfunction instrumentationProfiles(): readonly InkInstrumentationProfile[] {\n const override = certificationOverride();\n return override === undefined ? BUILTIN_PROFILES : [override, ...BUILTIN_PROFILES];\n}\n\nfunction certificationOverride(): InkInstrumentationProfile | undefined {\n const raw = process.env['TERMWRIGHT_CERTIFICATION_HOOK_PROFILE'];\n if (raw === undefined) return undefined;\n if (process.env['GITHUB_ACTIONS'] !== 'true') return undefined;\n try {\n const value = JSON.parse(raw) as Record<string, unknown>;\n const digest = process.env['TERMWRIGHT_CERTIFICATION_CANDIDATE_DIGEST'];\n const revision = process.env['TERMWRIGHT_CERTIFICATION_SOURCE_REVISION'];\n if (\n value['framework'] !== 'ink' ||\n !/^sha256:[a-f0-9]{64}$/u.test(digest ?? '') ||\n revision !== process.env['GITHUB_SHA'] ||\n value['sourceRevision'] !== revision ||\n value['candidateDigest'] !== digest ||\n typeof value['version'] !== 'string' ||\n !/^[a-f0-9]{64}$/u.test(String(value['rendererSha256'])) ||\n !/^[a-f0-9]{64}$/u.test(String(value['coreSha256']))\n )\n return undefined;\n return {\n version: value['version'],\n rendererSha256: String(value['rendererSha256']),\n coreSha256: String(value['coreSha256']),\n };\n } catch {\n return undefined;\n }\n}\n","{\n \"framework\": \"ink\",\n \"profiles\": [\n {\n \"coreSha256\": \"f632f6176e593183f0c0bb6e4a6e8a28d65f1c3899a33a84d4a95d26e1a82a58\",\n \"rendererSha256\": \"9e72b27731c38daac7e9f978e24f7bf1210c5cc26bf973e30f08c3ad4a9fe374\",\n \"version\": \"7.1.1\"\n }\n ],\n \"schemaVersion\": 1\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { P as ProbeRuntime, I as InkDomElement, M as MeasureElement } from './observe-C853n-dp.js';
2
- export { E as EnvSource, a as InkDomNode, b as InkObservation, i as isInstrumented, o as observeInkTree } from './observe-C853n-dp.js';
3
- import { ProbeInfo } from '@termwright/protocol';
1
+ import { P as ProbeRuntime, I as InkDomElement, a as InkFrameCapture } from './react-commit-bridge-BzePo8gM.js';
2
+ export { E as EnvSource, b as InkCommitEvent, c as InkDomNode, d as InkObservation, e as InkReconcilerInstrumentation, f as InkRendererRegistration, M as MeasureElement, R as ReactCommitBridge, g as activateInkRendererObservation, i as installReactCommitBridge, h as isInstrumented, o as observeInkTree, r as requireCommittedInkRoot } from './react-commit-bridge-BzePo8gM.js';
4
3
  import { ProbeChannel } from '@termwright/probe-runtime';
4
+ import { ProbeInfo } from '@termwright/protocol';
5
+ import './instrumentation.js';
5
6
 
6
7
  /** Build an application command with the zero-config Ink preload attached. */
7
8
 
@@ -30,6 +31,8 @@ declare const INK_ENTRY_PATTERN: RegExp;
30
31
  declare const INSTRUMENT_URL: string;
31
32
  declare function shouldShim(urlOrPath: string): boolean;
32
33
  declare function originalUrl(urlOrPath: string): string;
34
+ /** Ink's renderer instance, resolved beside the intercepted public entry. */
35
+ declare function reconcilerUrl(urlOrPath: string): string;
33
36
  /**
34
37
  * Forward the complete Ink namespace and shadow only `render`.
35
38
  *
@@ -38,50 +41,52 @@ declare function originalUrl(urlOrPath: string): string;
38
41
  */
39
42
  declare function buildShimSource(target: string, instrumentUrl?: string): string;
40
43
 
41
- /** Truthful gate for Ink's live-region coordinates. */
42
- interface GeometryGateOptions {
43
- readonly alternateScreen: boolean;
44
- readonly interactive?: boolean;
45
- readonly stdoutIsTTY: boolean;
46
- /** Injectable only so the default-interactivity branch is deterministic. */
47
- readonly inCi?: boolean;
48
- }
49
44
  /**
50
- * Reproduce Ink 7's `resolveInteractiveOption` and
51
- * `resolveAlternateScreenOption`. Layout coordinates are terminal-absolute
52
- * only if Ink actually entered the alternate screen on a TTY.
45
+ * Shadow of bytes written through the supplied JavaScript stdout/stderr stream methods.
46
+ * Direct fd, native and descendant output is deliberately outside this boundary.
53
47
  */
54
- declare function canPublishInkGeometry(options: GeometryGateOptions): boolean;
48
+ interface TerminalPosition {
49
+ readonly row: number;
50
+ readonly column: number;
51
+ readonly buffer: 'normal' | 'alternate';
52
+ }
53
+ interface InkTerminalTracker {
54
+ drain(): Promise<void>;
55
+ position(): TerminalPosition;
56
+ resize(columns: number, rows: number): void;
57
+ stop(): void;
58
+ }
59
+
60
+ /** Static probe identity, kept independent from the render-session runtime. */
61
+ declare function probeInfo(frameworkVersion?: string): ProbeInfo;
55
62
 
56
- /** A committed Ink host tree to snapshot/commit/marker publication. */
63
+ /** Certified Ink render capture to revision-paired semantic snapshots. */
57
64
 
58
- /** What this probe truthfully offers at handshake time. */
59
- declare function probeInfo(): ProbeInfo;
60
65
  interface InkSessionOptions {
61
66
  readonly channel: ProbeChannel;
62
67
  readonly resolveRoot: () => InkDomElement | null;
63
68
  readonly resolveExcluded?: () => InkDomElement | null;
64
- readonly measureElement: MeasureElement;
69
+ readonly resolveCapture: (root: InkDomElement) => InkFrameCapture | undefined;
70
+ /** Resolves after Ink has enqueued and flushed every stdout write for the captured render. */
71
+ readonly waitForRenderFlush: () => Promise<void>;
65
72
  readonly stdout: NodeJS.WriteStream;
66
- readonly includeGeometry: boolean;
73
+ /** Writes the authenticated marker through the same ordered transport as the frame. */
74
+ readonly writeMarker: (marker: string) => Promise<void>;
75
+ readonly tracker: InkTerminalTracker;
76
+ readonly onGuaranteeViolation?: (error: Error) => void;
67
77
  }
68
78
  interface InkProbeSession {
69
79
  readonly revision: number;
70
80
  readonly frames: number;
71
- notifyRender(): void;
72
- /** Settle all captures queued at the time of the call. Never rejects. */
81
+ /** Freeze a renderer commit; refresh-only calls wait when the host tree is ahead of its capture. */
82
+ notifyRender(options?: {
83
+ readonly allowUnsettled?: boolean;
84
+ /** Resolve with the first publication at or causally after this frame. */
85
+ readonly awaitPublication?: boolean;
86
+ }): Promise<number | null>;
73
87
  flush(): Promise<void>;
74
88
  stop(): void;
75
89
  }
76
- /**
77
- * Pair each observed commit with its output bytes.
78
- *
79
- * Ink invokes `onRender` after layout and before writing. The tree is frozen
80
- * synchronously in that callback; deferring observation would let a microtask
81
- * or a throttled commit mutate the host objects before they were read. Only
82
- * marker placement is deferred: after Ink returns and writes, stdout is
83
- * drained and the authenticated marker is appended.
84
- */
85
90
  declare function createInkSession(options: InkSessionOptions): InkProbeSession;
86
91
 
87
- export { type GeometryGateOptions, INK_ENTRY_PATTERN, INSTRUMENT_URL, InkDomElement, type InkProbeSession, type InkSessionOptions, MeasureElement, ORIGINAL_MARKER, PROBE_ENTRIES, type ProbeCommand, ProbeRuntime, buildShimSource, canPublishInkGeometry, createInkSession, originalUrl, probeInfo, shouldShim, withProbe };
92
+ export { INK_ENTRY_PATTERN, INSTRUMENT_URL, InkDomElement, type InkProbeSession, type InkSessionOptions, ORIGINAL_MARKER, PROBE_ENTRIES, type ProbeCommand, ProbeRuntime, buildShimSource, createInkSession, originalUrl, probeInfo, reconcilerUrl, shouldShim, withProbe };
package/dist/index.js CHANGED
@@ -1,20 +1,25 @@
1
1
  import {
2
- canPublishInkGeometry,
2
+ ReactCommitBridge,
3
+ activateInkRendererObservation,
3
4
  createInkSession,
5
+ installReactCommitBridge,
4
6
  observeInkTree,
5
- probeInfo
6
- } from "./chunk-Y5WYMWRU.js";
7
+ probeInfo,
8
+ requireCommittedInkRoot
9
+ } from "./chunk-Q75BSILO.js";
7
10
  import {
8
11
  INK_ENTRY_PATTERN,
9
12
  INSTRUMENT_URL,
10
13
  ORIGINAL_MARKER,
11
14
  buildShimSource,
12
15
  originalUrl,
16
+ reconcilerUrl,
13
17
  shouldShim
14
- } from "./chunk-IUFXTMZ7.js";
18
+ } from "./chunk-CLY2SLYH.js";
15
19
  import {
16
20
  isInstrumented
17
- } from "./chunk-LO7YF74P.js";
21
+ } from "./chunk-67M2GX5S.js";
22
+ import "./chunk-SLKX554P.js";
18
23
 
19
24
  // src/launch.ts
20
25
  import { fileURLToPath, pathToFileURL } from "url";
@@ -27,27 +32,29 @@ function withProbe(runtime, argv) {
27
32
  const [interpreter, ...rest] = argv;
28
33
  const flag = runtime === "bun" ? "--preload" : "--import";
29
34
  return {
30
- command: [
31
- interpreter,
32
- flag,
33
- pathToFileURL(PROBE_ENTRIES[runtime]).href,
34
- ...rest
35
- ],
35
+ command: [interpreter, flag, runtimePreloadSpecifier(runtime, PROBE_ENTRIES[runtime]), ...rest],
36
36
  runtime
37
37
  };
38
38
  }
39
+ function runtimePreloadSpecifier(runtime, entry) {
40
+ return runtime === "bun" ? entry : pathToFileURL(entry).href;
41
+ }
39
42
  export {
40
43
  INK_ENTRY_PATTERN,
41
44
  INSTRUMENT_URL,
42
45
  ORIGINAL_MARKER,
43
46
  PROBE_ENTRIES,
47
+ ReactCommitBridge,
48
+ activateInkRendererObservation,
44
49
  buildShimSource,
45
- canPublishInkGeometry,
46
50
  createInkSession,
51
+ installReactCommitBridge,
47
52
  isInstrumented,
48
53
  observeInkTree,
49
54
  originalUrl,
50
55
  probeInfo,
56
+ reconcilerUrl,
57
+ requireCommittedInkRoot,
51
58
  shouldShim,
52
59
  withProbe
53
60
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/launch.ts"],"sourcesContent":["/** Build an application command with the zero-config Ink preload attached. */\n\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport type { ProbeRuntime } from './runtime.js';\n\n/** Published preload paths; callers never have to guess package layout. */\nexport const PROBE_ENTRIES = {\n bun: fileURLToPath(new URL('./bun-preload.js', import.meta.url)),\n node: fileURLToPath(new URL('./node-hook.js', import.meta.url)),\n} as const;\n\nexport interface ProbeCommand {\n readonly command: readonly string[];\n readonly runtime: ProbeRuntime;\n}\n\n/** Prefix a normal Node or Bun command with the matching preload flag. */\nexport function withProbe(runtime: ProbeRuntime, argv: readonly string[]): ProbeCommand {\n if (argv.length === 0) throw new Error('withProbe needs an interpreter in argv');\n const [interpreter, ...rest] = argv as [string, ...string[]];\n const flag = runtime === 'bun' ? '--preload' : '--import';\n return {\n command: [\n interpreter,\n flag,\n pathToFileURL(PROBE_ENTRIES[runtime]).href,\n ...rest,\n ],\n runtime,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAEA,SAAS,eAAe,qBAAqB;AAItC,IAAM,gBAAgB;AAAA,EAC3B,KAAK,cAAc,IAAI,IAAI,oBAAoB,YAAY,GAAG,CAAC;AAAA,EAC/D,MAAM,cAAc,IAAI,IAAI,kBAAkB,YAAY,GAAG,CAAC;AAChE;AAQO,SAAS,UAAU,SAAuB,MAAuC;AACtF,MAAI,KAAK,WAAW,EAAG,OAAM,IAAI,MAAM,wCAAwC;AAC/E,QAAM,CAAC,aAAa,GAAG,IAAI,IAAI;AAC/B,QAAM,OAAO,YAAY,QAAQ,cAAc;AAC/C,SAAO;AAAA,IACL,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA,cAAc,cAAc,OAAO,CAAC,EAAE;AAAA,MACtC,GAAG;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/launch.ts"],"sourcesContent":["/** Build an application command with the zero-config Ink preload attached. */\n\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport type { ProbeRuntime } from './runtime.js';\n\n/** Published preload paths; callers never have to guess package layout. */\nexport const PROBE_ENTRIES = {\n bun: fileURLToPath(new URL('./bun-preload.js', import.meta.url)),\n node: fileURLToPath(new URL('./node-hook.js', import.meta.url)),\n} as const;\n\nexport interface ProbeCommand {\n readonly command: readonly string[];\n readonly runtime: ProbeRuntime;\n}\n\n/** Prefix a normal Node or Bun command with the matching preload flag. */\nexport function withProbe(runtime: ProbeRuntime, argv: readonly string[]): ProbeCommand {\n if (argv.length === 0) throw new Error('withProbe needs an interpreter in argv');\n const [interpreter, ...rest] = argv as [string, ...string[]];\n const flag = runtime === 'bun' ? '--preload' : '--import';\n return {\n command: [interpreter, flag, runtimePreloadSpecifier(runtime, PROBE_ENTRIES[runtime]), ...rest],\n runtime,\n };\n}\n\n/** Node needs a file URL on Windows; Bun's Windows preload resolver needs a native path. */\nexport function runtimePreloadSpecifier(runtime: ProbeRuntime, entry: string): string {\n return runtime === 'bun' ? entry : pathToFileURL(entry).href;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAEA,SAAS,eAAe,qBAAqB;AAItC,IAAM,gBAAgB;AAAA,EAC3B,KAAK,cAAc,IAAI,IAAI,oBAAoB,YAAY,GAAG,CAAC;AAAA,EAC/D,MAAM,cAAc,IAAI,IAAI,kBAAkB,YAAY,GAAG,CAAC;AAChE;AAQO,SAAS,UAAU,SAAuB,MAAuC;AACtF,MAAI,KAAK,WAAW,EAAG,OAAM,IAAI,MAAM,wCAAwC;AAC/E,QAAM,CAAC,aAAa,GAAG,IAAI,IAAI;AAC/B,QAAM,OAAO,YAAY,QAAQ,cAAc;AAC/C,SAAO;AAAA,IACL,SAAS,CAAC,aAAa,MAAM,wBAAwB,SAAS,cAAc,OAAO,CAAC,GAAG,GAAG,IAAI;AAAA,IAC9F;AAAA,EACF;AACF;AAGO,SAAS,wBAAwB,SAAuB,OAAuB;AACpF,SAAO,YAAY,QAAQ,QAAQ,cAAc,KAAK,EAAE;AAC1D;","names":[]}
@@ -1,10 +1,14 @@
1
1
  import { ReactNode, ComponentType } from 'react';
2
2
  import { RenderOptions, Instance } from 'ink';
3
- import { M as MeasureElement, E as EnvSource } from './observe-C853n-dp.js';
3
+ import { connectProbe } from '@termwright/probe-runtime';
4
+ import { M as MeasureElement, E as EnvSource, e as InkReconcilerInstrumentation } from './react-commit-bridge-BzePo8gM.js';
4
5
  import '@termwright/protocol';
6
+ import './instrumentation.js';
5
7
 
6
8
  /** The render wrapper imported by the replacement Ink entry module. */
7
9
 
10
+ /** Private cross-package hook used by the shipped fixture runner. */
11
+ declare const INK_FLUSH_NEXT_RENDER: unique symbol;
8
12
  type InkRender = (node: ReactNode, options?: NodeJS.WriteStream | RenderOptions) => Instance;
9
13
  /** Runtime Ink surface forwarded by the shim, kept structural to avoid cycles. */
10
14
  interface InkModule {
@@ -15,6 +19,12 @@ interface InkModule {
15
19
  /** @internal Used only by the in-process component harness. */
16
20
  interface InstrumentInkOptions {
17
21
  readonly env?: EnvSource;
22
+ /** Exact pinned in-process harness; never enabled by the application shim. */
23
+ readonly certifiedHarness?: boolean;
24
+ /** Ink's existing React reconciler instrumentation seam. */
25
+ readonly reconciler?: InkReconcilerInstrumentation;
26
+ /** @internal Deterministic transport seam for setup-failure tests. */
27
+ readonly connect?: typeof connectProbe;
18
28
  }
19
29
  /**
20
30
  * Wrap the original `ink.render` while preserving every other Ink export.
@@ -23,4 +33,4 @@ interface InstrumentInkOptions {
23
33
  */
24
34
  declare function wrapInkRender(ink: InkModule, options?: InstrumentInkOptions): InkRender;
25
35
 
26
- export { type InkModule, type InstrumentInkOptions, wrapInkRender };
36
+ export { INK_FLUSH_NEXT_RENDER, type InkModule, type InstrumentInkOptions, wrapInkRender };