{"_id":"staccato","_rev":"117-df14464a3cc9cd1e3553a91143dbf4f1","name":"staccato","description":"Write to a Node.js stream using the error-first callback style.","dist-tags":{"latest":"12.0.11","canary":"13.0.0-alpha.13"},"versions":{"0.0.0":{"name":"staccato","version":"0.0.0","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Durable logging for Strata database pages.","keywords":["btree","leveldb","levelup","binary","strata","database","json","b-tree","concurrent","persistence"],"contributors":[],"homepage":"https://github.com/bigeasy/transcript","bugs":{"url":"https://github.com/bigeasy/transcript/issues"},"licenses":[{"type":"MIT","url":"http://github.com/bigeasy/transcript/raw/master/LICENSE"}],"repository":{"type":"git","url":"http://github.com/bigeasy/transcript.git"},"dependencies":{"cadence":"0.0.36"},"devDependencies":{"proof":"0.0.41","mkdirp":"0.5.0","rimraf":"2.2.8"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"_id":"staccato@0.0.0","dist":{"shasum":"021e391f696f5003d765991dc016bd72a28c6a43","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.0.tgz","integrity":"sha512-Agd0cGqZh1Wx1endNwzEEIi9GAZrZjMQlkQf8aEtxYTQDsZAOBLXzSeSTrD127aXNcUrhvFRwCtJggD2EEoLyA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHhfzFTl5gmCH+9nWK7Cdz9kvhmHQPSYuqfDb0IB3caAIgRd8mqwey70prIlhzRZMPcvsL14feCNFfhci/KJdPzpI="}]},"_from":".","_npmVersion":"1.4.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{}},"0.0.1":{"name":"staccato","version":"0.0.1","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"licenses":[{"type":"MIT","url":"http://github.com/bigeasy/staccato/raw/master/LICENSE"}],"repository":{"type":"git","url":"http://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"0.0.37"},"devDependencies":{"proof":"0.0.45","mkdirp":"0.5.0","rimraf":"2.2.8"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"_id":"staccato@0.0.1","dist":{"shasum":"4d63b088d14fad2aa950f9e3d0626399139dd4a1","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.1.tgz","integrity":"sha512-u+AbS86tKdlGSovmBnONVAQCQVPS3kMF96skuigOnesjOZyBA0k3TVzc/tFfxqxtne/4Qp5HWOIAstK04b2HXA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCE/sI2DRM7mYtYYiML6vflaTJTX87WMcwP6RgRLejjygIgI0AGxFSTbB1jKKvWVZ72kGV9rwF2kQ8GsqJzd0cf9gw="}]},"_from":".","_npmVersion":"1.4.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{}},"0.0.2":{"name":"staccato","version":"0.0.2","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"licenses":[{"type":"MIT","url":"http://github.com/bigeasy/staccato/raw/master/LICENSE"}],"repository":{"type":"git","url":"http://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"0.0.40"},"devDependencies":{"proof":"0.0.47","mkdirp":"0.5.0","rimraf":"2.2.8"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"_id":"staccato@0.0.2","dist":{"shasum":"7c338d5fef66d17378088a920f98819e60a05144","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.2.tgz","integrity":"sha512-lUw4WnAOeQSW5tFyUABYkuX6lXqwHrfBUZSTXbGmcRqHkFk9ZngurDIu9BxyMLgNzEevr/4HeRuQphdwR3wVCg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCnTTy9ezuboxLWX0/yz0t6ondExP7wDQbHCJYfMwctRwIhAO60/XR1yY5cp1Eh32pEhixIBk90hXYxSaF5mjfjYNWe"}]},"_from":".","_npmVersion":"1.4.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{}},"0.0.3":{"name":"staccato","version":"0.0.3","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"licenses":[{"type":"MIT","url":"http://github.com/bigeasy/staccato/raw/master/LICENSE"}],"repository":{"type":"git","url":"http://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"0.0.41"},"devDependencies":{"proof":"0.0.48","mkdirp":"0.5.0","rimraf":"2.2.8"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"_id":"staccato@0.0.3","dist":{"shasum":"d285b71e8047a54558631b3dd9ca9e7314f2803c","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.3.tgz","integrity":"sha512-VR+kCQPt51c+qrfn7AwHYWMK/teEM0yQ8rQx6tQYFhyW1LtOpL/+Aa4YEmXpv8GjHcK2nQy4cV8GbhHwu4EN5g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcWD/N+HwQblDfFLfQntfdhMK5g8YhqgeUCE2vCW+CzgIhAOox8wq7Ij9ngphO+1moZmLsKCQdad9gajUorG4ecs8V"}]},"_from":".","_npmVersion":"1.4.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{}},"0.0.4":{"name":"staccato","version":"0.0.4","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"licenses":[{"type":"MIT","url":"http://github.com/bigeasy/staccato/raw/master/LICENSE"}],"repository":{"type":"git","url":"http://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"0.0.42"},"devDependencies":{"proof":"0.0.48","mkdirp":"0.5.0","rimraf":"2.2.8"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"_id":"staccato@0.0.4","dist":{"shasum":"33642da4ae2f6162643a68084cf717c7acd00778","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.4.tgz","integrity":"sha512-u5yPNbKVg8Fw5p/JtQBiLVZZOqpN72Edne4nNaaf+qIEKLYDRIjoWvRg41R8QIwP4222EaFg1BP5qACPBlejZw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDlY2p1WtRF9SVAZKQyyE7Bt2QtSGo5SZ8nDXK/0Z/GNQIgTzBCZrXi4TyMo/ocmYSpJW5z/8Ya8qsaYBTVpnYPkwM="}]},"_from":".","_npmVersion":"1.4.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{}},"0.0.5":{"name":"staccato","version":"0.0.5","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"licenses":[{"type":"MIT","url":"http://github.com/bigeasy/staccato/raw/master/LICENSE"}],"repository":{"type":"git","url":"http://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"0.0.43"},"devDependencies":{"proof":"0.0.50","mkdirp":"0.5.0","rimraf":"2.2.8"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"_id":"staccato@0.0.5","dist":{"shasum":"3dcfe6a1ffae208e528611063ea4aea2acd52bff","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.5.tgz","integrity":"sha512-pWM6/tyBO7ZdThSMyAGGoV3Q5Pr9/x7I6xnKi6kJO2E89SWitlx7vB9vRibSekcTR/sE4Z7J9e9aUPtOs6KgAg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDM6Q38hPpnApEqysGtIRK+c0wtf+eEBQR141Fo4qNHFAIgUWi5hQnGtK63jWnJ/uVbGebLtWury05H7yEXyF7N+SE="}]},"_from":".","_npmVersion":"1.4.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{}},"0.0.6":{"name":"staccato","version":"0.0.6","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"licenses":[{"type":"MIT","url":"http://github.com/bigeasy/staccato/raw/master/LICENSE"}],"repository":{"type":"git","url":"http://github.com/bigeasy/staccato.git"},"main":"staccato","dependencies":{"cadence":"0.0.48"},"devDependencies":{"proof":"0.0.50","mkdirp":"0.5.0","rimraf":"2.2.8"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"gitHead":"f42054a2914abf79e2a3d039cc3a7417172c92c4","_id":"staccato@0.0.6","_shasum":"d48be2adb523bdcf0c400ab602660bf0f3125bc9","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"dist":{"shasum":"d48be2adb523bdcf0c400ab602660bf0f3125bc9","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.6.tgz","integrity":"sha512-iyKyITvqeqU+3KXAbgW3zsWr6gcMDnTPya07xxlI+smZeHZgzHJvxKUfRF8w4uSp6PgRkbWO1PD2UhgDk7731A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCk0hijdsGos+V4Px5ZgH+R/hqk/9UdqOhE9+JgMn32RAIhAM9nIu2JxXZyQxrZwOb8hrQD3oNL3rZ5GwoOrKPQNdte"}]},"directories":{}},"0.0.7":{"name":"staccato","version":"0.0.7","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"main":"staccato","dependencies":{"cadence":"0.2.3"},"devDependencies":{"proof":"0.0.53","mkdirp":"0.5.1","rimraf":"2.4.2"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"gitHead":"2898e6f2226b661bd171a1aed0002cc19e1bf3fc","_id":"staccato@0.0.7","_shasum":"1be7218b2c22f039be1be03c2f65dde66013480d","_from":".","_npmVersion":"2.11.3","_nodeVersion":"0.12.7","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"dist":{"shasum":"1be7218b2c22f039be1be03c2f65dde66013480d","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.7.tgz","integrity":"sha512-cUC0tZUo3eshtwZK8L+WpFuvmbXgypGrkCWIZfNsZPSebDkw1S++pKpVKeGXIffNb90Qnw0y377AfH9CcEruPg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDqxSS6OjXfHxUwNqR1rlp68iM35kC20uSPS0zv6nq0yQIgexSzpQS3DpevQz3jxEMOabudbVQRNVYjhNYND3yo714="}]},"directories":{}},"0.0.8":{"name":"staccato","version":"0.0.8","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"http://github.com/bigeasy/staccato.git"},"main":"staccato","dependencies":{"cadence":"0.2.4"},"devDependencies":{"proof":"0.0.54","mkdirp":"0.5.1","rimraf":"2.4.2"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"gitHead":"d758d80df662e583a67f9ab5c3f495d8b8e2d1e5","_id":"staccato@0.0.8","_shasum":"8494cc025b641318b89c2444d0cc82c765c69885","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"dist":{"shasum":"8494cc025b641318b89c2444d0cc82c765c69885","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.8.tgz","integrity":"sha512-AzQUVD8hcS0UxWAwOgMn9EGmy+bj2ss2rv14QxlG/+j0Qd0nIWLlcQWyCX9mMSXNJndIVEU85XFKgGYN2ZzQ/Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDLHqMx4GOQ53CJRuSXvLjbjyYrR4dci89kjO6sZEsK3AIhAKUSX4hxWkYnxSGxJchX5IaSs31QKVUndoc4HsuTr6og"}]},"directories":{}},"0.0.9":{"name":"staccato","version":"0.0.9","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"main":"staccato","dependencies":{"cadence":"0.4.3","delta":"0.0.4"},"devDependencies":{"proof":"0.0.57","mkdirp":"0.5.1","rimraf":"2.5.0"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"gitHead":"921e5fdc67e7fc3269982e0cdf1ee04d0d13fa07","_id":"staccato@0.0.9","_shasum":"d8e2147e82200d74496a11259c4dc56b82e9fd4e","_from":".","_npmVersion":"2.14.9","_nodeVersion":"0.12.9","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"d8e2147e82200d74496a11259c4dc56b82e9fd4e","tarball":"https://registry.npmjs.org/staccato/-/staccato-0.0.9.tgz","integrity":"sha512-T0F9qYt4Xy3H8RR9YHJgWUNXoSeVKgEMi63xDKDPp1yHS+eB4k+Faue4n1JvCMQaaghpsBCDVR7/AiYKkabsyg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB5MLnN1AXxkT1VUJIEw0ZGciawIasXSCy7Fhzu/TCEPAiAVfqqHIm48MdUTl/EPf0ak7/NvVmBLDeZJJEDT55LG0w=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{}},"1.0.0":{"name":"staccato","version":"1.0.0","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"main":"staccato","dependencies":{"cadence":"1.0.x","delta":"1.0.x"},"devDependencies":{"proof":"1.0.x","mkdirp":"0.5.1","rimraf":"2.5.0"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"gitHead":"1cfdd1dd9aa651e989e4ccad952adc4008ea3c83","_id":"staccato@1.0.0","_shasum":"7fcc5722e9e95e30a545088e029a3f23b94158c4","_from":".","_npmVersion":"2.14.7","_nodeVersion":"4.2.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"7fcc5722e9e95e30a545088e029a3f23b94158c4","tarball":"https://registry.npmjs.org/staccato/-/staccato-1.0.0.tgz","integrity":"sha512-rpZr7l54u+MwDaihjkahfRMn/rUcCyEr3Rqn+dagweAQ1iDi/JijWLoz2rVRl4qf3S/dmAOWTnALVSbSYfcdOA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCNtZqiGzuE48UMAOm8wiTcJpQlzG/E+mvoORQo8ZYJVwIgJ/LT01EPXF0fG7RL2XyRKghYuobGm7le5ObI03n3MtM="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{}},"1.0.1":{"name":"staccato","version":"1.0.1","author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"contributors":[],"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"http://github.com/bigeasy/staccato.git"},"main":"staccato","dependencies":{"cadence":"1.x","delta":"1.x"},"devDependencies":{"proof":"1.x","mkdirp":"0.5.1","rimraf":"2.5.1"},"scripts":{"test":"proof platform win32 && proof test */*/*.t.js || t/test"},"gitHead":"9b0f8056bb0d7a989f4bf35549bcc16635ee8291","_id":"staccato@1.0.1","_shasum":"19c7dc11374d6f22c20d5de587ac99c107402b98","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"dist":{"shasum":"19c7dc11374d6f22c20d5de587ac99c107402b98","tarball":"https://registry.npmjs.org/staccato/-/staccato-1.0.1.tgz","integrity":"sha512-rkfEIMkK3pL5Vr4Td/rGVEgqXs3J755dbC9BEihOXRqPBONutyOMsQPxbhtpuw5EpXi2lyVQkdX7AxxlA2rdug==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCDtTnQS11crexLZaMZ4WnIF/eQgeO9LGqvE4ZPX7FTdgIhAOlze0Un1NQDSx7IypXSvgoercVF7noDfm5UMD2Rh+Vu"}]},"_npmOperationalInternal":{"host":"packages-9-west.internal.npmjs.com","tmp":"tmp/staccato-1.0.1.tgz_1454490938439_0.8989498422015458"},"directories":{}},"1.1.0":{"name":"staccato","version":"1.1.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"main":"staccato","dependencies":{"cadence":"1.0.x","delta":"1.1.x"},"devDependencies":{"proof":"1.0.x","mkdirp":"0.5.1","rimraf":"2.5.2"},"scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"885195dd15c51d0b502814954944ffdf3ac566a6","_id":"staccato@1.1.0","_shasum":"057cbe1e8c87b0bd0edba8018505061e87092071","_from":".","_npmVersion":"2.15.1","_nodeVersion":"4.4.4","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"057cbe1e8c87b0bd0edba8018505061e87092071","tarball":"https://registry.npmjs.org/staccato/-/staccato-1.1.0.tgz","integrity":"sha512-f1RO9ZImj3ecFbIlKi19Drs44vY9h/888HnHkjeptnYBZTtVg0/LdJJpbomy/J0vIz9xzj9twwZ2PHExpBTY3w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3WE8sN7plcl1hFI1ap2ZOjuthk8QytEQzDRdt4xydtAIgFgbQnWZKNEELv2kLncttq1MqHwb5BnhjKdlemD27Ze0="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/staccato-1.1.0.tgz_1469412008896_0.4938409789465368"},"directories":{}},"2.0.0":{"name":"staccato","version":"2.0.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0-beta.0","interrupt":"2.1.x","nascent.destructor":"0.0.2"},"devDependencies":{"mkdirp":"0.5.1","proof":"1.2.x","rimraf":"2.5.4"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"97b3d834b0b946d8b75af380d3d5b2a8ee858f5f","_id":"staccato@2.0.0","_shasum":"d11940fc1e60e562cb73fb5bf79c36132ced7ef9","_from":".","_npmVersion":"4.0.5","_nodeVersion":"4.7.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"d11940fc1e60e562cb73fb5bf79c36132ced7ef9","tarball":"https://registry.npmjs.org/staccato/-/staccato-2.0.0.tgz","integrity":"sha512-OiRTmL1xE3YmkwXeNy4nWYylfcW/X7/ZFSb2iaqa7/Cf/RtThA6ebSq9PjZRkch2xFQsAVb9ls8Q4UIPkFfeCg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB0lsMQHHBX1WFOqpgNyIiqFqV2ull393tmEDg2VS/CHAiEAtca2lB2C+5baI5pp3kX5SGi6Dlp1hMZ/z/8SgO1EtG8="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/staccato-2.0.0.tgz_1485378148630_0.5760619668290019"},"directories":{}},"2.0.1":{"name":"staccato","version":"2.0.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0-beta.0","interrupt":"2.1.x","nascent.destructor":"0.0.2"},"devDependencies":{"mkdirp":"0.5.1","proof":"1.2.x","rimraf":"2.5.4"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"6abd8ef86d74e941c4655db119f8c128f4a76e8b","_id":"staccato@2.0.1","_shasum":"2be9ef034942aedb6ece1082cc14b376ca7f4b18","_from":".","_npmVersion":"4.0.5","_nodeVersion":"4.7.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"2be9ef034942aedb6ece1082cc14b376ca7f4b18","tarball":"https://registry.npmjs.org/staccato/-/staccato-2.0.1.tgz","integrity":"sha512-gJEXbEuntSeieA/nPBQ17oIIVrHjYE6ynZk0X1QMr7qzboQAN0p6n3kQcQoFcTmJpXseaC4NMeT16UKjszgnKw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCvkeKoAQw2gvPRga/liz9Lf6k1X6FejNiSYYGV+nf5gQIgeBZW+ZeOlAtiXhcmpqI96hGVmlNAOfAlqa1swa0kZwQ="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/staccato-2.0.1.tgz_1485385136441_0.6678774706088006"},"directories":{}},"2.0.2":{"name":"staccato","version":"2.0.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0-beta.0","interrupt":"2.1.x","nascent.destructor":"0.0.3"},"devDependencies":{"mkdirp":"0.5.1","proof":"1.2.x","rimraf":"2.5.4"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"a31e9b19f1d6604b22306f2f356ac5ab9522395e","_id":"staccato@2.0.2","_shasum":"aaded1d838879e87a1b1447c676b152948afa6bd","_from":".","_npmVersion":"4.0.5","_nodeVersion":"4.7.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"aaded1d838879e87a1b1447c676b152948afa6bd","tarball":"https://registry.npmjs.org/staccato/-/staccato-2.0.2.tgz","integrity":"sha512-YGABOW8nUK985vul+/sGhiR4pjFNGKzhdr5OKcFG7cLxiHVXKcH2XcB7MeLsDLKQDgXfg50RH2UByDGaY/5Abg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCKLyMJk1naBxf6zxVtY14Mv8wrQ4nm2SC+16xWvn1m1wIhAMSL87b0IRajGueYF6OWM8nuCVBQPNHuGica2yo69jRj"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/staccato-2.0.2.tgz_1485390687295_0.17046848870813847"},"directories":{}},"3.0.0":{"name":"staccato","version":"3.0.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.0.0"},"devDependencies":{"mkdirp":"0.5.1","proof":"1.2.x","rimraf":"2.5.4"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"be7e05b1f8bd7c45bb00166c6ff275f287ec3634","_id":"staccato@3.0.0","_shasum":"addcd049f7aeed235234c98419d5a2bc1fd6ec8b","_from":".","_npmVersion":"4.0.5","_nodeVersion":"4.7.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"addcd049f7aeed235234c98419d5a2bc1fd6ec8b","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.0.tgz","integrity":"sha512-lsJDlLUi9sk2iPY0N1ouNVFQMYelb4n9zmicLebq5TQzWFW7gzb4D8qbuXtN88Dltp7+05Lzf9mQ37pB4nnVRQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3G7o7NhQfQQUazGEye/MzWsX/8e452pyIo7PZcCa7bwIgL5c4AIk370nJkE8rfpjkB+Stzzl86PFZdpipI2fO9lA="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/staccato-3.0.0.tgz_1486885193799_0.8115664888173342"},"directories":{}},"3.0.1":{"name":"staccato","version":"3.0.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.0.0"},"devDependencies":{"mkdirp":"0.5.1","proof":"1.2.x","rimraf":"2.5.4"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"62c58fae72aa9b7757efdea4ae0dd2eb459464be","_id":"staccato@3.0.1","_shasum":"04a419a6c4307acaac7cfe2d1223707f45b28368","_from":".","_npmVersion":"4.0.5","_nodeVersion":"4.7.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"04a419a6c4307acaac7cfe2d1223707f45b28368","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.1.tgz","integrity":"sha512-S7tEwPTpDxS02wMuKvqqjiTK0HdFXvoIJztElW6Nd7VcOe1hliIlv1zkox0jqFnzo7T487+FPamitdeKGxsRRw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDJDuhIC7T4HiVj6FUcKo834aEWfNmhwH/g13RLmEuArwIhAO4Tg/lWR09DhnXsi9lO1SXGWxo0Fi7KrRrrXi6Si+40"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/staccato-3.0.1.tgz_1486968653705_0.45598040777258575"},"directories":{}},"3.0.2":{"name":"staccato","version":"3.0.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.1.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"1.2.x","rimraf":"2.5.4"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"c2726b04ebe7db970c3377256ed91ac860467ca6","_id":"staccato@3.0.2","_shasum":"061bdd0d65f0cc5ecd8e8195107845ab6f0a5b44","_from":".","_npmVersion":"4.0.5","_nodeVersion":"4.7.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"061bdd0d65f0cc5ecd8e8195107845ab6f0a5b44","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.2.tgz","integrity":"sha512-CJLV47QxUgLgsXzT3K1GFfe2FEKbfV+HTcwN4TnqDc9kZfVW0TlW8Uq8iLmxhHeSKOnFYEJGEhvJegArg3ymUg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFNGboRXoOdLbv0cdJg0dDpV9wabpbNEwv4N+SXF2PeYAiAoXmpJ2i6ZYdSH4NE60XgrV2cP9nkhXYfO/nlytqywHw=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/staccato-3.0.2.tgz_1489595430425_0.8268162880558521"},"directories":{}},"3.0.3":{"name":"staccato","version":"3.0.3","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.1.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"5837b068c36f7561ee426f193901bfad3b56df6c","_id":"staccato@3.0.3","_shasum":"a94ed01648f4a0a5f8a4f1db2871cd571435194d","_from":".","_npmVersion":"4.4.4","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"a94ed01648f4a0a5f8a4f1db2871cd571435194d","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.3.tgz","integrity":"sha512-vbCm9dW1vkJ7pPXiFuX4WDXGvjzOmekXvgKP4rdn8yKpgdz2V5M+YiJMDndbiZgFVzyzob2DSf0SWxMWLcMzsw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICQVd658zSgiOmbsU4yryuYtDA454G5WlUHx8z3NMpbzAiAc41Ig8MR6np/E4R1C04nGP/QDFh7rKincfPo8XjsI/Q=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/staccato-3.0.3.tgz_1491495309758_0.32723777648061514"},"directories":{}},"3.0.4":{"name":"staccato","version":"3.0.4","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.2.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"8d86090447003894ecbf3180c79211fd47edd1cc","_id":"staccato@3.0.4","_shasum":"51889d2bfbd1ba8bbc52fc8416dd4807058554c1","_from":".","_npmVersion":"4.4.4","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"51889d2bfbd1ba8bbc52fc8416dd4807058554c1","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.4.tgz","integrity":"sha512-X4/AsIAU2Z7eVEvzclQV9bFRszjX3xx4HRyEcqRrjG+0G7CHMLDznwALDdx1KhC2dx4gXkjvvAnnTCv4UN2sgA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDy+D/x7IDtyACYv38MX+4j/R4LmSaDenvma29ntVm4nwIhAP757s6+CL2mKdfINgZDBO/71Iczmtly+uaUYujpOsAH"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/staccato-3.0.4.tgz_1491848085410_0.5936294805724174"},"directories":{}},"3.0.5":{"name":"staccato","version":"3.0.5","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.3.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"a0d2bebf8f86811760b31b24a017de347793d65e","_id":"staccato@3.0.5","_shasum":"379a5a634897e601f9ef98a7824115008cf053ff","_from":".","_npmVersion":"4.4.4","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"379a5a634897e601f9ef98a7824115008cf053ff","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.5.tgz","integrity":"sha512-7Vz32IctKeyW4hj/WRnDy8YZplDIp5AG3z6RCW1uMkOH6inpyxNww68PgsT//mSIXZEfNUkt40aqxm2Tcp1Jfw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCzavg42vM0Y+AOgLA8Tk52RXPRES5FB3Qs+GhTL6vZ6QIgeMsuIZYPC/yO7opTmR+AvX3aZbNU69X4QKVHjjVg0uY="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/staccato-3.0.5.tgz_1491930350969_0.561930262716487"},"directories":{}},"3.0.6":{"name":"staccato","version":"3.0.6","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.4.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"0762aff7df9aed0c550c7d16da05c5f430424453","_id":"staccato@3.0.6","_shasum":"579c305f271e28fe08d2c674e81d1bc8bb8226e9","_from":".","_npmVersion":"4.4.4","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"579c305f271e28fe08d2c674e81d1bc8bb8226e9","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.6.tgz","integrity":"sha512-rc2T7F4mAG5jPz+zx1c3oyTTJyWNX2sKgawoI7tlTS7BhQDNGh9fA6y0T7sQza9YBTlSz54xNwiImENKGbsIrw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDCxoDeeSGtDRBZcizlzQDZYzLLeQ9wWOnJv6q9PedbyQIgY3BKruJWrXgGSV4phKqsd9fRSgXho29WP1dxz2uatX0="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/staccato-3.0.6.tgz_1491933106283_0.22834251704625785"},"directories":{}},"3.0.7":{"name":"staccato","version":"3.0.7","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.6.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"19e12e7a99b5610098be7b8c2ab48edd79549d1e","_id":"staccato@3.0.7","_shasum":"3efc9e25e7f878ef9469bd3535a79955decb64f9","_from":".","_npmVersion":"4.4.4","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"3efc9e25e7f878ef9469bd3535a79955decb64f9","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.7.tgz","integrity":"sha512-nsSUxgypdgjJI8W0vafSNGlaQ41cSx2wmL3X1hD47UhoHRqgo0RSF/wcC8CPgnaeiRntaG+hgtpwxT+HmrQFfQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG2u3AtTNbIbXYgYOdfYebfnBvPIgb28eqZqKooEjhCVAiAukj86PYxs3mNlf78ZLFrrRYNXaKFGgqs75+8Gp4rCeg=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/staccato-3.0.7.tgz_1492015410920_0.856686677550897"},"directories":{}},"3.0.8":{"name":"staccato","version":"3.0.8","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.7.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"917880aa62287d92cf92c996a5201cef7c1492bc","_id":"staccato@3.0.8","_shasum":"d1417d8381f3acd2a9aca9471a084c4063b10a03","_from":".","_npmVersion":"4.4.4","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"d1417d8381f3acd2a9aca9471a084c4063b10a03","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.8.tgz","integrity":"sha512-xBb15TcyGuXHg/xlHZDREAETU6nrGf926arnekpnH1dT/QDrVrzTzm6f6yNVjVb2tWFRbArlpEWvwNyzAgT39w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCAGqwLaa+2ZR760GyMPevdhtPV67JsX7CnSJHYFFI8lwIhAMpSjIDijD2zH4nssxE72sg7txs6j4vdZYlWaduRmkve"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/staccato-3.0.8.tgz_1492064049005_0.8249293295666575"},"directories":{}},"3.0.9":{"name":"staccato","version":"3.0.9","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.8.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"02858256ce3723d95542ca76c1d27a2cc77060aa","_id":"staccato@3.0.9","_shasum":"6f887aabacb42dc32222641131e207d01f56b17e","_from":".","_npmVersion":"4.4.4","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"6f887aabacb42dc32222641131e207d01f56b17e","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.0.9.tgz","integrity":"sha512-qKEACVB0UQjvJEOQReUhiSaTZBsNvolCy+dHLlUMwRLOBkXqKNbYm9a1nwMPm/QTe23VO44JzAwizBQ7xUQ94g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDnQs9xTxVUUmznrhYl0QWyPOklUKQen5OjSK2d3FZ5iAiEApAuEyl7MPoM1i/NuEKhn2a8JIFO+t19LyZu76OLvyMc="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/staccato-3.0.9.tgz_1492066840169_0.7177475250791758"},"directories":{}},"3.2.0":{"name":"staccato","version":"3.2.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.12.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"e1cec78025f5e1ba5de179ddca716adcd013867e","_id":"staccato@3.2.0","_shasum":"776e865667bfe4d773cc08e4f35a2816ad28fcb3","_from":".","_npmVersion":"4.5.0","_nodeVersion":"4.7.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"776e865667bfe4d773cc08e4f35a2816ad28fcb3","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.2.0.tgz","integrity":"sha512-v7gCk/rQxvLinPQsSKtophPToXRDgsN/Cn5jaGA3afH7A1enC19B5rtT49MAMHTnoaaeYECGoc9/xyK7YIn1Lw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCMcu2EmmXs0vJLDR2TalnUdWz4/99/9Eg6yAQ9SI2x1AIhANn1ovgBROCbZl4SKiBZS8AwNg0KbgSTW2OgTzCnGSe/"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.2.0.tgz_1496467977640_0.48709332128055394"},"directories":{}},"3.3.0":{"name":"staccato","version":"3.3.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.12.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"74ddcbc150dd901fa16f14b85b9ef8c1873c43d6","_id":"staccato@3.3.0","_shasum":"ffe2d233de50ff3a8e65a942ba710ebb8f90a821","_from":".","_npmVersion":"4.5.0","_nodeVersion":"4.7.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"ffe2d233de50ff3a8e65a942ba710ebb8f90a821","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.3.0.tgz","integrity":"sha512-hm2RQDM1b/6tO8WEw3Knm/cBGqIfyAd7iQVB6GSMVmyr6w1J1WeucIIrDVux+4lSENIJM5Ia7978sgV2b7Yfvw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBQXkOmqqT8mq23unFyRzXftS0+eJ2dqSOsRvWiVcgl1AiBhiZqTTiLrkTMkodt5sDmfHfSp++9+k1pKyEXcHVZwdw=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.3.0.tgz_1496467996673_0.9822611815761775"},"directories":{}},"3.4.0":{"name":"staccato","version":"3.4.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.12.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"29d6987795a8b9f17cb2f1531d708ff61def76ca","_id":"staccato@3.4.0","_shasum":"21e7b4e5c57c623d5f1a238966a361f262e8767e","_from":".","_npmVersion":"4.5.0","_nodeVersion":"4.7.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"21e7b4e5c57c623d5f1a238966a361f262e8767e","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.4.0.tgz","integrity":"sha512-OVpVH7dtgaCd91njDwQgVApXrOJR8+L/ZNh5RGVLsxVlJnbeXr3fPZgEPvFvcZXg2srvexzwUTUxrrOotmzQ7g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAwDyuL59ziPDU6eywQXa9W/AG1YbEyONSVHGKkpBEX1AiBSCCn83cZ7MX/99ZM+Ly5yv5sL/XUrCFY52EASFXHsOg=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.4.0.tgz_1496468005190_0.12977141770534217"},"directories":{}},"3.5.0":{"name":"staccato","version":"3.5.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.12.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"abca4e970e8469f55dbdf20a7e025daf6b7467e4","_id":"staccato@3.5.0","_shasum":"332c827d290047a25b9e522ef6bd78e9b5566dc1","_from":".","_npmVersion":"4.5.0","_nodeVersion":"4.7.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"332c827d290047a25b9e522ef6bd78e9b5566dc1","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.5.0.tgz","integrity":"sha512-1f3lgV1/bsYaCZRO6yAQztnC51a6AkO6p/J2WpMtSLnyHiLBe4ix5IWCUCSBXT1iiFqQxdTPOOqgU5xkCisCRQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGVDlJ14n1Dw8aLNWAez5b9K1eSBSroYLgx3gj+v1r6rAiEAoknBMnBGZf6d6bDgk49ABAvLEeZF3oAksfAe20mE3p4="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.5.0.tgz_1496468056051_0.25055184843949974"},"directories":{}},"3.6.0":{"name":"staccato","version":"3.6.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.12.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"5424462ecdd8e2704cf6a568f5c21525765f38b5","_id":"staccato@3.6.0","_shasum":"015f5e9d7a686d9e9d65483583ec41dbfdbda4dd","_from":".","_npmVersion":"4.5.0","_nodeVersion":"4.7.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"015f5e9d7a686d9e9d65483583ec41dbfdbda4dd","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.6.0.tgz","integrity":"sha512-eDNEtg8HYpd/+T8XeFfRK+11hTU/Y4Xou8UpePtOLjm+tsqjxmd6cJqReWvuCeQJPVXL3mVDsXCUHIjUlj71oA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCXpAuJLqjCTNv9m3V0IV4/I4sr1g12gXmWE1kw5Tn59gIhAOwchSxs4VIZg3B02ayancDE0nDAG6sGFxSARCsShlyM"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.6.0.tgz_1496468076606_0.727400174131617"},"directories":{}},"3.6.1":{"name":"staccato","version":"3.6.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.12.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"744aa72748d7aab7f7c8748f5518b3e974cdd530","_id":"staccato@3.6.1","_shasum":"245507e28cc86ed8c60b0416608b80087597a4ac","_from":".","_npmVersion":"4.5.0","_nodeVersion":"4.7.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"245507e28cc86ed8c60b0416608b80087597a4ac","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.6.1.tgz","integrity":"sha512-Yd+JWn64aRNhKE2xoNLI2XS8P3bMozPfhQq3EPnoaEpe1gk8TCnQOIk6rX7ebt8yh1pJIw2R0/q81Icl8v6hqA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDUoXV9RypR3a987v95wUsUkQ1veexjYn1tDz3W8mHP7AiAt95+QcMm9381dcHwugKgL/nFewtcW+mPgN6IWRGR1Rw=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.6.1.tgz_1496559038208_0.17629931820556521"},"directories":{}},"3.6.2":{"name":"staccato","version":"3.6.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","interrupt":"5.0.x","destructible":"0.12.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"8e4e88cdaacd44b61af1c7a51b5ca9f229b0af75","_id":"staccato@3.6.2","_shasum":"3bd65e187dfb890ed420dae5ad0959ab30c75f47","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"3bd65e187dfb890ed420dae5ad0959ab30c75f47","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.6.2.tgz","integrity":"sha512-bAOjEyTFFAkRqOSbvILTHC0bjYI6PDLGNz0zRAveZGnR0rkNBKVfFNW/D9QDAm42u1TIbzVwt1TlWcOm8NNqRg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDY29kJcJAbSu55Tc/zQwvRgHDEh6PJRUu1U5ddIMfDQwIhANx1znsba9P0PwkFjxJEbpZ7qsWlHYfhlekgO2EFlgBQ"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.6.2.tgz_1497676819280_0.20325437420979142"},"directories":{}},"3.7.0":{"name":"staccato","version":"3.7.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","extant":"1.0.x","interrupt":"5.0.x","destructible":"0.13.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"e6b52de910b9f91af8e06fc2d037b084818ce972","_id":"staccato@3.7.0","_shasum":"21cf71f5644d94f514a7fe33f1973d1606838f8c","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"21cf71f5644d94f514a7fe33f1973d1606838f8c","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.7.0.tgz","integrity":"sha512-GKMeZHivdBZFRTae1u5+PHGGwRcYIpZmCy8Ia2R2Xawy/gJJfLZnc6HtZ5MTXP9J+jrciq0IrBrC+qiEegEcJw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFd1hdE5HhSW/CmpmhL5LIzYJFVpc5Ch06XQhXyBa/4FAiEAgXBO7UOFQ+rzx7ufb1n8rYCHONUPkfo4RR5CV9i3YYA="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.7.0.tgz_1497894132196_0.36975229694508016"},"directories":{}},"3.8.0":{"name":"staccato","version":"3.8.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","extant":"1.0.x","interrupt":"5.0.x","destructible":"0.13.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"8bc275b36b1fa9cb9821344afd0d89be87a64ac5","_id":"staccato@3.8.0","_shasum":"67c4128f579f6b51079a1392182e130df47d4e89","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"67c4128f579f6b51079a1392182e130df47d4e89","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.8.0.tgz","integrity":"sha512-piU2qyvxYC3SVQyLoJIYZKkU0Lm/sbmWHe7af8mi2Xfj6OaDf8TKWsOQBguz2tz8bSsoj39N2zX9GPKuq2wbWQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBdjogGGrmwZgvq0n3cLRDzXKWjBmZym233IK4S4nV91AiEAofDSUZkLaba/wXxTicBIsZxiPOqi4cAMFcAMfFXNYkc="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.8.0.tgz_1497894148293_0.7787044912111014"},"directories":{}},"3.8.1":{"name":"staccato","version":"3.8.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","extant":"1.0.x","interrupt":"5.0.x","destructible":"0.14.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"0cf499b28a5117908b31e1f802dbbc56d2e37497","_id":"staccato@3.8.1","_shasum":"21a4595fd88fa7b30e39186c6a2f3aa95b48370d","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.7.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"21a4595fd88fa7b30e39186c6a2f3aa95b48370d","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.8.1.tgz","integrity":"sha512-v6CmXANEoZIy3yz/Ccm8kAxZyIPFtJgs6F9y09hnPw5vHaYVJYHoOhy9MBJwi6ILyYmXjsuHfZJc6lywbj8ZCg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCzrXcGyK2wa6hDiEK40hCwZ47kocrVD40t0lnFUdQLuwIgbjhoFfHIEQ1pFpdASpCoxnas9kK6Y7CQDrtHtiqh5Rg="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.8.1.tgz_1500052471385_0.4216979637276381"},"directories":{}},"3.9.0":{"name":"staccato","version":"3.9.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","extant":"1.0.x","interrupt":"5.0.x","destructible":"0.14.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"965f9f7bc214f09c48e94eb5b9f37be5093d5a70","_id":"staccato@3.9.0","_shasum":"e704003fe7271f1aeb01b23ebb5108d8a97ce592","_from":".","_npmVersion":"3.10.10","_nodeVersion":"6.11.0","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"e704003fe7271f1aeb01b23ebb5108d8a97ce592","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.9.0.tgz","integrity":"sha512-CVFTJK37xSh0Ihb2IJ4nprCGb/XK7xXHUfoKyo8EpC4xUp+UW4wPBSpm3Qs0MT+s3RI9v+6rmj8Vk5U3b4M8PQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGSRiN7OoVe9ve9con7WYvqoMr5cROjDA73GyECBSsw+AiEAzV9f1QY9b8pEdIbUtcSQW7Uesom43ukeA+gl+uUoQs4="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.9.0.tgz_1505707384994_0.7922816057689488"},"directories":{}},"3.9.1":{"name":"staccato","version":"3.9.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","extant":"1.0.x","interrupt":"5.0.x","destructible":"0.15.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"a1ead86559903e93d63d7f2f43731ed198d5063e","_id":"staccato@3.9.1","_shasum":"bcf7159534e4d98782a385826a8dd2122de49c1b","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"bcf7159534e4d98782a385826a8dd2122de49c1b","tarball":"https://registry.npmjs.org/staccato/-/staccato-3.9.1.tgz","integrity":"sha512-3ljn38I9D7sLROOaOCnH2oeSZBl5xzB1omcS92t3zvnd8tsIb+DzWFdo/0k0O5a9wzUVVIXAPaGa0xnSQ3ACrw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDQ6xTrPrFcPGw92bLdhfjFfaFq62zr6SWAvacD3KatQAiEAgNpbFux6kffLxtR2DGiLdyBeZdn+JJACNMePxa8jIys="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-3.9.1.tgz_1505882213021_0.6950992734637111"},"directories":{}},"4.0.0":{"name":"staccato","version":"4.0.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","extant":"1.0.x","interrupt":"5.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"a50ea9d1786e5d15fcc272e9401c6e50cea737d0","_id":"staccato@4.0.0","_shasum":"1d13f397366f8241852cf975e946b10b18a35ae1","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"1d13f397366f8241852cf975e946b10b18a35ae1","tarball":"https://registry.npmjs.org/staccato/-/staccato-4.0.0.tgz","integrity":"sha512-WvATbDCIJkUKJryvmo0AY4stJuAYDZ52u+fXTEjCC1mU0G4eQ81EeYoH/d7RULBxClAbrtjqms6rwR26gnIXhw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDkP5oVFevEijJ+3zsi1FuX51z+cyr8fy3sfEQisupSwAiADgxMlH18s7CO4n9KdNk6c22UPzPem1HOblA5Dsghnww=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-4.0.0.tgz_1506447826486_0.3952757343649864"},"directories":{}},"5.0.0":{"name":"staccato","version":"5.0.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.0","extant":"1.0.x","interrupt":"5.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.1"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"17f67781536933f17fa4c3324cc9aa35a8bb530c","_id":"staccato@5.0.0","_shasum":"10562c08a45157d4b418b8977376d01acbd16b0e","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"10562c08a45157d4b418b8977376d01acbd16b0e","tarball":"https://registry.npmjs.org/staccato/-/staccato-5.0.0.tgz","integrity":"sha512-GBUHr7OeWjcYDzkIcyyhgLLv5W+sqNqWp3NV4mx9QBzGxmzo1+g6l7GMzw/3Nw3oSauGIcexxpVraHByWX+K0A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE5C8/9FCBe6KxwsEWTGton9WfzmlNFrGii2NLyOnvS6AiBjIgRwOKpd8dwrDBJFXBFw7SyipqQzKPJrOVKJs3cCqA=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-5.0.0.tgz_1506447834429_0.43097494123503566"},"directories":{}},"5.0.1":{"name":"staccato","version":"5.0.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.x","extant":"1.0.x","interrupt":"5.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"026735fb03883565252aee5400dc513d0987a013","_id":"staccato@5.0.1","_shasum":"52d26cc65a2c71804f4ad932111fc1c218716a66","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.7.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"52d26cc65a2c71804f4ad932111fc1c218716a66","tarball":"https://registry.npmjs.org/staccato/-/staccato-5.0.1.tgz","integrity":"sha512-55AqsAxEruuAtLjlITPTuY1ouzTgxxsH5n9tUPA2Smul2n7KV3+G4L2sRIpO4ZMefoYPNXrvBhxoj9wHTHWJaw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGF5b+7m0ne7Jm/GP5XFksBCOxzKuIk92bQasPVG8NksAiB5lVi0ZZUFdfv5nO35mCBiTpYGYk05NGdr1hPzoKyCSw=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-5.0.1.tgz_1509554259675_0.21821547369472682"},"directories":{}},"5.0.2":{"name":"staccato","version":"5.0.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.x","extant":"1.0.x","interrupt":"5.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"0a603745ba46fcf187ddaae1199264a9944d58e4","_id":"staccato@5.0.2","_shasum":"ec89d649e08fd06fe964d13d3fe5084246e99eab","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.7.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"ec89d649e08fd06fe964d13d3fe5084246e99eab","tarball":"https://registry.npmjs.org/staccato/-/staccato-5.0.2.tgz","integrity":"sha512-Prs39QD5WHypopq5g+oo2nb1UnN0VoPEBk8H5FckA3dCBPTA7AKvLuPgs2bj1uEBbUdIP5EsUwDx/YY2x9vcCw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID4Q96PjEy/aTt4CuFQl8BJl1XeagEDb8bk4l8jpf+FbAiEAoqiUfHrxoyJHwwyerglDxn8CW8cvukedmfHJdz8rXeM="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-5.0.2.tgz_1513205183701_0.29452347778715193"},"directories":{}},"5.0.3":{"name":"staccato","version":"5.0.3","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.x","extant":"1.0.x","interrupt":"5.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"02e3b75aee6a9096ba882a01afb07aae7576b20b","_id":"staccato@5.0.3","_shasum":"ce3a1aeb784a7c0f2db8eeee671596a0c1c577ba","_from":".","_npmVersion":"4.6.1","_nodeVersion":"4.8.7","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"ce3a1aeb784a7c0f2db8eeee671596a0c1c577ba","tarball":"https://registry.npmjs.org/staccato/-/staccato-5.0.3.tgz","integrity":"sha512-VFWZnjHpJ+X9LGYzh+hlsjejDOjfXbu/AsTl66qGt+bl6ZYdPH5ftFsou+PSUtIyFfrE4ao8/JyK4WQeUqJL7g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH9U/7eIa07FI3A5cAFFdUvKqZUJAcFkGjq+Hii8rNslAiAw0xBIeclusY0uDIWVlszu7Bd+7wdoZQi91EYQLj7Kvg=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-5.0.3.tgz_1515598015326_0.9689605107996613"},"directories":{}},"5.0.4":{"name":"staccato","version":"5.0.4","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.x","extant":"1.0.x","interrupt":"5.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"07770033b01436036147553cb1cb45b51d688736","_id":"staccato@5.0.4","_npmVersion":"5.6.0","_nodeVersion":"4.8.7","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-Zd76k78rvShGkSF/BnWOfzQqg2kpqb+Fyi4shAfpu7/fAczePX/zSep5xaswkyDduXOHJnmdhTc7vou0IkS0WA==","shasum":"1b0d6b4eeda1c658853a48d9a101003cd99fd55e","tarball":"https://registry.npmjs.org/staccato/-/staccato-5.0.4.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGQmjqL7aWQFAyU8tDsjfq4bXCRRq2Vkr/4G1RTkHbckAiBjlrNFVeOSrUJP+Xu4KKTmp7vDCtUxM8SRzgrNAQZefQ=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato-5.0.4.tgz_1515640317020_0.6906223380938172"},"directories":{}},"6.0.0":{"name":"staccato","version":"6.0.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.x","extant":"1.0.x","interrupt":"6.1.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"6a593a5e2c57680f4fbca27ccc46596fbc9b8de4","_id":"staccato@6.0.0","_npmVersion":"5.5.1","_nodeVersion":"8.9.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-KLsXukaZSPJUnpeDUqCgbP9gz1bPAr0Et6pp8jUwilz4lwitUd3i6vsX+u25TLvhiRiwzRJbwqtcY96iuPOWwA==","shasum":"e9eea5b62295ea527e0e01baae53a0fa183dd542","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.0.0.tgz","fileCount":16,"unpackedSize":16180,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDK/NrwiaP+vYgmiG3taKBadTBl0RI7pdfNJsPxKKRBUAiEA5LJtYObQ8sSaL9FxR4iFWyjtIKMjMLlD2Che7ua0VfI="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.0.0_1521501237019_0.9164519760554402"},"_hasShrinkwrap":false},"6.1.0":{"name":"staccato","version":"6.1.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.x","extant":"1.0.x","interrupt":"6.2.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"a9dfd5237cd70207d3512392f9d5ec6442c0ca93","_id":"staccato@6.1.0","_npmVersion":"5.5.1","_nodeVersion":"8.9.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-FYrmgiZ/u5ezEpm7JwGCCV4Ym7Zbfz87K9LvvzmB8c3+59VKkWfx+QLRM6lTn07Ywf2jbfFVT19XotAdgHwuSA==","shasum":"a585ab72de9f5a34eab7f302514ef76343cf90be","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.1.0.tgz","fileCount":16,"unpackedSize":16180,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa1rpLCRA9TVsSAnZWagAAVukP/iblwdO+6Jju3RWTf8EL\nEDfEBICJAFZbAnFQEU499OtIcAONmXug5A2O4TbaB/+dKCJR8FoGA+VAlArl\nFGKmIYROctMrfZmhcWVStZphBbYpHULXZghR+smmMWdz23aR3TZPANSu7SnY\n1fsY814Yuu3uBfOVDyLyBpCHc0qS846Z9djT3giCtlJZKlbJWiOAIM/sCSbe\neFDm13KWzubHbNMfBB/K88QdNFNEqYTcA8YH7XCfx6QBitSkULoSXwfVDkkY\nUH8ETfA5sFTS+dpGzbfHvPp+JscFpyWm5hbZPDtd8FQgLx4bjcWYRvc8e9g4\np0unde7EZGwCIYOCt4HGOMjspjjTa6YKmo+NCiihH01J8onGQl/5oXMJ4w1L\naSURh85AJai7KVpK+SUaERFj0Q4olXSeS7POdSwfwx5O15Ohoo76jrixPk7x\nqh6/rA2aylKiPYh/kLClirwKoOWafENoKqo2kPq2IE/bCTEib/FORcbE75Bd\nqK0Tiw0OA/ztJ0Y4iznZxt8BfyCpo7TL6FHK1sYxtezaf56nmnnsr/oR4YT5\n4fV+2w4ksHw1BJRROgCWiLqMkT5GEWxNEjJoc3mxs6OEFQ0pPHaI4+Ochwni\n31kmm2TRQoadvi9dgT2VPfohE15eNcvxuDHmWGit3UT1dAveoTYo6GkfWWON\nb02c\r\n=qy3R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDP72GR6vLF1iG4Ag25V3FmdwLA8DLCIhUVXKqvKWq3kgIgc4qf/cx99nMrmka8bxuosso00uiNZKX3ws3x0EfWqT4="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.1.0_1524021834827_0.9207360378454705"},"_hasShrinkwrap":false},"6.1.1":{"name":"staccato","version":"6.1.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.2.x","extant":"1.0.x","interrupt":"6.2.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"c1826b63d7029b20ade1bf9f5093f08c27b9971c","_id":"staccato@6.1.1","_npmVersion":"5.6.0","_nodeVersion":"10.1.0","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-lTKROgKySs2AWU4DlOuHUKjAuT3uVqFD3zrT2NW2eIWIyR0iW7iHdPR8x2LojN4lolYtDSFYmizB8TbxdvAq1A==","shasum":"f60769121e602690b86b5401b6cea545f7420dd6","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.1.1.tgz","fileCount":16,"unpackedSize":16158,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFvcsCRA9TVsSAnZWagAAWpQP/i4jtciazWXVBnBlXqCx\nzMDe/jdZ0yLYZRbNsX0+WkM3x19TYvFjOwx6SwyvRp/orWKwqDTQkaV3+zBS\neIe3QSxDxsH936rhip9r8grfo1LLXZuQOss/mVhaMniTLUi6S31cE1xf1Os8\nu+00buW1yau58rn9wukfuboBhCEmkYC+v0Hz/3Ys+ayySWxATdneDiN79v2Z\nLO/88NyV+v1xqBnARtWI0Ntqa+AarflaGY2ISndsRabDlms6UfOoRMfpbSTz\nRpJAl9w0N3KV1d9hjPKLVOk4HCyUOuBXOrZ+7a8gtD6yzaNIATOdXrlV5ZeZ\nPYHhb6g3E+ha8e1v7W6caK3kvODQcfVxIIGEndRVf1yWD2E+nc54hhL5qWXf\n89Y7/Ot/gl97yqPmXXlTKxwR1KJXflXaeuM3M0CfVHMiJ4YJmYokQX+1d1TY\nJ5QvHOr+GQdEtSr2fCnFI2JLWK8Ke30eJ4bqmIa/4IHAs1gO8dkcmuTIi6FO\nJrW52+VpjSEPXsgbzU/C3ccczzTPswneozzgpqAIMUtYHw9woR5jtXR+271Q\nuHPtpVhECcbPIvj4n1VB/UoeQjsmC2OL9vxT6C2viIEfsJXhHFLjDiBzn94z\nBqTczbJolqbxNbHG/aOOyoyq+NqFnBVGkgk1M4VCD9yMXKwYVk2tNURMVNgF\ndE/f\r\n=oTHM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZwiFSXHxqdEdHuPB4eRNOfzlnmxMK6wWMElItdXODaAIgZL0Bq9Mn9N7LZrreQIF7ZCvBqHBR+gqGUi4dFdCUbiI="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.1.1_1528231723848_0.7689627247423272"},"_hasShrinkwrap":false},"6.1.2":{"name":"staccato","version":"6.1.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.4.x","extant":"1.0.x","interrupt":"6.2.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"b7ceba1231c6700043ee9e2d2ea187580bea1e3f","_id":"staccato@6.1.2","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-lSy9RWdwS9FIIuWxcTHLoYgX83Ty4do+QXBFeJLrOdMex+uFPXfqFTq7/6NrMKJcinN5ho59m/zh8yZ8QTdP8Q==","shasum":"8315a24b8bfd2191617654b0de05e0172ccd89f9","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.1.2.tgz","fileCount":16,"unpackedSize":16158,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbJsX7CRA9TVsSAnZWagAArzcP/3vazV7agntA5ppoNppy\nMehoP6lWgaqgJzk3V2s7k81E4gWW26BTqBL64ucmhTnCRAfSDSojd5Ra/nmR\n5sKp86eRL+WK8pM8/nCRu11X9MsmFoYfC0WOOKp0wQpRg5CD/QgeEM9oO56C\nlumYGmg9thLJ77udE4b7b4grZSM2UcqEJ8oq8k+dFsAakHrzQ51sPcbnGY5a\nNDKwgoFiuBzXyKHUAL+a6yH89Gt0pZoDc4+ICclbsqPEHOo+N+45D0fTZVBU\nL3N8D4cxXYJjgcKsIWzfI579njNev24o455JnBY8xOLhVf7JbFDSF+FeocE9\nq1V/Mbkodv1HpHdBQi4a5UkysDK5wynsQK9GawAZrGDnVZRuhcl503YoOyup\n5EU8xg+w8uWoyTt00fmkZcCb75uR0QO2g+PlYtGwh1vSvfuxHGd1M0TkFEf/\n8ViuwcW1ohItj7kgzLMUq9i+I4hRHdgRTupnAQFiDpS5e6bvtI/CqzdyOFfX\nec5qd50RKkUNJDlz9FJVqQ01+D54zQ9N5XsozHghi4nJeZlUE6zBrrIkLku4\nnaGUfxWNEHsdNx09Lp46aU7hF+zemeuwPbsdilIn1UciXpJliwju8SHL0K57\nfpXJIYtQTgrZoN+l4ifOuiBNW1HqnCZOjXLhV9xJ17dwfyasx5q9auVvn10F\nAOsC\r\n=6FMQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCgYlJLfbLfyC2lANXT3w4YKKw5eNOvMMf7WRKpeaIHZAIhAPRnCJnXgKMVjHamAQs4lLiilQfEXiIMeGJH7/OvZsZO"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.1.2_1529267706918_0.4756318790621503"},"_hasShrinkwrap":false},"6.1.3":{"name":"staccato","version":"6.1.3","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.4.x","extant":"1.0.x","interrupt":"6.2.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"47d316013238ff55291256002d8d10878d855fdf","_id":"staccato@6.1.3","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-DgNSgknkZC/1PCrrt13IbgyoiCyhljgjafl7YnlUP1NAV9/wOF5Peyqr+SldXYIB2chpy+yD616J+MbUKLibgg==","shasum":"23779755b9e577e0c9d3e2dffb9b236b404e84e6","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.1.3.tgz","fileCount":7,"unpackedSize":9787,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWVdCCRA9TVsSAnZWagAAe4kP/12zpMRkp120EdcpGss8\nSWJyvrsLUzHNPlWqaMX7QjFGzi1ooVzUayS796LHAIqD9i04DCllM/iGe9bQ\nokrdz8dNJ26pMPAGvmn0eXyMeVSn/waW4k7jp5VLfDPNmFgfqDC3C9oEo/f0\nmv5Yg8Qus68jg4f608PLSpiVEZXyIQvajsIxxpqH+A+Pu++2FrqgghXwoWVC\nOXl8JeO0rOv5POeV/RSaUpFDIGyMfGA2b86b0GVrKvH8PdiLJgZSCUn72N3G\n5BWmqIKnYzOhuFnMoJpXZ/ABhITb3sfwo36TJuYp+ut9yCQWMqxpT2ZVFIU1\nWvn5StAQRxMzIr/+ZMRreZ/w7UGH1TesRO0h1U7k00dNmGTvBtzMx6Bon93h\n+LZ+d08lKIaLvxg04A59/qdz/JASoyxyOxM2PwgOoMA+f4vln4zL2ABXtk2m\nHnNjVRw0n3jDuI3F3rV5oYpUa9Cbwd8ZHbc9Gw7hTdsPiZ4S0E9USqF5WDvn\nk5K9ADT0T/reOGJjcy6u47XHt/CyuduYiHWnTH6PbjmrX9f26+DDPRDZHYib\nTNFYW4t/ULy/Y3C5G9XsvQYjQpss6Nf2YQjARWjdh0yDyo3OUSlVfPKpTuBG\ncISEgyBzEmgsfiyO9mzXAsSmljLI6XD3/v4sUa6Hk3RkOlrOjqXwzDt3Pr6b\nNHwg\r\n=4onL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGxrp3Ov15FUB1GqStQawZ1Zv7XomFKxQ23MeMSZlxO7AiEAhgrANuZbTVxEogVC3J5LoF/DRx5WluToXmOkidlU8QQ="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.1.3_1532581698873_0.46054909437509095"},"_hasShrinkwrap":false},"6.2.0":{"name":"staccato","version":"6.2.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.4.x","extant":"1.0.x","interrupt":"6.3.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"cf35497d6aa791c5ecd94f0babc32cb9ba775581","_id":"staccato@6.2.0","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-bxPWfHLtPaA70Bq+GNFAM65dseq110GaZnHfmfhxewwkymgG1nhvvrsNwfHmpX79xXuEN5U0IZvYWVx/S+fobA==","shasum":"3ec00daad4c893d2ded9f24ea80131f4b3286395","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.2.0.tgz","fileCount":7,"unpackedSize":9610,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbb86RCRA9TVsSAnZWagAAytsP/j5B3sqLaVxLBRGuDaWU\n4CJCrs3q4L+bJtwF0K0H2Zu6a65frGHJe9AWgYX7fp4uLwYbLtN3QoiBcsih\nG/PV8Bs+8QT5xMVjGY1KPkFv06TzngNWKkKTPcjfCan0r7myQE8jvoqaZyRz\nj7LfC2u1f+2/21prG5+uhhxGzCXGi8ZehFgrJOl0WSBNspTnjkd0H2tfvNJy\n5fyDfJ+r17/qtSTQ6pRSalxWHHzAgo5z0+z6RU4qdLiEyLInT6dn6Y0r/KyH\nDWdjktErLl1VhxaTJO0zfn3rm+SHr1evagbi8IolhsgJ9LwnliPtj09xZc4d\n1QoXzVMFmxytUANwen9hhIBHm95PLeAGe2+JNYNo/H9DwVrMunTFqEDDBkdh\nvdLdQouTt7bK2uXV3W4ZKLW0+C3+NPg7yUFp43gbl00OVCVQujx2Uaz1l+BI\nlk5Z3mtBoTA3MODnxeoa6Oa5/Mb9bIK+07Lut9HdkfX5nF5lCOyA6mPOfyPo\nzGrQVAwbJdEFMd8LwVe8LuYNFmgwp4hlziWzTl7gHPZzBguQMIxcaQaV0sQa\nHhF5iyRXm8gv4BytFEiRvrwr9DhnTQJG7dcKkYg94l3bivyV89hB+nxdViIQ\nTD9xqris1EZ7pOW9BlLP5ZsR7R3adeko+BHBRXQmgcty+Cyqa7sGPCjGgH0f\neRX5\r\n=OJa0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICaNoKD3Zillj9YPfX7vCkoGgY8kS7BdDMXdeV4D5ChnAiBGH7q+RitjCRO0qTK0ZBE5eRLNenTx4ttxUJSMk4sCVQ=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.2.0_1534054033171_0.8162103467815123"},"_hasShrinkwrap":false},"6.3.0":{"name":"staccato","version":"6.3.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.4.x","extant":"1.0.x","interrupt":"6.3.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"2f37907293dbe75dc34cc777fc65c003688fafb5","_id":"staccato@6.3.0","_npmVersion":"6.1.0","_nodeVersion":"10.4.0","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-t0L5SVDXPj1pqVu6MkMyraDV5db5hDoBqpRFdFZGGR/dbgQDn/Hs3qA7adu228YN6xWHgIpXFXZhUk1duc4Rzw==","shasum":"18b091a5cfdb75a5ebec9c98679b2741d9ffbb2d","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.3.0.tgz","fileCount":7,"unpackedSize":9608,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkK2hCRA9TVsSAnZWagAAohUP/1fCWUzALAJzTLh31m7V\nIsFOUJAYxMi9BStvBAM+YV5wrkyp95O7/Ett17K9F0/vodChK2P9QcncFahb\nmjcVLyaNXbqbBz0WP/RFFxl+25CZtc2Daa8+ypUQZd9fSTBn9ci5IirIRyom\ne9ipJdcgwQl+pyOqjJuI/EAMmkxwsCbrDI3jQZPS0eNjKbnzfR4Mu9Ngsv7U\nBPyp2D6uLXLMDmVMHm70pr+//232dfoPftgTqTK1J8ufxgkQC6dDaibTfMPG\n6x5lu3vPxD+ds2or4VoVFuVljxUgl/lYNmEHZuKzs+Nq5mtSgrpfxnvZ8Y0h\nTOwkJNxLS7nEnOBnXBs/gbmDAGzJhFfpkdGYdqx/TJiEAjovjesTVUYNXyGE\njN0vUZKw8EbEGBT3CYIGtlF30BAQ99Vql4cICMvu0I5to0GTDOX5oS9eRVW5\nnP3zVDLpZmUAWVzLCnCg9IWqSW8nx/Y9lih0OsKX5bw/ive0glKHorpghbKA\ngDV/1sIefK2tcyir5VRn3nhZWtF+Ttb27kZurceCmootYAnDUiigH6ZIeF1O\nzKa9AhJpMxOMgLLjdVQzxK2qturR8r6LG6pLJfDRsT3uCI8Jn+CylHw6L3iJ\n4aS/+vgAV3SEfrpkKjLNHuG8AhKGg9HYdfAFV6vxavkiBy35Xa6jkVwHdy58\nZ8ri\r\n=ZkuV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDNH/MXx0dWUDSlLQM6Ow+XnOmERZ2Dk1GhHZYyDS1YZAIhAO1H60We4gdBO9BmKxqDaIeCtFgoeqjfEw4Z9y+KretJ"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.3.0_1536208289334_0.7301564378097898"},"_hasShrinkwrap":false},"6.4.0":{"name":"staccato","version":"6.4.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"1.0.x","delta":"1.4.x","extant":"1.0.x","interrupt":"6.4.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"3.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"f650bef3b49184942c8b213b05ee40d32546c7fc","_id":"staccato@6.4.0","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-hOSuN6PSb9HfxgkPV1lTZaKgq3SRZdkFQFhzcrsgWHi1DOnFz71CH5eoNT/EDKyemZ3s53BNBQ0mly/so803ag==","shasum":"7b22e5aa42489a89ed9ea3e675fd2a2aa4882e63","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.4.0.tgz","fileCount":7,"unpackedSize":9608,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb8gw2CRA9TVsSAnZWagAAMwsP/3DO+tnqOvjg/AwDcipm\nQMySussuv7GHwGSnm1SkMdy74V1M3dCqloIfoGpYpUEKD5Q0wLNJ2MPZas4P\nEt03vhiPqXQ6UT1cuz2NlkdJcnFqP55NiBl6HokOiIB31K2P6ZGegGmJk1cs\nDEl15QvHq96PHSO2CO65lEBAHSH87KJmjrssMhMYkc2V7Fik0JQYVjOeC6JG\n/0Sijk5sG8MzHbwo2o/MIk7UEMcPOlbNYAzNpvn52aNFLSNKl8LSpK27lFgi\nSfCoB1OTMem/qja+vVnRa2sWuxkiNgj+pk0I7vagQe2o/goXPICLiGRothx9\n9ziEu3xme1/eZXv7S4HBY+9U9uUUlEl6+8CT7f/9KIiL+VoWGjkBLGTHVHza\nzFAJsC4t9qyE+gmxiyG0HEqNgVHzMkOyu/3CeAKx8VkBsbKcvwgjiztgxP1g\nHaZJT5SFbuFTF8DhJcc/s776+0PkOIhFeyLSna/XkzEHl3YFX1GeC86q2oZb\nAZsuso67VCwjtv642vmXiq9lk0pClKMvWgcEYyIKJCqwQZRTLXQIZOYdwVR5\nskeImyxU4zH02yHdG1SAMW0V9Y06Lsys4W5zQh4K3Fmdlxw3Mu6R0vbWHimp\nl3lCCAyyiiVxP43cIYDvKfkleYZVgC2EEzBruKlgl/pJ4PpSOTdLEklowOQE\ntn/S\r\n=eDry\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIqELQVPtlMttsZCHuNaS8UGbmbvl6VMmiIqE09cUYhAIhAJNvjlTzEWteq8R+2udciEzXQoQSPDm426WOBcCdvmmn"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.4.0_1542589493718_0.8085121814817009"},"_hasShrinkwrap":false},"6.5.0":{"name":"staccato","version":"6.5.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"2.0.x","delta":"1.4.x","extant":"1.0.x","interrupt":"6.4.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"5.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"042776965a708e56c45c4c1d8809b0633f94b223","_id":"staccato@6.5.0","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-rrT8ktteyIiviPkHO0KFdMa7Q2zDpptzUqkB8+f3gMXTDASB2W4nAwAf3opCuNblLq5KhupmVCn4tU52UUh0VA==","shasum":"d9f5b5b964bbd676e2ed6f07e0b6bd23e8d11256","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.5.0.tgz","fileCount":7,"unpackedSize":9606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+l92CRA9TVsSAnZWagAAj20QAKRPvZYZrtKc75rcE//E\n93zireyGeYCCd1dgwfXzJQbOHj5EAr6L7BWv2NU9nJmRJU9vZPorVb7ijbtF\nLN6a1BVjsVksvLdt7T29goeXI9QjvYwBGOo38dBPgSgHkhVPfk9bEeZcg3gq\nMr6OIT6hzrA6NI5vW8ng2WHYYcFYp7VD6ngJNGqrQtsx2qOGiJno9V26+COy\ng5HGvaCFj2ILqNJQR4GwWp+AKQCpiMiSjEwYtOKAg+Cnm7w7ZP11QFRByx0f\nj9/3j672bE1Alm4LTcYmr1KP2NzIo7ezWWFMc1c/YvRL7qjUdfjmrKqJ+vRH\nCB+KbJihiZA/B2fVJFb8FQRps1FkbZL5qno5pVozzq+kCOzc8wQl3irANSC9\n8XkyUiOkukzM27UZZKuy/syodpRFx73uXEHZLKgSWK48TLKI3toaGcKCc5Yx\nMxiuzNISEwldf9r8FbCC8SfUIr4jtIaA/E1oytPNbtWYpJURbBRE0rIyzZOo\n0LOvHfb2fq8T5iMHBIsw0X3Kumet80Mbumsr3fimqcKDNCWbS+V22sjrq9ZL\nU+gsQnRmE8olMVzBANIZ8tQE25JwKPlQyVJZAvvBPxQEXdZDBlZMKIosJbxw\nwQbltwb810f6cTkqkqx4P3wjrN9ea3JLLW7US/OHiI9FS5RptyPksN1J2I+q\n/nPW\r\n=azxa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC9t0QtsGjhWOsSgf3+GMBTsYrX4Vi0P6w0Cu0onH5InQIhAOsSJW/N4nSBrLfg+6U/LNdI5UC1rYQ+uOV4g2yGmf1D"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.5.0_1543135093805_0.33944178203251485"},"_hasShrinkwrap":false},"6.5.1":{"name":"staccato","version":"6.5.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"2.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"6.4.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"5.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"97dbbec4a45752f8b69cccb20198fa0d28bf191d","_id":"staccato@6.5.1","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-3phs+fMoEk1MwiQtLzw9BTAaQiVBIw5nzoZgnoE8LXhkZCuZ9w0m8KEPtuG7GbB7zW/r0+gp2y+rSkwz8YbMqg==","shasum":"33079537ee253cb79a1b484db89121326f58e3df","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.5.1.tgz","fileCount":7,"unpackedSize":9606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+pC7CRA9TVsSAnZWagAAFgIP/0a0tyOARYhsL3RonW0l\nTqlsFDbtIYNlIbEPRIwP0LJFR3oV3uBl9NqOpaUQDRwVpQe1IkwbWNLbWJZ1\nZlTMfWi9NXvtNUZK7IwxNtiTUfYYV0ATYgkNO/QHKw6pJ0Dm+ZWFXis7sD97\nCL3oQGHSr6Tye7asYGTkwKrgM70kpgTDtVEfcksDh3fz2j+fdLuwrY3RLgr3\nt1/3d7Da0ITXTuU4o3Si+azqgVdu/TWsNC9DfEFJ9TqJf8tMzYE+z/iLfC8n\na/QHXbLrU/V8sPYVfZG26MGi9kg3TtSOSdqT6fIBRhwxL1BYtJXk5zIAk3gI\nE0qcq5Wp9RmX7Cv1ONB6FVUqtvGEJzxgYtejK0L7VJr0xg/ZsGq5vrffpRRP\npk8qtLsAl3oLvGcJ2jSyFMZuGYmv8swLRlwqEz8ytq8iT21vKp064Tf7fyh0\nwgwFFKFBmRKzmp8tZxUsYsAsPn3ONXcpVTRUMkc9MAZ+51ox2BjlsdJZiXWi\n7Hw65HtIQdpJ6DqXDZkIA357ssh+decVQw9navOmiwGhZUpMyi9x101QGanW\nDiXhHDPUElThjQZWc52KLxM9Ea+8Dg5q2htPp+vQyl+Fl88AkKndFUfGxR0n\nwP6EknIBCCPPCSrF+/hv/SBo1t4E/fMwGHms+V+sP69XEyzXIkcM58A2cQxV\nN8rM\r\n=bkDh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBPRg6JlnIzOUnWu7qcnnXwi8UBU2+k2BtCs+g3/U39VAiBb5pqkQJn5x1t9WOs6FBuKk/uOEvsvANJs+++jRbU7nQ=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.5.1_1543147707223_0.18252491797309167"},"_hasShrinkwrap":false},"6.6.0":{"name":"staccato","version":"6.6.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"2.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"6.4.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"5.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"readme":"[![Build Status](https://travis-ci.org/bigeasy/staccato.svg?branch=master)](https://travis-ci.org/bigeasy/staccato) [![Coverage Status](https://coveralls.io/repos/bigeasy/staccato/badge.svg?branch=master&service=github)](https://coveralls.io/github/bigeasy/staccato?branch=master)\n\nWrite to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"08d4490fa0caf94d9414086b92cec46a09e9e36d","_id":"staccato@6.6.0","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-FzOlzbFtDYcYVyJYAgUU8Qo32nqX/o4YSy1ZvN+C9AyvMon7np3Uq6eqV71e1M3Dl7tl+YzsMH8HZMgyrJ9dtA==","shasum":"d68090ae638ce198bb6d34a402b661c5c06aa1eb","tarball":"https://registry.npmjs.org/staccato/-/staccato-6.6.0.tgz","fileCount":7,"unpackedSize":9635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb/74YCRA9TVsSAnZWagAAn2IP/j/jJ5qAlNdxq2j72uNG\nddx00aRJ5TzQ0rMoNquwN8jRNDNsNcC2NGwb9ugH/wQqccwGGpGvjyHCCf+c\n8u0HmKAKI7CShpCO9i3PnXo9IZTWnf7IyTKFB/j4BwpaDbA95tT6QDMbadF7\nwYCfwSKuU5zrJVb3fgdgnPTkh0NwcfwXOckzVp6z2KuAVHfDojwQgLIfkZAE\nMPKSZEZE/rTLeqjsPWnXIYy2vF9V5836QgO0rmdF24EUDejaHx2HNLcxE4f9\nDdi2WUBojqRonZ9kZyCChqvY1ngwLngtaRgfoo4I+ktSz4gfZoBdHQOpCrN7\nBPJenWVCgmldLxJ7GttqgPZJl1LCLrXV9DjRrRaSY8+OkF+bj1+fBKJepeSJ\nJSOiuOrxC3WdM9J7i6vp3BkmIH5gxKhr5NXembm/aYaizOavJvOVQ6RxygLH\nf2P0TtC8RUFgjTj4AoNPytyhfJ4x6Lqk0x6cfI5v+3HAw+2ARNOyX8tkE1Fq\neQJu1glU0gAUBShhuqZB5PTdJvgB7OV4xukW1Bxh1K23snl2EzFKZ3E6draU\n1ydRE+EYZOYs++r5Ml2Q057pvEDodffY1zn0qz8PWGAB7DbZVFAw8wP87NCT\n7Jp8O3RiXd6QnCV1SO/8P0v94SIzEz2l2LZ3FqyTYUVRhSXy+BN9MsP9h3Dh\ngmc4\r\n=HtfW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC1TFNfjbnLGUEucKDGMrobIDSizaoYp2BU/BuTIR5tSgIgNe6hCfrm6T1yp9i/08JwEYAkLl5PkEGZO2ADsQLfUAA="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_6.6.0_1543486999912_0.19564265698960925"},"_hasShrinkwrap":false},"7.0.0":{"name":"staccato","version":"7.0.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"2.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"6.4.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"5.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"82a0c2efde7fb4981e58e14d34bbf750fdaa604a","_id":"staccato@7.0.0","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-Tt4DMMX/sWTvkz2ga46YicOro3cQI6hAwQBCUk7JkjJTcluhbii9/1yzWSsVChxcbnAXd56zBdSj9KYIQySPow==","shasum":"e386ae1cc5eb5cab03de6aa8f4de8ab05cb34225","tarball":"https://registry.npmjs.org/staccato/-/staccato-7.0.0.tgz","fileCount":7,"unpackedSize":9635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcG1NOCRA9TVsSAnZWagAA/ScQAIQHVhrz0phzzawdbklp\nh+7sK5/eZqfLo2/OFTuk7LI/6xa1Pb+vobZ4l8Fy3qltRzBhqYefnq38faMT\nXgNVi0s/u09eupfZVBgrsU9ZFDojjyFCRcxo5FZGTVEYxnW5JdUxTcvCKbtH\nrGdLC1od1otkn5345M5XQDbAdtNYxAqic5CHCeyZv7m2u7liDr6HhCd5HhUW\nj4LFAiIS8Xu2r2mUIYj/8btLU09OkJz00AUn4AbSbbg8oEeE3glDHUDnAh00\n2EcughahmgYYChD6EEwhZ0F5l2dtsrMln6YbhpAOLdEwYv4FbjnvzcHSSAJt\nbfOrRC5kbphiqigchA1apuZfIVE7Xc4tS64vNWXjoTtC+sBlHPmgzu+gHIAT\nMaFJojKpCeUkbJjgXJ4jwn8KPCBQqpaHKb+a/FfBzHunBJc141+qL8uWS/ud\ndfajTBMTHKhig4JMW1WOAEqXUUPirhHBAapfB2LFHQnlDy84YGQ3leNYE8/N\nL/uHiFizNxmB89wv6bKZ/8DUs+5kfZSLsfcgZ9tECFefe5WHn9eeq/Cu7MEc\n1vorU8ztgS3kIIaLoqgIYlI95qwnHlPTPUuWTqe/FTK3c0XD8WfTin+zse+h\nusNXNbkcyJrnTHKRDq8S7P0AtNX+4jGwxzV23+iBVzksLM5bHW2t6khyH9tI\ngX1M\r\n=MEV/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHtNvxzZeNqbelikZ8xVJXrnYwsXfNn2L3GJCNErKgOTAiBo8C3adXF4M2akpbbQzl3IszXOQD1/l9pjKenGUCaVGw=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_7.0.0_1545294669732_0.1601298588309965"},"_hasShrinkwrap":false},"7.0.1":{"name":"staccato","version":"7.0.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"5.0.x","rimraf":"2.6.2"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"5f4a3900dfeb1888b6db62a954ddec16f61eda1b","_id":"staccato@7.0.1","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-leULzypXDhiEBDevWvex+C0DI8wv3olMyuRPOpdn58WOWuxb3RmeaBMFuzR7Jw/I6u0PlFnfU9W054MEQD3GWA==","shasum":"e9f47e7e6328d2f8ec4daf8a31069923e6ace336","tarball":"https://registry.npmjs.org/staccato/-/staccato-7.0.1.tgz","fileCount":7,"unpackedSize":9635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcG1YVCRA9TVsSAnZWagAA8gwQAJDtGX2oGb+uEQVhu0JX\nUvBc/xoxluPx3+NtMAw0ohDjoT0QgbpgoSpwbroHyTOpfxO8t+fb2rCD73Wi\nvj+J9wO6ITZJSRj5rHIDc1pUx+IIRBirxFTSaMZ6e83N4Zo7M0S+YjtNnNIb\nOrreaQ3j8OkqG4qchtb3gklmLWewsOEojc1OJY15+rEy+/iVwuJ07VHmWvbl\nLevcla+2g3KysavPUNSfNFVrQBPWaERERL/Zw+5pIfuMUNHzaHnuIG2BCpSL\nCARUgA9oM+Jf2y3vgvH3Ke2tAOOrUuw6cAsnWwINgdjJOXGIecsbecoGDLbF\nSWC8ARGG9/KJht7/sIxppp09BtfFg1AURi/IY76nJrkajQ9RIT3Tn/gK1hqL\n3nFy6+6K9zVHW3Vcil6ttfRAxhfBtzxP9fzfalCgR9Q/Kdztdy3IvIK7f4Z2\nIlWjWFKJhXySIYoWHgs1LwnEkb3gk17Eo1jASJxpAIIveWSe1Y8zIxfrYXuu\netcdnv56ndM50nBCic2egy5FwT2vuj5YKa1IAuSsT93XSbxkaZXVAMghn98X\nklk1neBC2ZV0ZKaAraBRCbQF4Fx6AUjeG5plOYDzHwK5Eix/Ku4Oz0p1eeD2\n+qdX2sujFO8Fg11WkxT035M/bb6RBDMD1v8/k6GsiP2ee/MX2FxI88yFa2CE\nin1M\r\n=Q9le\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDPRb2WOQg3zQKTXWRiUdE9HJVcdbW94Xp4xwAInC8mFAiBJguf5pq8cJFRmJnIMVqJC8GzyoRPa3u9Om3O7IBm4jA=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_7.0.1_1545295380644_0.8245666521936721"},"_hasShrinkwrap":false},"7.0.2":{"name":"staccato","version":"7.0.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"5.0.x","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"646f08110f7efa09acb65b5b1d613ea5503ad283","_id":"staccato@7.0.2","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-6CrS9elbHyJVfOQIkVRPqAXzWExURhMT3F/V2KOF6UV9VLgfa1TRHEymF9+8kKu+RMTtw1v3ssXz77djIsyOqQ==","shasum":"f1f0f02a777857831ad6aef883ea87908129ff6b","tarball":"https://registry.npmjs.org/staccato/-/staccato-7.0.2.tgz","fileCount":7,"unpackedSize":9635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcLYIlCRA9TVsSAnZWagAAJF4QAIq+tFQFBHSNeGCSWXa0\nldEK2cWF944hIvIz29OBb3ycFre+ztoUnM4JO+/BcXvY3RlHNpmQ3x5jZ/63\n4qjApDOI5xPFL9czOZMIdxqyTDsqUhXpRHapwSk72PmcSxsWELT7F+jURIej\n2i2cjgMZNi8+DXpn2GEXZg0Ji1jjUVSngFdugpM2O/uflgiT1g36jWaqB/Ab\nCmdM9YW/XYH67Izd26WRBWuLhMM1d/kUMzEA1xVTXpalfg1KSIYVKs64AW4r\nGleBoPUgZvP2C1+KbmRXnh9RqPxaPuS/R1UaivyXTnpHLNn8R6Y2Rt4V/iIm\n3lT4ZUKojAmGLp2DBVDHc8xlXMxh+m9hCmRbO0bqCxYQY8pdR+KRrXf8z99u\nRs/d+3c0rCoAQ0wSQeq4/EN1KsNMp7V2klqlhi21wjCX9gaZotj5ZVZNindO\nvYFS/0sWJpf7JbESbPmQ2g/yp9FaSSJcOw3P8PV9MkXLOzhXBBT35+jEbWe6\nJujWg5Ttq1KvgUiO4ZqRfj/SJRPxVepMR7h05S7EPb4KEo1ZQDBbj0svC0y0\n5LToUECqy4u4V4dEJ/2uR4FdJ0RQQbsT4apQGQ8YMBuzYzaDmhslRiwWoSGo\n4cEf0wFAuUcrR3rRHmml+ic2CgBxmXM+18rn7EMx13BpWTkXgtHTVzO642ga\nVMpV\r\n=C8Wt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCpqty4/dO7MJuNvCX1x8KzKxPSaaICSN4+66kRyRKFpgIhAOg2l8cBKwqnvYeUVu1w4d6zEHKKWE/lShs/lo5opQcE"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_7.0.2_1546486308559_0.48794022673729476"},"_hasShrinkwrap":false},"7.0.3":{"name":"staccato","version":"7.0.3","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"5.0.x","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"9be370ff0cd6a66facd5a4a1772c30c69824b7f7","_id":"staccato@7.0.3","_npmVersion":"6.1.0","_nodeVersion":"10.4.0","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-QZePWsGmG7NLKi7dXNGY2ANkRiAJL6rOivhk/3hh91XBDvscwnss1dmZkhUenhLRkGUXl8j9NOhP6wSAXbN30A==","shasum":"fd14bf29dfb869bd970bed21b07dd61427caef86","tarball":"https://registry.npmjs.org/staccato/-/staccato-7.0.3.tgz","fileCount":7,"unpackedSize":9635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcatVrCRA9TVsSAnZWagAAT30P/1MzeyaWrZtbLyoHzE5U\nwuOsN20k7bymeQPjpGY/OZrUOFURTKK8USc2XimTy8+8xE6NKvzkHseAOveY\nY6nUyAZbVOWQDH5R/rFguhLMsdb38zG2QZLOKvjDDqReNinBQHDc9RvBk6Wu\nujsuJd8xhPpSwiK9AAQbcsrKW0hwKPbV25Z7b7ONHPkR52QQ9VDsaZd0cHAf\nkPFKuQLMHS3cujq9AqvCSfQZ5s4V49ZDnHkyRiA8NOXISbghmOwNQNznP1eq\nQjYps0NqUfgIVIDOfdrLt4OVsJoApjH/gDo2wPuGbhf2vFzeHq1DwaC9nLFl\nA3+AA5ikGLtI5VhFAdyjF4vXnPdT/Y8lVFig/4BcWbYpTETEgDe6uqZWsDzM\nk5xoR0Li1Yah0JUob70N1TeSIzxklH16dqxiQY8en+YuZwmOZv1J3eGA1wCG\n0rKsEwM8IImYj31VTfxO4tkZfJtDVI5cwqD7KwHu1ECH/M7htp8mluVUR8E0\nMSBAdNdhN62Gj/q4p68vt3v8O0paW1KcDt0fGRqx9R0fg1H/06dzzxxez2Ip\nB9DP6Cqb6j4mR1e8OeQpy2RW1hQxtsuc3QrMYXGAqfYrdnYUrTpYy4kA8j8V\n9d57tkORUZ4V6q/xY6MJxGpqA6fxi7nX8heKPLuFqNxVFmJ6220sdord2C+4\nryHW\r\n=AT9+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCWjj5CIL86owctfxsKzeuF/dACJawFW8Otqw/XM/5ZyQIhAIMg+FH1LyEgnLnp2tT7Zm8qrXSUgFCX0vwaclLTLnHN"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_7.0.3_1550505322853_0.45285490768282255"},"_hasShrinkwrap":false},"7.0.4":{"name":"staccato","version":"7.0.4","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"7.0.x","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"proof test test/*.t.js"},"gitHead":"58c0f93689219cea3440a565207cbd9bd282e6ac","_id":"staccato@7.0.4","_npmVersion":"6.4.1","_nodeVersion":"10.15.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-RSjCKcd39/MP1h/DAiOGzP0pxRmm+RNwQ18kKLYy/kFuo8IwzX76oizszFHjLer4WwqxQYeLkhnE4sC+CgUU8A==","shasum":"9201723508a28ab14a7755bf965f4c9083864b9d","tarball":"https://registry.npmjs.org/staccato/-/staccato-7.0.4.tgz","fileCount":10,"unpackedSize":13446,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcd6H/CRA9TVsSAnZWagAAHAoP/1PHlW+xbG9HpwhsFBK7\nnvLHp9TdZtu8qPLZ9Fnn7R5NtBgIKOR51E3Rk7AZag+Cfno8n/qoThF6Bam2\nOEaRnGQ36uQweMQgE+AN0mnFb00bkThKRiujWXaTrnwJmoKY3mRU8S3pybZ9\n2wvV3C3QKNpzfZSbrq3OR2vncLlV4B9fj5ay/U7CKXGUgKSs16WR212jW3Tl\nXmRh3nPm3wrbmmdusR4TQPBApTqU6n+/VZpsz8DJghcIlNY7SxqM6Kw4uhUr\niapVduRKitTIcUddUy+VhZWN0JzUOWonjBjJYMFVKcSvbFr27a7gTAHnA/Lb\nIlWbBwP7jpwNqbCrs3OuuLYCDeYRiou4YTQ+aT/4uDcaJbixMLL3BxS4X+HC\nZTntFWfjvmEdHv9hQgc8Bs+LY8N2VB+bCfhjP9dO1L30l2qseLd1G5Vnkz/8\noG1UOKm2W6QMDnuo/EBPouEydSJQQ1KbBJYVr6ikTPQFCXp2+7xL3mJumkhV\npNCEN1NvwSscOYCrtXN5c/xApoAWfW9l1cSoYp3lXx6ry1YzZR9mTqRAZR0M\nq0KpPqwLMNie8M3Hlj75cVDUtSUA5j4KVO2+L8cVW7hQee+zTEcfpyi3On09\nWdy2wEBjio2qs+VmKcc0RJWMCr9HLe04e7tBfJzDFt/cfsokVBHIdkYRyuex\n5KYo\r\n=Zaju\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAvccb44DIZcXgXXrnWCRtwdCc4M+PCaD4twVqf3WV44AiBTU+RnAviX9V1BAvPx92iy2dCLl7haOETyRV74VaPsbQ=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_7.0.4_1551344126857_0.5970071487681019"},"_hasShrinkwrap":false},"7.0.5":{"name":"staccato","version":"7.0.5","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"7.0.x","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"proof test test/*.t.js"},"gitHead":"94b4960fa5c5a3af2c1b5b8f0565eb2ed5d4cb19","_id":"staccato@7.0.5","_npmVersion":"6.4.1","_nodeVersion":"10.15.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-Ps3NxAoZ6Os8bC8w6Qvyhq1UrUfRlD6i/74YKNTGxpf7KHVNEvouaUhdtHR3o3kKdiB/A+ZXx1xLlZZeocFYCg==","shasum":"f94d8b50b40753abb3bf7a8d4a46b75c65261ea6","tarball":"https://registry.npmjs.org/staccato/-/staccato-7.0.5.tgz","fileCount":7,"unpackedSize":9322,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcd6j7CRA9TVsSAnZWagAAifYP/jg6I9SpaP3ggtU+wKZ2\nliZyn7HFj3mMACdkaTwwAAZgwm4dAu+daxGTh1U6V+qyuCOyldzQbM/RrQP5\nd7LARETfAtgUAs48A61QfP3f7sMUjzKafk/aUKVN5kBlwIN9WthelKS16eBC\nKgKrpJ9jkGElLzoO7BPhgL/xYu3ejguXNOjTBbcO3ENTlzq0ayImUe/Q+svo\neZV7CPGCI+R1KW11uSvPnRZpWv6i0dk6QML7XhW4t5CCmXIcjWKqJsEhkNf0\naddDD0l15hoOmVIVHE+MBB7jMHncv7+YaBbZhT/QkArewsF2zgvbB0pSedPU\nm9AYejVBPfHfk9Z1BAHqlhs3i9Mn07Fv04UsnFdt0b3HtRVw/MT6vs3xlaQp\n/W40pw6j1i00Wix3mmp9zO+rrLCDjhheZZtvfjccHvGMJ1xc5Z5FWfmU17W7\n1nYYvOvf622ri0P2Ddc9twOy5IvNi6KVzAdKwoqqNrQk+4FboBam0S28gVs3\n+1/dFOv1zkkjykejw+7y5Fe4tR3sQxC/OasYBbpkz3uOiCzfaBEHHhb4dGXI\nWwX64bPz4yW0WPJ+t/sgd3+LoryzmvvOcW09F7W3sMvvKlwHsvRcj2Mwtrn6\nYB+R4BOc5Ge7Ht2lfiKYf/kK5uTwRY+Gr+K5F6WEOBGBvKE/VUlkg8QIf32X\nM+pX\r\n=8qWB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC5QEhd5wanvy2e/fDb3SpxwMW7a3abMOuPZoxSL/Bq8gIhAMbkXO7YaAe0Zh+CHdYoQffcAdzQwFKuyKTo/3/K3Kkf"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_7.0.5_1551345915150_0.1795619617757651"},"_hasShrinkwrap":false},"7.0.6":{"name":"staccato","version":"7.0.6","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"7.0.x","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"proof test test/*.t.js"},"gitHead":"b5a750b4129da66ed873fc52bbd0b45462014e92","_id":"staccato@7.0.6","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-K5f9i5QpW25W1fgNTvk5tib+15G1dfNJPm4XHTPZ3f1Akm/pldOw5a4iz/ngrVJ94i+xsc3kC3SnB2MXihr9xw==","shasum":"1f0b44a44e7f34201650bb5d04f2639e4f5ef32e","tarball":"https://registry.npmjs.org/staccato/-/staccato-7.0.6.tgz","fileCount":7,"unpackedSize":9475,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcj4+TCRA9TVsSAnZWagAAwSAP/2D347sIIs4jtr+w7eEx\nZIz/ygmIeJr2olnbEftSiZ4qLiM2i+1j2KLfUiJVscqvAd6TQz3fQWI3fU6O\nEldHzv/lTgukw296x/ZTC+x+6W44PXS8uDOofCu2gVKjJxoEuwbTzCfOAB7y\nCzv4xdix09dS8HNOFg5Erj7LgwavomtQ9DJxZosf2Z1jX0YhW7DtnRxQ3W3k\nxkcEYxVQWHWqM/0DQJFd1sXsrfPYMMQdOEuL+z0zV/ogK9AbzN3z+bMqlHIK\n1nNo0o4k1DUHHiR11RtbbD/UzMEhvXjDNRutZWW9j8AG+QWtfRc7Ap5J+uIo\nLwsr5uAdg/a98ziCcFtSEPjbX/z+6Q87ymLKKj8K8oWABKnrXPtxf1vjXgT5\nf77n2pL6hcFWlTcEI4FaFSreMJA6i0tTka03AmnnTCuOYFzRkHgB8B9WddPN\ninJD/IdYqhD3mDQuYMBtfPbdczUuVATbzJ9u2L7ALN3YsM2R2ObS1ickIzls\nD94VLZOL9/F1dzaVbGkDAInxlzHXF+sowtkPeRRegmbP2LSfk+8Dzsre3J+x\neJ7Eq9WIk11gvP0wdOwHz+oRz2yUTGxHBdw6Fmxx6oXI0kTZn0r6k5KOneeT\nLIQhRPGAdk+6xEQV8+4BmfvWO3J0+2jg6wTxzGOPjGehuCNfanGzAfSHtjPr\n9225\r\n=EsMH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFRTWDrNXZRewXtT9FYif9DyvAl4kJoLKiA5hI9kTPH1AiBDMTPuxvwdT3I9v8pES0aqdviTC5F2YkWJFIjz/coG4A=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_7.0.6_1552912275236_0.7216986094156437"},"_hasShrinkwrap":false},"7.0.7":{"name":"staccato","version":"7.0.7","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"7.0.x","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"proof test test/*.t.js"},"gitHead":"3b3402b46bf335d31851d5422f9524aead5a4905","_id":"staccato@7.0.7","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-h1yHRLjYzgqgwIS2FDGFIVqOC2KTnSj+2ecNEl8ak2Fn/y6SDh9YJXZYLxhPwtMr7RKspcXVjsQQZJwysR01VA==","shasum":"2677c823183658e03e02e513bb81515ac29fd13e","tarball":"https://registry.npmjs.org/staccato/-/staccato-7.0.7.tgz","fileCount":7,"unpackedSize":9686,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcopoiCRA9TVsSAnZWagAASTAP/RLc3rRo6pSPuJh9P7rE\n21PO0LMZfOes/rP2avX60gQFO7aNrrkWIjMPAKIqyC89DjywZDVz2cnmp3wx\nVLyyHPO1bxCdtAFiSE2ilF96OlwXpkW13Wpl3Jy+ZQFVJu67CtD7W/5zMvn4\n/N5j27VqiT2Xu0BWcu/yS+yIc2B2TMgDKwOQBBRVuSnUthmy+WVG4RQmueaV\nGCXGxfUy9/ninOL7fvPsyXR5Vjyi/z9qWgorJEbwq44cCUTLPE5ET13rlZBe\nTiTywBCr+ON/h5AMyXJP7XEMl68FKA6dWG2n5iFZyKlt8WZPCOHYUtTZBGto\norAWasAZ/WsJGuEgACQu5VGX0od+DxlYL2vjjHOHwM6y6o89ZT0CkNutNxrx\nYr4BGmEwMN7L1QEt+pjAJGDQJH+nCTlvgK6AH8mN45+uYp2/B4PbrRJb/LmH\n/pYYm0evPe62SgsYu7OM0Xb97mbX9NSHqVQ5qz+CV6uyn3t9CHE5ymfMrbCz\nMp7An/KQ6vtEQzO8lJZ5+vhRTj2Xf36zk4FfQQZnXrrKEnnVY5hvIyHQHIn6\nOZhMTZ4sqsWLB+3DsJrlCyrw8AvWayFBiqA9KCRC8Vqw5x8Gb3YiFXUUUVZv\nFhdtz7riRF6Du5o09RWgmYr8YxGhNWRXLALeTFgyD0lNiwDkjpk5cBPn65tF\nKnf0\r\n=7sHX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDdb/MCVm0lU1jksiTd20wlcLcYCK0GcTf/UHudL/LJUAiBp2iD0yzDBE/UM8KDCBFZ6cNUngeS7P7aEJMH4+VuMow=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_7.0.7_1554160161546_0.7919862506940241"},"_hasShrinkwrap":false},"8.0.0":{"name":"staccato","version":"8.0.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"7.0.x","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"proof test test/*.t.js"},"readme":"Write to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"dbbf6b77c26a92b93e9732ff0fc6d30307b6990d","_id":"staccato@8.0.0","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-JQSucdger+I+BiMce9z86h8N9RwsVjsTjxY6m7LvFG5WVuccU7SyoGje3hi1B09qCVLSI4g9c8zqEUZu64di4g==","shasum":"4e9e6325a7576da913150c060ba8606e957495bf","tarball":"https://registry.npmjs.org/staccato/-/staccato-8.0.0.tgz","fileCount":7,"unpackedSize":9305,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcosCZCRA9TVsSAnZWagAAg8oQAKRPbZ9+iQN9oV9GrrzO\nlEC89Lorg6P1iZlka+QtcwE5kky7X7spG7OYDS3eOUCvm4R5jjLBOTb6hlxj\npSh66nwg3rp4DjrJ0BFjSv8VQsjyyKzs479pc8UjXjewxoGxSDfsWbbOBBbD\nKWkXbMU1Nt9UvIK9x9bBKgVQQDkxcByt4veuo+CK+yhxdHDJsb6VyARd+y89\n4R9j9gJErvKcZMKeSBE7OwJMfM9Chda9GGdFiRZCf6YO1y/xwN/DO4cn+t70\nxJFYJ0XD+im9xOQrLDtm9lE6AauAnaAUac7ICDJi49258SBNI7jSlFJB9lJb\nAGRkPwR52zWl/K72qbAPRjmu82nS+EOUrzks7vQp6llksTGHErXef3c8In1z\nnsdtIUI4Dr57k6rgQaETX0chuuvGyQEcjl2PeJmre5A8H0Qv2zNyUgAwbFXh\nBjaH1ingI88WkWjt4sv2t+R+PAZ5LIOVulJjpk2RYOrPbJ8MRw9Ki4v/TawW\ngAtrSKrxs1M7pXBLa97oxlh/MJrk9+HLuy8nz6VaGafIVFM/5FYtpQ6MSqDi\nczq5h3o5V2VrbwFk6r33yErFMdFnStBJLA20b+EllZ/TqxmTBmes7rrgIv0l\nVa/L0GnSEUn07A2qnl623uetsrgCMJiPNkQo38ZanncIIB0l+7tW6xS6l5ac\n6dYZ\r\n=YTj/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDyRdtW6fY/BfJeDOBOFfd6kqVS7vSwjWf/dkDj1JnWbgIgfEIfhZWNlLB8QZxZB/MElOu+GoRtWrzcM8vVlfxQ/n8="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_8.0.0_1554170009157_0.936363257699456"},"_hasShrinkwrap":false},"8.0.1":{"name":"staccato","version":"8.0.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"7.0.x","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"proof test test/*.t.js"},"readme":"Write to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"19f72b52e0c864f5f5fa92edd604f8827d4ef874","_id":"staccato@8.0.1","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-T0i3FppmNrl7HlLiGAbdUNglBnd+3fBu4uMjbZtgSkGRa4qqH4HDq84TDxDBQUzBOL34Q3Hh/LrusUEvI9rYlg==","shasum":"cfee9537caf7ae2a0969412d551bb86fd5a343b7","tarball":"https://registry.npmjs.org/staccato/-/staccato-8.0.1.tgz","fileCount":7,"unpackedSize":9596,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJco3uuCRA9TVsSAnZWagAA+Q0QAJETZnh2QzVOv+Y+lQUe\nXEn+tf4Ci9QxW3Hi5GMPbUx2tfk1JZorIqD78Wesoy5TsGXYba6rWjd1WRBA\n7u8AUK8/LkJ3hTMyzmouPp2J/dXNeKGXtv3k0fw8Tt+8bGcyJrDDIEZnjmeg\nrc2PgC6MsKrLyXb15SiKGIx42gkEBeoiOgp69i1+wlA3uPnWQMq/6s0dnWwX\njLP7ISxJT+l7mrsAj5QKc8Zww47GmfUIQsnBqMJ/+yU+72FFPo4mDl/LjMwN\n3rb41PqjoAkVYCiqCeprObVrDa3tlSpVhoLyDsx5BI/07MpqokBJHHTCtCHz\nHDJ1NgGHtbWx0M+x9SkSDhnZ/r1USz+v56tuszAPxLNfamuRxv6ouXN81+K3\nPyr5AP9aKWhLL7Svatdq8Y0XfDNzDurPNA4NunCmyEnUK2AiBaZrHaFDF+Dq\nURTBkb6gDPoonq16gNfOyxPWNG72xnID63Ni2HKJ59QXLjp5RbpcequLf6Lk\nAw4/00iieWsFYrPvIm2hu/Zr23TfbF2Q2M37fjVsHSNs3fs6dVtTdcDtha0P\nmVcEb3rtN8QaG1Cx70Edy9TVbaboknfFNwo+VbjK0LMqmU3w8Ei8u374Ew7m\nJOF3oTogkCHdzFPyu6rMmurgTunbdYGcDHS10li6NbRYecmKn254iOQlwGw3\nWJDp\r\n=ZlmC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDuKCqfZSznWTQaGrYJJ3w4MsMkyNnKzm76vZOzA2mfsAiEAhRF/cTnXi6Q2cMcZZFlTJqrMqtbuntWqKw2VxaDeyk8="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_8.0.1_1554217901189_0.5327518987489681"},"_hasShrinkwrap":false},"8.0.2":{"name":"staccato","version":"8.0.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"cadence":"3.0.x","delta":"3.0.x","extant":"1.0.x","interrupt":"7.0.x"},"devDependencies":{"mkdirp":"0.5.1","proof":"7.0.x","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"proof test test/*.t.js"},"readme":"Write to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"5af222e298f1ec555e7e74a61f707060694d9fcd","_id":"staccato@8.0.2","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-Pfu2WmBjQy6+WCcLSPgh7F/51G3LCj5vOQK/nLHU2GSaILBX5xP5lTzKkQ7MzI86uPfl0MCfHUyeGePGPwrfFQ==","shasum":"f136b42c069d2ef122f1397e88138335d9f54e03","tarball":"https://registry.npmjs.org/staccato/-/staccato-8.0.2.tgz","fileCount":7,"unpackedSize":9596,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJctnWkCRA9TVsSAnZWagAAPcQQAICjDuldTWiyqJ43MwUy\nEobPS7Lqp9VhgmkbmLMd1nU+aqXMCuFTIbZBg3oE9t8krawpsqGwGf4gnMcL\nO3GjZne2IelWszXQrRMxaI4k1ws/I/NbgU+ACBQhg1E9rOCMSunXz01FrmIS\n1DMnmECCjqmLnq296Ry1B3iELYgMXw3z7QSPirMybp1jYCmNtFVOo3FAySPY\nZhmQuUE8+Qqt41Vz6lbI6B7HTOe/koJezTOnbIZf2lB4PqEQHl7zF/E/z9yw\nxnhXpBEgTBQlQkAEtuOhhRVdSwLxdqZInfdW57Zy4MZ/Th3o3v1N8qJxBRkX\ny2LuDgM2cUfrK6sb9IyzYDiMmAPhIH0DjXe18rMKi88L1bCwiu/mgTbq44S8\n2ZEAqBVvy2f7BtyjNO+rDSsKzp9tcWiw/u1+0VcvAc58z5Bvfv1uifCvrKBB\nw7n2/VfZOEEJ4nN/p7sOr8HyFJmMC9tV0ipsEb7ii+8x61mecyJ7Apzu1WE3\nnR7fNSjreTY1vRHgkRcwOFcqaG7G29Y/07LyQO2NhuFW4SXI0SWM9lHuPQ0M\nLFbBo5jlMbcIP8xw57MMCnzpOeYuy98P0evFtT7MY9uLxfqhL8VuNS/n3+Un\n44mlcZODtol6PPVXlFnvOzctnM0CPXA0kdGok6FcTHoNTl66At4cG4B0V7bt\nqxW/\r\n=Qtvi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG35Fm6CyTkW2hqpgw27E7q7soBDAOoL4MrNyalFm7NTAiEAiwdw7aV+eY3uk7jk1JbukHWo4j4fVf+q13KfINqr6nY="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_8.0.2_1555461539532_0.8390607360515168"},"_hasShrinkwrap":false},"10.0.0":{"name":"staccato","version":"10.0.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"0.0.x"},"devDependencies":{"mkdirp":"0.5.1","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"mocha"},"readme":"Write to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"06b6757c42c7e5514476d2c9a696a34b0c727014","_id":"staccato@10.0.0","_nodeVersion":"12.1.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-Ww+3GNJGHUIdIowOQ4D2hLn4EJSZwzh4JJITrGkinm9ObAG+0ABHQ4cjZylNQ0y9AYyVM/iSrAtFAdF2oOs2tw==","shasum":"340802e5f15c5bed6aa2404de4966daa069edd54","tarball":"https://registry.npmjs.org/staccato/-/staccato-10.0.0.tgz","fileCount":6,"unpackedSize":5708,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5e75CRA9TVsSAnZWagAAOZIQAKFhidMLpJ8hTqYoZYSc\nMYwtSJgQGHiTHnMsiBAgPFflMGHyfc46zTOpWrY+8gCCLjqU7ecmlpZKA3d4\nptoK62nhTQHUIBAa1nNQAFYWcwW5BujC92ShUvvIqX9CitfK/iFdLo3hZ83p\nkPZ1LkWkC5OkDfnm1EpOpw41stVjMyNHN47WiGxKn91TQN8k3K68e0eDPCCE\nLQw848quSGRl57slNp+pFZqsXV93Z1vD5rJQTAXIwS7qZLSygVfO0oukPQvb\naCuRXGZQ71lqmT7L5neAo256Fx+z5POYmbDxA4xBuVlYyEv4+s5qgUmUYCtp\na9LVEbVFTmhK7ebkDePL6LduPMkGMeT0KM341pdVF0TTzejGGuzYziP0mFBy\nnE8V0pP8ElQZF1PuUqB3YB76M1Yfu4eLoCjwPE0TlFfDusGF1GxGVB7WzJM+\nDpHzq8AZVsrF7KcmTGyKoUYH8PIelDlqSXrPCsaTKkhvLk1d2KYkc2/1Re21\nRP+ICIBk1H/p0OnR9vrVJ6oU8xI8ghIfG9mKTgHrTsGPEIUEXNzt5sX6D8Cy\n/dZ98W3t0wjaGt3cff+rBQXwz7cIgc8SHDMWWxazBKY7Y1on0aki5Pk3U6OZ\n4wIxKX7TPquuHphK+T6luwEY7gUBa+RmxN5bhBcwDYj7JLT38iVVtXWZLWRP\npqXI\r\n=YSZK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBKGGMFCWDM9tPjZHsMD3Nblzz02dw4xDun5uLQFsfgRAiAwkkSJfltjcQUvtHkUXSZN+Z8spNqc4WLPQ9zF8tIDNA=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_10.0.0_1558572792984_0.22242751314498088"},"_hasShrinkwrap":false},"10.0.1":{"name":"staccato","version":"10.0.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"0.0.x"},"devDependencies":{"mkdirp":"0.5.1","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"mocha"},"readme":"Write to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"59fa4ea8be535aa203414eceef3c1d437618bb73","_id":"staccato@10.0.1","_nodeVersion":"12.1.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-cx+bT7ZQ8R6VisgVC2U+ogA/TceK07gZ1oZ7TzNu8Pv1rq/aalPZQvH6Baj/+76grg94oazP6JTo/cPdTwoYJQ==","shasum":"051902b25a4a4bc508d80e5e0913fe05bfd0de24","tarball":"https://registry.npmjs.org/staccato/-/staccato-10.0.1.tgz","fileCount":6,"unpackedSize":5717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7MZlCRA9TVsSAnZWagAAkjkP/ivzwXoAECa/cJw6/2Ol\nN4t06DYyHpm1jK8Aq5VUFnRAQpCCReM88vdWmKH1V2JtQcMEn0A9XPsL+Voc\nxFHojKKX35MV2HRKxVL4MiwO8kcSh2bb/DjFvNH7Cgy6CRfcEb0rwZDXalho\nd0XJtVWmX8AtqY32GAoM8GsR+RXGBO99WzTbpu+gP73VPG7FGWZnt4AZQXvk\nyGiWoBtRTQOKvimq2qLigSAb5LPwwWvtYYLn2qu/1CbUbMK8froGf7qHZXn1\n2Xy++X3hqkfv3Rm+la5TqUoqCqNtkdqGx/WAP59nfqMA+bVuzTdliLXQ4Puv\nkgD4i006JpRmsV4UwARb6lYJX22By8q9CXA9ayE7gZVIh1kkvQcTFbSAdOcx\ng/m9i2kT3W5677oOwcGqCaegUtZrFvtVcfFJ7S40EtRGv6jO2zVBleAiMNtN\nN0NXveku/JunVHLkuGwjXHSn+/0iCmshWf663RjG3QSkjaf9UdCG05dKMY/k\nV7eo9mcDSQ3Bo6GqO+u/N86fEf5k6qEuAdoc8ej8Ff7HUlPUeQCdXJY9rzMk\ndRM0UOt6r2Rsa8Xy7MCXTXoMJj86LHdWF8xjE0cMVzBejCjY8MqQgO9ovANt\nck3W1TA8HvdO1ehsFvRE9/SclknFs7U80vRyFl2akLUP33L7USUbf9g8QHEp\ncxZT\r\n=Bzl9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAHVTDUY+CTx2xBcTVYBc5EFL/yjHi87ma1/Ujcd7RZeAiEAso04nJ42xQD9D9+eioAQl7uKs9PMzdYOri7JiP90YpE="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_10.0.1_1559021156306_0.3632245378337242"},"_hasShrinkwrap":false},"10.0.2":{"name":"staccato","version":"10.0.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"0.0.x"},"devDependencies":{"mkdirp":"0.5.1","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"mocha"},"readme":"Write to a Node.js stream using the error-first callback style. Staccato is part\nof the [Cadence](https://github.com/bigeasy/cadence) Universe. With it you can\nfeed a stream using error-first callbacks, a write to the stream will block if\nthe stream is paused or if there is too much back-pressure, so you can generate\na stream from a Cadence loop and the loop will pause when the stream is full.\n\n\n```javascript\nvar Staccato = require('staccato')\nvar out = new Staccato(fs.createWriteStream('./out.txt'))\n\nvar writer = cadence(function (async, bytes) {\n    var written = 0\n    var loop = async(function () {\n        crypto.randomBytes(1024, async())\n    }, function (bytes) {\n        written += bytes.length\n        out.write(bytes, async())\n    }, function () {\n        if (written >= bytes) {\n            return [ loop.break ]\n        }\n    })()\n})\n\nwriter(function (error) { if (error) throw error })\n```\n","readmeFilename":"README.md","gitHead":"41c4730adcb0902e15cf04b64244f8dba4ea1e4d","_id":"staccato@10.0.2","_nodeVersion":"12.3.1","_npmVersion":"6.9.0","dist":{"integrity":"sha512-sqt4EPFfaTcpuKnUl6QrVMiMe0i3xufJgh/6odnNvawnMvOx/7nxDAqQvwbx6xs3qL5yBI4x2Jq2OIXURvGL5A==","shasum":"19952b6ca2e2c113bdf9a4a9b8142e27850f892d","tarball":"https://registry.npmjs.org/staccato/-/staccato-10.0.2.tgz","fileCount":6,"unpackedSize":5717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdGrhXCRA9TVsSAnZWagAAhvcP/jGRcq8bUSC6pvAoLREr\nl1euCofU18WYCTAjoqWKFz9aRpMC4vp8KdH3kvp0QowajJKaNDKD8150HMXL\njRDbMkwQQdfui1oK4qYXXqh5bdlE3UPcRe44Ei9cmsv6WEDZmrzoBTi48f7v\nvcgqFzz49OCmj8qfGhrF6faMboPhDcjfktCCTAyK7qsj9XDK68zCbYFiu8Yb\nZGmD86H+/LChvtpsdLdwCGx6LRMPHAS9LJ8JAdqlyUZQ2V5sZV80WwP8PQW+\na3MzG58L5+s/CwNSv5X/qy6p5VEFbZb1Z/lfdmFAdN39P32xrAYunimTyed7\n+oTHMaD3JcEkhJwyXN9gpekLmE73uXXVH3FbXBVSk1+cfxI1+7zmwUVaZhmf\nh0lxECj/MxdPnPgs7nPJ+B1LN/IUu4RAgTErTJars+pdirU3KqtXChVYm8U6\nmYmFNCjATzwlM1geeCyMWbqLAgi1MM13Rc9Ltw5zF+QcAZ7ncpD+ohTH4SIF\n4NR8lWFE6eKpbIG8AnZl+eSt4p2izDpHp2DaUrm516RDs+1cF2hjwktlAr4p\nbpcW3EfZGT4VnNF9VPFyfVcak8n24K85gebmdsHd7uqfGjNTozsutGh4pVof\nslghQS7r21b5+MB/Fu9vTpLWg1LKptbD1z264oWteLOEHeDfk+VS24P8RvH6\nFw4R\r\n=3bJ8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCGWeoOA9lXY8UE/hRNhMgETMiOT/3Lyz8u2AQ6Te2wVQIgZThjo4aPnxkFUrP1x2dboZgwywdKtsSK+0t7FBtdbyU="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_10.0.2_1562032214230_0.9987368390488169"},"_hasShrinkwrap":false},"10.0.4":{"name":"staccato","version":"10.0.4","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"nop":"1.0.0","prospective":"0.0.x"},"devDependencies":{"mkdirp":"0.5.1","rimraf":"2.6.3"},"main":"staccato","scripts":{"test":"mocha"},"readme":"Write to a Node.js stream using `async`/`await`.\n\n```\nstream.on('error', (error) => {\n    console.log(error.stack)\n})\n\nconst writable = new Staccato.Writable(socket)\nconst readable = new Staccato.Readable(socket)\n\nconst read = Buffer.alloc(0), write = Buffer.from('begin')\nwhile (read != null && await writable.write([ write ])) {\n    write = consume(read)\n    read = await readable.read()\n}\n```\n\nThe above shows the motivation behind this library. Writing to a socket in\nNode.js is relatively undocumented. The documentation says that when `write`\nreturns `false` you're supposed to wait for a `\"drain\"` event before continuing\nto `write`. Staccato will do this for you in the `write` method. There hasn't\nbeen mention that `write` will also return false when an error destroys the\nstream. When the stream is destroyed there will be no `\"drain\"` event so waiting\non a drain will cause the program to hang. Staccato encapsulates the\ndrain-or-error logic.\n\nIn lieu of raising an exception, on error `Staccato.Writable.write()` will\nreturn `false`. This is because I've found that with sockets, error management\nought to be a separate concern. The error should be handled the moment it\noccurs. We should not wait until the next write to raise an exception based on\nthe error we detected. The error detection merely marks the `Staccato.Writable`\nas destroyed. Reporting the error reporting is supposed be handled in a separate\n`stream.on(\"error\", [function])` handler that can shutdown the writing process\naltogether, preventing a failed write.\n\nThe associated `Staccato.Readable` class is provided to facilitate error\nhandling as a separate concern. It swallows errors and returns them into\nend-of-stream messages. In my code, for the most part, I'm always looking for a\ntruncated stream in my buffer processing, so whether the stream ends prematurely\ndue to a detectable file or network error, or whether it ends prematurely\nbecause it was only partially written by the other side is doesn't matter.\n\nMore to the point in regards to `Staccato.Writable`, when a duplex stream failed\nI'd find that I'd be detecting errors on both read and write, so once again it\nmakes more sense to have error handling be a separate concern that shuts down\nthe duplex stream processing from the outside of the stream processing logic.\n","readmeFilename":"README.md","gitHead":"c4eef99467571e1e52860ff39b078e48ab03ce58","_id":"staccato@10.0.4","_nodeVersion":"12.3.1","_npmVersion":"6.9.0","dist":{"integrity":"sha512-gAYj6ZVsEvkjR3zZe5YqEcV1vx3ICCxbFEOH4RQpdtw+P1JpaEkB61z5LwmRVn0QI17Gc6JEsGG3WlUNF9WHrQ==","shasum":"0fa1cbf77ffa91cd82edb8200893c28c0da579fb","tarball":"https://registry.npmjs.org/staccato/-/staccato-10.0.4.tgz","fileCount":6,"unpackedSize":7735,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJtMzCRA9TVsSAnZWagAABDcP/2NNAh+flNf0gJ7KRy+h\nGyQwczhwhNbgbNJquj2lcp06JS9ueypgIndehTX0lJsxV6Vje1hK/Z0iFQKk\n/lDdXwyi+o+2KwhrDDQXjvQUsEQkkhFtoJEws4fOpcdOjYwAZz9L3ksJ4C/3\nS7SKUFpCDMIxXpT2SPJ4VzkaJ+ePRnBaxxVVJ5HZaJvyysl/11uDgkYJ+eI9\n0ekMk518gXLQmroWNbbMhxLPJQjF01/XRM8cWwSYGOz+VvfcOFkuDPVB4Lfg\nDoRhyM1hEXT6DalJvuyrtbGOdbIFToAhJDAMrIfDbvPbV0dQq84WutQAvytC\nWECUXos0aG/F1HbaXWF5FZTvgTZ4OnhFY6xZB4wXZjcAX/D5Lw2xU5RWXLHT\nMpgodICfbIogBZIPzMPyzYPGqrT2ILE9jtC/shc1m6+CTlbWFvbCP9Q99Wb8\njuP/qBGczGPlIvfq610EHjdUC87CvPAMjDaOBDWh/875+LZXXpp6Z3EoHY1q\nuxM+j/Mi3AVz4qHKXOL6FSEVkvDgHy3d/iaPMg2HOWtrG8kYQlRE5ONM59nX\nGWpOeTcsJBfIkhAaF+X4+WmNM7+QlnNI86HoTXUj6TzrACnzSjgiN8T8xpqP\nfGrzlIEFF6xEfLGE+SoPyIopWB5kH7aiuVImqgQhGaE8bMOmCV9D0TKM6Hg7\nvmyD\r\n=aRUk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHQiA8qdMkgkbYltJDB3NEYFC4MHm9kBkn+X25RHk8TFAiAKJQqlTjQgMnswzkbgcB66Ue7yxmX5Qj5uOxfE2riqdg=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_10.0.4_1562825522556_0.7779025979605056"},"_hasShrinkwrap":false},"10.0.5":{"name":"staccato","version":"10.0.5","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"nop":"1.0.0","prospective":"0.0.x"},"devDependencies":{"mkdirp":"0.5.1","rimraf":"3.0.0"},"main":"staccato","scripts":{"test":"mocha"},"readme":"Write to a Node.js stream using `async`/`await`.\n\n```\nstream.on('error', (error) => {\n    console.log(error.stack)\n})\n\nconst writable = new Staccato.Writable(socket)\nconst readable = new Staccato.Readable(socket)\n\nconst read = Buffer.alloc(0), write = Buffer.from('begin')\nwhile (read != null && await writable.write([ write ])) {\n    write = consume(read)\n    read = await readable.read()\n}\n```\n\nThe above shows the motivation behind this library. Writing to a socket in\nNode.js is relatively undocumented. The documentation says that when `write`\nreturns `false` you're supposed to wait for a `\"drain\"` event before continuing\nto `write`. Staccato will do this for you in the `write` method. There hasn't\nbeen mention that `write` will also return false when an error destroys the\nstream. When the stream is destroyed there will be no `\"drain\"` event so waiting\non a drain will cause the program to hang. Staccato encapsulates the\ndrain-or-error logic.\n\nIn lieu of raising an exception, on error `Staccato.Writable.write()` will\nreturn `false`. This is because I've found that with sockets, error management\nought to be a separate concern. The error should be handled the moment it\noccurs. We should not wait until the next write to raise an exception based on\nthe error we detected. The error detection merely marks the `Staccato.Writable`\nas destroyed. Reporting the error reporting is supposed be handled in a separate\n`stream.on(\"error\", [function])` handler that can shutdown the writing process\naltogether, preventing a failed write.\n\nThe associated `Staccato.Readable` class is provided to facilitate error\nhandling as a separate concern. It swallows errors and returns them into\nend-of-stream messages. In my code, for the most part, I'm always looking for a\ntruncated stream in my buffer processing, so whether the stream ends prematurely\ndue to a detectable file or network error, or whether it ends prematurely\nbecause it was only partially written by the other side is doesn't matter.\n\nMore to the point in regards to `Staccato.Writable`, when a duplex stream failed\nI'd find that I'd be detecting errors on both read and write, so once again it\nmakes more sense to have error handling be a separate concern that shuts down\nthe duplex stream processing from the outside of the stream processing logic.\n","readmeFilename":"README.md","gitHead":"897bddb04123904fed602ba451a702525ffecf4a","_id":"staccato@10.0.5","_nodeVersion":"12.7.0","_npmVersion":"6.10.0","dist":{"integrity":"sha512-zbuzdxrbWLrwX+/oocoYwVZwNijRQJGhSyM9sz1hbLicRCWnwualzuUCjL8a0hhKvJWoV54koV7iQTsUDZYmaQ==","shasum":"db20fe1e3f5e42eb4aca22a5f627883f54ddc199","tarball":"https://registry.npmjs.org/staccato/-/staccato-10.0.5.tgz","fileCount":6,"unpackedSize":7735,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdWvEtCRA9TVsSAnZWagAAOLAP/3Fd4OITAw7jEfznqmxS\nuuYwWAiTJTZ8NFmwSYxY2onB1mwP0a7Y25u2ANXUPGKwGbVMQgg2g7Rh8s4I\nAU4vzNM1iqbvRRNZRqJTTHfSxUnspfdsdsDtQ+HYqdhPA6TjBxGErh7Du/6J\nlJWVsxFT7iBSlABK4PseeGO7GaDqcj0q3PmXTh87cW9OKUjSF07vBz4aXe4W\n2QMo/bs7G0OY1UnkVey1yyPSwvb+JafRvnK82Ox4IRxTi2+GGHXS6833HeA0\nuy30YeUwko85Zcd12iI/p27TVUOJUFE7Ope5mgJwlOF3z4cS23QdNkaL2PEK\nF79NT85xOJ/EHBNaw32O7TW0+TvjL2tIabg/7eQqSfBiYEdWWnlUnQhO0oJe\nSjfe0EUrP6Z8KorG4bogSo1u4k3FzJrP0sVtaGseS+RKu7aJdmHO9wabV5Qd\n57R36OB40A6qkQ3vHk26Lk0n3nyZWavu+eirRYjvnwA589s9z/fqxXEeIkFg\n8+nX0o62+zvJ01Sl77+fz9pD3FNr0tNo+foe9Vw6h+9lriBiWDflMBUtJAOQ\n1prqjhEdArU4HbkYkiCKYjso8OOT0+X1wYc3K2CgjTK5131VAXZ9yPopqZeg\nNaiik17DZxnCnoYyLXuTE8pcEUIGJEoJY8AflXIwZSY3QSA+easx2Wmnm4G2\nttLR\r\n=oWYG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGp6EDKQks7NXSwUGReQBtQJVM9qewIJKu2Sgfe6WNR1AiEAx3Ab0RoeMunOqBow1SlNNGSUG1ImagIvjO7GUD4VDAA="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_10.0.5_1566241068317_0.4165806173288875"},"_hasShrinkwrap":false},"11.0.0-alpha.0":{"name":"staccato","version":"11.0.0-alpha.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"^0.1.0-alpha.1"},"devDependencies":{},"main":"staccato","scripts":{"test":"mocha"},"readme":"Write to a Node.js stream using `async`/`await`.\n\n```\nstream.on('error', (error) => {\n    console.log(error.stack)\n})\n\nconst writable = new Staccato.Writable(socket)\nconst readable = new Staccato.Readable(socket)\n\nconst read = Buffer.alloc(0), write = Buffer.from('begin')\nwhile (read != null && await writable.write([ write ])) {\n    write = consume(read)\n    read = await readable.read()\n}\n```\n\nThe above shows the motivation behind this library. Writing to a socket in\nNode.js is relatively undocumented. The documentation says that when `write`\nreturns `false` you're supposed to wait for a `\"drain\"` event before continuing\nto `write`. Staccato will do this for you in the `write` method. There hasn't\nbeen mention that `write` will also return false when an error destroys the\nstream. When the stream is destroyed there will be no `\"drain\"` event so waiting\non a drain will cause the program to hang. Staccato encapsulates the\ndrain-or-error logic.\n\nIn lieu of raising an exception, on error `Staccato.Writable.write()` will\nreturn `false`. This is because I've found that with sockets, error management\nought to be a separate concern. The error should be handled the moment it\noccurs. We should not wait until the next write to raise an exception based on\nthe error we detected. The error detection merely marks the `Staccato.Writable`\nas destroyed. Reporting the error reporting is supposed be handled in a separate\n`stream.on(\"error\", [function])` handler that can shutdown the writing process\naltogether, preventing a failed write.\n\nThe associated `Staccato.Readable` class is provided to facilitate error\nhandling as a separate concern. It swallows errors and returns them into\nend-of-stream messages. In my code, for the most part, I'm always looking for a\ntruncated stream in my buffer processing, so whether the stream ends prematurely\ndue to a detectable file or network error, or whether it ends prematurely\nbecause it was only partially written by the other side is doesn't matter.\n\nMore to the point in regards to `Staccato.Writable`, when a duplex stream failed\nI'd find that I'd be detecting errors on both read and write, so once again it\nmakes more sense to have error handling be a separate concern that shuts down\nthe duplex stream processing from the outside of the stream processing logic.\n","readmeFilename":"README.md","gitHead":"ad6a5aef48e159a8c2e53116b730fd7fa792d32e","_id":"staccato@11.0.0-alpha.0","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-9ESbFdVM3sZFG1lI93Arpe5f8B/dZY2prSoGmlklqERmQ/noeudpZ/gWwUCdW4PVc9i1Z2qdXLFQ5MEZH4hJcw==","shasum":"dde3cd232e79fb738737e9c9ff21a91b12361e84","tarball":"https://registry.npmjs.org/staccato/-/staccato-11.0.0-alpha.0.tgz","fileCount":6,"unpackedSize":7309,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJddxK3CRA9TVsSAnZWagAAVP4P/ieRod8YaPC2/0UT6tsy\nFYvu95EAiyg/3jdYZn1NjuM6Pgx8Ium4nVvuloCYGbV2NKnH1eB+bZBKq5DU\ncNzpwrpKYC0D/G9vUMfek/cBTW29VcMYKykwcDp9bQyOhZgpKZkrxx6ue/5X\nkPG0bebKxSZcVAVOULu0DF2lZdAwCoIpMVCYlNLmmUjjfGzmfdIU60BNL9vl\nGhwkJ2WOciVOwX7ZSXrg1sZ84I1V5doYjUeuJl0pouroSQKUq53EyUzVGEnq\n1Nnp+cjMKbmIbT/cXhuSVhjand4o9dk7TlJlJ+dSnqGzNcg42cMZBoN1eDpI\nrbaCGRvvdrWJCKw9Elh4KMBvaa2+6oThzXUErNOoTu4cjipF6jBCCvWQRgYN\nLJkOU7w6l8deoKVx7eSkviSQfakAjZANFSVKJiMMsQFhefSCPiluWFGsrxE1\ntQbCONl0SFGME2Svi64hcuZmowRYEp6o6DayxD6jdPlZUuRnDX4QPgponVhE\nOde0t/hdcLXN/vUnWDRbnUg+DALkwurlCCPB7GfB+fOKxFXg9QL8BgCLbomO\n5PBpaNFFAAWYlN+91TUv3OBnYZ50XceKDUI2e3oFIWEb926L82kC6uUjK3Zc\nO0fPL1XWVh6WDrC6ZU/ghQjdcj31xqqNPQDY4drzIwCv2BXfJS5rtw+D2g4c\nyxoB\r\n=Jrqd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD7WAdGEUto8+Kh1WdphlKfGgjH0HnUnlzsop3bJ1rUygIhANeJjnw0c618QM0+hoeY5Rm3YVsRjLrbIt/ZzsxHDwtY"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_11.0.0-alpha.0_1568084662636_0.2878472827999412"},"_hasShrinkwrap":false},"11.0.0-alpha.1":{"name":"staccato","version":"11.0.0-alpha.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"^0.1.0-alpha.1"},"devDependencies":{},"main":"staccato","scripts":{"test":"mocha"},"readme":"Write to a Node.js stream using `async`/`await`.\n\n```\nstream.on('error', (error) => {\n    console.log(error.stack)\n})\n\nconst writable = new Staccato.Writable(socket)\nconst readable = new Staccato.Readable(socket)\n\nconst read = Buffer.alloc(0), write = Buffer.from('begin')\nwhile (read != null && await writable.write([ write ])) {\n    write = consume(read)\n    read = await readable.read()\n}\n```\n\nThe above shows the motivation behind this library. Writing to a socket in\nNode.js is relatively undocumented. The documentation says that when `write`\nreturns `false` you're supposed to wait for a `\"drain\"` event before continuing\nto `write`. Staccato will do this for you in the `write` method. There hasn't\nbeen mention that `write` will also return false when an error destroys the\nstream. When the stream is destroyed there will be no `\"drain\"` event so waiting\non a drain will cause the program to hang. Staccato encapsulates the\ndrain-or-error logic.\n\nIn lieu of raising an exception, on error `Staccato.Writable.write()` will\nreturn `false`. This is because I've found that with sockets, error management\nought to be a separate concern. The error should be handled the moment it\noccurs. We should not wait until the next write to raise an exception based on\nthe error we detected. The error detection merely marks the `Staccato.Writable`\nas destroyed. Reporting the error reporting is supposed be handled in a separate\n`stream.on(\"error\", [function])` handler that can shutdown the writing process\naltogether, preventing a failed write.\n\nThe associated `Staccato.Readable` class is provided to facilitate error\nhandling as a separate concern. It swallows errors and returns them into\nend-of-stream messages. In my code, for the most part, I'm always looking for a\ntruncated stream in my buffer processing, so whether the stream ends prematurely\ndue to a detectable file or network error, or whether it ends prematurely\nbecause it was only partially written by the other side is doesn't matter.\n\nMore to the point in regards to `Staccato.Writable`, when a duplex stream failed\nI'd find that I'd be detecting errors on both read and write, so once again it\nmakes more sense to have error handling be a separate concern that shuts down\nthe duplex stream processing from the outside of the stream processing logic.\n","readmeFilename":"README.md","gitHead":"f29d5812ef8765886fd78a8fdb20c1c3774474c2","_id":"staccato@11.0.0-alpha.1","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-CYPauEq9DtsDULMZ07gbai0W4/RI5ZQb6T20s8QQB7rk5lpMKr06dBlnZvokqQod70Sj8yeEquh7ksHAeoDyhw==","shasum":"1e0303dd3de460f79a30d50c20129efc5129a950","tarball":"https://registry.npmjs.org/staccato/-/staccato-11.0.0-alpha.1.tgz","fileCount":6,"unpackedSize":7306,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJddyV0CRA9TVsSAnZWagAAm8YP/RnpQBckbfVM0HiTdy1s\nC6Mo0n7WQOYqaoTVfla35RWvQ3M9xk0aKqoLtCQZilu2vffacsLcwy9vOABM\nt+6BLx25Mg8zLv5Gw6Ka3n/oYDJtdtdbMKU/GzHDCueXBSpAxUmaoMomXbdC\nlANCECTgZMHlZlDVgTDCYtPDU8pByM8vgXhizAJVkqY8nDQf8h+kZKvzY+mC\nXXoPQ+HwfAIJhW3nXQor0U3X61BPMpmqh/ra84IsH9Q8VGYarDet68iv/RcT\nGNlWD1NQytu5Crr6GoBuX2vqz8CwjulPcJ0OWGQywxbbrq4AbL9g+HzSMxNF\n1T9ldxiuuLFg8GyUyXyIw8/Do1z9wynDVOPcjcRopxcIDxr+pKzmdduSPY49\ns8S6SHphYLLUz2TeX0PyNIev27whpKTX7UamKu3ALZ9iCjqQimkX4nW/6pvn\nuAFW74jSAFL0U0IL4MzHFNlLuOcmRCaVE20dhFop41xnmJL8RG5KiAeevmei\n90MuOOkUnEOi4MGwDy1p3aGKZfYpF+ViBZVdZU1b3NfCANuKqMBr+GlQXdHM\nGOoSkpGrI9WHDZF0SqICJptk0jHsVf1a5V56iOrFKvkF+PxWaE5FH0xk++yM\n+a51I1vqkmxtL1t3CSIzrpB8v7936zgD0HpxyhXWtKWYGHgDp0aqQS6TIIxt\nSTjw\r\n=AM0T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDPAhCVDok1kRSUpmbt7YSPez09Zu0bvIrPJSVoj+qwZAiBNy7dtyyYn5TNHb+cYciH7yLNAsB/PpZTCC/DiEi8JyQ=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_11.0.0-alpha.1_1568089459451_0.8360025720135127"},"_hasShrinkwrap":false},"11.0.0-alpha.2":{"name":"staccato","version":"11.0.0-alpha.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"^0.1.0-alpha.1"},"devDependencies":{"proof":"^8.0.0-alpha.7"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```\nstream.on('error', (error) => {\n    console.log(error.stack)\n})\n\nconst writable = new Staccato.Writable(socket)\nconst readable = new Staccato.Readable(socket)\n\nconst read = Buffer.alloc(0), write = Buffer.from('begin')\nwhile (read != null && await writable.write([ write ])) {\n    write = consume(read)\n    read = await readable.read()\n}\n```\n\nThe above shows the motivation behind this library. Writing to a socket in\nNode.js is relatively undocumented. The documentation says that when `write`\nreturns `false` you're supposed to wait for a `\"drain\"` event before continuing\nto `write`. Staccato will do this for you in the `write` method. There hasn't\nbeen mention that `write` will also return false when an error destroys the\nstream. When the stream is destroyed there will be no `\"drain\"` event so waiting\non a drain will cause the program to hang. Staccato encapsulates the\ndrain-or-error logic.\n\nIn lieu of raising an exception, on error `Staccato.Writable.write()` will\nreturn `false`. This is because I've found that with sockets, error management\nought to be a separate concern. The error should be handled the moment it\noccurs. We should not wait until the next write to raise an exception based on\nthe error we detected. The error detection merely marks the `Staccato.Writable`\nas destroyed. Reporting the error reporting is supposed be handled in a separate\n`stream.on(\"error\", [function])` handler that can shutdown the writing process\naltogether, preventing a failed write.\n\nThe associated `Staccato.Readable` class is provided to facilitate error\nhandling as a separate concern. It swallows errors and returns them into\nend-of-stream messages. In my code, for the most part, I'm always looking for a\ntruncated stream in my buffer processing, so whether the stream ends prematurely\ndue to a detectable file or network error, or whether it ends prematurely\nbecause it was only partially written by the other side is doesn't matter.\n\nMore to the point in regards to `Staccato.Writable`, when a duplex stream failed\nI'd find that I'd be detecting errors on both read and write, so once again it\nmakes more sense to have error handling be a separate concern that shuts down\nthe duplex stream processing from the outside of the stream processing logic.\n","readmeFilename":"README.md","gitHead":"ae01421d56d176cca4dc5f25752d3688853a0995","_id":"staccato@11.0.0-alpha.2","_nodeVersion":"12.11.1","_npmVersion":"6.11.3","dist":{"integrity":"sha512-YqFtjcdQxZuQnCUBzKIHb1ry8lcErqzCgANIGn2k2/TdkznhMeJxq9OvFs2EVk+qMv15fBRQVlNNWgv2qR8HiA==","shasum":"dd3cc308338ec57c18335c92dff2bddcc8c1ae17","tarball":"https://registry.npmjs.org/staccato/-/staccato-11.0.0-alpha.2.tgz","fileCount":6,"unpackedSize":8332,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoVDzCRA9TVsSAnZWagAAvl4P/0ne+NQ19wF4FORW6UWr\nFA2oplHIbWIJThU3Pbpck+O3H76MlQKMMu2avUBTc4UKJnPrmVkm8bhan2ii\n/jzsXJKxUcJAdti46Zm481TSIcBdwD3Jhgw3jpExHBVa30o+MEvZP2unYoAo\nK6kt3qQEhUk86wnbs3eB1QKCuYPZ5/FfhTZxFbyC9bMglcYzrC+GkrHnDrAE\nrArPEH25q70/FQurQ/FiQEn7HL4w6x3qsHrPGqn0gK0tvYyTvd9NVUoVnajf\nVh7V/YXA18jfER/YJ/7AxhoxmolINJ+3AR61tUa0HeGiV+yW7cp1pC71NVNa\no1WELt4kmyoPOkCcQY1F12SD6YPMZRBbAKYX7NTwwoaL/4ZPEqLJecCh0uUv\nHEB3RH42lCnX/BQ0SQSFmT3fHP0PBKXyxYT7iUnW0+QFFX2V4PIKVGUmoayR\n33uOKgfovkLzXItH/F4YyyKqcy8CN7PIyGL1XKCm2ZA14qsQks+7DsRi+Mr5\nZ3H3Iblo8LxponLTKsljoDnnVbUisr9EbFSxjGXYb18oOWfOv0ate0GPm4Fu\nCsgC0EdV/dbtBXb3xSy9yAQSzbVKpM2cjxb6hm99owH9sdcZwc/DY5NPqNzm\nQtWtUqerdOEq7Rt/8geeCki0lY8+OhnSzzJpY8bmAkBk31m97Lv4bAYEaVkI\njUP3\r\n=pgY/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBU6m/KvFxwFFlvwQjOf3rD+a5Wk/czC+lEZgG7/s2J4AiBPn5MDhm4UnjYR9Zgav/4bIg8sH+vUptRUugmIAFzw4g=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_11.0.0-alpha.2_1570853106884_0.068350938211859"},"_hasShrinkwrap":false},"11.0.0-alpha.3":{"name":"staccato","version":"11.0.0-alpha.3","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"^0.1.0-alpha.1"},"devDependencies":{"proof":"^8.0.0-alpha.7"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```\nstream.on('error', (error) => {\n    console.log(error.stack)\n})\n\nconst writable = new Staccato.Writable(socket)\nconst readable = new Staccato.Readable(socket)\n\nconst read = Buffer.alloc(0), write = Buffer.from('begin')\nwhile (read != null && await writable.write([ write ])) {\n    write = consume(read)\n    read = await readable.read()\n}\n```\n\nThe above shows the motivation behind this library. Writing to a socket in\nNode.js is relatively undocumented. The documentation says that when `write`\nreturns `false` you're supposed to wait for a `\"drain\"` event before continuing\nto `write`. Staccato will do this for you in the `write` method. There hasn't\nbeen mention that `write` will also return false when an error destroys the\nstream. When the stream is destroyed there will be no `\"drain\"` event so waiting\non a drain will cause the program to hang. Staccato encapsulates the\ndrain-or-error logic.\n\nIn lieu of raising an exception, on error `Staccato.Writable.write()` will\nreturn `false`. This is because I've found that with sockets, error management\nought to be a separate concern. The error should be handled the moment it\noccurs. We should not wait until the next write to raise an exception based on\nthe error we detected. The error detection merely marks the `Staccato.Writable`\nas destroyed. Reporting the error reporting is supposed be handled in a separate\n`stream.on(\"error\", [function])` handler that can shutdown the writing process\naltogether, preventing a failed write.\n\nThe associated `Staccato.Readable` class is provided to facilitate error\nhandling as a separate concern. It swallows errors and returns them into\nend-of-stream messages. In my code, for the most part, I'm always looking for a\ntruncated stream in my buffer processing, so whether the stream ends prematurely\ndue to a detectable file or network error, or whether it ends prematurely\nbecause it was only partially written by the other side is doesn't matter.\n\nMore to the point in regards to `Staccato.Writable`, when a duplex stream failed\nI'd find that I'd be detecting errors on both read and write, so once again it\nmakes more sense to have error handling be a separate concern that shuts down\nthe duplex stream processing from the outside of the stream processing logic.\n","readmeFilename":"README.md","gitHead":"2646152be4e87a6a226c0d691c7627d79a0c22c0","_id":"staccato@11.0.0-alpha.3","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-xug2eD9QpbnbtXgYy2sIFoNGiMmg9vha13xmADbDMUbaEkhC+A5cp6qFZvpJDKzucbMKz86QVmI4YS2/QYLn2w==","shasum":"4a0f9ad3b6302f1f2ba3c474f6eae97aa6d1ffca","tarball":"https://registry.npmjs.org/staccato/-/staccato-11.0.0-alpha.3.tgz","fileCount":6,"unpackedSize":8360,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsEbKCRA9TVsSAnZWagAARosQAIA9Xlt4XTfKuJTFGXOi\nn0ZWHRmck2h4mVw7PwOrQJeC9uqtAAewgFGohF+C8hA88aIgCogDuffZClAq\nO/ELO3etf8gQjIDTS3xXmO/Z6DUaSB0k2Ewq4qIsvHeM2TgoY7RVI++L7kye\n7e9n35b4THzpBKZISgCAP3RpVZ4TcJTAIwwHXgjd6uKP7ax5Cts4dz7o+UqB\nJ65KTLaOhcmsp25TbXhd8hZPKQJ5+eGqdPagpzAHz6Fe7SAAZCc+ivyBW5l7\nC5ZF501KKD9x20T0OeuWaakD6h3k+yop/kBw9I2j2Q/+OO4TfPMkskyb/D1r\nKeyt7ZeC+Z6SPLwj2dbTFi7tRciWy/c4DYQh0hZ6V7p6XagU0HjOANsYIsJh\nzsIZqNWHvrhD6RQwFeiiJfulhzIWicM3jyMeUqbPaeuBP469gvjUb6nJ7bJZ\nOaH+CyNRm39tpT2RqR5bcg/AhZ93iVyJqAyTtxtjqg9Rf2lE3BgHIjhR+sPT\n0kZa5y07w364+Xk1Km9S55tzgmiGnfUEOnm8TUoqDBfFHzwAnOto5D+pDhSs\n9FGWw6dvxy0h2UV8X8Bw/cRcj6fw0q1xV9vk8X44HhBDUyRhaXV16OZmfWWV\nNQRTRIT6GnMsIdPgFr2qFfiZ8TqUQ8kVrHU3356kVwtYeLL1Zd35ZIUaCToD\nvxRC\r\n=WcNs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICMs/asr3mSp7LQGVWyUPkI1zUkav79XNb5K/RMfVVZMAiBumiIU3ytB4xcu2a7e5e2cJPujubuc1HueJ8oD9X0kUQ=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_11.0.0-alpha.3_1571833545911_0.9686073712849328"},"_hasShrinkwrap":false},"12.0.0":{"name":"staccato","version":"12.0.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"^0.2.0"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"f2b8104055b568c273ea50ab8885ab39b33d71b3","_id":"staccato@12.0.0","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-okKORrF7hD9z7+gR6ny7ON5S5m6s3DG62M3jDQQVfHHqFfH0JDfkBHXY67N2WZAZjOyO6SOzSzZVxkxBh3RHbA==","shasum":"646f23d16f8afc87006f8a0b2ea9d26a1007122d","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.0.tgz","fileCount":6,"unpackedSize":8336,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJesLI0CRA9TVsSAnZWagAAst8P+gLbkhJDkFbUfFdlwvrW\nttTMsiBVOa8IenRDJXCaEZ89vmCmUC72a5FmGiX0GZjBUj0tNviHh2PzUebl\nhCLgHL8boin3dleCuMaY6PB4hxP4bsZWUZQ/ZRE4XIYhObXlEyQJUzVCjtDx\n9UQX+VEpgwJdcmyO1yOi8vEZBDg4fq/N8ldn6sxtEQtQxeMFAc6M1fPdPt1/\nGU8VON8P/e2NQuZjIVmKmlH8J4dXgysnQtsovyBJdjIDlkSEdKYZNcYM9H5Y\n0OOov8eH30qPkBsWpnhEBaiDXPwergGPYkNZmucJ9EbkcIjx/c9WNPvtPRuL\nqCToJyzfk63K2FttonMJOHuGiU6+VSWZCCSk/v/ORfehc5I/GZuI2gdHP4q/\n2j49a+kYbJ40ngz8kj1OXwc6KqOhS6O7bPg7EpJf09uDIZuFBXvo6bEcy4+u\n3m5p4yjep4JnGJMAv7ItjIbTYX7uJphREwZn3ziI5Z4xRWQce1gHiBxCtqcO\nn0B/cV7bLj8PqG8CsfSXzZfQXjuck6vt0vbQJnZ0bHQbz8wyxjzvA4sDhICx\nTZmboZqSPWFJokyA0Q25skjwa5W/m+rF+FfdUdEC4TdPH+9SqHOFIMVUZXDo\nGERtbmUkoZhHAaQ1cAhryTeXT0OPaai+o8wBoQ2DlIW4iJ5EJoo/AyvdGlN5\nm4yY\r\n=7ezR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD+7BUtdo3qZf85xsb7qGtgPX71IjWGjZ2GRp7m6nZ+KwIhAK4yErViZfcjTtnjwUQDS6aEZ9YjD2Fzhe2/3NCIC+3e"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.0_1588638259879_0.7254011349531453"},"_hasShrinkwrap":false},"12.0.1":{"name":"staccato","version":"12.0.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"^0.3.0"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"8a24697b07b728fa4aafdb6fb2917ce527c58693","_id":"staccato@12.0.1","_nodeVersion":"12.18.1","_npmVersion":"6.14.5","dist":{"integrity":"sha512-h//LgUhGvaGqjvLcGNS9ExWiPHLHJ0SgVr321tcINvHPfMGmWAMpObnSvdZVN7PtDC1ZNz+eS5dPxic4yeOzuA==","shasum":"4a861a8ec8587c27c82926c866b6be67e41d40f1","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.1.tgz","fileCount":6,"unpackedSize":8336,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfSPUGCRA9TVsSAnZWagAAxkcQAJOL3HUrTUs+EAOSWz8N\n0Jmkr9OhgCy8N8D0+ff14QIuudTQgjDpbq0F8pT5o2fvQQAv3Td4u9qCuH28\n0SgqUS4SMBfvBs3cUjdCR8P84oMQ9vnFOaC2QpEG5N2sYi7+RiUfI/p5NNl5\n3l1rHjWiFb7bNhsrpsj+7GjxKKN9iXldcPGTwJceHXPBjK+esnYzLGMjMjc6\nA5/qbU1NcKIOv2cQrDnqHwb+4Mu8qgVZjr0UlZNDEqDYesEvi8hWAVRivu9n\nEVKeCH1ZqoMF1l3e//W8JpWPBxSeL4IzMBb0K3Nq7uVb/QAOLKHvmyDtNwcv\nzMoJ2yUQM6heVsi+zC9gGoZjBa77sfucOdtJzHsEfIK24f2uPzPwIk5YKlES\nzfnaO4UMFor/RaXD+S7IfQg4hueUzrcm4iEBq+D0L0+tHqGDmABHhAB+AXee\n7EzPWO++kaj9Ri89ten4NA6FUmG/USA4+VR95tpxdM/A2rz418rMxu3EO6/M\nnQ+UnEqZ4SktJE7MLKrd5H38YihsXPnbXwX6nVDzqNB7x+PtGgPVexA6iSQ/\n/VcVfqhUARx4U86PJld/Q95KCpb7kocOgtxO2/MQaiUu8b/pLsvAhFiPsyBL\nMwiUIvRZo5ewNAlkRFQWtQ8uTtsZ6dbFL7s8JyxMwC7oDHga26Ep/3GytNQN\n39k7\r\n=LhIs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCZG1bJuihj2m7s7fdUWGxB5j7UPJP8H4EaTdiEf0IsjgIhAIo1MD8PS1SyjsbUkSVagfi9/mp3JNKFnpVxo0ZSpCJG"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.1_1598616838398_0.5731638268584662"},"_hasShrinkwrap":false},"12.0.2":{"name":"staccato","version":"12.0.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"^0.3.0"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"301af6cc1157fcfd74c2b829fd7161357a160964","_id":"staccato@12.0.2","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-627cRBUBmkbTD30/PpEhH8Yt2A3lEu0BcbA1uAxwni4wmYVdnTdLIRD3i4G48WMeJ4a5TLQ6cVZkNhlWHtXqsA==","shasum":"857ab5abcfb6562cd1ef0b10f772992abd693490","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.2.tgz","fileCount":6,"unpackedSize":8233,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf22nSCRA9TVsSAnZWagAAmPkP/iS4zPoLABOfM1BaJ1mU\njKA78AcaBHpFrDMoaQH2jQZMqIwQ+F4JkEhMfRS33LWT/YE4FtRAqjf90HEY\nc/ejHIpSxfEyIt8dHnUPVeMso7DQNdpQ7uRd5b8ZgIJrKcx2SVnh3jitM44H\n8WfcDEclLUmCpPq+/G7XusztV7LDLeYIzkdBUJcWwJ1QR12CJKgl8dozYGW7\nxv41Z39qZPno1QjdZZ5I51plFT9135c3tzg2YCZnlcD86QHUXyIQxqtjqDDb\nygiDWlFu71gzI7cB5xIDMe8XUn87O+e8RLiBJ/L3okGYzB+MiQz1D0ntYrIb\n8PTKBPXvMmC3AyjAfQfSO7UhsIp5IDcvfuNM39oBKM9ZWMaSQ1mgHknU/XsT\nsNTX6C+nSBmgBonComSY34/XCSo1fzqOq1whmdL1GihpLw6yg5t7o/6WD6kc\nunKrNySLdUgexDOMRSIg8nzdTcmIjfDGsGSbUAQ46U7H5JVyY86f9IbddXTK\nj1y99mOcRFRtCs1ETJoRIxujrhltX2sUScyl2CI/UVjpNkc7vLfbTvCP1OY+\ne9DlWebiTUZoVJI5pZonvvzDFOELVUjduqscoogJnFSitjCU/Nnk2/gEhhKj\n87Bdgqb0929KqvpB2LPGh6WtVyN++q43ulOMVqQNafJbKIo/reVbU1+aGQOr\nEjDc\r\n=wwAc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDmT5wcHqd2YYSMZtVRoTwi5AElazGpG7tZP2gkeGbwLAIhALsdK/fJO+OfRwloKCOlHmnzoXjiFSpboE9as6PAtAu0"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.2_1608214993951_0.3469424058531918"},"_hasShrinkwrap":false},"12.0.3":{"name":"staccato","version":"12.0.3","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"prospective":"^0.3.0"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"24b6a187222c8d955a6f2fc0f73bc66f2960ca8a","_id":"staccato@12.0.3","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-sJx62ZYIe8JajTwSpNJcpuuPrLvk7Fm/AVu9J3mn6XfheN4cp5sGPplUUTrp5OENvOX1oToeEnvlejHdADwZzg==","shasum":"afeb4490eb6ce4082c962bee3e5013e0b46d97a2","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.3.tgz","fileCount":6,"unpackedSize":8184,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf4HrXCRA9TVsSAnZWagAATEsP/2CAfvTXeR4i7cvZ0CsC\nQB8PY3hn3xsG4ZAFC6KEqFnNFyJoSxIOtZRoSlLWyFUfG/GgcMd6DtdEUmXB\n6O/NrkJ7VpMKTihNU0Uuhfirn7xBkZ8ovuKPHnRFFgE15Ze9wFiH1uwx3K+F\n488nByUxL0X2VHqVwdhe79TY/79nzgX+PliKu+1VmEPYShWiXG6dVMB+1eYN\nMDQtg1l18D536dzpSA+GMiUCFAoqwr7qCOZNGShkkeqSczjIrFroUgy3odOm\nYLkqwArn/QSMBHZ39C0eP1uh14Sd0RiznTb3rxu9oL4iyDfz3UsnhooEv4yE\nrPzGbbo6fjP3IagbMRmIBaXvHcK4iJFyIoaU+Y/IS31H7pAZjzPeVWCxZhny\niGRQ2IROORKVChS5KBkHgmSKzWKziEyNV9noLZy27TcMIcWjC7cq8mmxLA2R\nnds7rpVUjnhRMrEQSbc+lzNmQGfLOu9FXkLX5YQ2aLicaOP6G4e+suXLp9dl\nBmDL0TJYJW5Ytr5zmhhvFDFClkuB0q7p0DqhkM3M3RyJqL8T3H32ttEkIVVB\n4TutayTaV3/KDGfVTls66ofGowl4KJnPEkhkJl2ZpVfl7Ss2X07jvnuiYbiP\ndqtIik6K/Db9ooge8XOV6aCTthxvywEOdrkHf/icl2+neloksjXf9I+C/P5E\nj/ka\r\n=ezHV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBWVCZSOf4OvP4PItgHBKCzKZH59MDVpw5FaE8+Ys2T/AiBS9gMq2ks1K96O1ijo9zkbvGz8EEod7pY/OzLaYPeYFg=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.3_1608547031065_0.2755897314705764"},"_hasShrinkwrap":false},"12.0.4":{"name":"staccato","version":"12.0.4","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"interrupt":"11.0.0-alpha.13","prospective":"^0.3.0"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"7104239d53d499c85145582398feebbc02618d31","_id":"staccato@12.0.4","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-6Dp0lb0JAvs9cbyeZ5v2fGQnreFuIY+S4arskSV+5qQ/WWxk7g3f4XqJ8i0Vu6HQiho12W5SvhG3fT53VA/Aog==","shasum":"ad617c278541bd0a9b64dce14338da4359254c69","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.4.tgz","fileCount":8,"unpackedSize":11754,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5R0ECRA9TVsSAnZWagAAiggP/RCs4W36xXAQi0GI5o8i\njB2EoBhqczV0QjYxfkp1PQ3DBMrEFVfUyonXgo6xf6+6JcNcyfmsDMZcnqkd\nAvvYXBDwYWTn4LlfjUyfeAt/TGUaYJayo0BqZ37T28pERvIugTRf7Dk5R+jz\ngsW4bfy4riiXvH424xFiVO9AaYit8GAGHCYHx4PBmoDFvSzWb81GpcGlHMY5\nhqDxbcMQxekzxO+MpWvpQmDpOH3w26QJYN5I8diQPh2O8qBEuT4q8gUK4X4N\nSFvAJ05afqjS2wszbdyzUo8piViul7Fr4Btdg5NiKnCsBxSqmgbQdBHBwf3H\nWHK5q0wHJV8NpsG0O45+WLzPAcEZ72RosnzJ5Z1VNO+OvXus8V2kflJTFDXp\naJjmYRpyFrZwqI0ZyfFhkHsHeAFqEfYairRYlmIxy4/gfxjVQ5lsn8f3zAN7\nDDY6MG3ctncW3joH/4ldd9Q801OHSuKPu2X7AGyYcKu/4MshMXktNkEkQL4H\nYGxadaU2siwWEJoqVB86k0sEGXkKNJm5GkmiOBqxhCirOA8KlbbBGbF8Gmew\nl3gBhNAUs1WZ+y5NhlsIEfOfc3QA21g67PKhL2heYQkGsBFZUk8D/45P3GSD\nVe7iH7xaFLM/qUt0lsdrKOABrtFHXLdY+GpsWP8mMy7tt7MwdHPZ8IH9S21K\nQLeU\r\n=qiUc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCX1hhUP8+lO9PqnvuQ9zUbEhXYLf0i/7oeAQ5LljY8OAIgDgNRyKHAQc73ynO85ghT2uD/+u4baLw5iuQC6s66DGk="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.4_1608850692235_0.029923479931292407"},"_hasShrinkwrap":false},"12.0.5":{"name":"staccato","version":"12.0.5","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"interrupt":"11.0.0-alpha.14","prospective":"^0.3.0"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"6bb5c6c34f135d8f8109618f966fdf7198385e17","_id":"staccato@12.0.5","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-NxbDJDqhOQCzhKEzyXeY6t5qmyrnG/0C6NH1Bl6tocha4xslUppNrnDCzaHSU2lYPYgLFegoKtJMhjMO/LNm6A==","shasum":"c6d7f96bee86337b01ca5b89ad223e2523f8cdac","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.5.tgz","fileCount":8,"unpackedSize":11754,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5VOzCRA9TVsSAnZWagAAic0P/1m7nUhcI+mTqaXaK12Z\nsZb1fNQK5mNrbxTXkntpAoJS2TT9GCnmWoHTZl90G1wxjys9Yl8c79mM7Itv\n44Ot2P2Z3r+58qkmqxAG0puWa3aSmwGp7VjYZ7UNxI3izzswC4ngru8GZRbP\nSW9BkpG4xDDiQu4bLvlsHf0tVsHBc7c4nXqGGXvhXW06Zz6oIHus4sYPY0Ua\nxbqpzIPFzYmYvCKyRpiKzL03zYzwDlgoUiAc45fqEDKU+DNqVWUiTxjeKKFM\n08ZzGPfzBNhHsT586EURyrxnHwPMk34Rrs0C/3bGtC2QWdxs/ys4CmDWvjPb\nEFJtw2HE05+UuEKUgMi7lBmur6W9iWlwahqtqiIipYIXHqTcj58Q/t9UXxUJ\ngiNyzgIouGFSC7WEbuiWpaWXsCXNs001zwk08B8g0A8Ql4yYKtsQVz4PeHOU\nzbyhpxl/CjPouxtiB87usv/Kt5mA2QwGYzw38U1JCA5m0B20DmRDkPo4jSUC\n6uQbijIGVhOFAm5Jmfj49+B6QBDhTkzkloqiK+bbTfa97bxV3uwoi+7GcbYf\nkmpupz+IUG51ky/TYSPz24zMT2qhZW1YbZpB+lcEDLfAoNtjwBvEOYf5C42l\nQIpb9iAVi1FZ/hKTtDNb9C9fP9ePM2f7gu5clnoU3BgzkulcsoZellC3vLet\niiYC\r\n=ahPU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDM4vpLbr8FH/bLMc7Us/CL2iWcCWDmCxNvekAzosjB8QIgM+56wv3K3jWCxmUF9gEogBlz/ARq3F4AUjE6bZtzqHw="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.5_1608864691064_0.8509545931323017"},"_hasShrinkwrap":false},"12.0.6":{"name":"staccato","version":"12.0.6","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"interrupt":"11.0.0-alpha.14","prospective":"^0.3.0"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"639d4cc2af942c021fc5465e081fca1d9b80aee9","_id":"staccato@12.0.6","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-mTKGNxfuA0QnrosrY8Gq98QM7qY8djZDECfYVltYmQeA/DSJhS94G0xr7iC/zalviH2y99KSrQr4j1XbBhogWg==","shasum":"17a74b392739ce6b83f654352fc5ccefbfa5705a","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.6.tgz","fileCount":8,"unpackedSize":12013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5zM8CRA9TVsSAnZWagAAgQ0P/0uebY7qsJQJgxPK74XN\nwWC2CcGzKU0+LH7Ley/2adFyiZ4vI0P7xP1GKVdWRnUlqEda0grnKNdUyDUU\nS/BdKG6Wol9OKlc0ggzcCVI1mJmw6AzQH9goh5uxOvhqfn0aPTqDYUis7pZf\n3xzdsfHufKHB/wbwiyqW/LG1XKeRyS4ulw3YNFqwg41XLb4Rot7kiqDLrDjH\nRtmw3p8vg1DjCNmPy+53BzYOQpWhsYnByUVQLUJO9KvSs9lG1UJSdYuC1GG/\nrVsqqqWuccQyCVpuUa/o9nlIuq9A/vDn432TN+L2mT54IoWIs/iv1RXjVaMq\n57VLl0NXcEGO5zI5Nu6YiT3pTCehicFCbbDhu3dNRwICqH9XLzOK8gDaF0ek\n86kxZhTcQspVthT43/j9gySFrh2LkxmXJSjIoCZTEAMSn05JyCTyXoV/ohko\nxMDfEk7fSbziSjav0vUjFK/0Bzkyc6UoyCh1XWipTDuCGxm+7jCFiAOzITYr\ni5mn/b+lO7Q/C3dO5HgMUUX9PVY3g/bEky3F0W1JE8L6VLvFyzPUsylxWrTH\nL3f+Dt56KcgFS7xLvMO1NReEOuM+7M9lkUoBb8nuX9qMCATNnaoTzwO3TWOK\nSWFZSAvGKSYF7kXjpEeDVaMhasyEQYLD8MoIP76+lsvYFVC10D/ZSjwbidRw\nYHOQ\r\n=IXk4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEI4utV9nnBYDBVndiRxj0UtwgXjdJJaP+Zbq7p5Me0eAiEA5tFTfguXh3eKKviVV4RnYETzeYRqTaBGMmMtXmT3wmg="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.6_1608987452118_0.8589997759307577"},"_hasShrinkwrap":false},"12.0.7":{"name":"staccato","version":"12.0.7","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.0","interrupt":"11.0.0-alpha.16"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"1f3a8db72586226e32c320fa2c544aec000a9dcb","_id":"staccato@12.0.7","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-+fqW/VKTIW+NG4yp3nDCQUay0mJL05zNe5FrJgzY7TQqUBP2iG5AIubS5n3iD36OBlWrMchKPvMgxMaH6u/dqg==","shasum":"079abcc932cb85be614db6005c0964bbddb47691","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.7.tgz","fileCount":8,"unpackedSize":12456,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf8aI+CRA9TVsSAnZWagAAbOsP/3ZGqHuFECODiyQD+xG0\no3rIaUofa17dcWuerZdSVKOuJzBdpn9fZYsy0T5b3CA11JFKEu3wzDuFuOk9\nfy25wBVgZyuCX1G1GxflVIChkxGPlPnk//SLGHXh7kGZb/mYQMzBS2aUsK1D\nyx84llyIQWoEeyvNtRLwMWFj3fq0s9uZEkN1pUXaDGSkzwjB6wvuqTtGWGr1\nU/2cOR13OnaO8E5xuVBY7P/eN05atanjrw5ehmnGdagpgcrMqf2WPy0kTS4M\nkkj7kfxy4AHQFRx+68MGzEn++YYcasmlElU3GU7ab6rnw/JP093aG0dsmVIS\nmAy9YjPAPm+3RVvch0mywJjcTr+zk1ZNUzsdzbAC8CHq6BbE5RqcKy2NaSvx\nlsGJW6TcdnlzOmip4C2IFEnKu1EHbKSapjFNswynEcRdIrSjOgpJXyGzURnH\nxZSs3jTlPvx6VnKJQFchxcJJv2OnD+COvOFyjLcOxnnx8O1+8wVavrN3z6bE\njLH0mTmCw1t1y27Wdx6iDeZErwrYqoyx2jW8YkhW99apFzqEEWowvFZjxR9l\n1DZPZWHR8skE+DOrCcpZ+LcSRHw7AxBkljC9mfV9u9JsDC9IQMDnK6xaQGVF\nUXcFLVvl/TWqWX/2iP98hb9T2n5g285huG6X46SZOMDTdmyshmDOL0ILXN9i\nW5Ft\r\n=/0AH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC1tSYjvn9eA0DO+iOzHTfwVt6gZYmpCBPMFKjSaFovmAiEA1ApihWQcj0Op1H6EheiqZi/XSD4bEuymPcwyRBgBrCI="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.7_1609671229549_0.2011980650377554"},"_hasShrinkwrap":false},"12.0.8":{"name":"staccato","version":"12.0.8","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.0","interrupt":"11.0.0-alpha.16"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"d1bd34f35d4f2e9239251aa27f5e11215ca57457","_id":"staccato@12.0.8","_nodeVersion":"12.19.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-vbhZfLvOA/L/COzxPoeCqpDUDxaDaYIUQO5oavfT63CU3JBpNa4ZBmlcVuTDh/yLZM4sJWDrHrBSWUOrG3PPBA==","shasum":"710ca92478dcc0ad21a371bad5b9d0cd47b42d8d","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.8.tgz","fileCount":8,"unpackedSize":12776,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf8kWvCRA9TVsSAnZWagAASBQQAJROmR1vYqRGMG929UEo\nMTf0x0g0J7G1ZJdJu7UMlAuILPQhCholv54stgv4kHPSBVSqYBEIFaBj4cZC\nG++Ntv6Wi35KHLb0yFwepehA4ilSc6YbL4gkYkZfB83pb+tR3Ehol7NiXf9/\n/p4aXo39ayF7aPJ0GyxddC1HYiesZQ67gCP8wBg5wHpmvrLF7yGrhA6r6h+M\nD96qiRg3caFgdSKemhytqR2uwOd3QonZKtPtQw/fD85kN3BSwDs8bHNvpkaN\nvQCC7YPTrQFg/jWDbulMzGnhBfk4lwljNuT1HmvgMHQXvNoZ0TFrkkRxzIei\npZ45K8vLzu0cXZGNkPUgXALdc0Y2HySgQCI4efjUHGL101OLbNJQ6ZCuCgM/\nRBqArSjFobRtXWsSKwIk8sA3uuGUWIf0H59H6WFQ9jzsA0XLjE53O/ERyGav\np8PfNp8V0R9NtAIWJROa49McBz3WypTvIWt5HWa9v7YclsUS7vuwu7m0BODl\nh5ZuFxtVENFcIVbnNbfNYQ5ZP+mqNEqn30gJ++RELgR4incBLwlwwVdgwnIq\nWV7A9a/0PNa6r6aXCmBYZtVdNK15X+3HLcVoPCNmxGJEvXmcf7gv/ZIuAnHW\nGQSEQFln/b5XBHeWsSilfFIEmMuXlgoZqS6sssLQvbrrxnVNE0DVI4NIipP0\nob96\r\n=ZpFM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC6J/conunsrNA4B2aGSa2Ik4AA3MTKErpKO0MhaPEfaQIhAMaZhfyyWBf7shSO+z8zMQPUGdLajMnyn3rL7+zRroke"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.8_1609713070947_0.6347143107626205"},"_hasShrinkwrap":false},"12.0.9":{"name":"staccato","version":"12.0.9","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.16","perhaps":"0.0.4"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"aadd1d4d64b115f9de9316870eacf18aeb28ec3e","_id":"staccato@12.0.9","_nodeVersion":"12.19.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-U98Qi+WRLj7yC6MNnhDfK64g97AOcg38Yb/zyTQXTgIrudViKlTgn5Q4nMS6z3822RWvkZp6BPdyo572fNUq7w==","shasum":"30ab64b99a2f0378fa390006375a0b10a6b2e548","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.9.tgz","fileCount":8,"unpackedSize":17838,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf8y/zCRA9TVsSAnZWagAAnkMQAI7mDyBdgzT5cu6XLS5R\n8dGS4/917yhxH3UHGgqQVuMJMP27xo+3EubwbUUvD6NxtIjDuQX3Jz2QneZD\nybDeVaSY0x6jQWq9Qa1q8pnHo6flNSJV+l3Q6wHWoFY0XYF4YlwScVX7KqIu\nbBnsEZyDHxlOeKXlLOCtgVckLngoy6dxKR/LOH4ZuFXY+7oQBm+Cep/Rb3AC\nqO4IX4oKmHvBjUulA4u5HzL3gL07v9bCdEuYSE/U2PgO2Czra+TvqM0nkh+Q\nnxeYytZtMHTbNU6vGvVXn/IwOA+yQRyEsdUwOf2FUsbfle8Q7UQH36daOFrA\n2rjCD3S9tv6773ZmLiKjwScgugjNVSWqSvwLDLJbj3wXn0anivp+A0ao1A9Q\n10Vu7QCwshoLT3K0A7kW/Ggpt4EWFj2ew2UWm6jBWJlHq8T97lhhgATG+erg\nVXRvCFOS4A6oMDefm7lZzTowmHrSSeC0JQoGa9tsIrUyV336AqWsBdql/fsO\nUY5BAJsASEop6ETYShtOKUW2oa6P1Jb6pz+2FhrkEzsOLUW0g3M5D3sxGjU2\nFgdM30p79r2bs65Zn7uw3HUavbcsM/Bl/RFdet963HiwDHT0eUUUIy2nSf+m\nnZz4eqCk9S7C6X6zhjwMixD8tOdOfs9oU1OetXxRIIjd+FkI9ATnk+UcBnKd\nJPgq\r\n=ceyA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHN6vPI4EpIm+/AGPdRdUwDukEkB+tfIcmSqXRnniNlgIgI+Mpbu29Z6z8W8XiJWENoN1503ZUDQa3tN+CT7EOg+A="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.9_1609773043307_0.9834050620249271"},"_hasShrinkwrap":false},"12.0.10":{"name":"staccato","version":"12.0.10","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.16","perhaps":"0.0.6"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"d39e69dbb194f8b1cc2452d2491b2bf456bf0a37","_id":"staccato@12.0.10","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-FeqKngeq3CAswmIhkF03oA+zqH9yxahWCjtKCfKn/3aht0fpu3MDzqDlZUJo2g/g0Lz+df7cf1rQijnHKZzNAg==","shasum":"a65fade8d0f7986f1b2528e9c57ad5dde4ac1e04","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.10.tgz","fileCount":8,"unpackedSize":17825,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf+01RCRA9TVsSAnZWagAA8zsP/3gOfRQam8GqYgXG7Rng\nSm8U/WPu0/NtOsdftpTzf+WBTBbpoWzWataUetVpx7ZnhkAzdcZyfIk2OcMo\n5aH0s6dvEsH//KBs3TKUhtM8pUcaJclhdFPTRTrVLS6aZDi+YY9tBcmehLUS\nedatWJDicoe+MLImx18y0u0vkSz/ZUnWrQe3wgXaHK7EzE4kk7Q84HOLPPu0\nzIPzJJgJDTsQRe09zGAzoalrU7UQmY5tEUoDFR/PqqsSP1P7kT7tiNFj/6IE\nBrDSH+nJE4oYzJWxhS55ic04STtOeBTXo5gknXeL6kjnDhZZ0nFLXYq7ob/N\n2VujzPpaX7OYFlvSWUeltIeLeTLfsWi33A5qt56cfZlQC9Rru36YhXH/0E5w\niPvWaXIi9rtaFrYL65nEIskIse+hRTo6uoH/4pRmdUQwNBB5FMt5G+SiKI5i\nGljbiDf/mKBC4Zu42dTR5hmegRNEokKRvuft6feg389W6P8UNpt6KeVmYZ+l\nVvujU9OTEwhw89ME4VRwv5qVWiwUt6Ris6kVYCSvHt2CJz3i/Xs63S6Agq+8\n6y2aoG1qQHYA6MS2Np7jHa6OYpUK/wz59msxrHuVc7QhvjFwfFaoDw65282h\nZiMmjmc39rl2rw6ItDrzoCigGRObUFcCANLT4t7uerhjhPycEQhOZzc0oAbw\nJcob\r\n=bhSn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDiKHtGoT+08h0cY4qJ0tnaurQy9cx7EVyPN1uafA8QzAIhAMvgLxLmQjbBw55arNjVEfhfFsLnCP3R9hmBDbIGZZIH"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.10_1610304848519_0.839829593044263"},"_hasShrinkwrap":false},"12.0.11":{"name":"staccato","version":"12.0.11","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.17","perhaps":"0.0.6"},"devDependencies":{"proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"gitHead":"b21661d7600ee568212084686f111c537c851d0a","_id":"staccato@12.0.11","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-iCWxqIO7wP8P7ImeCwIMtrJmkrrIp1R0vWmC//aonRJcYzdk5mFU4rL3p63/kIzOCnM5SzGol/doetAn5/XsYg==","shasum":"a6b8ff6ba51968c63e264a173fdc9d0ab2fd03c8","tarball":"https://registry.npmjs.org/staccato/-/staccato-12.0.11.tgz","fileCount":8,"unpackedSize":17825,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDs7ECRA9TVsSAnZWagAADGYP/ipTI2TjvICWl/LrbchB\n8vvvJ7tm7HdI2EAf23d/EWwOfpJzjKbVB4Q5BiDJdSS6/U1N2/1S6oNtBDM+\ns1yOvQIX8rRpDP98cn+4+D8ZP/PsPFdQCAW0geMiudZardM6QoH/6wieNXDD\ntXLoUNUb30IDimWz6ar+3j4ALLlrjyxdcs5M7HaQmduhg3GoYnL0A6pt1HFZ\n8vu3wXcbkdOf4VSKUxQheOeMPWtfACvLnGcYio+t5jUqLFraFCnDh/FzIPYp\nln1F0ICVlCLcvwdM4Hf2oxSxOQOUGWRKtDiF9skTffDiftyTLky08O3AJByG\n7/sLGalNl4HeikqS6VGmQGBUHioMLld9yTgiCnO394c8rB1eFOU04mNLdWeD\n4tQ7T7qk8E+ZKVrttAE3K2GXv5AzP6PsEf+5AJwERXgJGSLO5rh9+Du7wwkd\n8jUqGYx7PeFuQuEm0WGgmKPylr55XUcZDFqAEFOwWJBoftFbn0m301ckYbUz\njBoX19dEo/IynvMFTjUusVk4BUY6+nAZ4gaV4d6Gti+SLgOSqL7qAfZ2qjtv\nDWG9mP+JQ2wvcxYa9ytes6MkYiSW81ByY7C8tWdLSTSP5xUJyzsa8n3+gyl0\nzcGicRDSL07Bog8WVQHiX58jJhLqrQr8xwyTzD5TVuep/rENzijIvzdrbn9a\n0awI\r\n=pTTL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEPmJhbJs+UmT5mLxVMd48zxgO6XUFXkugq3NorsrhyIAiEAkK8Q12yBmLheo8oyRnO+qJuyCUvQ7Xzl55zWRRHHj3g="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_12.0.11_1611583171843_0.3527743010773057"},"_hasShrinkwrap":false},"13.0.0-alpha.0":{"name":"staccato","version":"13.0.0-alpha.0","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.17","perhaps":"0.0.6"},"devDependencies":{"duplicitous":"0.2.4","proof":"^9.0.1"},"scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket to not map to exceptions. Sockets can emit multiple errors\nbut a catch block will only catch a single exception.\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error, the reader, the writer, or whoever got there\nfirst?\n\nIn previous implementations of this library I'd decided that the errors don't\nmatter to the application logic, only to the system administrator. We wouldn't\nrely on the errors to determine if communication has been cut. We'd assume that\nsocket streams can truncate for any reason, network error, a poorly implemented\nclient, or an attack. Our protocol do not need an error message on read. They\ncan verify the contents of the messages which they ought to do anyway. On error,\na read will just return `null` on the next read. Errors are funneled off to a\nlogging mechanism.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator, but\nyou used to have to test for write failure. I'd like to see if I can remove the\nconditional and replace it with `try`/`catch`, not for general socket errors,\nbut for the specific error of write-after-finish.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams close too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nThe documentation says that when `write` returns `false` you're supposed to wait\nfor a `\"drain\"` event before continuing to `write`. Staccato will do this for\nyou in the `write` method. The documentation for `write` does not mention that\n`write` will also return false when an error destroys the stream. When the\nstream is destroyed there will be no `\"drain\"` event so waiting on a drain will\ncause the program to hang. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n(Nice to have this function. I'm glad I wasn't imagining things.)\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem that, again, I'm glad it wasn't just my\nimagination.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nCurrently an alpha implementation as I walk though my code replacing the older\nStaccato with Staccato 13.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst Staccato = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst Duplex = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n","readmeFilename":"README.md","gitHead":"882838930a8ebb924a34cccd30deedb720be9124","_id":"staccato@13.0.0-alpha.0","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-rWv4KDMR/IoSgYTLixeKcbIKxJ7y0gUvhfdgTYJXU8/YqDl7zFf8V08H2PLd2dDL0U8KW6K7k6u09BwwQ1Rn+g==","shasum":"fa76c95ace0957df0d20183eac7050faf5c6c232","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.0.tgz","fileCount":8,"unpackedSize":30268,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgE/ZXCRA9TVsSAnZWagAAR70P/2nULHCVLKkFzFjPdAlx\noYAZa1HwpFCyaB5xD4aJXRqJUrtfTKq2tasCxw6tx0Bg0qOl/OTgLr8hcT2C\nkNpDmIq1FezGtSpzoHTRH99/ky2UpOwoNU7RkQIiKbEk0KqsQ29tult5zeIB\n0KIC7ahKw39o7/uT1+I1ajy6oBouIW1gZVa22NGQ7Di4C063uv+NSR3+qqeg\nnQMVVR1ir592yIBxwfjJWESz1XBF4sfSXyMjx/pxggENfJiJPnefDv5HHblp\nNx+iEzYXD0Knneo3dRK8i5ZoRpG32traE5pTOsQ+9uhkXsS2zQAgUwQeHAc+\nNO2UKnyx+F15TlGrewnFP9gKYiZsG/e8wE4lB4tgiceIainOsnYT5bbJckhO\nh8phJNRfk7mZaoxSIQ74nKQGzD7VEvCzLNvZ8ineM9wvHe/ZjWl+D60vOVki\nxDY0c5WNTyKCN3mvaRh0gA4PpIBkIIDFVdLBlGjEjw23CDsNEXwoW/kJISyz\ngpDdGqqufI7+DpRm0No+gT32t9wD0xz2UE9TOdLdwlVYqmMw1Vi1RZrA3FYR\nQVzhTRQDK5D19DrcHDd2f1iyl3DsrJT034P37AzfMPfb4qXp2a5ZNTjCeXam\noude/7V7TM7GYQuK1dpW1SH3BrG7bqky1a6NwtKU08fSU3VU2EYpeL6++RBg\nd84o\r\n=mrFv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCyEPhHq2Iaw9X6sDELEUd6xa2UzulOj8pLGkMljL0zHwIgSm0JDXzmFIZX8fzOMLcNoCjZnwWn5FKJcQv5vKyvWCU="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.0_1611920982553_0.41972030370665725"},"_hasShrinkwrap":false},"13.0.0-alpha.1":{"name":"staccato","version":"13.0.0-alpha.1","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.17","perhaps":"0.0.6"},"devDependencies":{"duplicitous":"0.2.5","proof":"^9.0.1"},"scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket to not map to exceptions. Sockets can emit multiple errors\nbut a catch block will only catch a single exception.\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error, the reader, the writer, or whoever got there\nfirst?\n\nIn previous implementations of this library I'd decided that the errors don't\nmatter to the application logic, only to the system administrator. We wouldn't\nrely on the errors to determine if communication has been cut. We'd assume that\nsocket streams can truncate for any reason, network error, a poorly implemented\nclient, or an attack. Our protocol do not need an error message on read. They\ncan verify the contents of the messages which they ought to do anyway. On error,\na read will just return `null` on the next read. Errors are funneled off to a\nlogging mechanism.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator, but\nyou used to have to test for write failure. I'd like to see if I can remove the\nconditional and replace it with `try`/`catch`, not for general socket errors,\nbut for the specific error of write-after-finish.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams close too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nThe documentation says that when `write` returns `false` you're supposed to wait\nfor a `\"drain\"` event before continuing to `write`. Staccato will do this for\nyou in the `write` method. The documentation for `write` does not mention that\n`write` will also return false when an error destroys the stream. When the\nstream is destroyed there will be no `\"drain\"` event so waiting on a drain will\ncause the program to hang. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n(Nice to have this function. I'm glad I wasn't imagining things.)\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem that, again, I'm glad it wasn't just my\nimagination.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nCurrently an alpha implementation as I walk though my code replacing the older\nStaccato with Staccato 13.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst { Staccato } = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n","readmeFilename":"README.md","gitHead":"de226cab895a366905356acbad858177f471619e","_id":"staccato@13.0.0-alpha.1","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-RExKPys5aHhSMl/bDoZp2gKr68d2P8vINKKObHWpSAMfNIm4s0mr5zsmWZ4V9h6uFYb1sYgflNm3ZQZCt96bFQ==","shasum":"c3212dd271ef72b750b851ed00e3fe4a6bc76a0f","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.1.tgz","fileCount":8,"unpackedSize":30278,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgFAXtCRA9TVsSAnZWagAAlc0P+gIiQbyHAYNaoOzva3I0\nspGsM+4tztRccJ/fUYeG17wWWDrt/9/+h+tX1q73JGnGZjaGskJPfJr1nlOz\n1Szsz0HgSRocIdCQDLaEzK7+aoql6D/keHqrDyR6oLyl1/hMsKwiSlviE7RH\nVoZS4pMbDkTySRd4oP2JF2j64AHBLqxt+waJ29yBA6sislo0NZTihRCc/zCc\nSnKb/Q8U+cZA3HPvpbV7EJowRmldFpTrg/qe/IQPkdU6ZP2ESHE0AXRu+sR6\nx8FeJNfrreIY0u1d5F8HQ36264fJZfTb5vb/8Iz8+IH5bhDYBJvR/YXTA1Il\nQby2KBe3yHGBnDt27MVaEUZJwqD2NQiGFjYcqHyQvdYnQpRwJCZwkiuAIWFa\nmx2PRvaCFMqFoab+bbBbpaYttOSiQ/qkp8PZi7CN6xDZjOEnNtXvs6efBTGt\nAtskpKvpXeviiCHSzy/PdlSNSKNAN4fbkcrhFBJSm5z3CXQVate92NU11x7O\nTq/1y0iYDbpTnRJXmvSPw1LjqtFA/ozlRbOyosQufwfQtdj8f1tOBhsCSg2L\nvtrv2oqwK26+omV4y34CatPurR8ZlnYwuzTjekNq24IofpQjgRGMGCMlhj/h\nwgREU1aWaHqAC6xz9gI1LqNc2zv3mP0kkp6w5nNJt39DngcxQDS1C2+XQsRj\ndSWO\r\n=jYXX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICYUAdwzl5YqfCWDFU0YSEteIko3MxV7L+83S4euhc8TAiAoqG9JUqzElicnz4SDxr9vH9XwNLJUweOVx6QdGCRstg=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.1_1611924972896_0.40109350787010056"},"_hasShrinkwrap":false},"13.0.0-alpha.2":{"name":"staccato","version":"13.0.0-alpha.2","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.17","perhaps":"0.0.6"},"devDependencies":{"duplicitous":"0.2.7","proof":"^9.0.1"},"scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket to not map to exceptions. Sockets can emit multiple errors\nbut a catch block will only catch a single exception.\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error, the reader, the writer, or whoever got there\nfirst?\n\nIn previous implementations of this library I'd decided that the errors don't\nmatter to the application logic, only to the system administrator. We wouldn't\nrely on the errors to determine if communication has been cut. We'd assume that\nsocket streams can truncate for any reason, network error, a poorly implemented\nclient, or an attack. Our protocol do not need an error message on read. They\ncan verify the contents of the messages which they ought to do anyway. On error,\na read will just return `null` on the next read. Errors are funneled off to a\nlogging mechanism.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator, but\nyou used to have to test for write failure. I'd like to see if I can remove the\nconditional and replace it with `try`/`catch`, not for general socket errors,\nbut for the specific error of write-after-finish.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams close too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nThe documentation says that when `write` returns `false` you're supposed to wait\nfor a `\"drain\"` event before continuing to `write`. Staccato will do this for\nyou in the `write` method. The documentation for `write` does not mention that\n`write` will also return false when an error destroys the stream. When the\nstream is destroyed there will be no `\"drain\"` event so waiting on a drain will\ncause the program to hang. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n(Nice to have this function. I'm glad I wasn't imagining things.)\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem that, again, I'm glad it wasn't just my\nimagination.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nCurrently an alpha implementation as I walk though my code replacing the older\nStaccato with Staccato 13.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst { Staccato } = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n","readmeFilename":"README.md","gitHead":"a5d7cc11773ca6dae0297c1282a6d93c8bf5c993","_id":"staccato@13.0.0-alpha.2","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-1FQQdkk+X+b93UBADdbqbkE9e76g29HWdJtStN0b415CU87u5vff8i8Pf+fd2JWKzWSosN1avnR8KIRjZMEBbw==","shasum":"ac61a75fb9cbb16d838d5d98238df07a850d5874","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.2.tgz","fileCount":5,"unpackedSize":27020,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgFW0TCRA9TVsSAnZWagAADrcP/0mdpkOZBYCDiK40K11E\nwYNwG5gWfnAfUXYI+AvrkSCf/mE6RugIaOVgmECZjgPLEM+tnsU/bqwbVC3Y\nUzMG58iu4VTeKKoZu/3kuRqrZEgqzKCe/Rwe8lt3rny4a5lbn5eZcm74d95U\nidwTzaTDJA+Iq0xoiZGlumGbHs88Rw/Rh23Fv0DSNWP76of++4O/6S0Ex4PO\ng0gJS14UqzqzdUFc2KGgIOAseMjQzX2idCu05sv9ta8wLU661C8Ff6o87tpw\nHX+Hn3DWi8Rq09dyFlrkn8Sx8F4szjtAJkFG2gVZJ95frYYP6c3h97a+mT6b\nIfRmYBGVSWOqZVaO+aZts43EKiM543C6jjdjRyzfjLy/6R4ktmA5VEX1M/PE\n7xNifUlec8IATcnVEJwS5Nir3aNz73WPQqVh7jrsokspQ5VzQ+Y7A9tOCIkI\nMuU/0LtEBtr6d48qr/QqvsNOEN0j1X+T+OnjWJPqnmN7c6g+gIRqOE6nyn+D\nQ+Grf2AeQEupguJXL6BiBScB/QNyOSFBlIaJErPEvPmLkhWYJPaTV6Fb6YcN\nO+HfaDcTHTUoJPDutyWsUOH1Kh69Hp5iGUf+AMcCqG5ltzO9FethMFU0rmyl\nkRj/A9OMDbN1ANXGcT4cVaDjC/WkpjB79jiwKncYE+HEVb1TRqDI43U0LRD5\nPhxu\r\n=YHDM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIExn5N04Y2duSB32b+1x78nu2y/TRZw/d4b+Jmh9iDirAiA1H3kTA/t++mI9Ib+tFUQvdgHGo7Evx9IBNdm+Gdc/PQ=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.2_1612016914303_0.48204916876223236"},"_hasShrinkwrap":false},"13.0.0-alpha.3":{"name":"staccato","version":"13.0.0-alpha.3","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.18","perhaps":"0.0.6"},"devDependencies":{"duplicitous":"0.2.8","proof":"^9.0.1"},"scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket to not map to exceptions. Sockets can emit multiple errors\nbut a catch block will only catch a single exception.\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error, the reader, the writer, or whoever got there\nfirst?\n\nIn previous implementations of this library I'd decided that the errors don't\nmatter to the application logic, only to the system administrator. We wouldn't\nrely on the errors to determine if communication has been cut. We'd assume that\nsocket streams can truncate for any reason, network error, a poorly implemented\nclient, or an attack. Our protocol do not need an error message on read. They\ncan verify the contents of the messages which they ought to do anyway. On error,\na read will just return `null` on the next read. Errors are funneled off to a\nlogging mechanism.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator, but\nyou used to have to test for write failure. I'd like to see if I can remove the\nconditional and replace it with `try`/`catch`, not for general socket errors,\nbut for the specific error of write-after-finish.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams close too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nThe documentation says that when `write` returns `false` you're supposed to wait\nfor a `\"drain\"` event before continuing to `write`. Staccato will do this for\nyou in the `write` method. The documentation for `write` does not mention that\n`write` will also return false when an error destroys the stream. When the\nstream is destroyed there will be no `\"drain\"` event so waiting on a drain will\ncause the program to hang. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n(Nice to have this function. I'm glad I wasn't imagining things.)\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem that, again, I'm glad it wasn't just my\nimagination.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nCurrently an alpha implementation as I walk though my code replacing the older\nStaccato with Staccato 13.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst { Staccato } = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"194f9a987c95401baa1c3cd4cf587ea607255c72","_id":"staccato@13.0.0-alpha.3","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-Eq3rleOPXY8JSlgoOipUgyrgG4tXSFFwmVdu/LX7jsHNP2wLdd/q1AnR1r8Wm59D98gSBTyQYxZge9kclrSYjA==","shasum":"034446b223aa82d56a973e93473aaa82c8abfab5","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.3.tgz","fileCount":5,"unpackedSize":29410,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgGDzvCRA9TVsSAnZWagAAhzQP+wcBJkofLeb21pw387zg\nqTCN2bdCq6nAQyKoGYs+m0eaFJzBPCkZsvf7s8IPSKraaryvke2kT0V1yIjn\n4u7Ypa4FUc5QmfOxnscpEQICtSeshsL5297IHdXk64FExrh31d98gVYC/Kgu\n9YYwl2PrWlH4z6452VxYhc/EHWwEQ4WyhavndTe5oi3zmUXjPJV0lzJKF71R\n7V8C/xtu4lqqxwPIXwOWLGDsPgn0eOIhUfEbWzJFaW0zKt0zvtPE2Ag+SV8o\nUt1Q27h5jMdHbyFlGXt0fK9Fq1f/iRPz8Hu+dB8Cgzmim/4jukekSclcbIn+\ntGcoqMKZSMvDxFh1ocD0F02vIfDdhwiVz+wi6jBc46f47TTp6KzrmxrEUCkB\nYIxIYeldzB/Dv2HH4wjWH8+8TrNQrX2DjNQwxhICCUJFJtS8UYuQXxMyiqRz\nyu9ccpLHbDmKGh7buSCkvMJnz9WjHvspfQ9V1O25NPN9Si5PXP4TRFsEjbjF\ni5br9+i788tkltm15V/zKL9BaOU67WPxNv1klE5jzdvzszmyCrYYe9GeC/pZ\noP4wjadjKOcplcS5ywABMt9HHnkTvx091T65JP3e3QDkQ4533qAeZXyX/aWj\n1O25jPbacfAizsPADVldphOos39yXiVo1r0QB8sPzq+Ts0M7bmzFfqksntqj\n5qQ5\r\n=42oa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQ8DVx1l/DTP4WE5b0/mszvYj2CBcgC6YZw+RYo/CcFAIhAMBwn68qJ4aaFRuZTSNNA/HZQ6JnXZIbPWlXhH0DbUiw"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.3_1612201199299_0.018922622602809858"},"_hasShrinkwrap":false},"13.0.0-alpha.4":{"name":"staccato","version":"13.0.0-alpha.4","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.20","perhaps":"0.0.6"},"devDependencies":{"duplicitous":"0.2.8","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket to not map to exceptions. Sockets can emit multiple errors\nbut a catch block will only catch a single exception.\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error, the reader, the writer, or whoever got there\nfirst?\n\nIn previous implementations of this library I'd decided that the errors don't\nmatter to the application logic, only to the system administrator. We wouldn't\nrely on the errors to determine if communication has been cut. We'd assume that\nsocket streams can truncate for any reason, network error, a poorly implemented\nclient, or an attack. Our protocol does not need an error message on read. They\ncan verify the contents of the messages which they ought to do anyway. On error,\na read will just return `null` on the next read. Errors are funneled off to a\nlogging mechanism.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator, but\nyou used to have to test for write failure. I'd like to see (okay, looks like\nyou did it, maybe fix this paragraph?) if I can remove the conditional and\nreplace it with `try`/`catch`, not for general socket errors, but for the\nspecific error of write-after-finish.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams close too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nThe documentation says that when `write` returns `false` you're supposed to wait\nfor a `\"drain\"` event before continuing to `write`. Staccato will do this for\nyou in the `write` method. The documentation for `write` does not mention that\n`write` will also return false when an error destroys the stream. When the\nstream is destroyed there will be no `\"drain\"` event so waiting on a drain will\ncause the program to hang. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n(Nice to have this function. I'm glad I wasn't imagining things.)\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem that, again, I'm glad it wasn't just my\nimagination.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nCurrently an alpha implementation as I walk though my code replacing the older\nStaccato with Staccato 13.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst { Staccato } = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"71e25bcb7e8b1c8899cedb1c05890328a973efa7","_id":"staccato@13.0.0-alpha.4","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-E0JQnJH5b1TRyKJm6u0yVi9X0V31alj8ULc3n953861+6eZTKjA/y9H52B3dlPd8djKN0CaQw/u8HKgbxhAQ8A==","shasum":"94b799854f668f54ddba949d59452a25bb2b2e10","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.4.tgz","fileCount":5,"unpackedSize":29505,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTb3mCRA9TVsSAnZWagAAGRwP/0G1Cj/LZ3AxhxDUvcOH\nZv/dboDE0O5DhUGkRFT2QwrozOUTYkmZ+kleKPXI+5R5sWjujc40zy0wFZxu\nVf2oiD0Bcnf5Ro6A/REzgylZJ76bm+NxMd+TesfbvE8YuXbZ6XFMU2tUsFXo\nSbCBVid0WlwE3CWBgA9UL4VTPF2y+CsEbKKqlHgPXPl2Oi6nwP5pw9DH7eGs\ncVQ+a7sGx2O9xbp4Y87+CazF6KuMgn+s2ovBQm4MAVMSoCPYm151ZSIGiKak\ndz7cIh6wuX4Nvv2zccIta/XyEAIkH11KL+BXG8XDcT7l+dmN9P0OaCfRiwiJ\nCFBvJ7Bru7Vl2dyD2WgN50/iWqhxlf0C/5Hlg34j3LsqwNDBuczUsu3aOwh4\ndIptplBVofhmywg5WYKZN2OzacHwz7OTMrJJ4jNWKv63RAAsGi4NTom6Gg0x\n1R61PJpbakEuq1RseLbSjx7uaxXTt1Ke/isUnZTlhECKseHZnnChdd8SP1v7\nbAu56nEHOP6FgJEtTVwx1iEcwP3UVsMEDx2zR4i84uSQ6QimPjKk6gjU4aUk\nFf/Hlgj5eEhrS7IIhT4ClpnPcOSv7U7oRaEXU/E1RzPZy55ZmlHuObPas8Sa\n+LAkyKx0jVThcdrCCKnecjk1rpzSCmCgF0+iJGKhURtjrketMuHrcHxCb8Jj\nQfl6\r\n=a0vc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGFLGyM9hc8P8FNLW7jgTgWC+wSt318RoygeEb4V0OwaAiAvSC5mzqEASC0S25cP6UPsBOqsdva2CkGWuhljIKaZGw=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.4_1615707622141_0.3122333431002422"},"_hasShrinkwrap":false},"13.0.0-alpha.5":{"name":"staccato","version":"13.0.0-alpha.5","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.20","perhaps":"0.0.9"},"devDependencies":{"duplicitous":"0.2.8","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket to not map to exceptions. Sockets can emit multiple errors\nbut a catch block will only catch a single exception.\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error, the reader, the writer, or whoever got there\nfirst?\n\nIn previous implementations of this library I'd decided that the errors don't\nmatter to the application logic, only to the system administrator. We wouldn't\nrely on the errors to determine if communication has been cut. We'd assume that\nsocket streams can truncate for any reason, network error, a poorly implemented\nclient, or an attack. Our protocol does not need an error message on read. They\ncan verify the contents of the messages which they ought to do anyway. On error,\na read will just return `null` on the next read. Errors are funneled off to a\nlogging mechanism.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator, but\nyou used to have to test for write failure. I'd like to see (okay, looks like\nyou did it, maybe fix this paragraph?) if I can remove the conditional and\nreplace it with `try`/`catch`, not for general socket errors, but for the\nspecific error of write-after-finish.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams close too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nThe documentation says that when `write` returns `false` you're supposed to wait\nfor a `\"drain\"` event before continuing to `write`. Staccato will do this for\nyou in the `write` method. The documentation for `write` does not mention that\n`write` will also return false when an error destroys the stream. When the\nstream is destroyed there will be no `\"drain\"` event so waiting on a drain will\ncause the program to hang. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n(Nice to have this function. I'm glad I wasn't imagining things.)\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem that, again, I'm glad it wasn't just my\nimagination.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nCurrently an alpha implementation as I walk though my code replacing the older\nStaccato with Staccato 13.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst { Staccato } = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"6fb6368d69c4691defdc32e6c645955e4f3d3f0e","_id":"staccato@13.0.0-alpha.5","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-sVteHwWILXdnXXRjoXHPwsmc+duWwGaQyMC1lgX2LivXEBs1q3eyJaOmuvPxW+yXNq5RHZ/0zdjjxLp3Y+/1lA==","shasum":"688370eddb61ada1342c5de16e303880fbc2ed48","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.5.tgz","fileCount":5,"unpackedSize":29505,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTrBLCRA9TVsSAnZWagAAPisP/152HGm4OeplISGqww/0\nHdVncxpLhCHG+PfIydi/9b/xkDQhsAH4FYAxJa4NfMNQXGvXT72cGGYsHWIG\nGA7NIN+z/YuRNlhzKNEqeRcx/WcxvOjd/p9nRR/KBy7VpkNjWW+J0uNL1+Jh\nOHcpBjwFeDTzAIoc84al5H5v18lYHRmOT3Rjnb003nAs+/9A5rQYTIKGWGGO\nUh7XRwcDKbiMobp3ylOramIrRSE5GAfXNICf+aOTRcq+28B5hPpifnRYPrt2\n+sYeDpPd9aCvFMdRADarszINgsT9d3H5Qxvyb7XaTqAcFXUlARJtH8wA3v9P\nMujN7MvqEdcgpEmBtch3fkbsO0crQc/J0RwR2tmeKFvvtGwbrSOEBEFUh/Bk\nFglQYRTxSpe0Vgk9YbWUmhLbMdADtJe/FKpQinecFxldYFYpyCpdCLBc6sd5\n15Y0xKYi6LphpH3WF/IMYpaEwPPqXi5ELlfG+Lkbna70j8jLZsUnJsEqjrBh\nGFsS0oGti7Wt8l+Ka9hO9LPUy++VplqyKWqQ4clpco94bzx09SotnlC2jdYX\nul+M3W7GofDqJFR0bpnd5bXvehjk4+YuHyZFBVtGpecsDw7ibZSHsXmyP2Yt\n7hKZ5iw9JeF89GrEU1N5FCVIwKNhbRY4GhudI78dQy+IR6xG6EkYyPx2VX+s\nIeOk\r\n=ve6G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVrHU9mfX2z0zWjHRe8kuB4ayVAcFKcVZPAFn/mVIbWAIgMiyk9dsaS100DRqK4CP5sjKZeMK2XUn43Foy2Om8Ua0="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.5_1615769674624_0.24214343140836903"},"_hasShrinkwrap":false},"13.0.0-alpha.6":{"name":"staccato","version":"13.0.0-alpha.6","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"comeuppance":"0.0.1","eject":"0.2.3","interrupt":"11.0.0-alpha.20","perhaps":"0.0.10"},"devDependencies":{"duplicitous":"0.2.8","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket to not map to exceptions. Sockets can emit multiple errors\nbut a catch block will only catch a single exception.\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error, the reader, the writer, or whoever got there\nfirst?\n\nIn previous implementations of this library I'd decided that the errors don't\nmatter to the application logic, only to the system administrator. We wouldn't\nrely on the errors to determine if communication has been cut. We'd assume that\nsocket streams can truncate for any reason, network error, a poorly implemented\nclient, or an attack. Our protocol does not need an error message on read. They\ncan verify the contents of the messages which they ought to do anyway. On error,\na read will just return `null` on the next read. Errors are funneled off to a\nlogging mechanism.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator, but\nyou used to have to test for write failure. I'd like to see (okay, looks like\nyou did it, maybe fix this paragraph?) if I can remove the conditional and\nreplace it with `try`/`catch`, not for general socket errors, but for the\nspecific error of write-after-finish.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams close too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nThe documentation says that when `write` returns `false` you're supposed to wait\nfor a `\"drain\"` event before continuing to `write`. Staccato will do this for\nyou in the `write` method. The documentation for `write` does not mention that\n`write` will also return false when an error destroys the stream. When the\nstream is destroyed there will be no `\"drain\"` event so waiting on a drain will\ncause the program to hang. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n(Nice to have this function. I'm glad I wasn't imagining things.)\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem that, again, I'm glad it wasn't just my\nimagination.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nCurrently an alpha implementation as I walk though my code replacing the older\nStaccato with Staccato 13.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst { Staccato } = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"58a9b801de934f7eac356b1de25ddc4540cb3e7c","_id":"staccato@13.0.0-alpha.6","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-NUUXM/m1aXNJWAgnsZoefPOw4L0CF8ZS6jptiHP8fvofVjqzmgIsBLuDVRRbjAsfxABUegUzj1CRvtZlY5sn1A==","shasum":"eab8651b84440410a72d2efc2a12e260bba1ef5e","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.6.tgz","fileCount":5,"unpackedSize":29510,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgYLE7CRA9TVsSAnZWagAAfTsP/2RDRvN2CPrwhJSVYPbf\nSnzIOkP17FAfxuSclK7KN8u3MesHs6iUxaMKO2EZdxIWFWyE/ZTRgeeSzkcQ\nEm0b9L1nQ6+/V/2Fl3MAglAQ7Ztf6w5OUgS+IT9P8wJOp+VrfE7OTZFF8hFw\nlph3ABRpbLl4XjH0ZbhNVsYB+02pbKMBgGmaxnqvDZ4DwJTPwqxYSyQed+cN\nSyssFSJmwIcyp7wM93tYZRpspLmKeiiRZXVOebe2cmJTmvOqm7svtU5bTmlN\nxXUu+wWs+ihlHLsXCjPjrX7GmbozDwyy5T1xN4rWyo7EJikZ626VQpKfj3oI\nWV/4gNiJ7tcV3tc5PpDM24dxG/4P4fmHvUH8O/KsL3fYNmIV5e/dEr8iWApR\nkWSq2gnqRJnlm81CYZIH8ym5VUKzYsOT6lzNY3bisHYpXDaSBfFDhHyhtIym\nfdsDh8uDd/aoUZlkhfXj4lmrLyH53eQQkXAjpjYejkZIUfOze4/45xZA1TvN\nskmzgUV297zYSyvhATujPsZVHVz/OSEOJ3UzjJYKCQ17gd+4r3YkXNEeGcFf\nyTOxpJgnyyl/yagw2tOEE2SpTMDXQAuqraJnDOO9V+/INHJcRy0SIDjfGFH9\nJgulgybe2JpjAm7o3SovYv3ItqbvyqIxXaz1BMDX1wuE+Y2u9nigvdO+Ann+\nfsCj\r\n=DTGU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCZ+tZQ7IO2hU/9boP/J5jgpv4ixWczTUf0DDKkbuBj1wIgGAU3emYgjo3+P54MSdKXCHH41i4zG47HzA9wvCvU2ok="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.6_1616949563225_0.3087346261930364"},"_hasShrinkwrap":false},"13.0.0-alpha.7":{"name":"staccato","version":"13.0.0-alpha.7","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"eject":"0.3.0","interrupt":"11.0.0-alpha.20","perhaps":"0.0.10"},"devDependencies":{"duplicitous":"0.2.8","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket do not map to exceptions. Sockets can emit multiple\nerrors. A catch block is designed to catch a single error and move on. You have\nto leave an error handler registered to ensure you do not crash with an\n`unhandledException` and then if the user excpects to catch errors in a catch\nblock, where does this dangling error go?\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error? Should it be the reader? Should it be the\nwriter? Should it be whichever checked for the error first?\n\nIn previous implementations of this library I'd decided that the specific\nnetwork errors don't matter to the application logic, only to the system\nadministrator. We shouldn't rely on the errors to determine if communication has\nbeen cut. We'd assume that socket streams can truncate for any reason, network\nerror, a poorly implemented client closing the socket early, or an attack. Our\nprotocol does not need an error message on read. They ought to be verifying the\ncontents of the messages with checksums and whatnot. A bad network connection is\none of many possible causes of data corruption. On error, a read will just\nreturn `null` on the next read and the application can deal with the truncation.\nErrors are funneled off to a logging mechanism for the administrators to\ninspect.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator.\n\nWrites on the other hand do produce an exception if the socket closes, but not a\nnetwork exception. They simply throw a write-after-finish if you attempt to\nwrite to the socket after it has closed for any reason.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams can close prematurely too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nFun fact about Node.js streams. The documentation says that when `write` returns\n`false` you're supposed to wait for a `\"drain\"` event before continuing to\n`write`. Staccato will do this for you in the `write` method. The documentation\nfor `write` does not mention that `write` will also return false when an error\ndestroys the stream. When the stream is destroyed there will be no `\"drain\"`\nevent so waiting on a drain after write returns `false` because of a destroyed\nsocket will cause the program to hang. Big mystery when it happens, too. A real\nwhodunnit. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem. I'm glad it wasn't just my imagination. We\ndon't use this error handler, though. We insist that you register your own\nnetwork error handler on your socket to log or ignore all network errors.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nStaccato is currently an alpha implementation as I walk though my code replacing\nthe older Staccato 12.x with Staccato 13.x.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst Staccato = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"116a76c0779fe6125a24b2e52ae6aa89440588ed","_id":"staccato@13.0.0-alpha.7","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-VUR1PtZC46h9eyYrVX+z1hlGEXp6WwBUXGikc3KqS4DbwaZua80KkWfXzjZUnvQcEenVs5L0xSBtVKbrxOsuQg==","shasum":"35d88239c4578a64f7d5b522498fb8281f77161f","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.7.tgz","fileCount":5,"unpackedSize":30106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+Tm0CRA9TVsSAnZWagAAokAP/iwhBupSsTkcyc9Sjwyd\nB1HXfKjFf5vjVnKYGrFABGY+nrP5b/HjW6NKH+wWIDyh+6RbBYTr+FUTjZ8o\nFqtfPKFDz6mMY5mTs4N2lsyNhkNcWsK6xiHaUpZ4idsFS7jEET7h9Z4ZN+gu\ne+ZWX03dnhZSXfcKsP53/G/hnz5T1Gv+wLjmXj4dU3PcKj2ExYaJ3uFZ9BUu\nkvlSaTMwo+SlJmb9/gXsoRlbx5huVuzOzIPp1WKS+5yce66n7XmKWICE6Kwu\neUnlpx+eiLJfF864ptptvoIQYi1ri6rVTT+ZzCeTWkO572L6p43TL4jNY6K7\nia7bvsI3LM7pSWzGhqaldJ0vHMgxQ8Zcl9iK9P3YYWTqEw+i41RysupAcMW/\neOlERAmXlw7BcsNFKjXzVsuHHwnYGtm9oiqqcZOvqsoaZVDnUeanKXSyHpR9\nIxDwqd8LP7+byHP72J35q4I71RZLZPFC2waxPZpFuu8BJJcAbbWUMnuuG6W7\n7/orsYT8ZjsBw/QqjIfnM5hw8p+CFZEcj2GOZBZ7zT+wRa+8c3Tr/BQxIIoE\nzWSP3/R6vaDE37g684zl8P4MadBl6a2OD05bIX0eM3Kws6/+dwT2jO+zeoZJ\nSqd6Zv6t7k4xNNHrzd47WNqB4vULBduPfyI68rmjsipCIfaNDgDn8HHHDvDf\nKf59\r\n=K4iE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG3paQfRXs5Dgn3tNGqDTnEZHW+zEkOhmzisZUNjxwb8AiEA8dgb5dRhph0Bk4AoOx5quLJIT5xHwdlSQ9RrIUX4GZ0="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.7_1626945972811_0.28641030876581985"},"_hasShrinkwrap":false},"13.0.0-alpha.8":{"name":"staccato","version":"13.0.0-alpha.8","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"eject":"0.3.0","interrupt":"11.0.0-alpha.20","perhaps":"0.0.10"},"devDependencies":{"duplicitous":"0.2.8","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket do not map to exceptions. Sockets can emit multiple\nerrors. A catch block is designed to catch a single error and move on. You have\nto leave an error handler registered to ensure you do not crash with an\n`unhandledException` and then if the user excpects to catch errors in a catch\nblock, where does this dangling error go?\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error? Should it be the reader? Should it be the\nwriter? Should it be whichever checked for the error first?\n\nIn previous implementations of this library I'd decided that the specific\nnetwork errors don't matter to the application logic, only to the system\nadministrator. We shouldn't rely on the errors to determine if communication has\nbeen cut. We'd assume that socket streams can truncate for any reason, network\nerror, a poorly implemented client closing the socket early, or an attack. Our\nprotocol does not need an error message on read. They ought to be verifying the\ncontents of the messages with checksums and whatnot. A bad network connection is\none of many possible causes of data corruption. On error, a read will just\nreturn `null` on the next read and the application can deal with the truncation.\nErrors are funneled off to a logging mechanism for the administrators to\ninspect.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator.\n\nWrites on the other hand do produce an exception if the socket closes, but not a\nnetwork exception. They simply throw a write-after-finish if you attempt to\nwrite to the socket after it has closed for any reason.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams can close prematurely too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nFun fact about Node.js streams. The documentation says that when `write` returns\n`false` you're supposed to wait for a `\"drain\"` event before continuing to\n`write`. Staccato will do this for you in the `write` method. The documentation\nfor `write` does not mention that `write` will also return false when an error\ndestroys the stream. When the stream is destroyed there will be no `\"drain\"`\nevent so waiting on a drain after write returns `false` because of a destroyed\nsocket will cause the program to hang. Big mystery when it happens, too. A real\nwhodunnit. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem. I'm glad it wasn't just my imagination. We\ndon't use this error handler, though. We insist that you register your own\nnetwork error handler on your socket to log or ignore all network errors.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nStaccato is currently an alpha implementation as I walk though my code replacing\nthe older Staccato 12.x with Staccato 13.x.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst Staccato = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"9f9fd5b866d38fa5d68dedc6e5f215eef855ce78","_id":"staccato@13.0.0-alpha.8","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-+vAfX3bwT9Ob7ws+FX/KT4W7wghxGEA1sspeHTyd/e1FgdTKi+LI+oORlkWbmzfXKXeQ8ViaPO0ftOV7765Hpg==","shasum":"2f9553a4b9644ac3236fc9494ddb0e8c4b6b0774","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.8.tgz","fileCount":5,"unpackedSize":30106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+Tn2CRA9TVsSAnZWagAAlqgP+gNRqVrFUsUfk/kEc8x8\nmqMLhN7W0ZqW/vR9Ic7IJrz3oDywCb8U5L7NmYmMSWQ7ermf3xVBPw1enHks\n75bptPzAEicNG/ImRr965YWz7hQtmx0U6NdnjuqkCx4G96a/8OssTXzNMSK2\ng5wETp8BGhw7YtuLv1IGqX6TXbeAiwTuiwL/u4w3sRGOLC/7tm9EgeLJYwSY\nBRSF+VBfynKKlzFCtkJtflPmKaW1GFRgkXYED8c2Ho+UctxGeqsWgW1dUWZW\nhby7N6wAMBl40J51eaVDwBJhuCVaAWdKAPIEicn+qn2WEt5CTADtKuDzMdKu\nmhsvnQLLo+//HTcEo2N3XZvgVuEGspxiGchhWHvOQ74rfJAh8FAdknDdWUS8\n5owk2T0d3fjLo4HDbNXB2OCuOb2gD9rN0w8Civ4JP2j6Lrfay1GzzhH08k7J\nPEjfpGCfmP7gWzS/MV5qJl5+FbGzn3JH+hPrcbte0oghP6o5B5ZUx+pcQlOT\nMpZp4lBVkrH9C1fGVzubwIWy+uIhzHJH743UIxhr7nYr79Ro1zY4F5ikXD/x\n81i8/m0f17cC1pf1tH2mBlCU0gngqQ7aIswQo6XdiZHwcNUeP39BDd/bgXg9\nVBjJKQItD7hTIu8zO48xA+2MmgEIhom8l1nmfOJkhkJIWRHYII0iYd5LF1be\n+sSk\r\n=1I+Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEEed9Om2b0TSB4QQsrG/8ugraePWes0Ixv/KZBCCNWpAiAIwmb6EshEwAkDCPARHkYUVbjPBc7MtDL1QxmzW1AjIg=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.8_1626946038148_0.11110839607869005"},"_hasShrinkwrap":false},"13.0.0-alpha.9":{"name":"staccato","version":"13.0.0-alpha.9","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"eject":"0.3.0","interrupt":"11.0.0-alpha.21","perhaps":"0.0.11"},"devDependencies":{"duplicitous":"0.2.9","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket do not map to exceptions. Sockets can emit multiple\nerrors. A catch block is designed to catch a single error and move on. You have\nto leave an error handler registered to ensure you do not crash with an\n`unhandledException` and then if the user excpects to catch errors in a catch\nblock, where does this dangling error go?\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error? Should it be the reader? Should it be the\nwriter? Should it be whichever checked for the error first?\n\nIn previous implementations of this library I'd decided that the specific\nnetwork errors don't matter to the application logic, only to the system\nadministrator. We shouldn't rely on the errors to determine if communication has\nbeen cut. We'd assume that socket streams can truncate for any reason, network\nerror, a poorly implemented client closing the socket early, or an attack. Our\nprotocol does not need an error message on read. They ought to be verifying the\ncontents of the messages with checksums and whatnot. A bad network connection is\none of many possible causes of data corruption. On error, a read will just\nreturn `null` on the next read and the application can deal with the truncation.\nErrors are funneled off to a logging mechanism for the administrators to\ninspect.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator.\n\nWrites on the other hand do produce an exception if the socket closes, but not a\nnetwork exception. They simply throw a write-after-finish if you attempt to\nwrite to the socket after it has closed for any reason.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams can close prematurely too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nFun fact about Node.js streams. The documentation says that when `write` returns\n`false` you're supposed to wait for a `\"drain\"` event before continuing to\n`write`. Staccato will do this for you in the `write` method. The documentation\nfor `write` does not mention that `write` will also return false when an error\ndestroys the stream. When the stream is destroyed there will be no `\"drain\"`\nevent so waiting on a drain after write returns `false` because of a destroyed\nsocket will cause the program to hang. Big mystery when it happens, too. A real\nwhodunnit. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem. I'm glad it wasn't just my imagination. We\ndon't use this error handler, though. We insist that you register your own\nnetwork error handler on your socket to log or ignore all network errors.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nStaccato is currently an alpha implementation as I walk though my code replacing\nthe older Staccato 12.x with Staccato 13.x.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst Staccato = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"21a434692edcab99db7ff02498bf5032b8d81182","_id":"staccato@13.0.0-alpha.9","_nodeVersion":"12.16.2","_npmVersion":"6.14.4","dist":{"integrity":"sha512-+sy+NoNOMtEv0euLs+6tCXgnX0flzoyS72V6W5KR0GLKOnnRqZBaOYlS/1sUYdxZ7Jw+qY0RmLC9eeQrNUYjSQ==","shasum":"092bebf060fc417dee95f3c1823faea6a38a550e","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.9.tgz","fileCount":5,"unpackedSize":30106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+cbvCRA9TVsSAnZWagAAgwEP/j+uruJG/r0uXXVJF2PX\n4MhXFayg6xKh0c74J/dllVKcv/WLefJgmGZc7s8RHiARC4pZRXfrvg9A2Sow\nJg4m+S3YTXYBKP73kvIK69fAU5RqLoQvWhF948JDj4u+ysKmZ/w8bMGdtiWl\nLKlJ2bQEiUFk2Iu9sLRQgWanTIEnWHGwyhldQoPiBfkZYoP/txyzTTGQ2azf\n1WRJbGLGhjp382h/Gaba2iJS5EM1v1xEW9dH6T+f86YhSw4uEvpgQKxDsWa1\nhJteNS2NVN6VriwuC7lG9gmHeaREkOX0YHG46PAg7cONpojEzm3yUZbjp76u\nQY3MqhtNsqDw3oBiBznu/BurdNgFi3Bkuj4S/UgGOlIUbxen1piWAN/cDd+P\nUBj7NNi00n3RaT17KVQQP+e49YckWnSwZDF1zSEpkGYahgISGTkS6PBX247U\n5/xbZextr2n0YbA0yUMrexpzqoCKg++eR/+aHmm52dmCgToEo6vlmAKdQ/2q\n4MKqVQaAC+LKiXwBgC4zKR6eUqSEnnaqS4LZwdxJyhMinU5i49AHjf4A+d4k\nwQ+Fm8cbqRmaaUTVKf1VyXatIjl3EBy9G/D5kGqim0fMZ3mCF6nkZbckTNJ7\nC/eoy4nGdfb0t5ew7F1CSsKlk5GzhRTuKnChz0ZNFtl2N/2tI+1QKz2RMbzG\nxCbQ\r\n=POGx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDpsr51AoThuQmDlJgGHNKc2nvxD27xTO+x+XwF+k9nHwIgSnkz/GjQQaFB73A/w7CDbIBJTzIZ9b0ixFxL2p3+XCk="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.9_1626982126980_0.46900253519169444"},"_hasShrinkwrap":false},"13.0.0-alpha.10":{"name":"staccato","version":"13.0.0-alpha.10","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"eject":"0.3.0","interrupt":"11.0.0-alpha.23","perhaps":"0.0.11"},"devDependencies":{"duplicitous":"0.2.9","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket do not map to exceptions. Sockets can emit multiple\nerrors. A catch block is designed to catch a single error and move on. You have\nto leave an error handler registered to ensure you do not crash with an\n`unhandledException` and then if the user excpects to catch errors in a catch\nblock, where does this dangling error go?\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error? Should it be the reader? Should it be the\nwriter? Should it be whichever checked for the error first?\n\nIn previous implementations of this library I'd decided that the specific\nnetwork errors don't matter to the application logic, only to the system\nadministrator. We shouldn't rely on the errors to determine if communication has\nbeen cut. We'd assume that socket streams can truncate for any reason, network\nerror, a poorly implemented client closing the socket early, or an attack. Our\nprotocol does not need an error message on read. They ought to be verifying the\ncontents of the messages with checksums and whatnot. A bad network connection is\none of many possible causes of data corruption. On error, a read will just\nreturn `null` on the next read and the application can deal with the truncation.\nErrors are funneled off to a logging mechanism for the administrators to\ninspect.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator.\n\nWrites on the other hand do produce an exception if the socket closes, but not a\nnetwork exception. They simply throw a write-after-finish if you attempt to\nwrite to the socket after it has closed for any reason.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams can close prematurely too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nFun fact about Node.js streams. The documentation says that when `write` returns\n`false` you're supposed to wait for a `\"drain\"` event before continuing to\n`write`. Staccato will do this for you in the `write` method. The documentation\nfor `write` does not mention that `write` will also return false when an error\ndestroys the stream. When the stream is destroyed there will be no `\"drain\"`\nevent so waiting on a drain after write returns `false` because of a destroyed\nsocket will cause the program to hang. Big mystery when it happens, too. A real\nwhodunnit. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem. I'm glad it wasn't just my imagination. We\ndon't use this error handler, though. We insist that you register your own\nnetwork error handler on your socket to log or ignore all network errors.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nStaccato is currently an alpha implementation as I walk though my code replacing\nthe older Staccato 12.x with Staccato 13.x.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst Staccato = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"9beb96f296d22000a28483d9a55b69ef46ce1abb","_id":"staccato@13.0.0-alpha.10","_nodeVersion":"16.5.0","_npmVersion":"7.19.1","dist":{"integrity":"sha512-oumvS+Nyxvx1WhCY94NBssv/R8aOUrZXDXhDOQDJe9GVOP97LtVRc1yCYVyqFnMf41ZeFdXgZpceDxbTeMixew==","shasum":"c5544107573818f7fe61034ff7e947818bc1e1be","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.10.tgz","fileCount":5,"unpackedSize":30107,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+mOxCRA9TVsSAnZWagAAs00P/jraMqhGKkP5dZKvTGr3\nhC7gYPOmr27f+oJgAzeOGqIKyyPsmEuvQFByU/cOibsNnKu8cZ7xRviBzqmf\n6iM40sNT+7UcCyM2y6fGyuv2xRMcm8SsyUr8aF26TSotDOU2caKCtI4TmgKS\nXI+npjPfQFX6GMIde2m0VWk5GP6smaUePIpmf+tVyxsroE+47p2WRKcjy3Cs\nUyRXvoX/VgIMguoEQ6sVGW6cQgdxQC4GwkVG61gdH8/mkw9FMI8K1L2XH6Xo\nhcUWfWAb1U+u2aaXZbxyXDQ69mSvbKtVx/242yUqZYCWvNzvqnBZxgpx0VHU\nwFtjY0WyhZdF5nGYix4IfWfFLKXYVRBBboGW9DQXLo3QT3DZizDuw4vUrICF\nGAmglFRXWlYzL9NeWe5ZqrxBgpt5ZEFosMyJU83h9+1ok9Ma4fAZsNfAhJPo\n0jgPNcY/XfOTfzcJR6aFl1rplZY6t0MWSNKv5HzNroiRSfy9pRzy86MoZlbe\n4DSpiqNASr2elM0YkXyI1omPqy429WfBccy2WTt7aFiV7JQlz+NRFZvnwyLr\nCfNuw0wVQfA2oV7lryt/H/KOmfxPFjqO7p6G5kXz4R++K6SDXdcMkBpFyYbs\nwRy3ePZTEm/Kp1j3OJBejpOm0dOxWmKcuNPkluAG5XUfeUKId2CNqZ/hG+pP\nCdzh\r\n=qWr+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQvTpNZj5g1jFofHtEGeq+vvbeHObltLeqBeOvlDv5+wIhAI+d3fHQrR6q0h2bnQVspMKQCfeIXA0B1uKSWGKr57PB"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.10_1627022257240_0.7133639604827755"},"_hasShrinkwrap":false},"13.0.0-alpha.11":{"name":"staccato","version":"13.0.0-alpha.11","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"eject":"0.3.0","interrupt":"11.0.0-alpha.24","perhaps":"0.0.11"},"devDependencies":{"duplicitous":"0.2.9","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket do not map to exceptions. Sockets can emit multiple\nerrors. A catch block is designed to catch a single error and move on. You have\nto leave an error handler registered to ensure you do not crash with an\n`unhandledException` and then if the user excpects to catch errors in a catch\nblock, where does this dangling error go?\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error? Should it be the reader? Should it be the\nwriter? Should it be whichever checked for the error first?\n\nIn previous implementations of this library I'd decided that the specific\nnetwork errors don't matter to the application logic, only to the system\nadministrator. We shouldn't rely on the errors to determine if communication has\nbeen cut. We'd assume that socket streams can truncate for any reason, network\nerror, a poorly implemented client closing the socket early, or an attack. Our\nprotocol does not need an error message on read. They ought to be verifying the\ncontents of the messages with checksums and whatnot. A bad network connection is\none of many possible causes of data corruption. On error, a read will just\nreturn `null` on the next read and the application can deal with the truncation.\nErrors are funneled off to a logging mechanism for the administrators to\ninspect.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator.\n\nWrites on the other hand do produce an exception if the socket closes, but not a\nnetwork exception. They simply throw a write-after-finish if you attempt to\nwrite to the socket after it has closed for any reason.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams can close prematurely too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nFun fact about Node.js streams. The documentation says that when `write` returns\n`false` you're supposed to wait for a `\"drain\"` event before continuing to\n`write`. Staccato will do this for you in the `write` method. The documentation\nfor `write` does not mention that `write` will also return false when an error\ndestroys the stream. When the stream is destroyed there will be no `\"drain\"`\nevent so waiting on a drain after write returns `false` because of a destroyed\nsocket will cause the program to hang. Big mystery when it happens, too. A real\nwhodunnit. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem. I'm glad it wasn't just my imagination. We\ndon't use this error handler, though. We insist that you register your own\nnetwork error handler on your socket to log or ignore all network errors.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nStaccato is currently an alpha implementation as I walk though my code replacing\nthe older Staccato 12.x with Staccato 13.x.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst Staccato = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"e6f678810f4e693a003cbaf422abd199f0ddb235","_id":"staccato@13.0.0-alpha.11","_nodeVersion":"16.4.2","_npmVersion":"7.18.1","dist":{"integrity":"sha512-D0e4Rli7SDXQVvxqtO5nCNp6r/TH8SkGCPl8UBqvqY8pHifRUinUEUS022FOEMRtDlfRv0ga7aBxm2vnDDUNyA==","shasum":"1ab886efa28b481a93334c57a406044ec5170198","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.11.tgz","fileCount":5,"unpackedSize":30107,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDl7+CRA9TVsSAnZWagAAQhQP/1auXETRPeT8FgIlNl/m\n1LVc3V2IHK0LTAlGpQLFdnlO4BnObTtpbhpD1TFCs6r3WzPNYX+1pyZxpJcI\nlb6LI2HJSyCAufuqACW78nXoDVvkxNlMjAgQ7fcOtkEjZKGB5g3Rqi9aaNoF\nMZ7ilMCBV0R4glfmjYReBJi3Zz1suGEhGELHyk2zvrRWBha+zjHKvO0XdBFU\nfL9cprAa4EJ5smpAaGOiVW1s2oKMD8N/AzGNOot/4Y8tm3j5sg2pYjKb6L98\nuXRdMGX1ssAZrjnXdoFubF7oMWiUoiaGQ35fx3c9dyrN0ggIuxI0hJM1Imtl\npdqLgKuyQdeoeeHINncBz7L7SJEUTUnd0NYFcq9RPwfGInjhOhdxCRlvfFKo\nWwsfWotEDFE5FxbPRl2Lrmv8iVb13PBicpwWucp4aX7cX287PuOfH+gOz6R/\nq7w1yKZ6mvAngYhudIuwbx5wwe/flHvPPpi4uSgEFW/5Vec4i470oZEHDZDa\nlQGNbOQd1Oyz2lIsyIPrZN4f+fP4GAK25gvSatVeIMRegJzuSnZKTJ6aa9gn\nrYJq5pQg97ctGtm3UhWbMF8YGnVsY6/t8mmfpnRHxTftP6R1Udrhy/oAtIpX\n51QNl7ihdezwrOo9YKOpNpmTg2acOZXeELQnNKSWqjopSPJQd4k0Y50OU/L0\ndV6p\r\n=HunZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEZNJnqxh8b964JF290kkLzeDAuwE7bnsOJT26UE5sEXAiAuduKZYvTdIssA+0HHhb/Pwhi7LPzp/W7xvWeZvlpJgg=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.11_1628331773799_0.8518192587065772"},"_hasShrinkwrap":false},"13.0.0-alpha.12":{"name":"staccato","version":"13.0.0-alpha.12","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"eject":"0.3.1","interrupt":"11.0.0-alpha.25","perhaps":"0.0.12"},"devDependencies":{"duplicitous":"0.2.10","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket do not map to exceptions. Sockets can emit multiple\nerrors. A catch block is designed to catch a single error and move on. You have\nto leave an error handler registered to ensure you do not crash with an\n`unhandledException` and then if the user excpects to catch errors in a catch\nblock, where does this dangling error go?\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error? Should it be the reader? Should it be the\nwriter? Should it be whichever checked for the error first?\n\nIn previous implementations of this library I'd decided that the specific\nnetwork errors don't matter to the application logic, only to the system\nadministrator. We shouldn't rely on the errors to determine if communication has\nbeen cut. We'd assume that socket streams can truncate for any reason, network\nerror, a poorly implemented client closing the socket early, or an attack. Our\nprotocol does not need an error message on read. They ought to be verifying the\ncontents of the messages with checksums and whatnot. A bad network connection is\none of many possible causes of data corruption. On error, a read will just\nreturn `null` on the next read and the application can deal with the truncation.\nErrors are funneled off to a logging mechanism for the administrators to\ninspect.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator.\n\nWrites on the other hand do produce an exception if the socket closes, but not a\nnetwork exception. They simply throw a write-after-finish if you attempt to\nwrite to the socket after it has closed for any reason.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams can close prematurely too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nFun fact about Node.js streams. The documentation says that when `write` returns\n`false` you're supposed to wait for a `\"drain\"` event before continuing to\n`write`. Staccato will do this for you in the `write` method. The documentation\nfor `write` does not mention that `write` will also return false when an error\ndestroys the stream. When the stream is destroyed there will be no `\"drain\"`\nevent so waiting on a drain after write returns `false` because of a destroyed\nsocket will cause the program to hang. Big mystery when it happens, too. A real\nwhodunnit. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem. I'm glad it wasn't just my imagination. We\ndon't use this error handler, though. We insist that you register your own\nnetwork error handler on your socket to log or ignore all network errors.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nStaccato is currently an alpha implementation as I walk though my code replacing\nthe older Staccato 12.x with Staccato 13.x.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst Staccato = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"352fa1703a8a599610aad6187279d8474f3392d2","_id":"staccato@13.0.0-alpha.12","_nodeVersion":"16.4.2","_npmVersion":"7.18.1","dist":{"integrity":"sha512-DOFvr0MN98VMeJealqGDpjKnOy98qiFo79EoWFeUsrDCvHi61O3OJS7lHPerDamS3ZMKCFJedlzamiwLwpImOQ==","shasum":"f91e2d15671cbb9e433cea33cd73fee5effa7002","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.12.tgz","fileCount":5,"unpackedSize":30108,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDUMLODgBLqXC1xHvAvNpIBTa57ukYcUYJTgNWgldM8zAiEAleHTzz1SGGlq2hixsXPFWBxLFqkCkTXTTJHfDXEkR0g="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.12_1632165342044_0.19987561156150346"},"_hasShrinkwrap":false},"13.0.0-alpha.13":{"name":"staccato","version":"13.0.0-alpha.13","description":"Write to a Node.js stream using the error-first callback style.","keywords":["cadence","streams","callbacks","control","flow"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/staccato","bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"dependencies":{"eject":"0.3.2","interrupt":"11.0.0-alpha.26","perhaps":"0.0.13"},"devDependencies":{"duplicitous":"0.2.11","proof":"^9.0.1"},"main":"staccato","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/staccato/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/staccato/actions)\n[![codecov](https://codecov.io/gh/bigeasy/staccato/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/staccato)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nWrite to a Node.js stream using `async`/`await`.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/staccato/issues/1  |\n| Documentation | https://bigeasy.github.io/staccato            |\n| Source        | https://github.com/bigeasy/staccato           |\n| Issues        | https://github.com/bigeasy/staccato/issues    |\n| CI            | https://travis-ci.org/bigeasy/staccato        |\n| Coverage:     | https://codecov.io/gh/bigeasy/staccato        |\n| License:      | MIT                                           |\n\n```\nnpm install staccato\n```\n\n```javascript\nconst staccato = new Staccato(socket)\n\nsocket.on('error', error => console.log(error.stack))\n\nawait Staccato.rescue(async () => {\n    for await (const buffer of staccato.readable) {\n        await staccato.writable.write([ buffer ])\n    }\n})\n\nsocket.destroy()\n```\n\nI've moved all my Node.js code over to `async`/`await` and I want an\n`async`/`await` interface to sockets. This is it.\n\nThe problem with an `async`/`await` interface to sockets is that the errors\nemitted by the socket do not map to exceptions. Sockets can emit multiple\nerrors. A catch block is designed to catch a single error and move on. You have\nto leave an error handler registered to ensure you do not crash with an\n`unhandledException` and then if the user excpects to catch errors in a catch\nblock, where does this dangling error go?\n\nEarly attempts to re-route error events to error-first callbacks had the problem\nof these dangling errors. Additionally, there was the problem of which half of\nthe socket should get the error? Should it be the reader? Should it be the\nwriter? Should it be whichever checked for the error first?\n\nIn previous implementations of this library I'd decided that the specific\nnetwork errors don't matter to the application logic, only to the system\nadministrator. We shouldn't rely on the errors to determine if communication has\nbeen cut. We'd assume that socket streams can truncate for any reason, network\nerror, a poorly implemented client closing the socket early, or an attack. Our\nprotocol does not need an error message on read. They ought to be verifying the\ncontents of the messages with checksums and whatnot. A bad network connection is\none of many possible causes of data corruption. On error, a read will just\nreturn `null` on the next read and the application can deal with the truncation.\nErrors are funneled off to a logging mechanism for the administrators to\ninspect.\n\nRead loops break when you get a `null` buffer as end-of-stream indicator.\n\nWrites on the other hand do produce an exception if the socket closes, but not a\nnetwork exception. They simply throw a write-after-finish if you attempt to\nwrite to the socket after it has closed for any reason.\n\nTherefore, input streams truncate. That's just the way the cookie crumbles.\nWrite streams can close prematurely too, and when they do you get an exception.\n\nWhen there is only one exception to catch we can provide a wrapper function that\nwill filter that exception and wave it through. This is `Socket.rescue()`.\n\nFor clients, an incomplete transaction or a disconnect usually means back off\nand retry. For servers, an incomplete transaction or a disconnect means log it\nand wait for the client to come back again, perhaps throttling the client that\ncan't get its act together and isn't backing-off.\n\nIn addition to providing the simplified interface above, Staccato captures some\nof the fiddly bits of Node.js socket handling.\n\nFun fact about Node.js streams. The documentation says that when `write` returns\n`false` you're supposed to wait for a `\"drain\"` event before continuing to\n`write`. Staccato will do this for you in the `write` method. The documentation\nfor `write` does not mention that `write` will also return false when an error\ndestroys the stream. When the stream is destroyed there will be no `\"drain\"`\nevent so waiting on a drain after write returns `false` because of a destroyed\nsocket will cause the program to hang. Big mystery when it happens, too. A real\nwhodunnit. Staccato encapsulates the drain-or-error logic.\n\nBoth `end` and `finish` are so fiddly there's now a\n[`stream.finished`](https://nodejs.org/api/stream.html#stream_stream_finished_stream_options_callback)\nfunction in the Node.js streams API dedicated to accurately detecting whether\nnot a socket has really ended or finished honest and for true. If you read the\nsource for this function it is full of caveats, exceptions and reports from the\nfield. Staccato employs this function and pulls some of the logic into Staccato.\n\nFurthermore, this function leaves its handlers registered by default to deal\nwith the dangling error problem. I'm glad it wasn't just my imagination. We\ndon't use this error handler, though. We insist that you register your own\nnetwork error handler on your socket to log or ignore all network errors.\n\nOh, yes. There is now an `async`/`await` interface to `stream.Readable`, I know.\nBut it went from not raising an exception to raising an exception between\nNode.js 12 and 14. Perhaps its settled down now, but that would leave the\nproblem of dangling errors unresolved, and doesn't settle the question as to\nwhat to do with the write side when the socket errors.\n\nStaccato is currently an alpha implementation as I walk though my code replacing\nthe older Staccato 12.x with Staccato 13.x.\n\n## Usage\n\nThis `README.md` is also a unit test using the\n[Proof](https://github.com/bigeasy/proof) unit test framework. We'll use the\nProof `okay` function to assert out statements in the readme. A Proof unit test\ngenerally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    okay('always okay')\n    okay(true, 'okay if true')\n    okay(1, 1, 'okay if equal')\n    okay({ value: 1 }, { value: 1 }, 'okay if deep strict equal')\n})\n```\n\nYou can run this unit test yourself to see the output from the various\ncode sections of the readme.\n\n```text\ngit clone git@github.com:bigeasy/staccato.git\ncd staccato\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\nThe `'staccato'` module exports a single `Staccato` object.\n\n```javascript\nconst Staccato = require('staccato')\n```\n\nStaccato was indented for use primarily with sockets which is a duplex stream,\nso our examples are going use a duplex stream. Rather than setting up and\ntearing down a server in each test, we're going to use module called Duplicitous\nwhich provides a mock duplex stream.\n\n```javascript\nconst { Duplex } = require('duplicitous')\n```\n\nStaccato believes that streams just truncate sometimes. This may or may not be\nan error that will be reported by the transport. The sender could just close the\nsocket. There's always an application requirement to validate the the data that\ncame from the outside. With that in mind, if an error occurs, Staccato will stop\nreturning blocks as if the stream had ended via `end()`. You should have an\nerror handler registered on the stream you provide to Staccato to log the error.\n\nTo read from the stream you use `staccato.readable`. When you reference\n`staccato.readable`, Staccato begins to treat the underlying stream as a\n`Readable` stream. If you never reference it, staccato will ingnore it.\n\n```javascript\nconst socket = new Duplex\nconst staccato = new Staccato(socket)\n\nsocket.input.write('a')\nsocket.input.write('b')\nsocket.input.write('c')\nsocket.input.end()\n\nconst gathered = []\nfor await (const block of staccato.readable) {\n    gathered.push(block)\n}\n\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nYou can use `read` from `staccato.readable` and you can pass it a `size` which\nwill await for `size` bytes before returning unless the stream has ended and\nless than `size` bytes are all that is left.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nduplex.input.write('abc')\nduplex.input.end()\n\nconst gathered = []\nfor (;;) {\n    const block = await staccato.readable.read(1)\n    if (block == null) {\n        break\n    }\n    gathered.push(block)\n}\n\nokay(gathered.length, 3, 'byte at a time')\nokay(Buffer.concat(gathered).toString(), 'abc', 'read')\n```\n\nStaccato will swallow the first error that occurs. It registers a `once` error\nhandler so it detect the error and stop reading. It has no choice but to do so.\nA `Readable` stream will not always emit `end` after an error occurs. You must\nregister your own error handler on the stream you provide to report any errors.\n\n```javascript\n{\n    const duplex = new Duplex\n    const staccato = new Staccato(duplex)\n\n    duplex.emit('error', new Error('error'))\n\n    const gathered = []\n    for (;;) {\n        const block = await staccato.readable.read(1)\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n    okay(gathered.length, 0, 'read stops on error')\n}\n```\n\nThe Staccato write method is an `async` function. The `Writable.write` method is\nsynchronous. If returns `false` if you've reached the high water mark of the\nbuffer, or if an error occurred and write stream has finished. The async\n`Staccato.write` performs this check and will asynchronously await the `\"drain\"`\nif necessary.\n\nThe `Staccato.write` accepts an array of buffers so it attempt can write all the\nbuffers you have available with synchronous calls to `Writable.write` and reduce\nthe trips to the micro-task queue.\n\nHere we leave our write loop early because of an error.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    await staccato.writable.write([ letter ])\n}\nstaccato.writable.end()\n\nokay(String(duplex.output.read()), 'abc', 'written')\n```\n\nIf you write after an error an `Staccato.Error` is thrown with a `code` property\nof `\"WRITE_AFTER_FINISH\"` and `symbol` property of\n`Staccato.Error.WRITE_AFTER_FINISH`.\n\n```javascript\nconst duplex = new Duplex\nconst staccato = new Staccato(duplex)\ntry {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n} catch (error) {\n    okay(error.symbol, Staccato.Error.WRITE_AFTER_FINISH, 'raised error at close')\n}\n```\n\nYou can use `Staccato.rescue()` to recover from the error and move on. You\nswallow the error when you do this so be sure to have that listener registered\non the stream's error event.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n        duplex.emit('error', new Error('error'))\n    }\n    staccato.writable.end()\n})\nokay(errors, [ 'error' ], 'errors occured')\n```\n\nIf there is no error, `Staccato.rescue()` returns normally.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nawait Staccato.rescue(async () => {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n})\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf there is an exception is raised other than a\n`Staccato.Error.WRITE_AFTER_FINISH` the exception is rethrown.\n\n```javascript\nconst caught = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\ntry {\n    await Staccato.rescue(async () => {\n        for (const letter of [ 'a', 'b', 'c' ]) {\n            await staccato.writable.write([ letter ])\n            throw new Error('thrown')\n        }\n        staccato.writable.end()\n    })\n} catch (error) {\n    caught.push(error.message)\n}\nokay(caught, [ 'thrown' ], 'exception rethrown')\n```\n\nYou can also use rescue with a `Promise`.\n\n```javascript\nconst errors = []\nconst duplex = new Duplex\nduplex.on('error', error => errors.push(error.message))\nconst staccato = new Staccato(duplex)\nconst promise = async function () {\n    for (const letter of [ 'a', 'b', 'c' ]) {\n        await staccato.writable.write([ letter ])\n    }\n    staccato.writable.end()\n} ()\nawait Staccato.rescue(promise)\nokay(errors, [], 'no errors occured')\nokay(String(duplex.output.read()), 'abc', 'no errors occured data written')\n```\n\nIf you want to, you can perform the writes synchronously using\n`Staccato.writable.stream.write()` but you'll have to perform the\ndrain-or-error check yourself. You can use `Staccato.writable.drain()`.\nIt will not hang forever if the stream has finished. It will return if\nthe stream finishes before the drain.\n\nTo keep from looping forever on this condition, you should check the\n`finished` property of `staccato.writable` before writing. If the stream\nis finished `staccato.write()` will always return true and `drain()` is a\nno-op.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nYou can't wait for errors. They can arrive after you've moved onto other\nthings. They arrive at any time. Some gremlin in your code could `write`\nto the socket a week after you've successfully served a request and\nthe socket will emit an error.\n\nYou can wait for the socket to `end` and `finish` though, so Staccato\nprovides a `done()` function. It returns a `Promise` if either `readable`\nhas not yet ended or `writable` has not yet finished.\n\nIf you haven't called the `readable` property getter it does not exist\nand there is no `\"end\"` listener registered. If you haven't called the\n`writable` property getter it does not exist and there is no `\"finish\"`\nlistener registered.\n\nTherefore in this example we're only waiting for writable to finish.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => console.log(error))\nconst staccato = new Staccato(duplex)\n\nfor (const letter of [ 'a', 'b', 'c' ]) {\n    if (staccato.writable.finished) {\n        break\n    }\n    if (!staccato.stream.write(letter)) {\n        await staccato.writable.drain()\n    }\n}\nstaccato.writable.end()\n\nconst promise = staccato.done()\nif (promise != null) {\n    await promise\n}\n\nokay(staccato.done(), null, 'once done, done is async')\nokay(duplex.output.read().toString(), 'abc', 'wrote')\n```\n\nStaccato will handle drain correctly.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.write([ 'abc', 'def' ])\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdef', 'drained')\n```\n\nNote that write accepts any form of iterable.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.write(function* () {\n    for (const string of [ 'abc', 'def' ]) {\n        yield string\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdef', 'iterated')\n```\n\n`Staccato.writeable.consume()` accepts an `async` iterator that returns arrays\nof buffers. With it you can consume a stream without writing a loop yourself. I\nuse it to consume queues created by the Avenue work queue so that my code is\njust a matter of connecting pipelines.\n\n```javascript\nconst duplex = new Duplex\nduplex.on('error', error => conosle.log(error))\nconst staccato = new Staccato(duplex)\n\nawait staccato.writable.consume(async function* () {\n    for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n        yield buffers\n    }\n} ())\nstaccato.writable.end()\n\nawait new Promise(resolve => duplex.output.once('finish', resolve))\n\nokay(String(duplex.output.read()), 'abcdefghi', 'async iterated')\n```\n\nIt will also correctly handle write back-pressure and drain.\n\n```javascript\nconst duplex = new Duplex({ writableHighWaterMark: 2 })\nconst staccato = new Staccato(duplex)\n\nconst promise = async function () {\n    await staccato.writable.consume(async function* () {\n        for (const buffers of [[ 'abc', 'def' ], [ 'ghi' ]]) {\n            yield buffers\n        }\n    } ())\n    staccato.writable.end()\n} ()\n\nconst gathered = []\nduplex.output.on('readable', () => {\n    for (;;) {\n        const block = duplex.output.read()\n        if (block == null) {\n            break\n        }\n        gathered.push(block)\n    }\n})\n\nawait new Promise(resolve => duplex.output.once('end', resolve))\nawait promise\n\nokay(Buffer.concat(gathered).toString(), 'abcdefghi', 'async iterated drained')\n```\n\nAnd here at the end I'm going to write some notes to self. I tagged a commit,\n`yet-another-discrepancy` so you can see it. This commit, the one where I add\nthis verbiage, and where I've resolved not to be a Node.js stream expert. The\nbehavior is still changing. In a Twitter argument I'd probably be told something\nlike I was depending on undefined behavior and that the change I made reflect\nthe proper use of the API both before and after Node.js 12, that it worked on\nNode.js 14 and Node.js 12 is not a Node.js issue, its a case of a developer who\ndidn't read the documentation.\n\nOkay, great. I don't care. I don't want to devote my life to becoming a grand\nmaster of this particular API. My goal now is to just the buffers off the wire\nand into my application where I know what's going on. I'm cruel. I do major\nversion upgrades of my little empire and forget the past. If I can get the\nbuffers into internal queues, I can start to manage workload and back-pressure.\n\nI don't go searching NPM for stream modules to use to build Node.js stream\npipelines. The only one I've ever used is `byline`, which I've moved away from\nbecause it sends the final, possibly partial and truncated line. I keep writing\nline splitting myself. Other than that, the only thing I'd ever use would be the\ncompression streams, so we can accommodate that somehow at some point, but for\nnow I'm going to do the right thing and ignore it.\n\nWhat else is there to add to a pipeline? Encryption? Terminate using `stud` or\n`nginx` or a hosing provider load balancer. But, even within Node.js you use the\nTLS version of the Net interface, you don't pipe through an encryption stream.\n\nI have an `async`/`await` work queue and an `async`/`await` message queue that\nnow provides a mechanism for back-pressure. Someday that work queue will use the\nmessage queue as its internal queue and it will have back-pressure.\n\nThere are two socket applications. One is for clients, the other is internal.\nFor clients we get into building servers serving thousands of sockets,\ninternally it's dozens of sockets handling zillions of messages. I've never\nliked the idea of counting on back-pressure to push back all the way through the\nsystem. Feel like trying to push a bowling ball with a line of toothpicks. Would\nrather throttle requests at ingress based on application metrics.\n\nAnd by throttle I mean back-pressure, sure.\n","readmeFilename":"README.md","gitHead":"9e38527ae0e2ef2e6e910833e86a95bafedd48a9","_id":"staccato@13.0.0-alpha.13","_nodeVersion":"12.16.2","_npmVersion":"6.14.4","dist":{"integrity":"sha512-PU1PqxXXNrSDXrbXoJIg9oyntW7C+FOh4mLdfujQRSa/YMaBfx7f27g/uPuky9cTYklkZKmuAQpCAN+jNMjXCw==","shasum":"c7e5841220525e8b38cd3f0c49c592052ac18eff","tarball":"https://registry.npmjs.org/staccato/-/staccato-13.0.0-alpha.13.tgz","fileCount":5,"unpackedSize":30108,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiAMe7CRA9TVsSAnZWagAAkXMP/ApOso/A55hXzie+KL2E\nb9nnCgRUwEa86UjZsAVuKk79EJkJBF+sEcLwwQOr96ihjGvfF00JnMTsCfB+\nz7HkWf6ZBpk7ehieb/AtB9+8mZRnyFZIdijhhJHoI0B1C0F4eFG9i1P2d6Ps\nEsHzz6X9TySFCyDujcNz9MTME0i844S1XCJRAv5b1cd3eOiW8/c31lcBYQLp\ntghr7gm12dhtpjfr2o/z+aIIfKUpBowcVTaG4vgxe3vzNTqoafN/3WhNSJ5T\nImyg9vL8quKFF4V/dRZAS40ilqivUF8/Z/jBQNpR0iSuV8K8XvE1Zdtk4Sxi\nXqWEGOkbn2brmqdx/RkAZ/SyPWCYzRWyidRUTbVXzlg9tPHrXGKvHMKQ3X2e\nHitEEHtMvnGSabjP/wEb5OX4DnSRD7wqtT8nnVOfCejkyD65c3ivJ3vTTKRO\nv2fmFA1rrJrtNhHodiUTb/n+YoKNbHXlkDuTm8eHma7dkMq4fazH6EohCZLk\nNiFTLMeL89TF+jghw1Rs9vqpvHs5a1lEKKnrwXq0CUMdHAw5+dSwfMr52Oyy\nPOeCKYdoh+Redg2ZKdKiPRke0Txyok5ij9tRf032hxz+CqBnPq4SWyZdmV5H\nj9fKLHjhkJP4pFq/UY9MItknU56/PVB2xMzzDU1e6DceUoCU75J9nI50B/0N\n8Sjx\r\n=2Fuq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCOZYnmEnRRfU+Qv9WGiJm/q3PMJA8Q/S3QAQAPlyd8uwIgT/wcZtzmngNsG0wSD02Z7hEgvd+qQGeVv2istP5ercI="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/staccato_13.0.0-alpha.13_1644218299059_0.11534333403928532"},"_hasShrinkwrap":false}},"readme":"","maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"time":{"modified":"2022-06-26T23:37:58.400Z","created":"2014-06-08T20:33:42.918Z","0.0.0":"2014-06-08T20:33:42.918Z","0.0.1":"2014-07-12T15:26:41.875Z","0.0.2":"2014-09-11T22:22:14.176Z","0.0.3":"2014-09-14T23:35:50.547Z","0.0.4":"2014-09-22T07:50:34.490Z","0.0.5":"2014-10-05T05:25:43.399Z","0.0.6":"2015-02-04T10:48:10.772Z","0.0.7":"2015-07-30T17:15:23.625Z","0.0.8":"2015-08-13T16:26:09.583Z","0.0.9":"2015-12-31T06:58:59.246Z","1.0.0":"2016-01-21T07:21:55.166Z","1.0.1":"2016-02-03T09:15:41.068Z","1.1.0":"2016-07-25T02:00:10.668Z","2.0.0":"2017-01-25T21:02:29.250Z","2.0.1":"2017-01-25T22:58:58.302Z","2.0.2":"2017-01-26T00:31:29.189Z","3.0.0":"2017-02-12T07:39:54.432Z","3.0.1":"2017-02-13T06:50:54.355Z","3.0.2":"2017-03-15T16:30:31.000Z","3.0.3":"2017-04-06T16:15:10.310Z","3.0.4":"2017-04-10T18:14:47.410Z","3.0.5":"2017-04-11T17:05:52.709Z","3.0.6":"2017-04-11T17:51:46.846Z","3.0.7":"2017-04-12T16:43:33.149Z","3.0.8":"2017-04-13T06:14:09.556Z","3.0.9":"2017-04-13T07:00:42.184Z","3.2.0":"2017-06-03T05:32:58.519Z","3.3.0":"2017-06-03T05:33:17.548Z","3.4.0":"2017-06-03T05:33:26.071Z","3.5.0":"2017-06-03T05:34:17.032Z","3.6.0":"2017-06-03T05:34:37.453Z","3.6.1":"2017-06-04T06:50:39.155Z","3.6.2":"2017-06-17T05:20:20.383Z","3.7.0":"2017-06-19T17:42:13.165Z","3.8.0":"2017-06-19T17:42:29.406Z","3.8.1":"2017-07-14T17:14:32.397Z","3.9.0":"2017-09-18T04:03:05.909Z","3.9.1":"2017-09-20T04:36:53.935Z","4.0.0":"2017-09-26T17:43:47.460Z","5.0.0":"2017-09-26T17:43:55.374Z","5.0.1":"2017-11-01T16:37:40.569Z","5.0.2":"2017-12-13T22:46:24.629Z","5.0.3":"2018-01-10T15:26:55.420Z","5.0.4":"2018-01-11T03:11:57.986Z","6.0.0":"2018-03-19T23:13:57.082Z","6.1.0":"2018-04-18T03:23:54.936Z","6.1.1":"2018-06-05T20:48:44.123Z","6.1.2":"2018-06-17T20:35:07.006Z","6.1.3":"2018-07-26T05:08:18.981Z","6.2.0":"2018-08-12T06:07:13.252Z","6.3.0":"2018-09-06T04:31:29.441Z","6.4.0":"2018-11-19T01:04:53.954Z","6.5.0":"2018-11-25T08:38:14.292Z","6.5.1":"2018-11-25T12:08:27.323Z","6.6.0":"2018-11-29T10:23:20.002Z","7.0.0":"2018-12-20T08:31:09.872Z","7.0.1":"2018-12-20T08:43:00.800Z","7.0.2":"2019-01-03T03:31:48.890Z","7.0.3":"2019-02-18T15:55:22.961Z","7.0.4":"2019-02-28T08:55:27.010Z","7.0.5":"2019-02-28T09:25:15.303Z","7.0.6":"2019-03-18T12:31:15.438Z","7.0.7":"2019-04-01T23:09:21.667Z","8.0.0":"2019-04-02T01:53:29.333Z","8.0.1":"2019-04-02T15:11:41.330Z","8.0.2":"2019-04-17T00:38:59.706Z","10.0.0":"2019-05-23T00:53:13.348Z","10.0.1":"2019-05-28T05:25:56.434Z","10.0.2":"2019-07-02T01:50:14.538Z","10.0.4":"2019-07-11T06:12:02.678Z","10.0.5":"2019-08-19T18:57:48.519Z","11.0.0-alpha.0":"2019-09-10T03:04:22.757Z","11.0.0-alpha.1":"2019-09-10T04:24:19.586Z","11.0.0-alpha.2":"2019-10-12T04:05:07.007Z","11.0.0-alpha.3":"2019-10-23T12:25:46.067Z","12.0.0":"2020-05-05T00:24:19.979Z","12.0.1":"2020-08-28T12:13:58.593Z","12.0.2":"2020-12-17T14:23:14.134Z","12.0.3":"2020-12-21T10:37:11.202Z","12.0.4":"2020-12-24T22:58:12.371Z","12.0.5":"2020-12-25T02:51:31.216Z","12.0.6":"2020-12-26T12:57:32.285Z","12.0.7":"2021-01-03T10:53:49.789Z","12.0.8":"2021-01-03T22:31:11.117Z","12.0.9":"2021-01-04T15:10:43.449Z","12.0.10":"2021-01-10T18:54:08.656Z","12.0.11":"2021-01-25T13:59:32.001Z","13.0.0-alpha.0":"2021-01-29T11:49:42.722Z","13.0.0-alpha.1":"2021-01-29T12:56:13.023Z","13.0.0-alpha.2":"2021-01-30T14:28:34.462Z","13.0.0-alpha.3":"2021-02-01T17:39:59.439Z","13.0.0-alpha.4":"2021-03-14T07:40:22.307Z","13.0.0-alpha.5":"2021-03-15T00:54:34.858Z","13.0.0-alpha.6":"2021-03-28T16:39:23.413Z","13.0.0-alpha.7":"2021-07-22T09:26:12.959Z","13.0.0-alpha.8":"2021-07-22T09:27:18.272Z","13.0.0-alpha.9":"2021-07-22T19:28:47.182Z","13.0.0-alpha.10":"2021-07-23T06:37:37.445Z","13.0.0-alpha.11":"2021-08-07T10:22:54.040Z","13.0.0-alpha.12":"2021-09-20T19:15:42.217Z","13.0.0-alpha.13":"2022-02-07T07:18:19.409Z"},"homepage":"https://github.com/bigeasy/staccato","keywords":["cadence","streams","callbacks","control","flow"],"repository":{"type":"git","url":"git+https://github.com/bigeasy/staccato.git"},"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"bugs":{"url":"https://github.com/bigeasy/staccato/issues"},"readmeFilename":"","license":"MIT"}