@natlibfi/marc-record-validators-melinda 10.2.3 → 10.3.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 (118) hide show
  1. package/.github/workflows/melinda-node-tests.yml +2 -2
  2. package/dist/access-rights.js.map +1 -1
  3. package/dist/access-rights.spec.js.map +1 -1
  4. package/dist/double-commas.js.map +1 -1
  5. package/dist/double-commas.spec.js.map +1 -1
  6. package/dist/duplicates-ind1.js.map +1 -1
  7. package/dist/duplicates-ind1.spec.js.map +1 -1
  8. package/dist/empty-fields.js.map +1 -1
  9. package/dist/empty-fields.spec.js.map +1 -1
  10. package/dist/ending-punctuation-conf.js.map +1 -1
  11. package/dist/ending-punctuation.js.map +1 -1
  12. package/dist/ending-punctuation.spec.js.map +1 -1
  13. package/dist/ending-whitespace.js.map +1 -1
  14. package/dist/ending-whitespace.spec.js.map +1 -1
  15. package/dist/field-exclusion.js.map +1 -1
  16. package/dist/field-exclusion.spec.js.map +1 -1
  17. package/dist/field-structure.js.map +1 -1
  18. package/dist/field-structure.spec.js.map +1 -1
  19. package/dist/fields-present.js.map +1 -1
  20. package/dist/fields-present.spec.js.map +1 -1
  21. package/dist/fixed-fields.js.map +1 -1
  22. package/dist/fixed-fields.spec.js.map +1 -1
  23. package/dist/identical-fields.js.map +1 -1
  24. package/dist/identical-fields.spec.js.map +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/indicator-fixes.js.map +1 -1
  27. package/dist/indicator-fixes.spec.js.map +1 -1
  28. package/dist/isbn-issn.js.map +1 -1
  29. package/dist/isbn-issn.spec.js.map +1 -1
  30. package/dist/item-language.js.map +1 -1
  31. package/dist/item-language.spec.js.map +1 -1
  32. package/dist/mergeField500Lisapainokset.js.map +1 -1
  33. package/dist/mergeField500Lisapainokset.spec.js.map +1 -1
  34. package/dist/multiple-subfield-0.js.map +1 -1
  35. package/dist/multiple-subfield-0.spec.js.map +1 -1
  36. package/dist/non-breaking-space.js.map +1 -1
  37. package/dist/non-breaking-space.spec.js.map +1 -1
  38. package/dist/normalize-identifiers.js.map +1 -1
  39. package/dist/normalize-identifiers.spec.js.map +1 -1
  40. package/dist/normalize-utf8-diacritics.js.map +1 -1
  41. package/dist/normalize-utf8-diacritics.spec.js.map +1 -1
  42. package/dist/punctuation/index.js.map +1 -1
  43. package/dist/punctuation/rules/aut.js.map +1 -1
  44. package/dist/punctuation/rules/bib.js.map +1 -1
  45. package/dist/punctuation/rules/index.js.map +1 -1
  46. package/dist/punctuation.spec.js.map +1 -1
  47. package/dist/punctuation2.js +677 -0
  48. package/dist/punctuation2.js.map +1 -0
  49. package/dist/punctuation2.spec.js +51 -0
  50. package/dist/punctuation2.spec.js.map +1 -0
  51. package/dist/reindexSubfield6OccurenceNumbers.js.map +1 -1
  52. package/dist/reindexSubfield6OccurenceNumbers.spec.js.map +1 -1
  53. package/dist/removeDuplicateDataFields.js +117 -30
  54. package/dist/removeDuplicateDataFields.js.map +1 -1
  55. package/dist/removeDuplicateDataFields.spec.js.map +1 -1
  56. package/dist/removeInferiorDataFields.js +98 -0
  57. package/dist/removeInferiorDataFields.js.map +1 -0
  58. package/dist/removeInferiorDataFields.spec.js +51 -0
  59. package/dist/removeInferiorDataFields.spec.js.map +1 -0
  60. package/dist/resolvable-ext-references-melinda.js.map +1 -1
  61. package/dist/resolvable-ext-references-melinda.spec.js.map +1 -1
  62. package/dist/resolveOrphanedSubfield6s.js.map +1 -1
  63. package/dist/resolveOrphanedSubfield6s.spec.js.map +1 -1
  64. package/dist/sort-tags.js.map +1 -1
  65. package/dist/sort-tags.spec.js.map +1 -1
  66. package/dist/subfield-exclusion.js.map +1 -1
  67. package/dist/subfield-exclusion.spec.js.map +1 -1
  68. package/dist/subfield6Utils.js.map +1 -1
  69. package/dist/subfield8Utils.js +17 -10
  70. package/dist/subfield8Utils.js.map +1 -1
  71. package/dist/unicode-decomposition.js.map +1 -1
  72. package/dist/unicode-decomposition.spec.js.map +1 -1
  73. package/dist/urn.js.map +1 -1
  74. package/dist/urn.spec.js.map +1 -1
  75. package/dist/utils.js.map +1 -1
  76. package/package.json +2 -2
  77. package/src/punctuation2.js +398 -0
  78. package/src/punctuation2.spec.js +52 -0
  79. package/src/removeDuplicateDataFields.js +141 -31
  80. package/src/removeInferiorDataFields.js +97 -0
  81. package/src/removeInferiorDataFields.spec.js +52 -0
  82. package/src/subfield8Utils.js +16 -11
  83. package/test-fixtures/punctuation2/01/expectedResult.json +12 -0
  84. package/test-fixtures/punctuation2/01/metadata.json +6 -0
  85. package/test-fixtures/punctuation2/01/record.json +37 -0
  86. package/test-fixtures/punctuation2/02/expectedResult.json +4 -0
  87. package/test-fixtures/punctuation2/02/metadata.json +6 -0
  88. package/test-fixtures/punctuation2/02/record.json +14 -0
  89. package/test-fixtures/punctuation2/04/expectedResult.json +7 -0
  90. package/test-fixtures/punctuation2/04/metadata.json +6 -0
  91. package/test-fixtures/punctuation2/04/record.json +22 -0
  92. package/test-fixtures/punctuation2/05/expectedResult.json +6 -0
  93. package/test-fixtures/punctuation2/05/metadata.json +6 -0
  94. package/test-fixtures/punctuation2/05/record.json +12 -0
  95. package/test-fixtures/punctuation2/98/expectedResult.json +39 -0
  96. package/test-fixtures/punctuation2/98/metadata.json +6 -0
  97. package/test-fixtures/punctuation2/98/record.json +37 -0
  98. package/test-fixtures/punctuation2/99/expectedResult.json +15 -0
  99. package/test-fixtures/punctuation2/99/metadata.json +6 -0
  100. package/test-fixtures/punctuation2/99/record.json +14 -0
  101. package/test-fixtures/remove-duplicate-datafields/f03/expectedResult.json +20 -0
  102. package/test-fixtures/remove-duplicate-datafields/f03/metadata.json +6 -0
  103. package/test-fixtures/remove-duplicate-datafields/f03/record.json +33 -0
  104. package/test-fixtures/remove-duplicate-datafields/f03b/expectedResult.json +20 -0
  105. package/test-fixtures/remove-duplicate-datafields/f03b/metadata.json +6 -0
  106. package/test-fixtures/remove-duplicate-datafields/f03b/record.json +35 -0
  107. package/test-fixtures/remove-duplicate-datafields/f03c/expectedResult.json +25 -0
  108. package/test-fixtures/remove-duplicate-datafields/f03c/metadata.json +6 -0
  109. package/test-fixtures/remove-duplicate-datafields/f03c/record.json +43 -0
  110. package/test-fixtures/remove-duplicate-datafields/f06/expectedResult.json +0 -18
  111. package/test-fixtures/remove-duplicate-datafields/v02/expectedResult.json +1 -3
  112. package/test-fixtures/remove-duplicate-datafields/v03/expectedResult.json +1 -2
  113. package/test-fixtures/remove-inferior-datafields/f01/expectedResult.json +21 -0
  114. package/test-fixtures/remove-inferior-datafields/f01/metadata.json +6 -0
  115. package/test-fixtures/remove-inferior-datafields/f01/record.json +31 -0
  116. package/test-fixtures/remove-inferior-datafields/v01/expectedResult.json +6 -0
  117. package/test-fixtures/remove-inferior-datafields/v01/metadata.json +6 -0
  118. package/test-fixtures/remove-inferior-datafields/v01/record.json +31 -0
@@ -1 +1 @@
1
- {"version":3,"file":"urn.spec.js","names":["describe","f337","tag","ind1","ind2","subfields","code","value","f337nonElectronic","ldf856","ldf856partial","f856URN","f856URNnotResource","f856URL","f020","f020second","it","validator","validatorFactory","expect","to","be","an","that","has","any","keys","description","a","validate","test","isLegalDeposit","valid","recfields","result","MarcRecord","fields","eql","fix","resfields","record","nonld","ld","skip"],"sources":["../src/urn.spec.js"],"sourcesContent":["import {expect} from 'chai';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from '../src/urn';\n\ndescribe('urn', async () => {\n // Fields\n const f337 = {\n tag: '337',\n ind1: ' ',\n ind2: ' ',\n subfields: [\n {code: 'b', value: 'c'},\n {code: '2', value: 'rdamedia'}\n ]\n };\n\n const f337nonElectronic = {\n tag: '337',\n ind1: ' ',\n ind2: ' ',\n subfields: [\n {code: 'b', value: 'n'},\n {code: '2', value: 'rdamedia'}\n ]\n };\n\n const ldf856 = {\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [\n {code: 'u', value: 'http://urn.fi/URN:ISBN:978-951-9155-47-0'},\n {code: 'z', value: 'Käytettävissä vapaakappalekirjastoissa'},\n {code: '5', value: 'FI-Vapaa'}\n ]\n };\n\n const ldf856partial = {\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [\n {code: 'u', value: 'http://urn.fi/URN:ISBN:978-951-9155-47-0'},\n {code: '5', value: 'FI-Vapaa'}\n ]\n };\n\n const f856URN = {\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [{code: 'u', value: 'http://urn.fi/URN:ISBN:978-951-9155-47-0'}]\n };\n\n const f856URNnotResource = {\n tag: '856',\n ind1: '4',\n ind2: '1',\n subfields: [{code: 'u', value: 'http://urn.fi/URN:ISBN:978-951-9155-47-0'}]\n };\n\n\n const f856URL = {\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [{code: 'u', value: 'http://foo.bar/'}]\n };\n\n const f020 = {\n tag: '020',\n ind1: ' ',\n ind2: ' ',\n subfields: [{code: 'a', value: '978-951-9155-47-0'}]\n };\n\n const f020second = {\n tag: '020',\n ind1: ' ',\n ind2: ' ',\n subfields: [{code: 'a', value: '9789519155470'}]\n };\n\n\n it('Creates a validator', async () => {\n const validator = await validatorFactory();\n\n expect(validator)\n .to.be.an('object')\n .that.has.any.keys('description', 'validate');\n\n expect(validator.description).to.be.a('string');\n expect(validator.validate).to.be.a('function');\n });\n\n // Tests\n const test = async isLegalDeposit => {\n const validator = await validatorFactory(isLegalDeposit);\n return {\n validate: async (valid, ...recfields) => {\n const result = await validator.validate(new MarcRecord({fields: recfields}));\n expect(result).to.eql({valid});\n },\n\n fix: async (recfields, resfields) => {\n const record = new MarcRecord({fields: recfields});\n await validator.fix(record);\n expect(record.fields).to.eql(resfields);\n }\n };\n };\n\n /// Non-legal and legal deposit\n const nonld = await test(false);\n const ld = await test(true);\n\n describe('#validate', () => {\n // Validate non-electoronic\n it('Finds the record valid; non-electronic record', async () => {\n await nonld.validate(true, f337nonElectronic);\n });\n\n // Validate non-legal deposit\n it('Finds the record valid; 856 with urn, and is non-legal deposit', async () => {\n await nonld.validate(true, f337, f856URN);\n });\n\n\n // we should recognize that 856 with second indicator 1 is not describing the resource itself\n it.skip('Finds the record invalid; 856 ind2: 1 with urn, and is non-legal deposit', async () => {\n await nonld.validate(false, f337, f856URNnotResource);\n });\n\n // should we require urn if we're not handling a legal deposit\n it('Finds the record invalid; 856 without urn, and is non-legal deposit', async () => {\n await nonld.validate(false, f337, f856URL);\n });\n\n\n // should we require urn if we're not handling a legal deposit\n it('Finds the record invalid; Missing 856, and is non-legal deposit', async () => {\n await nonld.validate(false, f337, f020);\n });\n\n // Validate legal deposit\n it('Finds the record invalid; 856 with urn, and is legal deposit', async () => {\n await ld.validate(false, f020, f337, f856URN);\n });\n\n it('Finds the record invalid; 856 without urn, and is legal deposit', async () => {\n await ld.validate(false, f020, f337, f856URL);\n });\n\n it('Finds the record invalid; Missing 856, and is legal deposit', async () => {\n await ld.validate(false, f337, f020);\n });\n\n it('Finds the record valid; 856 with URN and legal deposit subfields, and is legal deposit', async () => {\n await ld.validate(true, f337, f020, ldf856);\n });\n\n it('Finds the record valid; 856 with URN and legal deposit subfields and other f856s, and is legal deposit', async () => {\n await ld.validate(true, f337, f020, ldf856partial, ldf856, f856URL);\n });\n\n it('Finds the record invalid; 856 with URN and partial legal deposit subfields, and is legal deposit', async () => {\n await ld.validate(false, f337, f020, ldf856partial);\n });\n\n\n });\n\n describe('#fix', () => {\n // Fix non-legal deposit\n it('856 with urn, and is non-legal deposit; Nothing to add', async () => {\n await nonld.fix([f020, f856URL, f856URN], [f020, f856URL, f856URN]);\n });\n\n // should we actually add non-resolvable urns?\n it('856 without urn, and is non-legal deposit; Adds 856 with urn', async () => {\n await nonld.fix([f020, f856URL], [f020, f856URL, f856URN]);\n });\n\n // should we actually add non-resolvable urns?\n it('Missing 856, and is non-legal deposit; Adds 856 with urn', async () => {\n await nonld.fix([f020], [f020, f856URN]);\n });\n\n // should we actually add non-resolvable urns?\n it('Missing 856, and is non-legal deposit, two 020 fields; Adds 856 with urn from second 020', async () => {\n await nonld.fix([f020second, f020], [f020second, f020, f856URN]);\n });\n\n // should we actually add non resovable urns?\n // we should think about how to choose the isbn to use in case of several ISBNs\n it.skip('Missing 856, and is non-legal deposit, two 020 fields; Adds 856 with urn from first 020', async () => {\n await nonld.fix([f020, f020second], [f020, f020second, f856URN]);\n });\n\n\n // Fix legal deposit\n it('856 with urn and legal deposit fields, and is legal deposit; Nothing to add', async () => {\n await ld.fix([f020, f856URL, ldf856], [f020, f856URL, ldf856]);\n });\n\n it('856 without urn, and is legal deposit; Adds 856 with urn and legal deposit fields', async () => {\n await ld.fix([f020, f856URL], [f020, f856URL, ldf856]);\n });\n\n // we should test generating the URN in case of no ISBN\n\n // we should test creating Melinda-temp field\n\n // We shouldn't lock the open URN for legal deposit use\n it('Missing 856, and is legal deposit; Adds 856 with urn and legal deposit fields', async () => {\n await ld.fix([f020], [f020, ldf856]);\n });\n\n // We should actually do this instead of locking the original non-legal deposit URN for legal deposit use\n it.skip('856 with urn, and is legal deposit; Adds another f856 with URN and legal deposit fields', async () => {\n await ld.fix([f020, f856URL, f856URN], [f020, f856URL, f856URN, ldf856]);\n });\n\n // We should actually add a new urn in case of a non-resource URN\n it.skip('856 with non-resource-urn, and is legal deposit; Adds another f856 with URN and legal deposit fields', async () => {\n await ld.fix([f020, f856URL, f856URNnotResource], [f020, f856URL, f856URNnotResource, ldf856]);\n });\n\n\n });\n});\n"],"mappings":";;AAAA;AACA;AACA;AAA0C;AAE1CA,QAAQ,CAAC,KAAK,EAAE,YAAY;EAC1B;EACA,MAAMC,IAAI,GAAG;IACXC,GAAG,EAAE,KAAK;IACVC,IAAI,EAAE,GAAG;IACTC,IAAI,EAAE,GAAG;IACTC,SAAS,EAAE,CACT;MAACC,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAG,CAAC,EACvB;MAACD,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAU,CAAC;EAElC,CAAC;EAED,MAAMC,iBAAiB,GAAG;IACxBN,GAAG,EAAE,KAAK;IACVC,IAAI,EAAE,GAAG;IACTC,IAAI,EAAE,GAAG;IACTC,SAAS,EAAE,CACT;MAACC,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAG,CAAC,EACvB;MAACD,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAU,CAAC;EAElC,CAAC;EAED,MAAME,MAAM,GAAG;IACbP,GAAG,EAAE,KAAK;IACVC,IAAI,EAAE,GAAG;IACTC,IAAI,EAAE,GAAG;IACTC,SAAS,EAAE,CACT;MAACC,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAA0C,CAAC,EAC9D;MAACD,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAwC,CAAC,EAC5D;MAACD,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAU,CAAC;EAElC,CAAC;EAED,MAAMG,aAAa,GAAG;IACpBR,GAAG,EAAE,KAAK;IACVC,IAAI,EAAE,GAAG;IACTC,IAAI,EAAE,GAAG;IACTC,SAAS,EAAE,CACT;MAACC,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAA0C,CAAC,EAC9D;MAACD,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAU,CAAC;EAElC,CAAC;EAED,MAAMI,OAAO,GAAG;IACdT,GAAG,EAAE,KAAK;IACVC,IAAI,EAAE,GAAG;IACTC,IAAI,EAAE,GAAG;IACTC,SAAS,EAAE,CAAC;MAACC,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAA0C,CAAC;EAC5E,CAAC;EAED,MAAMK,kBAAkB,GAAG;IACzBV,GAAG,EAAE,KAAK;IACVC,IAAI,EAAE,GAAG;IACTC,IAAI,EAAE,GAAG;IACTC,SAAS,EAAE,CAAC;MAACC,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAA0C,CAAC;EAC5E,CAAC;EAGD,MAAMM,OAAO,GAAG;IACdX,GAAG,EAAE,KAAK;IACVC,IAAI,EAAE,GAAG;IACTC,IAAI,EAAE,GAAG;IACTC,SAAS,EAAE,CAAC;MAACC,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAiB,CAAC;EACnD,CAAC;EAED,MAAMO,IAAI,GAAG;IACXZ,GAAG,EAAE,KAAK;IACVC,IAAI,EAAE,GAAG;IACTC,IAAI,EAAE,GAAG;IACTC,SAAS,EAAE,CAAC;MAACC,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAmB,CAAC;EACrD,CAAC;EAED,MAAMQ,UAAU,GAAG;IACjBb,GAAG,EAAE,KAAK;IACVC,IAAI,EAAE,GAAG;IACTC,IAAI,EAAE,GAAG;IACTC,SAAS,EAAE,CAAC;MAACC,IAAI,EAAE,GAAG;MAAEC,KAAK,EAAE;IAAe,CAAC;EACjD,CAAC;EAGDS,EAAE,CAAC,qBAAqB,EAAE,YAAY;IACpC,MAAMC,SAAS,GAAG,MAAM,IAAAC,YAAgB,GAAE;IAE1C,IAAAC,YAAM,EAACF,SAAS,CAAC,CACdG,EAAE,CAACC,EAAE,CAACC,EAAE,CAAC,QAAQ,CAAC,CAClBC,IAAI,CAACC,GAAG,CAACC,GAAG,CAACC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC;IAE/C,IAAAP,YAAM,EAACF,SAAS,CAACU,WAAW,CAAC,CAACP,EAAE,CAACC,EAAE,CAACO,CAAC,CAAC,QAAQ,CAAC;IAC/C,IAAAT,YAAM,EAACF,SAAS,CAACY,QAAQ,CAAC,CAACT,EAAE,CAACC,EAAE,CAACO,CAAC,CAAC,UAAU,CAAC;EAChD,CAAC,CAAC;;EAEF;EACA,MAAME,IAAI,GAAG,MAAMC,cAAc,IAAI;IACnC,MAAMd,SAAS,GAAG,MAAM,IAAAC,YAAgB,EAACa,cAAc,CAAC;IACxD,OAAO;MACLF,QAAQ,EAAE,OAAOG,KAAK,EAAE,GAAGC,SAAS,KAAK;QACvC,MAAMC,MAAM,GAAG,MAAMjB,SAAS,CAACY,QAAQ,CAAC,IAAIM,sBAAU,CAAC;UAACC,MAAM,EAAEH;QAAS,CAAC,CAAC,CAAC;QAC5E,IAAAd,YAAM,EAACe,MAAM,CAAC,CAACd,EAAE,CAACiB,GAAG,CAAC;UAACL;QAAK,CAAC,CAAC;MAChC,CAAC;MAEDM,GAAG,EAAE,OAAOL,SAAS,EAAEM,SAAS,KAAK;QACnC,MAAMC,MAAM,GAAG,IAAIL,sBAAU,CAAC;UAACC,MAAM,EAAEH;QAAS,CAAC,CAAC;QAClD,MAAMhB,SAAS,CAACqB,GAAG,CAACE,MAAM,CAAC;QAC3B,IAAArB,YAAM,EAACqB,MAAM,CAACJ,MAAM,CAAC,CAAChB,EAAE,CAACiB,GAAG,CAACE,SAAS,CAAC;MACzC;IACF,CAAC;EACH,CAAC;;EAED;EACA,MAAME,KAAK,GAAG,MAAMX,IAAI,CAAC,KAAK,CAAC;EAC/B,MAAMY,EAAE,GAAG,MAAMZ,IAAI,CAAC,IAAI,CAAC;EAE3B9B,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC1B;IACAgB,EAAE,CAAC,+CAA+C,EAAE,YAAY;MAC9D,MAAMyB,KAAK,CAACZ,QAAQ,CAAC,IAAI,EAAErB,iBAAiB,CAAC;IAC/C,CAAC,CAAC;;IAEF;IACAQ,EAAE,CAAC,gEAAgE,EAAE,YAAY;MAC/E,MAAMyB,KAAK,CAACZ,QAAQ,CAAC,IAAI,EAAE5B,IAAI,EAAEU,OAAO,CAAC;IAC3C,CAAC,CAAC;;IAGF;IACAK,EAAE,CAAC2B,IAAI,CAAC,0EAA0E,EAAE,YAAY;MAC9F,MAAMF,KAAK,CAACZ,QAAQ,CAAC,KAAK,EAAE5B,IAAI,EAAEW,kBAAkB,CAAC;IACvD,CAAC,CAAC;;IAEF;IACAI,EAAE,CAAC,qEAAqE,EAAE,YAAY;MACpF,MAAMyB,KAAK,CAACZ,QAAQ,CAAC,KAAK,EAAE5B,IAAI,EAAEY,OAAO,CAAC;IAC5C,CAAC,CAAC;;IAGF;IACAG,EAAE,CAAC,iEAAiE,EAAE,YAAY;MAChF,MAAMyB,KAAK,CAACZ,QAAQ,CAAC,KAAK,EAAE5B,IAAI,EAAEa,IAAI,CAAC;IACzC,CAAC,CAAC;;IAEF;IACAE,EAAE,CAAC,8DAA8D,EAAE,YAAY;MAC7E,MAAM0B,EAAE,CAACb,QAAQ,CAAC,KAAK,EAAEf,IAAI,EAAEb,IAAI,EAAEU,OAAO,CAAC;IAC/C,CAAC,CAAC;IAEFK,EAAE,CAAC,iEAAiE,EAAE,YAAY;MAChF,MAAM0B,EAAE,CAACb,QAAQ,CAAC,KAAK,EAAEf,IAAI,EAAEb,IAAI,EAAEY,OAAO,CAAC;IAC/C,CAAC,CAAC;IAEFG,EAAE,CAAC,6DAA6D,EAAE,YAAY;MAC5E,MAAM0B,EAAE,CAACb,QAAQ,CAAC,KAAK,EAAE5B,IAAI,EAAEa,IAAI,CAAC;IACtC,CAAC,CAAC;IAEFE,EAAE,CAAC,wFAAwF,EAAE,YAAY;MACvG,MAAM0B,EAAE,CAACb,QAAQ,CAAC,IAAI,EAAE5B,IAAI,EAAEa,IAAI,EAAEL,MAAM,CAAC;IAC7C,CAAC,CAAC;IAEFO,EAAE,CAAC,wGAAwG,EAAE,YAAY;MACvH,MAAM0B,EAAE,CAACb,QAAQ,CAAC,IAAI,EAAE5B,IAAI,EAAEa,IAAI,EAAEJ,aAAa,EAAED,MAAM,EAAEI,OAAO,CAAC;IACrE,CAAC,CAAC;IAEFG,EAAE,CAAC,kGAAkG,EAAE,YAAY;MACjH,MAAM0B,EAAE,CAACb,QAAQ,CAAC,KAAK,EAAE5B,IAAI,EAAEa,IAAI,EAAEJ,aAAa,CAAC;IACrD,CAAC,CAAC;EAGJ,CAAC,CAAC;EAEFV,QAAQ,CAAC,MAAM,EAAE,MAAM;IACrB;IACAgB,EAAE,CAAC,wDAAwD,EAAE,YAAY;MACvE,MAAMyB,KAAK,CAACH,GAAG,CAAC,CAACxB,IAAI,EAAED,OAAO,EAAEF,OAAO,CAAC,EAAE,CAACG,IAAI,EAAED,OAAO,EAAEF,OAAO,CAAC,CAAC;IACrE,CAAC,CAAC;;IAEF;IACAK,EAAE,CAAC,8DAA8D,EAAE,YAAY;MAC7E,MAAMyB,KAAK,CAACH,GAAG,CAAC,CAACxB,IAAI,EAAED,OAAO,CAAC,EAAE,CAACC,IAAI,EAAED,OAAO,EAAEF,OAAO,CAAC,CAAC;IAC5D,CAAC,CAAC;;IAEF;IACAK,EAAE,CAAC,0DAA0D,EAAE,YAAY;MACzE,MAAMyB,KAAK,CAACH,GAAG,CAAC,CAACxB,IAAI,CAAC,EAAE,CAACA,IAAI,EAAEH,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC;;IAEF;IACAK,EAAE,CAAC,0FAA0F,EAAE,YAAY;MACzG,MAAMyB,KAAK,CAACH,GAAG,CAAC,CAACvB,UAAU,EAAED,IAAI,CAAC,EAAE,CAACC,UAAU,EAAED,IAAI,EAAEH,OAAO,CAAC,CAAC;IAClE,CAAC,CAAC;;IAEF;IACA;IACAK,EAAE,CAAC2B,IAAI,CAAC,yFAAyF,EAAE,YAAY;MAC7G,MAAMF,KAAK,CAACH,GAAG,CAAC,CAACxB,IAAI,EAAEC,UAAU,CAAC,EAAE,CAACD,IAAI,EAAEC,UAAU,EAAEJ,OAAO,CAAC,CAAC;IAClE,CAAC,CAAC;;IAGF;IACAK,EAAE,CAAC,6EAA6E,EAAE,YAAY;MAC5F,MAAM0B,EAAE,CAACJ,GAAG,CAAC,CAACxB,IAAI,EAAED,OAAO,EAAEJ,MAAM,CAAC,EAAE,CAACK,IAAI,EAAED,OAAO,EAAEJ,MAAM,CAAC,CAAC;IAChE,CAAC,CAAC;IAEFO,EAAE,CAAC,mFAAmF,EAAE,YAAY;MAClG,MAAM0B,EAAE,CAACJ,GAAG,CAAC,CAACxB,IAAI,EAAED,OAAO,CAAC,EAAE,CAACC,IAAI,EAAED,OAAO,EAAEJ,MAAM,CAAC,CAAC;IACxD,CAAC,CAAC;;IAEF;;IAEA;;IAEA;IACAO,EAAE,CAAC,+EAA+E,EAAE,YAAY;MAC9F,MAAM0B,EAAE,CAACJ,GAAG,CAAC,CAACxB,IAAI,CAAC,EAAE,CAACA,IAAI,EAAEL,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC;;IAEF;IACAO,EAAE,CAAC2B,IAAI,CAAC,yFAAyF,EAAE,YAAY;MAC7G,MAAMD,EAAE,CAACJ,GAAG,CAAC,CAACxB,IAAI,EAAED,OAAO,EAAEF,OAAO,CAAC,EAAE,CAACG,IAAI,EAAED,OAAO,EAAEF,OAAO,EAAEF,MAAM,CAAC,CAAC;IAC1E,CAAC,CAAC;;IAEF;IACAO,EAAE,CAAC2B,IAAI,CAAC,sGAAsG,EAAE,YAAY;MAC1H,MAAMD,EAAE,CAACJ,GAAG,CAAC,CAACxB,IAAI,EAAED,OAAO,EAAED,kBAAkB,CAAC,EAAE,CAACE,IAAI,EAAED,OAAO,EAAED,kBAAkB,EAAEH,MAAM,CAAC,CAAC;IAChG,CAAC,CAAC;EAGJ,CAAC,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"urn.spec.js","names":["_chai","require","_marcRecord","_urn","_interopRequireDefault","obj","__esModule","default","describe","f337","tag","ind1","ind2","subfields","code","value","f337nonElectronic","ldf856","ldf856partial","f856URN","f856URNnotResource","f856URL","f020","f020second","it","validator","validatorFactory","expect","to","be","an","that","has","any","keys","description","a","validate","test","isLegalDeposit","valid","recfields","result","MarcRecord","fields","eql","fix","resfields","record","nonld","ld","skip"],"sources":["../src/urn.spec.js"],"sourcesContent":["import {expect} from 'chai';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from '../src/urn';\n\ndescribe('urn', async () => {\n // Fields\n const f337 = {\n tag: '337',\n ind1: ' ',\n ind2: ' ',\n subfields: [\n {code: 'b', value: 'c'},\n {code: '2', value: 'rdamedia'}\n ]\n };\n\n const f337nonElectronic = {\n tag: '337',\n ind1: ' ',\n ind2: ' ',\n subfields: [\n {code: 'b', value: 'n'},\n {code: '2', value: 'rdamedia'}\n ]\n };\n\n const ldf856 = {\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [\n {code: 'u', value: 'http://urn.fi/URN:ISBN:978-951-9155-47-0'},\n {code: 'z', value: 'Käytettävissä vapaakappalekirjastoissa'},\n {code: '5', value: 'FI-Vapaa'}\n ]\n };\n\n const ldf856partial = {\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [\n {code: 'u', value: 'http://urn.fi/URN:ISBN:978-951-9155-47-0'},\n {code: '5', value: 'FI-Vapaa'}\n ]\n };\n\n const f856URN = {\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [{code: 'u', value: 'http://urn.fi/URN:ISBN:978-951-9155-47-0'}]\n };\n\n const f856URNnotResource = {\n tag: '856',\n ind1: '4',\n ind2: '1',\n subfields: [{code: 'u', value: 'http://urn.fi/URN:ISBN:978-951-9155-47-0'}]\n };\n\n\n const f856URL = {\n tag: '856',\n ind1: '4',\n ind2: '0',\n subfields: [{code: 'u', value: 'http://foo.bar/'}]\n };\n\n const f020 = {\n tag: '020',\n ind1: ' ',\n ind2: ' ',\n subfields: [{code: 'a', value: '978-951-9155-47-0'}]\n };\n\n const f020second = {\n tag: '020',\n ind1: ' ',\n ind2: ' ',\n subfields: [{code: 'a', value: '9789519155470'}]\n };\n\n\n it('Creates a validator', async () => {\n const validator = await validatorFactory();\n\n expect(validator)\n .to.be.an('object')\n .that.has.any.keys('description', 'validate');\n\n expect(validator.description).to.be.a('string');\n expect(validator.validate).to.be.a('function');\n });\n\n // Tests\n const test = async isLegalDeposit => {\n const validator = await validatorFactory(isLegalDeposit);\n return {\n validate: async (valid, ...recfields) => {\n const result = await validator.validate(new MarcRecord({fields: recfields}));\n expect(result).to.eql({valid});\n },\n\n fix: async (recfields, resfields) => {\n const record = new MarcRecord({fields: recfields});\n await validator.fix(record);\n expect(record.fields).to.eql(resfields);\n }\n };\n };\n\n /// Non-legal and legal deposit\n const nonld = await test(false);\n const ld = await test(true);\n\n describe('#validate', () => {\n // Validate non-electoronic\n it('Finds the record valid; non-electronic record', async () => {\n await nonld.validate(true, f337nonElectronic);\n });\n\n // Validate non-legal deposit\n it('Finds the record valid; 856 with urn, and is non-legal deposit', async () => {\n await nonld.validate(true, f337, f856URN);\n });\n\n\n // we should recognize that 856 with second indicator 1 is not describing the resource itself\n it.skip('Finds the record invalid; 856 ind2: 1 with urn, and is non-legal deposit', async () => {\n await nonld.validate(false, f337, f856URNnotResource);\n });\n\n // should we require urn if we're not handling a legal deposit\n it('Finds the record invalid; 856 without urn, and is non-legal deposit', async () => {\n await nonld.validate(false, f337, f856URL);\n });\n\n\n // should we require urn if we're not handling a legal deposit\n it('Finds the record invalid; Missing 856, and is non-legal deposit', async () => {\n await nonld.validate(false, f337, f020);\n });\n\n // Validate legal deposit\n it('Finds the record invalid; 856 with urn, and is legal deposit', async () => {\n await ld.validate(false, f020, f337, f856URN);\n });\n\n it('Finds the record invalid; 856 without urn, and is legal deposit', async () => {\n await ld.validate(false, f020, f337, f856URL);\n });\n\n it('Finds the record invalid; Missing 856, and is legal deposit', async () => {\n await ld.validate(false, f337, f020);\n });\n\n it('Finds the record valid; 856 with URN and legal deposit subfields, and is legal deposit', async () => {\n await ld.validate(true, f337, f020, ldf856);\n });\n\n it('Finds the record valid; 856 with URN and legal deposit subfields and other f856s, and is legal deposit', async () => {\n await ld.validate(true, f337, f020, ldf856partial, ldf856, f856URL);\n });\n\n it('Finds the record invalid; 856 with URN and partial legal deposit subfields, and is legal deposit', async () => {\n await ld.validate(false, f337, f020, ldf856partial);\n });\n\n\n });\n\n describe('#fix', () => {\n // Fix non-legal deposit\n it('856 with urn, and is non-legal deposit; Nothing to add', async () => {\n await nonld.fix([f020, f856URL, f856URN], [f020, f856URL, f856URN]);\n });\n\n // should we actually add non-resolvable urns?\n it('856 without urn, and is non-legal deposit; Adds 856 with urn', async () => {\n await nonld.fix([f020, f856URL], [f020, f856URL, f856URN]);\n });\n\n // should we actually add non-resolvable urns?\n it('Missing 856, and is non-legal deposit; Adds 856 with urn', async () => {\n await nonld.fix([f020], [f020, f856URN]);\n });\n\n // should we actually add non-resolvable urns?\n it('Missing 856, and is non-legal deposit, two 020 fields; Adds 856 with urn from second 020', async () => {\n await nonld.fix([f020second, f020], [f020second, f020, f856URN]);\n });\n\n // should we actually add non resovable urns?\n // we should think about how to choose the isbn to use in case of several ISBNs\n it.skip('Missing 856, and is non-legal deposit, two 020 fields; Adds 856 with urn from first 020', async () => {\n await nonld.fix([f020, f020second], [f020, f020second, f856URN]);\n });\n\n\n // Fix legal deposit\n it('856 with urn and legal deposit fields, and is legal deposit; Nothing to add', async () => {\n await ld.fix([f020, f856URL, ldf856], [f020, f856URL, ldf856]);\n });\n\n it('856 without urn, and is legal deposit; Adds 856 with urn and legal deposit fields', async () => {\n await ld.fix([f020, f856URL], [f020, f856URL, ldf856]);\n });\n\n // we should test generating the URN in case of no ISBN\n\n // we should test creating Melinda-temp field\n\n // We shouldn't lock the open URN for legal deposit use\n it('Missing 856, and is legal deposit; Adds 856 with urn and legal deposit fields', async () => {\n await ld.fix([f020], [f020, ldf856]);\n });\n\n // We should actually do this instead of locking the original non-legal deposit URN for legal deposit use\n it.skip('856 with urn, and is legal deposit; Adds another f856 with URN and legal deposit fields', async () => {\n await ld.fix([f020, f856URL, f856URN], [f020, f856URL, f856URN, ldf856]);\n });\n\n // We should actually add a new urn in case of a non-resource URN\n it.skip('856 with non-resource-urn, and is legal deposit; Adds another f856 with URN and legal deposit fields', async () => {\n await ld.fix([f020, f856URL, f856URNnotResource], [f020, f856URL, f856URNnotResource, ldf856]);\n });\n\n\n });\n});\n"],"mappings":"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"}
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":["isElectronicMaterial","record","f337s","get","some","field","mediaTypeIsC","subfields","sub","code","value","sourceIsRdamedia","nvdebug","message","func","undefined","fieldHasSubfield","subfieldCode","subfieldValue","sf","subfieldToString","fieldToString","f","tag","ind1","ind2","formatSubfields","map","join","fieldsToString","fields"],"sources":["../src/utils.js"],"sourcesContent":["export function isElectronicMaterial(record) {\n const f337s = record.get('337');\n\n return f337s.some(field => {\n const mediaTypeIsC = field.subfields.some(sub => sub.code === 'b' && sub.value === 'c');\n const sourceIsRdamedia = field.subfields.some(sub => sub.code === '2' && sub.value === 'rdamedia');\n return mediaTypeIsC && sourceIsRdamedia;\n });\n}\n\nexport function nvdebug(message, func = undefined) {\n if (func) { // eslint-disable-line functional/no-conditional-statement\n func(message);\n }\n //console.info(message); // eslint-disable-line no-console\n}\n\nexport function fieldHasSubfield(field, subfieldCode, subfieldValue = null) {\n if (!field.subfields) {\n return false;\n }\n if (subfieldValue === null) {\n return field.subfields.some(sf => sf.code === subfieldCode);\n }\n return field.subfields.some(sf => sf.code === subfieldCode && subfieldValue === sf.value);\n}\n\nexport function subfieldToString(sf) {\n return `‡${sf.code} ${sf.value}`;\n}\n\nexport function fieldToString(f) {\n if ('subfields' in f) {\n return `${f.tag} ${f.ind1}${f.ind2}${formatSubfields(f)}`;\n }\n return `${f.tag} ${f.value}`;\n\n function formatSubfields(field) {\n return field.subfields.map(sf => ` ${subfieldToString(sf)}`).join('');\n }\n}\n\nexport function fieldsToString(fields) {\n return fields.map(f => fieldToString(f)).join('\\t__SEPARATOR__\\t');\n}\n\n"],"mappings":";;;;;;;;;;;AAAO,SAASA,oBAAoB,CAACC,MAAM,EAAE;EAC3C,MAAMC,KAAK,GAAGD,MAAM,CAACE,GAAG,CAAC,KAAK,CAAC;EAE/B,OAAOD,KAAK,CAACE,IAAI,CAACC,KAAK,IAAI;IACzB,MAAMC,YAAY,GAAGD,KAAK,CAACE,SAAS,CAACH,IAAI,CAACI,GAAG,IAAIA,GAAG,CAACC,IAAI,KAAK,GAAG,IAAID,GAAG,CAACE,KAAK,KAAK,GAAG,CAAC;IACvF,MAAMC,gBAAgB,GAAGN,KAAK,CAACE,SAAS,CAACH,IAAI,CAACI,GAAG,IAAIA,GAAG,CAACC,IAAI,KAAK,GAAG,IAAID,GAAG,CAACE,KAAK,KAAK,UAAU,CAAC;IAClG,OAAOJ,YAAY,IAAIK,gBAAgB;EACzC,CAAC,CAAC;AACJ;AAEO,SAASC,OAAO,CAACC,OAAO,EAAEC,IAAI,GAAGC,SAAS,EAAE;EACjD,IAAID,IAAI,EAAE;IAAE;IACVA,IAAI,CAACD,OAAO,CAAC;EACf;EACA;AACF;;AAEO,SAASG,gBAAgB,CAACX,KAAK,EAAEY,YAAY,EAAEC,aAAa,GAAG,IAAI,EAAE;EAC1E,IAAI,CAACb,KAAK,CAACE,SAAS,EAAE;IACpB,OAAO,KAAK;EACd;EACA,IAAIW,aAAa,KAAK,IAAI,EAAE;IAC1B,OAAOb,KAAK,CAACE,SAAS,CAACH,IAAI,CAACe,EAAE,IAAIA,EAAE,CAACV,IAAI,KAAKQ,YAAY,CAAC;EAC7D;EACA,OAAOZ,KAAK,CAACE,SAAS,CAACH,IAAI,CAACe,EAAE,IAAIA,EAAE,CAACV,IAAI,KAAKQ,YAAY,IAAIC,aAAa,KAAKC,EAAE,CAACT,KAAK,CAAC;AAC3F;AAEO,SAASU,gBAAgB,CAACD,EAAE,EAAE;EACnC,OAAQ,IAAGA,EAAE,CAACV,IAAK,IAAGU,EAAE,CAACT,KAAM,EAAC;AAClC;AAEO,SAASW,aAAa,CAACC,CAAC,EAAE;EAC/B,IAAI,WAAW,IAAIA,CAAC,EAAE;IACpB,OAAQ,GAAEA,CAAC,CAACC,GAAI,IAAGD,CAAC,CAACE,IAAK,GAAEF,CAAC,CAACG,IAAK,GAAEC,eAAe,CAACJ,CAAC,CAAE,EAAC;EAC3D;EACA,OAAQ,GAAEA,CAAC,CAACC,GAAI,OAAMD,CAAC,CAACZ,KAAM,EAAC;EAE/B,SAASgB,eAAe,CAACrB,KAAK,EAAE;IAC9B,OAAOA,KAAK,CAACE,SAAS,CAACoB,GAAG,CAACR,EAAE,IAAK,IAAGC,gBAAgB,CAACD,EAAE,CAAE,EAAC,CAAC,CAACS,IAAI,CAAC,EAAE,CAAC;EACvE;AACF;AAEO,SAASC,cAAc,CAACC,MAAM,EAAE;EACrC,OAAOA,MAAM,CAACH,GAAG,CAACL,CAAC,IAAID,aAAa,CAACC,CAAC,CAAC,CAAC,CAACM,IAAI,CAAC,mBAAmB,CAAC;AACpE"}
1
+ {"version":3,"file":"utils.js","names":["isElectronicMaterial","record","f337s","get","some","field","mediaTypeIsC","subfields","sub","code","value","sourceIsRdamedia","nvdebug","message","func","undefined","fieldHasSubfield","subfieldCode","subfieldValue","sf","subfieldToString","fieldToString","f","tag","ind1","ind2","formatSubfields","map","join","fieldsToString","fields"],"sources":["../src/utils.js"],"sourcesContent":["export function isElectronicMaterial(record) {\n const f337s = record.get('337');\n\n return f337s.some(field => {\n const mediaTypeIsC = field.subfields.some(sub => sub.code === 'b' && sub.value === 'c');\n const sourceIsRdamedia = field.subfields.some(sub => sub.code === '2' && sub.value === 'rdamedia');\n return mediaTypeIsC && sourceIsRdamedia;\n });\n}\n\nexport function nvdebug(message, func = undefined) {\n if (func) { // eslint-disable-line functional/no-conditional-statement\n func(message);\n }\n //console.info(message); // eslint-disable-line no-console\n}\n\nexport function fieldHasSubfield(field, subfieldCode, subfieldValue = null) {\n if (!field.subfields) {\n return false;\n }\n if (subfieldValue === null) {\n return field.subfields.some(sf => sf.code === subfieldCode);\n }\n return field.subfields.some(sf => sf.code === subfieldCode && subfieldValue === sf.value);\n}\n\nexport function subfieldToString(sf) {\n return `‡${sf.code} ${sf.value}`;\n}\n\nexport function fieldToString(f) {\n if ('subfields' in f) {\n return `${f.tag} ${f.ind1}${f.ind2}${formatSubfields(f)}`;\n }\n return `${f.tag} ${f.value}`;\n\n function formatSubfields(field) {\n return field.subfields.map(sf => ` ${subfieldToString(sf)}`).join('');\n }\n}\n\nexport function fieldsToString(fields) {\n return fields.map(f => fieldToString(f)).join('\\t__SEPARATOR__\\t');\n}\n\n"],"mappings":";;;;;;;;;;;AAAO,SAASA,oBAAoBA,CAACC,MAAM,EAAE;EAC3C,MAAMC,KAAK,GAAGD,MAAM,CAACE,GAAG,CAAC,KAAK,CAAC;EAE/B,OAAOD,KAAK,CAACE,IAAI,CAACC,KAAK,IAAI;IACzB,MAAMC,YAAY,GAAGD,KAAK,CAACE,SAAS,CAACH,IAAI,CAACI,GAAG,IAAIA,GAAG,CAACC,IAAI,KAAK,GAAG,IAAID,GAAG,CAACE,KAAK,KAAK,GAAG,CAAC;IACvF,MAAMC,gBAAgB,GAAGN,KAAK,CAACE,SAAS,CAACH,IAAI,CAACI,GAAG,IAAIA,GAAG,CAACC,IAAI,KAAK,GAAG,IAAID,GAAG,CAACE,KAAK,KAAK,UAAU,CAAC;IAClG,OAAOJ,YAAY,IAAIK,gBAAgB;EACzC,CAAC,CAAC;AACJ;AAEO,SAASC,OAAOA,CAACC,OAAO,EAAEC,IAAI,GAAGC,SAAS,EAAE;EACjD,IAAID,IAAI,EAAE;IAAE;IACVA,IAAI,CAACD,OAAO,CAAC;EACf;EACA;AACF;;AAEO,SAASG,gBAAgBA,CAACX,KAAK,EAAEY,YAAY,EAAEC,aAAa,GAAG,IAAI,EAAE;EAC1E,IAAI,CAACb,KAAK,CAACE,SAAS,EAAE;IACpB,OAAO,KAAK;EACd;EACA,IAAIW,aAAa,KAAK,IAAI,EAAE;IAC1B,OAAOb,KAAK,CAACE,SAAS,CAACH,IAAI,CAACe,EAAE,IAAIA,EAAE,CAACV,IAAI,KAAKQ,YAAY,CAAC;EAC7D;EACA,OAAOZ,KAAK,CAACE,SAAS,CAACH,IAAI,CAACe,EAAE,IAAIA,EAAE,CAACV,IAAI,KAAKQ,YAAY,IAAIC,aAAa,KAAKC,EAAE,CAACT,KAAK,CAAC;AAC3F;AAEO,SAASU,gBAAgBA,CAACD,EAAE,EAAE;EACnC,OAAQ,IAAGA,EAAE,CAACV,IAAK,IAAGU,EAAE,CAACT,KAAM,EAAC;AAClC;AAEO,SAASW,aAAaA,CAACC,CAAC,EAAE;EAC/B,IAAI,WAAW,IAAIA,CAAC,EAAE;IACpB,OAAQ,GAAEA,CAAC,CAACC,GAAI,IAAGD,CAAC,CAACE,IAAK,GAAEF,CAAC,CAACG,IAAK,GAAEC,eAAe,CAACJ,CAAC,CAAE,EAAC;EAC3D;EACA,OAAQ,GAAEA,CAAC,CAACC,GAAI,OAAMD,CAAC,CAACZ,KAAM,EAAC;EAE/B,SAASgB,eAAeA,CAACrB,KAAK,EAAE;IAC9B,OAAOA,KAAK,CAACE,SAAS,CAACoB,GAAG,CAACR,EAAE,IAAK,IAAGC,gBAAgB,CAACD,EAAE,CAAE,EAAC,CAAC,CAACS,IAAI,CAAC,EAAE,CAAC;EACvE;AACF;AAEO,SAASC,cAAcA,CAACC,MAAM,EAAE;EACrC,OAAOA,MAAM,CAACH,GAAG,CAACL,CAAC,IAAID,aAAa,CAACC,CAAC,CAAC,CAAC,CAACM,IAAI,CAAC,mBAAmB,CAAC;AACpE"}
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "url": "git@github.com:natlibfi/marc-record-validators-melinda.git"
15
15
  },
16
16
  "license": "MIT",
17
- "version": "10.2.3",
17
+ "version": "10.3.0",
18
18
  "main": "./dist/index.js",
19
19
  "publishConfig": {
20
20
  "access": "public"
@@ -41,7 +41,7 @@
41
41
  "@natlibfi/marc-record-validate": "^7.0.3",
42
42
  "cld3-asm": "^3.1.1",
43
43
  "debug": "^4.3.4",
44
- "isbn3": "^1.1.30",
44
+ "isbn3": "^1.1.34",
45
45
  "langs": "^2.0.0",
46
46
  "node-fetch": "^2.6.8",
47
47
  "xml2js": ">=0.4.23 <1.0.0",
@@ -0,0 +1,398 @@
1
+ /*
2
+ * punctuation.js -- try and fix a marc field punctuation
3
+ *
4
+ * Author(s): Nicholas Volk <nicholas.volk@helsinki.fi>
5
+ *
6
+ * NOTE #1: https://www.kiwi.fi/display/kumea/Loppupisteohje is implemented via another validator/fixer.(ending-punctuation)
7
+ * NOTE #2: Validator/fixer punctuation does similar stuff, but focuses on X00 fields.
8
+ */
9
+ import {validateSingleField} from './ending-punctuation';
10
+ // import createDebugLogger from 'debug';
11
+ import {fieldToString, nvdebug} from './utils';
12
+ import clone from 'clone';
13
+
14
+ // const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/punctuation2');
15
+
16
+ export default function () {
17
+ return {
18
+ description: 'Add punctuation to data fields',
19
+ validate, fix
20
+ };
21
+
22
+ function fix(record) {
23
+ nvdebug('Add punctuation to data fields: fixer');
24
+ const res = {message: [], fix: [], valid: true};
25
+ record.fields.forEach(f => fieldFixPunctuation(f));
26
+ return res;
27
+ }
28
+
29
+ function validate(record) {
30
+ nvdebug('Add punctuation to data fields: validate');
31
+
32
+ const fieldsNeedingModification = record.fields.filter(f => fieldNeedsPunctuation(f));
33
+
34
+
35
+ const values = fieldsNeedingModification.map(f => fieldToString(f));
36
+ const newValues = fieldsNeedingModification.map(f => fieldGetFixedString(f));
37
+
38
+ const messages = values.map((val, i) => `'${val}' => '${newValues[i]}'`);
39
+
40
+ const res = {message: messages};
41
+
42
+ res.valid = res.message.length < 1; // eslint-disable-line functional/immutable-data
43
+ return res;
44
+ }
45
+ }
46
+
47
+
48
+ function fieldGetFixedString(field) {
49
+ const cloneField = clone(field);
50
+ cloneField.subfields.forEach((sf, i) => {
51
+ // NB! instead of next subfield, we should actually get next *non-control-subfield*!!!
52
+ // (In plain English: We should skip $0 - $9 at least, maybe $w as well...)
53
+ subfieldFixPunctuation(cloneField.tag, sf, i + 1 < cloneField.subfields.length ? cloneField.subfields[i + 1] : null);
54
+ });
55
+ return fieldToString(cloneField);
56
+ }
57
+
58
+ function fieldNeedsPunctuation(field) {
59
+ if (!field.subfields) {
60
+ return false;
61
+ }
62
+
63
+ const originalFieldAsString = fieldToString(field);
64
+ const modifiedFieldAsString = fieldGetFixedString(field);
65
+
66
+ return modifiedFieldAsString !== originalFieldAsString;
67
+ }
68
+
69
+ /////////////////////////////////////////////////////////////////////////////////////
70
+ // <= Above code is written for the validator logic <= //
71
+ // => Everything below was originally transferred from reducers' punctuation.js => //
72
+ /////////////////////////////////////////////////////////////////////////////////////
73
+
74
+
75
+ //const stripCrap = / *[-;:,+]+$/u;
76
+ const commaNeedsPuncAfter = /(?:[a-z0-9A-Z]|å|ä|ö|Å|Ä|Ö|\))$/u;
77
+ const defaultNeedsPuncAfter = /(?:[a-z0-9A-Z]|å|ä|ö|Å|Ä|Ö)$/u;
78
+ const defaultNeedsPuncAfter2 = /(?:[\]a-zA-Z0-9)]|ä|å|ö|Å|Ä|Ö)$/u;
79
+ const blocksPuncRHS = /^(?:\()/u;
80
+ const allowsPuncRHS = /^(?:[A-Za-z0-9]|å|ä|ö|Å|Ä|Ö)/u;
81
+
82
+ const dotIsProbablyPunc = /(?:[a-z0-9)]|å|ä|ö)\.$/u;
83
+ const puncIsProbablyPunc = /(?:[a-z0-9)]|å|ä|ö) ?[.,:;]$/u;
84
+ // NB! 65X: Finnish terms don't use punctuation, but international ones do. Neither one is currently (2021-11-08) coded here.
85
+
86
+ // Will unfortunately trigger "Sukunimi, Th." type:
87
+ const removeColons = {'code': 'abcdefghijklmnopqrstuvwxyz', 'remove': / *[;:]$/u};
88
+ const removeX00Comma = {'code': 'abcqde', 'followedBy': 'abcqde#01459', 'context': /.,$/u, 'remove': /,$/u};
89
+ const cleanRHS = {'code': 'abcd', 'followedBy': 'bcde', 'context': /(?:(?:[a-z0-9]|å|ä|ö)\.|,)$/u, 'contextRHS': blocksPuncRHS, 'remove': /[.,]$/u};
90
+ const cleanX00dCommaOrDot = {'code': 'd', 'followedBy': 'et#01459', 'context': /[0-9]-[,.]$/u, 'remove': /[,.]$/u};
91
+ const cleanX00aDot = {'code': 'abcde', 'followedBy': 'cdegj', 'context': dotIsProbablyPunc, 'remove': /\.$/u};
92
+ const cleanCorruption = {'code': 'abcdefghijklmnopqrstuvwxyz', 'remove': / \.$/u};
93
+ // These $e dot removals are tricky: before removing the comma, we should know that it ain't an abbreviation such as "esitt."...
94
+ const cleanX00eDot = {'code': 'e', 'followedBy': 'egj#059', 'context': /(?:[ai]ja|jä)[.,]$/u, 'remove': /\.$/u};
95
+
96
+ const X00RemoveDotAfterBracket = {'code': 'cq', 'context': /\)\.$/u, 'remove': /\.$/u};
97
+ // 390, 800, 810, 830...
98
+ const cleanPuncBeforeLanguage = {'code': 'atvxyz', 'followedBy': 'l', 'context': puncIsProbablyPunc, 'remove': / *[.,:;]$/u};
99
+
100
+
101
+ const addX00aComma = {'add': ',', 'code': 'abcqdej', 'followedBy': 'cdeg', 'context': commaNeedsPuncAfter, 'contextRHS': allowsPuncRHS};
102
+ const addX00aComma2 = {'add': ',', 'code': 'abcdej', 'followedBy': 'cdeg', 'context': /(?:[A-Z]|Å|Ä|Ö)\.$/u, 'contextRHS': allowsPuncRHS};
103
+ const addX00aDot = {'add': '.', 'code': 'abcde', 'followedBy': '#tu0159', 'context': defaultNeedsPuncAfter};
104
+
105
+ const addX10bDot = {'name': 'Add X10 pre-$b dot', 'add': '.', 'code': 'ab', 'followedBy': 'b', 'context': defaultNeedsPuncAfter};
106
+ const addX10eComma = {'add': ',', 'code': 'abe', 'followedBy': 'e', 'context': defaultNeedsPuncAfter};
107
+ const addX10Dot = {'name': 'Add X10 final dot', 'add': '.', 'code': 'abe', 'followedBy': '#0159', 'context': defaultNeedsPuncAfter};
108
+ const addLanguageComma = {'name': 'Add comma before 810$l', 'add': ',', 'code': 'tv', 'followedBy': 'l', 'context': defaultNeedsPuncAfter2};
109
+ const addColonToRelationshipInformation = {'name': 'Add \':\' to 7X0 $i relationship info', 'add': ':', 'code': 'i', 'context': /[a-z)åäö]$/u};
110
+
111
+ // 490:
112
+ const addSemicolonBeforeVolumeDesignation = {'name': 'Add " ;" before $v', 'add': ' ;', 'code': 'atxy', 'followedBy': 'v', 'context': /[^;]$/u};
113
+
114
+ const NONE = 0;
115
+ const ADD = 2;
116
+ const REMOVE = 1;
117
+ const REMOVE_AND_ADD = 3;
118
+
119
+ // Crappy punctuation consists of various crap that is somewhat common.
120
+ // We strip crap for merge decisions. We are not trying to actively remove crap here.
121
+
122
+ const removeX00Whatever = [removeX00Comma, cleanX00aDot, cleanX00eDot, cleanCorruption, cleanX00dCommaOrDot, cleanRHS, X00RemoveDotAfterBracket, removeColons, cleanPuncBeforeLanguage];
123
+ const removeX10Whatever = [removeX00Comma, cleanX00aDot, cleanX00eDot, cleanCorruption, removeColons, cleanPuncBeforeLanguage];
124
+
125
+ const cleanCrappyPunctuationRules = {
126
+ '100': removeX00Whatever,
127
+ '110': removeX10Whatever,
128
+ '600': removeX00Whatever,
129
+ '610': removeX10Whatever,
130
+ '700': removeX00Whatever,
131
+ '710': removeX10Whatever,
132
+ '800': removeX00Whatever,
133
+ '810': removeX10Whatever,
134
+ '245': [{'code': 'ab', 'followedBy': '!c', 'remove': / \/$/u}],
135
+ '300': [
136
+ {'code': 'a', 'followedBy': '!b', 'remove': / *:$/u},
137
+ {'code': 'a', 'followedBy': 'b', 'remove': /:$/u, 'context': /[^ ]:$/u},
138
+ {'code': 'ab', 'followedBy': '!c', 'remove': / *;$/u},
139
+ {'code': 'ab', 'followedBy': 'c', 'remove': /;$/u, 'context': /[^ ];$/u},
140
+ {'code': 'abc', 'followedBy': '!e', 'remove': / *\+$/u},
141
+ {'code': 'abc', 'followedBy': '!e', 'remove': / *\+$/u, 'context': /[^ ]\+$/u}
142
+
143
+ ],
144
+ '490': [{'code': 'a', 'followedBy': 'xy', 'remove': / ;$/u}],
145
+ '773': [{'code': 'abdghiklmnopqrstuwxyz', 'followedBy': 'abdghiklmnopqrstuwxyz', 'remove': /\. -$/u}]
146
+
147
+ };
148
+
149
+ const cleanLegalX00Comma = {'code': 'abcde', 'followedBy': 'cdegj', 'context': /.,$/u, 'remove': /,$/u};
150
+ // Accept upper case letters in X00$b, since they are probably Roman numerals.
151
+ const cleanLegalX00bDot = {'code': 'b', 'followedBy': 't#01459', context: /^[IVXLCDM]+\.$/u, 'remove': /\.$/u};
152
+ const cleanLegalX00Dot = {'code': 'abcdetvl', 'followedBy': 'tu#01459', 'context': /(?:[a-z0-9)]|å|ä|ö)\.$/u, 'remove': /\.$/u};
153
+ const cleanLanguageComma = {'name': 'language comma', 'code': 'tv', 'followedBy': 'l', 'context': /.,$/u, 'remove': /,$/u};
154
+
155
+
156
+ const legalX00punc = [cleanLegalX00Comma, cleanLegalX00bDot, cleanLegalX00Dot, cleanLanguageComma];
157
+
158
+ const cleanLegalX10Comma = {'name': 'X10comma', 'code': 'abe', 'followedBy': 'e', 'context': /.,$/u, 'remove': /,$/u};
159
+ const cleanLegalX10Dot = {'name': 'X10dot', 'code': 'ab', 'followedBy': 'b#059', 'context': /.\.$/u, 'remove': /\.$/u};
160
+
161
+ const legalX10punc = [cleanLegalX10Comma, cleanLegalX10Dot, cleanX00eDot, cleanLanguageComma];
162
+
163
+ const cleanValidPunctuationRules = {
164
+ '100': legalX00punc,
165
+ '110': legalX10punc,
166
+ '600': legalX00punc,
167
+ '610': legalX10punc,
168
+ '700': legalX00punc,
169
+ '710': legalX10punc,
170
+ '800': legalX00punc,
171
+ '810': legalX10punc,
172
+ '245': [
173
+ {'name': 'A:B', 'code': 'a', 'followedBy': 'b', 'remove': / [:;=]$/u},
174
+ {'name': 'AB:K', 'code': 'ab', 'followedBy': 'k', 'remove': / :$/u},
175
+ {'name': 'ABK:F', 'code': 'abk', 'followedBy': 'f', 'remove': /,$/u},
176
+ {'name': 'ABFNP:C', 'code': 'abfnp', 'followedBy': 'c', 'remove': / \/$/u},
177
+ {'name': 'ABN:N', 'code': 'abn', 'followedBy': 'n', 'remove': /\.$/u},
178
+ {'name': 'ABNP:#', 'code': 'abnp', 'followedBy': '#', 'remove': /\.$/u},
179
+ {'name': 'N:P', 'code': 'n', 'followedBy': 'p', 'remove': /,$/u}
180
+
181
+ ],
182
+ '260': [
183
+ {'code': 'a', 'followedBy': 'b', 'remove': / :$/u},
184
+ {'code': 'b', 'followedBy': 'c', 'remove': /,$/u},
185
+ {'code': 'c', 'followedBy': '#', 'remove': /\.$/u},
186
+ {'code': 'd', 'followedBy': 'e', 'remove': / :$/u},
187
+ {'code': 'e', 'followedBy': 'f', 'remove': /,$/u},
188
+ {'code': 'f', 'followedBy': '#', 'remove': /\.$/u} // Probably ')' but shouldit be removed?
189
+ ],
190
+ '264': [
191
+ {'code': 'a', 'followedBy': 'b', 'remove': / :$/u},
192
+ {'code': 'b', 'followedBy': 'c', 'remove': /,$/u},
193
+ {'code': 'c', 'followedBy': '#', 'remove': /\.$/u}
194
+ ],
195
+ '300': [
196
+ // NB! Remove crap as well, thus the '*' in / *:$/
197
+ {'code': 'a', 'followedBy': 'b', 'remove': / :$/u},
198
+ {'code': 'ab', 'followedBy': 'c', 'remove': / ;$/u},
199
+ {'code': 'abc', 'followedBy': 'e', 'remove': / \+$/u}
200
+ ],
201
+ '490': [
202
+ {'code': 'axy', 'followedBy': 'xy', 'remove': /,$/u},
203
+ {'code': 'axy', 'followedBy': 'v', 'remove': / *;$/u}
204
+ ],
205
+ '534': [{'code': 'p', 'followedBy': 'c', 'remove': /:$/u}]
206
+
207
+ };
208
+
209
+ // addColonToRelationshipInformation only applies to 700/710 but as others don't have $i, it's fine
210
+ const addX00 = [addX00aComma, addX00aComma2, addX00aDot, addLanguageComma, addSemicolonBeforeVolumeDesignation, addColonToRelationshipInformation];
211
+ const addX10 = [addX10bDot, addX10eComma, addX10Dot, addLanguageComma, addSemicolonBeforeVolumeDesignation, addColonToRelationshipInformation];
212
+ const addPairedPunctuationRules = {
213
+ '100': addX00,
214
+ '110': addX10,
215
+ '245': [
216
+ // Blah! Also "$a = $b" and "$a ; $b" can be valid... But ' :' is better than nothing, I guess...
217
+ {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter},
218
+ {'code': 'abk', 'followedBy': 'f', 'add': ',', 'context': defaultNeedsPuncAfter},
219
+ {'code': 'abfnp', 'followedBy': 'c', 'add': ' /', 'context': defaultNeedsPuncAfter}
220
+ ],
221
+ '260': [
222
+ {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter2},
223
+ {'code': 'b', 'followedBy': 'c', 'add': ',', 'context': defaultNeedsPuncAfter2},
224
+ {'code': 'abc', 'followedBy': 'a', 'add': ' ;', 'context': defaultNeedsPuncAfter2},
225
+ {'code': 'e', 'followedBy': 'f', 'add': ' :', 'context': defaultNeedsPuncAfter2},
226
+ {'code': 'f', 'followedBy': 'g', 'add': ',', 'context': defaultNeedsPuncAfter2}
227
+ ],
228
+ '264': [
229
+ {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter2},
230
+ {'code': 'b', 'followedBy': 'c', 'add': ',', 'context': defaultNeedsPuncAfter2},
231
+ // NB! The $c rule messes dotless exception "264 #4 $c p1983" up
232
+ // We'll need to add a hacky postprocessor for this? Add 'hasInd1': '0123' etc?
233
+ {'code': 'c', 'followedBy': '#', 'add': '.', 'context': defaultNeedsPuncAfter2}
234
+ ],
235
+ '300': [
236
+ {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter2},
237
+ {'code': 'ab', 'followedBy': 'c', 'add': ' ;', 'context': defaultNeedsPuncAfter2},
238
+ {'code': 'abc', 'followedBy': 'e', 'add': ' +', 'context': defaultNeedsPuncAfter2}
239
+ ],
240
+ '490': [
241
+ {'code': 'axy', 'followedBy': 'xy', 'add': ',', 'context': defaultNeedsPuncAfter},
242
+ addSemicolonBeforeVolumeDesignation
243
+ //{'code': 'axy', 'followedBy': 'v', 'add': ' ;', 'context': defaultNeedsPuncAfter}
244
+ ],
245
+ '506': [{'code': 'a', 'followedBy': '#', 'add': '.', 'context': defaultNeedsPuncAfter2}],
246
+ '534': [{'code': 'p', 'followedBy': 'c', 'add': ':', 'context': defaultNeedsPuncAfter2}],
247
+ '600': addX00,
248
+ '610': addX10,
249
+ '700': addX00,
250
+ '710': addX10,
251
+ '800': addX00,
252
+ '810': addX10,
253
+ '830': [
254
+ {'code': 'axy', 'followedBy': 'xy', 'add': ',', 'context': defaultNeedsPuncAfter},
255
+ addSemicolonBeforeVolumeDesignation
256
+ //{'code': 'axy', 'followedBy': 'v', 'add': ' ;', 'context': defaultNeedsPuncAfter}
257
+ ]
258
+
259
+ };
260
+
261
+ function ruleAppliesToSubfieldCode(targetSubfieldCodes, currSubfieldCode) {
262
+ const negation = targetSubfieldCodes.includes('!');
263
+ if (negation) {
264
+ return !targetSubfieldCodes.includes(currSubfieldCode);
265
+ }
266
+ return targetSubfieldCodes.includes(currSubfieldCode);
267
+ }
268
+
269
+
270
+ function ruleAppliesToCurrentSubfield(rule, subfield) {
271
+ if (!ruleAppliesToSubfieldCode(rule.code, subfield.code)) {
272
+ return false;
273
+ }
274
+ if ('context' in rule && !subfield.value.match(rule.context)) { // njsscan-ignore: regex_injection_dos
275
+ return false;
276
+ }
277
+ return true;
278
+ }
279
+
280
+ function ruleAppliesToNextSubfield(rule, nextSubfield) {
281
+ if (!('followedBy' in rule)) { // Return true, if we are not interested in the next subfield
282
+ return true;
283
+ }
284
+ // The '#' existence check applies only to the RHS field. LHS always exists.
285
+ if (nextSubfield === null) {
286
+ const negation = rule.followedBy.includes('!');
287
+ if (negation) {
288
+ return !rule.followedBy.includes('#');
289
+ }
290
+ return rule.followedBy.includes('#');
291
+ }
292
+
293
+ if (!ruleAppliesToSubfieldCode(rule.followedBy, nextSubfield.code)) {
294
+ return false;
295
+ }
296
+ if ('contextRHS' in rule && !nextSubfield.value.match(rule.contextRHS)) { // njsscan-ignore: regex_injection_dos
297
+ return false;
298
+ }
299
+ return true;
300
+ }
301
+
302
+ function checkRule(rule, subfield1, subfield2) {
303
+ //const name = rule.name || 'UNNAMED';
304
+ if (!ruleAppliesToCurrentSubfield(rule, subfield1)) {
305
+ //nvdebug(`${name}: FAIL ON LHS FIELD: '$${subfield1.code} ${subfield1.value}', SF=${rule.code}`, debug);
306
+ return false;
307
+ }
308
+
309
+ // NB! This is not a perfect solution. We might have $e$0$e where $e$0 punctuation should actually be based on $e$e rules
310
+ if (!ruleAppliesToNextSubfield(rule, subfield2)) {
311
+ //const msg = subfield2 ? `${name}: FAIL ON RHS FIELD '${subfield2.code}' not in [${rule.followedBy}]` : `${name}: FAIL ON RHS FIELD`;
312
+ //nvdebug(msg, debug);
313
+ return false;
314
+ }
315
+
316
+ //nvdebug(`${name}: ACCEPT ${rule.code}/${subfield1.code}, SF2=${rule.followedBy}/${subfield2 ? subfield2.code : '#'}`, debug);
317
+ return true;
318
+ }
319
+
320
+ function applyPunctuationRules(tag, subfield1, subfield2, ruleArray = null, operation = NONE) {
321
+
322
+ /*
323
+ if (ruleArray === null || operation === NONE) {
324
+ debug(`applyPunctuation(): No rules to apply!`);
325
+ return;
326
+ }
327
+ */
328
+ if (!(`${tag}` in ruleArray) || ruleArray === null || operation === NONE) {
329
+
330
+ /*
331
+ if (!['020', '650'].includes(tag) || !isControlSubfieldCode(subfield1.code)) { // eslint-disable-line functional/no-conditional-statement
332
+ nvdebug(`No punctuation rules found for ${tag} (looking for: ‡${subfield1.code})`, debug);
333
+
334
+ }
335
+ */
336
+ return;
337
+ }
338
+
339
+ //nvdebug(`OP=${operation} ${tag}: '${subfield1.code}: ${subfield1.value}' ??? '${subfield2 ? subfield2.code : '#'}'`, debug);
340
+ const activeRules = ruleArray[tag].filter(rule => checkRule(rule, subfield1, subfield2));
341
+
342
+ activeRules.forEach(rule => {
343
+ const originalValue = subfield1.value;
344
+ if (rule.remove && [REMOVE, REMOVE_AND_ADD].includes(operation) && subfield1.value.match(rule.remove)) { // eslint-disable-line functional/no-conditional-statement
345
+ //nvdebug(` PUNC REMOVAL TO BE PERFORMED FOR $${subfield1.code} '${subfield1.value}'`, debug);
346
+ subfield1.value = subfield1.value.replace(rule.remove, ''); // eslint-disable-line functional/immutable-data
347
+ //nvdebug(` PUNC REMOVAL PERFORMED FOR '${subfield1.value}'`, debug);
348
+ }
349
+ if (rule.add && [ADD, REMOVE_AND_ADD].includes(operation)) { // eslint-disable-line functional/no-conditional-statement
350
+ subfield1.value += rule.add; // eslint-disable-line functional/immutable-data
351
+ //nvdebug(` ADDED '${rule.add}' TO '${subfield1.value}'`, debug);
352
+ }
353
+ if (subfield1.value !== originalValue) { // eslint-disable-line functional/no-conditional-statement
354
+ //nvdebug(` PROCESS PUNC: '‡${subfield1.code} ${originalValue}' => '‡${subfield1.code} ${subfield1.value}'`, debug); // eslint-disable-line functional/immutable-data
355
+ }
356
+ });
357
+ }
358
+
359
+ function subfieldFixPunctuation(tag, subfield1, subfield2) {
360
+ applyPunctuationRules(tag, subfield1, subfield2, cleanCrappyPunctuationRules, REMOVE);
361
+ applyPunctuationRules(tag, subfield1, subfield2, addPairedPunctuationRules, ADD);
362
+ }
363
+
364
+
365
+ export function fieldStripPunctuation(field) {
366
+ if (!field.subfields) {
367
+ return field;
368
+ }
369
+
370
+ field.subfields.forEach((sf, i) => {
371
+ nvdebug(`FSP1: '${sf.value}'`);
372
+ applyPunctuationRules(field.tag, sf, i + 1 < field.subfields.length ? field.subfields[i + 1] : null, cleanValidPunctuationRules, REMOVE);
373
+ nvdebug(`FSP2: '${sf.value}'`);
374
+ applyPunctuationRules(field.tag, sf, i + 1 < field.subfields.length ? field.subfields[i + 1] : null, cleanCrappyPunctuationRules, REMOVE);
375
+ nvdebug(`FSP3: '${sf.value}'`);
376
+ });
377
+ return field;
378
+ }
379
+
380
+ export function fieldFixPunctuation(field) {
381
+ if (!field.subfields) {
382
+ return field;
383
+ }
384
+ nvdebug(`################### fieldFixPunctuation() TEST ${fieldToString(field)}`);
385
+
386
+ field.subfields.forEach((sf, i) => {
387
+ // NB! instead of next subfield, we should actually get next *non-control-subfield*!!!
388
+ // (In plain English: We should skip $0 - $9 at least, maybe $w as well...)
389
+ subfieldFixPunctuation(field.tag, sf, i + 1 < field.subfields.length ? field.subfields[i + 1] : null);
390
+ });
391
+
392
+ // Use shared code for final punctuation (sadly this does not fix intermediate punc):
393
+ if (field.useExternalEndPunctuation) { // eslint-disable-line functional/no-conditional-statement
394
+ // addFinalPunctuation(field); // local version. use shared code instead.
395
+ validateSingleField(field, false, true); // NB! Don't use field.tag as second argument! It's a string, not an int. 3rd arg must be true (=fix)
396
+ }
397
+ return field;
398
+ }
@@ -0,0 +1,52 @@
1
+ import {expect} from 'chai';
2
+ import {MarcRecord} from '@natlibfi/marc-record';
3
+ import validatorFactory from './punctuation2';
4
+ import {READERS} from '@natlibfi/fixura';
5
+ import generateTests from '@natlibfi/fixugen';
6
+ import createDebugLogger from 'debug';
7
+
8
+ generateTests({
9
+ callback,
10
+ path: [__dirname, '..', 'test-fixtures', 'punctuation2'],
11
+ useMetadataFile: true,
12
+ recurse: false,
13
+ fixura: {
14
+ reader: READERS.JSON
15
+ },
16
+ mocha: {
17
+ before: () => testValidatorFactory()
18
+ }
19
+ });
20
+ const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/punctuation2:test');
21
+
22
+ async function testValidatorFactory() {
23
+ const validator = await validatorFactory();
24
+
25
+ expect(validator)
26
+ .to.be.an('object')
27
+ .that.has.any.keys('description', 'validate');
28
+
29
+ expect(validator.description).to.be.a('string');
30
+ expect(validator.validate).to.be.a('function');
31
+ }
32
+
33
+ async function callback({getFixture, enabled = true, fix = false}) {
34
+ if (enabled === false) {
35
+ debug('TEST SKIPPED!');
36
+ return;
37
+ }
38
+
39
+ const validator = await validatorFactory();
40
+ const record = new MarcRecord(getFixture('record.json'));
41
+ const expectedResult = getFixture('expectedResult.json');
42
+ // console.log(expectedResult); // eslint-disable-line
43
+
44
+ if (!fix) {
45
+ const result = await validator.validate(record);
46
+ expect(result).to.eql(expectedResult);
47
+ return;
48
+ }
49
+
50
+ await validator.fix(record);
51
+ expect(record).to.eql(expectedResult);
52
+ }