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
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 24.x
- uses: dtolnay/rust-toolchain@stable
- run: npm ci
- run: npm run build
- run: npm test
107 changes: 107 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
name: Publish to npm

on:
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main]

jobs:
check:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
should_publish: ${{ steps.v.outputs.should_publish }}
version: ${{ steps.v.outputs.version }}
tag: ${{ steps.v.outputs.tag }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 24.x
- name: Check version against npm registry
id: v
run: |
NAME=$(node -p "require('./package.json').name")
VER=$(node -p "require('./package.json').version")
echo "version=$VER" >> "$GITHUB_OUTPUT"
if [[ "$VER" == *-* ]]; then
echo "tag=beta" >> "$GITHUB_OUTPUT"
else
echo "tag=latest" >> "$GITHUB_OUTPUT"
fi
if npm view "${NAME}@${VER}" version 2>/dev/null; then
echo "should_publish=false" >> "$GITHUB_OUTPUT"
echo "Version $VER already published, skipping."
else
echo "should_publish=true" >> "$GITHUB_OUTPUT"
echo "New version detected: $VER"
fi

build:
needs: check
if: needs.check.outputs.should_publish == 'true'
strategy:
fail-fast: true
matrix:
include:
- { triple: linux-x64-gnu, os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
- { triple: linux-arm64-gnu, os: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu }
- { triple: darwin-x64, os: macos-13, target: x86_64-apple-darwin }
- { triple: darwin-arm64, os: macos-14, target: aarch64-apple-darwin }
- { triple: win32-x64-msvc, os: windows-latest, target: x86_64-pc-windows-msvc }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 24.x
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- run: npm ci
- name: Build native addon
run: npm run build
- name: Name artifact
shell: bash
run: mv index.node "index-${{ matrix.triple }}.node"
- uses: actions/upload-artifact@v4
with:
name: bin-${{ matrix.triple }}
path: index-${{ matrix.triple }}.node
if-no-files-found: error

publish:
needs: [check, build]
runs-on: ubuntu-latest
permissions:
contents: write
# id-token: write enables npm trusted publishing (OIDC). The registry
# exchanges this short-lived token for a scoped upload credential, so no
# long-lived NPM_TOKEN secret is needed. Provenance is generated too.
id-token: write
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 24.x
registry-url: https://registry.npmjs.org
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Collect binaries into package root
run: find artifacts -name 'index-*.node' -exec cp {} . \;
- name: Publish to npm
run: npm publish --provenance --access public --tag ${{ needs.check.outputs.tag }}
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="${{ needs.check.outputs.version }}"
PRERELEASE=""
if [[ "$VERSION" == *-* ]]; then
PRERELEASE="--prerelease"
fi
gh release create "v${VERSION}" --generate-notes $PRERELEASE
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,30 @@ $ npm run build

This command uses the [@neon-rs/cli](https://www.npmjs.com/package/@neon-rs/cli) utility to assemble the binary Node addon from the output of `cargo`.

## Using yn

`yn` ships prebuilt binaries for the common desktop platforms
(`linux-x64-gnu`, `linux-arm64-gnu`, `darwin-x64`, `darwin-arm64`,
`win32-x64-msvc`) bundled in a single package. A small loader picks the binary
matching the host at runtime, so consumers just import the package:

```js
import { applyUpdates } from '@y-crdt/yn'
// or, CommonJS:
const { applyUpdates } = require('@y-crdt/yn')
```

## Exploring yn

After building yn, you can explore its exports at the Node console:
After building yn, you can explore its exports at the Node console. When run from
the project directory the loader falls back to the locally built `index.node`:

```sh
$ npm i
$ npm run build
$ node
> require('.').hello('node')
'hello node'
> require('.').applyUpdates(true, [])
Uint8Array(2) [ 0, 0 ]
```

## Available Scripts
Expand Down Expand Up @@ -79,7 +93,9 @@ yn/
| `README.md` | This file. |
| `src/` | The directory tree containing the Rust source code for the project. |
| `lib.rs` | Entry point for the Rust source code. |
| `index.node` | The main module, a [Node addon](https://nodejs.org/api/addons.html) generated by the build and pointed to by `"main"` in `package.json`. |
| `index.node` | A locally built [Node addon](https://nodejs.org/api/addons.html) for the host platform. In the published package this is replaced by per-platform `index-<triple>.node` binaries. |
| `load.cjs` | Runtime loader (the `"main"` entry) that selects the correct prebuilt binary for the host platform. |
| `index.mjs` | ESM entry point that re-exports the bindings from `load.cjs`. |
| `package.json` | The npm [manifest file](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), which informs the `npm` command. |
| `target/` | Binary artifacts generated by the Rust build. |

Expand Down
8 changes: 8 additions & 0 deletions index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ESM entry point. Native `.node` addons must be loaded through `require`, so we
// bridge to the CommonJS loader and re-export its bindings.
import { createRequire } from 'node:module'

const binding = createRequire(import.meta.url)('./load.cjs')

export const applyUpdates = binding.applyUpdates
export default binding
40 changes: 40 additions & 0 deletions load.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict'

// Runtime loader for the @y-crdt/yn native addon.
//
// The published package is a single "fat" tarball that bundles one prebuilt
// `index-<triple>.node` binary per supported platform. This loader selects the
// binary matching the host platform/arch. When developing locally it falls back
// to a freshly built `index.node` (the output of `npm run build`).

const { platform, arch } = process

function targetTriple () {
if (platform === 'darwin' && arch === 'arm64') return 'darwin-arm64'
if (platform === 'darwin' && arch === 'x64') return 'darwin-x64'
if (platform === 'linux' && arch === 'x64') return 'linux-x64-gnu'
if (platform === 'linux' && arch === 'arm64') return 'linux-arm64-gnu'
if (platform === 'win32' && arch === 'x64') return 'win32-x64-msvc'
return null
}

function load () {
const triple = targetTriple()
if (triple) {
try {
return require(`./index-${triple}.node`)
} catch (_) {
/* fall through to local dev build */
}
}
try {
return require('./index.node')
} catch (_) {
/* fall through to error */
}
throw new Error(
`@y-crdt/yn: no prebuilt binary available for ${platform}-${arch}`
)
}

module.exports = load()
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,20 @@
"name": "@y-crdt/yn",
"version": "0.1.0",
"description": "nodejs bindings to yrs",
"main": "index.node",
"main": "./load.cjs",
"exports": {
".": {
"import": "./index.mjs",
"require": "./load.cjs"
},
"./package.json": "./package.json"
},
"files": [
"load.cjs",
"index.mjs",
"index-*.node",
"README.md"
],
"scripts": {
"test": "cargo test",
"cargo-build": "cargo build --message-format=json-render-diagnostics > cargo.log",
Expand All @@ -16,6 +29,10 @@
},
"author": "Bartosz Sypytkowski",
"license": "ISC",
"repository": {
"type": "git",
"url": "git+https://github.com/y-crdt/yn.git"
},
"keywords": [
"y-crdt",
"yjs",
Expand Down