WebAssembly bindings for the PJS (Priority JSON Streaming) protocol, enabling high-performance JSON parsing and streaming in web browsers and Node.js environments.
Note
This package is part of the PJS workspace. For Rust usage, see the main pjson-rs crate.
pjs-wasm provides a JavaScript-friendly interface to the core PJS domain logic. It's compiled to WebAssembly for optimal performance while maintaining compatibility with web standards.
- Zero-copy JSON parsing where possible
- Priority-based streaming support
- Schema validation capabilities
- Optimized for size - minimal WASM bundle using
opt-level = "s" - Full TypeScript support - TypeScript definitions generated automatically
# Browser / bundler builds
npm install @pjson/wasm
# Node.js
npm install @pjson/node# Build for web (bundler target)
wasm-pack build --target web
# Build for Node.js
wasm-pack build --target nodejs
# Build for bundler (webpack, rollup, etc.)
wasm-pack build --target bundlerimport { PjsParser, version } from '@pjson/wasm';
// Check version
console.log(`Using PJS WASM version: ${version()}`);
// Create parser instance
const parser = new PjsParser();
// Parse JSON
const jsonString = '{"name": "Alice", "age": 30, "city": "NYC"}';
try {
const result = parser.parse(jsonString);
console.log(result);
} catch (error) {
console.error('Parse error:', error);
}<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>PJS WASM Demo</title>
</head>
<body>
<script type="module">
import init, { PjsParser, version } from './pkg/pjs_wasm.js';
async function run() {
// Initialize WASM module
await init();
console.log(`PJS WASM ${version()}`);
const parser = new PjsParser();
const data = parser.parse('{"message": "Hello from WASM!"}');
console.log(data);
}
run();
</script>
</body>
</html>import { PjsParser, version } from '@pjson/wasm';
const parser: PjsParser = new PjsParser();
try {
const result = parser.parse('{"key": "value"}');
console.log(result);
} catch (error) {
console.error('Failed to parse:', error);
}Main parser class for JSON parsing.
const parser = new PjsParser();Parse a JSON string and return the parsed object.
Parameters:
jsonStr- The JSON string to parse
Returns:
- Parsed JSON value
Throws:
- Error if the JSON is invalid
Example:
const parser = new PjsParser();
const data = parser.parse('{"id": 1, "name": "test"}');Get the parser version.
Returns:
- Version string
Example:
const v = PjsParser.version();
console.log(v); // e.g. "0.7.0"Generate priority-based frames from JSON data.
Parameters:
jsonStr- The JSON string to convert to framesminPriority- Minimum priority threshold (1-255)
Returns:
- Array of frames ordered by priority (highest first)
Throws:
- Error if JSON is invalid or priority is out of range
Example:
const parser = new PjsParser();
const frames = parser.generateFrames('{"id": 1, "name": "Alice"}', 50);
// Returns: [skeleton, critical_patches, high_patches, complete]Create a parser with custom priority configuration. config is taken by reference, so the same PriorityConfigBuilder instance can also be applied to a PriorityStream via its own withConfig.
Parameters:
config- Priority configuration builder
Returns:
- New parser instance with custom configuration
Example:
const config = new PriorityConfigBuilder()
.addCriticalField('user_id');
const parser = PjsParser.withConfig(config);
const stream = PriorityStream.withConfig(config);Get the WASM module version.
Returns:
- Version string
Example:
import { version } from '@pjson/wasm';
console.log(version()); // e.g. "0.7.0"SecurityConfig bounds JSON input size, nesting depth, array element count, and object key count to prevent DoS via oversized or maliciously crafted payloads. All limits are enforced during parsing, not just checked against the raw input size.
withSecurityConfig and setSecurityConfig accept the config by reference, so one SecurityConfig instance can be shared across a PjsParser and a PriorityStream (the setMax* builder methods still consume and return a new instance, as usual — build the config fully before sharing it).
import { PjsParser, PriorityStream, SecurityConfig } from '@pjson/wasm';
const security = new SecurityConfig()
.setMaxJsonSize(5 * 1024 * 1024) // 5 MB
.setMaxDepth(32) // 32 levels
.setMaxArrayElements(1000) // per array, at every nesting level
.setMaxObjectKeys(1000); // per object, at every nesting level
const parser = PjsParser.withSecurityConfig(security);
const stream = new PriorityStream();
stream.setSecurityConfig(security);Default limits:
- Max JSON size: 10 MB
- Max nesting depth: 64 levels
- Max array elements per level: 10,000
- Max object keys per level: 10,000
Warning
Building from source requires nightly Rust for GAT features.
- Rust nightly toolchain:
rustup install nightly - wasm-pack:
cargo install wasm-pack
# Development build
wasm-pack build
# Release build (optimized for size)
wasm-pack build --release
# Build for specific target
wasm-pack build --target web --release
wasm-pack build --target nodejs --release
wasm-pack build --target bundler --release# Run Rust tests
cargo test
# Run WASM tests in browser
wasm-pack test --chrome
wasm-pack test --firefox
# Run WASM tests in Node.js
wasm-pack test --nodeThe WASM binary is optimized for size using:
opt-level = "s"- Optimize for small binary sizelto = true- Link-time optimizationwasm-opt = ["-Os"]- Additional post-processing optimization
Typical bundle sizes:
- Uncompressed: ~100-200 KB
- Gzip compressed: ~40-80 KB
- Brotli compressed: ~35-70 KB
Note
This WASM build uses serde_json instead of sonic-rs (used in native builds) because sonic-rs is not WASM-compatible. While slightly slower, serde_json provides excellent performance and full WASM support.
pjs-wasm is built on top of pjs-domain, a pure Rust domain layer with:
- Zero external I/O dependencies
- WASM-compatible error types
- Clean architecture principles
- Full test coverage
pjs-wasm (WebAssembly bindings)
↓
pjs-domain (Pure domain logic)
↓
Value objects, entities, services
Requires browsers with WebAssembly support:
- Chrome/Edge 57+
- Firefox 52+
- Safari 11+
- Node.js 8+
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
Contributions are welcome! Please read the Contributing Guide first.
Generate priority-ordered frames from JSON data:
import { PjsParser, PriorityConstants } from '@pjson/wasm';
const parser = new PjsParser();
// Generate frames ordered by priority
const frames = parser.generateFrames(
JSON.stringify({
id: 1,
name: "Alice",
bio: "Software developer",
analytics: { views: 1000 }
}),
PriorityConstants.MEDIUM // Minimum priority threshold
);
// Frames are ordered: skeleton → critical → high → medium → completeimport { PriorityConstants } from '@pjson/wasm';
PriorityConstants.CRITICAL // 100 - Essential data (IDs, status)
PriorityConstants.HIGH // 80 - Important visible data (names, titles)
PriorityConstants.MEDIUM // 50 - Regular content
PriorityConstants.LOW // 25 - Supplementary data
PriorityConstants.BACKGROUND // 10 - Analytics, logsCustomize which fields receive which priorities:
import { PjsParser, PriorityConfigBuilder } from '@pjson/wasm';
const config = new PriorityConfigBuilder()
.addCriticalField('user_id')
.addCriticalField('session_id')
.addHighField('display_name')
.addHighField('email')
.addLowPattern('debug') // Fields containing "debug" → low priority
.addBackgroundPattern('trace') // Fields containing "trace" → background priority
.setLargeArrayThreshold(200) // Arrays >200 elements → background priority
.setLargeStringThreshold(5000); // Strings >5000 chars → low priority
const parser = PjsParser.withConfig(config);
const frames = parser.generateFrames(
JSON.stringify({ user_id: 123, display_name: "Alice" }),
PriorityConstants.LOW
);const parser = new PjsParser();
const frames = parser.generateFrames(jsonData, PriorityConstants.MEDIUM);
for (const frame of frames) {
switch (frame.frame_type) {
case 'Skeleton':
renderLoadingSkeleton(frame.payload);
break;
case 'Patch':
applyDataPatches(frame.payload.patches);
break;
case 'Complete':
finalizeRendering();
break;
}
}- WebSocket streaming transport in WASM bindings
- Compression support in WASM bindings
- Worker thread support for heavy parsing