@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.
- package/README.md +33 -15
- package/dist/bun-preload.js +19 -2
- package/dist/bun-preload.js.map +1 -1
- package/dist/{chunk-LO7YF74P.js → chunk-67M2GX5S.js} +1 -1
- package/dist/{chunk-LO7YF74P.js.map → chunk-67M2GX5S.js.map} +1 -1
- package/dist/{chunk-IUFXTMZ7.js → chunk-CLY2SLYH.js} +12 -2
- package/dist/chunk-CLY2SLYH.js.map +1 -0
- package/dist/chunk-Q75BSILO.js +649 -0
- package/dist/chunk-Q75BSILO.js.map +1 -0
- package/dist/chunk-SLKX554P.js +152 -0
- package/dist/chunk-SLKX554P.js.map +1 -0
- package/dist/index.d.ts +37 -32
- package/dist/index.js +19 -12
- package/dist/index.js.map +1 -1
- package/dist/instrument.d.ts +12 -2
- package/dist/instrument.js +628 -67
- package/dist/instrument.js.map +1 -1
- package/dist/instrumentation.d.ts +26 -0
- package/dist/instrumentation.js +23 -0
- package/dist/instrumentation.js.map +1 -0
- package/dist/node-hook.js +31 -8
- package/dist/node-hook.js.map +1 -1
- package/dist/react-commit-bridge-BzePo8gM.d.ts +169 -0
- package/package.json +15 -11
- package/dist/chunk-IUFXTMZ7.js.map +0 -1
- package/dist/chunk-Y5WYMWRU.js +0 -358
- package/dist/chunk-Y5WYMWRU.js.map +0 -1
- package/dist/observe-C853n-dp.d.ts +0 -71
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @termwright/probe-ink
|
|
2
2
|
|
|
3
|
-
Zero-config semantics for an Ink
|
|
3
|
+
Zero-config semantics for an exact-certified Ink application that imports nothing from
|
|
4
4
|
termwright. The application keeps its normal `import {render} from 'ink'` and
|
|
5
5
|
normal `render(<App />)` call; the launcher adds one preload flag.
|
|
6
6
|
|
|
@@ -10,12 +10,15 @@ normal `render(<App />)` call; the launcher adds one preload flag.
|
|
|
10
10
|
npm install --save-dev @termwright/probe-ink
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Peers:
|
|
13
|
+
Peers: an exact Ink version listed in the compatibility registry, React >= 19.2,
|
|
14
|
+
Node >= 22, or Bun. The Ink version is exact:
|
|
15
|
+
the preload verifies both instrumented upstream modules by SHA-256 before it
|
|
16
|
+
negotiates semantic capabilities.
|
|
14
17
|
|
|
15
18
|
```ts
|
|
16
|
-
import {withProbe} from '@termwright/probe-ink';
|
|
19
|
+
import { withProbe } from '@termwright/probe-ink';
|
|
17
20
|
|
|
18
|
-
const {command} = withProbe('node', ['node', 'app.mjs']);
|
|
21
|
+
const { command } = withProbe('node', ['node', 'app.mjs']);
|
|
19
22
|
await launchTerminal({
|
|
20
23
|
command,
|
|
21
24
|
env: {
|
|
@@ -27,7 +30,9 @@ await launchTerminal({
|
|
|
27
30
|
|
|
28
31
|
For Bun, pass `withProbe('bun', ['bun', 'app.tsx'])`; the launcher places
|
|
29
32
|
`--preload` before the application entry, where Bun requires it. Node uses
|
|
30
|
-
`--import`.
|
|
33
|
+
`--import`. Node receives a `file://` preload URL, including on Windows; Bun
|
|
34
|
+
receives a native absolute path because its Windows preload resolver does not
|
|
35
|
+
load `file://` entries.
|
|
31
36
|
|
|
32
37
|
Early Node 22 releases do not have `module.registerHooks`; the preload detects
|
|
33
38
|
that case and uses `module.register`. It does not change Node's warning policy.
|
|
@@ -37,10 +42,10 @@ without the probe, and byte parity retains it.
|
|
|
37
42
|
## Dormant and failure behaviour
|
|
38
43
|
|
|
39
44
|
Without both `TERMWRIGHT_ENDPOINT` and `TERMWRIGHT_TOKEN`, the preload installs
|
|
40
|
-
no loader hook and `ink.render` is untouched. If
|
|
41
|
-
the
|
|
42
|
-
|
|
43
|
-
faulted runs under
|
|
45
|
+
no loader hook and `ink.render` is untouched. If either Ink artifact does not
|
|
46
|
+
match 7.1.1 exactly, the adapter does not attach or advertise a partial
|
|
47
|
+
contract. An unreachable driver remains isolated from the application. Process
|
|
48
|
+
tests assert byte identity for dormant and faulted runs under Node and Bun.
|
|
44
49
|
|
|
45
50
|
## What is observed
|
|
46
51
|
|
|
@@ -68,12 +73,25 @@ marker last. A newer render that arrives during drain suppresses the stale
|
|
|
68
73
|
marker. Instrumented output is byte-identical to vanilla output after those
|
|
69
74
|
markers are removed.
|
|
70
75
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
The checksummed renderer hook records Yoga rectangles and the same nested
|
|
77
|
+
overflow intersections used by `render-node-to-output`. A paired output tracker
|
|
78
|
+
maps those relative rectangles into the committed normal or alternate VT
|
|
79
|
+
viewport. It handles terminal wrapping, wide cells, resize, fullscreen scroll,
|
|
80
|
+
and `<Static>` output retained above the live region. Hidden nodes publish
|
|
81
|
+
authoritative absence instead of fabricated zero-sized boxes. Every marker is
|
|
82
|
+
written only after the corresponding output bytes drain.
|
|
83
|
+
|
|
84
|
+
Ink does not expose pointer ownership. Bounds therefore do not enable click,
|
|
85
|
+
hover, or drag by themselves. Those actions require an application evidence
|
|
86
|
+
provider that publishes revision-bound pointer regions and a native hit test;
|
|
87
|
+
device input still travels through the real PTY.
|
|
88
|
+
|
|
89
|
+
Ink instrumentation also does not automatically certify terminal input modes.
|
|
90
|
+
Its output shadow sees JavaScript `stream.write` calls, but direct descriptor or
|
|
91
|
+
native writes and inherited descendant output bypass that surface. On ConPTY,
|
|
92
|
+
where the transport hides DEC mouse/focus negotiation, an opaque Ink child
|
|
93
|
+
therefore remains fail-closed. Hidden-mode pointer/focus input requires an
|
|
94
|
+
explicit provider backed by the application's production input-mode state.
|
|
77
95
|
|
|
78
96
|
## Deliberate limits
|
|
79
97
|
|
package/dist/bun-preload.js
CHANGED
|
@@ -2,12 +2,19 @@ import {
|
|
|
2
2
|
INK_ENTRY_PATTERN,
|
|
3
3
|
buildShimSource,
|
|
4
4
|
shouldShim
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-CLY2SLYH.js";
|
|
6
6
|
import {
|
|
7
7
|
isInstrumented
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-67M2GX5S.js";
|
|
9
|
+
import {
|
|
10
|
+
INK_CORE_PATTERN,
|
|
11
|
+
INK_RENDERER_PATTERN,
|
|
12
|
+
instrumentInkCore,
|
|
13
|
+
instrumentInkRenderer
|
|
14
|
+
} from "./chunk-SLKX554P.js";
|
|
9
15
|
|
|
10
16
|
// src/bun-preload.ts
|
|
17
|
+
import { readFile } from "fs/promises";
|
|
11
18
|
function installBunPreload(env = process.env) {
|
|
12
19
|
if (!isInstrumented(env)) return false;
|
|
13
20
|
const bun = globalThis.Bun;
|
|
@@ -19,6 +26,16 @@ function installBunPreload(env = process.env) {
|
|
|
19
26
|
if (!shouldShim(args.path)) return void 0;
|
|
20
27
|
return { loader: "js", contents: buildShimSource(args.path) };
|
|
21
28
|
});
|
|
29
|
+
build.onLoad({ filter: INK_RENDERER_PATTERN }, async (args) => {
|
|
30
|
+
const source = await readFile(args.path, "utf8");
|
|
31
|
+
const contents = instrumentInkRenderer(args.path, source);
|
|
32
|
+
return contents === void 0 ? void 0 : { loader: "js", contents };
|
|
33
|
+
});
|
|
34
|
+
build.onLoad({ filter: INK_CORE_PATTERN }, async (args) => {
|
|
35
|
+
const source = await readFile(args.path, "utf8");
|
|
36
|
+
const contents = instrumentInkCore(args.path, source);
|
|
37
|
+
return contents === void 0 ? void 0 : { loader: "js", contents };
|
|
38
|
+
});
|
|
22
39
|
}
|
|
23
40
|
});
|
|
24
41
|
return true;
|
package/dist/bun-preload.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bun-preload.ts"],"sourcesContent":["/** Bun entry: `bun --preload @termwright/probe-ink/bun-preload app.js`. */\n\nimport { buildShimSource, INK_ENTRY_PATTERN, shouldShim } from './shim.js';\nimport { isInstrumented } from './runtime.js';\n\ninterface BunPluginBuild {\n onLoad(\n options: { filter: RegExp },\n callback: (args: { path: string }) => Promise<{ loader: string; contents: string } | undefined>,\n ): void;\n}\n\ninterface BunGlobal {\n plugin(definition: { name: string; setup(build: BunPluginBuild): void }): void;\n}\n\n/** Register the Ink entry replacement before the application's first import. */\nexport function installBunPreload(
|
|
1
|
+
{"version":3,"sources":["../src/bun-preload.ts"],"sourcesContent":["/** Bun entry: `bun --preload @termwright/probe-ink/bun-preload app.js`. */\n\nimport { buildShimSource, INK_ENTRY_PATTERN, shouldShim } from './shim.js';\nimport { readFile } from 'node:fs/promises';\nimport {\n instrumentInkCore,\n instrumentInkRenderer,\n INK_CORE_PATTERN,\n INK_RENDERER_PATTERN,\n} from './instrumentation.js';\nimport { isInstrumented } from './runtime.js';\n\ninterface BunPluginBuild {\n onLoad(\n options: { filter: RegExp },\n callback: (args: { path: string }) => Promise<{ loader: string; contents: string } | undefined>,\n ): void;\n}\n\ninterface BunGlobal {\n plugin(definition: { name: string; setup(build: BunPluginBuild): void }): void;\n}\n\n/** Register the Ink entry replacement before the application's first import. */\nexport function installBunPreload(env: Record<string, string | undefined> = process.env): boolean {\n if (!isInstrumented(env)) return false;\n const bun = (globalThis as { Bun?: BunGlobal }).Bun;\n if (bun === undefined) return false;\n\n bun.plugin({\n name: 'termwright-ink',\n setup(build) {\n build.onLoad({ filter: INK_ENTRY_PATTERN }, async (args) => {\n if (!shouldShim(args.path)) return undefined;\n return { loader: 'js', contents: buildShimSource(args.path) };\n });\n build.onLoad({ filter: INK_RENDERER_PATTERN }, async (args) => {\n const source = await readFile(args.path, 'utf8');\n const contents = instrumentInkRenderer(args.path, source);\n return contents === undefined ? undefined : { loader: 'js', contents };\n });\n build.onLoad({ filter: INK_CORE_PATTERN }, async (args) => {\n const source = await readFile(args.path, 'utf8');\n const contents = instrumentInkCore(args.path, source);\n return contents === undefined ? undefined : { loader: 'js', contents };\n });\n },\n });\n return true;\n}\n\ninstallBunPreload();\n"],"mappings":";;;;;;;;;;;;;;;;AAGA,SAAS,gBAAgB;AAqBlB,SAAS,kBAAkB,MAA0C,QAAQ,KAAc;AAChG,MAAI,CAAC,eAAe,GAAG,EAAG,QAAO;AACjC,QAAM,MAAO,WAAmC;AAChD,MAAI,QAAQ,OAAW,QAAO;AAE9B,MAAI,OAAO;AAAA,IACT,MAAM;AAAA,IACN,MAAM,OAAO;AACX,YAAM,OAAO,EAAE,QAAQ,kBAAkB,GAAG,OAAO,SAAS;AAC1D,YAAI,CAAC,WAAW,KAAK,IAAI,EAAG,QAAO;AACnC,eAAO,EAAE,QAAQ,MAAM,UAAU,gBAAgB,KAAK,IAAI,EAAE;AAAA,MAC9D,CAAC;AACD,YAAM,OAAO,EAAE,QAAQ,qBAAqB,GAAG,OAAO,SAAS;AAC7D,cAAM,SAAS,MAAM,SAAS,KAAK,MAAM,MAAM;AAC/C,cAAM,WAAW,sBAAsB,KAAK,MAAM,MAAM;AACxD,eAAO,aAAa,SAAY,SAAY,EAAE,QAAQ,MAAM,SAAS;AAAA,MACvE,CAAC;AACD,YAAM,OAAO,EAAE,QAAQ,iBAAiB,GAAG,OAAO,SAAS;AACzD,cAAM,SAAS,MAAM,SAAS,KAAK,MAAM,MAAM;AAC/C,cAAM,WAAW,kBAAkB,KAAK,MAAM,MAAM;AACpD,eAAO,aAAa,SAAY,SAAY,EAAE,QAAQ,MAAM,SAAS;AAAA,MACvE,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,kBAAkB;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/** Runtime activation shared by the two preload entry points. */\n\nimport { ENV_ENDPOINT, ENV_TOKEN } from '@termwright/protocol';\n\n/** Runtimes into which the Ink probe can be injected. */\nexport type ProbeRuntime = 'bun' | 'node';\n\n/** Read-only environment view, so activation is testable without mutation. */\nexport type EnvSource = Readonly<Record<string, string | undefined>>;\n\n/** Both secrets are required. A partial environment remains fully dormant. */\nexport function isInstrumented(env: EnvSource): boolean {\n const endpoint = env[ENV_ENDPOINT];\n const token = env[ENV_TOKEN];\n return typeof endpoint === 'string'
|
|
1
|
+
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/** Runtime activation shared by the two preload entry points. */\n\nimport { ENV_ENDPOINT, ENV_TOKEN } from '@termwright/protocol';\n\n/** Runtimes into which the Ink probe can be injected. */\nexport type ProbeRuntime = 'bun' | 'node';\n\n/** Read-only environment view, so activation is testable without mutation. */\nexport type EnvSource = Readonly<Record<string, string | undefined>>;\n\n/** Both secrets are required. A partial environment remains fully dormant. */\nexport function isInstrumented(env: EnvSource): boolean {\n const endpoint = env[ENV_ENDPOINT];\n const token = env[ENV_TOKEN];\n return (\n typeof endpoint === 'string' &&\n endpoint.length > 0 &&\n typeof token === 'string' &&\n token.length > 0\n );\n}\n"],"mappings":";AAEA,SAAS,cAAc,iBAAiB;AASjC,SAAS,eAAe,KAAyB;AACtD,QAAM,WAAW,IAAI,YAAY;AACjC,QAAM,QAAQ,IAAI,SAAS;AAC3B,SACE,OAAO,aAAa,YACpB,SAAS,SAAS,KAClB,OAAO,UAAU,YACjB,MAAM,SAAS;AAEnB;","names":[]}
|
|
@@ -9,14 +9,23 @@ function shouldShim(urlOrPath) {
|
|
|
9
9
|
function originalUrl(urlOrPath) {
|
|
10
10
|
return `${urlOrPath}${urlOrPath.includes("?") ? "&" : "?"}${ORIGINAL_MARKER}`;
|
|
11
11
|
}
|
|
12
|
+
function reconcilerUrl(urlOrPath) {
|
|
13
|
+
const [path] = urlOrPath.split("?");
|
|
14
|
+
if (path === void 0 || !INK_ENTRY_PATTERN.test(path)) {
|
|
15
|
+
throw new Error(`Cannot resolve Ink reconciler beside ${urlOrPath}`);
|
|
16
|
+
}
|
|
17
|
+
return path.replace(/index\.js$/u, "reconciler.js");
|
|
18
|
+
}
|
|
12
19
|
function buildShimSource(target, instrumentUrl = INSTRUMENT_URL) {
|
|
13
20
|
const original = JSON.stringify(originalUrl(target));
|
|
21
|
+
const reconciler = JSON.stringify(reconcilerUrl(target));
|
|
14
22
|
const instrument = JSON.stringify(instrumentUrl);
|
|
15
23
|
return `import * as __termwright_original from ${original};
|
|
24
|
+
import __termwright_reconciler from ${reconciler};
|
|
16
25
|
import {wrapInkRender as __termwright_wrap} from ${instrument};
|
|
17
26
|
export * from ${original};
|
|
18
27
|
|
|
19
|
-
export const render = __termwright_wrap(__termwright_original);
|
|
28
|
+
export const render = __termwright_wrap(__termwright_original, {reconciler: __termwright_reconciler});
|
|
20
29
|
`;
|
|
21
30
|
}
|
|
22
31
|
|
|
@@ -26,6 +35,7 @@ export {
|
|
|
26
35
|
INSTRUMENT_URL,
|
|
27
36
|
shouldShim,
|
|
28
37
|
originalUrl,
|
|
38
|
+
reconcilerUrl,
|
|
29
39
|
buildShimSource
|
|
30
40
|
};
|
|
31
|
-
//# sourceMappingURL=chunk-
|
|
41
|
+
//# sourceMappingURL=chunk-CLY2SLYH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shim.ts"],"sourcesContent":["/** Replacement source for Ink's public entry module. */\n\n/** Marker on the shim's re-import of the untouched module. */\nexport const ORIGINAL_MARKER = 'termwright-original=1';\n\n/**\n * Ink's ESM entry under ordinary node_modules and Bun's versioned cache.\n * Matching the resolved path, rather than the bare specifier, is required by\n * both loader APIs used here.\n */\nexport const INK_ENTRY_PATTERN = /[\\\\/](?:ink|ink@[^\\\\/]+)[\\\\/]build[\\\\/]index\\.js$/u;\n\n/** The separately-built runtime imported by replacement module source. */\nexport const INSTRUMENT_URL = new URL('./instrument.js', import.meta.url).href;\n\nexport function shouldShim(urlOrPath: string): boolean {\n if (urlOrPath.includes(ORIGINAL_MARKER)) return false;\n return INK_ENTRY_PATTERN.test(urlOrPath.split('?')[0] ?? '');\n}\n\nexport function originalUrl(urlOrPath: string): string {\n return `${urlOrPath}${urlOrPath.includes('?') ? '&' : '?'}${ORIGINAL_MARKER}`;\n}\n\n/** Ink's renderer instance, resolved beside the intercepted public entry. */\nexport function reconcilerUrl(urlOrPath: string): string {\n const [path] = urlOrPath.split('?');\n if (path === undefined || !INK_ENTRY_PATTERN.test(path)) {\n throw new Error(`Cannot resolve Ink reconciler beside ${urlOrPath}`);\n }\n return path.replace(/index\\.js$/u, 'reconciler.js');\n}\n\n/**\n * Forward the complete Ink namespace and shadow only `render`.\n *\n * The wrapper receives the already-marked original namespace, so the runtime\n * never imports `ink` itself and cannot recurse through the loader hook.\n */\nexport function buildShimSource(target: string, instrumentUrl = INSTRUMENT_URL): string {\n const original = JSON.stringify(originalUrl(target));\n const reconciler = JSON.stringify(reconcilerUrl(target));\n const instrument = JSON.stringify(instrumentUrl);\n return `import * as __termwright_original from ${original};\nimport __termwright_reconciler from ${reconciler};\nimport {wrapInkRender as __termwright_wrap} from ${instrument};\nexport * from ${original};\n\nexport const render = __termwright_wrap(__termwright_original, {reconciler: __termwright_reconciler});\n`;\n}\n"],"mappings":";AAGO,IAAM,kBAAkB;AAOxB,IAAM,oBAAoB;AAG1B,IAAM,iBAAiB,IAAI,IAAI,mBAAmB,YAAY,GAAG,EAAE;AAEnE,SAAS,WAAW,WAA4B;AACrD,MAAI,UAAU,SAAS,eAAe,EAAG,QAAO;AAChD,SAAO,kBAAkB,KAAK,UAAU,MAAM,GAAG,EAAE,CAAC,KAAK,EAAE;AAC7D;AAEO,SAAS,YAAY,WAA2B;AACrD,SAAO,GAAG,SAAS,GAAG,UAAU,SAAS,GAAG,IAAI,MAAM,GAAG,GAAG,eAAe;AAC7E;AAGO,SAAS,cAAc,WAA2B;AACvD,QAAM,CAAC,IAAI,IAAI,UAAU,MAAM,GAAG;AAClC,MAAI,SAAS,UAAa,CAAC,kBAAkB,KAAK,IAAI,GAAG;AACvD,UAAM,IAAI,MAAM,wCAAwC,SAAS,EAAE;AAAA,EACrE;AACA,SAAO,KAAK,QAAQ,eAAe,eAAe;AACpD;AAQO,SAAS,gBAAgB,QAAgB,gBAAgB,gBAAwB;AACtF,QAAM,WAAW,KAAK,UAAU,YAAY,MAAM,CAAC;AACnD,QAAM,aAAa,KAAK,UAAU,cAAc,MAAM,CAAC;AACvD,QAAM,aAAa,KAAK,UAAU,aAAa;AAC/C,SAAO,0CAA0C,QAAQ;AAAA,sCACrB,UAAU;AAAA,mDACG,UAAU;AAAA,gBAC7C,QAAQ;AAAA;AAAA;AAAA;AAIxB;","names":[]}
|