npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

schema-stream

v3.2.2

Published

<div align="center"> <h1>schema-stream</h1> </div> <br />

Downloads

53,582

Readme

schema-stream is a foundational streaming JSON parser that enables immediate data access through structured stubs. Built on Zod schema validation, it provides type-safe parsing and progressive data access for JSON streams.

Key Features

  • 🔄 Stream JSON data with immediate access to partial results
  • 🔑 Path completion tracking for complex objects
  • 📝 Default value support via schema or explicit defaults
  • 🌳 Deep nested object and array support
  • ⚡ Zero dependencies except Zod
  • 🔍 TypeScript types inferred from schema

Installation

# npm
npm install schema-stream zod

# pnpm
pnpm add schema-stream zod

# bun
bun add schema-stream zod

Basic Usage

import { SchemaStream } from 'schema-stream';
import { z } from 'zod';

// Define your schema
const schema = z.object({
  users: z.array(z.object({
    name: z.string(),
    age: z.number()
  })),
  metadata: z.object({
    total: z.number(),
    page: z.number()
  })
});

// Create parser with optional defaults
const parser = new SchemaStream(schema, {
  metadata: { total: 0, page: 1 }
});

// Track completion paths
parser.onKeyComplete(({ completedPaths }) => {
  console.log('Completed:', completedPaths);
});

// Parse streaming data
const stream = parser.parse();
response.body.pipeThrough(stream);

// Read results with full type inference
const reader = stream.readable.getReader();
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  
  const result = JSON.parse(decoder.decode(value));
  // result is fully typed as z.infer<typeof schema>
  console.log(result);
}

Validation and Error Handling

schema-stream focuses solely on streaming JSON parsing with type stubs - it intentionally does not perform full Zod schema validation during parsing. This design choice enables:

  1. Faster parsing without validation overhead
  2. Immediate access to partial data
  3. Flexibility for downstream validation

For full schema validation and error handling, consider using:

  • zod-stream: Adds validation, OpenAI integration, and structured error handling
  • instructor: Complete solution for validated LLM extraction

Example of schema-stream being used by zod-stream:

const streamParser = new SchemaStream(response_model.schema, {
  typeDefaults: {
    string: null,
    number: null,
    boolean: null
  },
  onKeyComplete: ({ activePath, completedPaths }) => {
    _activePath = activePath;
    _completedPaths = completedPaths;
  }
});

// Create parser with validation stream
const parser = streamParser.parse({
  handleUnescapedNewLines: true
});

// Add validation in transform stream
const validationStream = new TransformStream({
  transform: async (chunk, controller) => {
    try {
      const parsedChunk = JSON.parse(decoder.decode(chunk));
      const validation = await schema.safeParseAsync(parsedChunk);
      
      controller.enqueue(encoder.encode(JSON.stringify({
        ...parsedChunk,
        _meta: {
          _isValid: validation.success,
          _activePath,
          _completedPaths
        }
      })));
    } catch (e) {
      controller.error(e);
    }
  }
});

// Chain streams
stream
  .pipeThrough(parser)
  .pipeThrough(validationStream);

Real-World Examples

Progressive UI Updates

const schema = z.object({
  analysis: z.object({
    sentiment: z.string(),
    keywords: z.array(z.string()),
    summary: z.string()
  }),
  metadata: z.object({
    processedAt: z.string(),
    wordCount: z.number()
  })
});

const parser = new SchemaStream(schema, {
  // Show loading states initially
  defaultData: {
    analysis: {
      sentiment: "analyzing...",
      keywords: ["loading..."],
      summary: "generating summary..."
    }
  },
  onKeyComplete({ activePath, completedPaths }) {
    // Update UI loading states based on completion
    updateLoadingStates(activePath, completedPaths);
  }
});

Nested Data Processing

const schema = z.object({
  users: z.array(z.object({
    id: z.string(),
    profile: z.object({
      name: z.string(),
      email: z.string(),
      preferences: z.object({
        theme: z.string(),
        notifications: z.boolean()
      })
    }),
    activity: z.array(z.object({
      timestamp: z.string(),
      action: z.string()
    }))
  }))
});

const parser = new SchemaStream(schema);

// Track specific paths for business logic
parser.onKeyComplete(({ activePath, completedPaths }) => {
  const path = activePath.join('.');
  
  // Process user profiles as they complete
  if (path.match(/users\.\d+\.profile$/)) {
    processUserProfile(/* ... */);
  }
  
  // Process activity logs in batches
  if (path.match(/users\.\d+\.activity\.\d+$/)) {
    batchActivityLog(/* ... */);
  }
});

API Reference

SchemaStream

class SchemaStream<T extends ZodObject<any>> {
  constructor(
    schema: T,
    options?: {
      defaultData?: NestedObject;
      typeDefaults?: {
        string?: string | null | undefined;
        number?: number | null | undefined;
        boolean?: boolean | null | undefined;
      };
      onKeyComplete?: (info: {
        activePath: (string | number | undefined)[];
        completedPaths: (string | number | undefined)[][];
      }) => void;
    }
  )

  // Create a stub instance of the schema with defaults
  getSchemaStub<T extends ZodRawShape>(
    schema: SchemaType<T>, 
    defaultData?: NestedObject
  ): z.infer<typeof schema>;

  // Parse streaming JSON data
  parse(options?: {
    stringBufferSize?: number;
    handleUnescapedNewLines?: boolean;
  }): TransformStream;
}

Constructor Options

  • schema: Zod schema defining the structure of your data
  • options:
    • defaultData: Initial values for schema properties
    • typeDefaults: Default values for primitive types
    • onKeyComplete: Callback for tracking parsing progress

Working with Defaults

There are two ways to provide default values in schema-stream:

1. Via Zod Schema

const schema = z.object({
  // Default via schema
  count: z.number().default(0),
  status: z.string().default('pending'),
  settings: z.object({
    enabled: z.boolean().default(true)
  }),
  tags: z.array(z.string()).default(['default'])
});

const parser = new SchemaStream(schema);

2. Via Constructor Options

// Global type defaults
const parser = new SchemaStream(schema, {
  typeDefaults: {
    string: "",      // Default for all strings
    number: 0,       // Default for all numbers
    boolean: false   // Default for all booleans
  }
});

// Specific property defaults
const parser = new SchemaStream(schema, {
  defaultData: {
    count: 100,
    status: 'ready',
    settings: {
      enabled: true
    }
  }
});

Priority order:

  1. Explicit defaultData values
  2. Zod schema defaults
  3. Global typeDefaults
  4. null (if no other default is found)

Completion Tracking

Track the progress of parsing with path information:

const parser = new SchemaStream(schema, {
  onKeyComplete({ activePath, completedPaths }) {
    // activePath: Current path being processed
    // completedPaths: Array of all completed paths
    console.log('Currently parsing:', activePath);
    console.log('Completed paths:', completedPaths);
  }
});

Parse Options

  • stringBufferSize: Size of the buffer for string values (default: 0)
  • handleUnescapedNewLines: Handle unescaped newlines in JSON (default: true)

Schema Stub Utility

Create a typed stub of your schema with defaults:

const schema = z.object({
  users: z.array(z.object({
    name: z.string(),
    age: z.number()
  }))
});

const parser = new SchemaStream(schema);
const stub = parser.getSchemaStub(schema, {
  users: [{ name: "default", age: 0 }]
});
// stub is fully typed as z.infer<typeof schema>

Integration with Island AI

schema-stream is designed as a foundational package that other tools build upon:

  • zod-stream: Adds validation and OpenAI integration

    // Example of zod-stream using schema-stream
    const zodStream = new ZodStream();
    const extraction = await zodStream.create({
      completionPromise: stream,
      response_model: { 
        schema: yourSchema,
        name: "Extract" 
      }
    });
  • instructor: High-level extraction

    const client = Instructor({
      client: oai,
      mode: "TOOLS"
    });
      
    const result = await client.chat.completions.create({
      response_model: { schema: yourSchema }
      // ...
    });
  • stream-hooks: React hooks for JSON streams

  • llm-polyglot: Universal LLM client

  • evalz: LLM output evaluation

Contributing

We welcome contributions! Check out:

Credits: Internal JSON parser logic adapted from streamparser-json.

License

MIT © hack.dance