A verified binary delta pipeline for React Native, Web, Node.js, and release CI.
Create compact patches, prove restored bytes, and plan multi-baseline delivery with one compatible format.
Documentation · Live Playground · Binary Patch Toolkit · Release Planner · 中文说明 · npm
Use it when your app already has an old version of a file and you want to move to a new version without transporting the complete replacement file.
| 1. Create the delta | 2. Deliver it your way | 3. Reconstruct the file |
|---|---|---|
Compare old.bin with new.bin and produce update.patch. |
Send or store the patch with your existing CDN, API, or offline workflow. | Apply update.patch to old.bin and write the restored new.bin. |
The library handles binary diffing and patching. Your application remains in control of transport, authentication, integrity checks, and when an output replaces live data.
- One wire format: Android, iOS, and Web produce compatible
ENDSLEY/BSDIFF43patches. - Every current RN runtime: legacy bridge and TurboModule/New Architecture are both supported.
- Native performance, browser reach: JNI/ObjC++ use the bundled C core; React Native Web runs that core as WebAssembly in a reusable module Worker.
- Control expensive native work: observe progress, cancel cooperatively, cap input/output sizes, and avoid exposing partial output files.
- No patch service required: Web diffing and patching happen locally in the browser.
- Inspect and prove compatibility: read patch metadata and verify restored bytes through the same API shape on native and Web.
- Release-side tooling: generate patches through
npx, publish verified manifests, and choose a patch or full-file fallback for each baseline.
| Android / iOS | React Native Web | |
|---|---|---|
| Input | Absolute file paths | ArrayBuffer, typed arrays, DataView, or Blob |
| Basic API | diff() / patch() |
diffBytes() / patchBytes() |
| Controlled API | startDiff() / startPatch() |
Binary startDiff() / startPatch() jobs |
| Verification | Paths via inspectPatch() / verifyPatch() |
Binary values via the same APIs |
| Engine | Native C via JNI / ObjC++ | Same C core via WASM Worker |
The same npm package includes a Node.js CLI for release pipelines:
npx react-native-bs-diff-patch diff old.bin new.bin -o update.patch
npx react-native-bs-diff-patch verify old.bin update.patch new.bin
npx react-native-bs-diff-patch bundle \
--from releases/ \
--to dist/app.bin \
--out dist/update-bundlebundle evaluates every baseline, retains efficient patches, adds a full-file
fallback, and writes a canonical verified manifest suitable for CDN selection
and detached signing. The CLI mounts host paths through NODEFS, while verified
patch application uses the bounded streaming core. Try the workflow in the browser with the
Release Planner.
For React Native, Node.js, and CLI consumers:
npm install react-native-bs-diff-patch@^0.5.0For standalone browser and desktop WebView consumers:
npm install bs-diff-patch-web@^0.5.0For pre-release verification of either locally prepared package, substitute its
tarball in a clean consumer. The standalone Web package uses
bs-diff-patch-web-0.5.0.tgz; the React Native package keeps its own tarball.
See the Web and desktop WebView SDK guide for the resource
graph, migration, and consumer checks.
For iOS, install Pods and rebuild the native application:
npx pod-installReact Native autolinking handles native registration. Adding a native module requires a native rebuild; a Metro reload is not enough.
Native APIs use absolute paths. Pick unique output paths in a writable cache or documents directory through the filesystem library already used by your app.
import { diff, patch } from 'react-native-bs-diff-patch';
const patchPath = `${cacheDirectory}/content-v2.patch`;
const restoredPath = `${cacheDirectory}/content-v2.restored`;
await diff(oldFilePath, newFilePath, patchPath);
await patch(oldFilePath, restoredPath, patchPath);Input files must already exist. Output paths must not exist, and all paths in a
single call must be different. Both functions resolve to 0 on success.
Use the job API for work that needs lifecycle control:
import { startPatch } from 'react-native-bs-diff-patch';
const job = startPatch(oldPath, outputPath, patchPath, {
maxInputBytes: 64 * 1024 * 1024,
maxOutputBytes: 128 * 1024 * 1024,
});
const unsubscribe = job.onProgress(({ phase, progress }) => {
renderProgress(phase, progress);
});
try {
await job.result;
// await job.cancel(); // cancel from your UI when needed
} finally {
unsubscribe();
}Standalone browser, Vite, and Tauri consumers should install
bs-diff-patch-web and import its ESM root; it exposes byte APIs without
React Native, Node.js, or a Node sidecar. Existing applications can keep using
react-native-bs-diff-patch/web, which remains supported. See the Web and
desktop WebView SDK guide for the resource graph, migration,
and CSP.
import { diffBytes, patchBytes } from 'bs-diff-patch-web';
const patchBytesValue = await diffBytes(oldFile, newFile, {
signal: abortController.signal,
maxInputBytes: 64 * 1024 * 1024,
onProgress: ({ phase, progress }) => {
renderProgress(phase, progress);
},
});
const restoredBytes = await patchBytes(oldFile, patchBytesValue, {
maxOutputBytes: 64 * 1024 * 1024,
});Web calls return a new Uint8Array and leave caller-owned buffers usable.
Aborted operations reject with EABORTED; configured binary limits reject with
ERESOURCE. Blob and File inputs are mounted read-only in the Worker, so
they do not need a full main-thread copy before the C core reads them.
Use startDiff() / startPatch() with binary inputs on Web, or the explicit
startDiffBytes() / startPatchBytes() aliases, when UI code needs a job
object with result, cancel(), and real C-core progress events.
Use inspectPatch() for a cheap structural check, then verifyPatch() to apply
into a temporary result and compare it with the expected target byte-for-byte:
import { inspectPatch, verifyPatch } from 'react-native-bs-diff-patch';
// Android / iOS use paths. Web uses File, Blob, ArrayBuffer, or typed arrays.
const metadata = await inspectPatch(patchPath);
const result = await verifyPatch(oldPath, patchPath, expectedPath, {
maxInputBytes: 64 * 1024 * 1024,
maxOutputBytes: 128 * 1024 * 1024,
});
if (!metadata.valid || !result.verified) {
throw new Error('Patch compatibility check failed');
}The native verification output is temporary and always cleaned up. The Web
form accepts oldFile, patchFile, and expectedFile in the same argument
order. Structural validity is diagnostic; authenticate trusted hashes in your
update manifest before replacing live data.
| API | Android | iOS | Web |
|---|---|---|---|
diff(oldPath, newPath, patchPath) |
Yes | Yes | No |
patch(oldPath, outputPath, patchPath) |
Yes | Yes | No |
startDiff(...) / startPatch(...) |
Paths | Paths | Binary |
startDiffBytes(...) / startPatchBytes(...) |
No | No | Yes |
diffBytes(oldData, newData, options?) |
No | No | Yes |
patchBytes(oldData, patchData, options?) |
No | No | Yes |
inspectPatch(path or binary, options?) |
Yes | Yes | Yes |
verifyPatch(old, patch, expected, options?) |
Yes | Yes | Yes |
| Legacy architecture, while supplied by RN | Yes | Yes | N/A |
| New Architecture / TurboModule | Yes | Yes | N/A |
Unavailable platform APIs reject with EUNSUPPORTED; the package never
silently switches to a different input model.
- Authenticate patches from remote or otherwise untrusted sources.
- Verify restored output before replacing application data.
- Use unique native output paths and remove outputs you no longer need.
- Set product-specific resource limits. Binary diffing can use several times the input size in peak memory.
- Runtime APIs accept
ENDSLEY/BSDIFF43. Convert existingBSDIFF40files offline withnpx react-native-bs-diff-patch convert legacy.patch -o compatible.patch, then verify them before publishing.
See Production recipes for integrity checks, downloads, cross-runtime exchange, error handling, and cleanup patterns.
CI covers Android and iOS API builds against React Native 0.73.11, 0.74.7, and 0.86.0, and runs the configured New Architecture assertions. These checks do not constitute Tauri WebView acceptance or downstream physical-device acceptance. Packed-consumer tests verify browser, ESM, CommonJS, Metro, and TypeScript resolution from the real npm package shape.
- Web and desktop WebView SDK — use the explicit ESM
bs-diff-patch-weband its/toolkitentry from Vite or Tauri without React Native or a Node sidecar. Existingreact-native-bs-diff-patch/weband/toolkitconsumers remain supported. - Getting started
- API reference
- Production recipes
- Verified Delta Pipeline
- Platform support
- Architecture and patch format
- Controllable native operations
- Large-file roadmap
- Troubleshooting
- Development and verification
See CONTRIBUTING.md for the local workflow and quality gates. Release history is in CHANGELOG.md; security reports follow SECURITY.md.
MIT