Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,15 @@ jobs:
quality=true
site=true
;;
scripts/prepare-package.mjs|scripts/test-package-consumers.mjs)
scripts/prepare-package.mjs|scripts/check-package-contract.mjs|scripts/test-package-consumers.mjs|scripts/test-sdk-consumers.mjs|scripts/sdk-consumer/**|examples/web-sdk/**)
quality=true
web=true
;;
bin/**|node/**|toolkit/**|action/**|action.yml|scripts/test-node-cli.mjs|scripts/test-toolkit.mjs|scripts/test-action.mjs)
quality=true
site=true
web=true
;;
benchmarks/**|scripts/benchmark-web.mjs)
quality=true
site=true
Expand Down Expand Up @@ -413,7 +418,13 @@ jobs:
- name: Checkout the code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- name: Set up Node.js
- name: Set up repository Node.js and dependencies
uses: ./.github/actions/setup

- name: Build package for prepack contract
run: yarn prepare

- name: Set up React Native fixture Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: ${{ matrix.node }}
Expand Down Expand Up @@ -451,7 +462,11 @@ jobs:
yarn test:web
yarn test:web:browser
yarn test:web:metro
yarn test:toolkit
yarn test:node
yarn test:action
yarn test:package
yarn test:sdk

- name: Verify npm package contents
run: npm pack --dry-run --ignore-scripts
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
publish-npm:
name: Publish to npm with OIDC
runs-on: ubuntu-latest
timeout-minutes: 20
timeout-minutes: 30
steps:
- name: Checkout the release tag
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
Expand Down Expand Up @@ -86,7 +86,11 @@ jobs:
yarn test:web
yarn test:web:browser
yarn test:web:metro
yarn test:toolkit
yarn test:node
yarn test:action
yarn test:package
yarn test:sdk
npm pack --dry-run --ignore-scripts
env:
CHROME_PATH: /usr/bin/google-chrome
Expand All @@ -109,3 +113,9 @@ jobs:
done
echo 'Published package did not expose provenance metadata in time.' >&2
exit 1

- name: Smoke test the published SDK
env:
PACKAGE_SPEC: react-native-bs-diff-patch@${{ steps.release.outputs.package_version }}
CHROME_PATH: /usr/bin/google-chrome
run: yarn test:sdk
1 change: 1 addition & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
- 'docs/**'
- 'site/**'
- 'web/**'
- 'toolkit/**'
- 'scripts/build-site.mjs'
- 'scripts/test-site.mjs'
- 'scripts/test-site-browser.mjs'
Expand Down
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,51 @@ All notable changes to this project are documented in this file. Releases use
[Semantic Versioning](https://semver.org/) and are generated from Conventional
Commits by release-it.

## [0.5.0](https://github.com/JimmyDaddy/react-native-bs-diff-patch/compare/v0.4.0...v0.5.0) (2026-08-31)

### Added

- add the explicit ESM `react-native-bs-diff-patch/web` entry for browser and
desktop WebView byte operations, including typed declarations, Worker jobs,
progress, cancellation, input/output limits, patch inspection, and
byte-for-byte verification;
- publish a Node-free browser/Worker WebAssembly artifact alongside the
Node-compatible artifact, while keeping the existing root React Native,
Node, and CLI loading paths;
- add the ESM `react-native-bs-diff-patch/toolkit` entry for normalized
manifests, multi-baseline bundles, canonical payloads, candidate selection,
error classification, and header-only inspection;
- add tarball consumer checks for `/web` and `/toolkit`, production Vite
resource loading, offline browser execution, and TypeScript resolution;
- add the Node release helpers, CLI, GitHub Action, and BSDIFF40 converter
needed to prepare verified release artifacts without changing the runtime's
`ENDSLEY/BSDIFF43` compatibility contract;
- add bilingual WebView integration, packaging, lifecycle, CSP, resource
budget, trust-boundary, and release documentation.

### Compatibility and release boundaries

- keep the existing root CommonJS build, React Native conditional exports,
native path API, and Node-compatible `web/bsdiffpatch.mjs`;
- keep `/web` and `/toolkit` ESM-only, and map the browser Worker graph to
`web/bsdiffpatch.browser.mjs` without a CDN or Node runtime fallback;
- keep `BSDIFF40` as a header-inspection/conversion case only; runtime
generation and application remain `ENDSLEY/BSDIFF43`;
- leave file authorization, persistence, and final replacement to downstream
desktop applications. Tauri WebView acceptance remains a downstream test,
and registry smoke checks remain a post-release validation step.

### Security and compatibility scope

- enforce a zero-byte output budget during C compressed output writes, and add
control-flow guards for malformed or non-progressing patch streams;
- classify toolkit array cycles as `EINVALID_MANIFEST`, preserve a literal
`__proto__` object key during canonicalization, and reject invalid selection
budgets even when the requested baseline has no matching candidate;
- retain first-match candidate selection rather than silently choosing the
smallest patch, while keeping the root React Native/Node compatibility paths
and the `ENDSLEY/BSDIFF43` runtime format unchanged.

## [0.4.0](https://github.com/JimmyDaddy/react-native-bs-diff-patch/compare/v0.3.0...v0.4.0) (2026-07-23)

### Features
Expand Down
29 changes: 28 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,14 @@ Web implementation changes should also pass:
yarn test:web
yarn test:web:browser
yarn test:web:metro
yarn test:sdk
```

`test:sdk` installs the prepared package tarball into an isolated consumer and
checks the public `/web` and `/toolkit` ESM entries, the production Vite
resource graph, and real byte round trips. It does not use a workspace link or
the registry's older package.

Documentation and site changes should pass:

```sh
Expand Down Expand Up @@ -119,12 +125,29 @@ Maintainers should run the quality gates, then create the release:

```sh
yarn prepare
yarn test:sdk
yarn typecheck
yarn lint
yarn test --runInBand
yarn release
yarn pack --dry-run
yarn release --no-increment
```

`yarn prepare` runs the React Native Builder Bob output step, package
preparation, and `scripts/check-package-contract.mjs`. The same contract check
runs from the `prepack` lifecycle before a tarball is created. Run
`node scripts/check-package-contract.mjs` directly when inspecting a prepared
tree without rebuilding it. `yarn build:web` produces separate Node and
browser/Worker WASM modules: `web/bsdiffpatch.mjs` keeps NODEFS for Node and
the CLI, while `web/bsdiffpatch.browser.mjs` is the Node-free browser build.

When `package.json` already contains the prepared version, use
`yarn release --no-increment` so release-it does not bump it again. This command
creates the release commit, tag, and GitHub Release; the GitHub Release then
triggers the npm workflow. Run it only with explicit maintainer authorization.
A local 0.5.0 tarball is not a registry release; do not describe it as
published until the GitHub Release and npm provenance checks have completed.

The npm package already trusts the `JimmyDaddy/react-native-bs-diff-patch`
repository and the `npm-publish.yml` workflow. No npm-side configuration is
required for a release. The release tag must exactly match
Expand All @@ -145,6 +168,10 @@ The `package.json` file contains various scripts for common tasks:
- `yarn test:web`: verify the WebAssembly patch format and round trip.
- `yarn test:web:browser`: exercise the public Web Worker API in Chrome.
- `yarn test:web:metro`: verify Metro resolves the React Native Web entry.
- `yarn test:sdk`: install the prepared tarball and verify `/web` and `/toolkit`
from an isolated Vite consumer.
- `node scripts/check-package-contract.mjs`: verify exports, declarations,
packed assets, and the Node-free browser resource graph.
- `yarn site:build`: render public Markdown and static site assets into `site-dist/`.
- `yarn site:test`: validate site structure and local links.
- `yarn site:test:browser`: verify the live Playground, docs, and mobile viewport.
Expand Down
108 changes: 80 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</p>

<p align="center">
<strong>Turn two versions of a file into a compact binary patch, then reconstruct the new file from the old file plus that patch.</strong><br />
One compatible format across React Native Android, iOS, and Web.
<strong>A verified binary delta pipeline for React Native, Web, Node.js, and release CI.</strong><br />
Create compact patches, prove restored bytes, and plan multi-baseline delivery with one compatible format.
</p>

<p align="center">
Expand All @@ -22,6 +22,7 @@
<a href="https://bs-dff-patch.corerobin.com/docs/">Documentation</a> ·
<a href="https://bs-dff-patch.corerobin.com/#playground">Live Playground</a> ·
<a href="https://bs-dff-patch.corerobin.com/tools/">Binary Patch Toolkit</a> ·
<a href="https://bs-dff-patch.corerobin.com/planner/">Release Planner</a> ·
<a href="./README.zh-CN.md">中文说明</a> ·
<a href="https://www.npmjs.com/package/react-native-bs-diff-patch">npm</a>
</p>
Expand Down Expand Up @@ -53,23 +54,55 @@ replaces live data.
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.

## Platform overview

| | 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()` | `AbortSignal` and binary limits |
| 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 |

## Release-side CLI and bundles

The same npm package includes a Node.js CLI for release pipelines:

```sh
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-bundle
```

`bundle` 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](https://bs-dff-patch.corerobin.com/planner/).

## Install

```sh
npm install react-native-bs-diff-patch
npm install react-native-bs-diff-patch@^0.5.0
```

The explicit `/web` and `/toolkit` entries are part of 0.5.0. For pre-release
verification of a locally prepared package, the same entries can be tested from
its tarball instead:

```sh
npm install ./react-native-bs-diff-patch-0.5.0.tgz
```

The registry's 0.4.x package predates these subpaths. See the [Web and desktop
WebView SDK guide](./docs/web-sdk.md) for the resource graph and consumer checks.

For iOS, install Pods and rebuild the native application:

```sh
Expand Down Expand Up @@ -123,24 +156,35 @@ try {

## Web: first round trip

```ts
import { diffBytes, patchBytes } from 'react-native-bs-diff-patch';
Standalone browser, Vite, and Tauri consumers should import the explicit ESM
entry `react-native-bs-diff-patch/web`; it exposes byte APIs and does not
require React Native. The root package keeps its conditional React Native and
browser resolution for existing applications. See the [Web and desktop WebView
SDK guide](./docs/web-sdk.md) for the published resource graph and CSP.

const oldBytes = await oldFile.arrayBuffer();
const newBytes = await newFile.arrayBuffer();
```ts
import { diffBytes, patchBytes } from 'react-native-bs-diff-patch/web';

const patchBytesValue = await diffBytes(oldBytes, newBytes, {
const patchBytesValue = await diffBytes(oldFile, newFile, {
signal: abortController.signal,
maxInputBytes: 64 * 1024 * 1024,
onProgress: ({ phase, progress }) => {
renderProgress(phase, progress);
},
});
const restoredBytes = await patchBytes(oldBytes, patchBytesValue, {
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`.
`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.

## Inspect and verify a patch

Expand Down Expand Up @@ -169,17 +213,18 @@ update manifest before replacing live data.

## API matrix

| API | Android | iOS | Web |
| --------------------------------------------- | ------- | --- | --- |
| `diff(oldPath, newPath, patchPath)` | Yes | Yes | No |
| `patch(oldPath, outputPath, patchPath)` | Yes | Yes | No |
| `startDiff(...)` / `startPatch(...)` | Yes | Yes | No |
| `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 |
| 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.
Expand All @@ -191,27 +236,34 @@ silently switches to a different input model.
- 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.
- Generate and apply patches with this library. Generic `BSDIFF40` patches are
not interchangeable with `ENDSLEY/BSDIFF43` patches.
- Runtime APIs accept `ENDSLEY/BSDIFF43`. Convert existing `BSDIFF40` files
offline with `npx react-native-bs-diff-patch convert legacy.patch -o
compatible.patch`, then verify them before publishing.

See [Production recipes](./docs/recipes.md) for integrity checks, downloads,
cross-runtime exchange, error handling, and cleanup patterns.

## Verified compatibility

CI compiles the Android and iOS APIs against React Native 0.73.11, 0.74.7, and
0.86.0, and runs device-level New Architecture assertions on Android and iOS.
Packed-consumer tests verify browser, ESM, CommonJS, Metro, and TypeScript
resolution from the real npm package shape.
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.

## Documentation

- [Web and desktop WebView SDK](./docs/web-sdk.md) — use the explicit ESM
`/web` and `/toolkit` entries from Vite or Tauri without React Native or a
Node sidecar.
- [Getting started](./docs/getting-started.md)
- [API reference](./docs/api-reference.md)
- [Production recipes](./docs/recipes.md)
- [Verified Delta Pipeline](./docs/verified-delta-pipeline.md)
- [Platform support](./docs/platform-support.md)
- [Architecture and patch format](./docs/architecture.md)
- [Controllable native operations](./docs/native-operations-v03.md)
- [Large-file roadmap](./docs/large-files-roadmap.md)
- [Troubleshooting](./docs/troubleshooting.md)
- [Development and verification](./docs/development.md)

Expand Down
Loading