UNPKG

inist-ark

Version:

NodeJS package used to handle "normalized" ARK for the INIST organization

270 lines (243 loc) 6.95 kB
'use strict'; function InistArk(opt) { opt = opt || {}; this.naan = opt.naan || '67375'; // '67375' is the INIST NAAN this.subpublisher = opt.subpublisher !== undefined ? opt.subpublisher : ''; this.alphabet = opt.alphabet || '0123456789BCDFGHJKLMNPQRSTVWXZ'; this.hyphen = opt.hyphen !== undefined ? opt.hyphen : true; } // // JS implentation of NCDA // see http://search.cpan.org/~jak/Noid/noid#NOID_CHECK_DIGIT_ALGORITHM // function ncda(input, alphabet) { var R = alphabet.length; var chr = input.split('') , ord = [] , pos = []; chr.forEach(function (c, i) { var z = alphabet.indexOf(c); ord.push(z > 0 ? z : 0); pos.push(i + 1); }); var sum = 0; pos.forEach(function (p, i) { sum += p * ord[i]; }); var x = sum % R; return alphabet[x]; } function check(id) { if (!id) { return false; } var semaphore = true; id.split('').reduce(function(prev, cur) { if (cur === prev) { semaphore = false; } return cur; }); return semaphore; } // // generate an ARK identifier of 8 characters // function uniqid(alphabet) { var id; while (check(id) === false) { id = ''; for (var i = 0; i < 8; i++) { id += alphabet[Math.floor(Math.random() * alphabet.length)]; } } return id; } // // INIST's ARK generator // Use it like this: // ark.generate(); // returns: ark:/67375/39D-L2DM2F95-7 // ark.generate({ subpublisher: '015' }); // returns: ark:/67375/015-X73BVHH2-2 // InistArk.prototype.generate = function (opt) { var self = this; opt = opt || {}; var subpublisher = opt.subpublisher !== undefined ? opt.subpublisher : self.subpublisher; var hyphen = opt.hyphen !== undefined ? opt.hyphen : self.hyphen; var naan = String(opt.naan || self.naan); var separator = hyphen ? '-' : ''; var identifier = uniqid(self.alphabet); if (naan.length !== 5) { var err1 = new Error('ARK naan is mandatory to generate a new one.'); err1.code = 'ark-naan-empty'; throw err1; } if (subpublisher === false) { return 'ark:/' + naan + '/' + identifier + separator + ncda(naan + identifier, self.alphabet); } // just check a subpublisher has been setup // cause the ARK will be invalid if no subpublisher choosed if (typeof subpublisher !== 'string' || subpublisher.length !== 3) { var err2 = new Error('ARK subpublisher is mandatory to generate a new one.'); err2.code = 'ark-subpublisher-empty'; throw err2; } return 'ark:/' + naan + '/' + subpublisher + separator + identifier + separator + ncda(naan + subpublisher + identifier, self.alphabet); }; // // INIST's ARK parser // Use it like this: // ark.parse('ark:/67375/39D-L2DM2F95-7'); // Returns: // { ark: 'ark:/67375/39D-L2DM2F95-7', // naan: '67375', // name: '39D-L2DM2F95-7', // subpublisher: '39D', // identifier: 'L2DM2F95', // checksum: '7' // } // InistArk.prototype.parse = function (rawArk) { var self = this; var seg = rawArk.split('/'); var err; if (seg.length !== 3) { err = new Error('Invalid ARK syntax'); err.code = 'ark-parts'; throw err; } if (seg[0] !== 'ark:') { err = new Error('Unknown ARK label'); err.code = 'ark-label'; throw err; } if (seg[1] !== String(self.naan)) { err = new Error('Unknow ARK NAAN'); err.code = 'ark-naan'; throw err; } var nameSplitted; if (self.hyphen) { nameSplitted = seg[2].split('-'); if (self.subpublisher === false) { nameSplitted.unshift(''); } } else { nameSplitted = ['', '', '']; nameSplitted[2] = seg[2].substr(-1); if (self.subpublisher === false) { nameSplitted[1] = seg[2].substr(0, 8); } else { nameSplitted[0] = seg[2].substr(0, 3); nameSplitted[1] = seg[2].substr(3, 8); } } if (nameSplitted.length !== 3) { err = new Error('Invalid ARK name syntax'); err.code = 'ark-name-parts'; throw err; } var result = { ark: rawArk, naan : seg[1], name: seg[2], subpublisher: nameSplitted[0], identifier: nameSplitted[1], checksum: nameSplitted[2] }; if (self.subpublisher !== false && result.subpublisher.length !== 3) { err = new Error('Invalid ARK subpublisher: should be 3 characters long'); err.code = 'ark-subpublisher-length'; throw err; } if (result.identifier.length !== 8) { err = new Error('Invalid ARK identifier: should be 8 characters long'); err.code = 'ark-identifier-length'; throw err; } if (result.checksum.length !== 1) { err = new Error('Invalid ARK checksum: should be 1 character long'); err.code = 'ark-checksum-length'; throw err; } return result; }; // // INIST's ARK validator // Use it like this: // ark.validate('ark:/67375/39D-L2DM2F95-7'); // Returns: // { ark: true, // false if one of the following fields is false // naan: true, // false if it's not the inist naan // name: true, // false if subpubliser, identifier or checksum is false // subpublisher: true, // false if not 3 char length and not respecting the alphabet // identifier: true, // false if not 8 chars len or if it does not respect the alphabet // checksum: true // false if the checksum is wrong // } // InistArk.prototype.validate = function (rawArk) { var self = this; var result = { ark: true, naan: true, name: true, subpublisher: true, identifier: true, checksum: true }; // try to parse the ARK to know which part is wrong try { var correctCheckSum; var ark = self.parse(rawArk); if (self.subpublisher === false) { correctCheckSum = ncda(ark.naan + ark.identifier, self.alphabet); } else { correctCheckSum = ncda(ark.naan + ark.subpublisher + ark.identifier, self.alphabet ); } result.checksum = correctCheckSum === ark.checksum; } catch (err) { result.ark = false; if (err.code === 'ark-naan') { result.naan = false; } if (err.code === 'ark-name-parts') { result.name = false; result.checksum = false; result.subpublisher = false; result.identifier = false; } if (err.code === 'ark-subpublisher-length') { result.subpublisher = false; result.checksum = false; } if (err.code === 'ark-identifier-length') { result.identifier = false; result.checksum = false; } if (err.code === 'ark-checksum-length') { result.checksum = false; } } if (result.checksum === false || result.subpublisher === false || result.identifier === false) { result.ark = false; result.name = false; } return result; }; module.exports = InistArk;