contributor-context
Contributor context for coding agents working in this repository. Human contributors should start with `README.md` and…
<!-- GENERATED from docs/api/lifecycle-and-types.md by scripts/build-agent-skill.mjs. Do not edit. -->
$ npx -y skills add crossbind/crossbind --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
Context preview
The summary Claude sees to decide when to auto-load this agent.
<!-- GENERATED from docs/api/lifecycle-and-types.md by scripts/build-agent-skill.mjs. Do not edit. -->
<!-- GENERATED from docs/api/lifecycle-and-types.md by scripts/build-agent-skill.mjs. Do not edit. -->
> Two short topics in one doc, because each is small enough not to deserve its own.
An object crossbind hands to JavaScript is freed when the garbage collector reclaims its last handle, and `.delete()` frees it at once.
Wherever the runtime has `FinalizationRegistry` (browsers, on the page thread and in a worker, Node.js, in a wasm build and in a Node-API addon, and React Native 0.88 and later), the collector frees:
The collector runs when it chooses, and it weighs only a handle's small JavaScript object, not the C++ memory behind it. Call `.delete()` on an object that holds a lot of memory or a resource, and on objects made in a loop; the handle cannot be used after it. On React Native before 0.88, whose Hermes engine has no `FinalizationRegistry`, `.delete()` is the only way to free an object.
What stays C++'s:
If your C++ class wraps a file handle, GPU buffer, network socket, etc., release it in the destructor (RAII), so `.delete()` closes it:
class FileReader {
public:
FileReader(const std::string& path) : fp_(std::fopen(path.c_str(), "r")) {}
~FileReader() { if (fp_) std::fclose(fp_); } // RAII closes on destruction
std::string readAll();
private:
FILE* fp_;
};JS:
const reader = new m.FileReader('/memfs/myapp/data.txt')
const text = reader.readAll()
reader.delete() // ~FileReader runs now and closes the fileStandard JS rules apply. If a JS proxy of a C++ shared_ptr captures a closure that holds the same proxy, you have a cycle that GC won't break. Solution: same as in regular JS — don't capture self-references in long-lived closures, or break the cycle explicitly when done.
crossbind emits declaration files for the native modules you import — C++ headers (`./native/native.h`), app-local Rust files (`./native/x.rs`), `cargo:` crate imports and `conan:` header imports — under `.crossbind/types/`, `.crossbind/rust-crates/types/` and `.crossbind/conan/types/`, never next to your sources. Wire them once by extending the shared config (`npm i -D @crossbind/typescript-config@beta`, TS 5.5+):
// tsconfig.json
{ "extends": "@crossbind/typescript-config" }What you get per import: classes with constructor/method signatures for the supported binding surface (primitives, `std::string`, `shared_ptr<Class>` on the C++ side; the full idiom table on the Rust side — see [`rust.md`](./rust.md)). Anything the surface parser does not understand is skipped with a log line and the exported symbol falls back to `any` — the export list itself always comes from the bridge, so names are never missing.
1. **Worker mode wraps calls in Promises — set `dts: 'promise'`.** By default generated signatures describe the direct (sync) surface; with `useWorker: true` every call returns a Promise at runtime. Declare it in `crossbind.config.js`:
export default { dts: 'promise', /* ... */ }Every generated method/function return becomes `Promise<...>`; write `await` per call, and `await new X(...)` for construction (TS cannot express async constructors — the await is a no-op on sync runtimes and required under a worker). A hand-written facade remains useful only when you want richer domain types than the generated surface.
2. **Package headers are not mirrored on the consumer side** — the package itself publishes them: set `types: true` in the package's `crossbind.config.js` and its build emits one combined declaration over every public header into `dist/types/index.d.ts`, wiring `types` + `typesVersions` in package.json automatically (worker-first packages combine it with `dts: 'promise'`). Only the runtime module surface (`m.FS` helpers, custom module interfaces) still warrants hand-written typings.
3. **`any` fallback is deliberate.** Vector classes and unparsed members degrade to `any` rather than blocking — tighten them with a facade where it matters.
Import C++ and Rust like JavaScript modules (WebAssembly & React Native)
Contributor context for coding agents working in this repository. Human contributors should start with `README.md` and…
<!-- GENERATED from docs/api/app.md by scripts/build-agent-skill.mjs. Do not edit. -->
<!-- GENERATED from docs/api/build-state.md by scripts/build-agent-skill.mjs. Do not edit. -->
<!-- GENERATED from docs/api/conan.md by scripts/build-agent-skill.mjs. Do not edit. -->
<!-- GENERATED from docs/api/cpp-binding-rules.md by scripts/build-agent-skill.mjs. Do not edit. -->