packages/but-sdk/README.md
@gitbutler/but-sdk is the local npm package that exposes GitButler Rust APIs to JavaScript/TypeScript through a native Node add-on.
The generated SDK surface is produced in two parts and merged into a single declaration file:
N-API bindings (functions)
#[but_api(napi)] in but-api generate *_napi exports via the but_api proc macro.but-napi links those generated exports and is built by napi-rs into a .node binary.napi-rs generates:
gitbutler-sdk.{PLATFORM}.node (Node native add-on)src/generated/index.js (runtime loader + JS bridge)src/generated/index.d.ts (N-API function declarations)Schema-derived TypeScript types (structs/enums)
but-ts collects registered JSON schemas from but-api (but_api::schema module) and emits TS type aliases.src/generated/index.d.ts.This package ships only generated artifacts (index.js, index.d.ts, *.node) and re-exports them through package exports.
Always run generation in this order to avoid duplicate schema blocks in index.d.ts:
pnpm --filter @gitbutler/but-sdk build:napi
pnpm --filter @gitbutler/but-sdk build:types
Or run the combined script:
pnpm --filter @gitbutler/but-sdk build
build:types appends schema types to the existing declaration file. Running it repeatedly without regenerating N-API declarations first can duplicate schema type sections.
crates/but-api, annotate a Rust API with #[but_api(napi)].pnpm --filter @gitbutler/but-sdk build:napi
You should see a new *Napi export in src/generated/index.d.ts.
schemars::JsonSchema.crates/but-api/src/schema.rs using TypeSchemaEntry.pnpm --filter @gitbutler/but-sdk build:types
You should see a new export type ... in src/generated/index.d.ts.
Baseline validation:
pnpm --filter @gitbutler/but-sdk check
Optional extra runtime/type sanity check:
pnpm --filter @gitbutler/but-sdk testTypes
Recommended contributor flow after changing bindings/types:
pnpm --filter @gitbutler/but-sdk build
pnpm --filter @gitbutler/but-sdk check
# optional
pnpm --filter @gitbutler/but-sdk testTypes
apps/lite pattern)Use native bindings in the Electron main process, then expose only typed IPC APIs to the renderer.
import { listProjectsStatelessNapi } from '@gitbutler/but-sdk';
export function listProjects() {
return listProjectsStatelessNapi();
}
import type { ProjectForFrontend } from '@gitbutler/but-sdk';
export interface LiteElectronApi {
listProjects(): Promise<ProjectForFrontend[]>;
}
const projects = await window.lite.listProjects();
This keeps native access out of the renderer while sharing the same generated TS types (ProjectForFrontend, StackEntry, StackDetails, etc.) across process boundaries.
See src/generated/index.d.ts for the complete generated API and type surface.
FSL-1.1-MIT - See LICENSE.md for details.