Skip to content

Commit 2fd44c0

Browse files
wan9chiclaude
andcommitted
feat(client): vite-task-client JS package
Adds the JS-side npm package `@voidzero-dev/vite-task-client` with its type-generation tooling. The package is a thin Node.js wrapper that attempts to load a runner-provided napi addon at runtime via the `VP_RUN_NODE_CLIENT_PATH` env var and gracefully no-ops when absent — so it's installable and importable on its own, independent of the runner's Rust implementation. The actual napi binding and the runner-side IPC server land in the follow-up PR on top of this one. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5833b37 commit 2fd44c0

10 files changed

Lines changed: 248 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,11 @@ jobs:
275275
- name: Deduplicate dependencies
276276
run: pnpm dedupe --check
277277

278+
- name: Check vite-task-client types are not stale
279+
run: |
280+
pnpm build-vite-task-client-types
281+
git diff --exit-code packages/vite-task-client/index.d.ts
282+
278283
done:
279284
runs-on: namespace-profile-linux-x64-default
280285
if: always()

.oxfmtrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"ignorePatterns": [
44
"crates/fspy_detours_sys/detours",
55
"crates/vite_task_graph/run-config.ts",
6-
"**/fixtures/*/snapshots"
6+
"**/fixtures/*/snapshots",
7+
"packages/vite-task-client/index.d.ts"
78
]
89
}

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
"license": "MIT",
55
"type": "module",
66
"scripts": {
7-
"prepare": "husky"
7+
"prepare": "husky",
8+
"build-vite-task-client-types": "tsc -p packages/vite-task-client/tsconfig.json"
89
},
910
"devDependencies": {
11+
"@tsconfig/strictest": "catalog:",
1012
"@types/node": "catalog:",
1113
"husky": "catalog:",
1214
"lint-staged": "catalog:",
1315
"oxfmt": "catalog:",
1416
"oxlint": "catalog:",
15-
"oxlint-tsgolint": "catalog:"
17+
"oxlint-tsgolint": "catalog:",
18+
"typescript": "catalog:"
1619
},
1720
"lint-staged": {
1821
"*": "oxfmt --no-error-on-unmatched-pattern",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# @voidzero-dev/vite-task-client
2+
3+
Node client that lets JS/TS tools report ignored inputs/outputs, fetch tracked env values, and opt out of caching when running inside a `vp run` task.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Tell the runner to ignore reads under `path` when inferring cache inputs.
3+
*
4+
* No-op when not running inside a runner.
5+
*
6+
* @param {string} path
7+
* @returns {void}
8+
*/
9+
export function ignoreInput(path: string): void;
10+
/**
11+
* Tell the runner to ignore writes under `path` when inferring cache outputs.
12+
*
13+
* No-op when not running inside a runner.
14+
*
15+
* @param {string} path
16+
* @returns {void}
17+
*/
18+
export function ignoreOutput(path: string): void;
19+
/**
20+
* Tell the runner not to cache this run.
21+
*
22+
* No-op when not running inside a runner.
23+
*
24+
* @returns {void}
25+
*/
26+
export function disableCache(): void;
27+
/**
28+
* Ask the runner for the value of the env var `name` and return it, or
29+
* `undefined` when the runner has no such env.
30+
*
31+
* With `tracked: true` (the default) the runner records `name` as a
32+
* dependency, so a change to its value invalidates this run's cache entry.
33+
*
34+
* Has no effect on `process.env`; the caller decides what to do with the
35+
* returned value. Returns `undefined` when not running inside a runner.
36+
*
37+
* @param {string} name
38+
* @param {{ tracked?: boolean }} [options]
39+
* @returns {string | undefined}
40+
*/
41+
export function getEnv(name: string, options?: {
42+
tracked?: boolean;
43+
}): string | undefined;
44+
/**
45+
* Ask the runner for every env whose name matches `pattern` (a glob, e.g.
46+
* `VITE_*`) and return the match-set as a plain object.
47+
*
48+
* With `tracked: true` (the default) the runner records the pattern as a
49+
* dependency, so adding, removing, or changing a matching env invalidates
50+
* this run's cache entry.
51+
*
52+
* Has no effect on `process.env`; the caller decides what to do with the
53+
* returned values. Returns an empty object when not running inside a runner.
54+
*
55+
* @param {string} pattern
56+
* @param {{ tracked?: boolean }} [options]
57+
* @returns {Record<string, string>}
58+
*/
59+
export function getEnvs(pattern: string, options?: {
60+
tracked?: boolean;
61+
}): Record<string, string>;

packages/vite-task-client/index.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// The JSDoc in this file is the source of truth for the package's public
2+
// types. `index.d.ts` is generated from it via `pnpm run build:types`
3+
// (using `tsc` with `@tsconfig/strictest`) — edit JSDoc here, not the
4+
// `.d.ts`. CI fails if the committed `.d.ts` drifts from a fresh regen.
5+
6+
import { createRequire } from 'node:module';
7+
8+
/**
9+
* Methods exposed by the napi addon. Keep this shape in sync with the
10+
* `RunnerClient` returned by `load()` in
11+
* `crates/vite_task_client_napi/src/lib.rs` — any new method added there
12+
* needs a matching entry here, and vice versa.
13+
*
14+
* @type {{
15+
* ignoreInput: (path: string) => void,
16+
* ignoreOutput: (path: string) => void,
17+
* disableCache: () => void,
18+
* getEnv: (name: string, options?: { tracked?: boolean }) => string | undefined,
19+
* getEnvs: (pattern: string, options?: { tracked?: boolean }) => Record<string, string>,
20+
* } | null | undefined}
21+
*/
22+
let addon;
23+
24+
function load() {
25+
if (addon !== undefined) return addon;
26+
try {
27+
const path = process.env['VP_RUN_NODE_CLIENT_PATH'];
28+
if (path) {
29+
// The addon exports a `load(options?)` factory rather than the
30+
// methods directly, so the addon shape can evolve in lockstep with
31+
// this wrapper: a future wrapper can pass `{ version: N }` to opt
32+
// into a new shape without breaking older addons that only know v1.
33+
// Today's wrapper passes nothing and accepts whatever the addon's
34+
// current default version returns.
35+
addon = createRequire(import.meta.url)(path).load();
36+
return addon;
37+
}
38+
} catch {
39+
// Fall through — the runner's IPC env is absent or the addon refused to
40+
// load. Memoize the unavailable decision so subsequent calls don't retry.
41+
}
42+
addon = null;
43+
return addon;
44+
}
45+
46+
/**
47+
* Tell the runner to ignore reads under `path` when inferring cache inputs.
48+
*
49+
* No-op when not running inside a runner.
50+
*
51+
* @param {string} path
52+
* @returns {void}
53+
*/
54+
export function ignoreInput(path) {
55+
load()?.ignoreInput(path);
56+
}
57+
58+
/**
59+
* Tell the runner to ignore writes under `path` when inferring cache outputs.
60+
*
61+
* No-op when not running inside a runner.
62+
*
63+
* @param {string} path
64+
* @returns {void}
65+
*/
66+
export function ignoreOutput(path) {
67+
load()?.ignoreOutput(path);
68+
}
69+
70+
/**
71+
* Tell the runner not to cache this run.
72+
*
73+
* No-op when not running inside a runner.
74+
*
75+
* @returns {void}
76+
*/
77+
export function disableCache() {
78+
load()?.disableCache();
79+
}
80+
81+
/**
82+
* Ask the runner for the value of the env var `name` and return it, or
83+
* `undefined` when the runner has no such env.
84+
*
85+
* With `tracked: true` (the default) the runner records `name` as a
86+
* dependency, so a change to its value invalidates this run's cache entry.
87+
*
88+
* Has no effect on `process.env`; the caller decides what to do with the
89+
* returned value. Returns `undefined` when not running inside a runner.
90+
*
91+
* @param {string} name
92+
* @param {{ tracked?: boolean }} [options]
93+
* @returns {string | undefined}
94+
*/
95+
export function getEnv(name, options) {
96+
const a = load();
97+
if (!a) return undefined;
98+
return a.getEnv(name, options);
99+
}
100+
101+
/**
102+
* Ask the runner for every env whose name matches `pattern` (a glob, e.g.
103+
* `VITE_*`) and return the match-set as a plain object.
104+
*
105+
* With `tracked: true` (the default) the runner records the pattern as a
106+
* dependency, so adding, removing, or changing a matching env invalidates
107+
* this run's cache entry.
108+
*
109+
* Has no effect on `process.env`; the caller decides what to do with the
110+
* returned values. Returns an empty object when not running inside a runner.
111+
*
112+
* @param {string} pattern
113+
* @param {{ tracked?: boolean }} [options]
114+
* @returns {Record<string, string>}
115+
*/
116+
export function getEnvs(pattern, options) {
117+
const a = load();
118+
if (!a) return {};
119+
return a.getEnvs(pattern, options);
120+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "@voidzero-dev/vite-task-client",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"main": "./index.js",
7+
"types": "./index.d.ts"
8+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"extends": "@tsconfig/strictest/tsconfig.json",
3+
"compilerOptions": {
4+
"allowJs": true,
5+
"checkJs": true,
6+
"declaration": true,
7+
"emitDeclarationOnly": true,
8+
"module": "NodeNext",
9+
"moduleResolution": "NodeNext",
10+
"target": "ES2022",
11+
"lib": ["ES2022"],
12+
"types": ["node"]
13+
},
14+
"include": ["index.js"]
15+
}

pnpm-lock.yaml

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
packages:
22
- .
33
- packages/tools
4+
- packages/vite-task-client
45

56
catalog:
7+
'@tsconfig/strictest': ^2.0.8
68
'@types/node': 25.0.3
79
husky: ^9.1.7
810
lint-staged: ^17.0.0
911
oxfmt: 0.42.0
1012
oxlint: ^1.55.0
1113
oxlint-tsgolint: ^0.18.0
14+
typescript: ^6.0.3
1215

1316
catalogMode: prefer

0 commit comments

Comments
 (0)