@natlibfi/marc-record-validators-melinda 12.1.0-alpha.1 → 13.0.0

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 (425) hide show
  1. package/.github/workflows/melinda-node-tests-and-publish.yml +17 -17
  2. package/README.md +1 -1
  3. package/dist/addMissingField041.js +6 -3
  4. package/dist/addMissingField041.js.map +2 -2
  5. package/dist/addMissingField336.js +7 -4
  6. package/dist/addMissingField336.js.map +2 -2
  7. package/dist/addMissingField337.js +6 -3
  8. package/dist/addMissingField337.js.map +2 -2
  9. package/dist/addMissingField338.js +8 -5
  10. package/dist/addMissingField338.js.map +2 -2
  11. package/dist/cyrillux-usemarcon-replacement.js +5 -2
  12. package/dist/cyrillux-usemarcon-replacement.js.map +2 -2
  13. package/dist/cyrillux.js +24 -18
  14. package/dist/cyrillux.js.map +2 -2
  15. package/dist/dataProvenanceUtils.js +19 -0
  16. package/dist/dataProvenanceUtils.js.map +7 -0
  17. package/dist/disambiguateSeriesStatements.js +2 -1
  18. package/dist/disambiguateSeriesStatements.js.map +2 -2
  19. package/dist/disambiguateSeriesStatements.test.js +1 -7
  20. package/dist/disambiguateSeriesStatements.test.js.map +2 -2
  21. package/dist/drop-terms.js +123 -0
  22. package/dist/drop-terms.js.map +7 -0
  23. package/dist/drop-terms.test.js +56 -0
  24. package/dist/drop-terms.test.js.map +7 -0
  25. package/dist/field-008-18-34-character-groups.test.js +1 -7
  26. package/dist/field-008-18-34-character-groups.test.js.map +2 -2
  27. package/dist/field-505-separators.test.js +1 -7
  28. package/dist/field-505-separators.test.js.map +2 -2
  29. package/dist/field-521-fix.test.js +1 -7
  30. package/dist/field-521-fix.test.js.map +2 -2
  31. package/dist/fix-33X.js +7 -4
  32. package/dist/fix-33X.js.map +2 -2
  33. package/dist/fix-33X.test.js +1 -1
  34. package/dist/fix-33X.test.js.map +1 -1
  35. package/dist/fix-country-codes.js +5 -0
  36. package/dist/fix-country-codes.js.map +2 -2
  37. package/dist/fix-country-codes.test.js +1 -7
  38. package/dist/fix-country-codes.test.js.map +2 -2
  39. package/dist/fix-language-codes.js +5 -1
  40. package/dist/fix-language-codes.js.map +2 -2
  41. package/dist/fix-sami-041.js +109 -0
  42. package/dist/fix-sami-041.js.map +7 -0
  43. package/dist/fix-sami-041.test.js +40 -0
  44. package/dist/fix-sami-041.test.js.map +7 -0
  45. package/dist/fixRelatorTerms.js +2 -0
  46. package/dist/fixRelatorTerms.js.map +2 -2
  47. package/dist/fixRelatorTerms.test.js +1 -7
  48. package/dist/fixRelatorTerms.test.js.map +2 -2
  49. package/dist/index.js +18 -4
  50. package/dist/index.js.map +2 -2
  51. package/dist/indicator-fixes.js +15 -1
  52. package/dist/indicator-fixes.js.map +2 -2
  53. package/dist/indicator-fixes.test.js +1 -7
  54. package/dist/indicator-fixes.test.js.map +2 -2
  55. package/dist/item-language.js +41 -39
  56. package/dist/item-language.js.map +2 -2
  57. package/dist/item-language.test.js +3 -3
  58. package/dist/item-language.test.js.map +2 -2
  59. package/dist/melindaCustomMergeFields.js +31 -0
  60. package/dist/melindaCustomMergeFields.js.map +2 -2
  61. package/dist/merge-fields/controlSubfields.js.map +2 -2
  62. package/dist/merge-fields/counterpartField.js +149 -152
  63. package/dist/merge-fields/counterpartField.js.map +3 -3
  64. package/dist/merge-fields/dataProvenance.js +6 -20
  65. package/dist/merge-fields/dataProvenance.js.map +2 -2
  66. package/dist/merge-fields/index.js +1 -1
  67. package/dist/merge-fields/index.js.map +2 -2
  68. package/dist/merge-fields/mergableIndicator.js +1 -5
  69. package/dist/merge-fields/mergableIndicator.js.map +2 -2
  70. package/dist/merge-fields/mergableTag.js +2 -0
  71. package/dist/merge-fields/mergableTag.js.map +2 -2
  72. package/dist/merge-fields/mergeConfig.js +2 -0
  73. package/dist/merge-fields/mergeConfig.js.map +2 -2
  74. package/dist/merge-fields/mergeConstraints.js +38 -35
  75. package/dist/merge-fields/mergeConstraints.js.map +3 -3
  76. package/dist/merge-fields/mergeField.js +4 -3
  77. package/dist/merge-fields/mergeField.js.map +2 -2
  78. package/dist/merge-fields/mergeOrAddSubfield.js +8 -7
  79. package/dist/merge-fields/mergeOrAddSubfield.js.map +2 -2
  80. package/dist/merge-fields/mergeSubfield.js +5 -1
  81. package/dist/merge-fields/mergeSubfield.js.map +2 -2
  82. package/dist/merge-fields/worldKnowledge.js +52 -0
  83. package/dist/merge-fields/worldKnowledge.js.map +2 -2
  84. package/dist/merge-fields.test.js +4 -9
  85. package/dist/merge-fields.test.js.map +2 -2
  86. package/dist/mergeField500Lisapainokset.test.js +1 -7
  87. package/dist/mergeField500Lisapainokset.test.js.map +2 -2
  88. package/dist/mergeRelatorTermFields.js +9 -6
  89. package/dist/mergeRelatorTermFields.js.map +2 -2
  90. package/dist/mergeRelatorTermFields.test.js +1 -7
  91. package/dist/mergeRelatorTermFields.test.js.map +2 -2
  92. package/dist/multiple-subfield-0.test.js +1 -7
  93. package/dist/multiple-subfield-0.test.js.map +2 -2
  94. package/dist/normalize-dashes.js +9 -6
  95. package/dist/normalize-dashes.js.map +2 -2
  96. package/dist/normalize-dashes.test.js +1 -7
  97. package/dist/normalize-dashes.test.js.map +2 -2
  98. package/dist/normalize-identifiers.js.map +2 -2
  99. package/dist/normalize-identifiers.test.js +1 -7
  100. package/dist/normalize-identifiers.test.js.map +2 -2
  101. package/dist/normalize-qualifying-information.test.js +1 -7
  102. package/dist/normalize-qualifying-information.test.js.map +2 -2
  103. package/dist/normalize-utf8-diacritics.js.map +2 -2
  104. package/dist/normalize-utf8-diacritics.test.js +1 -7
  105. package/dist/normalize-utf8-diacritics.test.js.map +2 -2
  106. package/dist/normalizeFieldForComparison.js +24 -14
  107. package/dist/normalizeFieldForComparison.js.map +2 -2
  108. package/dist/normalizeSubfieldValueForComparison.js.map +1 -1
  109. package/dist/prepublicationUtils.js +1 -1
  110. package/dist/prepublicationUtils.js.map +2 -2
  111. package/dist/punctuation.test.js +1 -7
  112. package/dist/punctuation.test.js.map +2 -2
  113. package/dist/punctuation2.js +15 -12
  114. package/dist/punctuation2.js.map +2 -2
  115. package/dist/punctuation2.test.js +1 -7
  116. package/dist/punctuation2.test.js.map +2 -2
  117. package/dist/reindexSubfield6OccurenceNumbers.js +11 -10
  118. package/dist/reindexSubfield6OccurenceNumbers.js.map +2 -2
  119. package/dist/reindexSubfield6OccurenceNumbers.test.js +1 -7
  120. package/dist/reindexSubfield6OccurenceNumbers.test.js.map +2 -2
  121. package/dist/remove-041-zxx.js +56 -0
  122. package/dist/remove-041-zxx.js.map +7 -0
  123. package/dist/remove-041-zxx.test.js +40 -0
  124. package/dist/remove-041-zxx.test.js.map +7 -0
  125. package/dist/removeDuplicateDataFields.js +4 -26
  126. package/dist/removeDuplicateDataFields.js.map +2 -2
  127. package/dist/removeDuplicateDataFields.test.js +1 -7
  128. package/dist/removeDuplicateDataFields.test.js.map +2 -2
  129. package/dist/removeInferiorDataFields.js +82 -45
  130. package/dist/removeInferiorDataFields.js.map +3 -3
  131. package/dist/removeInferiorDataFields.test.js +5 -9
  132. package/dist/removeInferiorDataFields.test.js.map +2 -2
  133. package/dist/resolveOrphanedSubfield6s.js +4 -5
  134. package/dist/resolveOrphanedSubfield6s.js.map +2 -2
  135. package/dist/resolveOrphanedSubfield6s.test.js +1 -7
  136. package/dist/resolveOrphanedSubfield6s.test.js.map +2 -2
  137. package/dist/sanitize-vocabulary-source-codes.test.js +1 -7
  138. package/dist/sanitize-vocabulary-source-codes.test.js.map +2 -2
  139. package/dist/sortFields.js +16 -1
  140. package/dist/sortFields.js.map +2 -2
  141. package/dist/sortFields.test.js +1 -7
  142. package/dist/sortFields.test.js.map +2 -2
  143. package/dist/sortRelatorTerms.test.js +1 -7
  144. package/dist/sortRelatorTerms.test.js.map +2 -2
  145. package/dist/sortSubfields.js +27 -21
  146. package/dist/sortSubfields.js.map +2 -2
  147. package/dist/sortSubfields.test.js +1 -7
  148. package/dist/sortSubfields.test.js.map +2 -2
  149. package/dist/stripPunctuation.js +7 -3
  150. package/dist/stripPunctuation.js.map +2 -2
  151. package/dist/stripPunctuation.test.js +1 -7
  152. package/dist/stripPunctuation.test.js.map +2 -2
  153. package/dist/subfield6Utils.js +2 -12
  154. package/dist/subfield6Utils.js.map +2 -2
  155. package/dist/subfield8Utils.js +0 -5
  156. package/dist/subfield8Utils.js.map +2 -2
  157. package/dist/subfieldValueNormalizations.test.js +1 -7
  158. package/dist/subfieldValueNormalizations.test.js.map +2 -2
  159. package/dist/sync-007-and-300.test.js +1 -7
  160. package/dist/sync-007-and-300.test.js.map +2 -2
  161. package/dist/sync-language.js +103 -0
  162. package/dist/sync-language.js.map +7 -0
  163. package/dist/sync-language.test.js +40 -0
  164. package/dist/sync-language.test.js.map +7 -0
  165. package/dist/translate-terms.js +124 -87
  166. package/dist/translate-terms.js.map +3 -3
  167. package/dist/translate-terms.test.js +5 -8
  168. package/dist/translate-terms.test.js.map +2 -2
  169. package/dist/typeOfDate-008.js +3 -1
  170. package/dist/typeOfDate-008.js.map +2 -2
  171. package/dist/update-field-540.js.map +2 -2
  172. package/dist/update-field-540.test.js +1 -7
  173. package/dist/update-field-540.test.js.map +2 -2
  174. package/dist/urn.js +13 -12
  175. package/dist/urn.js.map +2 -2
  176. package/dist/urn.test.js +1 -7
  177. package/dist/urn.test.js.map +2 -2
  178. package/dist/utils.js +29 -3
  179. package/dist/utils.js.map +2 -2
  180. package/package.json +20 -27
  181. package/src/addMissingField041.js +8 -4
  182. package/src/addMissingField336.js +10 -5
  183. package/src/addMissingField337.js +9 -5
  184. package/src/addMissingField338.js +11 -6
  185. package/src/cyrillux-usemarcon-replacement.js +9 -5
  186. package/src/cyrillux.js +34 -23
  187. package/src/dataProvenanceUtils.js +21 -0
  188. package/src/disambiguateSeriesStatements.js +4 -1
  189. package/src/disambiguateSeriesStatements.test.js +3 -8
  190. package/src/drop-terms.js +164 -0
  191. package/src/drop-terms.test.js +81 -0
  192. package/src/field-008-18-34-character-groups.test.js +3 -8
  193. package/src/field-505-separators.test.js +3 -8
  194. package/src/field-521-fix.test.js +3 -8
  195. package/src/fix-33X.js +10 -6
  196. package/src/fix-33X.test.js +1 -1
  197. package/src/fix-country-codes.js +7 -3
  198. package/src/fix-country-codes.test.js +3 -8
  199. package/src/fix-language-codes.js +8 -4
  200. package/src/fix-sami-041.js +147 -0
  201. package/src/fix-sami-041.test.js +52 -0
  202. package/src/fixRelatorTerms.js +5 -0
  203. package/src/fixRelatorTerms.test.js +3 -8
  204. package/src/index.js +20 -7
  205. package/src/indicator-fixes.js +22 -7
  206. package/src/indicator-fixes.test.js +3 -8
  207. package/src/item-language.js +49 -42
  208. package/src/item-language.test.js +3 -3
  209. package/src/melindaCustomMergeFields.js +31 -0
  210. package/src/merge-fields/controlSubfields.js +0 -1
  211. package/src/merge-fields/counterpartField.js +191 -290
  212. package/src/merge-fields/dataProvenance.js +8 -25
  213. package/src/merge-fields/index.js +1 -1
  214. package/src/merge-fields/mergableIndicator.js +4 -5
  215. package/src/merge-fields/mergableTag.js +2 -0
  216. package/src/merge-fields/mergeConfig.js +2 -1
  217. package/src/merge-fields/mergeConstraints.js +41 -36
  218. package/src/merge-fields/mergeField.js +4 -7
  219. package/src/merge-fields/mergeOrAddSubfield.js +8 -7
  220. package/src/merge-fields/mergeSubfield.js +11 -2
  221. package/src/merge-fields/worldKnowledge.js +77 -3
  222. package/src/merge-fields.test.js +5 -10
  223. package/src/mergeField500Lisapainokset.test.js +3 -8
  224. package/src/mergeRelatorTermFields.js +12 -11
  225. package/src/mergeRelatorTermFields.test.js +3 -8
  226. package/src/multiple-subfield-0.test.js +3 -8
  227. package/src/normalize-dashes.js +13 -7
  228. package/src/normalize-dashes.test.js +3 -8
  229. package/src/normalize-identifiers.js +12 -19
  230. package/src/normalize-identifiers.test.js +3 -8
  231. package/src/normalize-qualifying-information.test.js +3 -8
  232. package/src/normalize-utf8-diacritics.js +6 -3
  233. package/src/normalize-utf8-diacritics.test.js +3 -8
  234. package/src/normalizeFieldForComparison.js +39 -22
  235. package/src/normalizeSubfieldValueForComparison.js +2 -2
  236. package/src/prepublicationUtils.js +1 -1
  237. package/src/punctuation.test.js +3 -8
  238. package/src/punctuation2.js +44 -40
  239. package/src/punctuation2.test.js +3 -8
  240. package/src/reindexSubfield6OccurenceNumbers.js +13 -11
  241. package/src/reindexSubfield6OccurenceNumbers.test.js +3 -8
  242. package/src/remove-041-zxx.js +85 -0
  243. package/src/remove-041-zxx.test.js +52 -0
  244. package/src/removeDuplicateDataFields.js +30 -28
  245. package/src/removeDuplicateDataFields.test.js +3 -8
  246. package/src/removeInferiorDataFields.js +126 -71
  247. package/src/removeInferiorDataFields.test.js +7 -10
  248. package/src/resolveOrphanedSubfield6s.js +7 -5
  249. package/src/resolveOrphanedSubfield6s.test.js +3 -8
  250. package/src/sanitize-vocabulary-source-codes.test.js +3 -8
  251. package/src/sortFields.js +20 -1
  252. package/src/sortFields.test.js +3 -8
  253. package/src/sortRelatorTerms.test.js +3 -8
  254. package/src/sortSubfields.js +31 -25
  255. package/src/sortSubfields.test.js +3 -8
  256. package/src/stripPunctuation.js +10 -5
  257. package/src/stripPunctuation.test.js +3 -8
  258. package/src/subfield6Utils.js +31 -33
  259. package/src/subfield8Utils.js +10 -7
  260. package/src/subfieldValueNormalizations.test.js +3 -8
  261. package/src/sync-007-and-300.test.js +3 -8
  262. package/src/sync-language.js +148 -0
  263. package/src/sync-language.test.js +52 -0
  264. package/src/translate-terms.js +168 -109
  265. package/src/translate-terms.test.js +12 -16
  266. package/src/typeOfDate-008.js +4 -1
  267. package/src/update-field-540.js +7 -5
  268. package/src/update-field-540.test.js +3 -8
  269. package/src/urn.js +17 -13
  270. package/src/urn.test.js +3 -8
  271. package/src/utils.js +39 -12
  272. package/test-fixtures/cyrillux/f02b/expectedResult.json +23 -0
  273. package/test-fixtures/cyrillux/f02b/metadata.json +11 -0
  274. package/test-fixtures/cyrillux/f02b/record.json +14 -0
  275. package/test-fixtures/cyrillux/f14/expectedResult.json +32 -0
  276. package/test-fixtures/cyrillux/f14/metadata.json +10 -0
  277. package/test-fixtures/cyrillux/f14/record.json +14 -0
  278. package/test-fixtures/drop-terms/01/expectedResult.json +31 -0
  279. package/test-fixtures/drop-terms/01/metadata.json +6 -0
  280. package/test-fixtures/drop-terms/01/record.json +35 -0
  281. package/test-fixtures/drop-terms/02/expectedResult.json +7 -0
  282. package/test-fixtures/drop-terms/02/metadata.json +6 -0
  283. package/test-fixtures/drop-terms/02/record.json +40 -0
  284. package/test-fixtures/drop-terms/03/expectedResult.json +6 -0
  285. package/test-fixtures/drop-terms/03/metadata.json +18 -0
  286. package/test-fixtures/drop-terms/03/record.json +39 -0
  287. package/test-fixtures/drop-terms/04/expectedResult.json +6 -0
  288. package/test-fixtures/drop-terms/04/metadata.json +19 -0
  289. package/test-fixtures/drop-terms/04/record.json +24 -0
  290. package/test-fixtures/fix-language-codes/02/metadata.json +1 -1
  291. package/test-fixtures/fix-relator-terms/f02/metadata.json +1 -1
  292. package/test-fixtures/fix-relator-terms/f04/expectedResult.json +18 -0
  293. package/test-fixtures/fix-relator-terms/f04/metadata.json +5 -0
  294. package/test-fixtures/fix-relator-terms/f04/record.json +20 -0
  295. package/test-fixtures/fix-sami-041/01/expectedResult.json +6 -0
  296. package/test-fixtures/fix-sami-041/01/metadata.json +4 -0
  297. package/test-fixtures/fix-sami-041/01/record.json +14 -0
  298. package/test-fixtures/fix-sami-041/02/expectedResult.json +10 -0
  299. package/test-fixtures/fix-sami-041/02/metadata.json +4 -0
  300. package/test-fixtures/fix-sami-041/02/record.json +8 -0
  301. package/test-fixtures/fix-sami-041/03/expectedResult.json +5 -0
  302. package/test-fixtures/fix-sami-041/03/metadata.json +5 -0
  303. package/test-fixtures/fix-sami-041/03/record.json +8 -0
  304. package/test-fixtures/fix-sami-041/04/expectedResult.json +7 -0
  305. package/test-fixtures/fix-sami-041/04/metadata.json +4 -0
  306. package/test-fixtures/fix-sami-041/04/record.json +10 -0
  307. package/test-fixtures/fix-sami-041/05/expectedResult.json +10 -0
  308. package/test-fixtures/fix-sami-041/05/metadata.json +6 -0
  309. package/test-fixtures/fix-sami-041/05/record.json +8 -0
  310. package/test-fixtures/fix-sami-041/06/expectedResult.json +10 -0
  311. package/test-fixtures/fix-sami-041/06/metadata.json +4 -0
  312. package/test-fixtures/fix-sami-041/06/record.json +9 -0
  313. package/test-fixtures/indicator-fixes/11/expectedResult.json +10 -0
  314. package/test-fixtures/indicator-fixes/11/metadata.json +4 -0
  315. package/test-fixtures/indicator-fixes/11/record.json +10 -0
  316. package/test-fixtures/merge-fields/f042_01/expectedResult.json +12 -0
  317. package/test-fixtures/merge-fields/f042_01/metadata.json +6 -0
  318. package/test-fixtures/merge-fields/f042_01/record.json +13 -0
  319. package/test-fixtures/merge-fields/f05/metadata.json +1 -1
  320. package/test-fixtures/merge-fields/f06/expectedResult.json +42 -0
  321. package/test-fixtures/merge-fields/f06/metadata.json +6 -0
  322. package/test-fixtures/merge-fields/f06/record.json +41 -0
  323. package/test-fixtures/merge-fields/f07/expectedResult.json +18 -0
  324. package/test-fixtures/merge-fields/f07/metadata.json +6 -0
  325. package/test-fixtures/merge-fields/f07/record.json +18 -0
  326. package/test-fixtures/merge-fields/f08/expectedResult.json +12 -0
  327. package/test-fixtures/merge-fields/f08/metadata.json +7 -0
  328. package/test-fixtures/merge-fields/f08/record.json +10 -0
  329. package/test-fixtures/merge-fields/f09/expectedResult.json +14 -0
  330. package/test-fixtures/merge-fields/f09/metadata.json +6 -0
  331. package/test-fixtures/merge-fields/f09/record.json +14 -0
  332. package/test-fixtures/merge-fields/f10/expectedResult.json +25 -0
  333. package/test-fixtures/merge-fields/f10/metadata.json +6 -0
  334. package/test-fixtures/merge-fields/f10/record.json +25 -0
  335. package/test-fixtures/merge-fields/f11/expectedResult.json +40 -0
  336. package/test-fixtures/merge-fields/f11/metadata.json +7 -0
  337. package/test-fixtures/merge-fields/f11/record.json +50 -0
  338. package/test-fixtures/merge-fields/f12/expectedResult.json +17 -0
  339. package/test-fixtures/merge-fields/f12/metadata.json +6 -0
  340. package/test-fixtures/merge-fields/f12/record.json +25 -0
  341. package/test-fixtures/merge-fields/f13/expectedResult.json +18 -0
  342. package/test-fixtures/merge-fields/f13/metadata.json +6 -0
  343. package/test-fixtures/merge-fields/f13/record.json +28 -0
  344. package/test-fixtures/merge-fields/f14/expectedResult.json +25 -0
  345. package/test-fixtures/merge-fields/f14/metadata.json +6 -0
  346. package/test-fixtures/merge-fields/f14/record.json +25 -0
  347. package/test-fixtures/merge-fields/f15/expectedResult.json +8 -0
  348. package/test-fixtures/merge-fields/f15/metadata.json +6 -0
  349. package/test-fixtures/merge-fields/f15/record.json +9 -0
  350. package/test-fixtures/merge-fields/f16/expectedResult.json +12 -0
  351. package/test-fixtures/merge-fields/f16/metadata.json +7 -0
  352. package/test-fixtures/merge-fields/f16/record.json +13 -0
  353. package/test-fixtures/merge-fields/f300_01/expectedResult.json +9 -0
  354. package/test-fixtures/merge-fields/f300_01/metadata.json +6 -0
  355. package/test-fixtures/merge-fields/f300_01/record.json +8 -0
  356. package/test-fixtures/merge-fields/f300_02/expectedResult.json +13 -0
  357. package/test-fixtures/merge-fields/f300_02/metadata.json +6 -0
  358. package/test-fixtures/merge-fields/f300_02/record.json +16 -0
  359. package/test-fixtures/merge-fields/f300_03/expectedResult.json +13 -0
  360. package/test-fixtures/merge-fields/f300_03/metadata.json +6 -0
  361. package/test-fixtures/merge-fields/f300_03/record.json +16 -0
  362. package/test-fixtures/merge-fields/f490_01/expectedResult.json +13 -0
  363. package/test-fixtures/merge-fields/f490_01/metadata.json +6 -0
  364. package/test-fixtures/merge-fields/f490_01/record.json +16 -0
  365. package/test-fixtures/remove-041-zxx/01/expectedResult.json +5 -0
  366. package/test-fixtures/remove-041-zxx/01/metadata.json +5 -0
  367. package/test-fixtures/remove-041-zxx/01/record.json +10 -0
  368. package/test-fixtures/remove-041-zxx/02/expectedResult.json +7 -0
  369. package/test-fixtures/remove-041-zxx/02/metadata.json +5 -0
  370. package/test-fixtures/remove-041-zxx/02/record.json +9 -0
  371. package/test-fixtures/remove-041-zxx/11/expectedResult.json +10 -0
  372. package/test-fixtures/remove-041-zxx/11/metadata.json +5 -0
  373. package/test-fixtures/remove-041-zxx/11/record.json +9 -0
  374. package/test-fixtures/remove-041-zxx/12/expectedResult.json +10 -0
  375. package/test-fixtures/remove-041-zxx/12/metadata.json +5 -0
  376. package/test-fixtures/remove-041-zxx/12/record.json +9 -0
  377. package/test-fixtures/remove-inferior-datafields/f17/expectedResult.json +11 -0
  378. package/test-fixtures/remove-inferior-datafields/f17/metadata.json +5 -0
  379. package/test-fixtures/remove-inferior-datafields/f17/record.json +15 -0
  380. package/test-fixtures/remove-inferior-datafields/f18/expectedResult.json +13 -0
  381. package/test-fixtures/remove-inferior-datafields/f18/metadata.json +5 -0
  382. package/test-fixtures/remove-inferior-datafields/f18/record.json +18 -0
  383. package/test-fixtures/remove-inferior-datafields/f19/expectedResult.json +17 -0
  384. package/test-fixtures/remove-inferior-datafields/f19/metadata.json +5 -0
  385. package/test-fixtures/remove-inferior-datafields/f19/record.json +18 -0
  386. package/test-fixtures/remove-inferior-datafields/f20/expectedResult.json +15 -0
  387. package/test-fixtures/remove-inferior-datafields/f20/metadata.json +5 -0
  388. package/test-fixtures/remove-inferior-datafields/f20/record.json +29 -0
  389. package/test-fixtures/sort-fields/15/input.json +9 -0
  390. package/test-fixtures/sort-fields/15/metadata.json +5 -0
  391. package/test-fixtures/sort-fields/15/result.json +10 -0
  392. package/test-fixtures/sync-language/01/expectedResult.json +5 -0
  393. package/test-fixtures/sync-language/01/metadata.json +5 -0
  394. package/test-fixtures/sync-language/01/record.json +7 -0
  395. package/test-fixtures/sync-language/02/expectedResult.json +6 -0
  396. package/test-fixtures/sync-language/02/metadata.json +5 -0
  397. package/test-fixtures/sync-language/02/record.json +10 -0
  398. package/test-fixtures/sync-language/03/expectedResult.json +6 -0
  399. package/test-fixtures/sync-language/03/metadata.json +5 -0
  400. package/test-fixtures/sync-language/03/record.json +6 -0
  401. package/test-fixtures/sync-language/10/expectedResult.json +10 -0
  402. package/test-fixtures/sync-language/10/metadata.json +5 -0
  403. package/test-fixtures/sync-language/10/record.json +8 -0
  404. package/test-fixtures/sync-language/11/expectedResult.json +10 -0
  405. package/test-fixtures/sync-language/11/metadata.json +5 -0
  406. package/test-fixtures/sync-language/11/record.json +7 -0
  407. package/test-fixtures/sync-language/12/expectedResult.json +9 -0
  408. package/test-fixtures/sync-language/12/metadata.json +6 -0
  409. package/test-fixtures/sync-language/12/record.json +6 -0
  410. package/test-fixtures/sync-language/13/expectedResult.json +10 -0
  411. package/test-fixtures/sync-language/13/metadata.json +5 -0
  412. package/test-fixtures/sync-language/13/record.json +8 -0
  413. package/test-fixtures/sync-language/14/expectedResult.json +9 -0
  414. package/test-fixtures/sync-language/14/metadata.json +5 -0
  415. package/test-fixtures/sync-language/14/record.json +7 -0
  416. package/test-fixtures/sync-language/15/expectedResult.json +9 -0
  417. package/test-fixtures/sync-language/15/metadata.json +5 -0
  418. package/test-fixtures/sync-language/15/record.json +7 -0
  419. package/test-fixtures/translate-terms/05/expectedResult.json +12 -0
  420. package/test-fixtures/translate-terms/05/metadata.json +7 -0
  421. package/test-fixtures/translate-terms/05/record.json +11 -0
  422. package/test-fixtures/translate-terms/06/expectedResult.json +12 -0
  423. package/test-fixtures/translate-terms/06/metadata.json +7 -0
  424. package/test-fixtures/translate-terms/06/record.json +11 -0
  425. package/test-fixtures/translate-terms-data.js +23 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/merge-fields/controlSubfields.js"],
4
- "sourcesContent": ["import {MarcRecord} from '@natlibfi/marc-record';\nimport createDebugLogger from 'debug';\nimport {fieldHasSubfield, fieldToString, nvdebug, nvdebugSubfieldArray, subfieldIsRepeatable, subfieldsAreIdentical} from '../utils.js';\n\n//import {normalizeControlSubfieldValue} from './normalizeIdentifier';\nimport {normalizeControlSubfieldValue} from '../normalize-identifiers.js';\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:merge-fields:controlSubfields');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nfunction subfieldsAreEqual(field1, field2, subfieldCode) {\n // Check OK if neither one has given subfield.\n // Check fails if one field has given subfield and the other one does not\n if (!fieldHasSubfield(field1, subfieldCode)) {\n return !fieldHasSubfield(field2, subfieldCode);\n }\n if (!fieldHasSubfield(field2, subfieldCode)) {\n return false;\n }\n // Compare $3 subfields. If everything matches, OK, else FAIL:\n const sfSet1 = field1.subfields.filter(subfield => subfield.code === subfieldCode);\n const sfSet2 = field2.subfields.filter(subfield => subfield.code === subfieldCode);\n return MarcRecord.isEqual(sfSet1, sfSet2);\n}\n\nfunction subfieldsAreEmpty(field1, field2, subfieldCode) {\n if (!fieldHasSubfield(field1, subfieldCode) && !fieldHasSubfield(field2, subfieldCode)) {\n return true;\n }\n return false;\n}\n\n\nfunction sixlessIsSubset(fieldWith6, fieldWithout6) {\n // Remove $0 and $1, and then check that remaining $6-less field is a subset of the one with $6.\n // No need to check indicators.\n // NB! We could use punctuation-stripping here.\n const subset = fieldWithout6.subfields.filter(subfield => !['0', '1'].includes(subfield.code));\n return subset.every(sf => fieldWith6.subfields.some(sf2 => subfieldsAreIdentical(sf, sf2)));\n //return MarcRecord.isEqual(strippedField1, strippedField2);\n}\n\nfunction controlSubfield6PermitsMerge(field1, field2) {\n if (subfieldsAreEmpty(field1, field2, '6')) {\n return true;\n }\n\n // Handle cases where one has a $6 and the other has not:\n // Should this accept $0 (FI-ASTERI-N) vs none?\n if (!fieldHasSubfield(field1, '6') && fieldHasSubfield(field2, '6') && sixlessIsSubset(field2, field1)) {\n return true;\n }\n if (!fieldHasSubfield(field2, '6') && fieldHasSubfield(field1, '6') && sixlessIsSubset(field1, field2)) {\n return true;\n }\n\n // There are at least two (plus) fields involved (Field XXX (one) and field 880 (one plus).\n // Thus this generic solution can't handle them. Postprocess step removes some chains instead!\n debugDev(` controlSubfield6PermitsMerge() fails always on generic part (feature).`);\n return false;\n}\n\nfunction controlSubfield5PermitsMerge(field1, field2) {\n // field1.$5 XOR field2.$5 means false, NEITHER and BOTH mean true, regardless of value\n if (!fieldHasSubfield(field1, '5')) {\n if (!fieldHasSubfield(field2, '5')) {\n return true; // If neither one has $5, it's ok to merge\n }\n // If $5 contents are same, merge can be perfomed:\n const fives1 = field1.subfields.filter(sf => sf.code === '5');\n const fives2 = field2.subfields.filter(sf => sf.code === '5');\n if (fives1.every(sf1 => fives2.some(sf2 => sf1.value === sf2.value)) && fives2.every(sf2 => fives1.some(sf1 => sf1.value === sf2.value))) {\n return true;\n }\n return false;\n }\n if (!fieldHasSubfield(field2, '5')) {\n return false;\n }\n return true;\n}\n\nfunction controlSubfield9PermitsMerge(baseField, sourceField) {\n const baseFieldSubfields9 = baseField.subfields.filter(sf => sf.code === '9');\n const sourceFieldSubfields9 = sourceField.subfields.filter(sf => sf.code === '9');\n\n //nvdebug('CHECK $9', debugDev);\n // There are no $9s. Skip:\n if (baseFieldSubfields9.length === 0 && sourceFieldSubfields9.length === 0) {\n //nvdebug(` No subfield $9 detected`, debugDev);\n return true;\n }\n\n if (keepOrDropPreventsMerge()) {\n nvdebug(` Subfield $9 KEEPs and DROPs disallow merge`, debugDev);\n return false;\n }\n\n if (transPreventsMerge()) {\n nvdebug(` Subfield $9 <TRANS> mismatch disallows merge`, debugDev);\n return false;\n }\n\n //nvdebug('CHECK $9 OK', debugDev);\n\n return true;\n\n function subfieldHasKeepOrDrop(subfield) {\n // nvdebug(`Has <KEEP>? ${subfieldToString(subfield)}`, debugDev);\n return subfield.code === '9' && (/(?:<KEEP>|<DROP>)/u).test(subfield.value);\n }\n\n function subfieldHasTrans(subfield) {\n return subfield.code === '9' && (/<TRANS>/u).test(subfield.value);\n }\n\n function transPreventsMerge() {\n const trans1 = baseFieldSubfields9.filter(sf => subfieldHasTrans(sf));\n const trans2 = sourceFieldSubfields9.filter(sf => subfieldHasTrans(sf));\n if (trans1.length > 0 && trans2.length > 0) {\n if (!MarcRecord.isEqual(trans1, trans2)) {\n return true;\n }\n }\n return false;\n }\n\n function retainSubfieldForKeepComparison(subfield) {\n // Don't compare <KEEP>, <DROP> nor <TRANS> here (<TRANS> has it's own check)\n if (subfieldHasKeepOrDrop(subfield) || subfieldHasTrans(subfield)) {\n return false;\n }\n\n if (['0', '1'].includes(subfield.code)) {\n return false;\n }\n if (['100', '600', '700', '800'].includes(baseField.tag)) {\n // Despite $9 KEEP/DROP, we are interested in merging $d years (better than two separate fields)\n if (['d'].includes(subfield.code)) {\n return false;\n }\n }\n\n\n return true;\n }\n\n function acceptKeeplessSourceSubfield(sourceSubfield, tag, subfieldCode, subfieldValue) {\n if (sourceSubfield.code !== subfieldCode) {\n return false;\n }\n // In this context, there's no need to check the value of a non-repeatable subfield.\n // If value is different, pairing will fail when comparing the subfield itself.\n // This allows us to tolerate little differences in punctuation: different punctuation does not get copied to base,\n // so they don't alter base and and thus redundant when comparing.\n if (!subfieldIsRepeatable(tag, subfieldCode)) {\n return true;\n }\n return sourceSubfield.value === subfieldValue;\n }\n\n function keepOrDropPreventsMerge() {\n const keepOrDrop1 = baseFieldSubfields9.filter(sf => subfieldHasKeepOrDrop(sf));\n const keepOrDrop2 = sourceFieldSubfields9.filter(sf => subfieldHasKeepOrDrop(sf));\n\n if (keepOrDrop1.length === 0 && keepOrDrop2.length === 0) {\n return false;\n }\n\n if (baseField.tag.charAt(0) === '1' && !keepOrDrop2.some(sf => (/<DROP>/u).test(sf.value))) {\n return false;\n }\n\n const sf9lessField1 = baseField.subfields.filter(subfield => retainSubfieldForKeepComparison(subfield));\n const sf9lessField2 = sourceField.subfields.filter(subfield => retainSubfieldForKeepComparison(subfield));\n\n nvdebugSubfieldArray(baseField.subfields, 'FIELD ', debugDev);\n nvdebugSubfieldArray(sf9lessField1, 'FILTER ', debugDev);\n\n nvdebugSubfieldArray(sourceField.subfields, 'FIELD2 ', debugDev);\n nvdebugSubfieldArray(sf9lessField2, 'FILTER2 ', debugDev);\n\n // Keepless field can be a subset field with <KEEP>/<DROP>! Note that punctuation still causes remnants to fail.\n if (keepOrDrop1.length === 0) {\n return !sf9lessField1.every(sf => sf9lessField2.some(sf2 => subfieldsAreIdentical(sf, sf2)));\n }\n // However, to alleviate the above-mentioned punctuation problem, we can check keep/drop-less *source* subfields\n if (keepOrDrop2.length === 0) {\n const unhandledSubfield = sf9lessField2.find(sf2 => !sf9lessField1.some(sf => acceptKeeplessSourceSubfield(sf2, baseField.tag, sf.code, sf.value)));\n if (unhandledSubfield) {\n //nvdebug(`Failed to pair ${subfieldToString(unhandledSubfield)}`, debugDev);\n return true;\n }\n //return !sf9lessField2.every(sf2 => sf9lessField1.some(sf => subfieldsAreIdentical(sf, sf2)));\n return false;\n }\n\n //nvdebugSubfieldArray(sf9lessField2, 'SOURCE(?)', debugDev);\n //nvdebugSubfieldArray(sf9lessField1, 'BASE(?) ', debugDev);\n\n // $9 <KEEP> or <DROP> detected on both fields.\n // Non-keeps and non-drops must be equal, otherwise fail:\n if (MarcRecord.isEqual(sf9lessField1, sf9lessField2)) {\n return false;\n }\n // Prevent:\n return true;\n }\n}\n\nfunction getPrefix(value) {\n const normalizedValue = normalizeControlSubfieldValue(value);\n\n if (normalizedValue.match(/^\\([^)]+\\)[0-9]+$/u)) {\n return normalizedValue.substr(0, normalizedValue.indexOf(')') + 1);\n }\n\n if (value.match(/^https?:\\/\\//u)) {\n return normalizedValue.substr(0, normalizedValue.lastIndexOf('/') + 1);\n }\n\n return '';\n}\n\nfunction isMatchAfterNormalization(currSubfield, otherField) {\n // NB! Add implement isni normalizations (to normalize.js) and apply here:\n const normalizedCurrSubfieldValue = normalizeControlSubfieldValue(currSubfield.value);\n const prefix = getPrefix(normalizedCurrSubfieldValue);\n\n //debug(`FFS-PREFIX '${prefix}'`);\n // Look for same prefix + different identifier\n const hits = otherField.subfields.filter(sf2 => sf2.code === currSubfield.code && normalizeControlSubfieldValue(sf2.value).indexOf(prefix) === 0);\n if (hits.length === 0 || // <-- Nothing found, so it can't be a mismatch\n // Every opposing subfields match:\n hits.every(sf2 => normalizedCurrSubfieldValue === normalizeControlSubfieldValue(sf2.value))) {\n debugDev(`Subfield \u2021${currSubfield.code} check OK: No opposing ${prefix} prefixes found.`);\n return true;\n }\n\n debugDev(`Subfield \u2021${currSubfield.code} check FAILED: \u2021${currSubfield.code} '${currSubfield.value}' vs \u2021${currSubfield.code} '${hits[0].value}'.`);\n return false;\n}\n\nfunction controlSubfieldContainingIdentifierPermitsMerge(field1, field2, subfieldCode) {\n if (!fieldHasSubfield(field1, subfieldCode, null) || !fieldHasSubfield(field2, subfieldCode, null)) {\n return true;\n }\n\n const result = field1.subfields.every(subfield => {\n if (subfield.code !== subfieldCode) {\n return true;\n }\n\n debugDev(`Compare \u2021${subfieldCode} '${subfield.value}' with '${fieldToString(field2)}'.`);\n if (fieldHasSubfield(field2, field1.code, field1.value)) {\n return true;\n }\n\n return isMatchAfterNormalization(subfield, field2, subfieldCode);\n });\n\n if (!result) {\n debugDev(`Control subfield '${subfieldCode}' check failed.`);\n return false;\n }\n return true;\n}\n\nconst controlSubfieldsContainingIdentifier = ['w', '0', '1', '2']; // 2 ain't identifier, but the logic can be applied here as well\n\nexport function controlSubfieldsPermitMerge(baseField, sourceField) {\n // Check $w, $0, $1, $2 (which isn't an identifier per se, but the sama logic can be applied)\n if (!controlSubfieldsContainingIdentifier.every(subfieldCode => controlSubfieldContainingIdentifierPermitsMerge(baseField, sourceField, subfieldCode))) {\n //debug(' control subfields with identifiers failed');\n return false;\n }\n\n if (!subfieldsAreEqual(baseField, sourceField, '3')) {\n //debug(' similar control subfield fails');\n return false;\n }\n\n if (!controlSubfield5PermitsMerge(baseField, sourceField) || !controlSubfield6PermitsMerge(baseField, sourceField) || !controlSubfield9PermitsMerge(baseField, sourceField)) {\n return false;\n }\n // We fully prevent merging $8 subfields here, as they affect multiple fields! Also these would get screwed:\n // 38211 |8 3\\u |a kuoro |2 seko\n // 38211 |8 6\\u |a kuoro |2 seko |9 VIOLA<DROP>\n // Thus only copy works with $8...\n if (!subfieldsAreEmpty(baseField, sourceField, '8')) {\n // We could alleviate this a bit esp. for non-repeatable fields.\n // At least, if the source has '8' and otherwise the two fields are identical...\n const subsetOfSourceField = {\n 'tag': sourceField.tag,\n 'ind1': sourceField.ind1,\n 'ind2': sourceField.ind2, subfields: sourceField.subfields.filter(sf => sf.code !== '8')\n };\n if (fieldToString(baseField) === fieldToString(subsetOfSourceField)) {\n return true;\n }\n //debug(' csf8 failed');\n return false;\n }\n\n return true;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {MarcRecord} from '@natlibfi/marc-record';\nimport createDebugLogger from 'debug';\nimport {fieldHasSubfield, fieldToString, nvdebug, nvdebugSubfieldArray, subfieldIsRepeatable, subfieldsAreIdentical} from '../utils.js';\n\nimport {normalizeControlSubfieldValue} from '../normalize-identifiers.js';\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:merge-fields:controlSubfields');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nfunction subfieldsAreEqual(field1, field2, subfieldCode) {\n // Check OK if neither one has given subfield.\n // Check fails if one field has given subfield and the other one does not\n if (!fieldHasSubfield(field1, subfieldCode)) {\n return !fieldHasSubfield(field2, subfieldCode);\n }\n if (!fieldHasSubfield(field2, subfieldCode)) {\n return false;\n }\n // Compare $3 subfields. If everything matches, OK, else FAIL:\n const sfSet1 = field1.subfields.filter(subfield => subfield.code === subfieldCode);\n const sfSet2 = field2.subfields.filter(subfield => subfield.code === subfieldCode);\n return MarcRecord.isEqual(sfSet1, sfSet2);\n}\n\nfunction subfieldsAreEmpty(field1, field2, subfieldCode) {\n if (!fieldHasSubfield(field1, subfieldCode) && !fieldHasSubfield(field2, subfieldCode)) {\n return true;\n }\n return false;\n}\n\n\nfunction sixlessIsSubset(fieldWith6, fieldWithout6) {\n // Remove $0 and $1, and then check that remaining $6-less field is a subset of the one with $6.\n // No need to check indicators.\n // NB! We could use punctuation-stripping here.\n const subset = fieldWithout6.subfields.filter(subfield => !['0', '1'].includes(subfield.code));\n return subset.every(sf => fieldWith6.subfields.some(sf2 => subfieldsAreIdentical(sf, sf2)));\n //return MarcRecord.isEqual(strippedField1, strippedField2);\n}\n\nfunction controlSubfield6PermitsMerge(field1, field2) {\n if (subfieldsAreEmpty(field1, field2, '6')) {\n return true;\n }\n\n // Handle cases where one has a $6 and the other has not:\n // Should this accept $0 (FI-ASTERI-N) vs none?\n if (!fieldHasSubfield(field1, '6') && fieldHasSubfield(field2, '6') && sixlessIsSubset(field2, field1)) {\n return true;\n }\n if (!fieldHasSubfield(field2, '6') && fieldHasSubfield(field1, '6') && sixlessIsSubset(field1, field2)) {\n return true;\n }\n\n // There are at least two (plus) fields involved (Field XXX (one) and field 880 (one plus).\n // Thus this generic solution can't handle them. Postprocess step removes some chains instead!\n debugDev(` controlSubfield6PermitsMerge() fails always on generic part (feature).`);\n return false;\n}\n\nfunction controlSubfield5PermitsMerge(field1, field2) {\n // field1.$5 XOR field2.$5 means false, NEITHER and BOTH mean true, regardless of value\n if (!fieldHasSubfield(field1, '5')) {\n if (!fieldHasSubfield(field2, '5')) {\n return true; // If neither one has $5, it's ok to merge\n }\n // If $5 contents are same, merge can be perfomed:\n const fives1 = field1.subfields.filter(sf => sf.code === '5');\n const fives2 = field2.subfields.filter(sf => sf.code === '5');\n if (fives1.every(sf1 => fives2.some(sf2 => sf1.value === sf2.value)) && fives2.every(sf2 => fives1.some(sf1 => sf1.value === sf2.value))) {\n return true;\n }\n return false;\n }\n if (!fieldHasSubfield(field2, '5')) {\n return false;\n }\n return true;\n}\n\nfunction controlSubfield9PermitsMerge(baseField, sourceField) {\n const baseFieldSubfields9 = baseField.subfields.filter(sf => sf.code === '9');\n const sourceFieldSubfields9 = sourceField.subfields.filter(sf => sf.code === '9');\n\n //nvdebug('CHECK $9', debugDev);\n // There are no $9s. Skip:\n if (baseFieldSubfields9.length === 0 && sourceFieldSubfields9.length === 0) {\n //nvdebug(` No subfield $9 detected`, debugDev);\n return true;\n }\n\n if (keepOrDropPreventsMerge()) {\n nvdebug(` Subfield $9 KEEPs and DROPs disallow merge`, debugDev);\n return false;\n }\n\n if (transPreventsMerge()) {\n nvdebug(` Subfield $9 <TRANS> mismatch disallows merge`, debugDev);\n return false;\n }\n\n //nvdebug('CHECK $9 OK', debugDev);\n\n return true;\n\n function subfieldHasKeepOrDrop(subfield) {\n // nvdebug(`Has <KEEP>? ${subfieldToString(subfield)}`, debugDev);\n return subfield.code === '9' && (/(?:<KEEP>|<DROP>)/u).test(subfield.value);\n }\n\n function subfieldHasTrans(subfield) {\n return subfield.code === '9' && (/<TRANS>/u).test(subfield.value);\n }\n\n function transPreventsMerge() {\n const trans1 = baseFieldSubfields9.filter(sf => subfieldHasTrans(sf));\n const trans2 = sourceFieldSubfields9.filter(sf => subfieldHasTrans(sf));\n if (trans1.length > 0 && trans2.length > 0) {\n if (!MarcRecord.isEqual(trans1, trans2)) {\n return true;\n }\n }\n return false;\n }\n\n function retainSubfieldForKeepComparison(subfield) {\n // Don't compare <KEEP>, <DROP> nor <TRANS> here (<TRANS> has it's own check)\n if (subfieldHasKeepOrDrop(subfield) || subfieldHasTrans(subfield)) {\n return false;\n }\n\n if (['0', '1'].includes(subfield.code)) {\n return false;\n }\n if (['100', '600', '700', '800'].includes(baseField.tag)) {\n // Despite $9 KEEP/DROP, we are interested in merging $d years (better than two separate fields)\n if (['d'].includes(subfield.code)) {\n return false;\n }\n }\n\n\n return true;\n }\n\n function acceptKeeplessSourceSubfield(sourceSubfield, tag, subfieldCode, subfieldValue) {\n if (sourceSubfield.code !== subfieldCode) {\n return false;\n }\n // In this context, there's no need to check the value of a non-repeatable subfield.\n // If value is different, pairing will fail when comparing the subfield itself.\n // This allows us to tolerate little differences in punctuation: different punctuation does not get copied to base,\n // so they don't alter base and and thus redundant when comparing.\n if (!subfieldIsRepeatable(tag, subfieldCode)) {\n return true;\n }\n return sourceSubfield.value === subfieldValue;\n }\n\n function keepOrDropPreventsMerge() {\n const keepOrDrop1 = baseFieldSubfields9.filter(sf => subfieldHasKeepOrDrop(sf));\n const keepOrDrop2 = sourceFieldSubfields9.filter(sf => subfieldHasKeepOrDrop(sf));\n\n if (keepOrDrop1.length === 0 && keepOrDrop2.length === 0) {\n return false;\n }\n\n if (baseField.tag.charAt(0) === '1' && !keepOrDrop2.some(sf => (/<DROP>/u).test(sf.value))) {\n return false;\n }\n\n const sf9lessField1 = baseField.subfields.filter(subfield => retainSubfieldForKeepComparison(subfield));\n const sf9lessField2 = sourceField.subfields.filter(subfield => retainSubfieldForKeepComparison(subfield));\n\n nvdebugSubfieldArray(baseField.subfields, 'FIELD ', debugDev);\n nvdebugSubfieldArray(sf9lessField1, 'FILTER ', debugDev);\n\n nvdebugSubfieldArray(sourceField.subfields, 'FIELD2 ', debugDev);\n nvdebugSubfieldArray(sf9lessField2, 'FILTER2 ', debugDev);\n\n // Keepless field can be a subset field with <KEEP>/<DROP>! Note that punctuation still causes remnants to fail.\n if (keepOrDrop1.length === 0) {\n return !sf9lessField1.every(sf => sf9lessField2.some(sf2 => subfieldsAreIdentical(sf, sf2)));\n }\n // However, to alleviate the above-mentioned punctuation problem, we can check keep/drop-less *source* subfields\n if (keepOrDrop2.length === 0) {\n const unhandledSubfield = sf9lessField2.find(sf2 => !sf9lessField1.some(sf => acceptKeeplessSourceSubfield(sf2, baseField.tag, sf.code, sf.value)));\n if (unhandledSubfield) {\n //nvdebug(`Failed to pair ${subfieldToString(unhandledSubfield)}`, debugDev);\n return true;\n }\n //return !sf9lessField2.every(sf2 => sf9lessField1.some(sf => subfieldsAreIdentical(sf, sf2)));\n return false;\n }\n\n //nvdebugSubfieldArray(sf9lessField2, 'SOURCE(?)', debugDev);\n //nvdebugSubfieldArray(sf9lessField1, 'BASE(?) ', debugDev);\n\n // $9 <KEEP> or <DROP> detected on both fields.\n // Non-keeps and non-drops must be equal, otherwise fail:\n if (MarcRecord.isEqual(sf9lessField1, sf9lessField2)) {\n return false;\n }\n // Prevent:\n return true;\n }\n}\n\nfunction getPrefix(value) {\n const normalizedValue = normalizeControlSubfieldValue(value);\n\n if (normalizedValue.match(/^\\([^)]+\\)[0-9]+$/u)) {\n return normalizedValue.substr(0, normalizedValue.indexOf(')') + 1);\n }\n\n if (value.match(/^https?:\\/\\//u)) {\n return normalizedValue.substr(0, normalizedValue.lastIndexOf('/') + 1);\n }\n\n return '';\n}\n\nfunction isMatchAfterNormalization(currSubfield, otherField) {\n // NB! Add implement isni normalizations (to normalize.js) and apply here:\n const normalizedCurrSubfieldValue = normalizeControlSubfieldValue(currSubfield.value);\n const prefix = getPrefix(normalizedCurrSubfieldValue);\n\n //debug(`FFS-PREFIX '${prefix}'`);\n // Look for same prefix + different identifier\n const hits = otherField.subfields.filter(sf2 => sf2.code === currSubfield.code && normalizeControlSubfieldValue(sf2.value).indexOf(prefix) === 0);\n if (hits.length === 0 || // <-- Nothing found, so it can't be a mismatch\n // Every opposing subfields match:\n hits.every(sf2 => normalizedCurrSubfieldValue === normalizeControlSubfieldValue(sf2.value))) {\n debugDev(`Subfield \u2021${currSubfield.code} check OK: No opposing ${prefix} prefixes found.`);\n return true;\n }\n\n debugDev(`Subfield \u2021${currSubfield.code} check FAILED: \u2021${currSubfield.code} '${currSubfield.value}' vs \u2021${currSubfield.code} '${hits[0].value}'.`);\n return false;\n}\n\nfunction controlSubfieldContainingIdentifierPermitsMerge(field1, field2, subfieldCode) {\n if (!fieldHasSubfield(field1, subfieldCode, null) || !fieldHasSubfield(field2, subfieldCode, null)) {\n return true;\n }\n\n const result = field1.subfields.every(subfield => {\n if (subfield.code !== subfieldCode) {\n return true;\n }\n\n debugDev(`Compare \u2021${subfieldCode} '${subfield.value}' with '${fieldToString(field2)}'.`);\n if (fieldHasSubfield(field2, field1.code, field1.value)) {\n return true;\n }\n\n return isMatchAfterNormalization(subfield, field2, subfieldCode);\n });\n\n if (!result) {\n debugDev(`Control subfield '${subfieldCode}' check failed.`);\n return false;\n }\n return true;\n}\n\nconst controlSubfieldsContainingIdentifier = ['w', '0', '1', '2']; // 2 ain't identifier, but the logic can be applied here as well\n\nexport function controlSubfieldsPermitMerge(baseField, sourceField) {\n // Check $w, $0, $1, $2 (which isn't an identifier per se, but the sama logic can be applied)\n if (!controlSubfieldsContainingIdentifier.every(subfieldCode => controlSubfieldContainingIdentifierPermitsMerge(baseField, sourceField, subfieldCode))) {\n //debug(' control subfields with identifiers failed');\n return false;\n }\n\n if (!subfieldsAreEqual(baseField, sourceField, '3')) {\n //debug(' similar control subfield fails');\n return false;\n }\n\n if (!controlSubfield5PermitsMerge(baseField, sourceField) || !controlSubfield6PermitsMerge(baseField, sourceField) || !controlSubfield9PermitsMerge(baseField, sourceField)) {\n return false;\n }\n // We fully prevent merging $8 subfields here, as they affect multiple fields! Also these would get screwed:\n // 38211 |8 3\\u |a kuoro |2 seko\n // 38211 |8 6\\u |a kuoro |2 seko |9 VIOLA<DROP>\n // Thus only copy works with $8...\n if (!subfieldsAreEmpty(baseField, sourceField, '8')) {\n // We could alleviate this a bit esp. for non-repeatable fields.\n // At least, if the source has '8' and otherwise the two fields are identical...\n const subsetOfSourceField = {\n 'tag': sourceField.tag,\n 'ind1': sourceField.ind1,\n 'ind2': sourceField.ind2, subfields: sourceField.subfields.filter(sf => sf.code !== '8')\n };\n if (fieldToString(baseField) === fieldToString(subsetOfSourceField)) {\n return true;\n }\n //debug(' csf8 failed');\n return false;\n }\n\n return true;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,16 +1,15 @@
1
1
  import createDebugLogger from "debug";
2
- import { fieldHasSubfield, fieldHasNSubfields, fieldHasMultipleSubfields, fieldToString, nvdebug, removeCopyright } from "../utils.js";
2
+ import { fieldHasSubfield, fieldHasNSubfields, fieldHasMultipleSubfields, fieldToString, nvdebug, removeCopyright, tagIsRepeatable } from "../utils.js";
3
3
  import { cloneAndNormalizeFieldForComparison, cloneAndRemovePunctuation } from "../normalizeFieldForComparison.js";
4
4
  import { normalizeControlSubfieldValue } from "../normalize-identifiers.js";
5
5
  import { getMergeConstraintsForTag } from "./mergeConstraints.js";
6
6
  import { controlSubfieldsPermitMerge } from "./controlSubfields.js";
7
7
  import { mergableIndicator1, mergableIndicator2 } from "./mergableIndicator.js";
8
8
  import { partsAgree } from "../normalizeSubfieldValueForComparison.js";
9
- import { normalizeForSamenessCheck, valueCarriesMeaning } from "./worldKnowledge.js";
9
+ import { getSynonym, normalizeForSamenessCheck, valueCarriesMeaning } from "./worldKnowledge.js";
10
10
  import { provenanceSubfieldsPermitMerge } from "./dataProvenance.js";
11
11
  const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda:mergeField:counterpart");
12
12
  const debugDev = debug.extend("dev");
13
- const irrelevantSubfieldsInNameAndTitlePartComparison = "5689";
14
13
  const counterpartRegexps = {
15
14
  // NB! tag is from source!
16
15
  // Note that in the normal case, all source 1XX fields have been converted to 7XX fields.
@@ -47,7 +46,7 @@ export function splitToNameAndQualifier(value) {
47
46
  }
48
47
  return [value, void 0];
49
48
  }
50
- export function splitToNameAndQualifierAndProcessName(name) {
49
+ function splitToNameAndQualifierAndProcessName(name) {
51
50
  const [qualifierlessName, qualifier] = splitToNameAndQualifier(name);
52
51
  const [prefix, basename, suffix] = stripPrefixAndSuffix(qualifierlessName);
53
52
  return { name: getBestName(basename).toLowerCase(), prefix, suffix, qualifier };
@@ -120,78 +119,22 @@ function corporateNamesAgree(value1, value2, tag, subfieldCode) {
120
119
  }
121
120
  return true;
122
121
  }
123
- function pairableValue(tag, subfieldCode, value1, value2) {
124
- if (withAndWithoutQualifierAgree(value1, value2, tag, subfieldCode)) {
125
- return value1;
126
- }
127
- if (partsAgree(value1, value2, tag, subfieldCode) || corporateNamesAgree(value1, value2, tag, subfieldCode)) {
128
- return value1;
129
- }
130
- return void 0;
131
- }
132
122
  function counterpartExtraNormalize(tag, subfieldCode, value) {
133
123
  value = value.replace(/(\S)(?:,|\.|\?|!|\. -| *:| *;| =| \/)$/u, "$1");
134
124
  value = value.replace(/^\(([^()]+)\)$/u, "$1");
135
125
  value = value.replace(/^\[([^[\]]+)\]$/u, "$1");
136
- value = removeCopyright(value);
126
+ if (tag === "260" && subfieldCode === "c") {
127
+ value = removeCopyright(value);
128
+ }
137
129
  value = value.replace(/http:\/\//ug, "https://");
130
+ value = getSynonym(tag, subfieldCode, value);
138
131
  value = normalizeForSamenessCheck(tag, subfieldCode, value);
139
132
  return value;
140
133
  }
141
- function uniqueKeyMatches(baseField, sourceField, forcedKeyString = null) {
142
- const keySubfieldsAsString = forcedKeyString || getMergeConstraintsForTag(baseField.tag, "key");
143
- return optionalSubfieldComparison(baseField, sourceField, keySubfieldsAsString);
144
- }
145
- function optionalSubfieldComparison(originalBaseField, originalSourceField, keySubfieldsAsString) {
146
- const field1 = cloneAndNormalizeFieldForComparison(originalBaseField);
147
- const field2 = cloneAndNormalizeFieldForComparison(originalSourceField);
148
- if (keySubfieldsAsString === null) {
149
- return fieldToString(field1).substring(6) === fieldToString(field2).substring(6);
150
- }
151
- const subfieldArray = keySubfieldsAsString.split("");
152
- if (subfieldArray.length > 0 && !subfieldArray.some((sfCode) => hasCommonNominator(sfCode))) {
153
- return false;
154
- }
155
- return subfieldArray.every((subfieldCode) => testOptionalSubfield(originalBaseField.tag, subfieldCode));
156
- function hasCommonNominator(subfieldCode) {
157
- const subfields1 = field1.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field1.tag, subfield.code, subfield.value));
158
- const subfields2 = field2.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field2.tag, subfield.code, subfield.value));
159
- return subfields1.length > 0 && subfields2.length > 0;
160
- }
161
- function testOptionalSubfield(tag, subfieldCode) {
162
- const subfields1 = field1.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field1.tag, subfield.code, subfield.value));
163
- const subfields2 = field2.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field2.tag, subfield.code, subfield.value));
164
- if (subfields1.length === 0 || subfields2.length === 0) {
165
- return true;
166
- }
167
- const subfieldValues1 = subfields1.map((sf) => counterpartExtraNormalize(tag, subfieldCode, sf.value));
168
- const subfieldValues2 = subfields2.map((sf) => counterpartExtraNormalize(tag, subfieldCode, sf.value));
169
- if (subfieldValues1.every((val) => subfieldValues2.includes(val)) || subfieldValues2.every((val) => subfieldValues1.includes(val))) {
170
- return true;
171
- }
172
- if (subfieldValues1.length === 1 && subfieldValues2.length === 1) {
173
- return pairableValue(field1.tag, subfieldCode, subfieldValues1[0], subfieldValues2[0]) !== void 0;
174
- }
175
- return false;
176
- }
177
- }
178
- function mandatorySubfieldComparison(originalField1, originalField2, keySubfieldsAsString) {
179
- const field1 = cloneAndNormalizeFieldForComparison(originalField1);
180
- const field2 = cloneAndNormalizeFieldForComparison(originalField2);
181
- if (keySubfieldsAsString === null) {
182
- return fieldToString(field1) === fieldToString(field2);
183
- }
184
- const subfieldArray = keySubfieldsAsString.split("");
185
- return subfieldArray.every((subfieldCode) => mandatorySingleSubfieldComparison(subfieldCode));
186
- function mandatorySingleSubfieldComparison(subfieldCode) {
187
- const subfieldValues1 = field1.subfields.filter((subfield) => subfield.code === subfieldCode).map((sf) => sf.value);
188
- const subfieldValues2 = field2.subfields.filter((subfield) => subfield.code === subfieldCode).map((sf) => sf.value);
189
- if (subfieldValues1.length !== subfieldValues2.length) {
190
- debugDev(`mSC: Unique key: subfield ${subfieldCode} issues...`);
191
- return false;
192
- }
193
- return subfieldValues1.every((value) => subfieldValues2.includes(value));
194
- }
134
+ function hasCommonNominator(field1, field2, subfieldCode) {
135
+ const subfields1 = field1.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field1.tag, subfield.code, subfield.value));
136
+ const subfields2 = field2.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field2.tag, subfield.code, subfield.value));
137
+ return subfields1.length > 0 && subfields2.length > 0;
195
138
  }
196
139
  function tagToRegexp(tag, internalMerge = false) {
197
140
  if (internalMerge && tag in counterpartRegexpsSingle) {
@@ -204,7 +147,7 @@ function tagToRegexp(tag, internalMerge = false) {
204
147
  return new RegExp(`^${tag}$`, "u");
205
148
  }
206
149
  function areRequiredSubfieldsPresent(field) {
207
- const subfieldString = getMergeConstraintsForTag(field.tag, "required");
150
+ const subfieldString = getMergeConstraintsForTag(field.tag, "required").join("");
208
151
  if (subfieldString === null) {
209
152
  return true;
210
153
  }
@@ -218,15 +161,22 @@ function areRequiredSubfieldsPresent(field) {
218
161
  return true;
219
162
  });
220
163
  }
221
- function arePairedSubfieldsInBalance(field1, field2) {
222
- const subfieldString = getMergeConstraintsForTag(field1.tag, "paired");
223
- if (subfieldString === null) {
224
- return true;
164
+ function getUnbalancedPairedSubfieldCode(field1, field2) {
165
+ const fullSubfieldString = getMergeConstraintsForTag(field1.tag, "paired").join("") || "";
166
+ if (fullSubfieldString === "") {
167
+ return false;
168
+ }
169
+ const pairable = pairableIdentifier(field1, field2, "(FIN11)");
170
+ const subfieldString = pairable ? removeNameRelatedSubfieldCodes(fullSubfieldString, field1.tag) : fullSubfieldString;
171
+ nvdebug(`CHECK ${pairable ? "PAIRABLE " : ""}${field1.tag} PAIRS: '${fullSubfieldString}' => '${subfieldString}'`, debugDev);
172
+ if (subfieldString === "") {
173
+ return false;
225
174
  }
226
175
  const subfieldArray = subfieldString.split("");
227
- return subfieldArray.every((sfcode) => fieldHasNSubfields(field1, sfcode) === fieldHasNSubfields(field2, sfcode));
176
+ return subfieldArray.find((sfcode) => fieldHasNSubfields(field1, sfcode) !== fieldHasNSubfields(field2, sfcode));
228
177
  }
229
178
  function syntacticallyMergablePair(baseField, sourceField, config) {
179
+ nvdebug("CHECK SYNTAX", debugDev);
230
180
  if (!mergableIndicator1(baseField, sourceField, config)) {
231
181
  nvdebug(`non-mergable (reason: indicator1): ${JSON.stringify(config)}`, debugDev);
232
182
  return false;
@@ -247,14 +197,16 @@ function syntacticallyMergablePair(baseField, sourceField, config) {
247
197
  nvdebug("non-mergable (reason: missing subfields)", debugDev);
248
198
  return false;
249
199
  }
250
- if (!arePairedSubfieldsInBalance(baseField, sourceField)) {
251
- nvdebug("required subfield pair check failed.", debugDev);
200
+ const subfieldCodeThatFailsToPair = getUnbalancedPairedSubfieldCode(baseField, sourceField);
201
+ if (subfieldCodeThatFailsToPair) {
202
+ nvdebug(`non-mergable (reason: required subfield pair check failed for code: '${subfieldCodeThatFailsToPair}')`, debugDev);
252
203
  return false;
253
204
  }
254
205
  return true;
255
206
  }
256
207
  function mergablePair(baseField, sourceField, config) {
257
208
  if (!syntacticallyMergablePair(baseField, sourceField, config)) {
209
+ nvdebug("non-mergable (reason: syntax)", debugDev);
258
210
  return false;
259
211
  }
260
212
  if (!semanticallyMergablePair(baseField, sourceField)) {
@@ -266,21 +218,41 @@ function mergablePair(baseField, sourceField, config) {
266
218
  S: ${fieldToString(sourceField)}`, debugDev);
267
219
  return true;
268
220
  }
269
- function pairableAsteriIDs(baseField, sourceField) {
270
- const fin11a = getAsteriIDs(baseField);
271
- if (fin11a.length === 0) {
272
- return false;
221
+ function removeNameRelatedSubfieldCodes(codestring, tag) {
222
+ const removables = getNameRelatedSubfieldCodes(tag);
223
+ return removeCharsFromString(codestring, removables);
224
+ function removeCharsFromString(string, removableCharsAsString) {
225
+ const removableChars = removableCharsAsString.split("");
226
+ return string.split("").filter((c) => !removableChars.includes(c)).join("");
227
+ }
228
+ function getNameRelatedSubfieldCodes(tag2) {
229
+ if (["100", "600", "700", "800"].includes(tag2)) {
230
+ return "abcdq";
231
+ }
232
+ if (["110", "610", "710", "810"].includes(tag2)) {
233
+ return "abcdn";
234
+ }
235
+ if (["111", "611", "711", "811"].includes(tag2)) {
236
+ return "acden";
237
+ }
238
+ return "";
273
239
  }
274
- const fin11b = getAsteriIDs(sourceField);
275
- if (fin11b.length === 0) {
240
+ }
241
+ function pairableIdentifier(field1, field2, prefix) {
242
+ const normalizedPrefix = prefix;
243
+ nvdebug(`PREF '${prefix}' => '${normalizedPrefix}'`, debugDev);
244
+ const prefixLength = normalizedPrefix.length;
245
+ const identifiers1 = getIdentifiers(field1);
246
+ if (identifiers1.length !== 1) {
276
247
  return false;
277
248
  }
278
- if (!controlSubfieldsPermitMerge(baseField, sourceField)) {
249
+ const identifiers2 = getIdentifiers(field2);
250
+ if (identifiers2.length !== 1) {
279
251
  return false;
280
252
  }
281
- return true;
282
- function getAsteriIDs(field) {
283
- return field.subfields.filter((sf) => sf.code === "0").map((sf) => normalizeControlSubfieldValue(sf.value)).filter((val) => val.substring(0, 7) === "(FIN11)");
253
+ return identifiers1[0] === identifiers2[0];
254
+ function getIdentifiers(field) {
255
+ return field.subfields.filter((sf) => sf.code === "0").map((sf) => normalizeControlSubfieldValue(sf.value)).filter((val) => val.substring(0, prefixLength) === normalizedPrefix);
284
256
  }
285
257
  }
286
258
  function hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(field) {
@@ -295,84 +267,110 @@ function hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(field) {
295
267
  }
296
268
  return false;
297
269
  }
298
- function pairableName(baseField, sourceField) {
299
- const reducedField1 = fieldToNamePart(baseField);
300
- const reducedField2 = fieldToNamePart(sourceField);
301
- const string1 = fieldToString(reducedField1);
302
- const string2 = fieldToString(reducedField2);
303
- if (string1 === string2) {
270
+ function getRelevantSubfieldValues(field, subfieldCode) {
271
+ const values = field.subfields.filter((sf) => sf.code === subfieldCode).map((sf) => counterpartExtraNormalize(field.tag, subfieldCode, sf.value));
272
+ return values.filter((v) => valueCarriesMeaning(field.tag, subfieldCode, v));
273
+ }
274
+ function pairableValue(tag, subfieldCode, value1, value2) {
275
+ if (value1 === value2) {
276
+ return true;
277
+ }
278
+ if (withAndWithoutQualifierAgree(value1, value2, tag, subfieldCode)) {
279
+ return true;
280
+ }
281
+ if (partsAgree(value1, value2, tag, subfieldCode) || corporateNamesAgree(value1, value2, tag, subfieldCode)) {
304
282
  return true;
305
283
  }
306
- if (hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(reducedField1) || hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(reducedField2)) {
284
+ return false;
285
+ }
286
+ function pairableValueInArray(tag, subfieldCode, val, arr) {
287
+ return arr.some((val2) => pairableValue(tag, subfieldCode, val, val2));
288
+ }
289
+ function tightSubfieldMatch(field1, field2, subfieldCode, mustHave = false) {
290
+ nvdebug(`${subfieldCode} F1: ${fieldToString(field1)}`, debugDev);
291
+ nvdebug(`${subfieldCode} F2: ${fieldToString(field2)}`, debugDev);
292
+ const values1 = getRelevantSubfieldValues(field1, subfieldCode);
293
+ const values2 = getRelevantSubfieldValues(field2, subfieldCode);
294
+ if (!mustHave) {
295
+ if (values1.length === 0 || values2.length === 0) {
296
+ return true;
297
+ }
298
+ }
299
+ if (values1.length !== values2.length) {
307
300
  return false;
308
301
  }
309
- if (uniqueKeyMatches(reducedField1, reducedField2)) {
310
- nvdebug(` name match: '${fieldToString(reducedField1)}'`, debugDev);
302
+ nvdebug(`Compare $${subfieldCode} contents:
303
+ '${values1.join("'\n '")}' vs
304
+ '${values2.join("'\n '")}'`, debugDev);
305
+ return values1.every((v) => pairableValueInArray(field1.tag, subfieldCode, v, values2)) && values2.every((v) => pairableValueInArray(field1.tag, subfieldCode, v, values1));
306
+ }
307
+ function looseSubfieldMatch(field1, field2, subfieldCode) {
308
+ const values1 = getRelevantSubfieldValues(field1, subfieldCode);
309
+ const values2 = getRelevantSubfieldValues(field2, subfieldCode);
310
+ if (values1.length === 0 || values2.length === 0) {
311
+ return true;
312
+ }
313
+ if (values1.every((v) => pairableValueInArray(field1.tag, subfieldCode, v, values2))) {
311
314
  return true;
312
315
  }
313
- if (pairableAsteriIDs(baseField, sourceField)) {
316
+ if (values2.every((v) => pairableValueInArray(field1.tag, subfieldCode, v, values1))) {
314
317
  return true;
315
318
  }
316
- nvdebug(` name mismatch:`, debugDev);
317
- nvdebug(` '${fieldToString(reducedField1)}' vs`, debugDev);
318
- nvdebug(` '${fieldToString(reducedField2)}'`, debugDev);
319
319
  return false;
320
320
  }
321
321
  function semanticallyMergablePair(baseField, sourceField) {
322
- if (!titlePartsMatch(baseField, sourceField)) {
323
- nvdebug(` ${baseField.tag} is unmergable: Title part mismatch.`, debugDev);
322
+ const field1 = cloneAndNormalizeFieldForComparison(baseField);
323
+ const field2 = cloneAndNormalizeFieldForComparison(sourceField);
324
+ const string1 = fieldToString(field1);
325
+ const string2 = fieldToString(field2);
326
+ nvdebug(`IN ${baseField.tag}: pairableName():
327
+ '${string1}' vs
328
+ '${string2}'`, debugDev);
329
+ if (string1 === string2) {
330
+ return true;
331
+ }
332
+ const mergeConstraints = getMergeConstraintsForTag(field1.tag);
333
+ if (mergeConstraints.length === 0) {
324
334
  return false;
325
335
  }
326
- if (!pairableName(baseField, sourceField)) {
327
- nvdebug("Unmergable: Name part mismatch", debugDev);
336
+ if (hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(field1) || hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(field2)) {
337
+ nvdebug(`Unmergable: data is too complex to be automatically safely merged`, debugDev);
328
338
  return false;
329
339
  }
330
- return true;
331
- }
332
- function namePartThreshold(field) {
333
- if (!/[10]0$/u.test(field.tag)) {
334
- return -1;
340
+ const asteriMatch = pairableIdentifier(field1, field2, "(FIN11)");
341
+ const allRequired = mergeConstraints[0].required || "";
342
+ const reallyRequired = asteriMatch ? removeNameRelatedSubfieldCodes(allRequired, field1.tag) : allRequired;
343
+ if (!reallyRequired.split("").every((c) => tightSubfieldMatch(field1, field2, c, true))) {
344
+ return false;
335
345
  }
336
- const t = field.subfields.findIndex((currSubfield) => currSubfield.code === "t");
337
- const u = t;
338
- if (t === -1) {
339
- return u;
346
+ const allPaired = mergeConstraints[0].paired || "";
347
+ const reallyPaired = asteriMatch ? removeNameRelatedSubfieldCodes(allPaired, field1.tag) : allPaired;
348
+ if (!reallyPaired.split("").every((c) => tightSubfieldMatch(field1, field2, c, false))) {
349
+ return false;
340
350
  }
341
- if (u === -1) {
342
- return t;
351
+ const allKeys = mergeConstraints[0].key || "";
352
+ const relevantKeys = asteriMatch ? removeNameRelatedSubfieldCodes(allKeys, field1.tag) : allKeys;
353
+ if (!relevantKeys.split("").every((c) => looseSubfieldMatch(field1, field2, c))) {
354
+ return false;
343
355
  }
344
- return t > u ? u : t;
345
- }
346
- function fieldToNamePart(field) {
347
- const index = namePartThreshold(field);
348
- const relevantSubfields = field.subfields.filter((sf, i) => i < index || index === -1).filter((sf) => !irrelevantSubfieldsInNameAndTitlePartComparison.includes(sf.code));
349
- const subsetField = { "tag": field.tag, "ind1": field.ind1, "ind2": field.ind2, subfields: relevantSubfields };
350
- return subsetField;
351
- }
352
- function fieldToTitlePart(field) {
353
- const index = field.subfields.findIndex((currSubfield) => currSubfield.code === "t");
354
- const relevantSubfields = field.subfields.filter((sf, i) => i >= index).filter((sf) => !irrelevantSubfieldsInNameAndTitlePartComparison.includes(sf.code));
355
- const subsetField = { "tag": field.tag, "ind1": field.ind1, "ind2": field.ind2, subfields: relevantSubfields };
356
- debugDev(`Title subset: ${fieldToString(subsetField)}`);
357
- return subsetField;
358
- }
359
- function containsTitlePart(field) {
360
- return fieldCanHaveTitlePart(field) && fieldHasSubfield(field, "t");
361
- function fieldCanHaveTitlePart(field2) {
362
- return ["100", "110", "111", "700", "710", "711"].includes(field2.tag);
356
+ if (allRequired.length > 0) {
357
+ return true;
363
358
  }
364
- }
365
- function titlePartsMatch(field1, field2) {
366
- if (!containsTitlePart(field1)) {
367
- return !containsTitlePart(field2);
359
+ if (reallyPaired.length > 0 && field1.subfields.some((sf) => reallyPaired.includes(sf.code))) {
360
+ return true;
368
361
  }
369
- if (!containsTitlePart(field2)) {
370
- return false;
362
+ if (!tagIsRepeatable(field1.tag) || relevantKeys.length == 0) {
363
+ return true;
364
+ }
365
+ if (relevantKeys.length > 0) {
366
+ if (field1.subfields.some((sf) => relevantKeys.includes(sf.code)) || field2.subfields.some((sf) => relevantKeys.includes(sf.code))) {
367
+ return relevantKeys.split("").some((code) => hasCommonNominator(field1, field2, code));
368
+ }
371
369
  }
372
- debugDev(`TITLE PARTS NEED TO BE COMPARED`);
373
- const subset1 = fieldToTitlePart(field1);
374
- const subset2 = fieldToTitlePart(field2);
375
- return mandatorySubfieldComparison(subset1, subset2, "dfhklmnoprstxvg");
370
+ nvdebug(` name mismatch (${allKeys}):`, debugDev);
371
+ nvdebug(` '${fieldToString(baseField)}' vs`, debugDev);
372
+ nvdebug(` '${fieldToString(sourceField)}'`, debugDev);
373
+ return false;
376
374
  }
377
375
  function getAlternativeNamesFrom9XX(record, field) {
378
376
  if (!field.tag.match(/^(?:100|110|111|600|610|611|700|710|711)$/u)) {
@@ -418,7 +416,6 @@ function getCounterpartIndex(field, counterpartCands, altNames, config) {
418
416
  return normalizedCounterpartCands.findIndex((normCandField) => altNames.some((altName) => mergablePairWithAltName(normCandField, normalizedField, altName, config)));
419
417
  }
420
418
  function field264Exception(baseField, sourceRecord, sourceField, config) {
421
- nvdebug("Field 264 exception as per MET-456", debugDev);
422
419
  if (baseField.tag !== "264") {
423
420
  return false;
424
421
  }
@@ -459,15 +456,8 @@ export function getCounterpart(baseRecord, sourceRecord, field, config) {
459
456
  }
460
457
  nvdebug(`Compare incoming '${fieldToString(field)}' with (up to) ${counterpartCands.length} existing field(s)`, debugDev);
461
458
  const normalizedField = cloneAndNormalizeFieldForComparison(field);
462
- nvdebug(`Norm to: '${fieldToString(normalizedField)}'`, debugDev);
459
+ nvdebug(` Normalize incoming field to: '${fieldToString(normalizedField)}'`, debugDev);
463
460
  const uniqueAlternativeNames = getUniqueAlernativeNames();
464
- function getUniqueAlernativeNames() {
465
- if (baseIsSource(baseRecord, sourceRecord)) {
466
- return [];
467
- }
468
- const alternativeNames = getAlternativeNamesFrom9XX(baseRecord, field).concat(getAlternativeNamesFrom9XX(sourceRecord, field));
469
- return alternativeNames.filter((name, i) => alternativeNames.indexOf(name) === i);
470
- }
471
461
  const index = getCounterpartIndex(normalizedField, counterpartCands, uniqueAlternativeNames, config);
472
462
  if (index > -1) {
473
463
  return counterpartCands[index];
@@ -476,5 +466,12 @@ export function getCounterpart(baseRecord, sourceRecord, field, config) {
476
466
  return counterpartCands[0];
477
467
  }
478
468
  return null;
469
+ function getUniqueAlernativeNames() {
470
+ if (baseIsSource(baseRecord, sourceRecord)) {
471
+ return [];
472
+ }
473
+ const alternativeNames = getAlternativeNamesFrom9XX(baseRecord, field).concat(getAlternativeNamesFrom9XX(sourceRecord, field));
474
+ return alternativeNames.filter((name, i) => alternativeNames.indexOf(name) === i);
475
+ }
479
476
  }
480
477
  //# sourceMappingURL=counterpartField.js.map