@natlibfi/marc-record-validators-melinda 12.0.7 → 12.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/addMissingField041.js +6 -3
  2. package/dist/addMissingField041.js.map +2 -2
  3. package/dist/addMissingField336.js +7 -4
  4. package/dist/addMissingField336.js.map +2 -2
  5. package/dist/addMissingField337.js +6 -3
  6. package/dist/addMissingField337.js.map +2 -2
  7. package/dist/addMissingField338.js +8 -5
  8. package/dist/addMissingField338.js.map +2 -2
  9. package/dist/cyrillux-usemarcon-replacement.js +5 -2
  10. package/dist/cyrillux-usemarcon-replacement.js.map +2 -2
  11. package/dist/cyrillux.js +10 -7
  12. package/dist/cyrillux.js.map +2 -2
  13. package/dist/disambiguateSeriesStatements.js +2 -1
  14. package/dist/disambiguateSeriesStatements.js.map +2 -2
  15. package/dist/drop-terms.js +5 -4
  16. package/dist/drop-terms.js.map +2 -2
  17. package/dist/fix-33X.js +7 -4
  18. package/dist/fix-33X.js.map +2 -2
  19. package/dist/fix-country-codes.js +5 -0
  20. package/dist/fix-country-codes.js.map +2 -2
  21. package/dist/fix-language-codes.js +5 -1
  22. package/dist/fix-language-codes.js.map +2 -2
  23. package/dist/fix-sami-041.js +11 -10
  24. package/dist/fix-sami-041.js.map +2 -2
  25. package/dist/indicator-fixes.js +5 -1
  26. package/dist/indicator-fixes.js.map +2 -2
  27. package/dist/merge-fields/counterpartField.js +6 -6
  28. package/dist/merge-fields/counterpartField.js.map +2 -2
  29. package/dist/merge-fields/mergableIndicator.js +0 -3
  30. package/dist/merge-fields/mergableIndicator.js.map +2 -2
  31. package/dist/merge-fields/worldKnowledge.js.map +2 -2
  32. package/dist/mergeRelatorTermFields.js +9 -6
  33. package/dist/mergeRelatorTermFields.js.map +2 -2
  34. package/dist/normalize-dashes.js +7 -4
  35. package/dist/normalize-dashes.js.map +2 -2
  36. package/dist/normalize-identifiers.js.map +2 -2
  37. package/dist/normalize-utf8-diacritics.js.map +2 -2
  38. package/dist/normalizeFieldForComparison.js.map +1 -1
  39. package/dist/normalizeSubfieldValueForComparison.js.map +1 -1
  40. package/dist/punctuation2.js +5 -2
  41. package/dist/punctuation2.js.map +2 -2
  42. package/dist/reindexSubfield6OccurenceNumbers.js +11 -10
  43. package/dist/reindexSubfield6OccurenceNumbers.js.map +2 -2
  44. package/dist/removeDuplicateDataFields.js +3 -2
  45. package/dist/removeDuplicateDataFields.js.map +2 -2
  46. package/dist/removeInferiorDataFields.js.map +2 -2
  47. package/dist/resolveOrphanedSubfield6s.js +3 -2
  48. package/dist/resolveOrphanedSubfield6s.js.map +2 -2
  49. package/dist/sortSubfields.js +1 -1
  50. package/dist/sortSubfields.js.map +2 -2
  51. package/dist/stripPunctuation.js +4 -3
  52. package/dist/stripPunctuation.js.map +2 -2
  53. package/dist/subfield6Utils.js +4 -1
  54. package/dist/subfield6Utils.js.map +2 -2
  55. package/dist/subfield8Utils.js.map +2 -2
  56. package/dist/translate-terms.js +4 -3
  57. package/dist/translate-terms.js.map +2 -2
  58. package/dist/typeOfDate-008.js +3 -1
  59. package/dist/typeOfDate-008.js.map +2 -2
  60. package/dist/update-field-540.js.map +2 -2
  61. package/dist/urn.js +13 -12
  62. package/dist/urn.js.map +2 -2
  63. package/package.json +7 -7
  64. package/src/addMissingField041.js +8 -4
  65. package/src/addMissingField336.js +10 -5
  66. package/src/addMissingField337.js +9 -5
  67. package/src/addMissingField338.js +11 -6
  68. package/src/cyrillux-usemarcon-replacement.js +9 -5
  69. package/src/cyrillux.js +18 -12
  70. package/src/disambiguateSeriesStatements.js +4 -1
  71. package/src/drop-terms.js +8 -6
  72. package/src/fix-33X.js +10 -6
  73. package/src/fix-country-codes.js +7 -3
  74. package/src/fix-language-codes.js +8 -4
  75. package/src/fix-sami-041.js +13 -11
  76. package/src/indicator-fixes.js +10 -7
  77. package/src/merge-fields/counterpartField.js +10 -10
  78. package/src/merge-fields/mergableIndicator.js +3 -3
  79. package/src/merge-fields/worldKnowledge.js +11 -6
  80. package/src/mergeRelatorTermFields.js +12 -11
  81. package/src/normalize-dashes.js +11 -5
  82. package/src/normalize-identifiers.js +12 -19
  83. package/src/normalize-utf8-diacritics.js +6 -3
  84. package/src/normalizeFieldForComparison.js +2 -2
  85. package/src/normalizeSubfieldValueForComparison.js +2 -2
  86. package/src/punctuation2.js +34 -30
  87. package/src/reindexSubfield6OccurenceNumbers.js +13 -11
  88. package/src/removeDuplicateDataFields.js +29 -27
  89. package/src/removeInferiorDataFields.js +28 -24
  90. package/src/resolveOrphanedSubfield6s.js +6 -4
  91. package/src/sortSubfields.js +5 -5
  92. package/src/stripPunctuation.js +5 -3
  93. package/src/subfield6Utils.js +33 -35
  94. package/src/subfield8Utils.js +10 -7
  95. package/src/translate-terms.js +13 -9
  96. package/src/typeOfDate-008.js +4 -1
  97. package/src/update-field-540.js +7 -5
  98. package/src/urn.js +17 -13
  99. package/test-fixtures/drop-terms/02/metadata.json +1 -1
  100. package/test-fixtures/drop-terms/03/metadata.json +1 -1
  101. package/test-fixtures/drop-terms/04/metadata.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/subfield6Utils.js"],
4
- "sourcesContent": ["//import createDebugLogger from 'debug';\n// const debug = createDebugLogger('@natlibfi/marc-record-validator-melinda/subfield6Utils');\n\nimport {add8s, fieldsGetAllSubfield8LinkingNumbers, getSubfield8LinkingNumber, isValidSubfield8} from './subfield8Utils.js';\nimport {fieldHasSubfield, fieldToString, /* fieldsToString, */ nvdebug, subfieldToString} from './utils.js';\n\n//const debug = createDebugLogger('@natlibfi/melinda-marc-record-merge-reducers:subfield6Utils');\n//const debugData = debug.extend('data');\n//const debugDev = debug.extend('dev');\n\n// NB! Subfield 6 is non-repeatable and it should always comes first!\n// NB! Index size should always be 2 (preceding 0 required for 01..09) However, support for 100+ was added on 2023-02-27.\n// NB! Index value '00' are left as they are (is not paired/indexed/whatever.\nconst sf6Regexp = /^[0-9][0-9][0-9]-(?:[0-9][0-9]|[1-9][0-9]+)(?:[^0-9].*)?$/u;\n\nexport function isValidSubfield6(subfield) {\n if (subfield.code !== '6') {\n return false;\n }\n return subfield.value.match(sf6Regexp);\n}\n\nexport function subfield6GetTag(subfield) {\n if (isValidSubfield6(subfield)) {\n return subfield.value.substring(0, 3);\n }\n return undefined;\n}\n\nexport function subfield6GetOccurrenceNumber(subfield) {\n if (isValidSubfield6(subfield)) {\n // Skip \"TAG-\" prefix. 2023-02-20: removed 2-digit requirement from here...\n return subfield.value.substring(4).replace(/\\D.*$/u, '');\n }\n return undefined;\n}\n\nexport function subfield6GetOccurrenceNumberAsInteger(subfield) {\n const index = subfield6GetOccurrenceNumber(subfield);\n if (index === undefined || index === '00') {\n return 0;\n }\n const result = parseInt(index, 10);\n //nvdebug(`SF6: ${subfield.value} => ${index} => ${result}`, debug);\n return result;\n}\n\nexport function subfield6ResetOccurrenceNumber(subfield, occurrenceNumber) {\n if (!isValidSubfield6(subfield)) {\n return;\n }\n const occurrenceNumberAsString = typeof occurrenceNumber === 'number' ? intToOccurrenceNumberString(occurrenceNumber) : occurrenceNumber;\n\n const newValue = subfield.value.substring(0, 4) + occurrenceNumberAsString + subfield6GetTail(subfield);\n //nvdebug(`Set subfield $6 value from ${subfieldToString(subfield)} to ${newValue}`);\n subfield.value = newValue;\n}\n\n\nfunction subfield6GetTail(subfield) {\n if (isValidSubfield6(subfield)) {\n // Skip \"TAG-\" prefix. 2023-02-20: removed 2-digit requirement from here...\n return subfield.value.replace(/^\\d+-\\d+/u, '');\n }\n return '';\n}\n\nexport function subfield6HasWantedTagAndOccurrenceNumber(subfield, tagAndOccurrenceNumber) {\n if (subfield.code !== '6') {\n return false;\n }\n // We could also use generic code and go getTag()+'-'+getIndex() instead of regexp...\n const key = subfield.value.replace(/^([0-9][0-9][0-9]-[0-9][0-9]+).*$/u, '$1');\n //nvdebug(` Compare '${key}' vs '${tagAndOccurrenceNumber}'`);\n return key === tagAndOccurrenceNumber;\n}\n\n// <= SUBFIELD, FIELD =>\n\nexport function fieldGetUnambiguousTag(field) {\n const tags = field.subfields.filter(sf => subfield6GetTag(sf));\n if (tags.length === 1) {\n return subfield6GetTag(tags[0]);\n }\n return undefined;\n}\n\nexport function fieldGetUnambiguousOccurrenceNumber(field) {\n const occurrenceNumbers = field.subfields.filter(sf => subfield6GetOccurrenceNumber(sf));\n if (occurrenceNumbers.length === 1) {\n return subfield6GetOccurrenceNumber(occurrenceNumbers[0]);\n }\n return undefined;\n}\n\nexport function fieldHasOccurrenceNumber(field, occurrenceNumber) {\n //nvdebug(`${occurrenceNumber} vs ${fieldToString(field)}`);\n return field.subfields && field.subfields.some(sf => subfield6GetOccurrenceNumber(sf) === occurrenceNumber);\n}\n\nexport function fieldResetOccurrenceNumber(field, newOccurrenceNumber, oldOccurrenceNumber = undefined) {\n field.subfields.forEach(subfield => innerReset(subfield));\n\n function innerReset(subfield) {\n // (Optional) Check that this is really the occurrence number we wan't to reseot\n if (oldOccurrenceNumber !== undefined) {\n const currOccurrenceNumber = subfield6GetOccurrenceNumber(subfield);\n if (currOccurrenceNumber !== oldOccurrenceNumber) {\n return;\n }\n }\n subfield6ResetOccurrenceNumber(subfield, newOccurrenceNumber);\n }\n}\n\nexport function intToOccurrenceNumberString(i) {\n return i < 10 ? `0${i}` : `${i}`;\n}\n\nexport function fieldGetMaxSubfield6OccurrenceNumberAsInteger(field) {\n // used by reducer!\n //nvdebug(`Checking subfields $6 from ${JSON.stringify(field)}`);\n const sf6s = field.subfields ? field.subfields.filter(subfield => isValidSubfield6(subfield)) : [];\n if (sf6s.length === 0) {\n return 0;\n }\n // There should always be one, but here we check every subfield.\n //nvdebug(`Got ${field.subfields} $6-subfield(s) from ${JSON.stringify(field)}`, debug);\n const vals = sf6s.map(sf => subfield6GetOccurrenceNumberAsInteger(sf));\n return Math.max(...vals);\n}\n\nexport function fieldHasWantedTagAndOccurrenceNumber(field, tagAndOccurrenceNumber) {\n return field.subfields && field.subfields.some(sf => subfield6HasWantedTagAndOccurrenceNumber(sf, tagAndOccurrenceNumber));\n}\n\n\n/*\nexport function getFieldsWithGivenOccurrenceNumberSubfield6(record, occurrenceNumberAsString) {\n const record.fields.filter(field => field\n\n function fieldHasIndex(field, index) {\n if (!field.subfields) {\n return false;\n }\n return field.subfields.find(sf => isValidSubfield6(sf) && subfieldGetOccurrenceNumber6(sf) === index);\n }\n}\n*/\n\n\nexport function fieldHasValidSubfield6(field) {\n return field.subfields && field.subfields.some(sf => isValidSubfield6(sf));\n}\n\nexport function isSubfield6Pair(field, otherField) {\n // No need to log this:\n //nvdebug(`LOOK for $6-pair:\\n ${fieldToString(field)}\\n ${fieldToString(otherField)}`);\n if (!fieldHasValidSubfield6(field) || !fieldHasValidSubfield6(otherField)) {\n return false;\n }\n\n if (!tagsArePairable6(field.tag, otherField.tag)) {\n //nvdebug(` FAILED. REASON: TAGS NOT PAIRABLE!`);\n return false;\n }\n\n\n const fieldIndex = fieldGetUnambiguousOccurrenceNumber(field);\n if (fieldIndex === undefined || fieldIndex === '00') {\n //nvdebug(` FAILED. REASON: NO INDEX FOUND`);\n return false;\n }\n\n const otherFieldIndex = fieldGetUnambiguousOccurrenceNumber(otherField);\n\n\n if (fieldIndex !== otherFieldIndex) {\n //nvdebug(` FAILURE: INDEXES: ${fieldIndex} vs ${otherFieldIndex}`);\n return false;\n }\n\n if (fieldGetUnambiguousTag(field) !== otherField.tag || field.tag !== fieldGetUnambiguousTag(otherField)) {\n //nvdebug(` FAILURE: TAG vs $6 TAG`);\n return false;\n }\n return true;\n\n function tagsArePairable6(tag1, tag2) {\n // How to do XOR operation in one line? Well, this is probably more readable...\n if (tag1 === '880' && tag2 === '880') {\n return false;\n }\n if (tag1 !== '880' && tag2 !== '880') {\n return false;\n }\n return true;\n }\n}\n\n\nfunction subfieldSevenToOneOccurrenceNumber(subfield) {\n if (subfield.code !== '6' || subfield.value.substring(0, 1) !== '7') {\n return;\n }\n subfield.value = `1${subfield.value.substring(1)}`;\n}\n\nexport function fieldSevenToOneOccurrenceNumber(field) {\n if (field.tag !== '880') {\n return;\n }\n field.subfields.forEach(sf => subfieldSevenToOneOccurrenceNumber(sf));\n}\n\n\nexport function fieldGetOccurrenceNumberPairs(field, candFields) {\n // NB! TAG!=880 returns 880 fields, TAG==880 returns non-880 field\n //nvdebug(` Trying to finds pair for ${fieldToString(field)} in ${candFields.length} fields`);\n const pairs = candFields.filter(otherField => isSubfield6Pair(field, otherField));\n if (pairs.length === 0) {\n //nvdebug(`NO PAIRS FOUND FOR '${fieldToString(field)}'`);\n return pairs;\n }\n //nvdebug(`${pairs.length} PAIR(S) FOUND FOR '${fieldToString(field)}'`);\n pairs.forEach(pairedField => nvdebug(` '${fieldToString(pairedField)}'`));\n return pairs;\n}\n\nexport function fieldGetOccurrenceNumbers(field) {\n let occurrenceNumbers = [];\n field.subfields?.forEach(sf => subfieldExtractOccurrenceNumber(sf));\n\n function subfieldExtractOccurrenceNumber(sf) {\n if (!isValidSubfield6(sf)) {\n return;\n }\n const occurrenceNumber = subfield6GetOccurrenceNumber(sf);\n if (occurrenceNumber === '00' || occurrenceNumbers.includes(occurrenceNumber)) {\n return;\n }\n occurrenceNumbers.push(occurrenceNumber);\n }\n return occurrenceNumbers;\n}\n\nexport function fieldsGetOccurrenceNumbers(fields) {\n let occurrenceNumbers = [];\n\n fields.forEach(f => fieldProcessOccurrenceNumbers(f));\n\n function fieldProcessOccurrenceNumbers(f) {\n const newOccurrenceNumbers = fieldGetOccurrenceNumbers(f);\n newOccurrenceNumbers.forEach(occurrenceNumber => {\n if (!occurrenceNumbers.includes(occurrenceNumber)) {\n occurrenceNumbers.push(occurrenceNumber);\n }\n\n });\n }\n return occurrenceNumbers;\n}\n\n/*\nexport function fieldGetSubfield6Pair(field, record) {\n const pairedFields = record.fields.filter(otherField => isSubfield6Pair(field, otherField));\n if (pairedFields.length !== 1) {\n return undefined;\n }\n // NB! It is theoretically possible to have multiple pairable 880 fields (one for each encoding)\n nvdebug(`fieldGetSubfield6Pair(): ${fieldToString(field)} => ${fieldToString(pairedFields[0])}`);\n return pairedFields[0];\n}\n*/\n\n/*\nexport function pairAndStringify6(field, record) {\n const pair6 = fieldGetSubfield6Pair(field, record);\n if (!pair6) {\n return fieldToNormalizedString(field);\n }\n return fieldsToNormalizedString([field, pair6]);\n}\n*/\n\n// Frequencly list for $6 subfields in 1XX/7XX fields:\n// 231115 100\n// 183832 700\n// 28773 710\n// 2047 711\n// 661 110\n// 341 111\n// 284 130\n// 63 730\n// Thus there's a real risk of ending up with, say, identical 100 vs 700 chains.\n// Semi-hackily support 1XX/7XX-version: 7XX can be deleted if corresponding 1XX exists:\n\nexport function is7XX(tag) {\n return ['700', '710', '711', '730'].includes(tag);\n}\n\n\nfunction normalizeEntryTag(tag) {\n if (tag.match(/^[17](?:00|10|11|30)$/u)) {\n return `X${tag.substring(1)}`;\n }\n return tag;\n}\n\nfunction subfieldToNormalizedString(sf, tag, targetLinkingNumber = 0, normalizeOccurrenceNumber = false, normalizeEntryTagBoolean = false) {\n // targetLinkingNumber refers to $8.\n // normalizeEntryTagBoolean refers to 1XX/7XX tag values in subfield $6 value.\n if (isValidSubfield6(sf)) { // && targetLinkingNumber === 0) {\n // 1XX/7XX (entry tag) normalization:\n const tag2 = normalizeEntryTagBoolean ? normalizeEntryTag(tag) : tag;\n\n const occurrenceNumber = normalizeOccurrenceNumber ? 'XX' : subfield6GetOccurrenceNumber(sf);\n // If we are normalizing a $8 chain, don't normalize $6 occurrence number!\n // Replace $6 occurrence number with XX:\n return ` \u2021${sf.code} ${tag2}-${occurrenceNumber}${subfield6GetTail(sf)}`;\n }\n\n if (isValidSubfield8(sf)) {\n const currLinkingNumber = getSubfield8LinkingNumber(sf); //getSubfield8Index(sf);\n if (targetLinkingNumber > 0 && currLinkingNumber === targetLinkingNumber) {\n // For $8 we should only XX the index we are looking at...\n const normVal = sf.value.replace(/^[0-9]+/u, 'XX');\n return ` \u2021${sf.code} ${normVal}`;\n }\n return ''; // Other $8 subfields are meaningless in this context\n }\n return ` ${subfieldToString(sf)}`; // `\u2021${sf.code} ${sf.value}`;\n}\n\nexport function fieldToNormalizedString(field, targetLinkingNumber = 0, normalizeOccurrenceNumber = false, normalizeEntryTagBoolean = false) {\n if ('subfields' in field) {\n const tag2 = normalizeEntryTagBoolean ? normalizeEntryTag(field.tag) : field.tag;\n return `${tag2} ${field.ind1}${field.ind2}${formatAndNormalizeSubfields(field)}`;\n }\n return `${field.tag} ${field.value}`;\n\n function formatAndNormalizeSubfields(field) {\n return field.subfields.map(sf => subfieldToNormalizedString(sf, field.tag, targetLinkingNumber, normalizeOccurrenceNumber, normalizeEntryTagBoolean)).join('');\n }\n\n}\n\n\nfunction guessTargetLinkingNumber(fields, defaultTargetLinkingNumber) {\n if (defaultTargetLinkingNumber !== 0) {\n return defaultTargetLinkingNumber;\n }\n const linkingNumbers = fieldsGetAllSubfield8LinkingNumbers(fields);\n return linkingNumbers.length === 1 ? linkingNumbers[0] : 0;\n}\n\nexport function fieldsToNormalizedString(fields, defaultTargetLinkingNumber = 0, normalizeOccurrenceNumber = false, normalizeEntryTag = false) {\n const targetLinkingNumber = guessTargetLinkingNumber(fields, defaultTargetLinkingNumber);\n\n //nvdebug(`fieldsToNormalizedString: OCC: ${normalizeOccurrenceNumber}`);\n const strings = fields.map(field => fieldToNormalizedString(field, targetLinkingNumber, normalizeOccurrenceNumber, normalizeEntryTag));\n strings.sort();\n return strings.join('\\t__SEPARATOR__\\t');\n}\n\n\n/*\n\nexport function removeField6IfNeeded(field, record, fieldsAsString) {\n const pairField = fieldGetSubfield6Pair(field, record);\n const asString = pairField ? fieldsToNormalizedString([field, pairField]) : fieldToNormalizedString(field);\n nvdebug(`SOURCE: ${asString} -- REALITY: ${fieldToString(field)}`);\n const tmp = pairField ? fieldToString(pairField) : 'HUTI';\n nvdebug(`PAIR: ${tmp}`);\n nvdebug(`BASE:\\n ${fieldsAsString.join('\\n ')}`);\n if (!fieldsAsString.includes(asString)) {\n return;\n }\n nvdebug(`Duplicate $6 removal: ${fieldToString(field)}`);\n record.removeField(field);\n\n if (pairField === undefined) {\n return;\n }\n nvdebug(`Duplicate $6 removal (pair): ${fieldToString(pairField)}`);\n record.removeField(pairField);\n}\n*/\n\nfunction getFirstField(record, fields) {\n const fieldsAsStrings = fields.map(field => fieldToString(field));\n //record.fields.forEach((field, i) => nvdebug(`${i}:\\t${fieldToString(field)}`));\n //nvdebug(`getFirstField: ${fieldsAsStrings.join('\\t')}`);\n const i = record.fields.findIndex(field => fieldsAsStrings.includes(fieldToString(field)));\n if (i > -1) {\n const field = record.fields[i];\n //nvdebug(`1st F: ${i + 1}/${record.fields.length} ${fieldToString(field)}`);\n return field;\n }\n return undefined;\n}\n\nfunction isRelevantSubfield6Chain(fields) {\n if (fields.length < 2) { // 1 non-880-field and 1+ 880 fields\n return false;\n }\n const non880 = fields.filter(f => f.tag !== '880');\n if (non880.length !== 1) {\n return false;\n }\n\n const linkingNumbers = fieldsGetAllSubfield8LinkingNumbers(fields);\n if (linkingNumbers.length !== 0) {\n return false;\n }\n\n return fields.every(f => fieldHasSubfield(f, '6'));\n}\n\nexport function fieldIsFirstFieldInChain(field, chain, record) {\n // Interpretation of first: position of field in record (however, we might have a duplicate field. See tests...)\n const firstField = getFirstField(record, chain);\n if (firstField) {\n return fieldToString(field) === fieldToString(firstField);\n }\n return false;\n\n}\n\n\nexport function getAllLinkedSubfield6Fields(field, record) {\n const fields = get6s(field, record);\n const moreFields = add8s(fields, record);\n\n // Currently we don't handle fields with more than one $6 and/or $8 subfield.\n if (moreFields.length > fields.length) {\n return []; // Don't fix!\n }\n return moreFields;\n}\n\nexport function isFirstLinkedSubfield6Field(field, record) {\n if (!field.subfields) { // Is not a datafield\n return false;\n }\n const chain = getAllLinkedSubfield6Fields(field, record);\n if (!isRelevantSubfield6Chain(chain)) {\n //nvdebug(`Rejected 6: ${fieldsToString(chain)}`);\n return false;\n }\n\n return fieldIsFirstFieldInChain(field, chain, record);\n}\n\nexport function recordGetSubfield6ChainHeads(record) {\n return record.fields.filter(field => isFirstLinkedSubfield6Field(field, record));\n}\n\nexport function recordGetMaxSubfield6OccurrenceNumberAsInteger(record) {\n if (record.fields.length === 0) {\n return 0;\n }\n // Should we cache the value here?\n const vals = record.fields.map((field) => fieldGetMaxSubfield6OccurrenceNumberAsInteger(field));\n return Math.max(...vals);\n}\n\nexport function get6s(field, candidateFields) { // NB! Convert field to fields!!!\n // Get all fields with given occurrence number\n const sixes = field.subfields.filter(sf => isValidSubfield6(sf));\n\n if (sixes.length === 0) {\n return [field];\n }\n //nvdebug(`SIXES: ${sixes.length}`);\n const occurrenceNumbers = sixes.map(sf => subfield6GetOccurrenceNumber(sf)).filter(value => value !== undefined && value !== '00');\n //nvdebug(occurrenceNumbers.join(' -- '));\n\n const relevantFields = candidateFields.filter(f => occurrenceNumbers.some(o => fieldHasOccurrenceNumber(f, o)));\n //nvdebug(`${fieldToString(field)}: $6-RELFIELDS FOUND: ${relevantFields.length}...`);\n //relevantFields.forEach(f => nvdebug(fieldToString(f)));\n return relevantFields;\n}\n\nexport function resetSubfield6Tag(subfield, tag) {\n if (!isValidSubfield6(subfield)) {\n return;\n }\n // NB! mainly for 1XX<->7XX transfers\n const newValue = `${tag}-${subfield.value.substring(4)}`;\n //nvdebug(`Set subfield $6 value from ${subfieldToString(subfield)} to ${newValue}`, debugDev);\n subfield.value = newValue;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import createDebugLogger from 'debug';\nimport {add8s, fieldsGetAllSubfield8LinkingNumbers, getSubfield8LinkingNumber, isValidSubfield8} from './subfield8Utils.js';\nimport {fieldHasSubfield, fieldToString, /* fieldsToString, */ nvdebug, subfieldToString} from './utils.js';\n\nconst debug = createDebugLogger('@natlibfi/melinda-marc-record-merge-reducers:subfield6Utils');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\n// NB! Subfield 6 is non-repeatable and it should always comes first!\n// NB! Index size should always be 2 (preceding 0 required for 01..09) However, support for 100+ was added on 2023-02-27.\n// NB! Index value '00' are left as they are (is not paired/indexed/whatever.\nconst sf6Regexp = /^[0-9][0-9][0-9]-(?:[0-9][0-9]|[1-9][0-9]+)(?:[^0-9].*)?$/u;\n\nexport function isValidSubfield6(subfield) {\n if (subfield.code !== '6') {\n return false;\n }\n return subfield.value.match(sf6Regexp);\n}\n\nexport function subfield6GetTag(subfield) {\n if (isValidSubfield6(subfield)) {\n return subfield.value.substring(0, 3);\n }\n return undefined;\n}\n\nexport function subfield6GetOccurrenceNumber(subfield) {\n if (isValidSubfield6(subfield)) {\n // Skip \"TAG-\" prefix. 2023-02-20: removed 2-digit requirement from here...\n return subfield.value.substring(4).replace(/\\D.*$/u, '');\n }\n return undefined;\n}\n\nexport function subfield6GetOccurrenceNumberAsInteger(subfield) {\n const index = subfield6GetOccurrenceNumber(subfield);\n if (index === undefined || index === '00') {\n return 0;\n }\n const result = parseInt(index, 10);\n //nvdebug(`SF6: ${subfield.value} => ${index} => ${result}`, debugDev, debugDev);\n return result;\n}\n\nexport function subfield6ResetOccurrenceNumber(subfield, occurrenceNumber) {\n if (!isValidSubfield6(subfield)) {\n return;\n }\n const occurrenceNumberAsString = typeof occurrenceNumber === 'number' ? intToOccurrenceNumberString(occurrenceNumber) : occurrenceNumber;\n\n const newValue = subfield.value.substring(0, 4) + occurrenceNumberAsString + subfield6GetTail(subfield);\n //nvdebug(`Set subfield $6 value from ${subfieldToString(subfield)} to ${newValue}`, debugDev);\n subfield.value = newValue;\n}\n\n\nfunction subfield6GetTail(subfield) {\n if (isValidSubfield6(subfield)) {\n // Skip \"TAG-\" prefix. 2023-02-20: removed 2-digit requirement from here...\n return subfield.value.replace(/^\\d+-\\d+/u, '');\n }\n return '';\n}\n\nexport function subfield6HasWantedTagAndOccurrenceNumber(subfield, tagAndOccurrenceNumber) {\n if (subfield.code !== '6') {\n return false;\n }\n // We could also use generic code and go getTag()+'-'+getIndex() instead of regexp...\n const key = subfield.value.replace(/^([0-9][0-9][0-9]-[0-9][0-9]+).*$/u, '$1');\n //nvdebug(` Compare '${key}' vs '${tagAndOccurrenceNumber}'`, debugDev);\n return key === tagAndOccurrenceNumber;\n}\n\n// <= SUBFIELD, FIELD =>\n\nexport function fieldGetUnambiguousTag(field) {\n const tags = field.subfields.filter(sf => subfield6GetTag(sf));\n if (tags.length === 1) {\n return subfield6GetTag(tags[0]);\n }\n return undefined;\n}\n\nexport function fieldGetUnambiguousOccurrenceNumber(field) {\n const occurrenceNumbers = field.subfields.filter(sf => subfield6GetOccurrenceNumber(sf));\n if (occurrenceNumbers.length === 1) {\n return subfield6GetOccurrenceNumber(occurrenceNumbers[0]);\n }\n return undefined;\n}\n\nexport function fieldHasOccurrenceNumber(field, occurrenceNumber) {\n //nvdebug(`${occurrenceNumber} vs ${fieldToString(field)}`, debugDev);\n return field.subfields && field.subfields.some(sf => subfield6GetOccurrenceNumber(sf) === occurrenceNumber);\n}\n\nexport function fieldResetOccurrenceNumber(field, newOccurrenceNumber, oldOccurrenceNumber = undefined) {\n field.subfields.forEach(subfield => innerReset(subfield));\n\n function innerReset(subfield) {\n // (Optional) Check that this is really the occurrence number we wan't to reseot\n if (oldOccurrenceNumber !== undefined) {\n const currOccurrenceNumber = subfield6GetOccurrenceNumber(subfield);\n if (currOccurrenceNumber !== oldOccurrenceNumber) {\n return;\n }\n }\n subfield6ResetOccurrenceNumber(subfield, newOccurrenceNumber);\n }\n}\n\nexport function intToOccurrenceNumberString(i) {\n return i < 10 ? `0${i}` : `${i}`;\n}\n\nexport function fieldGetMaxSubfield6OccurrenceNumberAsInteger(field) {\n // used by reducer!\n //nvdebug(`Checking subfields $6 from ${JSON.stringify(field)}`, debugDev);\n const sf6s = field.subfields ? field.subfields.filter(subfield => isValidSubfield6(subfield)) : [];\n if (sf6s.length === 0) {\n return 0;\n }\n // There should always be one, but here we check every subfield.\n //nvdebug(`Got ${field.subfields} $6-subfield(s) from ${JSON.stringify(field)}`, debugDev);\n const vals = sf6s.map(sf => subfield6GetOccurrenceNumberAsInteger(sf));\n return Math.max(...vals);\n}\n\nexport function fieldHasWantedTagAndOccurrenceNumber(field, tagAndOccurrenceNumber) {\n return field.subfields && field.subfields.some(sf => subfield6HasWantedTagAndOccurrenceNumber(sf, tagAndOccurrenceNumber));\n}\n\n\n/*\nexport function getFieldsWithGivenOccurrenceNumberSubfield6(record, occurrenceNumberAsString) {\n const record.fields.filter(field => field\n\n function fieldHasIndex(field, index) {\n if (!field.subfields) {\n return false;\n }\n return field.subfields.find(sf => isValidSubfield6(sf) && subfieldGetOccurrenceNumber6(sf) === index);\n }\n}\n*/\n\n\nexport function fieldHasValidSubfield6(field) {\n return field.subfields && field.subfields.some(sf => isValidSubfield6(sf));\n}\n\nexport function isSubfield6Pair(field, otherField) {\n // No need to log this:\n //nvdebug(`LOOK for $6-pair:\\n ${fieldToString(field)}\\n ${fieldToString(otherField)}`, debugDev);\n if (!fieldHasValidSubfield6(field) || !fieldHasValidSubfield6(otherField)) {\n return false;\n }\n\n if (!tagsArePairable6(field.tag, otherField.tag)) {\n //nvdebug(` FAILED. REASON: TAGS NOT PAIRABLE!`, debugDev);\n return false;\n }\n\n\n const fieldIndex = fieldGetUnambiguousOccurrenceNumber(field);\n if (fieldIndex === undefined || fieldIndex === '00') {\n //nvdebug(` FAILED. REASON: NO INDEX FOUND`, debugDev);\n return false;\n }\n\n const otherFieldIndex = fieldGetUnambiguousOccurrenceNumber(otherField);\n\n\n if (fieldIndex !== otherFieldIndex) {\n //nvdebug(` FAILURE: INDEXES: ${fieldIndex} vs ${otherFieldIndex}`, debugDev);\n return false;\n }\n\n if (fieldGetUnambiguousTag(field) !== otherField.tag || field.tag !== fieldGetUnambiguousTag(otherField)) {\n //nvdebug(` FAILURE: TAG vs $6 TAG`, debugDev);\n return false;\n }\n return true;\n\n function tagsArePairable6(tag1, tag2) {\n // How to do XOR operation in one line? Well, this is probably more readable...\n if (tag1 === '880' && tag2 === '880') {\n return false;\n }\n if (tag1 !== '880' && tag2 !== '880') {\n return false;\n }\n return true;\n }\n}\n\n\nfunction subfieldSevenToOneOccurrenceNumber(subfield) {\n if (subfield.code !== '6' || subfield.value.substring(0, 1) !== '7') {\n return;\n }\n subfield.value = `1${subfield.value.substring(1)}`;\n}\n\nexport function fieldSevenToOneOccurrenceNumber(field) {\n if (field.tag !== '880') {\n return;\n }\n field.subfields.forEach(sf => subfieldSevenToOneOccurrenceNumber(sf));\n}\n\n\nexport function fieldGetOccurrenceNumberPairs(field, candFields) {\n // NB! TAG!=880 returns 880 fields, TAG==880 returns non-880 field\n //nvdebug(` Trying to finds pair for ${fieldToString(field)} in ${candFields.length} fields`);\n const pairs = candFields.filter(otherField => isSubfield6Pair(field, otherField));\n if (pairs.length === 0) {\n //nvdebug(`NO PAIRS FOUND FOR '${fieldToString(field)}'`, debugDev);\n return pairs;\n }\n //nvdebug(`${pairs.length} PAIR(S) FOUND FOR '${fieldToString(field)}'`, debugDev);\n pairs.forEach(pairedField => nvdebug(` '${fieldToString(pairedField)}'`, debugDev));\n return pairs;\n}\n\nexport function fieldGetOccurrenceNumbers(field) {\n let occurrenceNumbers = [];\n field.subfields?.forEach(sf => subfieldExtractOccurrenceNumber(sf));\n\n function subfieldExtractOccurrenceNumber(sf) {\n if (!isValidSubfield6(sf)) {\n return;\n }\n const occurrenceNumber = subfield6GetOccurrenceNumber(sf);\n if (occurrenceNumber === '00' || occurrenceNumbers.includes(occurrenceNumber)) {\n return;\n }\n occurrenceNumbers.push(occurrenceNumber);\n }\n return occurrenceNumbers;\n}\n\nexport function fieldsGetOccurrenceNumbers(fields) {\n let occurrenceNumbers = [];\n\n fields.forEach(f => fieldProcessOccurrenceNumbers(f));\n\n function fieldProcessOccurrenceNumbers(f) {\n const newOccurrenceNumbers = fieldGetOccurrenceNumbers(f);\n newOccurrenceNumbers.forEach(occurrenceNumber => {\n if (!occurrenceNumbers.includes(occurrenceNumber)) {\n occurrenceNumbers.push(occurrenceNumber);\n }\n\n });\n }\n return occurrenceNumbers;\n}\n\n/*\nexport function fieldGetSubfield6Pair(field, record) {\n const pairedFields = record.fields.filter(otherField => isSubfield6Pair(field, otherField));\n if (pairedFields.length !== 1) {\n return undefined;\n }\n // NB! It is theoretically possible to have multiple pairable 880 fields (one for each encoding)\n nvdebug(`fieldGetSubfield6Pair(): ${fieldToString(field)} => ${fieldToString(pairedFields[0])}`, debugDev);\n return pairedFields[0];\n}\n*/\n\n/*\nexport function pairAndStringify6(field, record) {\n const pair6 = fieldGetSubfield6Pair(field, record);\n if (!pair6) {\n return fieldToNormalizedString(field);\n }\n return fieldsToNormalizedString([field, pair6]);\n}\n*/\n\n// Frequencly list for $6 subfields in 1XX/7XX fields:\n// 231115 100\n// 183832 700\n// 28773 710\n// 2047 711\n// 661 110\n// 341 111\n// 284 130\n// 63 730\n// Thus there's a real risk of ending up with, say, identical 100 vs 700 chains.\n// Semi-hackily support 1XX/7XX-version: 7XX can be deleted if corresponding 1XX exists:\n\nexport function is7XX(tag) {\n return ['700', '710', '711', '730'].includes(tag);\n}\n\n\nfunction normalizeEntryTag(tag) {\n if (tag.match(/^[17](?:00|10|11|30)$/u)) {\n return `X${tag.substring(1)}`;\n }\n return tag;\n}\n\nfunction subfieldToNormalizedString(sf, tag, targetLinkingNumber = 0, normalizeOccurrenceNumber = false, normalizeEntryTagBoolean = false) {\n // targetLinkingNumber refers to $8.\n // normalizeEntryTagBoolean refers to 1XX/7XX tag values in subfield $6 value.\n if (isValidSubfield6(sf)) { // && targetLinkingNumber === 0) {\n // 1XX/7XX (entry tag) normalization:\n const tag2 = normalizeEntryTagBoolean ? normalizeEntryTag(tag) : tag;\n\n const occurrenceNumber = normalizeOccurrenceNumber ? 'XX' : subfield6GetOccurrenceNumber(sf);\n // If we are normalizing a $8 chain, don't normalize $6 occurrence number!\n // Replace $6 occurrence number with XX:\n return ` \u2021${sf.code} ${tag2}-${occurrenceNumber}${subfield6GetTail(sf)}`;\n }\n\n if (isValidSubfield8(sf)) {\n const currLinkingNumber = getSubfield8LinkingNumber(sf); //getSubfield8Index(sf);\n if (targetLinkingNumber > 0 && currLinkingNumber === targetLinkingNumber) {\n // For $8 we should only XX the index we are looking at...\n const normVal = sf.value.replace(/^[0-9]+/u, 'XX');\n return ` \u2021${sf.code} ${normVal}`;\n }\n return ''; // Other $8 subfields are meaningless in this context\n }\n return ` ${subfieldToString(sf)}`; // `\u2021${sf.code} ${sf.value}`;\n}\n\nexport function fieldToNormalizedString(field, targetLinkingNumber = 0, normalizeOccurrenceNumber = false, normalizeEntryTagBoolean = false) {\n if ('subfields' in field) {\n const tag2 = normalizeEntryTagBoolean ? normalizeEntryTag(field.tag) : field.tag;\n return `${tag2} ${field.ind1}${field.ind2}${formatAndNormalizeSubfields(field)}`;\n }\n return `${field.tag} ${field.value}`;\n\n function formatAndNormalizeSubfields(field) {\n return field.subfields.map(sf => subfieldToNormalizedString(sf, field.tag, targetLinkingNumber, normalizeOccurrenceNumber, normalizeEntryTagBoolean)).join('');\n }\n\n}\n\n\nfunction guessTargetLinkingNumber(fields, defaultTargetLinkingNumber) {\n if (defaultTargetLinkingNumber !== 0) {\n return defaultTargetLinkingNumber;\n }\n const linkingNumbers = fieldsGetAllSubfield8LinkingNumbers(fields);\n return linkingNumbers.length === 1 ? linkingNumbers[0] : 0;\n}\n\nexport function fieldsToNormalizedString(fields, defaultTargetLinkingNumber = 0, normalizeOccurrenceNumber = false, normalizeEntryTag = false) {\n const targetLinkingNumber = guessTargetLinkingNumber(fields, defaultTargetLinkingNumber);\n\n //nvdebug(`fieldsToNormalizedString: OCC: ${normalizeOccurrenceNumber}`, debugDev);\n const strings = fields.map(field => fieldToNormalizedString(field, targetLinkingNumber, normalizeOccurrenceNumber, normalizeEntryTag));\n strings.sort();\n return strings.join('\\t__SEPARATOR__\\t');\n}\n\n\n/*\n\nexport function removeField6IfNeeded(field, record, fieldsAsString) {\n const pairField = fieldGetSubfield6Pair(field, record);\n const asString = pairField ? fieldsToNormalizedString([field, pairField]) : fieldToNormalizedString(field);\n nvdebug(`SOURCE: ${asString} -- REALITY: ${fieldToString(field)}`, debugDev);\n const tmp = pairField ? fieldToString(pairField) : 'HUTI';\n nvdebug(`PAIR: ${tmp}`, debugDev);\n nvdebug(`BASE:\\n ${fieldsAsString.join('\\n ')}`, debugDev);\n if (!fieldsAsString.includes(asString)) {\n return;\n }\n nvdebug(`Duplicate $6 removal: ${fieldToString(field)}`, debugDev);\n record.removeField(field);\n\n if (pairField === undefined) {\n return;\n }\n nvdebug(`Duplicate $6 removal (pair): ${fieldToString(pairField)}`, debugDev);\n record.removeField(pairField);\n}\n*/\n\nfunction getFirstField(record, fields) {\n const fieldsAsStrings = fields.map(field => fieldToString(field));\n //record.fields.forEach((field, i) => nvdebug(`${i}:\\t${fieldToString(field)}`, debugDev));\n //nvdebug(`getFirstField: ${fieldsAsStrings.join('\\t')}`, debugDev);\n const i = record.fields.findIndex(field => fieldsAsStrings.includes(fieldToString(field)));\n if (i > -1) {\n const field = record.fields[i];\n //nvdebug(`1st F: ${i + 1}/${record.fields.length} ${fieldToString(field)}`, debugDev);\n return field;\n }\n return undefined;\n}\n\nfunction isRelevantSubfield6Chain(fields) {\n if (fields.length < 2) { // 1 non-880-field and 1+ 880 fields\n return false;\n }\n const non880 = fields.filter(f => f.tag !== '880');\n if (non880.length !== 1) {\n return false;\n }\n\n const linkingNumbers = fieldsGetAllSubfield8LinkingNumbers(fields);\n if (linkingNumbers.length !== 0) {\n return false;\n }\n\n return fields.every(f => fieldHasSubfield(f, '6'));\n}\n\nexport function fieldIsFirstFieldInChain(field, chain, record) {\n // Interpretation of first: position of field in record (however, we might have a duplicate field. See tests...)\n const firstField = getFirstField(record, chain);\n if (firstField) {\n return fieldToString(field) === fieldToString(firstField);\n }\n return false;\n\n}\n\n\nexport function getAllLinkedSubfield6Fields(field, record) {\n const fields = get6s(field, record);\n const moreFields = add8s(fields, record);\n\n // Currently we don't handle fields with more than one $6 and/or $8 subfield.\n if (moreFields.length > fields.length) {\n return []; // Don't fix!\n }\n return moreFields;\n}\n\nexport function isFirstLinkedSubfield6Field(field, record) {\n if (!field.subfields) { // Is not a datafield\n return false;\n }\n const chain = getAllLinkedSubfield6Fields(field, record);\n if (!isRelevantSubfield6Chain(chain)) {\n //nvdebug(`Rejected 6: ${fieldsToString(chain)}`, debugDev);\n return false;\n }\n\n return fieldIsFirstFieldInChain(field, chain, record);\n}\n\nexport function recordGetSubfield6ChainHeads(record) {\n return record.fields.filter(field => isFirstLinkedSubfield6Field(field, record));\n}\n\nexport function recordGetMaxSubfield6OccurrenceNumberAsInteger(record) {\n if (record.fields.length === 0) {\n return 0;\n }\n // Should we cache the value here?\n const vals = record.fields.map((field) => fieldGetMaxSubfield6OccurrenceNumberAsInteger(field));\n return Math.max(...vals);\n}\n\nexport function get6s(field, candidateFields) { // NB! Convert field to fields!!!\n // Get all fields with given occurrence number\n const sixes = field.subfields.filter(sf => isValidSubfield6(sf));\n\n if (sixes.length === 0) {\n return [field];\n }\n //nvdebug(`SIXES: ${sixes.length}`, debugDev);\n const occurrenceNumbers = sixes.map(sf => subfield6GetOccurrenceNumber(sf)).filter(value => value !== undefined && value !== '00');\n //nvdebug(occurrenceNumbers.join(' -- '), debugDev);\n\n const relevantFields = candidateFields.filter(f => occurrenceNumbers.some(o => fieldHasOccurrenceNumber(f, o)));\n //nvdebug(`${fieldToString(field)}: $6-RELFIELDS FOUND: ${relevantFields.length}...`, debugDev);\n //relevantFields.forEach(f => nvdebug(fieldToString(f), debugDev));\n return relevantFields;\n}\n\nexport function resetSubfield6Tag(subfield, tag) {\n if (!isValidSubfield6(subfield)) {\n return;\n }\n // NB! mainly for 1XX<->7XX transfers\n const newValue = `${tag}-${subfield.value.substring(4)}`;\n //nvdebug(`Set subfield $6 value from ${subfieldToString(subfield)} to ${newValue}`, debugDevDev);\n subfield.value = newValue;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["field", "normalizeEntryTag"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/subfield8Utils.js"],
4
- "sourcesContent": ["// import createDebugLogger from 'debug';\n// const debug = createDebugLogger('@natlibfi/marc-record-validator-melinda/subfield8Utils');\n\n// import {fieldToString, nvdebug} from './utils.js';\n\nconst sf8Regexp = /^([1-9][0-9]*)(?:\\.[0-9]+)?(?:\\\\[acprux])?$/u;\n\nexport function isValidSubfield8(subfield) {\n if (subfield.code !== '8') {\n return false;\n }\n\n //nvdebug(` IS VALID $8? '${subfieldToString(subfield)}'`);\n const match = subfield.value.match(sf8Regexp);\n //nvdebug(` IS VALID $8? '${subfieldToString(subfield)}' vs ${match.length}}`);\n return match && match.length > 0;\n}\n\nexport function getSubfield8Value(subfield) {\n if (!isValidSubfield8(subfield)) {\n return undefined;\n }\n return subfield.value;\n}\n\nexport function getSubfield8LinkingNumber(subfield) {\n const value = getSubfield8Value(subfield);\n if (value === undefined) {\n return 0;\n }\n return parseInt(value, 10);\n}\n\n\nexport function fieldHasLinkingNumber(field, linkingNumber) {\n if (!field.subfields) {\n return false;\n }\n return field.subfields.some(sf => getSubfield8LinkingNumber(sf) === linkingNumber);\n}\n\nexport function recordGetFieldsWithSubfield8LinkingNumber(record, linkingNumber) {\n if (linkingNumber < 1) {\n return;\n }\n return record.fields.filter(field => fieldHasLinkingNumber(field, linkingNumber));\n}\n\n\nexport function fieldsGetAllSubfield8LinkingNumbers(fields) {\n let subfield8LinkingNumbers = [];\n fields.forEach(field => {\n if (!field.subfields) {\n return;\n }\n field.subfields.forEach(sf => {\n const linkingNumber = getSubfield8LinkingNumber(sf);\n if (linkingNumber > 0 && !subfield8LinkingNumbers.includes(linkingNumber)) {\n //nvdebug(` LINK8: Add subfield \\$8 ${linkingNumber} to seen values list`);\n subfield8LinkingNumbers.push(linkingNumber);\n }\n });\n });\n\n return subfield8LinkingNumbers;\n}\n\nexport function recordGetAllSubfield8LinkingNumbers(record) {\n return fieldsGetAllSubfield8LinkingNumbers(record.fields);\n}\n\n\nexport function add8s(fields, record) {\n const linkingNumbers = fieldsGetAllSubfield8LinkingNumbers(fields);\n if (linkingNumbers.length === 0) {\n return fields;\n }\n\n //nvdebug(`Linking number(s): ${linkingNumbers.join(', ')}`);\n linkingNumbers.forEach(number => collectLinkingNumberFields(number));\n\n //fields.forEach(f => nvdebug(`AFTER ADDING 8s: '${fieldToString(f)}'`));\n\n return fields;\n\n function collectLinkingNumberFields(linkingNumber) {\n // Remove existing hits (to avoid field repetition):\n fields = fields.filter(f => !fieldHasLinkingNumber(f, linkingNumber));\n // Add them and their \"sisters\" back:\n const addableFields = record.fields.filter(f => fieldHasLinkingNumber(f, linkingNumber));\n //addableFields.forEach(f => nvdebug(`(RE-?)ADD ${fieldToString(f)}`));\n fields = fields.concat(addableFields);\n\n }\n}\n\nexport function fieldHasValidSubfield8(field) {\n return field.subfields && field.subfields.some(sf => isValidSubfield8(sf));\n}\n"],
5
- "mappings": "AAKA,MAAM,YAAY;AAEX,gBAAS,iBAAiB,UAAU;AACzC,MAAI,SAAS,SAAS,KAAK;AACzB,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,SAAS,MAAM,MAAM,SAAS;AAE5C,SAAO,SAAS,MAAM,SAAS;AACjC;AAEO,gBAAS,kBAAkB,UAAU;AAC1C,MAAI,CAAC,iBAAiB,QAAQ,GAAG;AAC/B,WAAO;AAAA,EACT;AACA,SAAO,SAAS;AAClB;AAEO,gBAAS,0BAA0B,UAAU;AAClD,QAAM,QAAQ,kBAAkB,QAAQ;AACxC,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AACA,SAAO,SAAS,OAAO,EAAE;AAC3B;AAGO,gBAAS,sBAAsB,OAAO,eAAe;AAC1D,MAAI,CAAC,MAAM,WAAW;AACpB,WAAO;AAAA,EACT;AACA,SAAO,MAAM,UAAU,KAAK,QAAM,0BAA0B,EAAE,MAAM,aAAa;AACnF;AAEO,gBAAS,0CAA0C,QAAQ,eAAe;AAC/E,MAAI,gBAAgB,GAAG;AACrB;AAAA,EACF;AACA,SAAO,OAAO,OAAO,OAAO,WAAS,sBAAsB,OAAO,aAAa,CAAC;AAClF;AAGO,gBAAS,oCAAoC,QAAQ;AAC1D,MAAI,0BAA0B,CAAC;AAC/B,SAAO,QAAQ,WAAS;AACtB,QAAI,CAAC,MAAM,WAAW;AACpB;AAAA,IACF;AACA,UAAM,UAAU,QAAQ,QAAM;AAC5B,YAAM,gBAAgB,0BAA0B,EAAE;AAClD,UAAI,gBAAgB,KAAK,CAAC,wBAAwB,SAAS,aAAa,GAAG;AAEzE,gCAAwB,KAAK,aAAa;AAAA,MAC5C;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;AAEO,gBAAS,oCAAoC,QAAQ;AAC1D,SAAO,oCAAoC,OAAO,MAAM;AAC1D;AAGO,gBAAS,MAAM,QAAQ,QAAQ;AACpC,QAAM,iBAAiB,oCAAoC,MAAM;AACjE,MAAI,eAAe,WAAW,GAAG;AAC/B,WAAO;AAAA,EACT;AAGA,iBAAe,QAAQ,YAAU,2BAA2B,MAAM,CAAC;AAInE,SAAO;AAEP,WAAS,2BAA2B,eAAe;AAEjD,aAAS,OAAO,OAAO,OAAK,CAAC,sBAAsB,GAAG,aAAa,CAAC;AAEpE,UAAM,gBAAgB,OAAO,OAAO,OAAO,OAAK,sBAAsB,GAAG,aAAa,CAAC;AAEvF,aAAS,OAAO,OAAO,aAAa;AAAA,EAEtC;AACF;AAEO,gBAAS,uBAAuB,OAAO;AAC5C,SAAO,MAAM,aAAa,MAAM,UAAU,KAAK,QAAM,iBAAiB,EAAE,CAAC;AAC3E;",
4
+ "sourcesContent": ["// import createDebugLogger from 'debug';\n// import {fieldToString, nvdebug} from './utils.js';\n\n// const debug = createDebugLogger('@natlibfi/marc-record-validator-melinda/subfield8Utils');\n//const debugData = debug.extend('data');\n//const debugDev = debug.extend('dev');\n\n\nconst sf8Regexp = /^([1-9][0-9]*)(?:\\.[0-9]+)?(?:\\\\[acprux])?$/u;\n\nexport function isValidSubfield8(subfield) {\n if (subfield.code !== '8') {\n return false;\n }\n\n //nvdebug(` IS VALID $8? '${subfieldToString(subfield)}'`, debugDev);\n const match = subfield.value.match(sf8Regexp);\n //nvdebug(` IS VALID $8? '${subfieldToString(subfield)}' vs ${match.length}}`, debugDev);\n return match && match.length > 0;\n}\n\nexport function getSubfield8Value(subfield) {\n if (!isValidSubfield8(subfield)) {\n return undefined;\n }\n return subfield.value;\n}\n\nexport function getSubfield8LinkingNumber(subfield) {\n const value = getSubfield8Value(subfield);\n if (value === undefined) {\n return 0;\n }\n return parseInt(value, 10);\n}\n\n\nexport function fieldHasLinkingNumber(field, linkingNumber) {\n if (!field.subfields) {\n return false;\n }\n return field.subfields.some(sf => getSubfield8LinkingNumber(sf) === linkingNumber);\n}\n\nexport function recordGetFieldsWithSubfield8LinkingNumber(record, linkingNumber) {\n if (linkingNumber < 1) {\n return;\n }\n return record.fields.filter(field => fieldHasLinkingNumber(field, linkingNumber));\n}\n\n\nexport function fieldsGetAllSubfield8LinkingNumbers(fields) {\n let subfield8LinkingNumbers = [];\n fields.forEach(field => {\n if (!field.subfields) {\n return;\n }\n field.subfields.forEach(sf => {\n const linkingNumber = getSubfield8LinkingNumber(sf);\n if (linkingNumber > 0 && !subfield8LinkingNumbers.includes(linkingNumber)) {\n //nvdebug(` LINK8: Add subfield \\$8 ${linkingNumber} to seen values list`, debugDev);\n subfield8LinkingNumbers.push(linkingNumber);\n }\n });\n });\n\n return subfield8LinkingNumbers;\n}\n\nexport function recordGetAllSubfield8LinkingNumbers(record) {\n return fieldsGetAllSubfield8LinkingNumbers(record.fields);\n}\n\n\nexport function add8s(fields, record) {\n const linkingNumbers = fieldsGetAllSubfield8LinkingNumbers(fields);\n if (linkingNumbers.length === 0) {\n return fields;\n }\n\n //nvdebug(`Linking number(s): ${linkingNumbers.join(', ')}`, debugDev);\n linkingNumbers.forEach(number => collectLinkingNumberFields(number));\n\n //fields.forEach(f => nvdebug(`AFTER ADDING 8s: '${fieldToString(f)}'`, debugDev));\n\n return fields;\n\n function collectLinkingNumberFields(linkingNumber) {\n // Remove existing hits (to avoid field repetition):\n fields = fields.filter(f => !fieldHasLinkingNumber(f, linkingNumber));\n // Add them and their \"sisters\" back:\n const addableFields = record.fields.filter(f => fieldHasLinkingNumber(f, linkingNumber));\n //addableFields.forEach(f => nvdebug(`(RE-?)ADD ${fieldToString(f)}`, debugDev));\n fields = fields.concat(addableFields);\n\n }\n}\n\nexport function fieldHasValidSubfield8(field) {\n return field.subfields && field.subfields.some(sf => isValidSubfield8(sf));\n}\n"],
5
+ "mappings": "AAQA,MAAM,YAAY;AAEX,gBAAS,iBAAiB,UAAU;AACzC,MAAI,SAAS,SAAS,KAAK;AACzB,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,SAAS,MAAM,MAAM,SAAS;AAE5C,SAAO,SAAS,MAAM,SAAS;AACjC;AAEO,gBAAS,kBAAkB,UAAU;AAC1C,MAAI,CAAC,iBAAiB,QAAQ,GAAG;AAC/B,WAAO;AAAA,EACT;AACA,SAAO,SAAS;AAClB;AAEO,gBAAS,0BAA0B,UAAU;AAClD,QAAM,QAAQ,kBAAkB,QAAQ;AACxC,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AACA,SAAO,SAAS,OAAO,EAAE;AAC3B;AAGO,gBAAS,sBAAsB,OAAO,eAAe;AAC1D,MAAI,CAAC,MAAM,WAAW;AACpB,WAAO;AAAA,EACT;AACA,SAAO,MAAM,UAAU,KAAK,QAAM,0BAA0B,EAAE,MAAM,aAAa;AACnF;AAEO,gBAAS,0CAA0C,QAAQ,eAAe;AAC/E,MAAI,gBAAgB,GAAG;AACrB;AAAA,EACF;AACA,SAAO,OAAO,OAAO,OAAO,WAAS,sBAAsB,OAAO,aAAa,CAAC;AAClF;AAGO,gBAAS,oCAAoC,QAAQ;AAC1D,MAAI,0BAA0B,CAAC;AAC/B,SAAO,QAAQ,WAAS;AACtB,QAAI,CAAC,MAAM,WAAW;AACpB;AAAA,IACF;AACA,UAAM,UAAU,QAAQ,QAAM;AAC5B,YAAM,gBAAgB,0BAA0B,EAAE;AAClD,UAAI,gBAAgB,KAAK,CAAC,wBAAwB,SAAS,aAAa,GAAG;AAEzE,gCAAwB,KAAK,aAAa;AAAA,MAC5C;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;AAEO,gBAAS,oCAAoC,QAAQ;AAC1D,SAAO,oCAAoC,OAAO,MAAM;AAC1D;AAGO,gBAAS,MAAM,QAAQ,QAAQ;AACpC,QAAM,iBAAiB,oCAAoC,MAAM;AACjE,MAAI,eAAe,WAAW,GAAG;AAC/B,WAAO;AAAA,EACT;AAGA,iBAAe,QAAQ,YAAU,2BAA2B,MAAM,CAAC;AAInE,SAAO;AAEP,WAAS,2BAA2B,eAAe;AAEjD,aAAS,OAAO,OAAO,OAAK,CAAC,sBAAsB,GAAG,aAAa,CAAC;AAEpE,UAAM,gBAAgB,OAAO,OAAO,OAAO,OAAK,sBAAsB,GAAG,aAAa,CAAC;AAEvF,aAAS,OAAO,OAAO,aAAa;AAAA,EAEtC;AACF;AAEO,gBAAS,uBAAuB,OAAO;AAC5C,SAAO,MAAM,aAAa,MAAM,UAAU,KAAK,QAAM,iBAAiB,EAAE,CAAC;AAC3E;",
6
6
  "names": []
7
7
  }
@@ -2,6 +2,7 @@ import clone from "clone";
2
2
  import createDebugLogger from "debug";
3
3
  import { fieldHasSubfield, fieldToString, nvdebug } from "./utils.js";
4
4
  const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda:translate-terms");
5
+ const debugDev = debug.extend("dev");
5
6
  const defaultTags = ["648", "650", "651", "655"];
6
7
  const swapLanguageCode = { "fin": "swe", "fi": "sv", "sv": "fi", "swe": "fin" };
7
8
  const changeAbbrHash = { "fi": "fin", "fin": "fi", "sv": "swe", "swe": "sv" };
@@ -14,7 +15,7 @@ export default function() {
14
15
  };
15
16
  async function fix(record) {
16
17
  const newFields = await getFields(record, defaultTags, []);
17
- newFields.forEach((nf) => nvdebug(`Add new field '${fieldToString(nf)}'`, debug));
18
+ newFields.forEach((nf) => nvdebug(`Add new field '${fieldToString(nf)}'`, debugDev));
18
19
  newFields.forEach((nf) => record.insertField(nf));
19
20
  const newFieldsAsStrings = newFields.map((f) => fieldToString(f));
20
21
  return { message: [], fix: newFieldsAsStrings, valid: true };
@@ -81,7 +82,7 @@ export default function() {
81
82
  }
82
83
  const data = await getTermData(uri);
83
84
  if (!data) {
84
- nvdebug(`No labels found for ${uri}`, debug);
85
+ nvdebug(`No labels found for ${uri}`, debugDev);
85
86
  return void 0;
86
87
  }
87
88
  const prefLabels = data.prefLabel;
@@ -151,7 +152,7 @@ function mapTagToLex(tag) {
151
152
  return "yso";
152
153
  }
153
154
  export async function getTermData(uri) {
154
- nvdebug(`getTermData(${uri})`);
155
+ nvdebug(`getTermData(${uri})`, debugDev);
155
156
  if (termCache[uri]) {
156
157
  return termCache[uri];
157
158
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/translate-terms.js"],
4
- "sourcesContent": ["import clone from 'clone';\nimport createDebugLogger from 'debug';\nimport {fieldHasSubfield, fieldToString, nvdebug} from './utils.js';\n\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:translate-terms');\nconst defaultTags = ['648', '650', '651', '655'];\n\nconst swapLanguageCode = {'fin': 'swe', 'fi': 'sv', 'sv': 'fi', 'swe': 'fin'};\nconst changeAbbrHash = {'fi': 'fin', 'fin': 'fi', 'sv': 'swe', 'swe': 'sv'};\n\nconst termCache = {};\n\n// Author(s): Nicholas Volk\nexport default function () {\n\n\n return {\n description: 'Translate yso (648, 650, 651) and slm (655) terms (FIN <=> SWE)',\n validate, fix\n };\n\n async function fix(record) {\n const newFields = await getFields(record, defaultTags, []);\n\n newFields.forEach(nf => nvdebug(`Add new field '${fieldToString(nf)}'`, debug));\n\n newFields.forEach(nf => record.insertField(nf));\n\n const newFieldsAsStrings = newFields.map(f => fieldToString(f));\n\n\n return {message: [], fix: newFieldsAsStrings, valid: true};\n }\n\n async function validate(record) {\n const newFields = await getFields(record, defaultTags, []);\n if (newFields.length === 0) {\n return {'message': [], 'valid': true};\n }\n const messages = newFields.map(f => fieldToString(f));\n\n return {'message': messages, 'valid': false};\n }\n\n async function getFields(record, tags, fieldsToAdd) {\n const [currTag, ...remainingTags] = tags;\n if (!currTag) {\n return fieldsToAdd;\n }\n const missingFields = await deriveMissingFields(record, currTag);\n\n const tmp = await getFields(record, remainingTags, [...fieldsToAdd, ...missingFields]);\n return tmp;\n }\n\n function getPairlessFinnishAndSwedishFields(record, tag) {\n const expectedLex = mapTagToLex(tag);\n if (!expectedLex) {\n return [];\n }\n const fields = record.get(tag);\n const finnishFields = fields.filter(f => isTranslatable(f, 'fin'));\n const swedishFields = fields.filter(f => isTranslatable(f, 'swe'));\n const finnishOnly = getMisses(finnishFields, swedishFields);\n const swedishOnly = getMisses(swedishFields, finnishFields);\n\n //console.log(` Looking at ${finnishOnly.length} + ${swedishOnly.length} fields`); // eslint-disable-line no-console\n return [...finnishOnly, ...swedishOnly].filter(f => tagAndFieldAgree(f));\n\n function tagAndFieldAgree(field) {\n // Check that tag and $2 value are pairable:\n const lexData = getLexiconAndLanguage(field); // $2 data\n return expectedLex === lexData.lex;\n }\n }\n\n async function deriveMissingFields(record, tag) {\n const pairlessFields = getPairlessFinnishAndSwedishFields(record, tag);\n\n // Dunno how to handle loop+promise combo in our normal coding style. Spent half a day trying... (I reckon it takes like 2 minuts to do this properly...)\n // Did a proper implementation in drop-terms.js...\n let prefLabels = [];\n for (let i=0; i < pairlessFields.length; i += 1) {\n prefLabels[i] = await getPrefLabel(pairlessFields[i]);\n }\n\n const missingFields = pairlessFields.map((f, i) => pairField(f, prefLabels[i]));\n return missingFields.filter(f => f);\n }\n\n function pairField(field, prefLabels) {\n if (!prefLabels) {\n return undefined;\n }\n //console.log(`pairField() WP 1: ${fieldToString(field)}`); // eslint-disable-line no-console\n const lexAndLang = getLexiconAndLanguage(field);\n //console.log(`pairField() WP 2: ${JSON.stringify(lexAndLang)}`); // eslint-disable-line no-console\n const twoLetterOtherLang = swapLanguageCodeBetweenLanguages(changeAbbr(lexAndLang.lang));\n const prefLabel = prefLabels.find(l => l.lang === twoLetterOtherLang);\n //console.log(`pairField() WP 4: ${JSON.stringify(prefLabel)}`); // eslint-disable-line no-console\n const sfA = {'code': 'a', 'value': prefLabel.value}; // field.subfields.field(sf => sf.code === 'a');\n const sf0 = clone(field.subfields.find(sf => sf.code === '0'));\n const sf2 = {'code': '2', 'value': `${lexAndLang.lex}/${lexAndLang.lang === 'fin' ? 'swe' : 'fin'}`}; // swap fin <=> swe\n const newField = {tag: field.tag, ind1: field.ind1, ind2: field.ind2, subfields: [sfA, sf2, sf0]};\n return newField;\n }\n\n\n\n async function getPrefLabel(field) {\n // Pre-requisite: tag vs $2 correlation has already been checked!\n const uri = fieldToUri(field);\n if (!uri) { // $0 is invalid or sumthing\n return undefined;\n }\n\n const data = await getTermData(uri);\n\n if (!data) { // Sanity check. Miss caused by illegal id etc.\n nvdebug(`No labels found for ${uri}`, debug);\n return undefined;\n }\n\n const prefLabels = data.prefLabel;\n\n const lexData = getLexiconAndLanguage(field); // $2 data\n const lang = changeAbbr(lexData.lang); // fi <=> fin (finto use 2 chars, we use 3-letters)\n const subfieldA = field.subfields.find(sf => sf.code === 'a');\n\n if (isLabel(prefLabels, subfieldA.value, lang)) {\n return prefLabels;\n }\n return undefined;\n }\n\n\n function fieldToUri(field) {\n const lex = mapTagToLex(field.tag);\n\n const subfield0 = field.subfields.find(sf => sf.code === '0');\n const id = subfield0.value.replace(/^[^0-9]+/u, '');\n return buildUri(lex, id);\n }\n\n function isTranslatable(field, lang) {\n const fieldAsString = fieldToString(field);\n\n // We should probably allow an optional $8 as the first subfield.\n if (!fieldAsString.match(/^... #7 (?: \u20218 [^\u2021]+ )*\u2021a [^\u2021]+ \u20212 [^\u2021]+ \u20210 [^\u2021]+(?: \u20219 [A-Z]+<(?:KEEP|DROP)>)*$/u)) {\n return false;\n }\n const lex = mapTagToLex(field.tag);\n const lexLang = `${lex}/${lang}`;\n if (!fieldHasSubfield(field, '2', lexLang)) {\n return false;\n }\n return fieldHasValidSubfield0(field);\n }\n\n\n function getMisses(fieldList1, fieldList2) {\n return fieldList1.filter(f => !hasSubfield0Match(f, fieldList2));\n }\n\n function hasSubfield0Match(field, pairFields) {\n const subfield0 = field.subfields.find(sf => sf.code === '0');\n return pairFields.some(f => f.subfields.some(sf => sf.code === '0' && sf.value === subfield0.value));\n }\n\n\n\n\n\n /*\n function getValidIdentifiers(record, tag, lang) {\n const lex = mapTagToLex(tag);\n const subfield2Value = `${lex}/${lang}`;\n const candFields = record.get(tag).filter(f => f.subfields.some(sf => sf.code === '2' && sf.value === subfield2Value)); // TODO: filter\n return [];\n }\n */\n\n}\n\nexport function fieldHasValidSubfield0(field, defaultLex = undefined) {\n const lex = defaultLex || mapTagToLex(field.tag);\n return field.subfields.some(sf => isValidSubfield0(sf, lex));\n}\n\nexport function isValidSubfield0(subfield, lex = '???') {\n if (subfield.code !== '0') {\n return false;\n }\n // 2025-12-10: supports both http and https as well. Note that translation will copy the original $0 idenfifier even though it might be non-standard.\n // Note that currently 'http' is teh standard!!!\n if (['yso', 'yso/fin', 'yso/swe'].includes(lex) && subfield.value.match(/^https?:\\/\\/www\\.yso\\.fi\\/onto\\/yso\\/p[0-9]+$/u)) {\n return true;\n }\n if (['slm', 'slm/fin', 'slm/swe'].includes(lex) && subfield.value.match(/^https?:\\/\\/urn\\.fi\\/URN:NBN:fi:au:slm:s[0-9]+$/u)) {\n return true;\n }\n return false;\n}\n\n\nexport function buildUri(lex, id) {\n if (['yso', 'yso/fin', 'yso/swe'].includes(lex)) {\n //return `http%3A%2F%2Fwww.yso.fi%2Fonto%2Fyso%2Fp${id}`;\n return `http://www.yso.fi/onto/yso/p${id}`;\n }\n if (['slm', 'slm/fin', 'slm/swe'].includes(lex)) {\n return `http://urn.fi/URN:NBN:fi:au:slm:s${id}`;\n }\n return undefined;\n}\n\nfunction mapTagToLex(tag) {\n if (tag === '655') {\n return 'slm';\n }\n return 'yso';\n}\n\nexport async function getTermData(uri) {\n nvdebug(`getTermData(${uri})`);\n if (termCache[uri]) { // Don't think current implementation uses the cache any more.\n //console.log(`CACHED ${uri}`); // eslint-disable-line no-console\n return termCache[uri];\n }\n const tmp = await getTermDataFromFinto(uri);\n termCache[uri] = tmp;\n return tmp;\n}\n\nasync function getTermDataFromFinto(uri) {\n const headers = {'Accept': 'application/json'};\n const uri2 = swaggerQuery(uri);\n\n const response = await fetch(uri2, {method: 'GET', headers});\n if (!response.ok) {\n return undefined;\n }\n const json = await response.json();\n\n if (!json.graph) {\n return undefined;\n }\n const arr = json.graph;\n const [hit] = arr.filter(row => row.uri === uri);\n const subset = {\n prefLabel: processLabel(hit?.prefLabel || undefined),\n altLabel: processLabel(hit?.altLabel || undefined)\n };\n //console.log(`NEW JSON: ${JSON.stringify(hit)}`); // eslint-disable-line no-console\n\n return subset;\n\n function swaggerQuery(uri) {\n // This would work for only yso, not yso-paikat etc `https://api.finto.fi/rest/v1/yso/data?format=application%2Fjson&uri=${uri}`;\n return `https://api.finto.fi/rest/v1/data?uri=${uri}&format=application%2Fjson`; // This is simpler, but contains more irrelevant data\n }\n\n function processLabel(label) {\n if (typeof label === 'object') {\n if (Array.isArray(label)) {\n return label;\n }\n return [label];\n }\n return [];\n }\n}\n\nexport function getLexiconAndLanguage(field) {\n // Assumes that field has exactly one $2\n const subfield2 = field.subfields.find(sf => sf.code === '2');\n if (subfield2) {\n if (subfield2.value === 'slm/fin') {\n return {'lex': 'slm', 'lang': 'fin'};\n }\n if (subfield2.value === 'slm/swe') {\n return {'lex': 'slm', 'lang': 'swe'};\n }\n if (subfield2.value === 'yso/fin') {\n return {'lex': 'yso', 'lang': 'fin'};\n }\n if (subfield2.value === 'yso/swe') {\n return {'lex': 'yso', 'lang': 'swe'};\n }\n }\n return {};\n}\n\n\n\nexport function isLabel(labels, term, lang = undefined) {\n const twoLetterLanguageCode = lang && lang.length === 3 ? changeAbbr(lang) : lang;\n return labels.some(l => isMatch(l));\n\n function isMatch(label) {\n if (label.value !== term) {\n return false;\n }\n if (!twoLetterLanguageCode) { // If language code is not defined, any hit will do\n return true;\n }\n return label.lang === twoLetterLanguageCode;\n }\n}\n\n\n function changeAbbr(abbr) {\n if (changeAbbrHash[abbr]) {\n return changeAbbrHash[abbr];\n }\n return abbr;\n }\n\n\n function swapLanguageCodeBetweenLanguages(code) {\n if (swapLanguageCode[code]) {\n return swapLanguageCode[code];\n }\n return code;\n }"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import clone from 'clone';\nimport createDebugLogger from 'debug';\nimport {fieldHasSubfield, fieldToString, nvdebug} from './utils.js';\n\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:translate-terms');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nconst defaultTags = ['648', '650', '651', '655'];\n\nconst swapLanguageCode = {'fin': 'swe', 'fi': 'sv', 'sv': 'fi', 'swe': 'fin'};\nconst changeAbbrHash = {'fi': 'fin', 'fin': 'fi', 'sv': 'swe', 'swe': 'sv'};\n\nconst termCache = {};\n\n// Author(s): Nicholas Volk\n// eslint-disable-next-line max-lines-per-function\nexport default function () {\n\n\n return {\n description: 'Translate yso (648, 650, 651) and slm (655) terms (FIN <=> SWE)',\n validate, fix\n };\n\n async function fix(record) {\n const newFields = await getFields(record, defaultTags, []);\n\n newFields.forEach(nf => nvdebug(`Add new field '${fieldToString(nf)}'`, debugDev));\n\n newFields.forEach(nf => record.insertField(nf));\n\n const newFieldsAsStrings = newFields.map(f => fieldToString(f));\n\n\n return {message: [], fix: newFieldsAsStrings, valid: true};\n }\n\n async function validate(record) {\n const newFields = await getFields(record, defaultTags, []);\n if (newFields.length === 0) {\n return {'message': [], 'valid': true};\n }\n const messages = newFields.map(f => fieldToString(f));\n\n return {'message': messages, 'valid': false};\n }\n\n async function getFields(record, tags, fieldsToAdd) {\n const [currTag, ...remainingTags] = tags;\n if (!currTag) {\n return fieldsToAdd;\n }\n const missingFields = await deriveMissingFields(record, currTag);\n\n const tmp = await getFields(record, remainingTags, [...fieldsToAdd, ...missingFields]);\n return tmp;\n }\n\n function getPairlessFinnishAndSwedishFields(record, tag) {\n const expectedLex = mapTagToLex(tag);\n if (!expectedLex) {\n return [];\n }\n const fields = record.get(tag);\n const finnishFields = fields.filter(f => isTranslatable(f, 'fin'));\n const swedishFields = fields.filter(f => isTranslatable(f, 'swe'));\n const finnishOnly = getMisses(finnishFields, swedishFields);\n const swedishOnly = getMisses(swedishFields, finnishFields);\n\n //nvdebug(` Looking at ${finnishOnly.length} + ${swedishOnly.length} fields`, debugDev);\n return [...finnishOnly, ...swedishOnly].filter(f => tagAndFieldAgree(f));\n\n function tagAndFieldAgree(field) {\n // Check that tag and $2 value are pairable:\n const lexData = getLexiconAndLanguage(field); // $2 data\n return expectedLex === lexData.lex;\n }\n }\n\n async function deriveMissingFields(record, tag) {\n const pairlessFields = getPairlessFinnishAndSwedishFields(record, tag);\n\n // Dunno how to handle loop+promise combo in our normal coding style. Spent half a day trying... (I reckon it takes like 2 minuts to do this properly...)\n // Did a proper implementation in drop-terms.js...\n let prefLabels = [];\n for (let i=0; i < pairlessFields.length; i += 1) {\n prefLabels[i] = await getPrefLabel(pairlessFields[i]);\n }\n\n const missingFields = pairlessFields.map((f, i) => pairField(f, prefLabels[i]));\n return missingFields.filter(f => f);\n }\n\n function pairField(field, prefLabels) {\n if (!prefLabels) {\n return undefined;\n }\n //nvdebug(`pairField() WP 1: ${fieldToString(field)}`, debugDev);\n const lexAndLang = getLexiconAndLanguage(field);\n //nvdebug(`pairField() WP 2: ${JSON.stringify(lexAndLang)}`, debugDev);\n const twoLetterOtherLang = swapLanguageCodeBetweenLanguages(changeAbbr(lexAndLang.lang));\n const prefLabel = prefLabels.find(l => l.lang === twoLetterOtherLang);\n //nvdebug(`pairField() WP 4: ${JSON.stringify(prefLabel)}`, debugDev);\n const sfA = {'code': 'a', 'value': prefLabel.value}; // field.subfields.field(sf => sf.code === 'a');\n const sf0 = clone(field.subfields.find(sf => sf.code === '0'));\n const sf2 = {'code': '2', 'value': `${lexAndLang.lex}/${lexAndLang.lang === 'fin' ? 'swe' : 'fin'}`}; // swap fin <=> swe\n const newField = {tag: field.tag, ind1: field.ind1, ind2: field.ind2, subfields: [sfA, sf2, sf0]};\n return newField;\n }\n\n\n\n async function getPrefLabel(field) {\n // Pre-requisite: tag vs $2 correlation has already been checked!\n const uri = fieldToUri(field);\n if (!uri) { // $0 is invalid or sumthing\n return undefined;\n }\n\n const data = await getTermData(uri);\n\n if (!data) { // Sanity check. Miss caused by illegal id etc.\n nvdebug(`No labels found for ${uri}`, debugDev);\n return undefined;\n }\n\n const prefLabels = data.prefLabel;\n\n const lexData = getLexiconAndLanguage(field); // $2 data\n const lang = changeAbbr(lexData.lang); // fi <=> fin (finto use 2 chars, we use 3-letters)\n const subfieldA = field.subfields.find(sf => sf.code === 'a');\n\n if (isLabel(prefLabels, subfieldA.value, lang)) {\n return prefLabels;\n }\n return undefined;\n }\n\n\n function fieldToUri(field) {\n const lex = mapTagToLex(field.tag);\n\n const subfield0 = field.subfields.find(sf => sf.code === '0');\n const id = subfield0.value.replace(/^[^0-9]+/u, '');\n return buildUri(lex, id);\n }\n\n function isTranslatable(field, lang) {\n const fieldAsString = fieldToString(field);\n\n // We should probably allow an optional $8 as the first subfield.\n if (!fieldAsString.match(/^... #7 (?: \u20218 [^\u2021]+ )*\u2021a [^\u2021]+ \u20212 [^\u2021]+ \u20210 [^\u2021]+(?: \u20219 [A-Z]+<(?:KEEP|DROP)>)*$/u)) {\n return false;\n }\n const lex = mapTagToLex(field.tag);\n const lexLang = `${lex}/${lang}`;\n if (!fieldHasSubfield(field, '2', lexLang)) {\n return false;\n }\n return fieldHasValidSubfield0(field);\n }\n\n\n function getMisses(fieldList1, fieldList2) {\n return fieldList1.filter(f => !hasSubfield0Match(f, fieldList2));\n }\n\n function hasSubfield0Match(field, pairFields) {\n const subfield0 = field.subfields.find(sf => sf.code === '0');\n return pairFields.some(f => f.subfields.some(sf => sf.code === '0' && sf.value === subfield0.value));\n }\n\n\n\n\n\n /*\n function getValidIdentifiers(record, tag, lang) {\n const lex = mapTagToLex(tag);\n const subfield2Value = `${lex}/${lang}`;\n const candFields = record.get(tag).filter(f => f.subfields.some(sf => sf.code === '2' && sf.value === subfield2Value)); // TODO: filter\n return [];\n }\n */\n\n}\n\nexport function fieldHasValidSubfield0(field, defaultLex = undefined) {\n const lex = defaultLex || mapTagToLex(field.tag);\n return field.subfields.some(sf => isValidSubfield0(sf, lex));\n}\n\nexport function isValidSubfield0(subfield, lex = '???') {\n if (subfield.code !== '0') {\n return false;\n }\n // 2025-12-10: supports both http and https as well. Note that translation will copy the original $0 idenfifier even though it might be non-standard.\n // Note that currently 'http' is teh standard!!!\n if (['yso', 'yso/fin', 'yso/swe'].includes(lex) && subfield.value.match(/^https?:\\/\\/www\\.yso\\.fi\\/onto\\/yso\\/p[0-9]+$/u)) {\n return true;\n }\n if (['slm', 'slm/fin', 'slm/swe'].includes(lex) && subfield.value.match(/^https?:\\/\\/urn\\.fi\\/URN:NBN:fi:au:slm:s[0-9]+$/u)) {\n return true;\n }\n return false;\n}\n\n\nexport function buildUri(lex, id) {\n if (['yso', 'yso/fin', 'yso/swe'].includes(lex)) {\n //return `http%3A%2F%2Fwww.yso.fi%2Fonto%2Fyso%2Fp${id}`;\n return `http://www.yso.fi/onto/yso/p${id}`;\n }\n if (['slm', 'slm/fin', 'slm/swe'].includes(lex)) {\n return `http://urn.fi/URN:NBN:fi:au:slm:s${id}`;\n }\n return undefined;\n}\n\nfunction mapTagToLex(tag) {\n if (tag === '655') {\n return 'slm';\n }\n return 'yso';\n}\n\nexport async function getTermData(uri) {\n nvdebug(`getTermData(${uri})`, debugDev);\n if (termCache[uri]) { // Don't think current implementation uses the cache any more.\n //nvdebug(`CACHED ${uri}`, debugDev);\n return termCache[uri];\n }\n const tmp = await getTermDataFromFinto(uri);\n termCache[uri] = tmp;\n return tmp;\n}\n\nasync function getTermDataFromFinto(uri) {\n const headers = {'Accept': 'application/json'};\n const uri2 = swaggerQuery(uri);\n\n const response = await fetch(uri2, {method: 'GET', headers});\n if (!response.ok) {\n return undefined;\n }\n const json = await response.json();\n\n if (!json.graph) {\n return undefined;\n }\n const arr = json.graph;\n const [hit] = arr.filter(row => row.uri === uri);\n const subset = {\n prefLabel: processLabel(hit?.prefLabel || undefined),\n altLabel: processLabel(hit?.altLabel || undefined)\n };\n //nvdebug(`NEW JSON: ${JSON.stringify(hit)}`, debugDev);\n\n return subset;\n\n function swaggerQuery(uri) {\n // This would work for only yso, not yso-paikat etc `https://api.finto.fi/rest/v1/yso/data?format=application%2Fjson&uri=${uri}`;\n return `https://api.finto.fi/rest/v1/data?uri=${uri}&format=application%2Fjson`; // This is simpler, but contains more irrelevant data\n }\n\n function processLabel(label) {\n if (typeof label === 'object') {\n if (Array.isArray(label)) {\n return label;\n }\n return [label];\n }\n return [];\n }\n}\n\nexport function getLexiconAndLanguage(field) {\n // Assumes that field has exactly one $2\n const subfield2 = field.subfields.find(sf => sf.code === '2');\n if (subfield2) {\n if (subfield2.value === 'slm/fin') {\n return {'lex': 'slm', 'lang': 'fin'};\n }\n if (subfield2.value === 'slm/swe') {\n return {'lex': 'slm', 'lang': 'swe'};\n }\n if (subfield2.value === 'yso/fin') {\n return {'lex': 'yso', 'lang': 'fin'};\n }\n if (subfield2.value === 'yso/swe') {\n return {'lex': 'yso', 'lang': 'swe'};\n }\n }\n return {};\n}\n\n\n\nexport function isLabel(labels, term, lang = undefined) {\n const twoLetterLanguageCode = lang && lang.length === 3 ? changeAbbr(lang) : lang;\n return labels.some(l => isMatch(l));\n\n function isMatch(label) {\n if (label.value !== term) {\n return false;\n }\n if (!twoLetterLanguageCode) { // If language code is not defined, any hit will do\n return true;\n }\n return label.lang === twoLetterLanguageCode;\n }\n}\n\n\n function changeAbbr(abbr) {\n if (changeAbbrHash[abbr]) {\n return changeAbbrHash[abbr];\n }\n return abbr;\n }\n\n\n function swapLanguageCodeBetweenLanguages(code) {\n if (swapLanguageCode[code]) {\n return swapLanguageCode[code];\n }\n return code;\n }"],
5
+ "mappings": "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",
6
6
  "names": ["uri"]
7
7
  }
@@ -1,5 +1,7 @@
1
1
  import createDebugLogger from "debug";
2
+ import { nvdebug } from "./utils.js";
2
3
  const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda/typeOfDate-008");
4
+ const debugDev = debug.extend("dev");
3
5
  export default function() {
4
6
  return {
5
7
  description: "Validates 008 06",
@@ -11,7 +13,7 @@ export default function() {
11
13
  const c06 = f008.value.substring(6, 7);
12
14
  const c1114 = f008.value.substring(11, 15);
13
15
  if (c06 === "t" && !/[0-9u]{4}/u.test(c1114)) {
14
- debug("is t and not valid 1114");
16
+ nvdebug("is t and not valid 1114", debugDev);
15
17
  return { valid: false, message: "Invalid 008 06" };
16
18
  }
17
19
  return { valid: true };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/typeOfDate-008.js"],
4
- "sourcesContent": ["import createDebugLogger from 'debug';\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/typeOfDate-008');\n\nexport default function () {\n return {\n description: 'Validates 008 06',\n validate,\n fix\n };\n\n function validate(record) {\n const [f008] = record.get(/008/u);\n const c06 = f008.value.substring(6, 7);\n const c1114 = f008.value.substring(11, 15);\n // if 008 06 = s, and 11-14 = #### (not year/digits)\n if (c06 === 't' && !(/[0-9u]{4}/u).test(c1114)) {\n debug('is t and not valid 1114');\n return {valid: false, message: 'Invalid 008 06'};\n }\n\n return {valid: true};\n }\n\n function fix(record) {\n // LDR/06=t ja 11-14=####, niin LDR/06 muutetaan s:ksi\n const [f008] = record.pop(/008/u);\n const c06 = f008.value.substring(6, 7);\n const c1114 = f008.value.substring(11, 15);\n // if 008 06 = s, and 11-14 = #### (not year/digits)\n if (c06 === 't' && !(/[0-9u]{4}/u).test(c1114)) {\n f008.value = `${f008.value.substring(0, 6)}s${f008.value.substring(7)}`;\n record.insertField(f008);\n return true;\n }\n\n record.insertField(f008);\n return true;\n }\n}\n"],
5
- "mappings": "AAAA,OAAO,uBAAuB;AAE9B,MAAM,QAAQ,kBAAkB,yDAAyD;AAEzF,0BAA2B;AACzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IACA;AAAA,EACF;AAEA,WAAS,SAAS,QAAQ;AACxB,UAAM,CAAC,IAAI,IAAI,OAAO,IAAI,MAAM;AAChC,UAAM,MAAM,KAAK,MAAM,UAAU,GAAG,CAAC;AACrC,UAAM,QAAQ,KAAK,MAAM,UAAU,IAAI,EAAE;AAEzC,QAAI,QAAQ,OAAO,CAAE,aAAc,KAAK,KAAK,GAAG;AAC9C,YAAM,yBAAyB;AAC/B,aAAO,EAAC,OAAO,OAAO,SAAS,iBAAgB;AAAA,IACjD;AAEA,WAAO,EAAC,OAAO,KAAI;AAAA,EACrB;AAEA,WAAS,IAAI,QAAQ;AAEnB,UAAM,CAAC,IAAI,IAAI,OAAO,IAAI,MAAM;AAChC,UAAM,MAAM,KAAK,MAAM,UAAU,GAAG,CAAC;AACrC,UAAM,QAAQ,KAAK,MAAM,UAAU,IAAI,EAAE;AAEzC,QAAI,QAAQ,OAAO,CAAE,aAAc,KAAK,KAAK,GAAG;AAC9C,WAAK,QAAQ,GAAG,KAAK,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,KAAK,MAAM,UAAU,CAAC,CAAC;AACrE,aAAO,YAAY,IAAI;AACvB,aAAO;AAAA,IACT;AAEA,WAAO,YAAY,IAAI;AACvB,WAAO;AAAA,EACT;AACF;",
4
+ "sourcesContent": ["import createDebugLogger from 'debug';\nimport { nvdebug } from './utils.js';\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/typeOfDate-008');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nexport default function () {\n return {\n description: 'Validates 008 06',\n validate,\n fix\n };\n\n function validate(record) {\n const [f008] = record.get(/008/u);\n const c06 = f008.value.substring(6, 7);\n const c1114 = f008.value.substring(11, 15);\n // if 008 06 = s, and 11-14 = #### (not year/digits)\n if (c06 === 't' && !(/[0-9u]{4}/u).test(c1114)) {\n nvdebug('is t and not valid 1114', debugDev);\n return {valid: false, message: 'Invalid 008 06'};\n }\n\n return {valid: true};\n }\n\n function fix(record) {\n // LDR/06=t ja 11-14=####, niin LDR/06 muutetaan s:ksi\n const [f008] = record.pop(/008/u);\n const c06 = f008.value.substring(6, 7);\n const c1114 = f008.value.substring(11, 15);\n // if 008 06 = s, and 11-14 = #### (not year/digits)\n if (c06 === 't' && !(/[0-9u]{4}/u).test(c1114)) {\n f008.value = `${f008.value.substring(0, 6)}s${f008.value.substring(7)}`;\n record.insertField(f008);\n return true;\n }\n\n record.insertField(f008);\n return true;\n }\n}\n"],
5
+ "mappings": "AAAA,OAAO,uBAAuB;AAC9B,SAAS,eAAe;AAExB,MAAM,QAAQ,kBAAkB,yDAAyD;AAEzF,MAAM,WAAW,MAAM,OAAO,KAAK;AAEnC,0BAA2B;AACzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IACA;AAAA,EACF;AAEA,WAAS,SAAS,QAAQ;AACxB,UAAM,CAAC,IAAI,IAAI,OAAO,IAAI,MAAM;AAChC,UAAM,MAAM,KAAK,MAAM,UAAU,GAAG,CAAC;AACrC,UAAM,QAAQ,KAAK,MAAM,UAAU,IAAI,EAAE;AAEzC,QAAI,QAAQ,OAAO,CAAE,aAAc,KAAK,KAAK,GAAG;AAC9C,cAAQ,2BAA2B,QAAQ;AAC3C,aAAO,EAAC,OAAO,OAAO,SAAS,iBAAgB;AAAA,IACjD;AAEA,WAAO,EAAC,OAAO,KAAI;AAAA,EACrB;AAEA,WAAS,IAAI,QAAQ;AAEnB,UAAM,CAAC,IAAI,IAAI,OAAO,IAAI,MAAM;AAChC,UAAM,MAAM,KAAK,MAAM,UAAU,GAAG,CAAC;AACrC,UAAM,QAAQ,KAAK,MAAM,UAAU,IAAI,EAAE;AAEzC,QAAI,QAAQ,OAAO,CAAE,aAAc,KAAK,KAAK,GAAG;AAC9C,WAAK,QAAQ,GAAG,KAAK,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,KAAK,MAAM,UAAU,CAAC,CAAC;AACrE,aAAO,YAAY,IAAI;AACvB,aAAO;AAAA,IACT;AAEA,WAAO,YAAY,IAAI;AACvB,WAAO;AAAA,EACT;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/update-field-540.js"],
4
- "sourcesContent": ["//import createDebugLogger from 'debug';\nimport {fieldToString} from './utils.js';\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/update-field-540');\n\n// Author(s): Nicholas Volk\nexport default function () {\n\n return {\n description: 'Validator for field 540 (modernization as per MELKEHITYS-2431)',\n validate, fix\n };\n\n function fix(record) {\n const fixedFields = getFieldsThatUseOldFormat(record, true);\n\n const fixedFieldsAsStrings = fixedFields.map(f => fieldToString(f));\n\n return {message: [], fix: fixedFieldsAsStrings, valid: true};\n }\n\n function validate(record) {\n const yeOldeFields = getFieldsThatUseOldFormat(record, false);\n if (yeOldeFields.length === 0) {\n return {'message': [], 'valid': true};\n }\n const messages = yeOldeFields.map(f => fieldToString(f));\n\n return {'message': messages, 'valid': false};\n }\n\n}\n\n\nconst licences = [\n {'license': 'CC BY 4.0', 'url': 'https://creativecommons.org/licenses/by/4.0/deed.fi'},\n {'license': 'CC BY-NC 4.0', 'url': 'https://creativecommons.org/licenses/by-nc/4.0/deed.fi'},\n {'license': 'CC BY-NC-ND 4.0', 'url': 'https://creativecommons.org/licenses/by-nc-nd/4.0/deed.fi'},\n {'license': 'CC BY-NC-SA 4.0', 'url': 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed.fi'},\n {'license': 'CC BY-ND 4.0', 'url': 'https://creativecommons.org/licenses/by-nd/4.0/deed.fi'},\n {'license': 'CC BY-SA 4.0', 'url': 'https://creativecommons.org/licenses/by-sa/4.0/deed.fi'},\n {'license': 'CC0 1.0', 'url': 'https://creativecommons.org/publicdomain/zero/1.0/deed.fi'}, // not seen/unused\n {'license': 'Public domain', 'url': 'https://creativecommons.org/publicdomain/mark/1.0/deed.fi'}\n];\n\nfunction findSubfieldIndex(field, subfield) {\n subfield.nvtmp = 1;\n const index = field.subfields.findIndex(sf => sf.nvtmp === 1);\n delete subfield.nvtmp;\n return index;\n}\n\nfunction validLicenseInSubfieldC(subfieldC, license) {\n if (subfieldC.code !== 'c') {\n return false;\n }\n //nvdebug(`Compare ${subfieldC.value} vs ${license.license}`);\n return license.license === subfieldC.value;\n}\n\nfunction validUrlInSubfieldU(subfieldU, license) {\n if (subfieldU.code !== 'u') {\n return false;\n }\n //nvdebug(`Compare ${subfieldU.value} vs ${license.url}`);\n return license.url === subfieldU.value;\n}\n\n\nfunction fixC(field, subfieldC) {\n // MELINDA-2431_\n subfieldC.code = 'f';\n const index = findSubfieldIndex(field, subfieldC);\n field.subfields.splice(index + 1, 0, {'code': '2', 'value': 'cc'});\n}\n\nfunction fieldHasOldCcLicense(field, fix) {\n if (field.tag !== '540') {\n return false;\n }\n //nvdebug(`NORM 540: ${fieldToString(field)}`);\n const validLicense = licences.find(license => field.subfields.some(sf => validLicenseInSubfieldC(sf, license)) && field.subfields.some(sf => validUrlInSubfieldU(sf, license)));\n if (!validLicense) {\n return false;\n }\n //nvdebug(` Found valid license`);\n if (fix) {\n const subfieldsC = field.subfields.filter(sf => validLicenseInSubfieldC(sf, validLicense));\n subfieldsC.forEach(c => fixC(field, c));\n }\n\n return true;\n}\n\n\nfunction getFieldsThatUseOldFormat(record, fix) {\n return record.fields.filter(f => fieldHasOldCcLicense(f, fix));\n}\n\n"],
5
- "mappings": "AACA,SAAQ,qBAAoB;AAK5B,0BAA2B;AAEzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,IAAI,QAAQ;AACnB,UAAM,cAAc,0BAA0B,QAAQ,IAAI;AAE1D,UAAM,uBAAuB,YAAY,IAAI,OAAK,cAAc,CAAC,CAAC;AAElE,WAAO,EAAC,SAAS,CAAC,GAAG,KAAK,sBAAsB,OAAO,KAAI;AAAA,EAC7D;AAEA,WAAS,SAAS,QAAQ;AACxB,UAAM,eAAe,0BAA0B,QAAQ,KAAK;AAC5D,QAAI,aAAa,WAAW,GAAG;AAC7B,aAAO,EAAC,WAAW,CAAC,GAAG,SAAS,KAAI;AAAA,IACtC;AACA,UAAM,WAAW,aAAa,IAAI,OAAK,cAAc,CAAC,CAAC;AAEvD,WAAO,EAAC,WAAW,UAAU,SAAS,MAAK;AAAA,EAC7C;AAEF;AAGA,MAAM,WAAW;AAAA,EACf,EAAC,WAAW,aAAa,OAAO,sDAAqD;AAAA,EACrF,EAAC,WAAW,gBAAgB,OAAO,yDAAwD;AAAA,EAC3F,EAAC,WAAW,mBAAmB,OAAO,4DAA2D;AAAA,EACjG,EAAC,WAAW,mBAAmB,OAAO,4DAA2D;AAAA,EACjG,EAAC,WAAW,gBAAgB,OAAO,yDAAwD;AAAA,EAC3F,EAAC,WAAW,gBAAgB,OAAO,yDAAwD;AAAA,EAC3F,EAAC,WAAW,WAAW,OAAO,4DAA2D;AAAA;AAAA,EACzF,EAAC,WAAW,iBAAiB,OAAO,4DAA2D;AACjG;AAEA,SAAS,kBAAkB,OAAO,UAAU;AAC1C,WAAS,QAAQ;AACjB,QAAM,QAAQ,MAAM,UAAU,UAAU,QAAM,GAAG,UAAU,CAAC;AAC5D,SAAO,SAAS;AAChB,SAAO;AACT;AAEA,SAAS,wBAAwB,WAAW,SAAS;AACnD,MAAI,UAAU,SAAS,KAAK;AAC1B,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,YAAY,UAAU;AACvC;AAEA,SAAS,oBAAoB,WAAW,SAAS;AAC/C,MAAI,UAAU,SAAS,KAAK;AAC1B,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,QAAQ,UAAU;AACnC;AAGA,SAAS,KAAK,OAAO,WAAW;AAE9B,YAAU,OAAO;AACjB,QAAM,QAAQ,kBAAkB,OAAO,SAAS;AAChD,QAAM,UAAU,OAAO,QAAQ,GAAG,GAAG,EAAC,QAAQ,KAAK,SAAS,KAAI,CAAC;AACnE;AAEA,SAAS,qBAAqB,OAAO,KAAK;AACxC,MAAI,MAAM,QAAQ,OAAO;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,SAAS,KAAK,aAAW,MAAM,UAAU,KAAK,QAAM,wBAAwB,IAAI,OAAO,CAAC,KAAK,MAAM,UAAU,KAAK,QAAM,oBAAoB,IAAI,OAAO,CAAC,CAAC;AAC9K,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAEA,MAAI,KAAK;AACP,UAAM,aAAa,MAAM,UAAU,OAAO,QAAM,wBAAwB,IAAI,YAAY,CAAC;AACzF,eAAW,QAAQ,OAAK,KAAK,OAAO,CAAC,CAAC;AAAA,EACxC;AAEA,SAAO;AACT;AAGA,SAAS,0BAA0B,QAAQ,KAAK;AAC9C,SAAO,OAAO,OAAO,OAAO,OAAK,qBAAqB,GAAG,GAAG,CAAC;AAC/D;",
4
+ "sourcesContent": ["//import createDebugLogger from 'debug';\nimport {fieldToString /*, nvdebug*/} from './utils.js';\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/update-field-540');\n//const debugData = debug.extend('data');\n//const debugDev = debug.extend('dev');\n\n// Author(s): Nicholas Volk\nexport default function () {\n\n return {\n description: 'Validator for field 540 (modernization as per MELKEHITYS-2431)',\n validate, fix\n };\n\n function fix(record) {\n const fixedFields = getFieldsThatUseOldFormat(record, true);\n\n const fixedFieldsAsStrings = fixedFields.map(f => fieldToString(f));\n\n return {message: [], fix: fixedFieldsAsStrings, valid: true};\n }\n\n function validate(record) {\n const yeOldeFields = getFieldsThatUseOldFormat(record, false);\n if (yeOldeFields.length === 0) {\n return {'message': [], 'valid': true};\n }\n const messages = yeOldeFields.map(f => fieldToString(f));\n\n return {'message': messages, 'valid': false};\n }\n\n}\n\n\nconst licences = [\n {'license': 'CC BY 4.0', 'url': 'https://creativecommons.org/licenses/by/4.0/deed.fi'},\n {'license': 'CC BY-NC 4.0', 'url': 'https://creativecommons.org/licenses/by-nc/4.0/deed.fi'},\n {'license': 'CC BY-NC-ND 4.0', 'url': 'https://creativecommons.org/licenses/by-nc-nd/4.0/deed.fi'},\n {'license': 'CC BY-NC-SA 4.0', 'url': 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed.fi'},\n {'license': 'CC BY-ND 4.0', 'url': 'https://creativecommons.org/licenses/by-nd/4.0/deed.fi'},\n {'license': 'CC BY-SA 4.0', 'url': 'https://creativecommons.org/licenses/by-sa/4.0/deed.fi'},\n {'license': 'CC0 1.0', 'url': 'https://creativecommons.org/publicdomain/zero/1.0/deed.fi'}, // not seen/unused\n {'license': 'Public domain', 'url': 'https://creativecommons.org/publicdomain/mark/1.0/deed.fi'}\n];\n\nfunction findSubfieldIndex(field, subfield) {\n subfield.nvtmp = 1;\n const index = field.subfields.findIndex(sf => sf.nvtmp === 1);\n delete subfield.nvtmp;\n return index;\n}\n\nfunction validLicenseInSubfieldC(subfieldC, license) {\n if (subfieldC.code !== 'c') {\n return false;\n }\n //nvdebug(`Compare ${subfieldC.value} vs ${license.license}`, debugDev);\n return license.license === subfieldC.value;\n}\n\nfunction validUrlInSubfieldU(subfieldU, license) {\n if (subfieldU.code !== 'u') {\n return false;\n }\n //nvdebug(`Compare ${subfieldU.value} vs ${license.url}`, debugDev);\n return license.url === subfieldU.value;\n}\n\n\nfunction fixC(field, subfieldC) {\n // MELINDA-2431_\n subfieldC.code = 'f';\n const index = findSubfieldIndex(field, subfieldC);\n field.subfields.splice(index + 1, 0, {'code': '2', 'value': 'cc'});\n}\n\nfunction fieldHasOldCcLicense(field, fix) {\n if (field.tag !== '540') {\n return false;\n }\n //nvdebug(`NORM 540: ${fieldToString(field)}`, debugDev);\n const validLicense = licences.find(license => field.subfields.some(sf => validLicenseInSubfieldC(sf, license)) && field.subfields.some(sf => validUrlInSubfieldU(sf, license)));\n if (!validLicense) {\n return false;\n }\n //nvdebug(` Found valid license`, debugDev);\n if (fix) {\n const subfieldsC = field.subfields.filter(sf => validLicenseInSubfieldC(sf, validLicense));\n subfieldsC.forEach(c => fixC(field, c));\n }\n\n return true;\n}\n\n\nfunction getFieldsThatUseOldFormat(record, fix) {\n return record.fields.filter(f => fieldHasOldCcLicense(f, fix));\n}\n\n"],
5
+ "mappings": "AACA,SAAQ,qBAAkC;AAO1C,0BAA2B;AAEzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,IAAI,QAAQ;AACnB,UAAM,cAAc,0BAA0B,QAAQ,IAAI;AAE1D,UAAM,uBAAuB,YAAY,IAAI,OAAK,cAAc,CAAC,CAAC;AAElE,WAAO,EAAC,SAAS,CAAC,GAAG,KAAK,sBAAsB,OAAO,KAAI;AAAA,EAC7D;AAEA,WAAS,SAAS,QAAQ;AACxB,UAAM,eAAe,0BAA0B,QAAQ,KAAK;AAC5D,QAAI,aAAa,WAAW,GAAG;AAC7B,aAAO,EAAC,WAAW,CAAC,GAAG,SAAS,KAAI;AAAA,IACtC;AACA,UAAM,WAAW,aAAa,IAAI,OAAK,cAAc,CAAC,CAAC;AAEvD,WAAO,EAAC,WAAW,UAAU,SAAS,MAAK;AAAA,EAC7C;AAEF;AAGA,MAAM,WAAW;AAAA,EACf,EAAC,WAAW,aAAa,OAAO,sDAAqD;AAAA,EACrF,EAAC,WAAW,gBAAgB,OAAO,yDAAwD;AAAA,EAC3F,EAAC,WAAW,mBAAmB,OAAO,4DAA2D;AAAA,EACjG,EAAC,WAAW,mBAAmB,OAAO,4DAA2D;AAAA,EACjG,EAAC,WAAW,gBAAgB,OAAO,yDAAwD;AAAA,EAC3F,EAAC,WAAW,gBAAgB,OAAO,yDAAwD;AAAA,EAC3F,EAAC,WAAW,WAAW,OAAO,4DAA2D;AAAA;AAAA,EACzF,EAAC,WAAW,iBAAiB,OAAO,4DAA2D;AACjG;AAEA,SAAS,kBAAkB,OAAO,UAAU;AAC1C,WAAS,QAAQ;AACjB,QAAM,QAAQ,MAAM,UAAU,UAAU,QAAM,GAAG,UAAU,CAAC;AAC5D,SAAO,SAAS;AAChB,SAAO;AACT;AAEA,SAAS,wBAAwB,WAAW,SAAS;AACnD,MAAI,UAAU,SAAS,KAAK;AAC1B,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,YAAY,UAAU;AACvC;AAEA,SAAS,oBAAoB,WAAW,SAAS;AAC/C,MAAI,UAAU,SAAS,KAAK;AAC1B,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,QAAQ,UAAU;AACnC;AAGA,SAAS,KAAK,OAAO,WAAW;AAE9B,YAAU,OAAO;AACjB,QAAM,QAAQ,kBAAkB,OAAO,SAAS;AAChD,QAAM,UAAU,OAAO,QAAQ,GAAG,GAAG,EAAC,QAAQ,KAAK,SAAS,KAAI,CAAC;AACnE;AAEA,SAAS,qBAAqB,OAAO,KAAK;AACxC,MAAI,MAAM,QAAQ,OAAO;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,SAAS,KAAK,aAAW,MAAM,UAAU,KAAK,QAAM,wBAAwB,IAAI,OAAO,CAAC,KAAK,MAAM,UAAU,KAAK,QAAM,oBAAoB,IAAI,OAAO,CAAC,CAAC;AAC9K,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAEA,MAAI,KAAK;AACP,UAAM,aAAa,MAAM,UAAU,OAAO,QAAM,wBAAwB,IAAI,YAAY,CAAC;AACzF,eAAW,QAAQ,OAAK,KAAK,OAAO,CAAC,CAAC;AAAA,EACxC;AAEA,SAAO;AACT;AAGA,SAAS,0BAA0B,QAAQ,KAAK;AAC9C,SAAO,OAAO,OAAO,OAAO,OAAK,qBAAqB,GAAG,GAAG,CAAC;AAC/D;",
6
6
  "names": []
7
7
  }
package/dist/urn.js CHANGED
@@ -1,9 +1,10 @@
1
- import { isElectronicMaterial } from "./utils.js";
1
+ import { isElectronicMaterial, nvdebug } from "./utils.js";
2
2
  import createDebugLogger from "debug";
3
3
  const URN_GENERATOR_URL = "https://generator.urn.fi/cgi-bin/urn_generator.cgi?type=nbn";
4
4
  export default function(isLegalDeposit = false, useMelindaTemp = true) {
5
5
  const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda:urn");
6
6
  const debugData = debug.extend("data");
7
+ const debugDev = debug.extend("dev");
7
8
  function hasLegalDepositURN(field) {
8
9
  if (field.tag !== "856" || ["1", "2", "3", "4"].includes(field.ind2)) {
9
10
  return false;
@@ -20,9 +21,9 @@ export default function(isLegalDeposit = false, useMelindaTemp = true) {
20
21
  };
21
22
  async function fix(record) {
22
23
  const f856sUrn = record.fields.filter(hasLegalDepositURN);
23
- debugData(`f856sUrn: ${JSON.stringify(f856sUrn)}`);
24
+ nvdebug(`f856sUrn: ${JSON.stringify(f856sUrn)}`, debugDev);
24
25
  const ldSubfields = isLegalDeposit ? createLDSubfields() : [];
25
- debugData(`IsLegalDeposit: ${isLegalDeposit}, LegalDepositSubfields: ${JSON.stringify(ldSubfields)}`);
26
+ nvdebug(`IsLegalDeposit: ${isLegalDeposit}, LegalDepositSubfields: ${JSON.stringify(ldSubfields)}`, debugDev);
26
27
  if (f856sUrn.length === 0) {
27
28
  const { code, value, generated } = await createURNSubfield(record);
28
29
  if (generated && useMelindaTemp) {
@@ -64,7 +65,7 @@ export default function(isLegalDeposit = false, useMelindaTemp = true) {
64
65
  }
65
66
  return acc;
66
67
  }, void 0);
67
- debugData(`isbns: ${isbn}`);
68
+ nvdebug(`isbns: ${isbn}`, debugDev);
68
69
  const { generated, value } = await createURN(isbn);
69
70
  return { code: "u", value, generated };
70
71
  async function createURN(isbn2 = false) {
@@ -101,31 +102,31 @@ export default function(isLegalDeposit = false, useMelindaTemp = true) {
101
102
  }
102
103
  }
103
104
  function validateLD(f856sUrn) {
104
- debug(`Validating the existence of legal deposit subfields`);
105
+ nvdebug(`Validating the existence of legal deposit subfields`, debugDev);
105
106
  const ldSubfields = createLDSubfields();
106
107
  const f856sUrnWithLdSubfields = f856sUrn.filter((field) => fieldHasLDSubfields(field, ldSubfields));
107
108
  if (f856sUrnWithLdSubfields.length > 0) {
108
- debug(`Record has ${f856sUrnWithLdSubfields.length} URN fields with all necessary legal deposit subfields`);
109
- debugData(`f856sUrnWithLdSubfields: ${JSON.stringify(f856sUrnWithLdSubfields)}`);
109
+ nvdebug(`Record has ${f856sUrnWithLdSubfields.length} URN fields with all necessary legal deposit subfields`, debugDev);
110
+ nvdebug(`f856sUrnWithLdSubfields: ${JSON.stringify(f856sUrnWithLdSubfields)}`, debugData);
110
111
  return true;
111
112
  }
112
113
  return false;
113
114
  }
114
115
  function validate(record) {
115
116
  if (!isElectronicMaterial(record)) {
116
- debug(`Record is not electronic - no need to validate legal deposit URNs`);
117
+ nvdebug(`Record is not electronic - no need to validate legal deposit URNs`, debugDev);
117
118
  return { valid: true };
118
119
  }
119
120
  const f856sUrn = record.fields.filter(hasLegalDepositURN);
120
121
  if (f856sUrn.length > 0) {
121
- debug(`Record has ${f856sUrn.length} URN fields`);
122
- debugData(`f856sUrn: ${JSON.stringify(f856sUrn)}`);
122
+ nvdebug(`Record has ${f856sUrn.length} URN fields`, debugDev);
123
+ nvdebug(`f856sUrn: ${JSON.stringify(f856sUrn)}`, debugData);
123
124
  if (!isLegalDeposit || validateLD(f856sUrn)) {
124
- debug(`Record is valid`);
125
+ nvdebug(`Record is valid`, debugDev);
125
126
  return { valid: true };
126
127
  }
127
128
  }
128
- debug(`No (valid) URN fields - Record is not valid`);
129
+ nvdebug(`No (valid) URN fields - Record is not valid`, debugDev);
129
130
  return { valid: false };
130
131
  }
131
132
  }
package/dist/urn.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/urn.js"],
4
- "sourcesContent": ["//import fetch from 'node-fetch';\nimport {isElectronicMaterial} from './utils.js';\nimport createDebugLogger from 'debug';\n\nconst URN_GENERATOR_URL = 'https://generator.urn.fi/cgi-bin/urn_generator.cgi?type=nbn';\n\nexport default function (isLegalDeposit = false, useMelindaTemp = true) {\n const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:urn');\n const debugData = debug.extend('data');\n\n //console.log(`IS LEGAL DEPOSIT? ${isLegalDeposit ? 'YES' : 'NO'}`); // eslint-disable-line no-console\n\n // We should check that the f856 with URN has second indicator '0' (Resource), ' ' (No information provided) or '8' (No display constant generated)\n // - if second indicator is '1' (Version of resource) or '2' (Related resource) the URN in f856 is not correct for the resource described in the record\n\n // This checks only the existence of URNs from the Finnish urn.fi -resolver\n\n function hasLegalDepositURN(field) {\n if (field.tag !== '856' || ['1', '2', '3', '4'].includes(field.ind2)) {\n return false;\n }\n\n // First attempt to fix MET-573. However, does useMelindaTemp come into play as well?\n if (isLegalDeposit && !field.subfields.some(sf => sf.code === '5' && sf.value === 'FI-Vapaa')) {\n return false;\n }\n\n return field.subfields.some(sf => sf.code === 'u' && (/^https?:\\/\\/urn\\.fi/u).test(sf.value));\n }\n\n\n return {\n description: 'Adds URN for record, to 856-field (if not existing). If isLegalDeposit is active, adds legal deposit subfields to the f856s with URN.',\n validate,\n fix\n };\n\n async function fix(record) {\n const f856sUrn = record.fields.filter(hasLegalDepositURN);\n debugData(`f856sUrn: ${JSON.stringify(f856sUrn)}`);\n\n const ldSubfields = isLegalDeposit ? createLDSubfields() : [];\n debugData(`IsLegalDeposit: ${isLegalDeposit}, LegalDepositSubfields: ${JSON.stringify(ldSubfields)}`);\n\n // We add the URN even if we're not getting the legalDeposit - where does this URN resolve?\n // We probably should not do these additions\n\n if (f856sUrn.length === 0) {\n const {code, value, generated} = await createURNSubfield(record);\n\n if (generated && useMelindaTemp) {\n const tempSubField = {code: '9', value: 'MELINDA<TEMP>'};\n\n record.insertField({\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [{code, value}, ...ldSubfields, tempSubField]\n });\n\n return true;\n }\n\n record.insertField({\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [{code, value}, ...ldSubfields]\n });\n\n return true;\n }\n\n if (isLegalDeposit) {\n\n // We add here legal deposit information to all URN-f856s - we probably should not do this\n // We should add extra f856 URN / URNs for legal deposits that already have a open (non-legal-deposit) URN\n // How do we decide which URN to use as a template if there are several URNs\n // We should check for existence of a legal deposit URN anyways\n\n f856sUrn.forEach(f => {\n // Change phrase from old to new if field with old phrase is found\n if (f.subfields.some(sf => hasOld856LdPhrase(sf))) {\n f.subfields\n .find(sf => hasOld856LdPhrase(sf))\n .value = 'K\u00E4ytett\u00E4viss\u00E4 vapaakappalety\u00F6asemilla';\n }\n\n // Create subfields if necessary\n ldSubfields.forEach(ldsf => {\n if (!f.subfields.some(sf => sf.code === ldsf.code && sf.value === ldsf.value && !hasOld856LdPhrase(sf))) {\n f.subfields.push(ldsf);\n }\n });\n });\n }\n\n return true;\n\n // We should check existence of URN in f024 i1: '7' $2 urn/URN for this too\n\n async function createURNSubfield(rec) {\n // isbn is picked from the last 020 $a in the record\n // what should we do in case of several 020 $a:s\n const isbn = rec.fields.reduce((acc, f) => {\n if (f.tag === '020') {\n const a = f.subfields.find(sf => sf.code === 'a');\n return a ? a.value : undefined;\n }\n\n return acc;\n }, undefined);\n\n debugData(`isbns: ${isbn}`);\n\n const {generated, value} = await createURN(isbn);\n return {code: 'u', value, generated};\n\n async function createURN(isbn = false) {\n if (isbn) {\n return {generated: false, value: `https://urn.fi/URN:ISBN:${isbn}`};\n }\n\n const response = await fetch(URN_GENERATOR_URL);\n const body = await response.text();\n\n // If we generated URN we could also add it to the 024\n // generated 024 should also have $9 MELINDA<TEMP>\n return {generated: true, value: `https://urn.fi/${body}`};\n }\n }\n\n function hasOld856LdPhrase({code, value}) {\n if (code === 'z' && value === 'K\u00E4ytett\u00E4viss\u00E4 vapaakappalekirjastoissa') {\n return true;\n }\n\n return false;\n }\n\n }\n\n // Later when the new subfields that have f506/f540 -type contents, we should add also them here\n function createLDSubfields() {\n return [\n {\n code: 'z',\n value: 'K\u00E4ytett\u00E4viss\u00E4 vapaakappalety\u00F6asemilla'\n },\n {\n code: '5',\n value: 'FI-Vapaa'\n }\n ];\n }\n\n function fieldHasLDSubfields(field, ldSubfields) {\n if (ldSubfields.every(ldsf => field.subfields.some(sf => sf.code === ldsf.code && sf.value === ldsf.value))) {\n return true;\n }\n }\n\n function validateLD(f856sUrn) {\n debug(`Validating the existence of legal deposit subfields`);\n const ldSubfields = createLDSubfields();\n const f856sUrnWithLdSubfields = f856sUrn.filter(field => fieldHasLDSubfields(field, ldSubfields));\n if (f856sUrnWithLdSubfields.length > 0) {\n debug(`Record has ${f856sUrnWithLdSubfields.length} URN fields with all necessary legal deposit subfields`);\n debugData(`f856sUrnWithLdSubfields: ${JSON.stringify(f856sUrnWithLdSubfields)}`);\n return true;\n }\n return false;\n }\n\n function validate(record) {\n // if not electronic skip this validator\n if (!isElectronicMaterial(record)) {\n debug(`Record is not electronic - no need to validate legal deposit URNs`);\n return {valid: true};\n }\n\n const f856sUrn = record.fields.filter(hasLegalDepositURN);\n\n if (f856sUrn.length > 0) {\n debug(`Record has ${f856sUrn.length} URN fields`);\n debugData(`f856sUrn: ${JSON.stringify(f856sUrn)}`);\n\n if (!isLegalDeposit || validateLD(f856sUrn)) {\n debug(`Record is valid`);\n return {valid: true};\n }\n }\n debug(`No (valid) URN fields - Record is not valid`);\n return {valid: false};\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["//import fetch from 'node-fetch';\nimport {isElectronicMaterial, nvdebug} from './utils.js';\nimport createDebugLogger from 'debug';\n\nconst URN_GENERATOR_URL = 'https://generator.urn.fi/cgi-bin/urn_generator.cgi?type=nbn';\n\n// eslint-disable-next-line max-lines-per-function\nexport default function (isLegalDeposit = false, useMelindaTemp = true) {\n const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:urn');\n const debugData = debug.extend('data');\n const debugDev = debug.extend('dev');\n\n\n //nvdebug(`IS LEGAL DEPOSIT? ${isLegalDeposit ? 'YES' : 'NO'}`, debugDev); // eslint-disable-line no-console\n\n // We should check that the f856 with URN has second indicator '0' (Resource), ' ' (No information provided) or '8' (No display constant generated)\n // - if second indicator is '1' (Version of resource) or '2' (Related resource) the URN in f856 is not correct for the resource described in the record\n\n // This checks only the existence of URNs from the Finnish urn.fi -resolver\n\n function hasLegalDepositURN(field) {\n if (field.tag !== '856' || ['1', '2', '3', '4'].includes(field.ind2)) {\n return false;\n }\n\n // First attempt to fix MET-573. However, does useMelindaTemp come into play as well?\n if (isLegalDeposit && !field.subfields.some(sf => sf.code === '5' && sf.value === 'FI-Vapaa')) {\n return false;\n }\n\n return field.subfields.some(sf => sf.code === 'u' && (/^https?:\\/\\/urn\\.fi/u).test(sf.value));\n }\n\n\n return {\n description: 'Adds URN for record, to 856-field (if not existing). If isLegalDeposit is active, adds legal deposit subfields to the f856s with URN.',\n validate,\n fix\n };\n\n // eslint-disable-next-line max-lines-per-function\n async function fix(record) {\n const f856sUrn = record.fields.filter(hasLegalDepositURN);\n nvdebug(`f856sUrn: ${JSON.stringify(f856sUrn)}`, debugDev);\n\n const ldSubfields = isLegalDeposit ? createLDSubfields() : [];\n nvdebug(`IsLegalDeposit: ${isLegalDeposit}, LegalDepositSubfields: ${JSON.stringify(ldSubfields)}`, debugDev);\n\n // We add the URN even if we're not getting the legalDeposit - where does this URN resolve?\n // We probably should not do these additions\n\n if (f856sUrn.length === 0) {\n const {code, value, generated} = await createURNSubfield(record);\n\n if (generated && useMelindaTemp) {\n const tempSubField = {code: '9', value: 'MELINDA<TEMP>'};\n\n record.insertField({\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [{code, value}, ...ldSubfields, tempSubField]\n });\n\n return true;\n }\n\n record.insertField({\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [{code, value}, ...ldSubfields]\n });\n\n return true;\n }\n\n if (isLegalDeposit) {\n\n // We add here legal deposit information to all URN-f856s - we probably should not do this\n // We should add extra f856 URN / URNs for legal deposits that already have a open (non-legal-deposit) URN\n // How do we decide which URN to use as a template if there are several URNs\n // We should check for existence of a legal deposit URN anyways\n\n f856sUrn.forEach(f => {\n // Change phrase from old to new if field with old phrase is found\n if (f.subfields.some(sf => hasOld856LdPhrase(sf))) {\n f.subfields\n .find(sf => hasOld856LdPhrase(sf))\n .value = 'K\u00E4ytett\u00E4viss\u00E4 vapaakappalety\u00F6asemilla';\n }\n\n // Create subfields if necessary\n ldSubfields.forEach(ldsf => {\n if (!f.subfields.some(sf => sf.code === ldsf.code && sf.value === ldsf.value && !hasOld856LdPhrase(sf))) {\n f.subfields.push(ldsf);\n }\n });\n });\n }\n\n return true;\n\n // We should check existence of URN in f024 i1: '7' $2 urn/URN for this too\n\n async function createURNSubfield(rec) {\n // isbn is picked from the last 020 $a in the record\n // what should we do in case of several 020 $a:s\n const isbn = rec.fields.reduce((acc, f) => {\n if (f.tag === '020') {\n const a = f.subfields.find(sf => sf.code === 'a');\n return a ? a.value : undefined;\n }\n\n return acc;\n }, undefined);\n\n nvdebug(`isbns: ${isbn}`, debugDev);\n\n const {generated, value} = await createURN(isbn);\n return {code: 'u', value, generated};\n\n async function createURN(isbn = false) {\n if (isbn) {\n return {generated: false, value: `https://urn.fi/URN:ISBN:${isbn}`};\n }\n\n const response = await fetch(URN_GENERATOR_URL);\n const body = await response.text();\n\n // If we generated URN we could also add it to the 024\n // generated 024 should also have $9 MELINDA<TEMP>\n return {generated: true, value: `https://urn.fi/${body}`};\n }\n }\n\n function hasOld856LdPhrase({code, value}) {\n if (code === 'z' && value === 'K\u00E4ytett\u00E4viss\u00E4 vapaakappalekirjastoissa') {\n return true;\n }\n\n return false;\n }\n\n }\n\n // Later when the new subfields that have f506/f540 -type contents, we should add also them here\n function createLDSubfields() {\n return [\n {\n code: 'z',\n value: 'K\u00E4ytett\u00E4viss\u00E4 vapaakappalety\u00F6asemilla'\n },\n {\n code: '5',\n value: 'FI-Vapaa'\n }\n ];\n }\n\n function fieldHasLDSubfields(field, ldSubfields) {\n if (ldSubfields.every(ldsf => field.subfields.some(sf => sf.code === ldsf.code && sf.value === ldsf.value))) {\n return true;\n }\n }\n\n function validateLD(f856sUrn) {\n nvdebug(`Validating the existence of legal deposit subfields`, debugDev);\n const ldSubfields = createLDSubfields();\n const f856sUrnWithLdSubfields = f856sUrn.filter(field => fieldHasLDSubfields(field, ldSubfields));\n if (f856sUrnWithLdSubfields.length > 0) {\n nvdebug(`Record has ${f856sUrnWithLdSubfields.length} URN fields with all necessary legal deposit subfields`, debugDev);\n nvdebug(`f856sUrnWithLdSubfields: ${JSON.stringify(f856sUrnWithLdSubfields)}`, debugData);\n return true;\n }\n return false;\n }\n\n function validate(record) {\n // if not electronic skip this validator\n if (!isElectronicMaterial(record)) {\n nvdebug(`Record is not electronic - no need to validate legal deposit URNs`, debugDev);\n return {valid: true};\n }\n\n const f856sUrn = record.fields.filter(hasLegalDepositURN);\n\n if (f856sUrn.length > 0) {\n nvdebug(`Record has ${f856sUrn.length} URN fields`, debugDev);\n nvdebug(`f856sUrn: ${JSON.stringify(f856sUrn)}`, debugData);\n\n if (!isLegalDeposit || validateLD(f856sUrn)) {\n nvdebug(`Record is valid`, debugDev);\n return {valid: true};\n }\n }\n nvdebug(`No (valid) URN fields - Record is not valid`, debugDev);\n return {valid: false};\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["isbn"]
7
7
  }
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "url": "https://github.com/NatLibFi/marc-record-validators-melinda"
15
15
  },
16
16
  "license": "MIT",
17
- "version": "12.0.7",
17
+ "version": "12.0.8",
18
18
  "main": "./dist/index.js",
19
19
  "publishConfig": {
20
20
  "access": "public"
@@ -35,8 +35,8 @@
35
35
  "dev:debug": "cross-env LOG_LEVEL=debug DEBUG=@natlibfi/* NODE_ENV=test"
36
36
  },
37
37
  "dependencies": {
38
- "@natlibfi/iso9-1995": "^1.1.0",
39
- "@natlibfi/issn-verify": "^2.0.1",
38
+ "@natlibfi/iso9-1995": "^1.1.1",
39
+ "@natlibfi/issn-verify": "^2.0.2",
40
40
  "@natlibfi/marc-record": "^10.0.1",
41
41
  "@natlibfi/marc-record-serializers": "^11.0.1",
42
42
  "@natlibfi/marc-record-validate": "^9.0.0",
@@ -46,7 +46,7 @@
46
46
  "cld3-asm": "^4.0.0",
47
47
  "clone": "^2.1.2",
48
48
  "debug": "^4.4.3",
49
- "isbn3": "^2.0.5",
49
+ "isbn3": "^2.0.6",
50
50
  "langs": "^2.0.0",
51
51
  "xml2js": "^0.6.2",
52
52
  "xregexp": "^5.1.2"
@@ -55,11 +55,11 @@
55
55
  "@natlibfi/marc-record-validate": "^9.0.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@natlibfi/fixugen": "^3.0.0",
59
- "@natlibfi/fixura": "^4.0.0",
58
+ "@natlibfi/fixugen": "^3.0.1",
59
+ "@natlibfi/fixura": "^4.0.1",
60
60
  "cross-env": "^10.1.0",
61
61
  "esbuild": "^0.27.3",
62
- "eslint": "^10.0.0",
62
+ "eslint": "^10.0.3",
63
63
  "fetch-mock": "^12.6.0"
64
64
  },
65
65
  "overrides": {
@@ -1,6 +1,10 @@
1
- //import createDebugLogger from 'debug';
1
+ import createDebugLogger from 'debug';
2
2
  import {fieldToString, nvdebug} from './utils.js';
3
3
 
4
+ const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:addMissingField041');
5
+ //const debugData = debug.extend('data');
6
+ const debugDev = debug.extend('dev');
7
+
4
8
  const description = 'Add missing 041 field based on 008/35-37';
5
9
 
6
10
  // const multimediaRegexp = /multimedia/ui;
@@ -12,7 +16,7 @@ export default function () {
12
16
  };
13
17
 
14
18
  function isRealLanguageCode(languageCode = '|||') {
15
- nvdebug(`Language code 008/35-37: ${languageCode}`);
19
+ nvdebug(`Language code 008/35-37: ${languageCode}`, debugDev);
16
20
  if (!languageCode.match(/^[a-z]{3}$/u) || ['mul', 'und', 'zxx'].includes(languageCode)) {
17
21
  return false;
18
22
  }
@@ -43,7 +47,7 @@ export default function () {
43
47
  }
44
48
 
45
49
  function fix(record) {
46
- nvdebug(`FIX ${description}...`);
50
+ nvdebug(`FIX ${description}...`, debugDev);
47
51
  const data = generateContent(record);
48
52
  const res = {message: [], fix: [], valid: true};
49
53
  if (data) {
@@ -54,7 +58,7 @@ export default function () {
54
58
  }
55
59
 
56
60
  function validate(record) {
57
- nvdebug(`VALIDATE ${description}...`);
61
+ nvdebug(`VALIDATE ${description}...`, debugDev);
58
62
  const data = generateContent(record);
59
63
  if (!data) {
60
64
  return {message: [], valid: true};