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 🙏

© 2026 – Pkg Stats / Ryan Hefner

jsmarc

v3.0.1

Published

A zero-dependency library for parsing, filtering, extracting, and explaining bibliographic MARC records

Readme

JsMarc

License: GPL v3 TypeScript Zero Dependencies

A zero-dependency TypeScript library for parsing, filtering, extracting, and explaining bibliographic MARC (MAchine Readable Cataloging) records. Works in browsers and Node.js with no bundler.

JsMarc was created by Clément Corbin to make MARC data accessible from the web and the terminal.


Features

  • Parse MARC records — Convert raw ISO 2709 MARC data into structured objects, handling leader, directory, fields, and subfields.
  • Filter records — Extract matching records from a batch by field/subfield values (e.g. filter by ISBN).
  • Extract data — Pull specific fields from large record sets, with JSON output.
  • Explain fields — Annotate parsed records with human-readable labels using standard MARC definition files (MARC21, UNIMARC).
  • Search fields — Reverse-lookup field codes by keyword (e.g. search "author" to find all related fields).
  • Works everywhere — Browser (ES modules), Node.js (native ESM), and a command-line tool.
  • Batch processing — Parse entire .mrc files or piped records, not just single entries.

Installation

npm install jsmarc

Or for development, clone the repository:

git clone https://github.com/corbin-c/jsmarc.git
cd jsmarc
npm install

No bundler required. The source ships as TypeScript — run it directly with a TypeScript-aware runtime (tsx, ts-node, etc.) or compile it yourself.

Node.js

Use native ESM imports with a TypeScript runtime such as tsx:

import { parseRecord } from 'jsmarc'
import { explainRecord } from 'jsmarc/helper'

Quick Start

Node.js

import { parseRecord } from 'jsmarc'
import { explainRecord } from 'jsmarc/helper'

const record = parseRecord(rawMarcString)
const explained = await explainRecord(record, 'marc21')

console.log(explained)

CLI (one-liner)

# Fetch records and display with field explanations
curl "https://z3950.nibroc.fr/?server=lx2.loc.gov:210/LCDB&isbn=0066620724&format=usmarc" | npx marc-node display - --format=marc21

CLI Usage

marc-node COMMAND FILE [OPTIONS]

If FILE is -, the tool reads from stdin. Run directly with tsx if not using npx:

tsx marc-node.ts COMMAND FILE [OPTIONS]

Commands

| Command | Description | | --------- | -------------------------------------------------------------------------- | | display | Parse and display records (with optional field explanation via --format) | | filter | Filter records by field/subfield values | | extract | Extract specific fields as JSON | | help | Show usage information |

Options

| Option | Syntax | Default | Description | | ---------------------- | --------------------- | -------- | --------------------------------------------------------------------- | | --encoding | string | utf8 | File encoding when reading from disk | | --record-separator | string | \u001d | Character that separates records in the batch | | --field-separator | string | \u001e | Field separator within a record | | --subfield-separator | string | \u001f | Subfield separator within a field | | --format | marc21 or unimarc | — | Enrich display with field/subfield labels from definitions | | --fields | notation string | * | Field notation (e.g. 020$a,856$u) — use \ to escape $ in shells | | --values | comma-separated | — | Values for filtering (quote values containing spaces) |

Examples

Display all records with explained fields:

curl "https://z3950.nibroc.fr/?server=lx2.loc.gov:210/LCDB&isbn=0066620724,0596001312&format=usmarc" | npx marc-node display - --format=marc21

Limit display to specific fields:

npx marc-node display /path/to/records.mrc --fields=856\$u

Extract fields as JSON:

npx marc-node extract /path/to/records.mrc --fields=100\$a,020\$a

Output:

[
  {
    "leader": "01208cam a22003014a 4500",
    "fields": [
      { "code": "020", "indicator": "  ", "subfields": [{ "code": "a", "value": "0066620724 (hc)" }] },
      { "code": "100", "indicator": "1 ", "subfields": [{ "code": "a", "value": "Torvalds, Linus," }] }
    ]
  }
]

Filter records by value:

npx marc-node filter ./records.mrc --fields=020\$a --values=0596001312,"0066620724 (hc)"

Only records whose 020$a matches one of the given comma-separated values are kept. Output is raw MARC.

Pipe from stdin:

cat /path/to/records.mrc | npx marc-node display - --fields=245\$a

API Reference

All exports from the parser and helper modules.

src/parser.ts

parseRecord(recordString, options?)

Parse a raw MARC record string into a structured MarcParser object.

| Parameter | Type | Default | Description | | ------------------- | -------------------- | ---------- | --------------------------------------------------------------------------------------------- | | recordString | string | required | Raw ISO 2709 MARC record | | options.toParse | string \| string[] | "*" | Field notation(s) to parse (e.g. "020\$a,856\$u" or ["020\$a"]). "*" parses all fields. | | options.fields | string | "\u001e" | Custom field separator | | options.subfields | string | "\u001f" | Custom subfield separator |

Returns: MarcParser — an object with the following shape:

{
  rawRecord: string,        // the original record string
  leader: string,           // first 24 characters
  header: string,           // leader + directory + field separator
  fieldSeparator: string,   // field separator used
  subfieldSeparator: string,// subfield separator used
  parseCode: string,        // field notation filter applied
  directory: [{             // parsed directory entries
    code: string,           // field tag (e.g. "001")
    length: string,         // field length (zero-padded)
    position: string        // byte offset from start of body (zero-padded)
  }],
  fields: [{               // parsed fields
    code: string,           // field tag
    value?: string,         // raw value (control fields: 001-009)
    indicator?: string,     // two indicator characters (data fields: 010+)
    subfields?: [{          // parsed subfields (data fields only)
      code: string,         // subfield code (single character)
      value: string         // subfield data
    }]
  }]
}

filterRecord(parsedRecord, fieldNotation, values)

Check if a parsed record contains matching values for a given field.

| Parameter | Type | Description | | --------------- | ------------ | ------------------------------------ | | parsedRecord | MarcParser | A record returned by parseRecord() | | fieldNotation | string | Field notation (e.g. "020\$a") | | values | string[] | Values to match against |

Returns: boolean — true if the record matches at least one value.

analyzeFieldNotation(notationString)

Parse a field notation string into a predicate function used internally by the parser.

| Parameter | Type | Description | | ---------------- | -------- | ----------------------------------------------------------------- | | notationString | string | Comma-separated field notations (e.g. "020\$a,856\$u") or "*" |

Returns: Function — a filter function (recordPart) => boolean that tests if a record part matches.

bin

Binary-safe string utility for byte-level length and slicing (handles multi-byte characters correctly in both browser and Node.js contexts).

bin.length(str) // → number (byte length)
bin.slice(str, start, end) // → string (byte-level slice)

MARC

Default configuration template with ISO 2709 separators:

MARC.recordSeparator // "\u001d"
MARC.fieldSeparator // "\u001e"
MARC.subfieldSeparator // "\u001f"

MarcParser

The parser class. Always prefer the parseRecord() wrapper unless you need the class directly.

src/helper.ts

explainRecord(parsedRecord, format)

Enrich a parsed record with human-readable labels from MARC definition files.

| Parameter | Type | Description | | -------------- | ------------ | ------------------------------------------------------------- | | parsedRecord | MarcParser | A record returned by parseRecord() | | format | string | Format key from formats.json (e.g. "marc21", "unimarc") |

Returns: Promise<object> — the record with added label properties on fields, subfields, and indicators.

explainField(field, format)

Same as explainRecord but operates on a single field object.

Returns: Promise<object> — the field object with added labels.

searchField(searchString, format)

Search for field/subfield codes by keyword in the definition files.

| Parameter | Type | Description | | -------------- | -------- | ----------------------------------------------- | | searchString | string | Keyword to search (e.g. "author", "auteur") | | format | string | Format key (e.g. "marc21", "unimarc") |

Returns: Promise<Array<{code: string, value: string}>> — matching code/label pairs.

await searchField('auteur', 'unimarc')
// [
//   { code: "200\$c", value: "Titre propre d'un auteur différent" },
//   { code: "701\$4", value: "Auteur d'oeuvre adaptée ou continuée" },
//   ...
// ]

formats

A Promise that resolves to the loaded format definitions registry (from formats.json). Used internally — you typically access definitions through explainRecord or searchField.

Project Structure

jsmarc/
├── app/                    # Web application
│   ├── index.html
│   ├── front.js
│   └── style.css
├── definitions/            # MARC format definitions
│   ├── marc21.json
│   └── unimarc.json
├── samples/                # Sample MARC record files
├── src/                    # Core source modules
│   ├── parser.ts           # Main MARC record parser (TypeScript)
│   ├── parser.js           # Legacy JS — used by web app via GitHub Pages
│   ├── parser.test.ts      # Vitest test suites
│   ├── helper.ts           # Field explanation & search via definitions
│   ├── helper.js           # Legacy JS — used by web app via GitHub Pages
│   ├── helper.test.ts
│   ├── CLI.ts              # Terminal display utilities (TypeScript)
│   ├── CLI.js              # Legacy JS — used by web app via GitHub Pages
├── marc-node.ts            # Node.js CLI executable (TypeScript, published as bin)
├── formats.json            # Registry mapping format names to definition files
├── rec.mrc                 # Sample record file
├── package.json            # npm package definition
├── tsconfig.json           # TypeScript configuration
├── vitest.config.ts        # Vitest test runner configuration
└── LICENSE                 # GNU GPL v3

Notes on the legacy .js files

The src/parser.js, src/helper.js, and src/CLI.js files are the original JavaScript implementations kept for backward compatibility with the web application at app/. They are excluded from the npm package. The TypeScript source (.ts files) is the canonical, maintained codebase that ships with the npm package.

Supported Formats

JsMarc ships with definition files for two MARC variants:

| Format | Source | License | Language | | ----------- | --------------------------------------------------------- | ------------- | -------- | | MARC21 | Library of Congress | Public domain | English | | UNIMARC | ABES (Agence Bibliographique de l'Enseignement Supérieur) | CC BY-SA | French |

Definitions provide human-readable labels for every field, subfield, and indicator value. These labels power the explainRecord and searchField APIs as well as the CLI's --format flag.

Format Definitions

The formats.json file maps format names to their definition files:

{
  "marc21": "definitions/marc21.json",
  "unimarc": "definitions/unimarc.json"
}

Adding a custom format

  1. Create a JSON file in definitions/ following the existing schema (see definitions/marc21.json for reference):

    {
      "010": {
        "value": "Library of Congress Control Number",
        "ind1": { "#": "Undefined" },
        "ind2": { "#": "Undefined" },
        "subfields": {
          "a": { "*": "LC control number" }
        }
      }
    }
  2. Add an entry to formats.json:

    {
      "myformat": "definitions/myformat.json"
    }
  3. Use it immediately:

    npx marc-node display records.mrc --format=myformat

Web App

A full-featured web interface is hosted at corbin-c.github.io/jsmarc/. It supports batch record parsing, filtering, data extraction (HTML table or JSON), and field explanation on hover.

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes — keep it dependency-free at runtime
  4. Run the test suite: npm test (Vitest)
  5. Run the type checker: npm run typecheck (TypeScript — tsc --noEmit)
  6. Submit a pull request

For major changes, consider opening an issue first to discuss your approach.

License

JsMarc is released under the GNU General Public License v3.0. You are free to use, modify, and distribute it under those terms.

Sample Records

The MARC records in samples/ and rec.mrc are provided for demonstration and educational purposes only. They were obtained from publicly accessible library catalogs and belong to their respective institutions (Library of Congress, SUDOC/ABES, OpenEdition, BnF). These files are not part of the JsMarc project and are not covered by the project's license. If you believe your rights are infringed, please open an issue.

Acknowledgments

  • Library of Congress for the MARC21 standard and public-domain field definitions
  • ABES for the UNIMARC field definitions (CC BY-SA)
  • The MARC standards community for decades of cataloging infrastructure
  • Web-Z3950 by the same author, used in CLI examples for fetching live records