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

@natlibfi/marc-record

v9.1.1

Published

MARC record implementation in JavaScript

Downloads

2,259

Readme

MARC record implementation in JavaScript

NPM Version Build Status Test Coverage

MARC record implementation in JavaScript. A JSON schema file specifies the data format.

This a fork of the original marc-record-js. The new implementation uses ES6 syntax and adds validation of the record structure.

Usage

import {MarcRecord} from '@natlibfi/marc-record';
const record = new MarcRecord();

Create record from object

const record = new MarcRecord(
  {
    leader: '02848ccm a22005894i 4500',
',
    fields: [
      {tag: '001', value: 'foo'},
      {tag: '002', value: 'bar'},
    ]
  }
)

Cloning a record

const recordB = MarcRecord.clone(recordA)

Record validation

When constructing or modifying the record, validation checks are run. You may need to alter these checks to work with incomplete intermediate records.

strict

validationOption strict: true sets all the other validationOptions as true regardless of if they are defined validationOption strict: false sets other validationOptions as they are defined or as default

noFailValidation

validationOption noFailValidation: false (default) throws an error when the record fails validation checks validationOption noFailValidation: true saves errors in validationErrors instead of throwing an error when the record fails validation checks

// return validationErrors
record.getValidationErrors();

Global validation options:

MarcRecord.getValidationOptions();

// Default settings
// These default validationOptions are (mostly) backwards compatible with marc-record-js < 7.3.0

MarcRecord.setValidationOptions(
  {
    fields: true,                  // Do not allow record without fields
    subfields: true,               // Do not allow empty subfields
    subfieldValues: true,          // Do not allow subfields without value
    controlFieldValues: true,      // Do not allow controlFields without value
    leader: false,                 // Do not allow record without leader, with empty leader or with leader with length != 24
    characters: false,             // Do not allow erronous characters in tags, indicators and subfield codes
    noControlCharacters: false,    // Do not allow ASCII control characters in field/subfield values
    noAdditionalProperties: false, // Do not allow additional properties in fields

    strict: false,                 // If true, set all validationOptions to true
    noFailValidation: false        // If true, do not error if validation fails, save errors in validationErrors instead
  }
);

You can reset global validation options to default with empty object:

// Reset to default
MarcRecord.setValidationOptions({});

You can set all global validation options to true with validationOption strict: true:

// Set all validationOptions to true with strict: true
MarcRecord.setValidationOptions({strict: true});

Record specific validation options can be given when constructing:

const record = new MarcRecord(
  {
    leader: '02848ccm a22005894i 4500',
    fields: []
  },
  {fields: false} // Allow empty fields
);

Validation examples:

The following examples demonstrate the invalid records, when default validation options are used. To fix the errors, either fix the record, or modify global/record-specific validation options.

// Error: fields[] is empty. Validation option: fields
new MarcRecord(
  {
    leader: '02848ccm a22005894i 4500',
    fields: []
  }
);

// Error: subfields[] is empty. Validation option: subfields
new MarcRecord(
  {
    leader: '02848ccm a22005894i 4500',
    fields: [
      {tag: "509", , ind1: " ", ind2: " ", subfields: []}
    ]
  }
);

// Error: subfield has no value. Validation option: subfieldValues
new MarcRecord(
  {
    leader: '02848ccm a22005894i 4500',
    fields: [
      {tag: "509", ind1: " ", ind2: " ", subfields: [{code: "a", value: ""}]}
    ]
  }
);

Record equality check

MarcRecord.isEqual(recordA, recordB);
recordA.equalsTo(recordB);

Querying for fields

get() returns fields which tags match the specified pattern:

record.get("776")         // Return fields with tag 776
record.get(/020|021/u)    // Return fields matching the regexp

getControlfields() returns so called control fields, that is, fields with simple value. These are generally fields 001 - 008.

record.getControlfields();  // Return all control fields

getDatafields() returns fields with subfields.

record.getDatafields();     // Return all data fields

getFields() fetches fields from record.

To get all 245 fields:

record.getFields('245');

To get all 001 fields which values is foo:

record.getFields('001', 'foo');

To get all 245 fields, which have specific subfields. All subfields given as argument should be present in the fetched fields:

// Fetch all 245 fields containing subfields a and b with specified values
record.getFields('245', [{code: 'a', value: 'foo'}, {code: 'b', value: 'bar'}]);

containsFieldWithValue() uses the same arguments than getFields(). It is a shorthand to check, if getFields() returns more than an empty list.

record.containsFieldWithValues('001', 'foo'); // getFields('001', 'foo').length > 0
record.containsFieldWithValues('245', [{code: 'a', value: 'foo'}]);

Custom queries: You can access record fields to implement your custom queries.

record.fields;    // Access to record fields

Adding fields

insertField / insertFields: Insertion handles the proper field ordering automatically.

// Insert single field
record.insertField({tag: "001", value: "foo"});

// Chained inserts
record
  .insertField({tag: "001", value: "A"})
  .insertField({tag: "003", value: "C"})
  .insertField({tag: "002", value: "B"});

// Insert from array:
record.insertFields([
  {tag: "001", value: "A"},
  {tag: "003", value: "C"},
  {tag: "002", value: "B"}
]);

appendField / appendFields: Appending fields to the end of record. In general, you close always use insert instead of append.

// Append single field:
record.appendField({tag: "001", value: "foo"});

// Chained appending
record
  .appendField({tag: "001", value: "A"})
  .appendField({tag: "003", value: "C"})
  .appendField({tag: "002", value: "B"});

// Append from array
record.appendFields([
  {tag: "001", value: "A"},
  {tag: "003", value: "C"},
  {tag: "002", value: "B"}
]);

Removing fields

Removing a field ONLY removes fields that are in the record. It DOES NOT compare the field content to find field.

So, always use queries to remove fields:

// Remove all 020 and 021 fields
const fields = record.get(/020|021/u);	// Returns an array
record.removeFields(fields); // Removes fields in array of matching fields

Failing examples:

// Example record
const record = new MarcRecord(
{
  leader: "02848ccm a22005894i 4500",
  fields: [
    {tag: "001", value: "bar"}
  ]
})

// FAIL: Even if fields have same values, they are different fields
record.removeField({tag: "001", value: "bar"})

// FAIL: removeField accepts fields, not strings
record.removeField("001")     // FAIL: removeField does not perform query
record.removeField(/^001$/u)  // FAIL: removeField does not perform query

// FAIL: insertField (may) insert copy of a parameter field
const field = {tag: "300", subfields: [{code: "a", value: "b"}]}
record
  .insertField(field)
  .removeField(field);

// "Direct query"
const field = record.fields[2];
record.removeField({...field})  // Obvious FAIL
record.removeField(field) // OK

Popping fields

Popping fields with queries. Query matches field tag. Matched fields are returned, and removed from record. Once you have modified the fields according to your needs, you can push them back with insert.

// Record tags: [001, 001, 002, 003, 003, 004, 005, 006]

// 1) Pop fields with query:
fields = record.pop(/(001|004)/u);

// Result:
// - Field tags: [001, 001, 004]
// - Record tags: [002, 003, 003, 005, 006]

// 2) Do something with fields
...

// 3) Push back modified fields:
record.insertFields(fields)
// Result: Record tags: [001, 001, 002, 003, 003, 004, 005, 006]

Type of Material detection

Type of material (BK/Book, CF/Computer File, CR/Continuing Resource, MP/Map, MU/Music, MX/Mixed Material, VM/Visual Material) of a given record can be queried in two ways:

// 1) Ask if record belong to a certain type
if (record.isBK()) {
  // Do something
}

// 2) Ask for record type:
if (record.getTypeOfMaterial() === 'MU') { // NB! Failure returns false
  // Do something else
}

Sorting fields

record.sortFields();

Chaining

Sorting, inserting and removing can be chained together:

record
  .removeFields(record.get(/005/u))       // Remove all 005 fields
  .insertField({tag: "005", value: "A"})  // Insert new 005 field
  .sortFields();                          // Sort fields

// Note: In this case, there is no need for sort, as insert puts the field to
// correct place. It is there just as an example.

See also

To serialize and unserialize MARC records, see marc-record-serializers

License and copyright

Copyright (c) 2014-2017 Pasi Tuominen [email protected]

Copyright (c) 2018-2024 University Of Helsinki (The National Library Of Finland)

This project's source code is licensed under the terms of MIT License or any later version.