crossbind 0.0.1 → 2.0.0-beta.50
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 +92 -0
- package/CHANGELOG.md +145 -0
- package/LICENSE +21 -0
- package/README.md +133 -0
- package/package.json +52 -4
- package/src/actions/buildBinTools.js +0 -0
- package/src/actions/buildCargo.js +88 -0
- package/src/actions/buildDependencies.js +157 -0
- package/src/actions/buildExternal.js +57 -0
- package/src/actions/buildJs.js +93 -0
- package/src/actions/buildLib.js +115 -0
- package/src/actions/buildTypes.js +33 -0
- package/src/actions/buildWasiCommand.js +101 -0
- package/src/actions/buildWasm.js +322 -0
- package/src/actions/createInterface.js +172 -0
- package/src/actions/createLib.js +249 -0
- package/src/actions/createXCFramework.js +82 -0
- package/src/actions/extensions.js +7 -0
- package/src/actions/getAllBridges.js +20 -0
- package/src/actions/getCmakeParameters.js +66 -0
- package/src/actions/getData.js +46 -0
- package/src/actions/getDependLibs.js +25 -0
- package/src/actions/isSourceNewer.js +35 -0
- package/src/actions/licenses.js +156 -0
- package/src/actions/run.js +305 -0
- package/src/actions/target.js +54 -0
- package/src/assets/cmake/CMakeLists.txt +61 -0
- package/src/assets/cmake/dist.cmake +57 -0
- package/src/assets/cmake/ios.toolchain.cmake +1176 -0
- package/src/assets/cpp-runtime/browser.cpp +12 -0
- package/src/assets/cpp-runtime/commonBridges.cpp +17 -0
- package/src/assets/cpp-runtime/crossbindEmptySource.cpp +9 -0
- package/src/assets/cpp-runtime/node.cpp +19 -0
- package/src/assets/js-runtime/adapters/exception-decode.js +79 -0
- package/src/assets/js-runtime/adapters/fs-browser.js +173 -0
- package/src/assets/js-runtime/adapters/fs-node.js +28 -0
- package/src/assets/js-runtime/adapters/path-fs.js +6 -0
- package/src/assets/js-runtime/adapters/path-url.js +8 -0
- package/src/assets/js-runtime/adapters/vector-coercion.js +209 -0
- package/src/assets/js-runtime/adapters/worker-comlink.js +309 -0
- package/src/assets/js-runtime/browser.js +16 -0
- package/src/assets/js-runtime/core.js +193 -0
- package/src/assets/js-runtime/edge.js +18 -0
- package/src/assets/js-runtime/node.js +20 -0
- package/src/assets/packaging/crossbind-package.podspec +17 -0
- package/src/assets/wasi-runtime/stubs.c +84 -0
- package/src/bin.js +416 -0
- package/src/index.js +19 -0
- package/src/integration/getCrossbindScript.js +180 -0
- package/src/integration/getDependFilePath.js +132 -0
- package/src/runtime/wasiRun.mjs +118 -0
- package/src/state/calculateDependencyParameters.js +97 -0
- package/src/state/index.js +128 -0
- package/src/state/loadConfig.js +215 -0
- package/src/utils/appRustCrates.js +106 -0
- package/src/utils/binTools.js +76 -0
- package/src/utils/bridgeAsyncGuard.js +37 -0
- package/src/utils/cargoTarget.js +15 -0
- package/src/utils/cppDts.js +201 -0
- package/src/utils/cppFieldBindings.js +23 -0
- package/src/utils/dependencyRebuild.js +99 -0
- package/src/utils/dirLock.js +71 -0
- package/src/utils/downloadAndExtractFile.js +102 -0
- package/src/utils/familyManifest.js +17 -0
- package/src/utils/findFiles.js +5 -0
- package/src/utils/fixPackageName.js +7 -0
- package/src/utils/flattenConfigForTable.js +75 -0
- package/src/utils/getAbsolutePath.js +14 -0
- package/src/utils/getCMakeListsFilePath.js +17 -0
- package/src/utils/getOsUserAndGroupId.js +11 -0
- package/src/utils/getParentPath.js +12 -0
- package/src/utils/hash.js +33 -0
- package/src/utils/inputStamp.js +28 -0
- package/src/utils/licenseReport.js +94 -0
- package/src/utils/linkLayout.js +34 -0
- package/src/utils/loadJs.js +31 -0
- package/src/utils/loadJson.js +16 -0
- package/src/utils/logger.js +110 -0
- package/src/utils/ociArtifact.js +124 -0
- package/src/utils/overrideDependency.js +99 -0
- package/src/utils/provenance.js +54 -0
- package/src/utils/pullDockerImage.js +105 -0
- package/src/utils/replaceBasePathForDocker.js +18 -0
- package/src/utils/resolveEmbindRust.js +66 -0
- package/src/utils/runCargo.js +228 -0
- package/src/utils/rustBridgeGen.js +1013 -0
- package/src/utils/rustMt.js +76 -0
- package/src/utils/rustSysroot.js +171 -0
- package/src/utils/rustSysrootPin.js +11 -0
- package/src/utils/systemKeys.js +29 -0
- package/src/utils/targets.js +109 -0
- package/src/utils/wasiToolchain.js +47 -0
- package/src/utils/writeIfChanged.js +8 -0
- package/src/utils/writeJson.js +9 -0
- package/vitest.config.js +40 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# AGENTS.md — core/crossbind
|
|
2
|
+
|
|
3
|
+
> The CLI + build orchestrator that everything else in the monorepo plugs into. **The most-touched package**, also the most blast-radius-sensitive.
|
|
4
|
+
|
|
5
|
+
## What lives here
|
|
6
|
+
|
|
7
|
+
- `bin.js` — `crossbind` / `crossbind` CLI entry; commander commands, target filtering, top-level orchestration.
|
|
8
|
+
- `src/index.js` — public API barrel re-exported as the `crossbind` npm package (`createLib`, `buildWasm`, `state`, `getData`, `isSourceNewer`, …).
|
|
9
|
+
- `src/actions/` — the actual build steps. **Most edits land here.**
|
|
10
|
+
- `src/state/` — config loading + target matrix.
|
|
11
|
+
- `src/utils/` — small helpers (logger, hashing, fs, JSON I/O, Docker pull, …).
|
|
12
|
+
- `src/integration/` — `getCrossbindScript`, `getDependFilePath` (consumer-side helpers used by plugins).
|
|
13
|
+
- `src/assets/` — runtime artifacts shipped with the published package: `js-runtime/` (browser/node/edge JS shims), `cpp-runtime/` (C++ entrypoints), `cmake/` (CMake templates), `packaging/` (podspec template).
|
|
14
|
+
|
|
15
|
+
Use `docs/CODEMAP.md` (repo root) for "concept → file" lookups before guessing.
|
|
16
|
+
|
|
17
|
+
## Build pipeline orchestration (the hot path)
|
|
18
|
+
|
|
19
|
+
Order of operations when a user runs `pnpm crossbind build`:
|
|
20
|
+
|
|
21
|
+
1. **`bin.js`** parses CLI flags, calls `getBuildTargets` to expand the target matrix.
|
|
22
|
+
2. For each target:
|
|
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
|
+
- **`actions/createXCFramework.js`** combines iOS slices into an `.xcframework` (darwin only).
|
|
25
|
+
- **`actions/buildWasm.js`** runs `emcc` for wasm targets, then **`actions/buildJs.js`** rolls up the JS loader.
|
|
26
|
+
3. **`actions/run.js`** is the shell-out boundary: every cmake / make / emcc / xcodebuild call goes through it.
|
|
27
|
+
|
|
28
|
+
Side-quests:
|
|
29
|
+
- **`actions/createInterface.js`** generates SWIG bridge files from `.h`.
|
|
30
|
+
- **`actions/getDependLibs.js`** resolves transitive C++ libs to link.
|
|
31
|
+
- **`actions/getCmakeParameters.js`** builds the `-D...` arg list for cmake.
|
|
32
|
+
- **`actions/isSourceNewer.js`** mtime check used by plugins to decide when to force-rebuild.
|
|
33
|
+
|
|
34
|
+
## Public API contract
|
|
35
|
+
|
|
36
|
+
Anything exported from `src/index.js` is consumed by `plugins/*` and a few CLI scripts (`scripts/check-*.js`). Treat its surface as semver-public — additive changes free, breaking changes need a beta bump and a CHANGELOG note.
|
|
37
|
+
|
|
38
|
+
Currently exported (snapshot):
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
state, createLib, createBridgeFile, buildWasm, isSourceNewer, getData,
|
|
42
|
+
getCmakeParameters, createXCFramework, getAllBridges, run, getTargetParams,
|
|
43
|
+
getBuildTargets, getFilteredBuildTargets, getFilteredTargetSpec,
|
|
44
|
+
getCrossbindScript, getDependFilePath, getParentPath
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If you add a new action, decide explicitly: is this internal (don't export) or part of the contract (export from `index.js` and document)?
|
|
48
|
+
|
|
49
|
+
## Logger conventions
|
|
50
|
+
|
|
51
|
+
`src/utils/logger.js` is the only canonical writer to stdout / stderr.
|
|
52
|
+
|
|
53
|
+
- `logger.startStep(target, fileType)` — opens an in-place TTY line ("compiling…").
|
|
54
|
+
- `logger.doneStep(target, fileType, detail?)` — replaces it with timing + detail.
|
|
55
|
+
- `logger.cachedStep(target, fileType)` — for cache-hit short-circuits.
|
|
56
|
+
- `logger.startTask(label)` / `doneTask(label, detail?)` / `cachedTask(label)` — for non-target work (xcframework, etc.).
|
|
57
|
+
- `logger.info(text)` / `logger.error(text)` — for one-off messages.
|
|
58
|
+
|
|
59
|
+
**Don't add `console.log` in this package.** Route everything through the logger; CI / non-TTY automatically gets newline output.
|
|
60
|
+
|
|
61
|
+
## Validation
|
|
62
|
+
|
|
63
|
+
Anything you change here triggers the **strict** validation gate from the root playbook:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pnpm run ci:linux:build && pnpm run e2e:dev && pnpm run e2e:prod
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Plus, depending on the area:
|
|
70
|
+
|
|
71
|
+
- Touched a new public export → search consumers: `grep -rn "from 'crossbind'" plugins examples scripts`.
|
|
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`).
|
|
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
|
+
- Touched `state/loadConfig.js` defaults → assume blast radius = whole monorepo. Run the full matrix.
|
|
76
|
+
|
|
77
|
+
## Common pitfalls
|
|
78
|
+
|
|
79
|
+
- **Adding `console.log` instead of using the logger.** Breaks the in-place TTY rendering and fails on CI's non-TTY pipe.
|
|
80
|
+
- **Throwing inside `actions/run.js` without surfacing stderr.** Wrap exec calls so the user sees the actual cmake/make/emcc error, not just "exit code 1".
|
|
81
|
+
- **Mutating `state.config` from inside an action.** Treat it as read-only after `loadConfig` runs.
|
|
82
|
+
- **Forgetting `--force`-equivalent paths.** `createLib` and `buildWasm` both honor `options.force`; new actions that cache should follow the same convention so plugins can opt out.
|
|
83
|
+
- **Hardcoding paths instead of going through `state.config.paths.*`.** The path resolution lives in `state/loadConfig.js` for a reason.
|
|
84
|
+
- **Adding a runtime adapter that doesn't compose `core.js`.** New `js-runtime/<runtime>.js` files should be thin shims that `createInitCrossbind(...)` composes from `adapters/*`.
|
|
85
|
+
|
|
86
|
+
## Reference
|
|
87
|
+
|
|
88
|
+
- `docs/ARCHITECTURE.md` — high-level flow + key abstractions.
|
|
89
|
+
- `docs/CODEMAP.md` — concept → file index.
|
|
90
|
+
- `docs/playbooks/bug-fix.md` — fix workflow.
|
|
91
|
+
- `docs/playbooks/new-package.md` — package author workflow (for `ports/` not this dir).
|
|
92
|
+
- Logger: `src/utils/logger.js` — read this before adding any user-facing output.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# crossbind
|
|
2
|
+
|
|
3
|
+
## 2.0.0-beta.50
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- **Generated framework identifiers moved to `dev.crossbind`.** Every iOS and macOS framework
|
|
8
|
+
crossbind builds now identifies itself as `dev.crossbind.<name>` rather than
|
|
9
|
+
`org.js.cpp.<name>`. Anything that pins, inspects or signs against the old identifier has to be
|
|
10
|
+
updated.
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- **`RUNNER=LOCAL` no longer needs nightly Rust for shared-memory wasm.** The st and mt sysroots
|
|
15
|
+
shipped inside the toolchain image are now readable by a host build too: the CLI resolves a
|
|
16
|
+
pinned index digest and streams the layer straight out of the registry over HTTPS — no docker, no
|
|
17
|
+
second artifact hosted anywhere, the same object the image embeds. mt links a std that was
|
|
18
|
+
already built with the shared-memory features, so `-Zbuild-std` and the nightly toolchain are
|
|
19
|
+
gone from that path. Where the host compiler does not match the one that produced the rlibs, the
|
|
20
|
+
build says so and falls back to the nightly rebuild instead of failing.
|
|
21
|
+
|
|
22
|
+
- **Cargo dependencies build themselves.** A cargo package carries every target in one package, so
|
|
23
|
+
a missing prebuilt never meant "not for this platform" — it meant nobody had built it, and
|
|
24
|
+
nothing did. On android that surfaced as an error; on wasm the dependency dropped out of the link
|
|
25
|
+
and the module built clean and died at init. The dependency step now builds it, and a guard
|
|
26
|
+
refuses to link one that is still missing afterwards.
|
|
27
|
+
|
|
28
|
+
- **`RUNNER=DOCKER_EXEC` works with the cargo mount.** `docker exec` does not create the working
|
|
29
|
+
directory `--workdir` names, and a container made before the cargo mount existed kept its
|
|
30
|
+
registry inside itself — which made bridge generation read a host path that was never written.
|
|
31
|
+
Both are handled, and a container that is missing, stopped or wrongly mounted now fails with the
|
|
32
|
+
exact `docker run` command that fixes it.
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- Pinned to the 1.0.2 toolchain images. They are published to GHCR, mirrored to Docker Hub with
|
|
37
|
+
byte-identical digests, and signed with keyless Sigstore certificates that verify on both
|
|
38
|
+
registries — including the android `linux/amd64` leaf the CLI pins directly.
|
|
39
|
+
|
|
40
|
+
## 2.0.0-beta.35
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- **wasi runner: a platform's env no longer replaces the shared one** — a recipe declares its
|
|
45
|
+
environment once for every platform and adds per-platform knobs on top, but the runner merged
|
|
46
|
+
the blocks with a shallow `Object.assign`, so the wasi block's `env` dropped the shared one.
|
|
47
|
+
Every `gdal*-wasi` command therefore ran without `GDAL_DATA` (and said so) even though the
|
|
48
|
+
data directory was mounted. `data` and `env` now merge key by key.
|
|
49
|
+
|
|
50
|
+
## 2.0.0-beta.34
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- **`decompress` and `follow-redirects` are gone** — upstream archives are extracted with the
|
|
55
|
+
system `tar` and downloaded with `fetch`, which follows redirects itself. That drops an
|
|
56
|
+
unfixable critical advisory (CVE-2026-53486, zip-slip; upstream unmaintained since 2020) and
|
|
57
|
+
a package with five advisories in its history from every consumer's tree.
|
|
58
|
+
- **Source downloads require https** — the scheme is asserted before the request and again on
|
|
59
|
+
the final URL, so a redirect cannot downgrade the transport. Loopback stays exempt for tests.
|
|
60
|
+
|
|
61
|
+
## 2.0.0-beta.33
|
|
62
|
+
|
|
63
|
+
### Minor Changes
|
|
64
|
+
|
|
65
|
+
- **`platform: 'wasi'`** — compile a project into a single WASI command component
|
|
66
|
+
(`wasm32-wasip3`, runs under wasmtime 47+). Dual-mode toolchain: host wasi-sdk via
|
|
67
|
+
`WASI_SDK_PATH` / `CROSSBIND_WASI_SDK_PATH`, or zero-config inside the digest-pinned
|
|
68
|
+
docker image. New `targetSpecs.specs.wasiFlags`; data lands in a real `dist/data/`
|
|
69
|
+
folder. See `docs/api/wasi.md`.
|
|
70
|
+
- **Rust bindings, first-class** — import plain Rust like a C++ header: direct crate
|
|
71
|
+
imports via the `cargo:` scheme (`import { Uuid } from 'cargo:uuid'`, declared in
|
|
72
|
+
the top-level `cargoDependencies` map), app-local `.rs` sources, and whole-crate
|
|
73
|
+
packages via `export.type: 'cargo'`. The engine does not depend on the binding
|
|
74
|
+
layer — consumers (or the bundler plugins) declare `@crossbind/core-embind-rust`.
|
|
75
|
+
TypeScript declarations are generated under `.crossbind/` (never in your source
|
|
76
|
+
tree) and wired by the shared `@crossbind/typescript-config` package - add one
|
|
77
|
+
`"extends": "@crossbind/typescript-config"` line (TS 5.5+) and never manage
|
|
78
|
+
the wiring by hand. See `docs/api/rust.md`.
|
|
79
|
+
- **`-bin` tool engine** — a recipe-declared `bin` map drives everything a
|
|
80
|
+
`-bin-wasi` package ships: generated `<tool>-wasi` npm command shims over the
|
|
81
|
+
in-engine runner (`src/runtime/wasiRun.mjs` — mounts and guest env resolved from
|
|
82
|
+
the config graph at call time), a derived `.npmignore`, a pure-data
|
|
83
|
+
`crossbind-bin.json` and an optional multicall multitool binary.
|
|
84
|
+
- **TypeScript declarations for C++ `.h` imports** — the bridge step now emits a
|
|
85
|
+
best-effort `.d.ts` per imported header (classes, constructors, method
|
|
86
|
+
signatures; unparsed members fall back to `any`) into the `.crossbind/types/`
|
|
87
|
+
mirror, alongside the Rust declarations - both wired by
|
|
88
|
+
`@crossbind/typescript-config`. A top-level `dts: 'sync' | 'promise'` config
|
|
89
|
+
field picks the flavor: `'promise'` wraps every generated return for
|
|
90
|
+
`useWorker`-style async runtimes (constructors stay sync-typed; write
|
|
91
|
+
`await new X(...)`). Packages can publish their types: `types: true` emits
|
|
92
|
+
one combined declaration over every public header into
|
|
93
|
+
`dist/types/index.d.ts` and wires package.json `types`/`typesVersions`
|
|
94
|
+
automatically. std::vector crosses as a typed `CppVector<T>`; shared_ptr
|
|
95
|
+
returns resolve to `X | null`.
|
|
96
|
+
- **`crossbind licenses`** — list bundled native dependencies with SPDX expressions,
|
|
97
|
+
write derived `THIRD-PARTY-LICENSES.md` notices and CycloneDX SBOMs
|
|
98
|
+
(`--notices` / `--sbom` / `--check`); `--platform` additionally rows up vendored
|
|
99
|
+
copies (recipe `bundled` map) and the statically linked wasi toolchain runtime.
|
|
100
|
+
- **Provenance + derived license for `-bin` packages** — every wasi build stamps a
|
|
101
|
+
machine-readable `crossbind.provenance` block (recipe, source sha256, build
|
|
102
|
+
environment, SBOM pointer) and derives the npm `license` field as the AND of all
|
|
103
|
+
effective component licenses, plus a derived root `LICENSE` (the same
|
|
104
|
+
expression followed by every component's full license text - built from the
|
|
105
|
+
same rows as the NOTICE and SBOM).
|
|
106
|
+
|
|
107
|
+
## 2.0.0-beta.26
|
|
108
|
+
|
|
109
|
+
**Potentially breaking — `VectorUChar`.** The shared runtime (`commonBridges.cpp`) now
|
|
110
|
+
registers `std::vector<unsigned char>` as `VectorUChar` for every target (wasm and the
|
|
111
|
+
native JSI runtimes). If your own code — or a wrapped library such as gdal3.js —
|
|
112
|
+
already registers `VectorUChar` locally, remove that registration before upgrading:
|
|
113
|
+
embind aborts initialisation when the same vector name is registered twice.
|
|
114
|
+
|
|
115
|
+
## 2.0.0-beta
|
|
116
|
+
|
|
117
|
+
The 2.0.0 line is a ground-up rework — native iOS/Android via JSI, multi-bundler
|
|
118
|
+
plugins (Vite, Rollup, Webpack, Metro), an MCP server, and per-library
|
|
119
|
+
`@crossbind/port-*` families — published on the `beta` dist-tag. Per-release
|
|
120
|
+
notes for the beta line are tracked in the git history and on npm until 2.0
|
|
121
|
+
stabilises; the entries below cover the 1.x line.
|
|
122
|
+
|
|
123
|
+
## 1.0.4
|
|
124
|
+
|
|
125
|
+
### Patch Changes
|
|
126
|
+
|
|
127
|
+
- fix: workaround for race condition with turbomodule in android.
|
|
128
|
+
|
|
129
|
+
## 1.0.1
|
|
130
|
+
|
|
131
|
+
### Patch Changes
|
|
132
|
+
|
|
133
|
+
- fix: include prebuilt/_/_config.general.name_/_.h in dependency header search paths
|
|
134
|
+
|
|
135
|
+
## 1.0.0
|
|
136
|
+
|
|
137
|
+
### Major Changes
|
|
138
|
+
|
|
139
|
+
- 🚀 first stable release
|
|
140
|
+
|
|
141
|
+
## 1.0.0-beta.33
|
|
142
|
+
|
|
143
|
+
### Patch Changes
|
|
144
|
+
|
|
145
|
+
- chore: add initial version of CHANGELOGS files
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Buğra Sarı and crossbind contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<a href="https://crossbind.dev">
|
|
3
|
+
<picture>
|
|
4
|
+
<img alt="crossbind logo" src="https://crossbind.dev/img/logo.png" height="128">
|
|
5
|
+
</picture>
|
|
6
|
+
</a>
|
|
7
|
+
<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">
|
|
21
|
+
</div>
|
|
22
|
+
|
|
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
|
|
62
|
+
npm create crossbind@beta
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Basic Usage
|
|
66
|
+
**src/index.js**
|
|
67
|
+
```js
|
|
68
|
+
import { initNative, Factorial } from './native/Factorial.h';
|
|
69
|
+
|
|
70
|
+
await initNative();
|
|
71
|
+
const factorial = new Factorial(99999);
|
|
72
|
+
const result = factorial.calculate();
|
|
73
|
+
console.log(result);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**src/native/Factorial.h**
|
|
77
|
+
```c++
|
|
78
|
+
class Factorial {
|
|
79
|
+
private:
|
|
80
|
+
int number;
|
|
81
|
+
|
|
82
|
+
public:
|
|
83
|
+
Factorial(int num) : number(num) {}
|
|
84
|
+
|
|
85
|
+
int calculate() {
|
|
86
|
+
if (number < 0) return -1;
|
|
87
|
+
|
|
88
|
+
int result = 1;
|
|
89
|
+
for (int i = 2; i <= number; i++) {
|
|
90
|
+
result *= i;
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
```
|
|
96
|
+
|
|
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) |  |
|
|
103
|
+
| [@crossbind/port-geos](https://www.npmjs.com/package/@crossbind/port-geos) |  |
|
|
104
|
+
| [@crossbind/port-proj](https://www.npmjs.com/package/@crossbind/port-proj) |  |
|
|
105
|
+
| [@crossbind/port-spatialite](https://www.npmjs.com/package/@crossbind/port-spatialite) |  |
|
|
106
|
+
| [@crossbind/port-sqlite3](https://www.npmjs.com/package/@crossbind/port-sqlite3) |  |
|
|
107
|
+
| [@crossbind/port-openssl](https://www.npmjs.com/package/@crossbind/port-openssl) |  |
|
|
108
|
+
| [@crossbind/port-curl](https://www.npmjs.com/package/@crossbind/port-curl) |  |
|
|
109
|
+
| [@crossbind/port-tiff](https://www.npmjs.com/package/@crossbind/port-tiff) |  |
|
|
110
|
+
| [@crossbind/port-geotiff](https://www.npmjs.com/package/@crossbind/port-geotiff) |  |
|
|
111
|
+
| [@crossbind/port-webp](https://www.npmjs.com/package/@crossbind/port-webp) |  |
|
|
112
|
+
| [@crossbind/port-expat](https://www.npmjs.com/package/@crossbind/port-expat) |  |
|
|
113
|
+
| [@crossbind/port-iconv](https://www.npmjs.com/package/@crossbind/port-iconv) |  |
|
|
114
|
+
| [@crossbind/port-zlib](https://www.npmjs.com/package/@crossbind/port-zlib) |  |
|
|
115
|
+
| [@crossbind/port-zstd](https://www.npmjs.com/package/@crossbind/port-zstd) |  |
|
|
116
|
+
| [@crossbind/port-lerc](https://www.npmjs.com/package/@crossbind/port-lerc) |  |
|
|
117
|
+
| [@crossbind/port-jpegturbo](https://www.npmjs.com/package/@crossbind/port-jpegturbo) |  |
|
|
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).
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
[MIT](https://github.com/crossbind/crossbind/blob/main/LICENSE)
|
|
132
|
+
|
|
133
|
+
Copyright (c) 2026, Buğra Sarı
|
package/package.json
CHANGED
|
@@ -1,7 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crossbind",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Name reserved. crossbind is the new name of cpp.js — release in progress.",
|
|
3
|
+
"version": "2.0.0-beta.50",
|
|
5
4
|
"license": "MIT",
|
|
6
|
-
"homepage": "https://crossbind.dev"
|
|
7
|
-
|
|
5
|
+
"homepage": "https://crossbind.dev",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/crossbind/crossbind.git"
|
|
9
|
+
},
|
|
10
|
+
"description": "Bind C++ to JavaScript without extra code. crossbind supports WebAssembly and React Native for cross-platform compatibility.",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=20"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"crossbind": "./src/bin.js"
|
|
17
|
+
},
|
|
18
|
+
"main": "src/index.js",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
21
|
+
"vitest": "^4.1.10"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@rollup/plugin-commonjs": "^29.0.3",
|
|
25
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
26
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
27
|
+
"@rollup/plugin-virtual": "^3.0.2",
|
|
28
|
+
"comlink": "^4.4.2",
|
|
29
|
+
"commander": "^15.0.0",
|
|
30
|
+
"glob": "^13.0.6",
|
|
31
|
+
"log-update": "^8.0.0",
|
|
32
|
+
"picocolors": "^1.1.1",
|
|
33
|
+
"replace": "^1.2.2",
|
|
34
|
+
"rollup": "^4.62.4",
|
|
35
|
+
"spdx-expression-parse": "^5.0.0",
|
|
36
|
+
"upath": "^3.0.8"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"webassembly",
|
|
40
|
+
"react-native",
|
|
41
|
+
"bind",
|
|
42
|
+
"c++",
|
|
43
|
+
"emscripten",
|
|
44
|
+
"embind",
|
|
45
|
+
"swig",
|
|
46
|
+
"android",
|
|
47
|
+
"ios",
|
|
48
|
+
"mobile"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"test:coverage": "vitest run --coverage"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import state from '../state/index.js';
|
|
4
|
+
import logger from '../utils/logger.js';
|
|
5
|
+
import { cargoTripleFor } from '../utils/cargoTarget.js';
|
|
6
|
+
import { isMtWasm, assertMtRustToolchain, cargoTargetDirFor, cargoBuildInvocation } from '../utils/rustMt.js';
|
|
7
|
+
import generateRustBridge from '../utils/rustBridgeGen.js';
|
|
8
|
+
import runCargo from '../utils/runCargo.js';
|
|
9
|
+
import { sysrootFor } from '../utils/rustSysroot.js';
|
|
10
|
+
|
|
11
|
+
// buildType 'cargo': build a Rust staticlib on the host for the target's triple and stage it
|
|
12
|
+
// like any other prebuilt (lib/lib<name>.a + include/*), so the normal prebuilt-link machinery
|
|
13
|
+
// picks it up for every platform. The crate is local source; no download step (no buildExternal).
|
|
14
|
+
export default function buildCargo(target, libdir) {
|
|
15
|
+
const triple = cargoTripleFor(target);
|
|
16
|
+
if (!triple) {
|
|
17
|
+
throw new Error(`crossbind: buildType 'cargo' does not support platform '${target.platform}/${target.arch}'.`);
|
|
18
|
+
}
|
|
19
|
+
const { paths, export: exp, general } = state.config;
|
|
20
|
+
const crateDir = path.resolve(paths.project, exp.crate ?? 'crate');
|
|
21
|
+
const libName = exp.libName?.[0] ?? general.name;
|
|
22
|
+
|
|
23
|
+
const probe = runCargo(['--version'], { capture: true, target });
|
|
24
|
+
if (probe.error) {
|
|
25
|
+
throw new Error('crossbind: cargo not found on PATH - install Rust (https://rustup.rs) to build cargo packages.');
|
|
26
|
+
}
|
|
27
|
+
const isMt = isMtWasm(target);
|
|
28
|
+
const sysroot = sysrootFor(target);
|
|
29
|
+
// Only the nightly rebuild needs a nightly toolchain on this machine.
|
|
30
|
+
if (isMt && !sysroot) assertMtRustToolchain();
|
|
31
|
+
|
|
32
|
+
// Auto mode (the default): the user's crate is plain Rust and crossbind generates a companion
|
|
33
|
+
// bridge crate from its pub surface, then builds THAT (it bundles the user crate as a dep).
|
|
34
|
+
// A crate that calls embind_rs::bindings! itself opts out and is built directly.
|
|
35
|
+
const isManual = fs.readFileSync(`${crateDir}/src/lib.rs`, 'utf8').includes('bindings!');
|
|
36
|
+
const buildDir = isManual
|
|
37
|
+
? crateDir
|
|
38
|
+
: generateRustBridge({
|
|
39
|
+
crateDir,
|
|
40
|
+
vectors: exp.bindings?.vectors ?? [],
|
|
41
|
+
dtsFile: `${paths.project}/dist/js/index.d.ts`,
|
|
42
|
+
dtsMode: state.config.dts,
|
|
43
|
+
keepName: libName,
|
|
44
|
+
log: (m) => logger.info(m),
|
|
45
|
+
}).bridgeDir;
|
|
46
|
+
|
|
47
|
+
const t0 = performance.now();
|
|
48
|
+
const targetDir = cargoTargetDirFor(buildDir, target);
|
|
49
|
+
const { args, rustflags, panic, allowUnstable } = cargoBuildInvocation({
|
|
50
|
+
target,
|
|
51
|
+
triple,
|
|
52
|
+
targetDir,
|
|
53
|
+
manifestPath: `${buildDir}/Cargo.toml`,
|
|
54
|
+
sysroot,
|
|
55
|
+
});
|
|
56
|
+
const build = runCargo(args, {
|
|
57
|
+
rustflags, panic, allowUnstable, target,
|
|
58
|
+
});
|
|
59
|
+
if (build.status !== 0) {
|
|
60
|
+
const how = sysroot ? ' (prebuilt sysroot)' : ` (is the target installed? rustup target add ${triple})`;
|
|
61
|
+
throw new Error(`crossbind: cargo build failed for ${triple}${isMt && !sysroot ? ' (mt/build-std)' : how}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Rust staticlib output is lib<crate>.a; stage it under the export libName the CMakeLists expects.
|
|
65
|
+
const crateName = readCrateName(buildDir).replaceAll('-', '_');
|
|
66
|
+
const built = `${targetDir}/${triple}/release/lib${crateName}.a`;
|
|
67
|
+
if (!fs.existsSync(built)) {
|
|
68
|
+
throw new Error(`crossbind: expected cargo output ${built} not found (is crate-type = ["staticlib"]?)`);
|
|
69
|
+
}
|
|
70
|
+
fs.mkdirSync(`${libdir}/lib`, { recursive: true });
|
|
71
|
+
fs.copyFileSync(built, `${libdir}/lib/lib${libName}.a`);
|
|
72
|
+
|
|
73
|
+
fs.mkdirSync(`${libdir}/include`, { recursive: true });
|
|
74
|
+
(exp.headers ?? []).forEach((header) => {
|
|
75
|
+
fs.copyFileSync(path.resolve(paths.project, header), `${libdir}/include/${path.basename(header)}`);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const ms = Math.round(performance.now() - t0);
|
|
79
|
+
logger.doneStep(target, 'Source', `cargo ${ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`}, ${triple}${isMt && !sysroot ? '/build-std' : ''}${sysroot ? `/${isMt ? 'mt' : 'st'}-sysroot` : ''}`);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function readCrateName(crateDir) {
|
|
84
|
+
const toml = fs.readFileSync(`${crateDir}/Cargo.toml`, 'utf8');
|
|
85
|
+
const name = toml.match(/^\s*name\s*=\s*"([^"]+)"/m)?.[1];
|
|
86
|
+
if (!name) throw new Error(`crossbind: could not read [package] name from ${crateDir}/Cargo.toml`);
|
|
87
|
+
return name.replaceAll('-', '_');
|
|
88
|
+
}
|