@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,358 +0,0 @@
1
- // src/annotations.ts
2
- import { validateProbeAnnotations } from "@termwright/protocol";
3
- var REGISTRY = /* @__PURE__ */ Symbol.for("termwright.annotation.ink.v1");
4
- function channel() {
5
- const scope = globalThis;
6
- const present = scope[REGISTRY];
7
- if (present?.entries instanceof WeakMap && present.listeners instanceof Set) {
8
- return present;
9
- }
10
- const created = {
11
- entries: /* @__PURE__ */ new WeakMap(),
12
- listeners: /* @__PURE__ */ new Set()
13
- };
14
- Object.defineProperty(scope, REGISTRY, { configurable: true, value: created });
15
- return created;
16
- }
17
- function onInkAnnotationChange(handler) {
18
- const listeners = channel().listeners;
19
- listeners.add(handler);
20
- return () => listeners.delete(handler);
21
- }
22
- function strings(refs, idFor, maxTargets) {
23
- if (refs === void 0) return void 0;
24
- if (!Array.isArray(refs)) return null;
25
- const length = Object.getOwnPropertyDescriptor(refs, "length")?.value;
26
- if (!Number.isSafeInteger(length) || length < 0 || length > maxTargets) return null;
27
- const ids = [];
28
- for (let index = 0; index < length; index += 1) {
29
- try {
30
- const descriptor = Object.getOwnPropertyDescriptor(refs, String(index));
31
- if (descriptor === void 0 || !("value" in descriptor)) return null;
32
- const ref = descriptor.value;
33
- if (!(ref instanceof WeakRef)) return null;
34
- const target = WeakRef.prototype.deref.call(ref);
35
- if (target !== void 0) ids.push(idFor(target));
36
- } catch {
37
- return null;
38
- }
39
- }
40
- return ids.length === 0 ? void 0 : ids;
41
- }
42
- function ownData(value, key) {
43
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
44
- return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
45
- }
46
- function annotationForInkNode(node, idFor, limits) {
47
- try {
48
- const slot = channel().entries.get(node);
49
- const value = slot?.current;
50
- if (value === void 0) return void 0;
51
- const role = ownData(value, "role");
52
- const name = ownData(value, "name");
53
- const description = ownData(value, "description");
54
- const testId = ownData(value, "testId");
55
- const extended = ownData(value, "extended");
56
- const actions = ownData(value, "actions");
57
- const labelledBy = strings(ownData(value, "labelledBy"), idFor, limits.maxRelationTargets);
58
- const describedBy = strings(ownData(value, "describedBy"), idFor, limits.maxRelationTargets);
59
- const candidate = {
60
- ...role === void 0 ? {} : { role },
61
- ...name === void 0 ? {} : { name },
62
- ...description === void 0 ? {} : { description },
63
- ...testId === void 0 ? {} : { testId },
64
- ...extended === void 0 ? {} : { extended },
65
- ...actions === void 0 ? {} : { actions },
66
- ...labelledBy === void 0 ? {} : { labelledBy },
67
- ...describedBy === void 0 ? {} : { describedBy }
68
- };
69
- if (Object.keys(candidate).length === 0) return void 0;
70
- const validated = validateProbeAnnotations(candidate, limits);
71
- return validated.ok ? validated.annotations : void 0;
72
- } catch {
73
- return void 0;
74
- }
75
- }
76
-
77
- // src/observe.ts
78
- var isElement = (node) => node.nodeName !== "#text";
79
- function observeInkTree(root, options) {
80
- const objects = [];
81
- const ids = identityStore(root);
82
- let truncated = false;
83
- const visit = (node, parent, depth, ancestorHidden) => {
84
- if (node === options.excluded) return;
85
- if (depth > options.limits.maxDepth || objects.length >= options.limits.maxNodes) {
86
- truncated = true;
87
- return;
88
- }
89
- const hidden = ancestorHidden || node.style?.display === "none";
90
- const state = observedState(node, !hidden);
91
- const annotations = annotationForInkNode(
92
- node,
93
- (target) => ids.idFor(target),
94
- options.limits
95
- );
96
- const accessibility = observedAccessibility(node);
97
- const geometry = geometryOf(node, options);
98
- const text = isTextHost(node) ? textOf(node, options.limits.maxStringBytes) : void 0;
99
- const unobservable = unobservableFor(node, geometry !== void 0, text !== void 0);
100
- objects.push({
101
- identity: { kind: "stable", value: ids.idFor(node) },
102
- frameworkType: node.nodeName,
103
- ...parent === void 0 ? {} : { parent: ids.idFor(parent) },
104
- ...geometry === void 0 ? {} : { geometry: { intendedRect: geometry } },
105
- ...state === void 0 ? {} : { state },
106
- ...text === void 0 ? {} : { text },
107
- ...accessibility === void 0 ? {} : { accessibility },
108
- ...annotations === void 0 ? {} : { annotations },
109
- unobservable
110
- });
111
- for (const child of node.childNodes) {
112
- if (isElement(child)) visit(child, node, depth + 1, hidden);
113
- }
114
- };
115
- visit(root, void 0, 0, false);
116
- return { frame: { frame: options.frame, objects }, truncated };
117
- }
118
- var stores = /* @__PURE__ */ new WeakMap();
119
- function identityStore(root) {
120
- let store = stores.get(root);
121
- if (store !== void 0) return store;
122
- const ids = /* @__PURE__ */ new WeakMap();
123
- let nextId = 0;
124
- store = {
125
- idFor(node) {
126
- const existing = ids.get(node);
127
- if (existing !== void 0) return existing;
128
- nextId += 1;
129
- const id = String(nextId);
130
- ids.set(node, id);
131
- return id;
132
- }
133
- };
134
- stores.set(root, store);
135
- return store;
136
- }
137
- function isTextHost(node) {
138
- return node.nodeName === "ink-text" || node.nodeName === "ink-virtual-text";
139
- }
140
- function observedAccessibility(node) {
141
- const role = node.internal_accessibility?.role;
142
- return role === void 0 ? void 0 : { role };
143
- }
144
- function observedState(node, displayed) {
145
- const accessibility = node.internal_accessibility?.state;
146
- const state = {
147
- displayed,
148
- ...accessibility?.checked === void 0 ? {} : { checked: accessibility.checked },
149
- ...accessibility?.disabled === void 0 ? {} : { disabled: accessibility.disabled },
150
- ...accessibility?.expanded === void 0 ? {} : { expanded: accessibility.expanded },
151
- ...accessibility?.readonly === void 0 ? {} : { readonly: accessibility.readonly },
152
- ...accessibility?.selected === void 0 ? {} : { selected: accessibility.selected },
153
- ...accessibility?.busy === void 0 ? {} : { busy: accessibility.busy },
154
- ...accessibility?.multiline === void 0 ? {} : { multiline: accessibility.multiline }
155
- };
156
- return state;
157
- }
158
- function geometryOf(node, options) {
159
- if (options.includeGeometry !== true || options.measureElement === void 0) return void 0;
160
- if (node.nodeName === "ink-virtual-text") return void 0;
161
- try {
162
- const measured = options.measureElement(node);
163
- if (!Number.isFinite(measured.x) || !Number.isFinite(measured.y) || !Number.isFinite(measured.width) || !Number.isFinite(measured.height) || measured.width <= 0 || measured.height <= 0) return void 0;
164
- return {
165
- row: Math.trunc(measured.y),
166
- column: Math.trunc(measured.x),
167
- width: Math.trunc(measured.width),
168
- height: Math.trunc(measured.height)
169
- };
170
- } catch {
171
- return void 0;
172
- }
173
- }
174
- function textOf(node, maxBytes) {
175
- const parts = [];
176
- let bytes = 0;
177
- const append = (value) => {
178
- for (const codePoint of value) {
179
- const size = Buffer.byteLength(codePoint, "utf8");
180
- if (bytes + size > maxBytes) return;
181
- parts.push(codePoint);
182
- bytes += size;
183
- }
184
- };
185
- for (const child of node.childNodes) {
186
- if (bytes >= maxBytes) break;
187
- if (!isElement(child)) append(child.nodeValue);
188
- }
189
- const text = parts.join("").replace(/\s+/gu, " ").trim();
190
- return text.length === 0 ? void 0 : text;
191
- }
192
- function unobservableFor(node, hasGeometry, hasText) {
193
- const result = [
194
- "focused",
195
- "value",
196
- "selectedIndex",
197
- "textSelection",
198
- "scroll",
199
- "scrollExtent",
200
- "visibleRect",
201
- "paintOrder"
202
- ];
203
- const state = node.internal_accessibility?.state;
204
- if (state?.disabled === void 0) result.push("disabled");
205
- if (state?.checked === void 0) result.push("checked");
206
- if (state?.expanded === void 0) result.push("expanded");
207
- if (state?.readonly === void 0) result.push("readonly");
208
- if (state?.selected === void 0) result.push("selected");
209
- if (state?.busy === void 0) result.push("busy");
210
- if (state?.multiline === void 0) result.push("multiline");
211
- if (!hasGeometry) result.push("intendedRect");
212
- if (!hasText && isTextHost(node)) result.push("text");
213
- return result;
214
- }
215
- function hasStaticContent(root) {
216
- const stack = [root];
217
- while (stack.length > 0) {
218
- const node = stack.pop();
219
- if (node.internal_static === true || node.staticNode !== void 0) return true;
220
- for (const child of node.childNodes) if (isElement(child)) stack.push(child);
221
- }
222
- return false;
223
- }
224
-
225
- // src/geometry.ts
226
- function canPublishInkGeometry(options) {
227
- const interactive = options.interactive ?? (!(options.inCi ?? runningInCi()) && options.stdoutIsTTY);
228
- return options.alternateScreen && interactive && options.stdoutIsTTY;
229
- }
230
- function runningInCi() {
231
- return enabledEnvironmentFlag("CI") || enabledEnvironmentFlag("CONTINUOUS_INTEGRATION");
232
- }
233
- function enabledEnvironmentFlag(name) {
234
- const value = process.env[name];
235
- return value !== void 0 && value !== "0" && value !== "false";
236
- }
237
-
238
- // src/version.ts
239
- var PACKAGE_VERSION = "0.2.0";
240
-
241
- // src/session.ts
242
- import { recognize } from "@termwright/recognizers";
243
- function probeInfo() {
244
- return {
245
- framework: "ink",
246
- probeVersion: PACKAGE_VERSION,
247
- identityKind: "stable",
248
- // The optional @termwright/ink SDK writes author intent to the shared weak
249
- // registry. Ink's own aria metadata travels separately as framework facts.
250
- capabilities: ["stable-identity", "annotations"]
251
- };
252
- }
253
- function createInkSession(options) {
254
- let revision = 0;
255
- let frames = 0;
256
- let latestFrame = 0;
257
- let staticSeen = false;
258
- let stopped = false;
259
- let queue = Promise.resolve();
260
- const fail = () => {
261
- if (stopped) return;
262
- stopped = true;
263
- options.channel.close();
264
- };
265
- const writeMarker = async (frame, marker) => {
266
- await nextMacrotask();
267
- if (stopped || !options.channel.isOpen) return;
268
- if (frame !== latestFrame) {
269
- options.channel.recordCoalescedEvent();
270
- return;
271
- }
272
- await drain(options.stdout);
273
- if (stopped || !options.channel.isOpen) return;
274
- if (frame !== latestFrame) {
275
- options.channel.recordCoalescedEvent();
276
- return;
277
- }
278
- options.stdout.write(marker);
279
- };
280
- return {
281
- get revision() {
282
- return revision;
283
- },
284
- get frames() {
285
- return frames;
286
- },
287
- notifyRender() {
288
- if (stopped) return;
289
- frames += 1;
290
- const frame = frames;
291
- latestFrame = frame;
292
- try {
293
- const root = options.resolveRoot();
294
- if (root === null) return;
295
- staticSeen ||= hasStaticContent(root);
296
- const includeGeometry = options.includeGeometry && !staticSeen;
297
- const excluded = options.resolveExcluded?.();
298
- const observation = observeInkTree(root, {
299
- frame,
300
- limits: options.channel.session.limits,
301
- ...excluded === void 0 ? {} : { excluded },
302
- measureElement: options.measureElement,
303
- includeGeometry
304
- });
305
- revision += 1;
306
- const snapshot = recognize(observation.frame, {
307
- sessionId: options.channel.session.sessionId,
308
- revision,
309
- columns: options.stdout.columns ?? 80,
310
- rows: options.stdout.rows ?? 24,
311
- framework: "ink",
312
- paintOrderKnown: false,
313
- maxStringBytes: options.channel.session.limits.maxStringBytes,
314
- qualified: options.channel.session.protocol === "termwright/2"
315
- });
316
- const marker = options.channel.publish(snapshot, {
317
- probeEvents: observation.frame.objects.length + (observation.frame.operations?.length ?? 0)
318
- });
319
- if (marker === void 0) return;
320
- queue = queue.then(() => writeMarker(frame, marker)).catch(fail);
321
- } catch {
322
- fail();
323
- }
324
- },
325
- async flush() {
326
- await queue.catch(() => void 0);
327
- },
328
- stop() {
329
- fail();
330
- }
331
- };
332
- }
333
- function nextMacrotask() {
334
- return new Promise((resolve) => setImmediate(resolve));
335
- }
336
- function drain(stream) {
337
- return new Promise((resolve) => {
338
- if (stream.writableEnded || stream.destroyed) {
339
- resolve();
340
- return;
341
- }
342
- try {
343
- stream.write("", () => resolve());
344
- } catch {
345
- resolve();
346
- }
347
- });
348
- }
349
-
350
- export {
351
- onInkAnnotationChange,
352
- observeInkTree,
353
- canPublishInkGeometry,
354
- PACKAGE_VERSION,
355
- probeInfo,
356
- createInkSession
357
- };
358
- //# sourceMappingURL=chunk-Y5WYMWRU.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/annotations.ts","../src/observe.ts","../src/geometry.ts","../src/version.ts","../src/session.ts"],"sourcesContent":["import type {\n ProbeAnnotations,\n ProtocolLimits,\n} 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';\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 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 };\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 = (\n node: InkDomElement,\n) => { readonly x: number; readonly y: number; readonly width: number; readonly height: number };\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 readonly measureElement?: MeasureElement;\n /** Only true when live-region coordinates are proven viewport-absolute. */\n readonly includeGeometry?: boolean;\n}\n\nexport interface InkObservation {\n readonly frame: ProbeFrame;\n readonly truncated: boolean;\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\n const visit = (\n node: InkDomElement,\n parent: InkDomElement | undefined,\n depth: number,\n ancestorHidden: boolean,\n ): void => {\n if (node === options.excluded) return;\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 // 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 text = isTextHost(node) ? textOf(node, options.limits.maxStringBytes) : undefined;\n const unobservable = unobservableFor(node, geometry !== undefined, text !== undefined);\n\n objects.push({\n identity: { kind: 'stable', value: ids.idFor(node) },\n frameworkType: node.nodeName,\n ...(parent === undefined ? {} : { parent: ids.idFor(parent) }),\n ...(geometry === undefined ? {} : { geometry: { intendedRect: 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 node.childNodes) {\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 return { frame: { frame: options.frame, objects }, truncated };\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 };\n return state;\n}\n\nfunction geometryOf(\n node: InkDomElement,\n options: ObserveInkOptions,\n): ProbeRect | undefined {\n if (options.includeGeometry !== true || options.measureElement === undefined) return undefined;\n if (node.nodeName === 'ink-virtual-text') return undefined;\n try {\n const measured = options.measureElement(node);\n if (\n !Number.isFinite(measured.x)\n || !Number.isFinite(measured.y)\n || !Number.isFinite(measured.width)\n || !Number.isFinite(measured.height)\n || measured.width <= 0\n || measured.height <= 0\n ) return undefined;\n return {\n row: Math.trunc(measured.y),\n column: Math.trunc(measured.x),\n width: Math.trunc(measured.width),\n height: Math.trunc(measured.height),\n };\n } catch {\n return undefined;\n }\n}\n\nfunction textOf(node: InkDomElement, 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 node.childNodes) {\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 'visibleRect',\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');\n if (!hasText && isTextHost(node)) result.push('text');\n return result;\n}\n\n/** `<Static>` moves the live region down by an offset Ink does not expose. */\nexport function hasStaticContent(root: InkDomElement): boolean {\n const stack: InkDomElement[] = [root];\n while (stack.length > 0) {\n const node = stack.pop() as InkDomElement;\n if (node.internal_static === true || node.staticNode !== undefined) return true;\n for (const child of node.childNodes) if (isElement(child)) stack.push(child);\n }\n return false;\n}\n","/** Truthful gate for Ink's live-region coordinates. */\n\nexport interface GeometryGateOptions {\n readonly alternateScreen: boolean;\n readonly interactive?: boolean;\n readonly stdoutIsTTY: boolean;\n /** Injectable only so the default-interactivity branch is deterministic. */\n readonly inCi?: boolean;\n}\n\n/**\n * Reproduce Ink 7's `resolveInteractiveOption` and\n * `resolveAlternateScreenOption`. Layout coordinates are terminal-absolute\n * only if Ink actually entered the alternate screen on a TTY.\n */\nexport function canPublishInkGeometry(options: GeometryGateOptions): boolean {\n const interactive = options.interactive\n ?? (!(options.inCi ?? runningInCi()) && options.stdoutIsTTY);\n return options.alternateScreen && interactive && options.stdoutIsTTY;\n}\n\nfunction runningInCi(): boolean {\n return enabledEnvironmentFlag('CI') || enabledEnvironmentFlag('CONTINUOUS_INTEGRATION');\n}\n\nfunction enabledEnvironmentFlag(name: string): boolean {\n const value = process.env[name];\n return value !== undefined && value !== '0' && value !== 'false';\n}\n","/** Synchronized from package.json by scripts/sync-protocol-version.mjs. */\nexport const PACKAGE_VERSION = '0.2.0';\n","/** A committed Ink host tree to snapshot/commit/marker publication. */\n\nimport type { ProbeInfo, ProtocolLimits, SemanticSnapshot } from '@termwright/protocol';\nimport { recognize } from '@termwright/recognizers';\nimport type { ProbeChannel } from '@termwright/probe-runtime';\nimport {\n hasStaticContent,\n observeInkTree,\n type InkDomElement,\n type MeasureElement,\n} from './observe.js';\nimport { PACKAGE_VERSION } from './version.js';\n\n/** What this probe truthfully offers at handshake time. */\nexport function probeInfo(): ProbeInfo {\n return {\n framework: 'ink',\n probeVersion: PACKAGE_VERSION,\n identityKind: 'stable',\n // The optional @termwright/ink SDK writes author intent to the shared weak\n // registry. Ink's own aria metadata travels separately as framework facts.\n capabilities: ['stable-identity', 'annotations'],\n };\n}\n\nexport interface InkSessionOptions {\n readonly channel: ProbeChannel;\n readonly resolveRoot: () => InkDomElement | null;\n readonly resolveExcluded?: () => InkDomElement | null;\n readonly measureElement: MeasureElement;\n readonly stdout: NodeJS.WriteStream;\n readonly includeGeometry: boolean;\n}\n\nexport interface InkProbeSession {\n readonly revision: number;\n readonly frames: number;\n notifyRender(): void;\n /** Settle all captures queued at the time of the call. Never rejects. */\n flush(): Promise<void>;\n stop(): void;\n}\n\n/**\n * Pair each observed commit with its output bytes.\n *\n * Ink invokes `onRender` after layout and before writing. The tree is frozen\n * synchronously in that callback; deferring observation would let a microtask\n * or a throttled commit mutate the host objects before they were read. Only\n * marker placement is deferred: after Ink returns and writes, stdout is\n * drained and the authenticated marker is appended.\n */\nexport function createInkSession(options: InkSessionOptions): InkProbeSession {\n let revision = 0;\n let frames = 0;\n let latestFrame = 0;\n let staticSeen = false;\n let stopped = false;\n let queue: Promise<void> = Promise.resolve();\n\n const fail = (): void => {\n if (stopped) return;\n stopped = true;\n options.channel.close();\n };\n\n const writeMarker = async (frame: number, marker: string): Promise<void> => {\n await nextMacrotask();\n if (stopped || !options.channel.isOpen) return;\n if (frame !== latestFrame) {\n options.channel.recordCoalescedEvent();\n return;\n }\n await drain(options.stdout);\n // A newer render may have written while this drain was pending. Marker N\n // after frame N+1 bytes is actively misleading, so drop it and let the\n // newer full snapshot establish the next pairing.\n if (stopped || !options.channel.isOpen) return;\n if (frame !== latestFrame) {\n options.channel.recordCoalescedEvent();\n return;\n }\n options.stdout.write(marker);\n };\n\n return {\n get revision() {\n return revision;\n },\n get frames() {\n return frames;\n },\n notifyRender() {\n if (stopped) return;\n frames += 1;\n const frame = frames;\n // Even an unobservable/failed frame supersedes a queued old marker.\n latestFrame = frame;\n\n try {\n const root = options.resolveRoot();\n if (root === null) return;\n // Static output scrolls the live region down. Removing <Static> later\n // does not erase bytes already written above it, so loss of absolute\n // coordinates is sticky for this session.\n staticSeen ||= hasStaticContent(root);\n const includeGeometry = options.includeGeometry && !staticSeen;\n const excluded = options.resolveExcluded?.();\n const observation = observeInkTree(root, {\n frame,\n limits: options.channel.session.limits as ProtocolLimits,\n ...(excluded === undefined ? {} : { excluded }),\n measureElement: options.measureElement,\n includeGeometry,\n });\n\n revision += 1;\n const snapshot: SemanticSnapshot = recognize(observation.frame, {\n sessionId: options.channel.session.sessionId,\n revision,\n columns: options.stdout.columns ?? 80,\n rows: options.stdout.rows ?? 24,\n framework: 'ink',\n paintOrderKnown: false,\n maxStringBytes: options.channel.session.limits.maxStringBytes,\n qualified: options.channel.session.protocol === 'termwright/2',\n });\n const marker = options.channel.publish(snapshot, {\n probeEvents: observation.frame.objects.length + (observation.frame.operations?.length ?? 0),\n });\n if (marker === undefined) return;\n queue = queue.then(() => writeMarker(frame, marker)).catch(fail);\n } catch {\n fail();\n }\n },\n async flush() {\n await queue.catch(() => undefined);\n },\n stop() {\n fail();\n },\n };\n}\n\nfunction nextMacrotask(): Promise<void> {\n return new Promise((resolve) => setImmediate(resolve));\n}\n\nfunction drain(stream: NodeJS.WriteStream): Promise<void> {\n return new Promise((resolve) => {\n if (stream.writableEnded || stream.destroyed) {\n resolve();\n return;\n }\n try {\n stream.write('', () => resolve());\n } catch {\n resolve();\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -1,71 +0,0 @@
1
- import { ProbeFrame, ProtocolLimits } from '@termwright/protocol';
2
-
3
- /** Runtime activation shared by the two preload entry points. */
4
- /** Runtimes into which the Ink probe can be injected. */
5
- type ProbeRuntime = 'bun' | 'node';
6
- /** Read-only environment view, so activation is testable without mutation. */
7
- type EnvSource = Readonly<Record<string, string | undefined>>;
8
- /** Both secrets are required. A partial environment remains fully dormant. */
9
- declare function isInstrumented(env: EnvSource): boolean;
10
-
11
- /** Ink's retained host tree to framework-neutral Probe IR. */
12
-
13
- /** Structural subset of Ink's DOM node. No runtime import from `ink`. */
14
- interface InkDomElement {
15
- readonly nodeName: 'ink-root' | 'ink-box' | 'ink-text' | 'ink-virtual-text';
16
- readonly childNodes: readonly InkDomNode[];
17
- readonly parentNode?: InkDomElement;
18
- readonly style?: {
19
- readonly display?: string;
20
- };
21
- readonly internal_static?: boolean;
22
- readonly staticNode?: InkDomElement;
23
- readonly internal_accessibility?: {
24
- readonly role?: string;
25
- readonly state?: {
26
- readonly checked?: boolean;
27
- readonly disabled?: boolean;
28
- readonly expanded?: boolean;
29
- readonly readonly?: boolean;
30
- readonly selected?: boolean;
31
- readonly busy?: boolean;
32
- readonly multiline?: boolean;
33
- };
34
- };
35
- }
36
- interface InkTextNode {
37
- readonly nodeName: '#text';
38
- readonly nodeValue: string;
39
- readonly parentNode?: InkDomElement;
40
- }
41
- type InkDomNode = InkDomElement | InkTextNode;
42
- /** Public Ink measurement function, kept injectable for tests and isolation. */
43
- type MeasureElement = (node: InkDomElement) => {
44
- readonly x: number;
45
- readonly y: number;
46
- readonly width: number;
47
- readonly height: number;
48
- };
49
- interface ObserveInkOptions {
50
- readonly frame: number;
51
- readonly limits: ProtocolLimits;
52
- /** The probe's own hidden Box. It is the sole injected node and is omitted. */
53
- readonly excluded?: InkDomElement | null;
54
- readonly measureElement?: MeasureElement;
55
- /** Only true when live-region coordinates are proven viewport-absolute. */
56
- readonly includeGeometry?: boolean;
57
- }
58
- interface InkObservation {
59
- readonly frame: ProbeFrame;
60
- readonly truncated: boolean;
61
- }
62
- /**
63
- * Observe every Ink host element, including plain unannotated layout boxes.
64
- *
65
- * Source component names do not survive Ink's reconciler. `frameworkType` is
66
- * therefore deliberately one of Ink's four host kinds; inventing `Button` or
67
- * a component stack here would be false provenance.
68
- */
69
- declare function observeInkTree(root: InkDomElement, options: ObserveInkOptions): InkObservation;
70
-
71
- export { type EnvSource as E, type InkDomElement as I, type MeasureElement as M, type ProbeRuntime as P, type InkDomNode as a, type InkObservation as b, isInstrumented as i, observeInkTree as o };