crossbind 2.0.0-beta.51 → 2.0.0-beta.53

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/AGENTS.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## What lives here
6
6
 
7
- - `bin.js` — `crossbind` / `crossbind` CLI entry; commander commands, target filtering, top-level orchestration.
7
+ - `src/bin.js` — `crossbind` CLI entry; commander commands, target filtering and top-level orchestration.
8
8
  - `src/index.js` — public API barrel re-exported as the `crossbind` npm package (`createLib`, `buildWasm`, `state`, `getData`, `isSourceNewer`, …).
9
9
  - `src/actions/` — the actual build steps. **Most edits land here.**
10
10
  - `src/state/` — config loading + target matrix.
@@ -18,7 +18,7 @@ Use `docs/CODEMAP.md` (repo root) for "concept → file" lookups before guessing
18
18
 
19
19
  Order of operations when a user runs `pnpm crossbind build`:
20
20
 
21
- 1. **`bin.js`** parses CLI flags, calls `getBuildTargets` to expand the target matrix.
21
+ 1. **`src/bin.js`** parses CLI flags, calls `getBuildTargets` to expand the target matrix.
22
22
  2. For each target:
23
23
  - **`actions/createLib.js`** compiles user C++ to a static lib per platform (cmake/make for wasm/android via Docker; xcodebuild on darwin for iOS). Cache short-circuit when `<output>/prebuilt/<target>/lib` exists, unless `options.force`.
24
24
  - **`actions/createXCFramework.js`** combines iOS slices into an `.xcframework` (darwin only).
@@ -68,9 +68,9 @@ pnpm run ci:linux:build && pnpm run e2e:dev && pnpm run e2e:prod
68
68
 
69
69
  Plus, depending on the area:
70
70
 
71
- - Touched a new public export → search consumers: `grep -rn "from 'crossbind'" plugins examples scripts`.
71
+ - Touched a new public export → search consumers: `rg "from ['\"]crossbind['\"]" plugins examples e2e scripts`.
72
72
  - Touched `actions/run.js` (Docker / Xcode shell-out) → smoke a fresh `pnpm crossbind build` against `examples/lib-prebuilt-matrix` (smallest C++ surface).
73
- - Touched `src/assets/js-runtime/` (browser/node/edge adapters) → at least one sample per runtime must build + run (`examples/web-vue-vite`, `examples/backend-nodejs-wasm`, `examples/cloud-cloudflare-worker`).
73
+ - Touched `src/assets/js-runtime/` (browser/node/edge adapters) → at least one example per runtime must build and run (`examples/web-vue-vite`, `examples/backend-nodejs-wasm`, `examples/cloud-cloudflare-worker`).
74
74
  - Touched `src/assets/js-runtime/adapters/worker-comlink.js` → coverage MUST include embind **instance** methods, not just statics: statics skip embind's `this` conversion, so a statics-only smoke passes while every instance call is broken (this shipped once, as beta.26's "Expected null or instance of X, got an instance of X"). Run the `e2e/web-vite-multithread` e2e (its Counter spec exists for exactly this), and before publishing run gdal3.js's `packages/wasm-test` suite against the packed tarball — it calls every binding, one method at a time.
75
75
  - Touched `state/loadConfig.js` defaults → assume blast radius = whole monorepo. Run the full matrix.
76
76
 
@@ -88,5 +88,5 @@ Plus, depending on the area:
88
88
  - `docs/ARCHITECTURE.md` — high-level flow + key abstractions.
89
89
  - `docs/CODEMAP.md` — concept → file index.
90
90
  - `docs/playbooks/bug-fix.md` — fix workflow.
91
- - `docs/playbooks/new-package.md` — package author workflow (for `ports/` not this dir).
91
+ - `docs/playbooks/new-port.md` — port-authoring workflow (for `ports/`, not this directory).
92
92
  - Logger: `src/utils/logger.js` — read this before adding any user-facing output.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # crossbind
2
2
 
3
+ ## 2.0.0-beta.53
4
+
5
+ ### Patch Changes
6
+
7
+ - The rebuild the source stamp triggers now clears the previous upstream release's configure
8
+ output and staged install tree first. Without that, an install step fails where it cannot
9
+ overwrite a file it did not create, which is what stopped the beta.52 publish partway.
10
+
11
+ ## 2.0.0-beta.52
12
+
13
+ ### Patch Changes
14
+
15
+ - A prebuilt library is now rebuilt when its upstream source changes. The cache keyed on existence
16
+ alone, so bumping nativeVersion produced binaries of the previous upstream release while the
17
+ manifest, provenance and licence metadata already named the new one.
18
+
3
19
  ## 2.0.0-beta.51
4
20
 
5
21
  ### Patch Changes
package/README.md CHANGED
@@ -1,133 +1,348 @@
1
1
  <div align="center">
2
2
  <a href="https://crossbind.dev">
3
- <picture>
4
- <img alt="crossbind logo" src="https://crossbind.dev/img/logo.png" height="128">
5
- </picture>
3
+ <img src="https://raw.githubusercontent.com/crossbind/crossbind/main/landing/public/favicon.svg" alt="crossbind logo" height="96">
6
4
  </a>
5
+
7
6
  <h1>crossbind</h1>
8
- <p align="center">
9
- <strong>Bind C++ to JavaScript with no extra code.</strong><br>
10
- WebAssembly, WASI & React Native
11
- </p>
12
-
13
- <a href="https://www.npmjs.com/package/crossbind/v/beta"><img alt="NPM version" src="https://img.shields.io/npm/v/crossbind/beta?style=for-the-badge&label=npm" /></a>
14
- <a href="https://github.com/crossbind/crossbind/pkgs/container/web"><img alt="Build image" src="https://img.shields.io/badge/ghcr.io-crossbind%2Fweb-20B2AA?style=for-the-badge&logo=docker&label=image" /></a>
15
- <a href="https://github.com/crossbind/crossbind/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/crossbind/crossbind?style=for-the-badge" /></a>
16
- <br />
17
- <img alt="CodeQL" src="https://img.shields.io/github/actions/workflow/status/crossbind/crossbind/github-code-scanning/codeql?branch=main&style=for-the-badge&label=CodeQL">
18
- <img alt="Linux Build" src="https://img.shields.io/github/actions/workflow/status/crossbind/crossbind/build-linux.yml?branch=main&style=for-the-badge&label=Linux%20Build">
19
- <img alt="Macos Build" src="https://img.shields.io/github/actions/workflow/status/crossbind/crossbind/build-macos.yml?branch=main&style=for-the-badge&label=Macos%20Build">
20
- <img alt="Windows Build" src="https://img.shields.io/github/actions/workflow/status/crossbind/crossbind/build-windows.yml?branch=main&style=for-the-badge&label=Windows%20Build">
7
+
8
+ <p>
9
+ <strong>Import C++ and Rust like JavaScript modules.</strong><br>
10
+ Build, package, and run native libraries across browsers, Node.js, edge
11
+ runtimes, iOS, Android, and WASI.
12
+ </p>
13
+
14
+ <p>
15
+ <a href="https://www.npmjs.com/package/crossbind/v/beta"><img src="https://img.shields.io/npm/v/crossbind/beta?style=flat-square&label=npm%20beta" alt="npm beta version"></a>
16
+ <a href="https://github.com/crossbind/crossbind/actions/workflows/build-linux.yml"><img src="https://img.shields.io/github/actions/workflow/status/crossbind/crossbind/build-linux.yml?branch=main&style=flat-square&label=linux" alt="Linux build status"></a>
17
+ <a href="https://github.com/crossbind/crossbind/blob/main/LICENSE"><img src="https://img.shields.io/github/license/crossbind/crossbind?style=flat-square" alt="MIT license"></a>
18
+ </p>
19
+
20
+ <p>
21
+ <a href="#start-with-a-coding-agent">Coding agents</a>
22
+ · <a href="https://crossbind.dev/guide/">Guide</a>
23
+ · <a href="https://crossbind.dev/guide/quick-start/">Quick start</a>
24
+ · <a href="https://crossbind.dev/guide/packages/">Libraries</a>
25
+ · <a href="https://github.com/crossbind/crossbind/tree/main/examples">Examples</a>
26
+ · <a href="https://github.com/crossbind/crossbind/blob/main/CONTRIBUTING.md">Contributing</a>
27
+ </p>
21
28
  </div>
22
29
 
23
- <h3 align="center">
24
- <a href="https://crossbind.dev/docs/guide/getting-started/introduction"><strong>For Developers</strong></a>
25
- <span> · </span>
26
- <a href="https://crossbind.dev/docs/agent/overview"><strong>For AI Agents</strong></a>
27
- <span> · </span>
28
- <a href="https://crossbind.dev/docs/package/package/showcase">Showcase</a>
29
- </h3>
30
-
31
- ## Why crossbind?
32
- - **No glue code** — write standard C++ headers; bindings are generated for you.
33
- - **Rust too** — import a crates.io crate (`import { Uuid } from 'cargo:uuid'`) or a plain `.rs` file the same way; no proc-macros, no glue.
34
- - **Single source, multi-target** — the same code runs in browsers, Node.js, iOS, Android — and as WASI components.
35
- - **Battle-tested libraries** — drop-in support for GDAL, GEOS, OpenSSL, SQLite, PROJ, and more.
36
- - **CLI tools from npm** — upstream command-line tools (gdal, proj, sqlite3, curl, …) prebuilt as WASI components: install the `-bin-wasi` package, run `<tool>-wasi` under wasmtime, no compiler involved.
37
- - **Bundler-agnostic** — first-class plugins for Vite, Rollup, Webpack, Metro, and React Native.
38
- - **AI-agent ready** — Claude Code plugin, MCP server, and vendor-neutral `AGENTS.md` snippet so your agent recommends crossbind correctly and integrates it for you.
39
-
40
- ## For AI coding agents
41
- When you describe a problem ("use C++ in browser", "add GDAL to my Vite app", "wrap libsodium for crossbind"), your AI coding agent recommends crossbind correctly and walks you through the integration. **Native plugins for 6 clients** — pick yours:
42
-
43
- | Client | Install |
44
- |--------|---------|
45
- | 🔌 **Claude Code** | `/plugin marketplace add crossbind/crossbind` then `/plugin install crossbind` |
46
- | 🎯 **Cursor 2.5+** | Settings → Plugins → Add from GitHub: `crossbind/crossbind` |
47
- | 🧪 **OpenAI Codex CLI** | Add `crossbind/crossbind` to `~/.agents/plugins/marketplace.json` |
48
- | 🐙 **GitHub Copilot CLI** | Auto-discovers when running in this repo |
49
- | 💎 **Gemini CLI** | `gemini extension install https://github.com/crossbind/crossbind` |
50
- | ⚡ **OpenCode** | Add `@crossbind/mcp` to your `opencode.json` |
51
-
52
- Plus two universal fallbacks: **🧰 [`@crossbind/mcp`](https://www.npmjs.com/package/@crossbind/mcp)** server (any MCP-aware client) and **📄 [AGENTS.md snippet](https://crossbind.dev/docs/agent/install/snippet)** (no install).
53
-
54
- All clients share the same skills + slash commands + MCP tools — single source of truth at [`agents/`](./agents/), zero duplication.
55
-
56
- Full agent guide, runtime/config API reference, and troubleshooting catalogue: [**crossbind.dev/docs/agent/overview**](https://crossbind.dev/docs/agent/overview). Programmatic discovery via [llms.txt](https://crossbind.dev/llms.txt) + [llms-full.txt](https://crossbind.dev/llms-full.txt).
57
-
58
- ## Create a New Project
59
- Requires **Docker** + **Node 22+**. Mobile builds also need CMake 3.28+, Xcode, and CocoaPods — see the full [prerequisites](https://crossbind.dev/docs/guide/getting-started/prerequisites) page.
60
-
61
- ```sh
30
+ crossbind makes native libraries as easy to distribute and consume as
31
+ JavaScript packages. A C++ header, an app-local Rust file, or a declared Cargo
32
+ crate becomes an importable module; crossbind generates the binding layer,
33
+ resolves the transitive native dependency graph, and selects the right artifact
34
+ for every target.
35
+
36
+ > **Note** — crossbind 2.0 is currently published on the npm `beta` channel.
37
+ > The commands below use `@beta` deliberately.
38
+
39
+ ## Start with a coding agent
40
+
41
+ The shortest path is to give your coding agent the crossbind skill. It inspects
42
+ the existing project, detects the package manager, bundler, target runtime, and
43
+ native sources, checks whether a prebuilt port already exists, wires the right
44
+ integration, and verifies the build.
45
+
46
+ ```bash
47
+ npx skills add https://github.com/crossbind/crossbind/tree/main/agents/skills --global --yes
48
+ ```
49
+
50
+ Then describe the outcome you want:
51
+
52
+ ```text
53
+ Add crossbind to this project so I can call C++ or Rust from JavaScript.
54
+ Inspect the repository, choose the correct integration, make the smallest
55
+ required changes, and verify the build.
56
+ ```
57
+
58
+ The skill uses the agent's existing filesystem and terminal access; no
59
+ crossbind-specific background service is required. Its behavior and sources of
60
+ truth are documented in the
61
+ [agent guide](https://github.com/crossbind/crossbind/blob/main/docs/agent-overview.md).
62
+
63
+ ## Why crossbind
64
+
65
+ The header import is the front door. Underneath it, crossbind provides the
66
+ native library distribution layer for JavaScript runtimes: versioned packages,
67
+ target-specific artifacts, transitive dependencies, build recipes, and runtime
68
+ adapters.
69
+
70
+ - **Target-aware distribution.** A package family can carry WebAssembly, iOS,
71
+ Android, and WASI variants; each build consumes only the artifact valid for
72
+ its platform, architecture, runtime, environment, and build type.
73
+ - **Native dependency resolution.** Package manifests and crossbind configs
74
+ carry the transitive native graph, so prerequisites are built and linked in
75
+ the correct order.
76
+ - **A reproducible package contract.** Port recipes record upstream versions,
77
+ source integrity, licenses, dependencies, and target-specific artifacts.
78
+ - **No hand-written binding glue.** Public functions, classes, methods, enums,
79
+ containers, and supported standard-library types are generated from the
80
+ interface you already own.
81
+ - **C++ and Rust.** Import local native sources, a `cargo:` crate, or a reusable
82
+ native package through the same project.
83
+ - **First-party integrations and ports.** Bundler plugins provide incremental
84
+ builds, while version-pinned ports make libraries such as GDAL, SQLite,
85
+ OpenSSL, GEOS, and PROJ available without rebuilding upstream source.
86
+
87
+ ## Quick start
88
+
89
+ Prefer to scaffold and configure the project yourself? You need
90
+ [Node.js 22+](https://nodejs.org/) and
91
+ [Docker](https://www.docker.com/). Docker carries the browser, Android, and
92
+ WASI toolchains and is pulled automatically on the first build. iOS additionally
93
+ requires macOS, CMake 3.28+, Xcode, and CocoaPods.
94
+
95
+ Create a project:
96
+
97
+ ```bash
62
98
  npm create crossbind@beta
63
99
  ```
64
100
 
65
- ## Basic Usage
66
- **src/index.js**
101
+ If you are unsure which template to choose, start with **Web → React → Vite**.
102
+ Then run the commands printed by the scaffolder:
103
+
104
+ ```bash
105
+ cd my-app
106
+ npm install
107
+ npm run dev
108
+ ```
109
+
110
+ The first native build takes longer because it pulls the toolchain and fills the
111
+ build cache. Later rebuilds are incremental.
112
+
113
+ ### Your first native call
114
+
115
+ Put the public C++ interface under `src/native`:
116
+
117
+ ```cpp
118
+ // src/native/hello.h
119
+ #pragma once
120
+ #include <string>
121
+
122
+ inline std::string hello(const std::string& name) {
123
+ return "Hello, " + name + "!";
124
+ }
125
+ ```
126
+
127
+ Import the header from JavaScript and initialize the runtime before the first
128
+ native call:
129
+
67
130
  ```js
68
- import { initNative, Factorial } from './native/Factorial.h';
131
+ // src/main.js
132
+ import { initNative, hello } from './native/hello.h';
69
133
 
70
134
  await initNative();
71
- const factorial = new Factorial(99999);
72
- const result = factorial.calculate();
73
- console.log(result);
135
+ console.log(hello('crossbind'));
136
+ ```
137
+
138
+ `initNative()` is exported by every generated native module. One call starts
139
+ the runtime and binds all native modules imported by the application.
140
+
141
+ ## Add crossbind to an existing Vite app
142
+
143
+ Install the Vite integration:
144
+
145
+ ```bash
146
+ npm install --save-dev @crossbind/plugin-vite@beta
74
147
  ```
75
148
 
76
- **src/native/Factorial.h**
77
- ```c++
78
- class Factorial {
79
- private:
80
- int number;
149
+ Register it in `vite.config.js`:
150
+
151
+ ```js
152
+ import { defineConfig } from 'vite';
153
+ import viteCrossbindPlugin from '@crossbind/plugin-vite';
81
154
 
82
- public:
83
- Factorial(int num) : number(num) {}
155
+ export default defineConfig({
156
+ plugins: [viteCrossbindPlugin()],
157
+ });
158
+ ```
84
159
 
85
- int calculate() {
86
- if (number < 0) return -1;
160
+ Add the build-time configuration at the project root:
87
161
 
88
- int result = 1;
89
- for (int i = 2; i <= number; i++) {
90
- result *= i;
91
- }
92
- return result;
93
- }
162
+ ```js
163
+ // crossbind.config.js
164
+ export default {
165
+ paths: {
166
+ config: import.meta.url,
167
+ },
94
168
  };
95
169
  ```
96
170
 
97
- ## Official Packages
98
- Officially maintained, prebuilt C++ libraries you can install as npm packages and use directly from JavaScript:
99
-
100
- | Package | Latest (beta) |
101
- | --- | --- |
102
- | [@crossbind/port-gdal](https://www.npmjs.com/package/@crossbind/port-gdal) | ![npm](https://img.shields.io/npm/v/@crossbind/port-gdal/beta) |
103
- | [@crossbind/port-geos](https://www.npmjs.com/package/@crossbind/port-geos) | ![npm](https://img.shields.io/npm/v/@crossbind/port-geos/beta) |
104
- | [@crossbind/port-proj](https://www.npmjs.com/package/@crossbind/port-proj) | ![npm](https://img.shields.io/npm/v/@crossbind/port-proj/beta) |
105
- | [@crossbind/port-spatialite](https://www.npmjs.com/package/@crossbind/port-spatialite) | ![npm](https://img.shields.io/npm/v/@crossbind/port-spatialite/beta) |
106
- | [@crossbind/port-sqlite3](https://www.npmjs.com/package/@crossbind/port-sqlite3) | ![npm](https://img.shields.io/npm/v/@crossbind/port-sqlite3/beta) |
107
- | [@crossbind/port-openssl](https://www.npmjs.com/package/@crossbind/port-openssl) | ![npm](https://img.shields.io/npm/v/@crossbind/port-openssl/beta) |
108
- | [@crossbind/port-curl](https://www.npmjs.com/package/@crossbind/port-curl) | ![npm](https://img.shields.io/npm/v/@crossbind/port-curl/beta) |
109
- | [@crossbind/port-tiff](https://www.npmjs.com/package/@crossbind/port-tiff) | ![npm](https://img.shields.io/npm/v/@crossbind/port-tiff/beta) |
110
- | [@crossbind/port-geotiff](https://www.npmjs.com/package/@crossbind/port-geotiff) | ![npm](https://img.shields.io/npm/v/@crossbind/port-geotiff/beta) |
111
- | [@crossbind/port-webp](https://www.npmjs.com/package/@crossbind/port-webp) | ![npm](https://img.shields.io/npm/v/@crossbind/port-webp/beta) |
112
- | [@crossbind/port-expat](https://www.npmjs.com/package/@crossbind/port-expat) | ![npm](https://img.shields.io/npm/v/@crossbind/port-expat/beta) |
113
- | [@crossbind/port-iconv](https://www.npmjs.com/package/@crossbind/port-iconv) | ![npm](https://img.shields.io/npm/v/@crossbind/port-iconv/beta) |
114
- | [@crossbind/port-zlib](https://www.npmjs.com/package/@crossbind/port-zlib) | ![npm](https://img.shields.io/npm/v/@crossbind/port-zlib/beta) |
115
- | [@crossbind/port-zstd](https://www.npmjs.com/package/@crossbind/port-zstd) | ![npm](https://img.shields.io/npm/v/@crossbind/port-zstd/beta) |
116
- | [@crossbind/port-lerc](https://www.npmjs.com/package/@crossbind/port-lerc) | ![npm](https://img.shields.io/npm/v/@crossbind/port-lerc/beta) |
117
- | [@crossbind/port-jpegturbo](https://www.npmjs.com/package/@crossbind/port-jpegturbo) | ![npm](https://img.shields.io/npm/v/@crossbind/port-jpegturbo/beta) |
118
-
119
- Browse all available packages at [crossbind.dev/docs/package/package/showcase](https://crossbind.dev/docs/package/package/showcase).
120
-
121
- ## 🌱 Community Packages
122
- Community-maintained, prebuilt C++ libraries packaged for crossbind — published under the [crossbind-community](https://github.com/crossbind-community) organization. Anyone can contribute new packages following the same standard, and they'll be listed here.
123
-
124
- | Package | Repository |
125
- | --- | --- |
126
- | simdjson | [github.com/crossbind-community/package-simdjson](https://github.com/crossbind-community/package-simdjson) |
127
-
128
- Want to add yours? Start a discussion at [crossbind/crossbind/discussions](https://github.com/crossbind/crossbind/discussions).
171
+ You can now add the header and JavaScript import from the quick-start example.
172
+ For React, Vue, Svelte, Webpack, Rspack, Rollup, Metro, and framework-specific
173
+ setups, use the [integration playbooks](https://github.com/crossbind/crossbind/blob/main/docs/playbooks/integration/README.md)
174
+ or start from the closest project in [`examples/`](https://github.com/crossbind/crossbind/tree/main/examples).
175
+
176
+ ## How it works
177
+
178
+ ```text
179
+ C++ / Rust source header, .rs, or
180
+ or prebuilt port cargo: import
181
+ | |
182
+ v |
183
+ versioned native package graph |
184
+ dependencies + target variants |
185
+ | |
186
+ +-------------------+-------------------+
187
+ |
188
+ v
189
+ target resolution
190
+ + binding generation
191
+ + native build and link
192
+ |
193
+ +---------------------+---------------------+
194
+ | | |
195
+ v v v
196
+ browser / Node / edge iOS / Android WASI
197
+ JavaScript + Wasm native library + JSI command component
198
+ ```
199
+
200
+ There are two configuration surfaces:
201
+
202
+ | Surface | When it is read | What belongs there |
203
+ | --------------------- | --------------- | -------------------------------------------------------------------------------------- |
204
+ | `crossbind.config.js` | Build time | Native paths, dependencies, target, runtime (`st`/`mt`), output, and compiler settings |
205
+ | `initNative(options)` | Runtime | Worker mode, filesystem behavior, environment, asset resolution, and lifecycle hooks |
206
+
207
+ Do not put runtime options such as `useWorker` in `crossbind.config.js`; they
208
+ only take effect when passed to `initNative(...)`. See the
209
+ [runtime and configuration reference](https://github.com/crossbind/crossbind/blob/main/docs/api/README.md) for the complete
210
+ contract.
211
+
212
+ ## Runtimes
213
+
214
+ | Runtime | Output | Important behavior |
215
+ | ------------ | ------------------------------------- | ------------------------------------------------------------------------------------- |
216
+ | Browser | JavaScript loader + WebAssembly | Single-threaded by default; multithreaded builds need COOP/COEP headers in production |
217
+ | Node.js | JavaScript loader + WebAssembly | Supports CommonJS or ESM and reads the host filesystem |
218
+ | Edge | JavaScript loader + WebAssembly | Single-threaded, memory-backed, with no OPFS or nested worker mode |
219
+ | React Native | Native iOS/Android libraries over JSI | No WebAssembly or browser isolation headers |
220
+ | WASI | One `wasm32-wasip3` command component | No JavaScript host; run with a compatible WASI runtime such as wasmtime 47+ |
221
+
222
+ Threading and worker execution are independent choices:
223
+
224
+ - `target.runtime: 'mt'` enables a multithreaded build.
225
+ - `initNative({ useWorker: true })` moves the module into a browser Web Worker.
226
+ - Browser OPFS persistence requires `useWorker: true`.
227
+ - Browser `mt` builds require COOP/COEP response headers in production.
228
+
229
+ Read [Threading and workers](https://github.com/crossbind/crossbind/blob/main/docs/api/threading.md)
230
+ and [Filesystem](https://github.com/crossbind/crossbind/blob/main/docs/api/filesystem.md)
231
+ before enabling those features.
232
+
233
+ ## Build integrations
234
+
235
+ | Build tool or runtime | Packages |
236
+ | --------------------- | --------------------------------------------------------------- |
237
+ | Vite | `@crossbind/plugin-vite` |
238
+ | Rollup | `@crossbind/plugin-rollup` |
239
+ | Webpack / Rspack | `@crossbind/plugin-webpack`, `@crossbind/plugin-webpack-loader` |
240
+ | React Native / Metro | `@crossbind/plugin-react-native`, `@crossbind/plugin-metro` |
241
+ | No bundler | `crossbind` CLI |
242
+
243
+ The plugins resolve native imports, generate bridges, trigger builds, watch
244
+ native source directories, and place the produced assets into the application
245
+ bundle. The CLI exposes the same build pipeline for Node.js, edge, library, and
246
+ WASI projects.
247
+
248
+ ## Prebuilt libraries and commands
249
+
250
+ The repository contains 16 port families:
251
+
252
+ **cURL · Expat · GDAL · GEOS · GeoTIFF · iconv · libjpeg-turbo · LERC ·
253
+ OpenSSL · PROJ · SpatiaLite · SQLite · libTIFF · WebP · zlib · Zstandard**
254
+
255
+ Each family has target-specific packages. For example, a browser or Node.js
256
+ project uses `@crossbind/port-gdal-wasm`, while mobile and WASI builds use the
257
+ matching `-android`, `-ios`, or `-wasi` variant. Import the variant's
258
+ `crossbind.config.js` into the application's dependency list:
259
+
260
+ ```bash
261
+ npm install @crossbind/port-gdal-wasm@beta
262
+ ```
263
+
264
+ ```js
265
+ // crossbind.config.js
266
+ import gdal from '@crossbind/port-gdal-wasm/crossbind.config.js';
267
+
268
+ export default {
269
+ dependencies: [gdal],
270
+ paths: {
271
+ config: import.meta.url,
272
+ },
273
+ };
274
+ ```
275
+
276
+ The variant declares its transitive native dependency graph, so link order is
277
+ derived automatically. Install and register every platform variant your
278
+ project actually builds. See the [package guide](https://crossbind.dev/guide/packages/)
279
+ and [`ports/README.md`](https://github.com/crossbind/crossbind/blob/main/ports/README.md)
280
+ for the package and license contracts.
281
+
282
+ Some upstream command-line tools are also published as WASI-powered npm
283
+ commands. They require `wasmtime` on `PATH`, but no compiler:
284
+
285
+ ```bash
286
+ npm install --global @crossbind/port-gdal-bin-wasi@beta
287
+ gdalinfo-wasi --version
288
+ ```
289
+
290
+ ## Rust
291
+
292
+ crossbind supports app-local `.rs` files, direct crates.io imports through the
293
+ `cargo:` scheme, and reusable Cargo-backed packages. Rust uses the same
294
+ generated-module and `initNative()` model as C++.
295
+
296
+ A local Rust toolchain is required. WebAssembly multithreading currently needs
297
+ nightly Rust, and WASI command builds do not yet support Rust because there is
298
+ no `wasm32-wasip3` Rust target. Start with the
299
+ [Rust guide](https://github.com/crossbind/crossbind/blob/main/docs/api/rust.md)
300
+ for the supported type surface and configuration.
301
+
302
+ ## Documentation
303
+
304
+ - [Guide](https://crossbind.dev/guide/) — product concepts, quick start,
305
+ runtimes, bundlers, bindings, packages, and troubleshooting.
306
+ - [Runtime and configuration API](https://github.com/crossbind/crossbind/blob/main/docs/api/README.md)
307
+ — canonical build-time and runtime behavior.
308
+ - [Integration playbooks](https://github.com/crossbind/crossbind/blob/main/docs/playbooks/integration/README.md)
309
+ — Vite, Webpack/Rspack, Rollup, Node.js, edge, React Native, and more.
310
+ - [Examples](https://github.com/crossbind/crossbind/tree/main/examples) —
311
+ runnable reference applications and library templates.
312
+ - [Architecture](https://github.com/crossbind/crossbind/blob/main/docs/ARCHITECTURE.md)
313
+ and [codemap](https://github.com/crossbind/crossbind/blob/main/docs/CODEMAP.md)
314
+ — how the monorepo is organized and where each subsystem lives.
315
+ - [Troubleshooting](https://github.com/crossbind/crossbind/blob/main/docs/api/troubleshooting.md)
316
+ — common errors and their standard fixes.
317
+ - [Changelog](https://github.com/crossbind/crossbind/blob/main/CHANGELOG.md) —
318
+ release history.
319
+
320
+ ## Contributing
321
+
322
+ This is a pnpm monorepo. To work on crossbind itself:
323
+
324
+ ```bash
325
+ git clone https://github.com/crossbind/crossbind.git
326
+ cd crossbind
327
+ pnpm install
328
+ pnpm run doctor
329
+ pnpm test
330
+ ```
331
+
332
+ Use a package filter for day-to-day builds; the full native matrix is
333
+ intentionally expensive. Read
334
+ [CONTRIBUTING.md](https://github.com/crossbind/crossbind/blob/main/CONTRIBUTING.md)
335
+ for the repository layout, validation matrix, coding conventions, and
336
+ pull-request process.
337
+
338
+ ## Support
339
+
340
+ - Ask usage questions in [GitHub Discussions](https://github.com/crossbind/crossbind/discussions).
341
+ - Report reproducible bugs in [GitHub Issues](https://github.com/crossbind/crossbind/issues).
129
342
 
130
343
  ## License
131
- [MIT](https://github.com/crossbind/crossbind/blob/main/LICENSE)
132
344
 
133
- Copyright (c) 2026, Buğra Sarı
345
+ crossbind is available under the
346
+ [MIT License](https://github.com/crossbind/crossbind/blob/main/LICENSE).
347
+
348
+ Copyright © 2026 Buğra Sarı
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crossbind",
3
- "version": "2.0.0-beta.51",
3
+ "version": "2.0.0-beta.53",
4
4
  "license": "MIT",
5
5
  "homepage": "https://crossbind.dev",
6
6
  "repository": {
@@ -6,6 +6,7 @@ import { getBuildTargets, getFilteredTargetSpec } from './target.js';
6
6
  import state from '../state/index.js';
7
7
  import logger from '../utils/logger.js';
8
8
  import findFiles from '../utils/findFiles.js';
9
+ import { getSourceFingerprint, isSourceFingerprintStale, staleTargetDirectories, writeSourceFingerprint } from '../utils/sourceFingerprint.js';
9
10
 
10
11
  export default function buildLib(targetParams, options = {}) {
11
12
  let isChanged = false;
@@ -19,8 +20,20 @@ export default function buildLib(targetParams, options = {}) {
19
20
  // Cargo re-enters the build unconditionally: cargo is the incremental cache, and the
20
21
  // existence-only skip below would keep serving a stale staged staticlib after source edits.
21
22
  const isCargo = state.config.export?.type === 'cargo';
22
- if (isCargo || !fs.existsSync(`${state.config.paths.output}/prebuilt/${target.path}/lib`)) {
23
- createLib(target, 'Source', { buildSource: true });
23
+ const libdir = `${state.config.paths.output}/prebuilt/${target.path}`;
24
+ const sourceFingerprint = getSourceFingerprint(state.config);
25
+ const sourceChanged = isSourceFingerprintStale(libdir, sourceFingerprint);
26
+ if (isCargo || sourceChanged || !fs.existsSync(`${libdir}/lib`)) {
27
+ if (sourceChanged) {
28
+ for (const stale of staleTargetDirectories({
29
+ buildPath: state.config.paths.build,
30
+ outputPath: state.config.paths.output,
31
+ targetPath: target.path,
32
+ })) fs.rmSync(stale, { recursive: true, force: true });
33
+ }
34
+ // createLib caches on existence too, so re-entering is not enough: a stale stamp means
35
+ // the lib on disk came from a different upstream release and has to be rebuilt.
36
+ createLib(target, 'Source', { buildSource: true, force: sourceChanged });
24
37
 
25
38
  const modules = [];
26
39
  state.config.paths.module.forEach((modulePath) => {
@@ -34,6 +47,7 @@ export default function buildLib(targetParams, options = {}) {
34
47
  const fileName = modulePath.split('/').at(-1);
35
48
  fs.copyFileSync(modulePath, `${state.config.paths.output}/prebuilt/${target.path}/swig/${fileName}`);
36
49
  });
50
+ writeSourceFingerprint(libdir, sourceFingerprint);
37
51
  isChanged = true;
38
52
  } else {
39
53
  // The skip is existence-only; without this warning a source edit is served stale silently.
@@ -79,7 +93,7 @@ export default function buildLib(targetParams, options = {}) {
79
93
  : `-Wl,-u,_crossbind_keep_${l}`)).join(' ');
80
94
  ldFlags = `, 'OTHER_LDFLAGS' => '${flags}'`;
81
95
  }
82
- const distPodSpecContent = fs.readFileSync(`${state.config.paths.cli}/assets/packaging/crossbind-package.podspec`, { encoding: 'utf8', flag: 'r' })
96
+ const distPodSpecContent = fs.readFileSync(`${state.config.paths.cli}/assets/packaging/crossbind-port.podspec`, { encoding: 'utf8', flag: 'r' })
83
97
  // module_name must be a valid C99 identifier (pod names may carry dashes).
84
98
  .replace('___PROJECT_MODULE_NAME___', state.config.general.name.replace(/[^a-zA-Z0-9_]/g, '_'))
85
99
  .replaceAll('___PROJECT_NAME___', state.config.general.name)
@@ -0,0 +1,40 @@
1
+ import fs from 'node:fs';
2
+ import { getContentHash } from './hash.js';
3
+
4
+ // The prebuilt-lib cache in buildLib is existence-only, so a nativeVersion or recipe-sha256 bump
5
+ // would otherwise be served from the previous build - shipping the old upstream under the new
6
+ // version, with provenance and licence metadata already claiming the new one.
7
+ export const SOURCE_FINGERPRINT_FILE = 'crossbind-source.fingerprint';
8
+
9
+ export function getSourceFingerprint(config) {
10
+ const nativeVersion = config?.package?.nativeVersion || null;
11
+ const sha256 = config?.build?.sha256 || null;
12
+ // Packages that build from local sources pin neither; they keep the existence-only behaviour.
13
+ if (!nativeVersion && !sha256) return null;
14
+ return getContentHash(JSON.stringify({ nativeVersion, sha256 }));
15
+ }
16
+
17
+ export function isSourceFingerprintStale(libdir, fingerprint) {
18
+ if (!fingerprint) return false;
19
+ const file = `${libdir}/${SOURCE_FINGERPRINT_FILE}`;
20
+ return !fs.existsSync(file) || fs.readFileSync(file, { encoding: 'utf8' }) !== fingerprint;
21
+ }
22
+
23
+ export function writeSourceFingerprint(libdir, fingerprint) {
24
+ if (!fingerprint) return;
25
+ fs.mkdirSync(libdir, { recursive: true });
26
+ fs.writeFileSync(`${libdir}/${SOURCE_FINGERPRINT_FILE}`, fingerprint);
27
+ }
28
+
29
+ // A changed upstream release leaves the previous configure output and install tree behind. Reusing
30
+ // them makes `make install` fail where it cannot chmod a file it did not create, and would leave
31
+ // files of the old version in the output next to the new ones.
32
+ export function staleTargetDirectories({ buildPath, outputPath, targetPath }) {
33
+ return [
34
+ `${buildPath}/Source-Release/${targetPath}`,
35
+ `${buildPath}/Source-Release/prebuilt/${targetPath}`,
36
+ `${buildPath}/Source-Debug/${targetPath}`,
37
+ `${buildPath}/Source-Debug/prebuilt/${targetPath}`,
38
+ `${outputPath}/prebuilt/${targetPath}`,
39
+ ];
40
+ }