{"id":21198588,"url":"https://github.com/natlibfi/marc-record-js","last_synced_at":"2025-07-10T06:30:40.375Z","repository":{"id":33152435,"uuid":"144279240","full_name":"NatLibFi/marc-record-js","owner":"NatLibFi","description":"MARC record implementation in JavaScript","archived":false,"fork":false,"pushed_at":"2024-11-05T12:14:21.000Z","size":1453,"stargazers_count":6,"open_issues_count":3,"forks_count":1,"subscribers_count":11,"default_branch":"main","last_synced_at":"2024-11-05T12:17:08.176Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NatLibFi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-10T11:43:39.000Z","updated_at":"2024-09-25T14:07:49.000Z","dependencies_parsed_at":"2023-12-12T13:45:53.174Z","dependency_job_id":"4d3c8607-3309-4a30-8ac5-bf4451bb6b80","html_url":"https://github.com/NatLibFi/marc-record-js","commit_stats":{"total_commits":114,"total_committers":9,"mean_commits":"12.666666666666666","dds":0.5087719298245614,"last_synced_commit":"9495dcb5c84b38fcf1ed133482a11e243ffa680e"},"previous_names":[],"tags_count":73,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NatLibFi%2Fmarc-record-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NatLibFi%2Fmarc-record-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NatLibFi%2Fmarc-record-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NatLibFi%2Fmarc-record-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NatLibFi","download_url":"https://codeload.github.com/NatLibFi/marc-record-js/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225622927,"owners_count":17498168,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-20T19:52:28.115Z","updated_at":"2025-07-10T06:30:40.364Z","avatar_url":"https://github.com/NatLibFi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MARC record implementation in JavaScript\n\n[![NPM Version](https://img.shields.io/npm/v/@natlibfi/marc-record.svg)](https://npmjs.org/package/@natlibfi/marc-record)\n[![Build Status](https://travis-ci.org/NatLibFi/marc-record-js.svg)](https://travis-ci.org/NatLibFi/marc-record-js)\n[![Test Coverage](https://codeclimate.com/github/NatLibFi/marc-record-js/badges/coverage.svg)](https://codeclimate.com/github/NatLibFi/marc-record-js/coverage)\n\nMARC record implementation in JavaScript. [A JSON schema](src/schema.js) file specifies the data format.\n\nThis a fork of the original [marc-record-js](https://github.com/petuomin/marc-record-js). The new implementation uses ES6 syntax and adds validation of the record structure.\n\n## Usage\n```js\nimport {MarcRecord} from '@natlibfi/marc-record';\nconst record = new MarcRecord();\n```\n### Create record from object\n```js\nconst record = new MarcRecord(\n  {\n    leader: '02848ccm a22005894i 4500',\n',\n    fields: [\n      {tag: '001', value: 'foo'},\n      {tag: '002', value: 'bar'},\n    ]\n  }\n)\n```\n\n### Cloning a record\n```js\nconst recordB = MarcRecord.clone(recordA)\n```\n\n### Record validation\n\nWhen constructing or modifying the record, validation checks are run. You may need to alter these checks to work with incomplete intermediate records.\n\n**strict**\n\nvalidationOption `strict: true` sets all the other validationOptions as true regardless of if they are defined\nvalidationOption `strict: false` sets other validationOptions as they are defined or as default\n\n**noFailValidation**\n\nvalidationOption `noFailValidation: false` (default) throws an error when the record fails validation checks\nvalidationOption `noFailValidation: true` saves errors in validationErrors instead of throwing an error when the record fails validation checks\n\n```js\n// return validationErrors\nrecord.getValidationErrors();\n```\n\n**Global validation options:**\n\n```js\nMarcRecord.getValidationOptions();\n\n// Default settings\n// These default validationOptions are (mostly) backwards compatible with marc-record-js \u003c 7.3.0\n\nMarcRecord.setValidationOptions(\n  {\n    fields: true,                  // Do not allow record without fields\n    subfields: true,               // Do not allow empty subfields\n    subfieldValues: true,          // Do not allow subfields without value\n    controlFieldValues: true,      // Do not allow controlFields without value\n    leader: false,                 // Do not allow record without leader, with empty leader or with leader with length != 24\n    characters: false,             // Do not allow erronous characters in tags, indicators and subfield codes\n    noControlCharacters: false,    // Do not allow ASCII control characters in field/subfield values\n    noAdditionalProperties: false, // Do not allow additional properties in fields\n\n    strict: false,                 // If true, set all validationOptions to true\n    noFailValidation: false        // If true, do not error if validation fails, save errors in validationErrors instead\n  }\n);\n```\n\nYou can reset global validation options to default with empty object:\n\n```js\n// Reset to default\nMarcRecord.setValidationOptions({});\n```\n\nYou can set all global validation options to true with validationOption strict: true:\n\n```js\n// Set all validationOptions to true with strict: true\nMarcRecord.setValidationOptions({strict: true});\n```\n\n**Record specific validation options** can be given when constructing:\n\n```js\nconst record = new MarcRecord(\n  {\n    leader: '02848ccm a22005894i 4500',\n    fields: []\n  },\n  {fields: false} // Allow empty fields\n);\n```\n\n**Validation examples:**\n\nThe 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.\n\n```js\n// Error: fields[] is empty. Validation option: fields\nnew MarcRecord(\n  {\n    leader: '02848ccm a22005894i 4500',\n    fields: []\n  }\n);\n\n// Error: subfields[] is empty. Validation option: subfields\nnew MarcRecord(\n  {\n    leader: '02848ccm a22005894i 4500',\n    fields: [\n      {tag: \"509\", , ind1: \" \", ind2: \" \", subfields: []}\n    ]\n  }\n);\n\n// Error: subfield has no value. Validation option: subfieldValues\nnew MarcRecord(\n  {\n    leader: '02848ccm a22005894i 4500',\n    fields: [\n      {tag: \"509\", ind1: \" \", ind2: \" \", subfields: [{code: \"a\", value: \"\"}]}\n    ]\n  }\n);\n```\n\n### Record equality check\n```ks\nMarcRecord.isEqual(recordA, recordB);\nrecordA.equalsTo(recordB);\n```\n\n### Querying for fields\n\n**get()** returns fields which tags match the specified pattern:\n\n```js\nrecord.get(\"776\")         // Return fields with tag 776\nrecord.get(/020|021/u)    // Return fields matching the regexp\n```\n\n**getControlfields()** returns so called control fields, that is, fields\nwith simple value. These are generally fields 001 - 008.\n\n```js\nrecord.getControlfields();  // Return all control fields\n```\n\n**getDatafields()** returns fields with subfields.\n\n```js\nrecord.getDatafields();     // Return all data fields\n```\n\n**getFields()** fetches fields from record.\n\nTo get all 245 fields:\n\n```js\nrecord.getFields('245');\n```\n\nTo get all 001 fields which values is foo:\n\n```js\nrecord.getFields('001', 'foo');\n```\n\nTo get all 245 fields, which have specific subfields. All subfields given as argument should be present in the fetched fields:\n\n```js\n// Fetch all 245 fields containing subfields a and b with specified values\nrecord.getFields('245', [{code: 'a', value: 'foo'}, {code: 'b', value: 'bar'}]);\n```\n\n**containsFieldWithValue()** uses the same arguments than getFields(). It is\na shorthand to check, if getFields() returns more than an empty list.\n\n```js\nrecord.containsFieldWithValues('001', 'foo'); // getFields('001', 'foo').length \u003e 0\nrecord.containsFieldWithValues('245', [{code: 'a', value: 'foo'}]);\n```\n\n**Custom queries:** You can access record fields to implement your custom\nqueries.\n\n```js\nrecord.fields;    // Access to record fields\n```\n\n### Adding fields\n\n**insertField / insertFields:** Insertion handles the proper field ordering automatically.\n\n```js\n// Insert single field\nrecord.insertField({tag: \"001\", value: \"foo\"});\n\n// Chained inserts\nrecord\n  .insertField({tag: \"001\", value: \"A\"})\n  .insertField({tag: \"003\", value: \"C\"})\n  .insertField({tag: \"002\", value: \"B\"});\n\n// Insert from array:\nrecord.insertFields([\n  {tag: \"001\", value: \"A\"},\n  {tag: \"003\", value: \"C\"},\n  {tag: \"002\", value: \"B\"}\n]);\n```\n\n**appendField / appendFields:** Appending fields to the end of record. In general, you\nclose always use insert instead of append.\n\n```js\n// Append single field:\nrecord.appendField({tag: \"001\", value: \"foo\"});\n\n// Chained appending\nrecord\n  .appendField({tag: \"001\", value: \"A\"})\n  .appendField({tag: \"003\", value: \"C\"})\n  .appendField({tag: \"002\", value: \"B\"});\n\n// Append from array\nrecord.appendFields([\n  {tag: \"001\", value: \"A\"},\n  {tag: \"003\", value: \"C\"},\n  {tag: \"002\", value: \"B\"}\n]);\n\n```\n\n### Removing fields\n\nRemoving a field **ONLY** removes fields that are in the record. It\n**DOES NOT** compare the field content to find field.\n\nSo, always use queries to remove fields:\n```js\n// Remove all 020 and 021 fields\nconst fields = record.get(/020|021/u);\t// Returns an array\nrecord.removeFields(fields); // Removes fields in array of matching fields\n```\n\nFailing examples:\n\n```js\n// Example record\nconst record = new MarcRecord(\n{\n  leader: \"02848ccm a22005894i 4500\",\n  fields: [\n    {tag: \"001\", value: \"bar\"}\n  ]\n})\n\n// FAIL: Even if fields have same values, they are different fields\nrecord.removeField({tag: \"001\", value: \"bar\"})\n\n// FAIL: removeField accepts fields, not strings\nrecord.removeField(\"001\")     // FAIL: removeField does not perform query\nrecord.removeField(/^001$/u)  // FAIL: removeField does not perform query\n\n// FAIL: insertField (may) insert copy of a parameter field\nconst field = {tag: \"300\", subfields: [{code: \"a\", value: \"b\"}]}\nrecord\n  .insertField(field)\n  .removeField(field);\n\n// \"Direct query\"\nconst field = record.fields[2];\nrecord.removeField({...field})  // Obvious FAIL\nrecord.removeField(field) // OK\n```\n\n### Popping fields\n\nPopping 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.\n\n```js\n// Record tags: [001, 001, 002, 003, 003, 004, 005, 006]\n\n// 1) Pop fields with query:\nfields = record.pop(/(001|004)/u);\n\n// Result:\n// - Field tags: [001, 001, 004]\n// - Record tags: [002, 003, 003, 005, 006]\n\n// 2) Do something with fields\n...\n\n// 3) Push back modified fields:\nrecord.insertFields(fields)\n// Result: Record tags: [001, 001, 002, 003, 003, 004, 005, 006]\n```\n\n### Type of Material detection\n\nType 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:\n\n```js\n// 1) Ask if record belong to a certain type\nif (record.isBK()) {\n  // Do something\n}\n\n// 2) Ask for record type:\nif (record.getTypeOfMaterial() === 'MU') { // NB! Failure returns false\n  // Do something else\n}\n```\n\n## Sorting fields\n\n```js\nrecord.sortFields();\n```\n\n## Chaining\n\nSorting, inserting and removing can be chained together:\n\n```js\nrecord\n  .removeFields(record.get(/005/u))       // Remove all 005 fields\n  .insertField({tag: \"005\", value: \"A\"})  // Insert new 005 field\n  .sortFields();                          // Sort fields\n\n// Note: In this case, there is no need for sort, as insert puts the field to\n// correct place. It is there just as an example.\n```\n\n\n\n## See also\nTo serialize and unserialize MARC records, see [marc-record-serializers](https://github.com/natlibfi/marc-record-serializers)\n\n\n## License and copyright\n\nCopyright (c) 2014-2017 **Pasi Tuominen \u003cpasi.tuominen@gmail.com\u003e**\n\nCopyright (c) 2018-2025 **University Of Helsinki (The National Library Of Finland)**\n\nThis project's source code is licensed under the terms of **MIT License** or any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnatlibfi%2Fmarc-record-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnatlibfi%2Fmarc-record-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnatlibfi%2Fmarc-record-js/lists"}