{"_id":"@cwrc/salve-leafwriter","_rev":"1-f924555638d077493be0fe440ef62009","name":"@cwrc/salve-leafwriter","dist-tags":{"latest":"1.2.2"},"versions":{"1.2.1":{"name":"@cwrc/salve-leafwriter","description":"A fork with support for documentation of Salve, a Javascript library which implements a validator able to validate an XML document on the basis of a subset of RelaxNG.","version":"1.2.1","versionedSources":"lib/salve/validate.ts","keywords":["RelaxNG","Relax NG","rng","XML","validation"],"publishConfig":{"access":"public"},"homepage":"https://github.com/mangalam-research/salve","author":{"name":"Louis-Dominique Dubeau","email":"louisd@mangalamresearch.org"},"contributors":[{"name":"Jesse Bethel","email":"keisetsu@gmail.com"},{"name":"Bennett Buchanan","email":"buchanan.3rd@gmail.com"},{"name":"Raff Viglianti","email":"raffaeleviglianti@gmai.com"},{"name":"Luciano Frizzera","email":"lucaju@gmail.com"}],"main":"lib/salve/validate.js","browser":"./salve.min.js","types":"lib/salve/validate.d.ts","repository":{"type":"git","url":"git+https://gitlab.com/calincs/cwrc/leaf-writer/salve.git"},"bugs":{"url":"https://gitlab.com/calincs/cwrc/leaf-writer/salve/-/issues"},"license":"MPL-2.0","engines":{"node":">=8","npm":">=6.14.7"},"dependencies":{"@trust/webcrypto":"git+https://github.com/lddubeau/webcrypto.git#fix/pass-string-to-digest","@types/xregexp":"^4.3.0","argparse":">=1 <2","file-url":"^3.0.0","node-fetch":"^2.6.1","require-dir":"^1.2.0","saxes":"^4.0.2","temp":"^0.9.4","tslib":"^1.14.1","xregexp":"^4.4.1"},"devDependencies":{"@commitlint/cli":"^8.3.5","@commitlint/config-angular":"^8.3.4","@types/argparse":"^1.0.38","@types/chai":"^4.2.18","@types/file-url":"^2.0.0","@types/mocha":"^5.2.7","@types/node":"^10.17.60","@types/node-fetch":"^2.5.10","@types/require-dir":"^1.0.1","@types/temp":"^0.8.34","bluebird":"^3.7.2","chai":"^4.3.4","child-process-promise":"^2.2.1","commitizen":"^4.2.4","conventional-changelog-cli":"^2.1.1","del":"^6.0.0","eslint":"^6.8.0","eslint-config-lddubeau-base":"^4.0.2","fancy-log":"^2.0.0","git-cz":"^4.7.6","gulp":"^4.0.2","gulp-eslint":"^6.0.0","gulp-newer":"^1.4.0","husky":"^3.1.0","karma":"^4.4.1","karma-browserstack-launcher":"^1.6.0","karma-chai":"^0.1.0","karma-chrome-launcher":"^3.1.0","karma-firefox-launcher":"^1.3.0","karma-mocha":"^1.3.0","karma-mocha-reporter":"^2.2.5","karma-source-map-support":"^1.4.0","merge-options":"^2.0.0","mocha":"^6.2.3","renovate-config-lddubeau":"^1.0.0","source-map-support":"^0.5.19","terser-webpack-plugin":"^2.3.8","touch":"^3.1.0","ts-node":"^8.10.2","tslint":"^5.20.1","tslint-config-lddubeau":"^4.1.2","typedoc":"^0.17.8","typescript":"^3.9.9","versync":"^5.0.0","webpack":"^4.46.0","webpack-cli":"^3.3.12"},"bin":{"salve-convert":"bin/salve-convert"},"scripts":{"build":"gulp","test":"gulp test","install_test":"gulp install_test","preversion":"npm test && npm run install_test && git flow release start $npm_package_version","version":"versync -b sync -a && conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md","cm":"git-cz"},"gitHead":"8891423fc6d7d6ea19963a397851b356c4b41698","_id":"@cwrc/salve-leafwriter@1.2.1","_nodeVersion":"16.14.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-0PMOGKFruWldJACKLYDkFi9T3FrbHGREPlFyGVOH/Zts/Nn4YqGASpPpYv2yuL9kVjOr8dyVjVKtbFTWi8fwDw==","shasum":"7e43078576308973347c40634a1875bfe3eb79d1","tarball":"https://registry.npmjs.org/@cwrc/salve-leafwriter/-/salve-leafwriter-1.2.1.tgz","fileCount":1412,"unpackedSize":11688590,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC8XnxOY9zwD7Rvc3MPt4b4V3G7TpWT0B1GTUYge4qHuAiBFa6zQRREIRDagZMay6CMnmsAQGoa14SJic1Ob19HL7g=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRc12ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmon6w//QztUj+8ynI3j1e+3Lmt6sAT7N4nDl5ij4Ycr5Ft4hwRHtGz8\r\n1rTtCHtPxPZHCj1pnizyR1AIhtKtbEx4lclcrEXmVXcPy96lyb41DGCfYoKC\r\nkI7TZ4jHtD7aKDn/AiUpPgBCHjYrMZVOP7qh06yK/+zLM+VM7h8M5yeGvBsj\r\nGkCTMEDd9PsUAxtzNyuC2nr12wmyq+K2b3BjIVfqb/mM7JN/ytcWLise65dO\r\nGW2gTgNqYLkBQ8hYrEM9XMxZnSwv3c70fl+xliHjY6EVrHfVakpO2nnzU5la\r\nGhCOfJWvtmsfKNCia5chGBjTZJ9Ip5VtpQr8ByMy0RNFa8g9XCwoz20P9MoA\r\nQ/XGZ5Z6ut8A8gTiVjhPR8l81NyPgs6e3cfxCtbGH4D9s/Hxr7ui1rYIhIoY\r\n1rgl8mJMKIOu/5MdmJxO8+zCv359/7cwQbTcbjY6zPDXtvXP7G11qhIobf3+\r\nFXF8nyd/k5C89y9M8GzZb7IEf/+yZiXQT9QNWaXCOaQ0XBdwJPw2keV6ZAVL\r\nRHmNrnJtt5k9w67lkqgyJ1TUau6M3ASd82w+yYkfEyQXLFDT5vVU+c9z8PvA\r\nhpCWYfYw0Uz60yqTs3tKUIpw7bg+l/XHE0sR9/L9dTwprL2oUFgUOee1B8CQ\r\npoPGZgTyJGVrTn3/DZMhTuYdkf2CcIVtO+U=\r\n=Xcki\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"lucaju","email":"lucaju@gmail.com"},"directories":{},"maintainers":[{"name":"canadian-writing-research-collaboratory","email":"cwrc@ualberta.ca"},{"name":"lucaju","email":"lucaju@gmail.com"},{"name":"ajmacdonald","email":"andrewjames.code@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/salve-leafwriter_1.2.1_1648741750668_0.6512610315568408"},"_hasShrinkwrap":false},"1.2.2":{"name":"@cwrc/salve-leafwriter","description":"A fork with support for documentation of Salve, a Javascript library which implements a validator able to validate an XML document on the basis of a subset of RelaxNG.","version":"1.2.2","versionedSources":"lib/salve/validate.ts","keywords":["RelaxNG","Relax NG","rng","XML","validation"],"publishConfig":{"access":"public"},"homepage":"https://github.com/mangalam-research/salve","author":{"name":"Louis-Dominique Dubeau","email":"louisd@mangalamresearch.org"},"contributors":[{"name":"Jesse Bethel","email":"keisetsu@gmail.com"},{"name":"Bennett Buchanan","email":"buchanan.3rd@gmail.com"},{"name":"Raff Viglianti","email":"raffaeleviglianti@gmai.com"},{"name":"Luciano Frizzera","email":"lucaju@gmail.com"}],"main":"lib/salve/validate.js","browser":"./salve.min.js","types":"lib/salve/validate.d.ts","repository":{"type":"git","url":"git+https://gitlab.com/calincs/cwrc/leaf-writer/salve.git"},"bugs":{"url":"https://gitlab.com/calincs/cwrc/leaf-writer/salve/-/issues"},"license":"MPL-2.0","engines":{"node":">=8","npm":">=6.14.7"},"dependencies":{"@trust/webcrypto":"git+https://github.com/lddubeau/webcrypto.git#fix/pass-string-to-digest","@types/xregexp":"^4.3.0","argparse":">=1 <2","file-url":"^3.0.0","node-fetch":"^2.6.1","require-dir":"^1.2.0","saxes":"^4.0.2","temp":"^0.9.4","tslib":"^1.14.1","xregexp":"^4.4.1"},"devDependencies":{"@commitlint/cli":"^8.3.5","@commitlint/config-angular":"^8.3.4","@types/argparse":"^1.0.38","@types/chai":"^4.2.18","@types/file-url":"^2.0.0","@types/mocha":"^5.2.7","@types/node":"^10.17.60","@types/node-fetch":"^2.5.10","@types/require-dir":"^1.0.1","@types/temp":"^0.8.34","bluebird":"^3.7.2","chai":"^4.3.4","child-process-promise":"^2.2.1","commitizen":"^4.2.4","conventional-changelog-cli":"^2.1.1","del":"^6.0.0","eslint":"^6.8.0","eslint-config-lddubeau-base":"^4.0.2","fancy-log":"^2.0.0","git-cz":"^4.7.6","gulp":"^4.0.2","gulp-eslint":"^6.0.0","gulp-newer":"^1.4.0","husky":"^3.1.0","karma":"^4.4.1","karma-browserstack-launcher":"^1.6.0","karma-chai":"^0.1.0","karma-chrome-launcher":"^3.1.0","karma-firefox-launcher":"^1.3.0","karma-mocha":"^1.3.0","karma-mocha-reporter":"^2.2.5","karma-source-map-support":"^1.4.0","merge-options":"^2.0.0","mocha":"^6.2.3","renovate-config-lddubeau":"^1.0.0","source-map-support":"^0.5.19","terser-webpack-plugin":"^2.3.8","touch":"^3.1.0","ts-node":"^8.10.2","tslint":"^5.20.1","tslint-config-lddubeau":"^4.1.2","typedoc":"^0.17.8","typescript":"^3.9.9","versync":"^5.0.0","webpack":"^4.46.0","webpack-cli":"^3.3.12"},"bin":{"salve-convert":"bin/salve-convert"},"scripts":{"build":"gulp","test":"gulp test","install_test":"gulp install_test","preversion":"npm test && npm run install_test && git flow release start $npm_package_version","version":"versync -b sync -a && conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md","cm":"git-cz","salve:publish":"cd build/dist && npm publish"},"gitHead":"985bf30e634a2358a1401a46c893315560ea33d4","_id":"@cwrc/salve-leafwriter@1.2.2","_nodeVersion":"16.14.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-hCFT/By5UmLPpuoOdFyRr4sgaC74fKm0EyR9OWoY572KGBmjkaxBJK5gaQcwohWtT0UHeaZz0APIKvJpnDsP/Q==","shasum":"2e7c50e0c184c8055c90de75736f73db729328a8","tarball":"https://registry.npmjs.org/@cwrc/salve-leafwriter/-/salve-leafwriter-1.2.2.tgz","fileCount":236,"unpackedSize":2715428,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDM9DS3gC0jzhGXVrn/8UcclpS6SRzARYGM7Fiv1OtsmQIhAMXdX4oyCJx8oRP+Rzs5wbPjj9bNLmlTCAAWd4Di5sZG"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRiRBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqE5w/9EPPfr0/Cgg5Lnc/ophuLUEMSM46977LSkLTE5k0vUdBQN9kh\r\nWnjaiEthCvrxGe4AbzZZ1wUGmL6QQxpuLNaLpmEzA+U09PY0C4NwbG5i3Ovv\r\nLbI8LGnCwIF6dOL6ZVSRbRuWRbxlnnSRpggvQ06py4jYQPAuQok1ixj9Qir2\r\n1Gg683eesa/rU4achPwLmH1z2EbptYX5oqdQ8Eczzgwso/FOtVK/FonlxXYn\r\nY62TlQq6UYRa7lTeZ8g2hH+Bt77I3/Geq2PmCNDLjk/T53Q7uUnxCl3HN/3l\r\nvcIqbd2/zXAg4U+7bLtzcGYUQJMfvtFz5PTdyV6lIwI83aqH6lWRtYoiOLk2\r\nJJSj7X3tnsNK0KeBU66W4334RkqWjXPFXKyIEkmceWUDKfERs3+6rIEq8slJ\r\n5yfka4fDSKZBWZcPbxDSxZPh+diZfqasA4d/hZuEjHqrJJ86DBDYY9nxhNdu\r\nDnjh44KLv27koVyjsdRR8Vn6ekLARfxCRGpbH1o21s5Ev5ZCRHqSQyLt0zcp\r\nhkXmFlaJ+Q2rYaoFKteOZgYueq+pnarzBX9NsDZpkqqFCWZzCxNOyi7iuCdG\r\nTTnoX2nrrei7Tk6OQdkTFPHZKC/Gsm3MVnuuIm+FQ3Li6uiYzV1LVdU7lPB5\r\nHls/fch/TjDPIVp2mYQrONW/WOuZbuh23oU=\r\n=wC1h\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"lucaju","email":"lucaju@gmail.com"},"directories":{},"maintainers":[{"name":"canadian-writing-research-collaboratory","email":"cwrc@ualberta.ca"},{"name":"lucaju","email":"lucaju@gmail.com"},{"name":"ajmacdonald","email":"andrewjames.code@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/salve-leafwriter_1.2.2_1648763969747_0.6116092363243326"},"_hasShrinkwrap":false}},"time":{"created":"2022-03-31T15:49:10.588Z","1.2.1":"2022-03-31T15:49:10.948Z","modified":"2022-03-31T21:59:30.079Z","1.2.2":"2022-03-31T21:59:29.951Z"},"maintainers":[{"name":"canadian-writing-research-collaboratory","email":"cwrc@ualberta.ca"},{"name":"lucaju","email":"lucaju@gmail.com"},{"name":"ajmacdonald","email":"andrewjames.code@gmail.com"}],"description":"A fork with support for documentation of Salve, a Javascript library which implements a validator able to validate an XML document on the basis of a subset of RelaxNG.","homepage":"https://github.com/mangalam-research/salve","keywords":["RelaxNG","Relax NG","rng","XML","validation"],"repository":{"type":"git","url":"git+https://gitlab.com/calincs/cwrc/leaf-writer/salve.git"},"contributors":[{"name":"Jesse Bethel","email":"keisetsu@gmail.com"},{"name":"Bennett Buchanan","email":"buchanan.3rd@gmail.com"},{"name":"Raff Viglianti","email":"raffaeleviglianti@gmai.com"},{"name":"Luciano Frizzera","email":"lucaju@gmail.com"}],"author":{"name":"Louis-Dominique Dubeau","email":"louisd@mangalamresearch.org"},"bugs":{"url":"https://gitlab.com/calincs/cwrc/leaf-writer/salve/-/issues"},"license":"MPL-2.0","readme":"[![Package bagge](https://badge.fury.io/js/salve.svg)](https://badge.fury.io/js/salve)\n[![License badge](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)\n[![Travis badge](https://travis-ci.org/mangalam-research/salve.svg?branch=master)](https://travis-ci.org/mangalam-research/salve)\n[![Greenkeeper badge](https://badges.greenkeeper.io/mangalam-research/salve.svg)](https://greenkeeper.io/)\n\nIntroduction\n============\n\nSalve (Schema-Aware Library for Validation and Edition) is a TypeScript library\nwhich implements a validator able to validate an XML document on the basis of a\nsubset of Relax NG (RNG). It is developed as part of the Buddhist Translators\nWorkbench. It can be seen in action in [wed](https://github.com/mangalam-research/wed).\n\nSalve is used for validating XML with custom Relax NG schemas. We've also\nvalidated files that use the [TEI standard](http://www.tei-c.org/) and the\nDocBook v5.0 schema. We want to support as much Relax NG as reasonably possible,\nbut salve currently has the following limitations:\n\n* XML Schema types ``ENTITY`` and ``ENTITIES`` are treated as a ``string``.\n\n* None of the XML Schema types that deal with time allow the\n  parameters ``minInclusive``, ``minExclusive``, ``maxInclusive`` and\n  ``maxExclusive``.\n\n* Salve does not verify that numerical values validated as ``float`` or\n  ``double`` fit within the limits of ``float`` or ``double``. (This is a common\n  limitation of validators. We tested with ``jing`` and ``xmllint --relaxng``\n  and found they do not raise errors if, for instance, a validation that expects\n  a float is given a value that cannot be represented with a float.)\n\nIf someone wishes to use salve but needs support for any of the features that\nare missing, they may ask for the feature to be added. Submit an issue on GitHub\nfor it. If you do submit an issue to add a feature, please make a case for\nit. Even better, if someone wishes for a feature to be added, they can\ncontribute code to salve that will add the feature they want. A solid\ncontribution is more likely to result in the feature being speedily added to\nsalve than asking for us to add the feature, and waiting until we have time for\nit.\n\nA full validation solution has the following components:\n\n* A tokenizer: responsible for recognizing XML tokens, tag names, tag\n  delimiters, attribute names, attribute values, etc.\n\n* A parser: responsible for converting tokens to validation events (see below).\n\n* A well-formedness checker. Please check the [Events](#events) section for more\n  information about what this concretely means.\n\n* A validator: responsible for checking that validation events are valid against\n  a schema, telling the parser what is possible at the current point in\n  validation, and telling the parser what is possible in general (e.g., what\n  namespace uris are used in the schema). **This is what salve offers, and only\n  this!**\n\nA good example of this division of labor can be found in ``bin/parse.js`` and in\nthe test suite. In both cases the tokenizer function is performed by ``saxes``,\nand the parser function is performed by a parser object that ``saxes`` creates,\ncustomized to call salve's ``Walker.fireEvent()``.\n\nSalve has a sister library named\n[salve-dom](https://github.com/mangalam-research/salve-dom) which uses the\nparsing facilities available in a browser to provide the tokenizer,\nwell-formedness and parser components described above.\n\nNOTE: If you are looking at the source tree of salve as cloned from GitHub, know\nthat executables cannot be executed from ``bin``. They can be executed after a\nbuild, from the ``build/dist/bin`` directory.\n\nIf you are looking at the files installed by ``npm`` when you install salve as a\n*package*, the files in ``bin`` *are* those you want to execute.\n\nBasic Usage\n===========\n\nA typical usage scenario would be as follows:\n\n    // Import the validation module\n    const salve = require(\"salve-leafwriter\");\n\n    // Source is a URL to the Relax NG schema to use. A ``file://`` URL\n    // may be used to load from the local fs.\n    const grammar = salve.convertRNGToPattern(source).pattern;\n\n    // Get a walker on which to fire events.\n    const walker = grammar.newWalker();\n\nThen the code that parses the XML file to be validated should call\n``fireEvent()`` on ``walker``. Remember to call the ``end()`` method on your\nwalker at the end of validation to make sure that there are no unclosed tags,\netc.\n\nThe file ``bin/parse.js`` (included in salve's source but not in the npm module)\ncontains an example of a rudimentary parser runnable in Node.js::\n\n    $ node ....../parse.js [rng] [xml to validate]\n\nThe ``[rng]`` parameter is the Relax NG schema, recorded in the full XML format\nused by Relax NG (not the compact form). The ``[xml to validate]`` parameter is\nthe XML file to validate against the schema.\n\nConverting Schemas\n==================\n\nAs you can see above, a Relax NG schema, stored in XML, needs to be converted to\nsalve's internal format before salve can use it. Internally this happens:\n\n* The XML file recording the Relax NG schema is converted to a tree of objects\n  representing the XML.\n\n* The XML tree is validated against the Relax NG schema.\n\n* The XML tree is simplified as described in the Relax NG specification.\n\n* Constraints specified by the Relax NG specification are checked.\n\n* The XML tree is converted to a \"pattern\", which is a structure internal to\n  salve.\n\nThe simplest usage is like this::\n\n    const result = salve.convertRNGToPattern(source);\n\nBy default the conversion returns an object with a grammar stored on the\n``pattern`` field, may reveal some simplification warnings on the ``warnings``\nfield, and provides a the simplified schema as an XML tree on the ``simplified``\nfield. In trivial use-case scenarios, only ``pattern`` and ``warnings`` are\nused.\n\nIn some cases, the code using ``convertRNGToPattern`` may want to serialize the\nresult of simplification for future use. To do this, it should use\n``writeTreeToJSON`` and pass the value of the ``simplified`` field to serialize\nthe simplified XML tree to JSON. The serialized JSON may then be read with\n``readTreeFromJSON`` to create a structure identical to the original\n``pattern``. Consider the following code::\n\n    const result = salve.convertRNGToPattern(source);\n    const json = salve.writeTreeToJSON(result.simplified);\n    const x = salve.readTreeFromJSON(json);\n\nAfter executing it, ``x`` contains a pattern which represents the same Relax NG\nschema as ``result.pattern``.\n\nDo note that that ``writeTreeToJSON`` takes **an XML tree** and produces JSON,\nwhereas ``readTreeFromJSON`` reads a JSON and produces **a pattern** rather than\nan XML tree. There is currently no use-case scenario that requires the two\nfunctions to mirror one-another.\n\nOptionally, you may pass an options object as the 2nd argument of\n``convertRNGToPattern`` with ``createManifest`` set to ``true``. If you do this,\nthen you also get a ``manifest`` field which is an array of objects containing\nfile paths and their corresponding hashes. Manifests are useful to allow systems\nthat use salve to know whether a pattern needs to be regenerated with\n``convertRNGToPattern``. This is necessary if the system allows a schema to\nchange after use. Consider the following scenario. Alice uses an XML editor that\nuses salve to perform on the fly validation.\n\n1. Alice edits the file ``foo.xml`` with the schema ``foo.rng``. It so happens\n   that ``foo.rng`` imports ``math.rng``. The editor will use\n   ``convertRNGToPattern`` to convert ``foo.rng`` and ``math.rng`` into the\n   format salve needs. It also uses ``writeTreeToJSON`` to cache the\n   result. Alice sees a brief progress indicator while the editor converts the\n   schema.\n\n2. Over the span of a week, Alice continues editing ``foo.xml``. Each time she\n   opens the editor, the editor uses ``readTreeFromJSON`` to load the tree from\n   cache instead of using ``convertRNGToPattern`` over and over. As far as Alice\n   is concerned, the editor starts immediately. There's no progress indicator\n   needed because ``readTreeFromJSON`` is super fast.\n\n3. Alice then changes ``math.rng`` to add new elements. When Alice starts the\n   XML editor to edit ``foo.xml`` again, the XML editor must be able to detect\n   that the schema needs to go through ``convertRNGToPattern`` *again* because\n   ``math.rng`` has changed.\n\nThe manifest is used to support the scenario above. If the XML editor stores the\nconverted schema, and the manifest into its cache, then it can detect if and\nwhen it needs to convert the schema anew.\n\nSecurity note: It is up to you to decide what strength hash you need. **The\nmanifest is not designed for the sake of providing security.** So its hashes are\nnot designed to detect willful tampering but rather to quickly determine whether\na schema was edited. In the vast majority of real world usage scenarios, using a\nstronger hash would not provide better security because if an attacker can\nreplace a schema with their own file, they also can access the manifest and\nreplace the hash in the manifest.\n\nEvents\n======\n\nSalve expects that the events it receives are those that would be emitted when\nvalidating a **well-formed document**. That is, passing to salve the events\nemitted from a document that is malformed will cause salve to behave in an\nundefined manner. (It may crash. It may generate misleading errors. It may not\nreport any errors.) This situation is due to the fact that salve is currently\ndeveloped in a context where the documents it validates cannot be malformed\n(because they are represented as DOM trees). So salve contains no functionality\nto handle problems with well-formedness. Salve **can be used on malformed\ndocuments**, provided you take care of reporting malformedness issues yourself\nand strategize how you will pass events to salve.\n\nMultiple strategies are possible for using salve in a context where\nwell-formedness is not guaranteed. There is no one-size-fits-all solution\nhere. A primitive parser could abort as soon as evidence surfaces that the\ndocument is malformed. A more sophisticated parser could process the problematic\nstructure so as to generate an error but give salve something well-formed. For\ninstance if parsing ``<foo></baz>``, such parser could emit an error on\nencountering ``</baz>`` and replace the event that would be emitted for\n``</baz>`` with the event that would be emitted for ``</foo>``, and salve will\nhappily validate it. The user will still get the error produced by the parser,\nand the parser will still be able to continue validating the document with\nsalve.\n\nThe parser is responsible for calling ``fireEvent()`` on the walker returned by\nthe tree created from the RNG. (See above.) The events currently supported by\n``fireEvent()`` are defined below:\n\n``\"enterStartTag\", [uri, local-name]``\n  Emitted when encountering the beginning of a start tag (the string \"<tag\",\n  where \"tag\" is the applicable tag name) or the equivalent. The qualified\n  name should be resolved to its uri and local-name components.\n\n``\"leaveStartTag\", []``\n  Emitted when encountering the end of a start tag (the string \">\") or\n  equivalent.\n\n``\"endTag\", [uri, local-name]``\n  Emitted when encountering an end tag.\n\n``\"attributeName\", [uri, local-name]``\n  Emitted when encountering an attribute name.\n\n``\"attributeValue\", [value]``\n  Emitted when encountering an attribute value\n\n``\"text\", [value]``\n  Emitted when encountering text. This event must be fired for all instances\n  of text, **including white space.** Moreover, salve requires that you fire\n  one ``text`` event per consecutive sequence of text. For instance, if you\n  have the text ``foo bar`` you may not fire one event for ``foo `` and\n  another for ``bar``. Or if you have a sequence of lines, you may not fire one\n  event per line. You have to concatenate the lines and fire a single ``text``\n  event.\n\n  Do not generate ``text`` events with an empty string as the\n  value. (Conversely, a valid document **must** have an ``attributeValue`` for\n  all attributes, even those that have empty text as a value.)\n\nSalve support a couple of compact events that serve to pass as one event data\nthat would normally be passed as multiple events:\n\n``\"attributeNameAndValue\", [uri, local-name, value]``\n  Combines the ``attributeName`` and ``attributeValue`` events into one event.\n\n``\"startTagAndAttributes\", [uri, local-name, [attribute-data...]]``\n  Combines the ``enterStartTag``, ``attributeNameAndValue`` and\n  ``leaveStartTag`` events. The ``attribute-data`` part of the event must be a\n  sequence of ``uri, local-name, value`` as would be passed to with\n  ``attributeNameAndValue``.\n\n  For instance if an element named ``foo`` has the attribute ``a`` with the\n  value ``valA``, the event would be: ``\"startTagAndAttributes\", \"\", foo,\n  \"\", \"a\", \"valA\"``.\n\n.. note:: The compact events do not allow salve to be very precise with\n          reporting errors. It is recommended to use them only when optimizing\n          for speed, at the expense of precision.\n\n.. note:: When reporting possible events, salve *never* returns compact events\n          in the list.\n\nThe reason for the set of events supported is that salve is designed to handle\n**not only** XML modeled as a DOM tree but also XML parsed as a text string\nbeing dynamically edited. The best and closest example of this would be what\n``nxml-mode`` does in Emacs. If the user starts a new document and types only\nthe following into their editing buffer::\n\n    <html\n\nthen what the parser has seen by the time it gets to the end of the buffer is an\n``enterStartTag`` event with an empty uri and the local-name \"html\". The parser\nwill not see a ``leaveStartTag`` event until the user enters the greater-than\nsymbol ending the start tag.\n\nYou must call ``enterContext()`` or ``enterContextWithMapping`` each time you\nencounter a start tag that defines namespaces and call ``leaveContext()`` when\nyou encounter its corresponding end tag. You must also call\n``definePrefix(...)`` for each prefix defined by the element. Example::\n\n    <p xmlns=\"q\" xmlns:foo=\"foons\">...\n\nwould require calling::\n\n    enterContext()\n    definePrefix(\"\", \"q\")\n    definePrefix(\"foo\", \"foons\")\n\nPresumably, after the above, your code would call ``resolveName(\"p\")`` on your\nwalker to determine what namespace ``p`` is in, which would yield the result\n``\"q\"``. And then it would fire the ``enterStartTag`` event with ``q`` as the\nnamespace and ``p`` as the local name of the tag::\n\n    \"enterStartTag\", [\"q\", \"p\"]\n\nNote the order of the events. The new context must start before salve sees the\n``enterStartTag`` event because the way namespaces work, a start tag can declare\nits own namespace. So by the time ``enterStartTag`` is issued, salve must know\nwhat namespaces are declared by the tag. If the events were not issued this way,\nthen the start tag ``p`` in the example would be interpreted to be in the\ndefault namespace in effect **before** it started, which could be other than\n``q``. Similarly, ``leaveContext`` must be issued after the corresponding\n``endTag`` event.\n\n**Note on performance:** if you already have a simple JavaScript object that\nmaps prefixes to URIs it is better to call ``enterContextWithMapping`` and pass\nyour object to this method. ``enterContextWithMapping`` enters a new context and\nimmediately initializes it with the mapping you pass. This is faster than\ncalling ``enterContext`` and calling ``definePrefix`` a bunch of times.\n\nFor the lazy: it is possible to call ``enterContext()`` for each start tag and\n``leaveContext()`` for each end tag irrespective of whether or not the start tag\ndeclares new namespaces. The test suite does it this way.  Note, however, that\nperformance will be affected somewhat because name resolution will have to\npotentially search a deeper stack of contexts than would be strictly necessary.\n\nSupport for Guided Editing\n==========================\n\nCalling the ``possible()`` method on a walker will return the list of valid\n``Event`` objects that could be fired on the walker, given what the walker has\nseen so far.  If the user is editing a document which contains only the text::\n\n    <html\n\nand hits a function key which makes the editor call ``possible()``, then the\neditor can tell the user what attributes would be possible to add to this\nelement. In editing facilities like ``nxml-mode`` in Emacs this is called\ncompletion. Similarly, once the start tag is ended by adding the greater-than\nsymbol::\n\n   <html>\n\nand the user again asks for possibilities, calling ``possible()`` will return\nthe list of ``Event`` objects that could be fired. Note here that it is the\nresponsibility of the editor to translate what salve returns into something the\nuser can use. The ``possible()`` function returns only ``Event`` objects.\n\nEditors that would depend on salve for guided editing would most likely need to\nuse the ``clone()`` method on the walker to record the state of parsing at\nstrategic points in the document being edited. This is to avoid needless\nreparsing. How frequently this should happen depends on the structure of the\neditor. The ``clone()`` method and the code it depends on has been optimized\nsince early versions of salve, but it is possible to call it too often,\nresulting in a slower validation speed than could be attained with less\naggressive cloning.\n\nOverbroad Possibilities\n-----------------------\n\n``possible()`` may at times report possibilities that allow for a document\nstructure that is ultimately invalid. This could happen, for instance, where the\nRelax NG schema uses ``data`` to specify that the document should contain a\n``positiveInteger`` between 1 and 10. The ``possible()`` method will report that\na string matching the regular expression ``/^\\+?\\d+$/`` is possible, when in\nfact the number ``11`` would match the expression but be invalid. The software\nthat uses salve should be prepared to handle such a situation.\n\nName Classes\n------------\n\n.. note:: The symbol ``ns`` used in this section corresponds to ``uri``\n          elsewhere in this document and ``name`` corresponds to ``local-name``\n          elsewhere. We find the ``uri``, ``local-name`` pair to be clearer than\n          ``ns``, ``name``. Is ``ns`` meant to be a namespace prefix? A URI? Is\n          ``name`` a qualified name, a local name, something else? So for the\n          purpose of documentation, we use ``uri``, ``local-name`` wherever we\n          can. However, the Relax NG specification uses the ``ns``, ``name``\n          nomenclature, which salve also follows internally. The name class\n          support is designed to be a close representation of what is described\n          in the Relax NG specification. Hence the choice of nomenclature in\n          this section.\n\nThe term \"name class\" is defined in the Relax NG specification, please refer to\nthe specification for details.\n\nSupport for Relax NG's name classes introduces a few peculiarities in how\npossibilities are reported to clients using salve. The three events that accept\nnames are affected: ``enterStartTag``, ``endTag``, and ``attributeName``. When\nsalve returns these events as possibilities, their lone parameter is an instance\nof ``name_patterns.Base`` class. This object has a ``.match`` method that takes\na namespace and a name and will return ``true`` if the namespace and name match\nthe pattern, or ``false`` if not.\n\nClient code that wants to provide a sophisticated analysis of what a name class\ndoes could use the ``.toObject()`` method to get a plain JavaScript object from\nsuch an object. The returned object is essentially a syntax tree representing\nthe name class. Each pattern has a unique structure. The possible patterns are:\n\n* ``Name``, a pattern with fields ``ns`` and ``name`` which respectively record\n  the namespace URL and local name that this object matches. (Corresponds to the\n  ``<name>`` element in the simplified Relax NG syntax.)\n\n* ``NameChoice``, a pattern with fields ``a`` and ``b`` which are two name\n  classes. (Corresponds to a ``<choice>`` element appearing inside a name class\n  in the simplified Relax NG syntax.)\n\n* ``NsName``, a pattern with the field ``ns`` which is the namespace that this\n  object would match. The object matches any name. It may have an optional\n  ``except`` field that contains a name class for patterns that it should not\n  match. The lack of ``name`` field distinguishes it from ``Name``.\n  (Corresponds to an ``<nsName>`` element in the simplified Relax NG syntax.)\n\n* ``AnyName``, a pattern. It has the ``pattern`` field set to ``AnyName``. We\n  use this ``pattern`` field because ``AnyName`` does not require any other\n  fields so ``{}`` would be its representation. This representation would too\n  easily mask possible coding errors. ``AnyName`` matches any combination of\n  namespace and name. May have an optional ``except`` field that contains a name\n  class for patterns it should not match. It corresponds to an ``<anyName>``\n  element in the simplified Relax NG syntax.\n\n.. note:: We do not use the ``pattern`` field for all patterns above because the\n          only reason to do so would be to distinguish ambiguous structures. For\n          instance, if Relax NG were to introduce a ``<superName>`` element that\n          also needs ``ns`` and ``name`` fields then it would look the same as\n          ``<name>`` and we would not be able to distinguish one from the\n          other. However, Relax NG is stable. In the unlikely event a new\n          version of Relax NG is released, we'll cross whatever bridge needs to\n          be crossed.\n\nNote that the ``<except>`` element from Relax NG does not have a corresponding\nobject because the presence of ``<except>`` in a name class is recorded in the\n``except`` field of the patterns above.\n\nHere are a couple of examples. The name class for::\n\n    element (foo | bar | foo:foo) { ... }\n\nwould be recorded as (after partial beautification)::\n\n    {\n        a: {\n            a: {ns: \"\", name: \"foo\"},\n            b: {ns: \"\", name: \"bar\"}\n        },\n        b: {ns: \"foo:foo\", name: \"foo\"}\n    }\n\nThe name class for::\n\n    element * - (foo:* - foo:a) { ... }\n\nwould be recorded as (after partial beautification)::\n\n    {\n        pattern: \"AnyName\",\n        except: {\n            ns: \"foo:foo\",\n            except: {ns: \"foo:foo\", name: \"a\"}\n        }\n    }\n\nClients may want to call the ``.simple()`` method on a name pattern to determine\nwhether it is simple or not. A pattern is deemed \"simple\" if it is composed only\nof ``Name`` and ``NameChoice`` objects. Such a pattern could be presented to a\nuser as a finite list of possibilities. Otherwise, if the pattern is not simple,\nthen either the number of choices is unbounded or it not a discrete list of\nitems. In such a case, the client code may instead present to the user a field\nin which to enter the name of the element or attribute to be created and\nvalidate the name against the pattern. The method ``.toArray()`` can be used to\nreduce a pattern which is simple to an array of ``Name`` objects.\n\nEvent Asymmetry\n---------------\n\n**Note that the events returned by ``possible()`` are *not identical* to the\nevents that ``fireEvent()`` expects.** While most events returned are exactly\nthose that would be passed to ``fireEvent()``, there are three exceptions: the\n``enterStartTag``, ``endTag`` and ``attributeName`` events returned by\n``possible()`` will have a single parameter after the event name which is an\nobject of ``name_patterns.Base`` class. However, when passing a corresponding\nevent to ``fireEvent()``, the same events take two string parameters after the\nevent name: a namespace URL and a local name. To spell it out, they are of this\nform::\n\n    event_name, [uri, local-name]\n\nwhere ``event_name`` is the string which is the name of the event to fire,\n``uri`` is the namespace URI and ``local-name`` is the local name of the element\nor attribute.\n\nError Messages\n--------------\n\nError messages that report attribute or element names use the\n``name_patterns.Name`` class to record names, even in cases where\n``patterns.EName`` would do. This is for consistency purposes, because some\nerror messages **must** use ``name_patterns`` objects to report their\nerrors. Rather than have some error messages use ``EName`` and some use the\nobject in ``name_patterns`` they all use the objects in ``name_patterns``, with\nthe simple cases using ``name_patterns.Name``.\n\nIn most cases, in order to present the end user of your application with error\nmessages that make sense *to the user*, you will need to process error\nmessages. This is because error messages generated by salve provide in the error\nobject ``(ns, local name)`` pairs. A user would most likely like to see a\nnamespace prefix rather than URI (``ns``). However, since namespace prefixes are\na matter of user preference, and there may be many ways to decide how to\nassociate a namespace prefix with a URI, salve does not take a position in this\nmatter and lets the application that uses it decide how it wants to present URIs\nto users. The application also has to determine what strategy to use to present\ncomplex (i.e., non-simple) name patterns to the user. Again, there is no\none-size-fits-all solution.\n\nMisplaced Elements\n==================\n\nA problem occurs when validating an XML document that contains an unexpected\nelement. In such case, salve will issue an error but then what should it do with\nthe contents of the misplaced element? Salve handles this in two ways:\n\n1. If the unexpected element is known in the schema and has only one definition,\n   then salve will assume that the user meant to use the element defined in the\n   schema and will validate it as such.\n\n2. Otherwise, salve will turn off validation until the element is closed.\n\nConsider the following case::\n\n    <p>Here we have a <name><first>John</first><last>Doe</last></name>\n    because the <emph>person's name</emph> is not known.</p>\n\nIf ``name`` cannot appear in ``p`` but ``name`` has only one definition in the\nschema, then salve will emit an error upon encountering the ``enterStartTag``\nevent for ``name``, and then validate ``name`` as if it had been found in a\nvalid place. If it turns out that the schema defines one ``name`` element which\ncan appear inside a ``person`` element and another ``name`` element which can\nappear inside a ``location`` element (which would be possible with Relax NG),\nthen salve will emit an error but won't perform any validation inside\n``name``. Validation will resume after the ``endTag`` event for\n``name``. (Future versions of salve may implement logic to figure out ambiguous\ncases such as this one.) This latter scenario also occurs if ``name`` is not\ndefined at all by the schema.\n\nDocumentation\n=============\n\nThe code is documented using ``typedoc``. The following command will generate\nthe documentation::\n\n    $ gulp doc\n\nYou may need to create a ``gulp.local`` module to tell ``gulp`` where to get\n``rst2html``. (Defaults are such that ``gulp`` will use your ``PATH`` to locate\nsuch tools.) The formatted documentation will appear in the ``build/api/``\nsubdirectory, and the ``README.html`` in the root of the source tree.\n\n**NOTE**: All the public interfaces of salve are available through the\n``validate`` module. However, ``validate`` is a facade that exposes interfaces\nthat are implemented in separate modules like ``patterns`` and ``formats``.\n\nDependencies\n============\n\nIn Node\n-------\n\nWhenever you call on salve's functionalities to read a Relax NG schema, the\n``fetch`` function must be available in the global space for salve to use. On\nNode, this means you must load a polyfill to provide this function.\n\nRunning salve's tests **additionally** requires that the development\ndependencies be installed. Please see the ``package.json`` file for details\nregarding these dependencies. Note that ``gulp`` should be installed so that its\nexecutable is in your path.  Either this, or you will have to execute\n``./node_modules/.bin/gulp``\n\nIf you want to contribute to salve, your code will have to pass the checks\nlisted in ``.glerbl/repo_conf.py``. So you either have to install glerbl to get\nthose checks done for you or run the checks through other means. See\n[Contributing](#contributing).\n\nIn The Browser\n--------------\n\nThe following lists the most prominent cases. It is not practical for us to keep\ntrack of every single feature that old browsers like IE11 don't support.\n\n* ``fetch`` must be present.\n\n* ``Promise`` must be present.\n\n* ``Object.assign`` must be present.\n\n* ``URL`` must be present.\n\n* ``Symbol`` [and ``Symbol.iterator``] must be present.\n\n* The String methods introduced by ES6 (``includes``, ``endsWith``, etc.)\n\n* ``Array.prototype.includes``\n\n* Old ``Set`` and ``Map`` implementations like those in IE11 are either broken\n  or incomplete.\n\nOn old browsers, we recommend using ``core-js`` to take care of many of these in\none fell swoop. You'll have to provide polyfills for ``fetch`` and ``URL`` from\nother sources.\n\nNote that we do not support old browsers. Notably, salve won't run on any\nversion of IE.\n\nBuild System\n============\n\nSalve uses gulp. Salve's build setup gets the values for its configuration\nvariables from three sources:\n\n* Internal default values.\n\n* From an optional ``gulp.local.js`` module that can override the\n  internal defaults.\n\n* From command line options that can override everything above.\n\nThe variables that can be set are:\n\n+-----------------------+------------------------------------------------------+\n|Name                   | Meaning                                              |\n+=======================+======================================================+\n|``doc_private``        | Whether to produce documentation for private         |\n|                       | entities. You can set ``doc_private`` to ``false``   |\n|                       | using ``no_doc_private``.                            |\n+-----------------------+------------------------------------------------------+\n|``mocha_grep``         | ``--grep`` parameter for Mocha                       |\n+-----------------------+------------------------------------------------------+\n|``rst2html``           | ``rst2html`` command to run                          |\n+-----------------------+------------------------------------------------------+\n\nNote that when used on the command line, underscores become dashes, thus\n``--mocha-grep`` and ``--doc-private``.\n\nThe ``gulp.local.js`` file is a module. You must export values\nlike this::\n\n    exports.doc_private = true\n\nBuilding\n========\n\nRun::\n\n    $ gulp\n\nThis will create a ``build/dist/`` subdirectory in which the JavaScript\nnecessary to validate XML files against a prepared Relax NG schema. You could\ncopy what is in ``build/dist>`` to a server to serve these files to a client\nthat would then perform validation.\n\nDeploying\n=========\n\nWhen you install salve through `npm`, you get a package that contains:\n\n* a hierarchy of CommonJS modules in `lib`,\n* a minified UMD build as `salve.min.js`.\n\nThe UMD build can be loaded in a CommonJS environment, in a AMD environment or\nas \"plain scripts\" in a browser. If you use the latter, then salve will be\naccessible as the `salve` global.\n\nTesting\n=======\n\nRunning the following command from the root of salve will run the tests::\n\n    $ gulp test\n\nRunning ``mocha`` directly also works, but this may run the test against stale\ncode, whereas ``gulp test`` always runs a build first.\n\nContributing\n============\n\nContributions must pass the commit checks turned on in\n``glerbl/repo_conf.py``. Use ``glerbl install`` to install the hooks. Glerbl\nitself can be found at https://github.com/lddubeau/glerbl. It will eventually\nmake its way to the Python package repository so that ``pip install glerbl``\nwill work.\n\nSchema File Format\n==================\n\n``writeTreeToJSON`` converts a Relax NG file formatted in XML into a more\ncompact format used by salve at validation time. Salve supports version 3 of\nthis file format. Versions 0 to 2 are now obsolete. The structure is::\n\n    {\"v\":<version>,\"o\":<options>,\"d\":[...]}\n\nThe ``v`` field gives the version number of the data. The ``o`` field is a bit\nfield of options indicating how the file was created. Right now the only thing\nit records is whether or not element paths are present in the generated\nfile. The ``d`` field contains the actual schema. Each item in it is of the\nform::\n\n   [<array type>, ...]\n\nThe first element, ``<array type>``, determines how to interpret the array. The\narray type could indicate that the array should be interpreted as an actual\narray or that it should be interpreted as an object of type ``Group`` or\n``Choice``, etc. If it is an array, then ``<array type>`` is discarded and the\nrest of the array is the converted array. If it is another type of object then\nagain the ``<array type>`` is discarded and an object is created with the rest\nof the array as its constructor's parameters. All the array's elements after\n``<array type>`` can be JSON primitive types, or arrays to be interpreted as\nactual arrays or as objects as described above.\n\nLicense\n=======\n\nOriginal Code\n-------------\n\nCode completely original to salve is released under the [Mozilla Public License\nversion 2.0](http://www.mozilla.org/MPL/2.0/). Copyright 2013-2016 Mangalam\nResearch Center for Buddhist Languages, Berkeley, CA.\n\nRNG Simplification Code\n-----------------------\n\nThe RNG simplification files coded in XSL were adapted from [Nicolas Debeissat's\ncode](https://github.com/ndebeiss/jsrelaxngvalidator/commit/8d353c73880ff519b31193905638cc97a93d1fad). These\nfiles were originally released under the [CeCILL\nlicense](http://www.cecill.info/index.en.html). Nicolas in [March\n2016](https://github.com/ndebeiss/jsrelaxngvalidator/commit/f7336b2472baec60ab16571b865447e1146196ab)\nthen changed the license to the Apache License 2.0.\n\nIn the version of these files bundled with salve, multiple bugs have been\ncorrected, some minor and some major, and some changes have been made for\nsalve's own internal purposes. For the sake of simplicity, these changes are\nalso covered by the original licenses that apply to Nicolas' code.\n\nCredits\n=======\n\nSalve is designed and developed by Louis-Dominique Dubeau, Director of\nSoftware Development for the Buddhist Translators Workbench project,\nMangalam Research Center for Buddhist Languages.\n\nJesse Bethel has contributed to salve's documentation, and migrated salve's\nbuild system from Make to Grunt.\n\n[![Mangalam Research Logo](https://secure.gravatar.com/avatar/7fc4e7a64d9f789a90057e7737e39b2a)](http://www.mangalamresearch.org/)\n\nThis software has been made possible in part by a Level I Digital Humanities\nStart-up Grant and a Level II Digital Humanities Start-up Grant from the\nNational Endowment for the Humanities (grant numbers HD-51383-11 and\nHD-51772-13). Any views, findings, conclusions, or recommendations expressed in\nthis software do not necessarily represent those of the National Endowment for\nthe Humanities.\n\n[![NEH](http://www.neh.gov/files/neh_logo_horizontal_rgb.jpg)](http://www.neh.gov/)\n\n#  LocalWords:  fireEvent js chai semver json xmllint xsltproc npm\n#  LocalWords:  RNG minified rng XSLT xsl constructTree newWalker mk\n#  LocalWords:  xml enterStartTag uri leaveStartTag endTag nxml html\n#  LocalWords:  attributeName attributeValue Debeissat's API\n#  LocalWords:  CeCILL tokenizer Makefile README boolean anyName RST\n#  LocalWords:  nsName URIs uris enterContext leaveContext xmlns rst\n#  LocalWords:  definePrefix useNameResolver foons resolveName HD NG\n#  LocalWords:  args param TEI glerbl Github reStructuredText readme\n#  LocalWords:  validator namespace RequireJS subdirectory DOM cli\n#  LocalWords:  Dubeau Mangalam argparse Gruntfile Bethel unclosed\n#  LocalWords:  runnable namespaces reparsing amd executables usr lt\n#  LocalWords:  deployable schemas LocalWords api dir maxInclusive\n#  LocalWords:  minInclusive minExclusive maxExclusive cd abcd jing\n#  LocalWords:  github jison NaN baz emph lodash xregexp XRegExp ns\n#  LocalWords:  init positiveInteger NCName NameChoice superName\n#  LocalWords:  EName\n","readmeFilename":"README.md"}