{"_id":"@bonniernews/atlas-seq-matcher","_rev":"65-ae056c8280482c58cccff74df8f8cf2c","name":"@bonniernews/atlas-seq-matcher","dist-tags":{"latest":"1.0.4"},"versions":{"1.0.3":{"name":"@bonniernews/atlas-seq-matcher","version":"1.0.3","keywords":["atlassubbed","utils","utility","streams","stream","parser","sequence","matcher"],"author":{"name":"atlassubbed","email":"atlassubbed@gmail.com"},"license":"Apache-2.0","_id":"@bonniernews/atlas-seq-matcher@1.0.3","maintainers":[{"name":"linuss","email":"contact@linusskold.com"},{"name":"markusn","email":"markus@botten.org"},{"name":"joelabrahamsson","email":"mail@joelabrahamsson.com"},{"name":"paed01","email":"pal.edman@outlook.com"},{"name":"jonaswalden","email":"jonas.waldeen@gmail.com"},{"name":"indeedshouts","email":"indeedshouts@gmail.com"},{"name":"marcusgronblad","email":"marcus.gronblad@bonniernews.se"}],"homepage":"https://github.com/BonnierNews/atlas-seq-matcher#readme","bugs":{"url":"https://github.com/BonnierNews/atlas-seq-matcher/issues"},"dist":{"shasum":"27560f8a630b0f9eb7a133ff4b6c5063d093d2a4","tarball":"https://registry.npmjs.org/@bonniernews/atlas-seq-matcher/-/atlas-seq-matcher-1.0.3.tgz","fileCount":7,"integrity":"sha512-bcYMYhnl3qx0kyQi7X3c6+eD48vJYKQtr3dUq6h6IMQuJXwBw5EnDz20UWxWR2gQydKF/MYQD/F7i6VPnYSvGQ==","signatures":[{"sig":"MEUCIQDK5iycR5lfqvRNxmAgtTTSmKewj/FBVTaVsz9TgzXWGgIgJg3+iG/CM1vxp0IG/WBw4hzKE05OPchwQQuBbF2qbNs=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":9736,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJz4MCRA9TVsSAnZWagAA1iEQAJExGxiwhROeq4+0rdpJ\ndx47L81M03ElFIdOXh0QYFSBRt9FhXxuCALXg1MgYPliC677OEB4AtNtzVYY\nGVzK9Gc1mrq1L6GgQMvvz1idnELEBkzSwkT2rCPuUC9wdMA2kexCI9chD3Ab\nVXqeCIz5HfoEUnhtkCiQ0IeRlazH5M0jnWvlAk0UNi5wKM0e6bwTEUxpXu/+\nChfEBERy7nrun30mw3lb6B5kpAQLCwEZB/MByRIBfljc8+yWqnz3gbt1n01J\nzgXz2/QP8D7AsHvKfOJMQZ/5mYG4cLb0uky3UPkCTSgYRI+PvvGroLhsLPOO\ncrQ0igyj8zSrh89i+gQdVeGIDeFza3jc0wZRskgoUwZYIyT9kDXr6DpYWipS\nvv/SFNEFdxlDXXXCsDcvJdwkOWODlOzt3c0G3cQAsHCI2AmLX/EYBY4Yw9Us\nni8PvmJ7F8BvOsym8qplxw7alNXOZOaq66vSybvHxn/RktkcyRqGeRxrz+21\nO2EouSvx1K3VOa/NqtF+umtcMSvfH34ZF0IOghZs32i8hGdejKYNCiWo0/Yf\n+6ulfeolT1UMj0BcSiucK6QaiFX2hlOu0FNE7zB7EsqzdWOMokjdTaB5GmNN\ne58aRARPcBtT5jbTG92PrpFBw1KToFu9oMLFk4Q0mXhDYJ04dRMoWLkkrxVq\nex7Y\r\n=hqtD\r\n-----END PGP SIGNATURE-----\r\n"},"main":"./src/SeqMatcher.js","gitHead":"99e11a6d183ed4d88bee0f18aab64ec8cfd0b87e","scripts":{"test":"mocha --colors --recursive","test-server":"mocha --colors --watch --recursive './test/**/*.test.js'"},"_npmUser":{"name":"markusn","email":"markus@botten.org"},"repository":{"url":"git+https://github.com/BonnierNews/atlas-seq-matcher.git","type":"git"},"_npmVersion":"6.14.14","description":"Global substring matcher that accepts one character code at a time.","directories":{},"_nodeVersion":"12.22.5","_hasShrinkwrap":false,"devDependencies":{"chai":"^4.1.2","mocha":"^5.2.0"},"_npmOperationalInternal":{"tmp":"tmp/atlas-seq-matcher_1.0.3_1629961740378_0.39563743592888323","host":"s3://npm-registry-packages"}},"1.0.4":{"name":"@bonniernews/atlas-seq-matcher","version":"1.0.4","keywords":["atlassubbed","utils","utility","streams","stream","parser","sequence","matcher"],"author":{"name":"atlassubbed","email":"atlassubbed@gmail.com"},"license":"Apache-2.0","_id":"@bonniernews/atlas-seq-matcher@1.0.4","maintainers":[{"name":"mikael.mattsson","email":"mikael.mattsson@bonniernews.se"},{"name":"morre","email":"marten.persson@hdsydsvenskan.se"},{"name":"takolander","email":"william.takolander@bonniernews.se"},{"name":"vitryssen","email":"andre.nordlund@bonniernews.se"},{"name":"herbola","email":"herman.jansson@bonniernews.se"},{"name":"nifo","email":"niklas.forsstrom@bonniernews.se"},{"name":"bn-crehn","email":"christoffer.rehn@bonniernews.se"},{"name":"johark","email":"johan.arkad@bonniernews.se"},{"name":"aliceboberg","email":"alice.boberg@bonniernews.se"},{"name":"varneynz","email":"simon.varney@bonniernews.se"},{"name":"gusliden","email":"gusliden@gmail.com"},{"name":"mxtr","email":"max.olofsson@bonniernews.se"},{"name":"karlbergc","email":"christoffer.karlberg@bonniernews.se"},{"name":"martindanielson","email":"martin@rocketblast.com"},{"name":"joelekman","email":"joel.ekman@bonniernews.se"},{"name":"chavah","email":"chavah.forler@bonniernews.se"},{"name":"matsrorbecker","email":"mats@rorbecker.com"},{"name":"axeljohanssonbonniernews","email":"axel.johansson@bonniernews.se"},{"name":"andolf","email":"christian@weahead.se"},{"name":"markusn","email":"markus@botten.org"},{"name":"linuss","email":"contact@linusskold.com"},{"name":"joelabrahamsson","email":"mail@joelabrahamsson.com"},{"name":"jonaswalden","email":"jonas.waldeen@gmail.com"},{"name":"indeedshouts","email":"indeedshouts@gmail.com"},{"name":"marcusgronblad","email":"marcus.gronblad@bonniernews.se"}],"homepage":"https://github.com/BonnierNews/atlas-seq-matcher#readme","bugs":{"url":"https://github.com/BonnierNews/atlas-seq-matcher/issues"},"dist":{"shasum":"3755e56e2e11dd0b28adedf9611b4e40771aa4be","tarball":"https://registry.npmjs.org/@bonniernews/atlas-seq-matcher/-/atlas-seq-matcher-1.0.4.tgz","fileCount":7,"integrity":"sha512-W/+k/UmJysMhG0Xl+h4RM6HSMR57jR22tn8DFEZA3/uz8KrB0W8TaJZdocWoSvBRW65zMDEwEsGt/mdHl1XzHQ==","signatures":[{"sig":"MEUCIQClEfIt32+wDz44ZPsBPV2ppoOhzq4bJ68NWcDS1EYJdAIgBtt+Uq3YgGpDa4XZ1nCa8Pt7Uh7M1GLtyxRUCT+AMFg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":10156,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkHCjiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmphDA//ZNYymBVWgP8anLkx74VsG2nzB2P8PX/RSOeQGkLAJ9DhBDTz\r\nUl8VAcLhrQ9Aro4vpVfd7h6Mvfc+ybKm8qsyttGPWj6X1q+hqrFHvtQrp7Rp\r\na3SqHSs3wg2asUCmb5TfjxcCPW8jsCoTlPsU7uCKi3tC5msi7BbWxrPHNWQv\r\nzoWTPlZTujWFCN7N4Sp1MEbMRRCN0xGwQ2yu1eaRYvrM+IoHUO5EgnKbU6FB\r\nZL6mksvgaVPv5Z2kvsCfJ/W3SCeWDbOn9S0kx6yk/bDGlgSdCrgao9to1el4\r\nV6jq5aRINU9tP/qofmIszktRVnnyv9Zl6ZkaQiJbY4u4oVXmcx7zJNz2QX4g\r\ngTbwGR67ic6lBmW9QjEFtUcPfmJEu/NovhSu1HiKWWu1gXABP45B3ixa5DcQ\r\nJBcb0bMfpu0JbyyIawc5yJcqqcoH6eUDiRDba/1AZVktkRTI88CcZtsmmNQc\r\n5Of7ZB4OaC93HgbnwzjNN1byToEhB5sIole0/SJsV9ZJczH21lNPC17gajnd\r\nk55MWTfN3dl7VbDYGtQvey8KzJkpaNN8YR5pM0GUpkEodJlWPOEUhKwFPdpT\r\nKo4+FCnxZUw9xBSaeqFEO0cMXzm0FInzVqoUG97JUG3G0TJq0SFJphbkU3dc\r\noOQ/6EzGbrX+yG3NfbezVYGexvBIo92B5Wg=\r\n=j7wD\r\n-----END PGP SIGNATURE-----\r\n"},"main":"./src/SeqMatcher.js","gitHead":"dd23538dec6261ddd19fe5206cb67343ac02a5c2","scripts":{"test":"mocha --colors --recursive","test-server":"mocha --colors --watch --recursive './test/**/*.test.js'"},"_npmUser":{"name":"markusn","email":"markus@botten.org"},"repository":{"url":"git+https://github.com/BonnierNews/atlas-seq-matcher.git","type":"git"},"_npmVersion":"6.14.15","description":"Global substring matcher that accepts one character code at a time.","directories":{},"_nodeVersion":"14.17.4","_hasShrinkwrap":false,"devDependencies":{"chai":"^4.1.2","mocha":"^10.2.0"},"_npmOperationalInternal":{"tmp":"tmp/atlas-seq-matcher_1.0.4_1679567074431_0.5178851242726379","host":"s3://npm-registry-packages"}}},"time":{"created":"2021-08-26T07:09:00.328Z","modified":"2026-08-26T12:06:45.744Z","1.0.3":"2021-08-26T07:09:00.552Z","1.0.4":"2023-03-23T10:24:34.578Z"},"bugs":{"url":"https://github.com/BonnierNews/atlas-seq-matcher/issues"},"author":{"name":"atlassubbed","email":"atlassubbed@gmail.com"},"license":"Apache-2.0","homepage":"https://github.com/BonnierNews/atlas-seq-matcher#readme","keywords":["atlassubbed","utils","utility","streams","stream","parser","sequence","matcher"],"repository":{"url":"git+https://github.com/BonnierNews/atlas-seq-matcher.git","type":"git"},"description":"Global substring matcher that accepts one character code at a time.","maintainers":[{"email":"markus@botten.org","name":"markusn"},{"email":"max.olofsson@bonniernews.se","name":"mxtr"},{"email":"mats@rorbecker.com","name":"matsrorbecker"},{"email":"dan.arola@bonniernews.se","name":"dan.karlsson"},{"email":"adil.aboulkacim@bonniernews.se","name":"adil.aboulkacim"},{"email":"marie.winther@bonniernews.se","name":"marie-winther"},{"email":"marcus.gronblad@bonniernews.se","name":"marcusgronblad"},{"email":"axel.johansson@bonniernews.se","name":"axeljohanssonbonniernews"},{"email":"christoffer.karlberg@bonniernews.se","name":"karlbergc"},{"email":"chavah.forler@bonniernews.se","name":"chavah"},{"email":"joel.ekman@bonniernews.se","name":"joelekman"},{"email":"adam.hakansson@bonniernews.se","name":"adam.hakansson"},{"email":"marcus.hilleso@expressen.se","name":"hilleso"},{"email":"gusliden@gmail.com","name":"gusliden"},{"email":"andreas.samuelsson@bonniernews.se","name":"andreas.samuelsson"},{"email":"jesper.zachrisson@bonniernews.se","name":"jzachrisson"},{"email":"mattias.norlander@gmail.com","name":"norla"},{"email":"simon.varney@bonniernews.se","name":"varneynz"},{"email":"alice.boberg@bonniernews.se","name":"aliceboberg"},{"email":"johan.arkad@bonniernews.se","name":"johark"},{"email":"niklas.forsstrom@bonniernews.se","name":"nifo"},{"email":"herman.jansson@bonniernews.se","name":"herbola"},{"email":"andre.nordlund@bonniernews.se","name":"vitryssen"},{"email":"william.takolander@bonniernews.se","name":"takolander"},{"email":"marten.persson@hdsydsvenskan.se","name":"morre"},{"email":"mikael.mattsson@bonniernews.se","name":"mikael.mattsson"},{"email":"seb.christiansson@bonniernews.se","name":"schristianssonbn"},{"email":"mattias.olla@bonniernews.se","name":"mattiasobn"},{"email":"markus@daghall.se","name":"daghall"},{"email":"ericandersson1@gmail.com","name":"erandersson"},{"email":"jonathan.ek@bonniernews.se","name":"jonaek"},{"email":"amundsentb@gmail.com","name":"amundsentb"},{"email":"daniel.rasmussen@bonniernews.se","name":"drgeobn"},{"email":"oscar@tholander.nu","name":"oscartholander"},{"email":"peter.pettersson@bonniernews.se","name":"peterpettersson"},{"email":"basistoscar@gmail.com","name":"liroliro"},{"email":"andreas.egneblad@bonniernews.se","name":"torkelberget"},{"email":"johan@kasperi.se","name":"johankasperi"},{"email":"jon.eldeklint@bonniernews.se","name":"jon.eldeklint"},{"email":"niklas.blom@bonniernews.se","name":"nblom"},{"email":"jakob.pedersen@bonniernews.se","name":"jackesa"},{"email":"olle.nordin@bonniernews.se","name":"ollenolle"},{"email":"emil@thatemil.com","name":"emilbjorklund"},{"email":"japlilian@gmail.com","name":"lilianj"},{"email":"kristoffer.jansson@bonniernews.se","name":"kristofferjansson"},{"email":"kit.gustavsson@bonniernews.se","name":"kitgus"},{"email":"alexi.rahman@r76.se","name":"odynvolk"}],"readme":"# atlas-seq-matcher\n\nFork of [atlas-seq-matcher](https://github.com/atlassubbed/atlas-seq-matcher) without pinning to node 10.\n\nGlobal substring matcher that accepts one character code at a time.\n\n[![Built latest](https://github.com/BonnierNews/atlas-seq-matcher/actions/workflows/build-latest.yaml/badge.svg)](https://github.com/BonnierNews/atlas-seq-matcher/actions/workflows/build-latest.yaml)\n---\n\n## install\n\n```\nnpm install --save @bonniernews/atlas-seq-matcher\n```\n\n## why\n\nIf you are iterating through a `Buffer` or `String`, you might want to know whether or not the current character code completes a sequence you are interested in capturing.\n\nFor example, I'm writing an HTML parser and I need to detect a closing style or script tag without polluting the parser with extra logic. This `SeqMatcher` is fast and only keeps the current position in the string in memory.\n\n## examples\n\n#### capturing words in a stream\n\nIn this example, we'll [create an object transform stream](https://nodejs.org/api/stream.html#stream_implementing_a_transform_stream) which outputs script-end events when it finds a script-closing tag:\n\n```javascript\n// ./ScriptEndStream.js\nconst { Transform } = require(\"stream\")\nconst SeqMatcher = require(\"@bonniernews/atlas-seq-matcher\")\n\n// implements transform stream API\nmodule.exports = class ScriptEndStream extends Transform {\n  constructor(){\n    // this stream outputs data as whole objects\n    super({readableObjectMode: true})\n    this.matcher = SeqMatcher(\"</script>\");\n  }\n  _transform(chunk, encoding, done){\n    if (chunk === null) return this.end();\n    const { matcher }  = this, chunkSize = chunk.length;\n    for (let i = 0; i < chunkSize; i++){\n      if (matcher.found(chunk[i])){\n        // current i is the \">\" character of our script-end tag.\n        this.push({msg: \"found a script ending tag!\"})\n      }\n    }\n    // done processing this chunk. next!\n    done(null)\n  }\n  _flush(done){\n    // resets the internal matcher position to 0\n    this.matcher.reset() \n    done(null)\n  }\n}\n```\n\nUsing the stream is pretty easy, we'll call it `Parser`:\n\n```javascript\nconst Parser = require(\"./ScriptEndStream\")\nconst htmlStream = fs.createReadStream(\"./index.html\");\nhtmlStream.pipe(new Parser()).on(\"data\", event => {\n  console.log(event.msg) // found a script ending tag!\n})\n```\n\n#### what about using regexp?\n\nThe above example is somewhat contrived, as there are better ways to \"count\" the number of script-end tags in your text. The following is a much faster way to do it, but requires your potentially long string to exist entirely in memory:\n\n```javascript\n...\nconst myHtml = fs.readFileSync(\"./index.html\");\nconst matcher = /<\\/script>/g\n// number of script-end tags\nmyHtml.match(matcher).length;\n```\n\nThis example becomes useless when you are dealing with hundreds of megabytes of HTML. \n\n#### what about using regexp in a stream?\n\nOkay, so we don't want our entire string in memory -- let's just use a regexp with our stream interface:\n\n```javascript\nconst { Transform } = require(\"stream\")\n\n// implements transform stream API\nmodule.exports = class ScriptEndStream extends Transform {\n  constructor(){\n    super({readableObjectMode: true})\n    this.matcher = /<\\/script>/g\n  }\n  _transform(chunk, encoding, done){\n    if (chunk === null) return this.end();\n    const { matcher }  = this, chunkSize = chunk.length;\n    // DOESN'T WORK FOR EDGE CASES!\n    const matches = chunk.toString().match(matcher);\n    if (matches) matches.forEach(match => {\n      this.push({msg: \"found a script ending tag!\"})\n    })\n    done(null)\n  }\n  _flush(done){\n    this.matcher.reset(), \n    done(null)\n  }\n}\n```\n\nThis seems nice, but it fails to capture matches which have been split at the chunk-level. For example consider the following case:\n  \n  1. First chunk: `\"my first chunk</scri\"`\n  2. Second chunk: `\"pt>my second chunk\"`\n\nThis solution fails to capture script-end tags that lie on the boundary. When working with streams or are already iterating through characters, just use `SeqMatcher`. Since `SeqMatcher` works on the character level, it is chunk-agnostic and does not run into boundary problems.\n\n#### resetting the matcher\n\nIf you need to flush your stream or do some cleanup, you can manually reset the internal position of the matcher, which effectively erases its memory of what it has seen:\n\n```javascript\n...\nmatcher.reset()\n```\n\n## caveats\n\n#### performance with trie\n\nThe trie implementation of the `SeqMatcher` looks cleaner, but is slower than the current implementation by a factor 4, since each invocation of `matcher.found` will result in an extra object key lookup, which is slower than char code lookups and setting/reading a local integer. Recall that `matcher.found` is being called on the order of a million times for a typical HTML document, and maybe billions of times for continuous HTML streaming.\n\nA concise trie implementation can be found in the [trie-implementation](https://github.com/atlassubbed/atlas-seq-matcher/tree/trie-implementation) branch.\n\n#### single-char sequences\n\nIf you're trying to match a single character (e.g. `SeqMatcher(\"a\")`), don't use `SeqMatcher`, just do it at top level, since it's a *single* character. While this module passes tests for single-char sequences, it has not been optimized for them.\n","readmeFilename":"README.md"}