Visitar URL original
pjs/crates/pjs-wasm at main · bug-ops/pjs · GitHub
Skip to content

Latest commit

 

History

History

pjs-wasm

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

README.md

PJS WASM - WebAssembly Bindings for Priority JSON Streaming

npm (browser) npm (node) Bundle Size License

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.

Overview

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.

Features

  • 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

Installation

Using npm

# Browser / bundler builds
npm install @pjson/wasm

# Node.js
npm install @pjson/node

Using wasm-pack (for development)

# 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 bundler

Usage

Basic Parsing

import { 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);
}

In Browser (ES Modules)

<!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>

With TypeScript

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);
}

API Reference

PjsParser

Main parser class for JSON parsing.

Constructor

const parser = new PjsParser();

Methods

parse(jsonStr: string): any

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"}');
version(): string (static)

Get the parser version.

Returns:

  • Version string

Example:

const v = PjsParser.version();
console.log(v); // e.g. "0.7.0"
generateFrames(jsonStr: string, minPriority: number): Frame[]

Generate priority-based frames from JSON data.

Parameters:

  • jsonStr - The JSON string to convert to frames
  • minPriority - 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]
withConfig(config: PriorityConfigBuilder): PjsParser (static)

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);

version(): string

Get the WASM module version.

Returns:

  • Version string

Example:

import { version } from '@pjson/wasm';
console.log(version()); // e.g. "0.7.0"

Security Limits

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

Building from Source

Prerequisites

Warning

Building from source requires nightly Rust for GAT features.

  • Rust nightly toolchain: rustup install nightly
  • wasm-pack: cargo install wasm-pack

Build Commands

# 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

Testing

# 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 --node

Performance Considerations

Bundle Size

The WASM binary is optimized for size using:

  • opt-level = "s" - Optimize for small binary size
  • lto = true - Link-time optimization
  • wasm-opt = ["-Os"] - Additional post-processing optimization

Typical bundle sizes:

  • Uncompressed: ~100-200 KB
  • Gzip compressed: ~40-80 KB
  • Brotli compressed: ~35-70 KB

Parser Selection

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.

Architecture

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

Browser Compatibility

Requires browsers with WebAssembly support:

  • Chrome/Edge 57+
  • Firefox 52+
  • Safari 11+
  • Node.js 8+

License

Licensed under either of:

at your option.

Contributing

Contributions are welcome! Please read the Contributing Guide first.

Links

Advanced Features

Priority-Based Frame Generation

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 → complete

Priority Constants

import { 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, logs

Custom Priority Configuration

Customize 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
);

Progressive Rendering Example

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;
    }
}

Roadmap

  • WebSocket streaming transport in WASM bindings
  • Compression support in WASM bindings
  • Worker thread support for heavy parsing