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

parse-sng

v4.0.1

Published

A library that parses the .sng file format.

Downloads

13

Readme

.sng is a simple and generic binary container format that groups a list of files and metadata into a single file. The file format specification can be found here: https://github.com/mdsitton/SngFileFormat

Works in a browser context and Node.js v17.0.0 and later.

API

/**
 * A class that reads and parses a .sng `Uint8Array` stream and emits
 * events when the different components of the stream have been parsed.
 */
class SngStream {

    constructor(
        /**
         * A `ReadableStream` for the binary contents of the .sng file.
         */
        sngStream: ReadableStream<Uint8Array>,
        config?: SngStreamConfig,
    ) { }

    /**
     * Starts processing the provided .sng stream. Event listeners should be attached before calling this.
     */
    start(): void

    /**
     * Registers `listener` to be called once when the .sng header has been parsed.
     * The `SngHeader` object is passed to `listener`.
     *
     * This event is emitted before any `file` events are emitted.
     */
    on(event: 'header', listener: (header: SngHeader) => void): void

    /**
     * Registers `listener` to be called when each file in .sng has started to parse.
     * The `fileName` is passed to `listener`, along with a `ReadableStream`
     * for the (unmasked) binary contents of the file.
     *
     * If `nextFile` is `null`, there are no more files to read.
     * Otherwise, `nextFile` must be called to emit the next file event.
     *
     * Cancelling `fileStream` will cancel the source stream.
     */
    on(event: 'file', listener: (fileName: string, fileStream: ReadableStream<Uint8Array>, nextFile: (() => void) | null) => void): void

    /**
     * Registers `listener` to be called once if an error occurs during the stream.
     *
     * The source stream is canceled and the error is passed to the listener.
     * It will usually by type `Error`. ((error instanceof Error) === true)
     *
     * This can either happen when `sngStream` emits an `error` event, or
     * if the .sng's header failed to parse.
     */
    on(event: 'error', listener: (error: unknown) => void): void
}

interface SngStreamConfig {
  /**
   * The .sng format doesn't list a `song.ini` file in the `fileMeta`; that information is stored in `metadata`.
   *
   * Set this to true for `SngStream` to generate and emit a `song.ini` file in the `file` or `files` events.
   *
   * Default: `false`.
   */
  generateSongIni: boolean
}

interface SngHeader {
    fileIdentifier: string
    version: number
    xorMask: Uint8Array
    metadata: {
        [key: string]: string
    }
    fileMeta: {
        filename: string
        contentsLen: bigint
        contentsIndex: bigint
    }[]
}

Parse .sng Stream Node.js Example

import { createReadStream, createWriteStream } from 'fs'
import { SngStream } from 'parse-sng'
import { Readable } from 'stream'
import { mkdir } from 'fs/promises'
import { join, parse } from 'path'

const sngStream = new SngStream(
  Readable.toWeb(createReadStream('C:/dev/test.sng')) as any,
  { generateSongIni: true },
)

sngStream.on('header', header => {
    console.log('Header:', header)
})

sngStream.on('file', async (fileName, fileStream, nextFile) => {
  console.log(`Starting to read file ${fileName}`)
  const reader = fileStream.getReader()

  await mkdir(join('C:/dev/output', parse(fileName).dir), { recursive: true })
  const writeStream = createWriteStream(join('C:/dev/output', fileName))
  while(true) {
    const { done, value } = await reader.read()
    if (done) { break }
    writeStream.write(value)
  }
  writeStream.close()

  console.log(`Finished reading file ${fileName}`)

  if (nextFile) {
    nextFile()
  } else {
    console.log('test.sng has been fully parsed')
  }
})

sngStream.on('error', error => {
  if (error instanceof Error) {
    console.log('Error: ', error.name, error.message)
  } else {
    console.log(error)
  }
})

sngStream.start()