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
134 changes: 106 additions & 28 deletions .github/workflows/release-wasm.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,47 @@
name: Release — WASM SDK (npm)
name: Release — WASM SDK (npm + GitHub Packages)

on:
push:
tags: ['v[0-9]+.[0-9]+.[0-9]+']
workflow_dispatch:
inputs:
tag_name:
description: 'Tag name to publish (e.g. v0.2.2) — used for version sync'
description: 'Tag name to publish (e.g. v0.2.4) — used for version sync'
required: true
default: 'v0.2.2'
default: 'v0.2.4'

permissions:
contents: write
contents: write # upload GitHub Release assets
packages: write # publish to GitHub Packages npm registry

jobs:
publish-wasm:
name: Publish WASM package
name: Build & publish WASM package
runs-on: ubuntu-latest
environment: npm

steps:
- uses: actions/checkout@v4

# Node.js is needed for npm pack / publish and wasm-pack post-processing.
# Registry URL is overridden per-publish step via .npmrc; the value here
# merely ensures `NODE_AUTH_TOKEN` is wired into the environment.
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'

- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown

- uses: Swatinem/rust-cache@v2

- uses: taiki-e/install-action@wasm-pack

# ─────────────────────────────────────────────────────────────────────
# 1. Verify that the Git tag matches the Cargo workspace version.
# ─────────────────────────────────────────────────────────────────────
- name: Verify version consistency
env:
INPUT_TAG_NAME: ${{ inputs.tag_name }}
Expand All @@ -42,79 +54,145 @@ jobs:
echo "ERROR: tag $TAG_VERSION ≠ Cargo.toml $CARGO_VERSION"
exit 1
fi
echo "VERSION=$TAG_VERSION" >> "$GITHUB_ENV"
echo "TAG_NAME=$TAG_NAME" >> "$GITHUB_ENV"

# ─────────────────────────────────────────────────────────────────────
# 2. Compile the Rust crate to WebAssembly and generate JS/TS glue.
# ─────────────────────────────────────────────────────────────────────
- name: Build WASM package
run: |
cd crates/edgeparse-wasm
wasm-pack build --target web --release

- name: Sync npm metadata
env:
INPUT_TAG_NAME: ${{ inputs.tag_name }}
# ─────────────────────────────────────────────────────────────────────
# 3. Patch pkg/package.json with release metadata (shared across both
# registries; the name/scope is adjusted per publish step below).
# ─────────────────────────────────────────────────────────────────────
- name: Sync package metadata
run: |
node -e "
const fs = require('fs');
const refName = process.env.INPUT_TAG_NAME || process.env.GITHUB_REF_NAME;
const version = refName.replace(/^v/, '');
const path = 'crates/edgeparse-wasm/pkg/package.json';
const pkg = JSON.parse(fs.readFileSync(path, 'utf8'));
pkg.name = 'edgeparse-wasm';
pkg.version = version;
const version = process.env.VERSION;
const pkgPath = 'crates/edgeparse-wasm/pkg/package.json';
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
pkg.version = version;
pkg.description = 'EdgeParse PDF parser — WebAssembly build for browsers';
pkg.homepage = 'https://edgeparse.io/docs/api/wasm/';
pkg.repository = {
pkg.homepage = 'https://edgeparse.io/docs/api/wasm/';
pkg.keywords = ['pdf', 'parser', 'wasm', 'webassembly', 'browser', 'extraction', 'markdown'];
pkg.repository = {
type: 'git',
url: 'git+https://github.com/raphaelmansuy/edgeparse.git',
directory: 'crates/edgeparse-wasm'
};
pkg.publishConfig = { access: 'public' };
pkg.exports = {
'.': { import: './edgeparse_wasm.js', types: './edgeparse_wasm.d.ts' }
};
pkg.files = [
'edgeparse_wasm_bg.wasm',
'edgeparse_wasm.js',
'edgeparse_wasm.d.ts',
'edgeparse_wasm_bg.wasm.d.ts',
'README.md',
'LICENSE'
];
fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n');
console.log('Version synced to: ' + version);
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
console.log('Metadata synced to version: ' + version);
"

- name: Stage package docs
- name: Stage README and LICENSE
run: |
cp README.md crates/edgeparse-wasm/pkg/README.md
cp LICENSE crates/edgeparse-wasm/pkg/LICENSE
cp LICENSE crates/edgeparse-wasm/pkg/LICENSE

# ─────────────────────────────────────────────────────────────────────
# 4. Pack the tarball once (reused by both registries and the Release).
# ─────────────────────────────────────────────────────────────────────
- name: Pack npm tarball
run: |
node -e "
const fs = require('fs');
const pkgPath = 'crates/edgeparse-wasm/pkg/package.json';
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
pkg.name = 'edgeparse-wasm';
pkg.publishConfig = { access: 'public', registry: 'https://registry.npmjs.org' };
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
"
cd crates/edgeparse-wasm/pkg
npm pack

- uses: actions/upload-artifact@v4
with:
name: wasm-package
path: crates/edgeparse-wasm/pkg/*.tgz
retention-days: 30

# ─────────────────────────────────────────────────────────────────────
# 5a. Publish to npm (primary registry — enables jsDelivr & unpkg CDNs).
# Uses NPM_TOKEN Classic Automation token stored as a repository
# secret. "already-published" is treated as idempotent.
# ─────────────────────────────────────────────────────────────────────
- name: Publish to npm registry
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
node -e "
const fs = require('fs');
const pkgPath = 'crates/edgeparse-wasm/pkg/package.json';
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
pkg.name = 'edgeparse-wasm';
pkg.publishConfig = { access: 'public', registry: 'https://registry.npmjs.org' };
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
"
cd crates/edgeparse-wasm/pkg
npm publish --access public --registry https://registry.npmjs.org \
|| { CODE=$?; [ "$CODE" -eq 1 ] && npm info edgeparse-wasm@${{ env.VERSION }} >/dev/null 2>&1 && echo "Already published — skipping." || exit $CODE; }

- name: Skip WASM npm publication
# ─────────────────────────────────────────────────────────────────────
# 5b. Publish to GitHub Packages (secondary registry — useful for
# enterprise / GitHub-native consumers).
# The package is scoped as @raphaelmansuy/edgeparse-wasm.
# Uses the built-in GITHUB_TOKEN — no extra secret required.
# ─────────────────────────────────────────────────────────────────────
- name: Publish to GitHub Packages
env:
NODE_AUTH_TOKEN: ${{ github.token }}
run: |
echo "::warning::WASM npm publication is disabled. The package tarball will still be uploaded to the GitHub Release."
node -e "
const fs = require('fs');
const pkgPath = 'crates/edgeparse-wasm/pkg/package.json';
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
pkg.name = '@raphaelmansuy/edgeparse-wasm';
pkg.publishConfig = {
access: 'public',
registry: 'https://npm.pkg.github.com'
};
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
"
# Write a scoped .npmrc so npm uses the right registry for this scope.
echo "@raphaelmansuy:registry=https://npm.pkg.github.com" >> ~/.npmrc
echo "//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc
cd crates/edgeparse-wasm/pkg
npm publish --registry https://npm.pkg.github.com \
|| { CODE=$?; echo "GitHub Packages publish exited $CODE — may already exist for this version." ; }

# ─────────────────────────────────────────────────────────────────────
# 6. Attach the tarball to the GitHub Release.
# ─────────────────────────────────────────────────────────────────────
- name: Ensure GitHub Release exists
env:
GH_TOKEN: ${{ github.token }}
INPUT_TAG_NAME: ${{ inputs.tag_name }}
run: |
TAG_NAME="${INPUT_TAG_NAME:-$GITHUB_REF_NAME}"
gh release view "$TAG_NAME" --repo "${{ github.repository }}" \
|| gh release create "$TAG_NAME" \
--repo "${{ github.repository }}" \
--title "$TAG_NAME" \
--generate-notes

- name: Upload npm tarball to GitHub Release
- name: Upload tarball to GitHub Release
env:
GH_TOKEN: ${{ github.token }}
INPUT_TAG_NAME: ${{ inputs.tag_name }}
run: |
TAG_NAME="${INPUT_TAG_NAME:-$GITHUB_REF_NAME}"
gh release upload "$TAG_NAME" crates/edgeparse-wasm/pkg/*.tgz \
--repo "${{ github.repository }}" --clobber

21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ this project adheres to [Semantic Versioning](https://semver.org/).

---

## [0.2.4] — 2026-04-13

### Added
- **WASM npm publication** — `edgeparse-wasm` is now published to the npm public registry on every tagged release; jsDelivr and unpkg CDNs become available automatically
- **GitHub Packages secondary registry** — `@raphaelmansuy/edgeparse-wasm` is published to `npm.pkg.github.com` alongside the npm release, providing a GitHub-native install path for enterprise users
- **CDN quick-start** — `docs/09-wasm-sdk.md` now includes copy-pasteable `<script type="module">` examples for jsDelivr and unpkg (no build tool required)
- **Framework quick-starts** — Vite + React, Next.js App Router, Webpack 5, and Service Worker PWA examples added to the WASM SDK docs
- **`exports` field in `pkg/package.json`** — adds a proper ESM exports map for bundler interop

### Changed
- `release-wasm.yml` now publishes to npm (primary) and GitHub Packages (secondary) instead of skipping publication; both steps treat "already published" as non-fatal
- `release-wasm.yml` requires `packages: write` permission (for GitHub Packages) and existing `contents: write` (for GitHub Releases)
- `pkg/package.json` carries full metadata (keywords, exports, publishConfig) so it is ready to publish without CI patching the file from scratch
- `docs/09-wasm-sdk.md` consolidated installation and distribution section with all four channels (npm, jsDelivr, unpkg, GitHub Packages)
- `docs/07-cicd-publishing.md` updated with WASM npm rows in the artifacts table, `NPM_TOKEN` scope note, and step-by-step token setup instructions

### Fixed
- `INPUT_TAG_NAME` env variable now written to `$GITHUB_ENV` so downstream steps in `release-wasm.yml` can reference `${{ env.TAG_NAME }}` without re-reading inputs

---

## [0.2.3] — 2026-03-28

### Added
Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ default-members = [
]

[workspace.package]
version = "0.2.3"
version = "0.2.4"
edition = "2021"
rust-version = "1.85"
license = "Apache-2.0"
Expand Down
35 changes: 30 additions & 5 deletions crates/edgeparse-wasm/pkg/package.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,46 @@
{
"name": "@edgeparse/edgeparse-wasm",
"name": "edgeparse-wasm",
"type": "module",
"description": "EdgeParse PDF parser — WebAssembly build for browsers",
"version": "0.2.3",
"version": "0.2.4",
"license": "Apache-2.0",
"homepage": "https://edgeparse.io/docs/api/wasm/",
"repository": {
"type": "git",
"url": "https://github.com/raphaelmansuy/edgeparse"
"url": "git+https://github.com/raphaelmansuy/edgeparse.git",
"directory": "crates/edgeparse-wasm"
},
"keywords": [
"pdf",
"parser",
"wasm",
"webassembly",
"browser",
"extraction",
"markdown"
],
"files": [
"edgeparse_wasm_bg.wasm",
"edgeparse_wasm.js",
"edgeparse_wasm.d.ts"
"edgeparse_wasm.d.ts",
"edgeparse_wasm_bg.wasm.d.ts",
"README.md",
"LICENSE"
],
"main": "edgeparse_wasm.js",
"module": "edgeparse_wasm.js",
"types": "edgeparse_wasm.d.ts",
"exports": {
".": {
"import": "./edgeparse_wasm.js",
"types": "./edgeparse_wasm.d.ts"
}
},
"sideEffects": [
"./snippets/*"
]
],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
}
}
Loading
Loading