{"_id":"fracture","_rev":"88-6fbf62287c531edfa8d33ccdad2f0c0b","name":"fracture","description":"A error-first callback work queue that splits work on a hashed key.","dist-tags":{"latest":"0.2.0","canary":"0.3.0-alpha.74"},"versions":{"0.0.0":{"name":"fracture","version":"0.0.0","author":{"name":"Stewart Ulm","email":"stewart.ulm@gmail.com"},"license":"MIT","description":"A fractured NPM package","repository":{"type":"git","url":"git+https://bitbucket.org/%3Astewartulm/fracture.git"},"homepage":"https://bitbucket.org/%3Astewartulm/fracture#readme","_id":"fracture@0.0.0","scripts":{},"_shasum":"df9d188bb1034dfa79736f161ad92454373f3eda","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.5.0","_npmUser":{"name":"stewart-ulm","email":"stewart@kayak.com"},"dist":{"shasum":"df9d188bb1034dfa79736f161ad92454373f3eda","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.0.0.tgz","integrity":"sha512-FxXZ2hZMF9+OiveLFsWJus4xn+ONWbo+mgC0llfn/Xm2evGJZ0IPzBOKCvexcw1pa5OEmS1qdIU6Spe7038tlQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDrIWt2XI6ckHbQfkISrfxZ5C1fRXKUPLM53d72xvlhfAiEA0pNtrOsIfJJoin84bOAgY0R13oO0/jzC1e12rguQWmg="}]},"maintainers":[{"name":"stewart-ulm","email":"stewart@kayak.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/fracture-0.0.0.tgz_1480172783866_0.6318462111521512"},"directories":{}},"0.0.2":{"name":"fracture","version":"0.0.2","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/fracture.git"},"dependencies":{},"devDependencies":{"proof":"1.2.x"},"main":"fracture","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"213c29f614e58eed01ca275a63293e885eabd7d2","_id":"fracture@0.0.2","_shasum":"856e8c7185ff6da2e98f0c9cc7ea93a4f0f59d48","_from":".","_npmVersion":"4.0.5","_nodeVersion":"4.7.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"856e8c7185ff6da2e98f0c9cc7ea93a4f0f59d48","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.0.2.tgz","integrity":"sha512-BM286PSQU7kHF5Vw/bmtnTGdVGn8skpi1U8g3Pcqvq41bZLIQILi7m0sdlS9UZQNLfeUfiwE83wqBH+kbzEgOQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDAd0jn/xEO15FOG3AyVlLSTZrIJBD1SOfwpQe31FKP5AiAgsYKwUTHIS1sNFozkPZgiNQOtmLQ1bmxbBoBm0hJuzw=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/fracture-0.0.2.tgz_1490015761810_0.5513148074969649"},"directories":{}},"0.0.3":{"name":"fracture","version":"0.0.3","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/fracture.git"},"dependencies":{"abend":"1.0.x","cadence":"1.0.x","extant":"0.0.1","hash.fnv":"1.0.x","operation":"1.2.x","turnstile":"2.2.9"},"devDependencies":{"proof":"1.2.x"},"main":"fracture","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"32baa20048e991558594127eeb19b145fc21c975","_id":"fracture@0.0.3","_shasum":"77fa6f3d053e2d9ba50c148d3980f5ac11ab8d97","_from":".","_npmVersion":"4.0.5","_nodeVersion":"4.7.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"77fa6f3d053e2d9ba50c148d3980f5ac11ab8d97","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.0.3.tgz","integrity":"sha512-oUs2D2GgcVOoXlwqL1FVGJZTsZb7J7zugnl71uMUpseLiiXob31ae2vT5Uz0AUJQGr+uMaZpWxcJWSn5XzVM7Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCk/EDyik/DURQVxWu1uV0JPzPfjlCgkPdDwh8A0IAKvQIhAK0r/uJAINfEsAYGFwOQFpXHP2JkuDdvOJT3zKUWNBfe"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/fracture-0.0.3.tgz_1490032006672_0.6590741027612239"},"directories":{}},"0.1.0":{"name":"fracture","version":"0.1.0","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/bigeasy/fracture.git"},"dependencies":{"abend":"1.0.x","cadence":"1.0.x","extant":"1.0.1","hash.fnv":"1.0.x","magazine":"3.0.x","nop":"1.0.x","operation":"1.6.x","turnstile":"3.2.3"},"devDependencies":{"proof":"3.0.x"},"main":"fracture","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"316c443e4c264855da4d1669517105cbc5906a23","_id":"fracture@0.1.0","_shasum":"cfec5a9a687086e2b0995efe50351991df0c1469","_from":".","_npmVersion":"2.15.11","_nodeVersion":"4.8.1","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"shasum":"cfec5a9a687086e2b0995efe50351991df0c1469","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.1.0.tgz","integrity":"sha512-VNWokW5m8U+m9nzKq7bbptee95+IkIuVz6POmbkpjKCDhybethCtEQ7d3+XA0loHYodv7j1/cs3Bbm7WWq1Fmg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGDddNPMg4kukv8oC1XKhHWkvLt6pMiwclCLc+0xNal7AiAB+XS5iro6y+hZOep5F8NvVZUG6+jwPSbgMtV8JppkaA=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture-0.1.0.tgz_1509030192397_0.3298405474051833"},"directories":{}},"0.1.1":{"name":"fracture","version":"0.1.1","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"abend":"1.0.x","cadence":"1.0.x","extant":"1.0.1","hash.fnv":"1.0.x","magazine":"3.0.x","nop":"1.0.x","operation":"1.6.x","turnstile":"3.2.3"},"devDependencies":{"proof":"3.0.x"},"main":"fracture","scripts":{"test":"proof platform win32 && proof test */*.t.js || t/test"},"gitHead":"42a4c844c2d5d344816d9c660a3a8f6b9d98c341","_id":"fracture@0.1.1","_npmVersion":"5.5.1","_nodeVersion":"8.9.3","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-2HsEc7/r2twSHlRAxtxd9FCCr8W4kUHgjjkeAleJpGdJBHdPRmLAnOaEVpe81Dq8Ar10R7Gx50qpzvCe5ap36A==","shasum":"53a85907af5b017e519fd89eff3e55d6bd3dd235","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.1.1.tgz","fileCount":10,"unpackedSize":10666,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGaoKtFofyBHAJAJG5xIbyOISJbdJPFxqdQiVoihCLO/AiAKxNjQGvnE+vqjuP2kCHIxO1Dti0bvgRU7ADObF2WzUw=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.1.1_1518698721941_0.3568741910603488"},"_hasShrinkwrap":false},"0.1.2":{"name":"fracture","version":"0.1.2","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"abend":"1.0.x","cadence":"2.0.x","extant":"1.0.x","hash.fnv":"1.0.x","magazine":"4.0.x","nop":"1.0.x","turnstile":"3.2.3"},"devDependencies":{"proof":"7.0.x"},"main":"fracture","scripts":{"test":"proof test test/*.t.js"},"gitHead":"650c7e5f5fba2ed28e6ce807b09e8c6963b15d84","_id":"fracture@0.1.2","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"dist":{"integrity":"sha512-wfJYw1fk8blFxS2VnSGHTfX0EqDinKElWkPh4/kn9zB6HtKjRRO/WJVGi06/Uz5ncEUaXF/ljOjxWED0z5C1Rg==","shasum":"40b4e84a54c99e2ba419510238f01e1a5d45a574","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.1.2.tgz","fileCount":4,"unpackedSize":7523,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcpschCRA9TVsSAnZWagAAc28P/2ib6nQoUJVtgctLd5MY\nlYbbAj5YdzvQnnU72lt2yGSr8u5VnhwqRsAWwmzu/cuIdg/fU2j10Jd2Aq+V\nuCrDZm72S5ed6SvVbk1UCWmzcJ1wEWp/V6Mu9SVn6h8qphzrCVn8+MYPL32i\nhJxCLPbT6AKxdSBHmFGYKN6rSpXDIbIt+SE/bJh4+5nn+l0GvyhmamnDGoJB\n/sADPwYmHZ3QUy61JyZdeALNSvlYsp9/S/O9DOJD5lbmGi65URw5mEpJn31V\nO41NYdqnE6SMdj918EbLC1iqj6Qlg+/UBmhys2piitgPONGFZmDFnlBnEoJI\nnH+5SFX2zeqYl75Hh83GXcupsY3TQT1BoGYbK/2BgsMW1oM12ma2bOzlWgwD\nalmwByVPvdkYxiLnR0jAgt3Rnbds6wfhqONZwELSgw+Wy5PgsKSJ8tbh7Haq\nsknm44S6RgO0UA6frmYIxkF1ChtOQ3IT+vRzpRA5xeh2jyV0VAwFq/kPz7gS\nU5a5db7PjNj975EUikCet1mgbLnxp89gKVhUNMotvCIREXVhWzr04J8ynoU+\nElHwN6avC520mm0XQJBg75ARLoBJjAbyPDjoDuciwq2QRKW0fRlghNpyAZyz\ndI5pfCcz/A4eVN2wHryb9Ox7wzuZss1fXYsSQjLfzpUMLIFs5OobY1rXeq8F\nqgpQ\r\n=zBBF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCVzoylLD2DE0DQARzMsY6lybPGDDEpoFLjo6HS0BQ5fgIhAIqH1Wq6TD9BkDZz1ZnwtloOLg6sH8Xy7OyEBxZBNENQ"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.1.2_1554433824755_0.5599555228969573"},"_hasShrinkwrap":false},"0.2.0":{"name":"fracture","version":"0.2.0","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.1.x","extant":"1.0.x","hash.fnv":"1.0.x","nop":"1.0.x"},"devDependencies":{"destructible":"4.0.x"},"main":"fracture","scripts":{"test":"mocha test/*.test.js"},"gitHead":"92fb3c2c27741a99890c90236b33d6e746f5630e","_id":"fracture@0.2.0","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-uJf8iCl0nHlEGDrafkO2an2JYaEPhlRNN+NkFTi6fDZLYugGCnb4Ssdm8GtIKKWfxrXVwNYS3/6luuQGIRqC9g==","shasum":"a85b53656864ac080ffb8babe4a813a155eec1bf","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.2.0.tgz","fileCount":4,"unpackedSize":10036,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh2rd+CRA9TVsSAnZWagAA0OYQAIRpBBnUMaigRRaqhkIo\nJ3WfNGmmugE4w7l7MTFrkKyFeDiKWIfn1n7Su0Zkhmz5+ynzq4JoPDBobOoG\nBrS4l9Hn2CmbVdxpCKxkxMpLu8al8lHQ98RUdUOI7akTjREE0edqhQ/ILZe+\no5srLNrKajWj6pcRnXz/gByl9PXaz4KGh/avKQ8ym5C79YOhlG5BQ6Sg96mm\nTtaGu2B0Vnpz/e/G97xI8gRTzZtxAfksX1lzzufN8gCqf5bi1sfNQ73N3mZC\nqy4U96MzKEom5tI3qmg1pQSX/K9BFq2sV+OLAndcw9A4dRn0JlnDVWcmDb18\nOXhpO/vc1uZ88JlM1ESfsUfjHOY6ubzjSiShVR+OQhkULdIQlWxiVGxYvNhK\njta5G+BhO69Sut4bxeo22zDo4pKg7QzO2VJaXWHb7MCX9WTKAQAu+9qp8q1y\nEasaOLcW70Oajn8hC/2HKvelJm0FSR45bDnSCRO7olbQAL5zcF/k9Sr/ixSC\n76wvwnaAI1s87K7daytuogHearfBHYtnIrfcmxPX9CKFDQuB989KnL1vMP3X\nVHtxzQHsApPRz51Y0jGdgCeb6jKU8e1JEYXFrms/TAab6/uBabpXqUKH7YVn\nxCzbVq9nf8WqDBTeJlLYPZAvIyvInB3cuG7+OD09VhnQ4Ei7pweEpUXdMNcv\n1fjn\r\n=pzd1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAk1NjuwEaqHadhEly+eK81Hk1kUD4Y31b0qMfvL5XItAiBQTZPywEsFXJOw2BQ9GNBAtKySR2CKLUWw2pkNw6oG/w=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.2.0_1566708542606_0.261476875234405"},"_hasShrinkwrap":false},"0.3.0-alpha.0":{"name":"fracture","version":"0.3.0-alpha.0","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.1.x","extant":"1.0.x","hash.fnv":"1.0.x","nop":"1.0.x"},"devDependencies":{"destructible":"4.0.x"},"main":"fracture","scripts":{"test":"mocha test/*.test.js"},"readme":"An `async`/`await` work queue that splits work on a hashed key.\n","readmeFilename":"README.md","gitHead":"c9035ada2f65ef612f43bcd4b5bb0769d30b56d3","_id":"fracture@0.3.0-alpha.0","_nodeVersion":"12.7.0","_npmVersion":"6.10.0","dist":{"integrity":"sha512-PZYaSZwqVRBgwrGKXkLD4ss0cIjlbruNzAiZVohyCdvNSq3f3j2UCz+V7YUjZ9QC0ztw8Yr/LZBgq8TQ12C1HA==","shasum":"9c03cd1e198e6d2d2670ca75cabd79cdd829a25b","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.0.tgz","fileCount":4,"unpackedSize":9988,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdhoqaCRA9TVsSAnZWagAA33YQAIARwB6rIk4CuUrIGLZx\nYG7XmHwBbNO3kb+QICWlaDBvwWxNK5oG2BPTlQxMqvQptv9VtsDlblXuuPPA\nn7rbampCQppjHlj6yULE4hzNtJy8K62qSXETs3z66ZDpZ3YfcJ78v9dxul64\nNO+IYwf/YjQLyVDfMuj2mU5Jv9F7yBBr5fWbtJFDrJBBhDu0n9W9MbaxaYbn\ngVy7kieNvLPcWFm9ju1G5yFEvvMH+UYboZHuZCdC61Jp8joBtBLbgvZ53BaN\n4JP8VXXj4lVYUkAE2AANgJiFq4W9vQ/KAj5hgNq0RfB/QkQYYppNApePeKMs\neFLAlPrX4wsDdsQ40e+8tPpUPa5GqCVMROttSM5fSpAlMKORSxIaEQsIetdB\ndEt2OuqzyjgfMFNv6/wjPM5POTFj+49FKNT055e/nbTxUo3On3EPJbix8SQr\nyyX6yOBlssgRq3G1KFzKR5ZLC+5prM9ZrKkNc0OiPeRvsGJHS0+4dnxMssSL\nJ5ftMcRnKdbAsfqNEU6SZBHWWSs7keF2CXw9QD1j3K4fKa/bhOV8mZylzGrc\nzclB2K/8rnO3fKQ2/RmTm58KJYskSYJZ6CVyaB/bjU2r11nmhVe1SP0C0lXw\nPCjG34fwBNwZUxtcTZSiAH/HNyGiGFpwUbIUeprYNaCP1orjh+bidUGq3WlF\n31QE\r\n=zzc5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE3aYbp4KpgGA2weHqrSrLc7ZZd9fSP+Z7ePVoHEectHAiEAlT6SUoGUoSGdTT3RyFEOi5eWoxxcWpI2ato4r3Myb7c="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.0_1569098393636_0.2488276181080915"},"_hasShrinkwrap":false},"0.3.0-alpha.1":{"name":"fracture","version":"0.3.0-alpha.1","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.1.x","extant":"1.0.x","hash.fnv":"1.0.x","nop":"1.0.x"},"devDependencies":{"destructible":"4.0.x","proof":"^8.0.0-alpha.1"},"main":"fracture","scripts":{"test":"proof test/*.test.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that splits work on a hashed key.\n","readmeFilename":"README.md","gitHead":"98747a9415ca9972c732e50b9a16af95dfa558be","_id":"fracture@0.3.0-alpha.1","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-6Pme9rN+QAteO3KVbvKoF5qOeijNS1tuT+ANh5PY63TSnrortbaBEba+yiJ93PuT27BTr8e/0n1vlLhpit0qlg==","shasum":"0fba9dad26f2364fbb2bb5602630fd18eeb0bfdc","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.1.tgz","fileCount":4,"unpackedSize":13360,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedF/uCRA9TVsSAnZWagAAZFQQAJuBdojeo2QMM2mlYAxm\n+hEBpjD3nd9dGvZc/+FDVXgVYWk1Ocz6oXBhpz5nM1pmvAt8go65dUw9FG3y\n3nwuScR8GJkimjQXJ16lJkYpHICukFWw4aJOxDnvkgagt9eNeqdeTyTXDxiU\nx3uBqphcWD7GvAR1KKeCueXZczbVrGrv5aMPNhasHvr6d0XykHxqSP63RhS0\no7rxRrOqk293ugpaAm0fKzWBV/vJ3irFnbY+Z26f5+HAMkMB+b1Ekg3Oe0zH\nkYsu4x/Q3LDKdNu2yytTsdWj1tl48fNxqJQAkxRF/cZvHshq0K+2YZAw5+Fh\ndm0HKgyzP+IswPHpvZVavjE2cUNJ/rL8aPkU52ncDUVj0AaF/OBYvqAd5epp\nI87Aj3GHXZFoqD9JIvmU3ZvOBgzFJeXDu6UQTuuncFKfHg7/gNkC8t6SZJcD\ni9GIMH3hJJu+jNUYGYY6CpJGF/WNb19bHgD+wFkjk8BPABmFLBba65UADb7v\n+gL88mtii29oBndxABFcGoT8qU/bcc2+PYrxJRRGPPHEIo22E31wsEMsrMsT\noDTO9OgyNfCcwsIwKI1MIyqFQJK2HeBuUZkWqQhHDDPV2y0YTg23P/cOjAO1\nOc5fP6LkHMUtMx1Dk19P4D1ImdL9c5ITHIMrHQ9MpaQOreVjLo1Hd9WmKYSX\nH/g5\r\n=FH4I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDvCzl+WK1hXghdKcsSYeTOcp5Kg5ZZ/ABZQwaG6uPxiAIgbPRB/IlpHE/KHfNOrk6ICT+CBjFDzZE5iv+0scBhjtE="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.1_1584685038312_0.3546575835766572"},"_hasShrinkwrap":false},"0.3.0-alpha.2":{"name":"fracture","version":"0.3.0-alpha.2","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.2.0-alpha.3","extant":"1.0.x","hash.fnv":"1.0.x","nop":"1.0.x"},"devDependencies":{"destructible":"5.2.0-alpha.5","proof":"^8.0.0-alpha.1"},"main":"fracture","scripts":{"test":"proof test/*.test.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that splits work on a hashed key.\n","readmeFilename":"README.md","gitHead":"a4bf2e0cda28c76a0c2e212c2122b6db72751864","_id":"fracture@0.3.0-alpha.2","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-7z2SJCT1krtjbKiPUOA6oGYl3oMZfuozxytdzj86NJSIeOEUjVsDespwvyCwBc+J32hkm3Er/T97K/aojjTU4Q==","shasum":"59bf821b4da9cdfe5780d22de9148c3d441678c0","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.2.tgz","fileCount":4,"unpackedSize":13376,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedGzMCRA9TVsSAnZWagAANRUQAIONq0tkEHA2FC1wY3Ol\nT5vJPYzD/phLh0KO60FJzuag3UC+JTY9NNAAnH+H0cAYSvq86I3Umg8+r2qm\nD/vB6P0uJVTew84lR+wbytIOVr3dpaRk+BUqnmi40sfX1camXm7+Ro1920om\nwtdGs0TfaMFzv4jgsLNb7Y9O7G1KJFziwoF6qLbltmqyb61KD57jA17yymH6\nbj5xf1DrAU87Sz908LT/OV8gKhLkEkx5Hq8nsunFzctDYlQbD2n4Eah39v/0\nL2F5TVQIX721u1KFgucRt4cd0oy7b/5CZ4BrrG0E0qYpezhO1BJpR7fmL3N5\nBhA+kn2GHqXZrkMte8meA/0tOzA5iWVGphIt4LH+fArRqwyfmLCBASwlZq56\nZCGEYrGXzwr+gzY6B68Id+AbjpQReB9SAiyqjMBjYgmm93PFx4HOHOuy5Oa4\njeaKEF1M0pDdGv2d6ug0P6ehl1iWo21z37tQW3yvY1ikJqNswpeDQjk3lseG\niTEE+Tvm5uBy5GQpINuI7ekZoANvoPjKNjw61xcEMG730wmxQWA3CajVAHDy\n2AAqZh+B26WXsbCJe/iJxaeYcXhimOc6KazLo3Kn6aqLZtQQmJBl0ml6leME\n5zIPDjoJe+7ATICJ3bOZr3Q3tevGLAAUPfQK7iuazfA+wjuDfIPQsJgO7ixy\ngVdQ\r\n=eDQN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBeCvU7M3IIhYkSPDWFzVx4JkJYyXlrXge8mstYjBDtnAiBw4qRvIs0+j8Qb/YoHEG0jJFUr9XS/JSUCkdiMoeHbcA=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.2_1584688331705_0.738854053226641"},"_hasShrinkwrap":false},"0.3.0-alpha.3":{"name":"fracture","version":"0.3.0-alpha.3","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.2.0-alpha.3","extant":"1.0.x","hash.fnv":"1.0.x","nop":"1.0.x"},"devDependencies":{"destructible":"5.2.0-alpha.5","proof":"^8.0.0-alpha.1"},"main":"fracture","scripts":{"test":"proof test/*.test.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that splits work on a hashed key.\n","readmeFilename":"README.md","gitHead":"7ea489edc21978ed2761603c7cf41f5796f4025f","_id":"fracture@0.3.0-alpha.3","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-rqsGAuK7tTxvBfeLw3Mwy+EQj4KH1wsE1D9tY2mGtOlDMFhi/QQWBH2A1o3RY2efBzW3HK/P5/8paeOaIzF9Iw==","shasum":"44cf4a00ed1a61e18c125ab2ec57ac9942479911","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.3.tgz","fileCount":4,"unpackedSize":12825,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedbnjCRA9TVsSAnZWagAAxiYP/3Wy7IxRN/sDYJW08diS\nWWcHbiuQzyE8HYBX5PIf84GbVSeMBUHfAKRjporKHhwBjvKjrXeGuG+D9hDC\nG/hjt105N8gFZqL9WXh0YIhgJ1Yl1PCtcfYdeyUmt0tInWt/o92L36ofYxMJ\nJcCJjFLKOBf5h2fGZ77mNlx7//p1qbv2qdwwI70fMV0k7S87fy+Z9k5/5yxG\n/vxDMywQJqwEZT9SwtPRLlR+FPPHzeoVQ67PLgjq5E27E1lkIbM2Ajemrp7M\nq10vzBbQ0lq+BqdXuuV02GUiukfCg/QLtmx4tFJ3Vvnq5YdtJpvV3hvPpPSO\nvNG0fH6gxiQR6ASqD69qhR6mmhH8utPNTYNqeJybjPCOvjir6nQFi/4KgbhE\n4l4IAL+QcRzSlqpbqPxw7gd/52NOS5Uazg8C51Ti1n70qNGkfbNHXOea0ZmK\nUapqZGfFMqAzvF60y70SMhj+zcZvCzlLM2G2REMvpDX0lcKEtFEt1OsBn0km\njDJ7RN4+m98gdcbMfBRx29HzzaJkjFnwbioSPMXbYF24HIItAH9bQqt3OjYG\nciUC6buf25efuRJ6/Xsmd4uC2SKZdcqHQnlmuS6w9UQsRxWLKN45/+zCoqMB\nXCHGe/I6C2lTrE28z+uqa8gfuN2nvzKNG22btSoBPM7EkIZw0VSHXfdaI0F4\nRgqb\r\n=7HgR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUCkTZkeIc/tnOvLlTG8ycEeYFT6zldhyqVtu2ZXrvgQIhAKpRD2Nrglaf1tUZMDdUOs0W/XkZ9Y0wHOqSPO/fqMiE"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.3_1584773602775_0.1692560135142256"},"_hasShrinkwrap":false},"0.3.0-alpha.4":{"name":"fracture","version":"0.3.0-alpha.4","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.2.0-alpha.3","extant":"1.0.x","hash.fnv":"1.0.x","nop":"1.0.x"},"devDependencies":{"destructible":"5.2.0-alpha.5","proof":"^8.0.0-alpha.1"},"main":"fracture","scripts":{"test":"proof test/*.test.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that splits work on a hashed key.\n","readmeFilename":"README.md","gitHead":"05c5d51708084bce2a56da971ec2463b19fec287","_id":"fracture@0.3.0-alpha.4","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-9AXbXoJryReMf9lhh8XFhsolEMH36EfsGg2zBapGUnm1xhlcgZZJVC/E+CSn0OcQFD3Ej5Ge4rQG2sBwn9iVfA==","shasum":"e1320a5f2a6583626868fddd0c1fdd4e39ded932","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.4.tgz","fileCount":4,"unpackedSize":12825,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedcZ0CRA9TVsSAnZWagAAqUsP/2FasMI3SM83LPZOpBAS\nNROLrVv1TU9lDLg393+B8gLdGemfSHl0jDBpzel4FfSY6cB+RmAzYCh15yff\nv1c+BAeXDjsXL93khcCFiJq0BcNUg1ss+++2XZekTqh3kHqugYyaFMtBTmFp\noXoJa2/RCJvSPOiOh9QgGaVZLOdEecxoTEjvbil03yRRDvXjVCv6X/ReO77l\n8Qv1lHshSCHjtGrLgN6Uv0YhlQgi47KN59LDNQ2c7zuKaKDQQgxEyLFiLPuh\nAyiv/JDWTI+isJ+IWBum+XuRsoJRYhQ4NiuNUkqbdeHj5zJqG3Ff+NTwYwfT\nOyBtE6PYLKw1CuZM6wppsVLN6rrRJ4XKewqAex3MQcySFsAJmBl+SPJNa/14\nVnluxAzOeBhylv0M9C1mipP0j3geWPBEeib8UPLLphkAk9b6dsV1bZlj5Zay\ndw0n4Ri7ioOSL8OuYg3L5Bq93jIH1jU2DnRvvLg+KtBA9+lHjaHHB6I3pBQr\nW0cthVmLO55rCfwFGOjmnCPMIqdjNlLi/lyYtgP6nnT74YjI6niHh2+2eGvx\nks/e+N3ePYLxV8ywdnEIYCZmcFBlsUGvNGoLqvoKkj0W+w1QVrzYKHe0DCuE\nvGy8K0HCfJBYJ0/uj/XoZbEXdw4qm0vXSunEKI8UhfBl3osu3LQG2Jfx9tbf\nIszS\r\n=5AoU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD9HPnswJDVYm15EOc1su+P/GsUncrB4aXp3S0sSMQI5gIgb9vd1/oiJkTARL8GwdZjtN8uZi7hznp/d5Hm0jcKRI4="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.4_1584776819963_0.5440041692867743"},"_hasShrinkwrap":false},"0.3.0-alpha.5":{"name":"fracture","version":"0.3.0-alpha.5","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.2.0-alpha.3","extant":"1.0.x","hash.fnv":"1.0.x","nop":"1.0.x"},"devDependencies":{"destructible":"5.2.0-alpha.5","proof":"^8.0.0-alpha.1"},"main":"fracture","scripts":{"test":"proof test/*.test.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that splits work on a hashed key.\n","readmeFilename":"README.md","gitHead":"12662dded9d75be7e2a6fed1036b2590ed7dc298","_id":"fracture@0.3.0-alpha.5","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-JNvzceD6jgwCEX1//YrX3/nfilLTMHsTOGNJsQnwljiFan6DuRKSJURc4S4vkCXvnHDjycKxAxcX3J30e4YGdg==","shasum":"f7ee62e006290948dfdf50a5ef8d2927a5c56a10","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.5.tgz","fileCount":4,"unpackedSize":12830,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedcl6CRA9TVsSAnZWagAA+/AQAIh3w8cPmJsjp9QguO4E\nar/LPRtq48klZhf9NVRtFKs6CwB+mvuUvsfxKij4zCAhMEvAoBAkUxtEdpu1\n9uECKFECoR7j0oRZSoKXAzncNO9pZRyf60/KvT6T+JzdvxUnx3y2TAjRRMeE\nG6UCRSQ2wJ8O0syirdFWs0i0bwPjOJxABkBAQ6mv5x8o4XMAwhhfAdS36RD3\n5THccqQIboIFoV4BmUeavIcXsaq+9HSvMd/evl6E6txJtgrx+bvN4wsjdbd/\nupa6YL5gz/fEVRoeMb9JPIRQoSWSC1LP3sY66WBjI/TKMqwwnJkMSLkAkqE2\n7KU6hASPel0uPCCCr3CCbX3ju4B3NyfCJmhpDUvQ/9OqUA08OSdBXzpFlb7f\n/w5qL9Oyv09uO15rwoOCrOpvM88gRxe6svtOnUwTBRUgKgZUqjK5l2FLUMF3\nUOf5dCxyq+v0aighqvfssC9CGYPL1qW1J8aWmCh8FkTl+heV+8uFyYaYcCb8\nd+ridUFvMBfPb7a/djRL/uCBcOyHT++J9Laeg4KXdd7EMuwzPaQeq2bSH/Bz\nKffDR53cFCsOyDmBCUVsgeOjrJZlEevntXpk4lNrFi0IrcdLOajy1dUftnpU\nvp7PJmzawXfKeO+Mhjt4mWmwHEd5xUDZqpgHJPMDkLetulomT6EzlMB3ayZp\nNclZ\r\n=eL+2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDjeJcgMPhmAEWuUgDYLZZYlzgUBVObVdYOfQJp1gWCVwIgAWPLPRGjmkIOAQ52QRVLCZlncyeCGFJw3i34k0SjaHg="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.5_1584777593692_0.6361878351563901"},"_hasShrinkwrap":false},"0.3.0-alpha.6":{"name":"fracture","version":"0.3.0-alpha.6","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.2.0-alpha.3","extant":"1.0.x","hash.fnv":"1.0.x","nop":"1.0.x"},"devDependencies":{"destructible":"5.2.0-alpha.5","proof":"^8.0.0-alpha.1"},"main":"fracture","scripts":{"test":"proof test/*.test.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that splits work on a hashed key.\n","readmeFilename":"README.md","gitHead":"1ab931d72e95bfe7d4ee3de9ed6956a2b0b93241","_id":"fracture@0.3.0-alpha.6","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-0nRNXUH8PNGXcISdXmo8NEBLheCuAgu8WiG6n2iy0PBDW2GDUDzWogut0sFxbM5+Vp0TLK7ugL3Hd9PoIMOqdA==","shasum":"1c84b92aac20ed9096f0957f00f3d4562891fcdd","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.6.tgz","fileCount":4,"unpackedSize":12810,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeddpWCRA9TVsSAnZWagAAIOgP/0LmpyIUeRTHusg2fA1u\noVaUExdV/NetgMdbJUHKVsj4XiL3QZw0T10Zx5c0fCdpaUSdfJHZGXkM+zWl\nWs7CesOnpPXkHVB/BnBRVZcav2DuWNCKUTZjdD7DTvoVXnX6gyJaIW1uOqGv\nQdtBCh3VqO7+uwVVcS0p1YbwwWGTelXST8Kh4DZhFLgxfW47yPGx0iLh+mwS\nrDAtONziU2CBZ7hapO0dG8FVjF7zr1VSuxANCu/27/7Mcs1RufEPggVx8pxm\narmcS+dXZ+NVnSJZ/5Ar3vJCXBZOAKcBErb8I0C3gnZDnUbDlvUX1slywiTt\nDOBkhInVb2aeFpDxg5WIVCaEUw1Mb8SIqOQR7BTdao6PbJ1n3+txwJcgEpmj\nGZnF32kFnXNJPKLbhorfcFjYqSMzab/VoHcPO6CVW+dBZXgme2rJU7Oiz0Hd\nWGdfI0t8z4MLR9qwYKb5rpYrds/c49vNyxPgzWE411CMfgpgy8TnYjEsedEN\nCjc6LsMFxD3nogBuAuxMlWYLR7rBqpyggILDn4hmC4Z8uIrLz+EDk2Q9TSO+\n4TleuDWW3rfmEH/4W4zsYTUPV21TZTh18oxjsFL6sOlLBsQxg02KGDqWCkpX\ni8Lvx6d6rf8W+tudnHuq8eGqIOGWRHuMoMU5TzmudGwOV08Havs+732HVsKn\nCpAI\r\n=TiPC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCpFcjdIWHyjjsJqfH2nWtwNgHzhPXFPTS85rV99PyAgwIhAJbFIxCKc1tdX7Zt6f77II8jxv85koxChGjSbVesFs0o"}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.6_1584781910523_0.1853775139359224"},"_hasShrinkwrap":false},"0.3.0-alpha.7":{"name":"fracture","version":"0.3.0-alpha.7","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.2.0-alpha.3","extant":"1.0.x","hash.fnv":"1.0.x","nop":"1.0.x"},"devDependencies":{"destructible":"5.2.0-alpha.5","proof":"^8.0.0-alpha.1"},"main":"fracture","scripts":{"test":"proof test/*.test.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that splits work on a hashed key.\n","readmeFilename":"README.md","gitHead":"210f09162f7f4034d92efe8bf8ff6bdac62641c4","_id":"fracture@0.3.0-alpha.7","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-FCmbLOI2xFTbhDVVAHbD1nef5J/v9/LoeTub9Mw0/hDzV0lJwlBrsxlekNjeyYALP6+FeR/aRIiGvWSZEsGPuw==","shasum":"af0285197f681cd4bce9a3029ab9dac23d52a7ae","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.7.tgz","fileCount":4,"unpackedSize":12862,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJed1fNCRA9TVsSAnZWagAACxwP/jR0aT5+oB1Dpxpm0nAW\nmeln3i7BIKzFQi0DdJtcKoQ9o9zCKQ5BuW5m2UTt4Twtua98GvxNH9ZGbRdc\n8p4xSWAYeTsSiMDGqfyQb1Jd8otyJllZVWVqdJum4j+EiIZW07I+tnJUe1SV\nykdJHRv+67zl3jZ/l8b4aYz7MO7Jx7uw4cXmxaOym5uDh/9ddwSi6oZTkLAU\nlAws9tkAv8Me4bP+pzx2XyM1iTOcOBc3ZdKPQ/jZOujjpIGJiDPWYph90PfQ\n3Q07fIZ5vTA0FLHYZLLM0OJCvnlRrfjsPiI5pO4QK+p0E0RmqtfoPi+RSIR+\n9udA+zrCeMr2tec7K50Q1pouIUs5fWt2dxYNpW3wxweFhLG4WPxf3+R8ZY+b\nwvVI6aN8ihddtJ3BIk9fV6CiR00gqZpZOaQdI1czc3jhCOB/Ih+4SprOiV77\nXBFH3eJ5Ykfb1xfqk+Ey+BX71xvZqu8Xub5ZKmKMfFvmUsO5SJkAHZqndAKH\nE2o1wYLlogmmgMjC/ElNM6maXzIVBvN7a10MMQHEfG/g106gffMyesGmmj5O\nCLmXME2gh0NwIF8sYm2eczDiN0m9zsPV9Pv9El3xhf8WZKNl5gz+1WrgBnLe\nbIz+n5ymrbuZGk8rQkl9PfHzXyZV3ENDzTLpdrByG/+aassUnh+HeflXj9OH\nTuZL\r\n=tuRO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBAUYgYTy4sBvhge29AUjouZMmPqud7Ydovu3jATNHFeAiB7+5bWk/McILUm2HOPyUdXpLXQrMITKd7GtHUDqNMHLw=="}]},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.7_1584879564657_0.8084521959679456"},"_hasShrinkwrap":false},"0.3.0-alpha.8":{"name":"fracture","version":"0.3.0-alpha.8","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.33","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.18","proof":"^9.0.2","rescue":"7.0.0-alpha.3"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"316ee3deed16930a62006574480bee790b3d1bbf","_id":"fracture@0.3.0-alpha.8","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-32x6ELdLePd5HSx/kmOD7g0UG7DrGn0uIXcQqpGZ0kNfYMNkJUr5vjOQDuI0XVBokYheE/9rzo4AU7nRH4ABuA==","shasum":"2f211aff36fcc04958f182904d8cf34661d75ced","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.8.tgz","fileCount":4,"unpackedSize":8252,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf35SbCRA9TVsSAnZWagAAYZAP/jXaTQ7ItFjA4dZSKf7h\nUPCYft4mixZwOkVsszOfNEWr22HciIqWk2uQ3VN8b6k69RlVBZGtTFxQgcRo\ndEUySoE921abj0FT77fK4pykhtJJ2ekc69F1F4RXRlnf16NebPQHcR0mHq9k\nwYJT8xFPJWLD4cMnhqSL3aqmsFiu5ZzRtxSwQSErjs+zLIxYlstguDGKRuhc\nmgoa69QZfYuATzFTB/H3AMLNN9HTyq8MKRxNWAhjaHmBuyDzOcfpo8YFeI0t\nxXXVX6KaHyEs5B4w4FEywRHByhiKIiE1eTGJjpg5ssYB6p7o5NdEpY+DQimz\nAYGHfrwpH5mpWLj4m6tFcri5ViMW8JK2M+LmDfQ7OTBQB9OM7o5wniRn8RDg\njQt/CtSoZhy6fvjMKbn3MxAKGJqN0RvpxmO0MpMb+sPKpEyvZhRLLbQ10MJz\niVO5Hj07PWxqiER57TlRTdYgHrTL+SYoVYpZfrop3QgCp5kERukKEe1GUBVN\n0GN6WSKSsLkT1kmdssJAlFZSjLPfuWyZ9iElkQmmARpqF7oIX4xdIQif2wFi\nycyY9ukQIqeFcGIwdumeW5jYZzxFjFzs9FVcDeEfysjViakPTlmNyAJRV6G0\nSenKNC2C244g0wTog+NOlVmnqXSaPkl4m42QScX9uvUL7NNG1LryIQRSKqvf\nmBps\r\n=tgLp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBrx97TFQ4lFkBXvqqXEM57dYyr++gPDc18lwJAl5X1RAiBnGpc1wCouPRmpUJouNho9FFaAyNvSX4y5nFtuqyIGMg=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.8_1608488090860_0.940675122707183"},"_hasShrinkwrap":false},"0.3.0-alpha.9":{"name":"fracture","version":"0.3.0-alpha.9","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.34","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.19","proof":"^9.0.2","rescue":"7.0.0-alpha.3"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"50131b189400878e808ed9653d8e060fdcdfade3","_id":"fracture@0.3.0-alpha.9","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-tDmjFEOYH/XX7A5mcA9VKfmuMLpG0EgsZBuYknRpo5VxNS6sID9f/NTo5J2NLKYcn2t7tmxTlqybpoBo5nWRsQ==","shasum":"724d77659bebc988d8f78b4c09165ca9395b032f","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.9.tgz","fileCount":4,"unpackedSize":8425,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf4EsxCRA9TVsSAnZWagAAbkQP/0gpEKX18hRzBRCEG0Wr\nqrUfq3zVD5j1gunAM8jk5GaqQDPDr+7X6rH+1ewR9OreHXOQBdbCRsjtKRz9\nXARCmAqpes7SvF2kKp3ly1IQ/OSw0m0dLqV8IZK9g2SjUv0HIqGmySfRpPgE\nr4ojj4KwJDl9fx+uzkRb3XeUwdRVftWSU8Km4gm5nbnO++xjexC7/y9Dc806\nGBuqc0W+DDY1m0xPYsJo/kt6zKRFFOhk1v5S54meOnO+A4nTAxmpeKFv0rt+\nzONpM3AfW1QVakRCU5yRlPx75SJ9+EHDCz5O7iId3m6RCzqbA0j/MipETpKe\nUG9EOrU+atxts+Tuiy0vL8miAevIgrHn00xMzvDw5SmXNifuI+A6Xnh51pfL\n0Z9ApLdJxOes3zpYDDGzfBxSZXQyynFAFj7Q9CpYoWPrR1ME8m0P5o0u2cVf\npJNXcQS7EMZgXHKXZh3v4oOT2Kz+tYU2591AjS2iz8zIgX8zjG5b8sKKyftx\nnPM31X2tp7UmCFNgf93S0ksBMokUtNZfmhJNlwl4eqrGTdO9EDFQE4aRtCGD\nORWLk+IKxy2cZjLyCzbJPPpuJyNIt/AFj0yqaWeZPC4dWjZjhx3dC6xmqyST\nzLJyr5vc8lbW4VxP60nEcA7VpzqoXm7YjWGT1tfsVmlHKSSnufvqaoLfqPn6\n4jn+\r\n=8qzp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF1ysTyEed8tF0V1raguB7nwEAfKx3AvaoemLentnrpaAiEAztbJDadjEuHDOfntDEj3xtK/FPtNw4fne2zyCG27l8g="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.9_1608534833240_0.7673142156784347"},"_hasShrinkwrap":false},"0.3.0-alpha.10":{"name":"fracture","version":"0.3.0-alpha.10","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.34","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.19","proof":"^9.0.2","rescue":"7.0.0-alpha.4"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"893042427a5daccc9169c48083eee69b0c60fbb6","_id":"fracture@0.3.0-alpha.10","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-cApT5LbKmxNXOt1Ywjf68JrclWagcXJpCop2uNgLnUZrvjrbPUmO88V06sGrl41Uph85JkYXKlFFPlZ7hVgb8w==","shasum":"6c4600cf9adeed88f2726ecf1ba10d7f737bcbd1","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.10.tgz","fileCount":4,"unpackedSize":8426,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf4PQpCRA9TVsSAnZWagAALAAP/2KGzL+7al93cCyXf9v0\nNizlY/pzxcVUBsQfJyciAGoxNxYpinkc/53Pl8DJHbqYoEBE6Vn3T0hvo+s+\nQ50c67n5iFXBSrypg/L63dB/V9V2oSbTgNTTOZaHexd5VezzYLIYWMOh8sid\nyQLWUyDCu7f6tm3ZHlDi4/SooxUKZp0gU5u0NhpuBlzaLhzCT6DpXWZDDpWa\njQGFjsyDbFKp3wF0XlBtQLNpb+DaYWTEBlRwrIWCJyfBdhzJD1lNrUkZzVBY\nJpw5RADL8PYnOrFbPQlxLbGet6LK7sNRgMnInJw16mnuZh6v1Dpbzd/HQjoE\nZu72CMYCchXW7W4Kw+iGfqDxnlnV6L0EHVcP9713iRO8AacRn/eO6PNFni2e\nDM4Fnj6vLmTBSfiMK0UEOY2Dfm8kcV8juKgk6tt7T+fM82UVkv7miOtTxkwM\n+3I8q5xUVmyzdX9gBD4spEW6bTDaNOEgFOHS1H8M1wERl7/ICntthQEdRwsi\n8cPgGDmkVsG7yW9E5mgaI21M+ZAiejfMORmHE7G5PGCXWWdyzXUVmxdCf0P6\nZ1NuxIgKDIrTQSWJNTRM32xdmAH7H10oGAGlW9n5oXcICWfjJbtZXFeelaOc\nBcvk9wE1w53s9s8tol+7V0utSY1gbfFLEkABxLxWcPI6pXyM7GvfpTt1BseN\nUpd3\r\n=W9Uu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDNPOAr0KGpQJpXjIY8mom1reBvKNllrXVNiuCoUJKM/QIgVstHQuKrRPARVrSFgwWoahr918xWRl5+ZZ0Cx7c67sY="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.10_1608578089364_0.582622163097535"},"_hasShrinkwrap":false},"0.3.0-alpha.11":{"name":"fracture","version":"0.3.0-alpha.11","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.36","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.21","proof":"^9.0.2","rescue":"7.0.0-alpha.6"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"4c23ff252ae295209cb2ef24355ff34c2050214e","_id":"fracture@0.3.0-alpha.11","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-CjBEfV2kQM4kSEd+rGf0wtuU9S1Xfj87EPP5Ma45OVWBvVrD2IhFEAKEmXA+/Gqk+A3spHPmmbUirh6ANb2TEA==","shasum":"b91ffcf50594a12f334997cded870a01f1ca8817","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.11.tgz","fileCount":4,"unpackedSize":8341,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf4lmiCRA9TVsSAnZWagAAUr8P+wXu8/lOYatYieSrsmSK\nzZipzfDP30AyhgporA67dKPFjF814Wvd1+eCy8BYeXqHn1KzMjtJQNwxPnM5\n+qL/IkQw9fVhhOasBeZVtJjrKbFwhE3Dy0+VWW9ei4Ge5QAnX9EmSnIk4hAC\ndDz0j+XGcCezjchOOjm11+USmo76tgly6dl1Tytkdb+fbcnvSfegjpYN3G4g\nK7zdBU9aEQ1ZMKeyE8SyLbs7QaKuCZO5zH1yhdiXDOwcZxJyDRPphMYV84D2\nhH/keWnUhX2auhf+rWfZ4r9FLSl+yN0wv0EVMGgPsV3E7deZSr2zLEV56TJI\n7hvnO1B/HUeaFeVCsLk7ZXRk64aSbTA8m3mP0RiC6xzULfp2+E0zop1foGO+\nL/15uI6qQ519+pyePCvVzReMqAwlc6RVVkQbfogNYV9rWSeh2ubXW0P5801M\ncS59BMii3PdrzKnteS2jSUlcoWkSbR/uGcQYYESVcY/3PhGfTaWg8is9mCAM\nlfV5zGmYRrs9FvrL4xOO9hBAGyOB0SkPmVJ9/L7Va0OisWFGE1bBJm7NEAYq\nR2JkTSMkGTOr/fxuDb3ghFlJkkjhwSX/ZCNUjrrYiQYevUWzD4sK26lRpCvt\nw/ZTmNm/NS0ubA0Y27f9Gtq460rfA5snT31t9CqWZS3Ml04QQodW4IZq3kGn\nkODt\r\n=hYf/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbCGAMCachEDycDAiLpZ+vZwvfvD+4GNIvPLOjCv/dgAIhAKWOvjw8GQ83+mVNfsDdCJRJ3WqlDHYmG9uICuXbqH3t"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.11_1608669602015_0.9255730055360869"},"_hasShrinkwrap":false},"0.3.0-alpha.12":{"name":"fracture","version":"0.3.0-alpha.12","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.37","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.22","proof":"^9.0.2","rescue":"7.0.0-alpha.6"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"2e9575e9e84e6a324267bfe24a92e31f2a8b8744","_id":"fracture@0.3.0-alpha.12","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-euYgP++cmSaI4ZulZiSvIngY0TMEHirCIdwx8QjYdxYImr0zI4UUYj/FDWQ02PQwNvV1bG5L+heykAeMm5+mEg==","shasum":"8661e631cdd8f086e6baa7e82b5ab4774ccc83be","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.12.tgz","fileCount":4,"unpackedSize":8386,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf4olYCRA9TVsSAnZWagAAiSsP/2Y6pI3KmEwGOVxpn7Ys\n2SMGlkOA2fLmD0Ynlvi2iR706ScqnIVc7Ungu6undE/FxWkbhcS/TPjbIUX/\nFho0L5AieMk9iTDObH/vo5n5wdtPyPCyv9+04u7B5Nqu+FZuOywSOXxRRjE6\njpn0LCWflupHN9LY1zhp+g+RikJetXY7rCzxja1x8DQ1gQfr93f7Z1rssgqu\nK16Ra2jjG1p5ta56ejqV8yLnrFH8S2GR7OqH7WT8fgfprmWEuHZf5AiwLlpb\n00k4BxgiGWPN2ceQScVIt+WLuSCb739Nrt/FGzelx+hLaB2BMYAumnhRjMe8\n2evLaG27PO4rDJ0f1JifiPE5sl+hpvkXWKInBActn/b5jOcoULOB1FH4CFa7\nG92hS0ZhjQUt7hUq7AKfYpXJ5O1zSS6UCV8kXuHDg+CQtBNGs2TUp1L9Mb6X\n9imNO64IqQVG5jSP0txEcDcC1ay/V3NwMtHRpTvU8j5FwvfZ/XDgwIMLj81M\nN5dJm9yhldbdln//derjEZUfYgFbeEcB323i6/OwHyeCPE6elbAbJU5DkXjF\nly/YZNhyAF+qetgUvp1hhiw3myOV2GhuxaaI/Vpup6W5msAODLUYiLgdH4aP\nalGBnKAq4caSt9S69lNHhzwQxO7Oo9meKuUXlNA6n20ODzGt3+5PVuQif7QU\nO/Z3\r\n=UUQ8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDChoaoY6Yrweor6C75+4Z6Ap+ghc4z8+rcfjiPEdM3nAIhAJa80gTtOkULrwTq346nlRII9A/3xDnZvRIim8U7mj9c"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.12_1608681815618_0.3608103271251877"},"_hasShrinkwrap":false},"0.3.0-alpha.13":{"name":"fracture","version":"0.3.0-alpha.13","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.37","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.22","proof":"^9.0.2","rescue":"7.0.0-alpha.6"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"41daa8c417e0b21dde0e2175aa8db8519c1045d2","_id":"fracture@0.3.0-alpha.13","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-RFedN2BhsBP458mE+aDgos0x+olWOJk6HH9YpNCLwW8NaGsTShPW50C1ZAU1D4I+xMsMvGFaVssEgNelrxouAw==","shasum":"55c50542b5f6dc239fc0362c487ceda3ab74003f","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.13.tgz","fileCount":4,"unpackedSize":8387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf451nCRA9TVsSAnZWagAAcA8QAI1qeqcg7EtAf4jpPwNe\nwdS32MABpzLpyPnyXS0tz/sX4cIGocFrFKy396vEKf72vrJcpjtul1vl3DNl\nM1EnMWV6P+qqYpVKzd5t8Mni4RhwfGHjn2psSeUiZmS1VFGwSg1HdxV/uCd+\nRD4kS/qru+veqr/SnWCwxBAXhUoyhfx2W97Clp69K2RT7ddl2JDBhc0rDX3q\n3tCSNyHmqrGQXWW1E0qmIHrlXY8WiQ3DZ5J7WzzqTz+/YcdGNJuLaBgwdtpk\nyGvAA6bA2zh51YezUc1rQPZt514VwPukw6AYRSG7/0HPLS5HAQbvkNYw1ICM\nPCFzWGTgikFwpKc1mDKpXv15aNzm9uObX773i+q3xUY9zZh5SyAVQlFHvFwI\n3ipF2qNVhE9ekWmRloypXhnuv8bE6ZbF5r+yuoaFJFXMscAHWML37J6wJAVz\n8NxwOVajHbR/hLeDu4r05w1ENxywWfGdqBWm5ClIlmVSdxpmn09s0nBGfmPO\njIW6qeBygr6UYXPUBzu6YqBCjsOXFKlVjpra8llKQJL4wRk7/remD9c3Ub55\nKlcc2UpCTD1AqB0W4uWM2n8s6b6WxxUCj4VhxV23egWMt/fqRHO0UDz3H0cH\nTPOjfvycSiaWfSaRkknXBukkSifw5AG5Jq1Pm++ff6JVRmNEKfdUga85+UeP\n25s2\r\n=lPwe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFeOQbrTxesfJbeRQVocqbVjjDKqvciIKtOvO38xxn4zAiEA9ftEGwNzRC8qp+QfC0XWQOlq+2YGHFWmv0yXJSKsZaU="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.13_1608752487298_0.32965792258086846"},"_hasShrinkwrap":false},"0.3.0-alpha.14":{"name":"fracture","version":"0.3.0-alpha.14","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.37","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.22","proof":"^9.0.2","rescue":"7.0.0-alpha.6"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"55461c048f5e18cd207cea813034258f86792b9a","_id":"fracture@0.3.0-alpha.14","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-9rAcrJJ4ziIaGQfOYfXltpl/0ZN0q8ZoM5SOk5rRNaL+X7GSqMzpQae4FCf/1olT2Ae+kgdTcvLDZM6bGOPM3Q==","shasum":"93d04606009de5290129f978ccea1dd5c7e32d13","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.14.tgz","fileCount":4,"unpackedSize":8259,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5AofCRA9TVsSAnZWagAAqBMQAIaAVXchZhlqIG8DpvHW\nL9XZTD5vGFPPyC4Q3HhooEjKF1Cpu3RtsK4z9HlPy9dRNpDFmtdavsfWvx6l\ngdej709er6ZHZr/Gj3Mamuj9RrtnWHTD6X4wudjoVp5RZWiK8Mw823pllOKd\nO3t7W46LTlXlo9K1zd8+PxPoXisH8Lre1W8ERqu8pUfplz7yKMVFO4MAMT33\n+AbO6XJMBUYO7d+mLyzN2TzMNhrtNuiV6xJkJzO47SarZPQhbrwBvB9B1XxX\nmy4JdpBsH5gy09ebNUptBERiMFyfF3JOawKKLW98tyABItmp1SK/lnvkaPSm\nTeimyPoC0Qm9xXckd+VQjG7Ek3WKKxVv1W6Ffz1ByOg2T16vLfKNwOX56/CC\njRAfKVFJhK42+3Hktf7cvHUl3ukguTSFPA5Gjog96Ez0/+JRqbLtGjHrNyxn\nLj1TtdhL8Ekew9ApBmBXP0yYZXn4n4jXgREJmXdwL6xCIKEIwVtgqmCtIBL5\nt2Hx7odYmgT8tz7IEEkSMrIX/A3qAIUPs8LFlJ0viKLCxLDpTbGa9h/9nMfj\nrsjC8URr/Xq7j90HkY14Jgb4GP3UjzfsFN3fwSbIoqRcMaOZZ3Rb2LvNNc4r\nbpkUySBa0H59ExYgl9frle5equcjLqQximv/Eydzwjf+4BG6HAyXXy/idry6\nLQ/0\r\n=rZmQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBhpM+uRJABSeJqv+7vH+7cndj2xpT/TsRIrdPzm8NplAiBo9LfxgmTwKhcDu6PS1tuuQCUkmwQuyxQqF3VOFvk/Sw=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.14_1608780318932_0.5902854216409401"},"_hasShrinkwrap":false},"0.3.0-alpha.15":{"name":"fracture","version":"0.3.0-alpha.15","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.37","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.24","proof":"^9.0.2","rescue":"7.0.0-alpha.6"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"f3b2ebeddba6e5e61a82d5b0df38be82f0c0612c","_id":"fracture@0.3.0-alpha.15","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-NmFmyL2wLdMKHoUBq87Uny01UmEaDPojU4aGs5miAZShCaExj26GHczeV73yIvWo8+Ib8Exzg3t7qmhlyZzQ5Q==","shasum":"771a7cebd78687a7adf8bf28ffd966689045353d","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.15.tgz","fileCount":4,"unpackedSize":8259,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5CS4CRA9TVsSAnZWagAAPvIQAINcGkrzCaJvUAedzgc8\n5JppzuDby6ZueL59lHi7F59qNerbpekVXlpLhdU7lHx/YkanRU6jJY2Msvek\nqyzMaMjB7Z4vk80O1cGGrhE2EPHYwsb89KCaFWlG2Pwc1MfoN1ALDq0PG63O\nNBA/ElIH7ypeL7PrXm8AcbF5gpaiAI0fh/mkbWs/FAPyn7Egnc+kFOkG+XG5\ncPBYS3eWRvth/BxdXI6D1ovSrScnQk4rrmCd59ymYjvrt57wpEPqz1KCrPYY\nEXmsUvQa/p9r8YDymgsms0mfviNSv1PuATD/5C4Me+srXE3cLGB0gr/7q2RZ\njvGnx0/4Q797Tn7FM3TFLCUMC+afCmFx2MYTjSIdDSr9v5rhLv40nAThQ1LG\nzbsVK/3u814BYzcj2TiF5hS9BPsMTS0v+JgmXJ3NK+dvjaNWZlETw3jCCD+W\nEaIb7qybKH6yxQyce8XWCOBWwUHOHXBhoXiYozWwjDzc+dZRsPtv2O1xA5JS\nrjt3XRwhsgtIPzu+GRElur3jzW7iVNhVF7STZE3pJHB7WnvjVh7cTj3Hzpwa\nr7XCZpXHPCk87X20Upfe4dChcVvipiDVjEiWgdQ0qdSSWKgFpfbPDF9uFpqx\nou2XdJAVpBHUn98HlDdHUjtg4dN/k/JrMSka8LRHmkZb47dErfYW4AgcE5Db\nW26k\r\n=5qLC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICmMXOeogFKh2ipIUMf3dDDYR7igC51hLBIHEDI/NITfAiEA9uXRZqKzpy6sDN9T5x8zszDT69ZR4AgIdCV1DKxLgmE="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.15_1608787128475_0.6106571902764606"},"_hasShrinkwrap":false},"0.3.0-alpha.16":{"name":"fracture","version":"0.3.0-alpha.16","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.39","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.25","proof":"^9.0.2","rescue":"7.0.0-alpha.7"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"c4ead403841db1dd92556e1e78f9efa3be48e67a","_id":"fracture@0.3.0-alpha.16","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-TgaptLaZAEyHEpWgrkQMfdcGWuihA/AaBR4z4tReJvdmSRlAJ+Upe9QgA+1dh3173S/Xlout0XY7HSGpYjRUxA==","shasum":"7193b8f9686a987e38a4a525a525358b41651a3b","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.16.tgz","fileCount":4,"unpackedSize":8259,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5ChICRA9TVsSAnZWagAA7ioP/Az8HiNv/rm7Y2sPE8bl\njvlH8RNT6Sd2DoCh3RffOxtqrJle2YWXZmlVjwV5PXY4PXPlouhDS9gnlhks\nKK++sBlimJZ2s9d7wES42hQU/coXZaVLUIRx44uOlFBg3h2UAGX8vEvEfaJT\nhSZN/7dmyrIy/YvG7oZRqtfAq+wdskZmfJj/+pZb/ykgh8/hWyCqZSdO+lFk\nf2EuwgtGqFtb0+liSAaBUBoy1jKESeRodN3MjJBA7oR5eV64c0sE3zA9GKeW\nfUioheVbn85DFxNVjEraTnVTGwiUy+EpI9QIhHM5MC+LwJorfJVmltIa+SMQ\n3fH+YmRhLhrf4OqReA2FTw+XfcSR5VYirbc/KBv19B5nXBlYjfi0VoS3yyI0\nLsWhd/q2E2GSgnnZ68F8vdNOthRsBTLhbhONzol32xU2oiGdyOZX/XQL7EV8\ngp/cIM8Qxcgt66zB3BeXPsuFQY8GmITKLc3HY/90KAu04RaipXF/Bpgg91T8\nQX+m3IJ6/FZJRz+PbPA5b/etVwGRHFs/8kX1BmowgM9k9ael4F1nGIM7btXd\ngKSJE5YIx24z/Tqvn8K3MjMSBE1ExzoftnBh5JP16doBV1VRUr/a6Mzzsrns\n8Sdd5olAqQZLIEUJ+S7t2yY37p6PiRSpjvHnfDYnBFBp96gRbqf7He4yp814\n31Xb\r\n=I2jN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFQbZhVndkCdYF4eIkcNVq7vMON4GmzwZJtQf6MRtS2fAiAd23gLTD7RT2WGUf7Uny4Cv6nX8H+eNCo6K4MC9LDpWw=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.16_1608788039722_0.9108578494246524"},"_hasShrinkwrap":false},"0.3.0-alpha.17":{"name":"fracture","version":"0.3.0-alpha.17","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.39","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.25","proof":"^9.0.2","rescue":"7.0.0-alpha.7"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"61949654213e786d9003f670de84279ef70e157f","_id":"fracture@0.3.0-alpha.17","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-9TxuVV/TM2uJzYbxLi8lENAvHirR+NkAntncaVErYcqAykoWTUio5roMhqJTF7WmLUymkgmBs4JJbHRtZHphMg==","shasum":"d00a20462ab630519a1fd133a41fbe1ed4259acd","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.17.tgz","fileCount":4,"unpackedSize":8304,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5EHmCRA9TVsSAnZWagAAWcEP/1u9hRnjvnp1ddhZ+Be9\nRbSvJAzMuu9khTyOUBO7F98ENLi8cnwx5R8KhuArTqZn0FOnmsxyFnnUwTQZ\nCkSckrJkgM/3rfLpnHnhtSJADymO45xaUXTZmyBWstrPNW9qKTzta7A7r9fg\nVXamNT4RM+pIISyLCLMV6cgFYCWWuJ7BlTUmNHjoaoeqyuqTdgjbJy/lY79S\nc+pi7YjKvmFgitt/P6gKPr2M0VGrkfpBYtVPZVGOHpS4nRLGVUwd42lWyT2M\nlD81f2Rz1g+KsB4yrvx5ggcCD9zwjxRdLylKWwXKhz7SvYyacmLwHd/3Hv9A\n+UCE0L1wq2xSz1omC65d2bk8UbQtsVA/xzi+bGJ+ruoYBaLjIxsuTuqJcNXJ\n7/TFauE4GMiPPbEyJB6U/oeY/gxsJEmuj0f71X3EF5CMod11/SFHGI1VEWGq\n+q3Y1+MEZJ84XjIOybNHQjS8XHK6/ZnqJhxaubng7fqr5Xqv6vMHm62WEgfX\nxhBd8VedHiQtWl9ms7mDSgbKCw7GqSiwDdn0GezgRr8OXtRE2uDCsINAaeSW\nCZO+J87dVQz1v/crKAA/wInSAsQiPsvFy0R8AYtBHDpZlR6KOrM7/iuVI+nR\nL5MySG7eWne6KjUNfZZ9sl4TYNNPtv3Ylcdd0edNy/44/OpNF9m9wTtSZwLZ\nPnZz\r\n=BEgQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDwaNyKRsPsgd9OFKXOV2/o4aMyWKUO6a3YBx95IV8mOAIgFFUP4YjDG6735L2hLy6JGbs86o9NM++GYnusB1YIGQo="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.17_1608794598198_0.8308971911669332"},"_hasShrinkwrap":false},"0.3.0-alpha.18":{"name":"fracture","version":"0.3.0-alpha.18","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.39","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.25","proof":"^9.0.2","rescue":"7.0.0-alpha.7"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"a0052df654c088479a7245e48043d6a10b064950","_id":"fracture@0.3.0-alpha.18","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-ZduF5eICC8QdWtonn1kaAlUxjRBZvqK2tI/4U+NTx9qhrKHty7TBbxY3LlAZb2HTTQxSgU7Lrjm+Z4ja1iykAQ==","shasum":"6b59fed2de9f512111d991b537d4f167427d232d","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.18.tgz","fileCount":4,"unpackedSize":8311,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5GAoCRA9TVsSAnZWagAAkAoQAJdzbchxTTvCR/E4Lfvy\nCxhJgKjE6/1Ih+Tc98J+6HWxZp0vTIeJIP24LsBHNnQJ6puJQVZ74o1HajqP\ndJ0Y15BzKk/f80CjcHvZoqP9Q9vCpAIm7R52OS5rPHBMpWaNI4P2ERrq5gj1\nT+J/2cDBFivWbmiQEgu5j085CQyhLrMk8BsOi3ttYNsdzGBeEaX7n/m6vx46\nZ/9SWBnsak75KcZYxoaAUCrrky3gBLUfAPyBxBwVCn+JR8GnVxAvCbakBo5Y\n+28vtZPZ813Njpa5vjnSi1QE8KJgSRLX4sfhHdNbPgXuVucGqBdIAE8hGEbU\nm3RHmP9rZIeAF37xhiZtMq59q/rJRSrVxWgm1BGvfemA2PaT0GyOox6kcG3q\nfPeL3VrmNFRbzyP7MClL/qIqfiuqgdNfXfQpsv3nbZDxQyyUMtWlkEZjRbQ0\n3/xhqB/gYIwtbCW41OrHX1f/IwX3dIbxGty+9YM54MaWA7sASt37R6Gp+pZB\nJVlv330llS9avFdEnZW8/MnEVMZ9hnjyLjdiFDl5FBeoaR+7jmtOQJWcnEBg\njK1MvsgLQwY2YvcJYEeSBFz66jtSU7doIKTdOnmNt0gvyYVYrfp1SUD27NX9\n1QFzpaiOr93RrO03g6bVraZTmbLOC62w1MSjKRXJc9nb4zWiTfmIZS9Q2g+6\nrC/2\r\n=7+Uh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBECf89mrMCSP03a1A75ZgWKbdgFhjAgzvMA1SM72JmiAiAiEPB/m7XbGKjjofVGJ+EBYwCCyfTL1C1aVqsIJbhpww=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.18_1608802344055_0.7694314288361808"},"_hasShrinkwrap":false},"0.3.0-alpha.19":{"name":"fracture","version":"0.3.0-alpha.19","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.39","vivifyer":"2.0.3"},"devDependencies":{"destructible":"7.0.0-alpha.25","proof":"^9.0.2","rescue":"7.0.0-alpha.7"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"6b8c8f66af2c531c99b5c897ddd2974f6d18b4c2","_id":"fracture@0.3.0-alpha.19","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-woPpkIHoB1mF1I5WRHHtk++FW+TIVINQE8Pb13Gw/+tsYQngN06yAfRYeR/gCE9yLX6wkAWVqehaP/6qlZ9EMQ==","shasum":"0379bcd6841c4d59202a5ab198ded04a6c48bf7d","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.19.tgz","fileCount":4,"unpackedSize":8311,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5GBhCRA9TVsSAnZWagAAObgP+wc/M0oS9caK3lnOUIj8\nZ08kPFmHh0vJVKp9fCHowbi11AzF/pBi+rmZIamlOdMrjpr7wwsqf71bLbe7\nrGSuUnJV+42lWOBBC1NjHouveMPrjIoj9/g1+iyHnX0pP4Nf+1B4ue752PUj\nCxJ8+KoMirghUidSpxIilkurZDJa1xYXuHg7GAvLAT24zdXj2vj75lPH16ZG\nh/dARjJdFfszRznJUqNGOJkGqoQmXdxBv11o3zPnwk3gZfw/bYTXaZ0LC415\n+sVXjhfTPXfl/X2o2T9avncs0WZDll9v5IufoNiQCRF+GgRzANl13YSlL9T/\nse5zaFnY0m+Pl9OPP490UCW9aVkOGirbE+9zn5TaX0YbEnUrVn/LsJ0SEM2r\nAsrAgCWobZ4Pl+VOUVGH6cqbuECeYyGossV7+Ai2I2cZWBUlaWAgCGgAGqJJ\nUicthnaMhbaUWfrPK2d4pWygnuM2eWFBEO6WNjPRy7qdJ7QvEbII/iPvV2b2\nHp0OL5xTmzrJYLeCYbV7JNSOKrMV022cCBluwxPqKBUc+uQc9FwjaVGRWOYu\ni6Ee2t6TlKBqf/hi4AfqCkE6eXkKEnFS7bNFHSix9pR7WG/NUrYbZVjRi65l\nFUhwithHjnooy7lZceFykSpHAqDY/90TLTgSnOvhHXGdCH3AqZI6rmA1hSN1\n2TYK\r\n=+dpB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICoH2rNNrOxGCrgXxjPgcIjlYVALJ58S1WD0mmmsmbwmAiEAuanow1vJi4trEE6OvFZCbZwUIAC8hHxmyyzoihdh4fQ="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.19_1608802401419_0.37974174490672663"},"_hasShrinkwrap":false},"0.3.0-alpha.20":{"name":"fracture","version":"0.3.0-alpha.20","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.39","vivifyer":"2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.25","proof":"^9.0.2","rescue":"7.0.0-alpha.7"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"dc336e1603bd695738b47629e953ccb2d4b8dccf","_id":"fracture@0.3.0-alpha.20","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-3V/hZFXmoMTZwsJVlNneFGHYG8iK9/NXgB8PaSPf/4j+E9LWbuXIbna38dww3J/3Sl08Ba5/TLhGuCVPqz0wDA==","shasum":"617fd4197da1068825c1b58fe189cc2048b1b093","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.20.tgz","fileCount":4,"unpackedSize":8309,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5HTxCRA9TVsSAnZWagAA064P/iaw/bbwMT9mHXlWf4BL\n6lBrJeF30iePJRQsEL1p8C+77v8NlrZZAVQ0Bg92lj/sQHhW2mWMKQ20UMOo\nXjL74ithnxJcFFOqalMJqZBwlZ7q/+wkMLx5JRuNTaSBBXCzlzgJfzUNjZ4o\nbVu9mA2zqhFB7QXV3O2iW4D5kPPgKifgBohlx2C1fDV448h9n19pxKElZ619\nPZ0x/W8coPC0du/Mttx+/Mw+0reNBb/XV8S2foK30yb6FL898WO9eH5gAkUR\ncT4nhdsmuXT8fX2M/oZuOzBIWxReDjeb07jR3Vj4uYddirriD5w5YaundARe\nhvA87Pqkx4bhqO7V3SQjHgyqdhD/eZzpGMcrMmF7J7+DDbLjxOsDERv+PqfN\ny5I91fr/P50ttaQc0FWFNQh/6+SDgcvr7sG/RhhtgVwGDL5t7Llvns1CX9G/\n941Zaw/dXFZWjZxljpf+jqCJBur0NBBWa3jkEZIlnHH2f9hA12lgK4kE3Yp7\n/fUWYXw+liuv9AinKzLYzTua3Oy3v1z7B247PkQV+N1AQYwwRwtrAtmYu8Cn\nQI1oY7+5NRHpWyFVLj2SSvPap1cGrNMR4DUmTwW56fithcnXldXL4pAxIpZl\nIADrPfu8WSZS+847FlCjlyfosjicjPWgF1pX32VfyFpVhyjQe9cfPjxoYb2h\nX1kM\r\n=4gsk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDdnOWCIjK3UJuFfKuttZ2pff1m7hQDoG2pF6MYKJqw4QIhAJwKer8CBsCq9WO5iI/FDuQMzvQH1GbZ56pX9uZoe+Sw"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.20_1608807665326_0.9197267689939954"},"_hasShrinkwrap":false},"0.3.0-alpha.21":{"name":"fracture","version":"0.3.0-alpha.21","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"avenue":"0.4.1","extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.40","vivifyer":"2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.26","proof":"^9.0.2","rescue":"7.0.0-alpha.8"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"325cbd4f355dfc59e04c4c9e680d3c0b91adef3a","_id":"fracture@0.3.0-alpha.21","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-W7XBEU/WhUxWfFlW8SXQto7e10BOtPjD4KCNFJyO40v69c7ZiH/WpJyCg7VxGSLJvkJZo7RbVBuHN44Jpduv4g==","shasum":"e49cb6208b15a0199335f8d8f62d2ab8d542780e","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.21.tgz","fileCount":4,"unpackedSize":8309,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5V47CRA9TVsSAnZWagAADUkP+wbh8WNSG26BpxcaFOw0\n8BrtX2zu3wFJBgho3MocYqgAbx17xCuhrywmhiCY8GKTZH6g/ejB16xTnZEW\nyDQkOZZivvCt9LLdX/F3cndt0bW/Lbvf3mzgPGozHkKAss95eBYSgW5sWEp5\nB5/L7EQxa2doQa5rfuztDzZXCgDVQ1bBwcONCqFfxZjGSRkTfUIx29X6NUZa\nq4b1KEfDtqbhjST7g8rrFlgX2X5UuUKudRvqf3XHeN5Y/eBz5HHEavWRJosl\nhN2sdCrH7NIUWVQSEjN4fqCbJoHW1Znz+XHGZVrl4p2Njfu3go6o42KhSfiq\nw0KsZ3Ej23FTxeKS7cXa1qOnwY5vggYQwwhBItDuXDlN4OgMW+MdwoZrfFRG\nLGKMNy6JIrnJeePRRrjzLaa/jdg1AQncqttxCAo/GIqF9G9UOuQSw73U0zWo\nF5RWcgunwNUcu22FLiM0nF5AhDA3UrWC38GY3AFVipZdwLKLUmDzR2Ik6Z4B\ncJyOmbgGdZTu/YB8oct/wKkoX+hcKiGjG8XAAngcWQnyQv+G9i2uEPfnrO6o\n0cRCfbm2tD7PQbAkWZmCSXNEZrlxeoZbCHlvTt/UO0Qibf2YDcqL1tCMzdRL\n//AyOqdJEYpl5srjJgO8lNP2fcSavmU7io21yciR4/lubzsyKF25UNqEBpc5\nGQsN\r\n=IqSo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF9uFtLOnpLceaR3F2Lws589Cai3XPnI7C0+zS7iiUMNAiATNjistt8QAY5cf7zOKQCzc2LhXwmrtyJwYnl3WLoqsg=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.21_1608867387150_0.23724866582786053"},"_hasShrinkwrap":false},"0.3.0-alpha.22":{"name":"fracture","version":"0.3.0-alpha.22","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.42","vivifyer":"2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.27","proof":"^9.0.2","rescue":"7.0.0-alpha.8"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"91e55295a4bd8ab597bdb30a68d37317ce6d02ae","_id":"fracture@0.3.0-alpha.22","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-FZXaM5B5O71q2aEo6NYBXd2g2UImxHYfnySCaPgJg9KxVA0WaoudoNiho3vK8vEm0kEZSeS9xenN9q3ulhlZ4Q==","shasum":"e38bac4dc2a6f581736fcb0ea111676f3b3811d8","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.22.tgz","fileCount":4,"unpackedSize":8394,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf5uFxCRA9TVsSAnZWagAAjcgP/RmxPpxSurb1XdKd4D+g\nWfxYf14ACq4c2Wyj+SgzgTuQ9s4cccYHW58RnUsyNK8yJscKgUsHdysfrytB\nHbq1JUFWJ0SL6p1klMb67JTNpDaNcKGBd896OfFjEI9CjzWZEzg6ScBNcjg1\nXrS+JTVe4HSFE0ds8pNUbzebiQuxgACk6la3JCpqS4hjVyzN2yBqfIl7jdck\nkT4mubUK9K5irTF9p6+Y508bJHqOwUCn93mu+eHgeDwBdCPoMNJ/htbeoY8V\nR1PQFQwpAFfS7BkiTgPxlP96W1YNCXRGSEQ+4nZizVYR+Y+GaTmj9nUBUSul\n6a44RHBLYkZJgAmSri9B5Ut1Y3E39IFkFLwsfLfIQ6/k5maYN45SVjUBsolv\nBr5vZecfvIMux/rrlZNarNUTq9LlGhmfU2rIh9YlH+ydjhp1BRioEyIo2ZqW\n8TK5Kvja3WYE3qh49go05uB0YTysiOtOTCVI03LsEqDCVPUdn17Vw3m//juO\nhpy5hxfl8KM4VXd3ccpgHLHc44gCwPjrtenwPkRmTyuSD4+Rpu1JFje7vVn0\nSDqY67LVFL7wDLgA69wqJfbdWO+RigXnsnE6PqnbSH7Q+ySDY8w0Ma+IgzHs\nOZ5b39Gn9FBPTJp4e8bWj4A5gkIMZXf7dLFLOWXiiYz5+LLguFz7R62f4kmG\n+Tdm\r\n=cpog\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDLUVLhALhX53W/cAFhCNvxXygopr0ptmB9xWjvV/0cywIgIo5zlRBOBnbQn020woNGc6jWQYMgSDjXr0APctb+Gf0="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.22_1608966512730_0.9143227572283181"},"_hasShrinkwrap":false},"0.3.0-alpha.23":{"name":"fracture","version":"0.3.0-alpha.23","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.42","vivifyer":"2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.27","proof":"^9.0.2","rescue":"7.0.0-alpha.8"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"bf642cd6cd129a38b0f93af9d5524964c47c69a2","_id":"fracture@0.3.0-alpha.23","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-E0Xui81Q3CwGplJFoAj8zUdcT3Q9JBDS1OfQpsx9hmcr0aNe0LcD3oMNqskTsYodZ7Q1Q9Oef/3FTJh4CEe8ig==","shasum":"123cd402262b38bea36263851d8bcee1c539bde0","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.23.tgz","fileCount":4,"unpackedSize":8443,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf6/5LCRA9TVsSAnZWagAAVzIQAIHBWg3ECGPrC8VyRa30\nWI/89ftNgpfWxfpwjIvWKydE807BVbPvy6KDi1Sg3/Cvdsel7Y0/si+qWLnc\nuQgHsYfkHuQiQXlkyEZXrQiRVrRBbuMfY+wUeq8rDawtBi3NzpcOYBwHvHMd\nbruNJmjpk52/1gKvScNBeWg/GnG/Ydk7EU0Qr2uI1FXC/L54sl3QNYhfTDep\nubq7TwBCDYwk1ZmyFZkJ1QIQjOdvGnJBzdpIrgf2xBekZg/kA3rm0+HzMZvh\nll74Dkpe44dsqYk1bxh7eoYRtbQ7jUV3L5qX6im3+Ow4/vDJJWGOuLiXAP6G\nqM+Uq0tFHLzcH6iC9Hq7lJnUeIs+TgbzzW+xhnQ193JqLtgQ0TwKOMZOMRon\nyHYCcGVy4JvcGy78s40MUp8Q2dMKpxIPX3MGsQTUX3xhJ1+9xEjMmNJ42ZGn\n8vdbfFaR/eSjKLkzW3DSfv6yhwYG5Rg95397E46XqcfL2xofozvMDmc51Dli\nhkXKTHD4IfTQAvHKlLErvkTxL/PXszt9cx0L8Ctf505Y8ZzrpkTcUA5rQ2rR\nYVYtvBb6lbuE0Y1F2f+W1j/CqzVVPPoV3AIHdxSj3KkBF143mMhd07b92DLs\npTmaWg2OLqxqxRh/QS3bMT4HMkq1uKqJ/3gmYXAjf+v9soOvL70z4MrzmXCG\nnDnw\r\n=ddg+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDLVtfFB6/lImwbCQgUDJgz/PaXnwVQ3Je+Rk31nEcdyAiEA1bf7GJLwYImk4YCPCx85EVr8klXbPF2l08t8AU76BDw="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.23_1609301578655_0.21156067955745694"},"_hasShrinkwrap":false},"0.3.0-alpha.24":{"name":"fracture","version":"0.3.0-alpha.24","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.42","vivifyer":"2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.27","proof":"^9.0.2","rescue":"7.0.0-alpha.8"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"cddf6d5ebe2c79a177dad2fd8affe5ee69d06afa","_id":"fracture@0.3.0-alpha.24","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-P27AnsztWETnFtDoT4ePa0Yqvs5J7fwa2K1flql1AyMT0BZfthgGSYcQ3piUYE1NXdyBykVlBSxIwqnrMsNiXw==","shasum":"dac9e38c513eb323d1cf4550f459419ea0f4318b","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.24.tgz","fileCount":4,"unpackedSize":8330,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf7ay2CRA9TVsSAnZWagAAa8cP/1Z89m+9f3tZOfGEl862\nH1Q0Y4O4KggzOveqQDUd1Ns/CnnDmBncTDFbgYaOp2Cl+8R0TeSlf/3F0Lfs\nolCEyygPv8UZRvRRymOP651G3d7IKLcCv6mnUH82sW2Ea4mkiMxBMWUat2PX\nyK5T/0bkeYP8HOnm1/AxzaiekSPJtICJfkxu4A2a9ANCx4fzKmgRhdLLzSKi\nYzNZTB6qW7HRLURvxm0fI4AFHTQf6g+jGLabqwyKoR81rCnki0qHIDupRJiZ\n6ZWDx04Vf2Nb9s0hkwVa1eqdyPQlV90LWmocWYq4TSoPnfEHjYIzOdZd/egl\n33zse0xSgpJ0suWk7mOkeGJSND6E7i8VaZDNoFYTkC7e326EirEOK0cqmazx\n2LkaQ04woZ4g9O5j8yNllQKXmDoz470bSggSEQfkZtoiZr1gG5id2BH1AUBN\nWOyffzZV5vjd8L+1HXspqDBBVVbgvJCO5EeU4i3cqXk06WdZFu4QJCMQ9Wqz\nMIaLW1irJw9wUiT+PkEUOfwJysBppA019LBUeDb6sG1Hvpwe+DrgDHIoH2eh\nCEw/R2cxkdXZpS3c0+wc6jaGE4EzU1Kkd/jRfwNRKuiMQiX/MvgqfVsnod4V\nJc3inmAzl81jgIQV5K9X0rJBG/yg8BRGCOycw7y5weXZixnC1tbJwhcx9iOi\necH2\r\n=NdGs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHPMuUyE/zfwS+OILYBgbnLojWbn8HLDsV1OzLre0qhFAiB6nEmuS6GbU8gRqLXSWFXUt2YEBSR2i1Pn1LvJpzvecw=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.24_1609411766211_0.1626143102104909"},"_hasShrinkwrap":false},"0.3.0-alpha.25":{"name":"fracture","version":"0.3.0-alpha.25","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.43","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.30","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"5cb6d1f29c66c4aa9a01b3c816e554918abb7c63","_id":"fracture@0.3.0-alpha.25","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-j7hDYWyJWsQukwAs4IANueQtzpnLBcGYC1xoMgiuI6MD6do7dsF2f8b3qE81RDPywFoHxudb2yEJ6wyzbRQ2uQ==","shasum":"002d6e0dc796b90da9ab449be996e5fdf1f39adc","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.25.tgz","fileCount":4,"unpackedSize":8331,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf8AiaCRA9TVsSAnZWagAAvrsP/38hhvZFhBLUCVisHo+N\nEBHnlP7hXUE47zJPEb0v2H55ORbq3YfcgMjAxQYsuEl3ibXew6DXJWi+0BEu\nXlwM0aSIOyswzpWRzTCfyBjd3aD7lTRQvQakvYSL31X3PpvrXj2iGXm/UFox\nh7wf/sQLBGLZz0/dbJXSrEKx1EbDbHQ15Do0GnXEHtYQauCL6I9LLRqd83Oy\nX1vBcZSKsrqp3Hh+SBQJm+5dg3DP7e77/i/Um1go5/ICV/JeL4pzH6chRvS2\nmB2YzkGUYTxzx1C/InBTwVQfjF+nSWMnDMqtXPAOePAJfTJ8Z2gaWpK5Aiyk\nPB1cPBPsSCgNbTHnfcH0seNTx3wZ5kXrdsK60av8VCWvtHQ56KttRL4yoPCy\n8JKXbiaqYTFve1TCvepEc8kNwvzpcYnjkWa2f2jrg25GoCS8/0HbDfAAMPtY\nAn/Vq3TtcK9YQuqweDmgUFCqbRGZq0fkw+oSH9TwJHXVoAdIXgQyOkIMptm4\nUoAtSLtZBT92IX24qicvz47cTkM2Cbo8yd6Fe18E0jkHg3qaTYXuCu9fAbb9\nD6tiK43smvATQdtMjkUAC7CEHOb1AOlw/q37LfRG7nGWF4zLfgFwq4OqGQ4J\n70ltFxVcoME0IEewtO4M+izVacNlzwqvhIu+3mxtsP1QTxU5jPk77Cxw3VKM\nOUoG\r\n=1Fa0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUlYLMBw+gFH7B0htLmdmdYKX+fdrm6kCS83imbJuPKAIgPyfU3gbODuUy4X+bK5h4fUNLS8S1hLFAXksBmwCBSqM="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.25_1609566362018_0.2918750326673165"},"_hasShrinkwrap":false},"0.3.0-alpha.26":{"name":"fracture","version":"0.3.0-alpha.26","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","turnstile":"6.0.0-alpha.43","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.30","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"24044a2e03663c2ad0662058b4c757009581fbbe","_id":"fracture@0.3.0-alpha.26","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-mi/yJO0E6ZtoyOy6yzMt0yY547OHowZ8q5TMXHEdEX2e9EINKH7EIlDVJNPRaZDgJz2V836DPGa6WYk6qTzMRg==","shasum":"099a763596bd07454e3b62f58cbe3fed94c59638","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.26.tgz","fileCount":4,"unpackedSize":8289,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf8CrbCRA9TVsSAnZWagAA+TYP/17X6+ZaaauT60pALmTC\nY0J5RB1l9FTNYK70ZrLZ69A0UO1OwBBp9JGRjtUz8DET9wStD8Zjw7vLSlzn\ni3eaJTKMCNxF/cqO0KXOHb6IHzvuPWXRndRNddLlJtWyUz2IBlrO+j186gDh\neVXZBk1RJ3nSZ+YaMfxA00QIjmebql6iDaONLs2TE4bnB2GZ02+MKUT4hzjS\nl9CqaaUgpfm+Vwf70jvvzwGs4EleSG958qAEv6LFGTrxeTHLmoXmdGJYLWVC\npzjm5E9A5EXOncTjwvelAO9mGmA8A/zD97JKQ5H+FJCpKOn2aY9yHD/95Li1\nfJ49vxUJNQFKtgonlcLp49leeK7S0DoTTT/htlGgIQts864Vp7iBSK8XXiP4\n4lne4mq/QWWMdwe8ztCPV1dkducO41t9utLccHNoKVljGWyYhkmYy7TL0F8X\n9QV8Z0dPYU6GOgvUb/DyTXJU6AS1MQLRc4e+0kDfL8wDT7WnWkD4cma+kqkV\n/wtDR2e5a3yNbyXRTpy5/uJDxqi32RDXvnRK1kcY/cmCop8pLt10h2aRrgoF\nOXFBNpdq2kjHHV9P3hjJfTzb55do8OmnLo9TCn5ceD9Tgz6AmEoDNjt/gUjC\nQGzZ1B8JoeIgpSB9Ias6y9jTZEzuYEByP//QOkWsLCx85pL5fLsSnzxQJWWX\nRswZ\r\n=ZBTi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDu02rY9dMtSGdl9pBamUasspZYWOxKW9J/aOisacH3NgIhANBzEIlFqfFib2UrFg9Og8XsbTLRTfGnoBx4tnf7Wl3Q"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.26_1609575130806_0.4918120233921277"},"_hasShrinkwrap":false},"0.3.0-alpha.27":{"name":"fracture","version":"0.3.0-alpha.27","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.3","turnstile":"6.0.0-alpha.43","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.30","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"ae67a60942f0373fc74bb1c789bbdd615f005c0d","_id":"fracture@0.3.0-alpha.27","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-modyY5MroLliqX/+FKBH5PknGd6orFtiUb9b1vPVz0jHjTe5kyFUCUJ3d6EkvEAwWS9GR/wBSt+zBbTlccXk+w==","shasum":"bef3cf45259c896a764eb8316872217aa0c0aabe","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.27.tgz","fileCount":4,"unpackedSize":8169,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf8aBTCRA9TVsSAnZWagAAahEP/3BY/v5GcmtqEzREFeh1\nvV8hIU1nFKYdWO+8LmvnA7twnd/THlmxQ0cJni0c5dEpt8PijENt5eBiEimq\nlgWMhMxr1mwAj43vxcK+kNynAa5fdjpKNV16lLPFAfA0jYpGeMKhcTRdh2aI\n2JRrySHidpATNUUanpc12OVeKl5rm2Jw4e+2AHhncSz3k7wi3E7/b+fGvkvS\n235li+MKkjc/0hwW5VPEc+CjjP2BeL/Tf3qJUwYQWa5EkndzndcLRxorDSFn\n2b0a45bRMWt1zSgvXO5nDZMxFz0i1dEw8t9sBoBayYCDsumnJNafBUk7hwYO\ny3zOPKzptQsaw/vY5dtvRKH/b5S7tx0wWNVI0SFyR/it9ShQt5FIElDGwkXv\nUM0I3gII+hpWqWNQ7GhGN/jvZvJAx7LAB8K47Izc/LWThVxIUjhOSOBBKIMB\nbsM8BE4MZOGL5+iixk1uAE9NUTIwEm7UdbRRTjjo2YtCZlpcgfa/iqOW1So8\n6cQP8mr9cO1I18zHSGy8xB00pLWcKQ4qCnn7kvOosYnu0SjzVxb3qRCLmBm2\ndPYvRSFYoxv1Qho4lEEmENrz+Ct15q37FB9C+6IAdsawvxqK4i9psNTX+jfL\nFvUkMTg3yg5ifLabCpcLy39B1hvrNitvGans4SzVF3nWwMda63tZnrlBqKCt\nGobP\r\n=Y//5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC4Jhmd0BFOajmzh8lXBOtUhLTg18BDYF2V6Zr35tppUAiAv+KYBPAyUmgiG+9p+nKvldHTA2U9F9OgEZuzU9N/i3A=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.27_1609670738617_0.6741886627493974"},"_hasShrinkwrap":false},"0.3.0-alpha.28":{"name":"fracture","version":"0.3.0-alpha.28","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.4","turnstile":"6.0.0-alpha.45","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.32","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"4bbc9c528bfb86c670fc7808603fff67a7d23186","_id":"fracture@0.3.0-alpha.28","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-FmgHJ6jOK5SDF10/PJW9zX+Jakx1pXLx8SAKYTI+7UsiFCqojG2nl8h3XM/qoZ3/6BS70Cjqg+ZomVK8gWnsQg==","shasum":"403e24465ff228a804b39a7a4254ea1c82a15469","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.28.tgz","fileCount":4,"unpackedSize":8387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf9DDcCRA9TVsSAnZWagAAJuwP/27RKBLL6Sfq7v3hnx3U\n0/3eEgtyuQpjcnNf+e66OzHqb0N53re3DnwiBcBc71ezEwomgzDbHOoYwb51\nt/x29/S/ECqsq6hUEs6C4MMBKNUxr3zADkY3e2R8krA5nYchLZE3BJvRqy80\nvHOGXaKP1W2/7hFb87aGTU4QDsVXoUasJrUbmVGOG52cnmdm+iX9OLml2SHk\nSirwIemWNz7a/qd1Lo8iowkNqudqvDacBeTpI0KwtGKAOZ8JC0oSME1tL8fQ\nH4nZ+A8uyC2MMzwO5VY0kXBokMkVHi703UafV15Uw5xcYziZD7waYKyj/a8B\n3k7IfDESUwxYngkpfOmOJeUoGssJz9Jj8XxO/cy749z5SLUT9QaQFRzB+L/a\nz5ULyn9tffQbk47ATKTJ3md4IuYb5dW7NcNSZJSqu+rZJTYejJgun2XlAkCf\nvfJO2wj+PCiOuJbxBmPGtMqVTZJm4qJnZJDz1g9py0gJkFx+cjfqLlsx/kwP\nWp94H5Lm7jpKeADSPb63XQuQMyNbeb2+c1RFKFIg1PbuVAt5qqxN8V84furW\n/VRru67Ov5Ajf5AJ/XbXcQtE+uiarGDG7FmcFhZPR7z2++tX4f+42I8v8b6X\nZtXT4EYRy/Xqd7hGZN5Pm2UL8r5lonGUKFjfoJCxDh0NoNEmb/PAw7vJ25nV\nTW0H\r\n=VfVb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHsp3Gkw6r8NMQvHI4OLocPkI9Op+PjHC0MYuEnBILMiAiB+CoMlPCY4dWSoNCNxyk1V5JKDfKRWquJhN42v9JtnJA=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.28_1609838811774_0.6865206500756165"},"_hasShrinkwrap":false},"0.3.0-alpha.29":{"name":"fracture","version":"0.3.0-alpha.29","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.4","turnstile":"6.0.0-alpha.46","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.33","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```\nnpm install fracture\n```\n","readmeFilename":"README.md","gitHead":"aa814478281766a5c87100b1bfdc9e782a541838","_id":"fracture@0.3.0-alpha.29","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-64nJWgtLm0qslwwf6qXbLH02HoU+CiuvdVR+TN6CBbBMdRR+ZkZSl5gCVQXSU8Yu9eWV5TkKcpDkCLyqrAlSLQ==","shasum":"06220adf7024c68a0fb8331d65d433a03d87a325","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.29.tgz","fileCount":4,"unpackedSize":8402,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf+KIhCRA9TVsSAnZWagAAq9MP/ixJ7VZ4c0c7NMKTLejS\nKqn4JD368h6bvUg06bzAR/YdUFlQ7Hjt2I3Iu8iNCL45Ju2RQ8EzaFmO/crm\nL0sTKi7tvabhCbtDq4ssbabwIum6dFHUIDxrd6/XK+hVqpW5FIPsxJiut3tu\nTp0YZOctJQhDsudfWJSRJjwT9hYQ4rj0e8zPp8TSqLADwTFuwUZRLbKpyOY7\njGYZTSnzJVxv0k2FQVQEfgwzLa5MZKc/poNh3DztEufcvDChqSPjmi8iOpjN\nzvqBg3euP9muyFGyD5aJttZcbgI0ovdkZK0F2gveGW6PtQs++40LITzHPhwR\nH34UIRG1xkZNK0STwoe8e1FXucxYL2lxk14as2tAGexSORA8PLxwdafQjWbk\nCGNYY7Wub/N0FPLVAxtFlLYso00B14AvUL/vpSfZhiBRPYPnj5yMOM7M3D1D\nEm1VcvehRnCL+1BIWtK6OlpHBg32ZaOqULUcQUABcdeBFMHZUZ8T6m8aQiZL\nlQqWvWqdghqjP/XziE/tzpjzTQm8dMXN8+BlYZVBjvFtEVs+yvzIAQYulNqn\neefPbO5LqtM97OhppAYagT32L1EakDk6x2NnH3LlDnekAGwrG5DcyfwHvSuL\n9VNH8wYqylqeWYJPiRk02TBCU/OEJiMEo3VFWT5+Axm2o7txrS4L0FBzEzia\n4ItY\r\n=XPIe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAvDRJ1ot6f8dP49G33NLP9PCd3NKzb6m29Ga7XMSfTnAiEAghqzxWkkThthkpxu7WP6iPT3vycYF/JI0aif93Z9Poc="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.29_1610129952813_0.03890114836396763"},"_hasShrinkwrap":false},"0.3.0-alpha.30":{"name":"fracture","version":"0.3.0-alpha.30","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.48","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.36","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await fracture.destructible.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"b7dc43b7dd0cdd8521dca797c07a980fb9803bec","_id":"fracture@0.3.0-alpha.30","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-hZanPPAPMRgClbIrIUxA174S15YS8sZgtaU/8ORvaMGmK+zOlgQRivKRFGl7W3zHqAKQl544zU6P6QR5oiVEpQ==","shasum":"11e6e1a75a524ebf00d6d4cdbc5a4bf548c9e4bc","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.30.tgz","fileCount":6,"unpackedSize":44147,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf+1RKCRA9TVsSAnZWagAAvLUQAJkJczTbfl19r0uQtg5V\nrOQQeq2AXR16oM1td3WdUT+Qeo3FqvmBW159wfsLERYTtRAt4r7ShT/Wwi4x\nbwzslc0MGauKtmgmPMMmRkSRZj4AUM2worC/LGk5mjKwuRuqs0FJHrcLyUpj\nT7bcBzSwlEsEaN26pmMHTlOnuHGOoq16QMRcc0U4zBMmX6tvoh9QyvXDNLIX\nAsK9tY5yIRpDsNFMFfU8FW/UhVmyGUqcpmqLTP9EuKsWHMrkEPfOvyNu6Ufg\nxBkd2masW8t7zKc1/xptoYSwisFN8Rg53/bCcQYc9avYgJCMFZAdvVlvc7go\n9WW9kaYQNt6ICFZ4kFUKaGV4TCLIZUlkjkuMp0XCg91ShH2m4MaVj96KLKPJ\ngLsc2NwVVCdTNjkJ+teJvuLp+tDgOHYlNXCLsQh5NNkO8oxjFk0bgvmbH+kC\ntESXi6tiCPu4yEL3fUHyhXzPAoJ3TEx/5U2opuoLCDbxnRi+ShGR7zdACnfO\nSsKDdvv1/S09CLxIYUhdlMS1b1A561B0y8iT+wdRfjVpSvO4VhPpRDo1JR1I\nmBgwrVjpcAnEdzMnuk9j9wiiJ19dJpmuOt5vUeLvRfUZ1jbBrTy+mmavFriU\nDc388EYiPscDBGqYiVfNQYC6PLp/k5/ajPnR7uitFJzOyS87MB8YpkiZZc/F\nfGdB\r\n=XBxG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGpZ+DZRMe43uoeJvqxSE/uDfzF9V1n+bHXuUVsCi2NEAiB9ZQj7Ay7tbzXsxys37o+YxJlUlIKxA23/shKS8Vwp0g=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.30_1610306634010_0.043575089064149086"},"_hasShrinkwrap":false},"0.3.0-alpha.31":{"name":"fracture","version":"0.3.0-alpha.31","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.50","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.38","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"b2d04d7336517107d265e85b4f5d11d30cfd6f65","_id":"fracture@0.3.0-alpha.31","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-zd91Dv8tzNXd3zREX6A7LLS5lvTgTYHEt7u0vxsZZkXTrp8p10m/6aCCqewC6DCvM2mKTSOrE7iNnIyPYSMvzA==","shasum":"a2497dc264c4563881b3c4187561a6175465f043","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.31.tgz","fileCount":6,"unpackedSize":44381,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf/EbFCRA9TVsSAnZWagAAZ6AQAIOjLtExZk7/mZCYpbnG\n2KyP1N9Hd0sHM+JsUb0Xmv8vM4cac+9xnE0Gu6x9l4ty9YGyKQGRoY0qNP33\nRPrI1jJL20H/4raoKjsW+ElO068q52wsJ2E1UiqDBiCdfQ4elowzxscVLfHt\nmzlJf33GCw1ujRICNZJ+xnT8nGOcjzHT3lwda0kAGG0UoT0WmVMacwGEPVjU\n92e3NuOqH4cnv3vFFHA6hGd0Z1lbwmkDcZ7R5hxVGbjK0excLQNUkUdS+CoX\njHJhwWwSqJzBn3eURSg1+ka42JuG96CmpY7XFBmAj4Tg5ndwp4hlpD9sIYcv\nduv+qA7lry1rrX4TshfQJnSObKcV3LiNT+Z4zR8t6o5NNczL6ejAmfw1qOk+\n73ymT8hm4OuJI1uupAS7qTK8RGtyqcS5IU8wV0NJJ0tFyeOUdQzHz1ajt/S0\noa8oWeBu5c5lDVf9qMjLIp+VziumoKSe6sSFuEdhIYxmZ5dpzfDo0YSZjaWQ\nSFnDP6WZmsWdQYsmvFIWthIVh70AR+dy65IFW3L74abjJLs8O3LGsUoyIqnX\naedQZEkjcdyoDTglRw0xshyT3k7YF4vsVyvmNaB3vt3c7sXITzRBg+XuUmpt\nSc3T2vlFksKZNpHzjkl0M5Jsr8DNw4o8WPZOFrvVaf6Br9KK7UPcFaKVJEz7\nUkPr\r\n=lgUW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJLizqckzF+1XJt0NO+gnFe7XrplgJ1T2gPxLre88H3gIhAJS2JtS+fL3raEXKZ7cFXfD8uLz7ICa/6oskFdBhVM1e"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.31_1610368709360_0.702796939262391"},"_hasShrinkwrap":false},"0.3.0-alpha.32":{"name":"fracture","version":"0.3.0-alpha.32","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.52","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.38","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"bbe6d6fea3477751d0677b5e85f966dfcc320544","_id":"fracture@0.3.0-alpha.32","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-1RWAcJ7gQYCGXYwi5AzJY47RAqnVf0SELoQxOSRqqDrkMjJtdQeeHWiuMebyIOIoMs76mAlEyhFqVZBcBSwfKw==","shasum":"a725953c02e95f527db19fb671917394e2fccf72","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.32.tgz","fileCount":6,"unpackedSize":44381,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf/WO3CRA9TVsSAnZWagAAKX0P/1u2mnExPLwIOrxLqLp6\nw5gpjI9c0bH3hK6v8s0i05LtUfUZmsLjLafyh0L40vtir8WZP5D7ytOzY3Ja\nzrWrrRybx+8d/M75XzC96reJmrOd6KBByOYzo0EMIcfEB4gPaiBwrJiZv5cR\nrSjnrUbsBPuFxG1X1xRIR/YL3Hz90rqV/nGZCOW2AFd8Kq9b/fXjimiPDuZD\ny5Lfv+vQ2TOycdT4RXo7q+tkk/coJNg/FpjkL+btYvl3Q/D53fK1QgP+bm8w\nmde5Phm/DYo0RvckWPMPnyroTkRrVCswU2aOT3LXJxEdOO4XBWUMB2ruj8zR\nCrFOPH6L7g12TVu+zWYxvMrV7aArngo8co4cOcgngszEBa+8GBfFTn53Fq5F\nER4sqVOvpCr16UDxcFflp0KJ2AG3em7gzye9QoMqRH14CAjZN9IDQ08HTpYQ\n3T55TZua7ihfkqlOo2Z7TgckZvOnoSVsR95MU3D1WbXBssQvgnJqcwnTdiVt\nkk+ZWjep7Q1i/yWhm3QSqrRZ164AA3Wf+lzXBxRQ4uMhkW20qymTT57FJggP\nBDTNRwjTjMhunxYy7te+mISlcbYWvP16SRW5l0zh5RbAzVwl4Paslrtkt7OL\n8Tv+qKzf1NV++ztZIQ1np4jtRK9JIKx/UbT1B/wZN5ELKwYMtOlwE5SRdwCG\nWqTT\r\n=GwCK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVO/Fv3DwAX45YNjrgTNkGChsLKA7oTmi/CF6OlnoswAIgVyJrfdAdIAxFWFJHLoB1GNiGVlPX17FYOgnkYvetCL0="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.32_1610441655063_0.00983201731140948"},"_hasShrinkwrap":false},"0.3.0-alpha.33":{"name":"fracture","version":"0.3.0-alpha.33","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.53","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.39","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"a4885c4b2c47a023a96b3b0f1967415c1a7f7e97","_id":"fracture@0.3.0-alpha.33","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-Ma8F41bXCkif/fiuypLcsWGY6lOdumGR8lSA03NzZ1eorYcEjl0k49j8VH1SOOq21kvkdMYoSeCAKAxY7822zA==","shasum":"bc1ce6e8ae32fc252c7e30c5375552d9714b7762","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.33.tgz","fileCount":6,"unpackedSize":44381,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf/8rCCRA9TVsSAnZWagAA5g0P/A4ooqpJ38qpC/OhaKAM\nr8kTU+FljImht1g4fZRAu6tW2bjg6xvEfdC6edLr3P/u7RjS/blghthIxnT+\nr7VM786+qGtjhgN0xAiCVpX5caZP54aJaT25UvXCXs/CiO1CufDDOOF8vVNN\nGcHQP065UJouY6msRTLZ3fy3LKrDK0ZABi20pz52GD4v45gXFv7wplpm6H5G\nfgkFTjEF/peHt5kGR0FXlcwYqS5aLLSPSEDShuRyn8sZ2M90X9FJExWIdwWR\nQznB5jgF3IfHTPMa6bk+LoTnExttfi0OakKXWej94SyxUkZ/o/y2OMMejDo5\nLrLHTFL2GCu0HNpDQnKcfNaV1kjH85L9nLOKoVMw9yDCwRtvTLI11FHmSZyv\nqW/LSyPtelGHZ4bR1XusIqAuiuZULdSle1ZGy/0bfDtTdxMKhcoUM0V8UZet\n6pdSkClwERy3bi5nJCuK0Ns8wFSp1rcK8vxnifwtqiBwBJ38JzdoCAgqzFDf\naFKunRtMkpxltWuGMk9lSW29ioRoy2chmw73Bc4bKPriNJR/t645a0is+PaO\nHSWV9ldUjUOYdCxAICtuj0QogWGTTiOEgcxK038+ZRaGDafvWmCZPEkVgelu\nNBGBixK5xd7p0tk/3fPVueutlC4wpZEhCfB38mChL8fgzK33qAcDc+GZgQve\n4jzx\r\n=zvns\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLvo0QiOZFzyBREPqwPwfipRma2DFJwyGxUNORjJWPtQIhAL+Gm3cai/B0gAhaH3z3y3zV7fKAhdQ3/9wVyL04Rw4S"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.33_1610599106304_0.5263392406150533"},"_hasShrinkwrap":false},"0.3.0-alpha.34":{"name":"fracture","version":"0.3.0-alpha.34","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.53","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.39","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"0e6e1f0004cf4780f55771f10901f434764796a6","_id":"fracture@0.3.0-alpha.34","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-a/XGkhZSvbDqjCm0dTuEyFOns3RlsbMFWfbvl/F/I+8JF5OIbrx/ZqYW01LsteSTXG+2/PGAhyn/WY3ME6n1UQ==","shasum":"9366d66fbdb1cd65d117584f77565110d90f4888","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.34.tgz","fileCount":6,"unpackedSize":45502,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgAweVCRA9TVsSAnZWagAALSYP/1yaS7SzrIyvXEwURPWU\n+3whQz2XXM9WiROpVbM0vKCOsN/d7XZO3etXbwbhoJUxwXegZg3RaCnK97xd\nuJXEZAiR6zEBwQn9KyqfwpBx//00E6eRzl17EFGejJgbwyFhh0brBDjENB3W\nsIKw0kgWrBuPOdD231yX7T15xh1g/Jz6fVtPqI/Msr85EHRyNsWxhvQ5bzMm\nSED58uCi9PCA47VuID4qi6djiYaRRGrWpI2LdfIFZ4tLG+zksJpAh2Chk9F/\ny8z6e/UMegqHIjLyoUI0KEjpYIMCkEgu3Z35+qmTEl0DwQ6bvBYqAvg6W7n3\nIw6trUEnW8SLBV7RxvvATRrRfyR8EbVnp8fvANuAxEFk6hLqOu1eP/ZSzR8i\nPT0apH4/cP+/SRomLZk45kT1c9gifpsTr9Us2hKmz1ll96cyZjwnd/gy9OtV\n3ZBzP+5rYTO3sl+TZQfdfN7VS3WqK4EVuluwIzmLj75vo9mW3Vkd6GQ5DyzI\nnpbBto3hE0LD2SWyOfDzVxXhicURS5/bKW5BhDVZsqG11J6qPN/mHi+GQ7gk\nbnfJu56XTBW12KmCYh+ez+r8EX89COc9r1kIApKT0JBAJJvl18YLsb+0Ggne\n+kcz20fiABCum2iv3YJ8IR6KW4OcF0c6UKBm3EscSgNCKV1ge8HloXXp//3L\nst2p\r\n=nTOA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxHQECwqKQF9YIhFcI3WWDSpHl5YPBH30qDZFiHt7dgQIgU68d4n7glhmynqpIqxZq1zVaYNG/YbBpRfAPSUPQE9w="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.34_1610811284863_0.2449019712751761"},"_hasShrinkwrap":false},"0.3.0-alpha.35":{"name":"fracture","version":"0.3.0-alpha.35","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.53","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.39","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"d025887e7f86d6b82e7691a07a9ab800fb30f5f5","_id":"fracture@0.3.0-alpha.35","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-dwlJdI/gZsuNJ6XcduSvDdkdqdF6xFa8t8FB02r0i+ZoSjg33Z2GMlQHunnTEv6rYIW5G4KvvieMAfi6z5DTBw==","shasum":"c292a8538a43b1315bf2c0567705a327a3d6963f","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.35.tgz","fileCount":6,"unpackedSize":45510,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgA7z7CRA9TVsSAnZWagAAAGIP/1Fcge0gRb49eXNd+uqY\n2P+w/FI1CyuQOZ5ffYHS23HeyTCAu0FwRA++Qwa28nTrXq4Qy/f+Q9OcUuFk\nbHj8wD29xR58g1O6MNK25QRP06Qii3wrkSzGCZVsIZSwRLr1ztB3mFLs+eBy\nwVPF9fJ9oynC9LO2/fbxOCVXC5rNliJP5DTOXA52A6cSuh62xkSzj3U8CRLg\n2LJ8tzP3SJ9hZ8WC61FRYCtce0Ubmo+4ucbapRUsadxPzd4u9BUxmMsdhokT\nA3AevtntGm+rnM1MBCSYrQTQP1v36Ckcc4m1AtSTe6QJtYrCS9HrqmZw5mpP\n5brAXUIhncqbcoampZ8etw3zHGzhTE7VKo20zeKMOy0VCTRjZ0jG6SSGSBnF\nfF/L1DKcbkqYPhvEKQZxzs5Ip3CvFWdZTzWWVOCEcNGlOruoMMP2djrLm7K1\nyi5ekefJxss/1P4/5b7zdjUXMVP5cTJAuQg4QJxpMXXSmi1lM40V8f2PFkF7\nN4PcVTQYO/LHfz5zW03u4fiarj6u8BfzScixib34VPcBy+GOecTCc9Qd4DVd\nOA7NmClOiUaWMpD0waQfEkI58KrIq5Zgz5jf5vEm0uqzETyLz+zffbi9xBJy\n/I83SDAQpl4SzwqE+VDCxIMqXw0RPv2giEddTkYfsF3kbQBxUjt6Qkl/rhv6\nPPEl\r\n=7cDH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAFAczxdrOVXCmazgmMDCR5fYjBvA9fBSJMu69FWvgzIAiA7eLn/UKLNT7ToA5s6dzhDAy+HH2G/JTHCd1Ujt/k1Pw=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.35_1610857722643_0.6923312092361944"},"_hasShrinkwrap":false},"0.3.0-alpha.36":{"name":"fracture","version":"0.3.0-alpha.36","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.53","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.39","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"5f446a6139c1e0ebb02c845a0f94a681d548ffba","_id":"fracture@0.3.0-alpha.36","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-G3HXs4tQlKIFGaLosFKuYRgaX3bp+DeS7hxHKOhc4zxl9C6rSIc2VAvZ1whcyG7OjmKdgctezXuUZoVhf0cpZA==","shasum":"6dcc11159ac6a88c5b9ba6137eaae35ea440bb5d","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.36.tgz","fileCount":6,"unpackedSize":45510,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgA76yCRA9TVsSAnZWagAAoKAP/Rh5tvS8Y2zvRqD8y2rx\n3HUPUfdNDq3buSPOEqLar95VwjJAly0En6QZsVVIEbvb1ZdCib/6SNpabzra\nAF+O6qVBKPodMf/RjSIj1o3JrJwMUfjx4VsanHhh4vov+dAuYhRFLnjOBDgm\nomzKiiZmtJ2FFdHx/BSueysjiNl9pc5wYTtV7+VkqDlFKew+SLCSz547/3fj\nUSj02vjuxpONWZ+RpFUlYLCReolMKaH8gmTEXyha/QFW31XUHI3V4L7fvnAC\nGQUxAFEoAXf8MEu7GbpaUref640q+UbHdJEmbMBjIsCYbIpSu/BFq1GddKfg\nLpBLfjNVun5906hHk2IMDEZCvJuzVlm2vL0ryaMi1a8yv7ZwrZd9c7UMXP0O\nPw3C/mgmRDCObeh+Sz2+FTXh2cNvkImA43iZLpD62IeJFxIMZ5aUtQAUgrlJ\nSwF3vhiayrEl5knfhFsHCUAtTI+WPYEYILGoCuGhbzz8v2uqodlgIDVwUgSE\neJn8xWIvhkdfS/LLrUK6Xljelsf6sFS/5R6VH44rEwljQqRtCav/D7T05RZn\nmn6O0+sgQxfYQVnhHGVCmqyhJbxvD83r3qGtqh69pXZh4bEMoLXV9llyfYQZ\nU5hvQ0Zj/JEPAtY87D2lcafptZWYNzNyVwzzygNS9gRvIJyCplChBpca7P1J\nepi+\r\n=FhNb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBX7wBZH8nEZRw2Ex/ig1pfb3LD9oBEOaO7PvpCqvXpbAiBKCtehvCeqcvjeLq5itayAvq8s7ntLpraAK7CyggKDzw=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.36_1610858162426_0.7521050727797873"},"_hasShrinkwrap":false},"0.3.0-alpha.37":{"name":"fracture","version":"0.3.0-alpha.37","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.53","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.39","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"f2f6e7b24f29b257d84450a9bb6a8d3cf127890b","_id":"fracture@0.3.0-alpha.37","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-lk+UTuUZZ5o7r+x5yMvu+ATQPtOA69VlHEzR2mV+0zpMCX4FmaGu6fmpESl7eR7GSOHHCNLo9Lr2y46tPeksFQ==","shasum":"5475662368af9a441431bbce8b36269e37250595","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.37.tgz","fileCount":6,"unpackedSize":45799,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgBDlSCRA9TVsSAnZWagAAWeAP/ixURAtQRmy+73x1ZhQY\nzDE0Tx5UPfL6VBMIdiqfaAa36Pl6o1ahPCx4nfFy0RNPLwim/zvZojRjKEg1\nfR67ZLxRhctwN4AarOH4VYN/u5KhzHYFYHdqLo4FcGDPdDdFLZeI23+RLtas\nG0hKRfrp9P/QYYbxugvsfCZkEsAoS6JKH/45JzyYG/HLHQ5lQRQje06crjkq\nX/oXQRvQ0TFRV0SMG87MNhz3bNpHk7GlTm22/8DBjQJrovZdAwSsiTI06tfA\nYSgB1KJ07ChpQEIwbwUkv/A9fUnfnMkE09wsPB4joWBrKGRFJQEi8zkVlnyX\neLur+sMN3OawB80woZW3mhuA0RB5C1KfnocWadL5cI8pfOttZZ8DupsH4hCH\nA9E7w5bVF/CvvdMXuVZcB042suVVJO4zTf2kUOMjIms5acah8pZ2IwRdkJCU\ndsK2Jm/GWKnFP8zZa/Tn1mwFChj6OcWWjhidAR6fbEEbEUk74UgA2p4h8ez2\nBZDkUntGxP9wEMRGzUK1hj8LgO09XkdsVxQyeN8sXd5jsCwVHyB1HswhzK3g\nw+3Ovgw8S8FSfOSbntkUlu8GMqcQHOQ8vfdZXNTz5Pa92m0DnTN8jIWBv71s\nP1qnkhg0dMgcTGSUSQq3PRJlpirMDvih6kN+Y29J9U8ry8VdnRWDOVXSYB4E\n+7N8\r\n=lfGE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHMgbE8Z9do3U+TVz6UHTQvckBSgbS7IZn7DBZWnUgXRAiBBSlxcmDf6QXXIM/yggjudQ5REgWWaIUEqtlI87WDZxA=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.37_1610889553633_0.4781392504242501"},"_hasShrinkwrap":false},"0.3.0-alpha.38":{"name":"fracture","version":"0.3.0-alpha.38","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.53","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.39","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"f968e5affaac2c846d05ba0750f99e64887a8c46","_id":"fracture@0.3.0-alpha.38","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-WgZ/TW+1ZigeWEoaYifpg2d7zAbP474PKzHkgnFdgqa5sIDkV3LNMqYYqMa2OVgQH4ituKEEbGZUCaLTsKerNg==","shasum":"7797eccf98afea2dcfc6e4a910ec4cba03c05a55","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.38.tgz","fileCount":6,"unpackedSize":46654,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgBEzUCRA9TVsSAnZWagAAhjgP+wRTaoh64J2MeTODtRRx\nV9Ps3noRuZy2MPZNUhop7j7eFZhsljTGfIcSIvcsCE6yi7KujoGtXGlssbTO\nKPCw+9A8Itf7o2Q8cugbGBpheVj2eiM3YoxWoEGWI4cPghLHFktzjyu6PGC+\n0O5a/IcmY7yVHVNsuAuJ5OSBzil50d8PtsMd8ytGM6PDcI1zbVLAt+e4tfQa\nCY/gnevT+Fx18XUhOlgpAEaxAAIqrjxSSb3CwHm4SbHOg0Df66KDZu1q52pg\nzmJwWyoDzeHjVnETx9RF9nP7r+M8aTBTjqHeKcwBnpLZ+yU3OByb/UoM8at7\n/jVQUBapp+X/Nn/gN0Gyx9fOT1KbrxD4Df5roku9GkdWkRdrnb3RQ4eumeXd\nUf2mkVa1EE1a/+MeXdDztycXEw0EF5I/X/AKGF6PmryMYF9ORobu27nJrcAK\nMoPzmSRdzBW5FK0GI40ca96hwRWHbjddcss4M8zKx2cbX7wOHY15zkNb3hRr\n5sWHod+TQNOiyayJPybxBZqbIX7S09EGSrE2HHbFZnfKmpUxewMVgf9QKsTn\nO4HXdpjVW530bd7/SHaI3rJj86K5ybyggZNFqSeHTKN2gw2W//4RulDS/KxX\neI8y5Zos6LPQtNbZXXSjT2C2iBq/ChHakl9UDWl7NrlpNwotChnLb1+FWVxJ\n8k6/\r\n=NF4/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBZTqgoCr0lSju4hIYngWWnEoDsdM1flxpNW+FmLjgvAIhALDf5Za/CCrcIFF2jfb8iG3ovCvHWxKm9aNfxbbuYD7O"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.38_1610894548509_0.42466548708898544"},"_hasShrinkwrap":false},"0.3.0-alpha.39":{"name":"fracture","version":"0.3.0-alpha.39","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.55","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.45","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"0faf8c0e40486ee0876c5584d89e2aa1ba128448","_id":"fracture@0.3.0-alpha.39","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-MeGf0h45lSvZ0xkwOtEh7GsmGUfXyJ7ElxRDw9sdfRDmHQe44c4SC+9HaFndhWyXQadjiNMpyaULH7qh2PUILg==","shasum":"cea033a29e41f4bd716cbd7bc02553b653e03da6","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.39.tgz","fileCount":6,"unpackedSize":48287,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgBu6SCRA9TVsSAnZWagAAkaUP/A0gzD4VHfquF5Oz65Ss\nOnWnYjafCSOkfLHGPPbWysL4X9IusiuLtlCHRpWHugB2njAxS/V90Mpzit65\n+D3YVXfumLxWqIeUoDW89Iyy+9gHDTA/vJa9Hyt/AKSbnmU1NKqnTHd18fn0\nbPQDjJvEdxjfSntC2kyRVpAsJy6RNI7Gs4ww99r+SwASu5GPms2z7jemyAG3\nDAEfaMf7y9BCTofzmr50rNQeNYcrSKXktrSR4/J/DbGY0X680ISbip/LvEIo\ntJ7/dPlBqI3GZ+HbwfWz4JeuTP7wg+2xmrRGx0fSO9le7NN6DSetcvbUrW8J\nz/ECTbNOymkcH2c/KvjEgYypRKPsq/JSyHtnlFg8FkhhYLw1rOitqM/nRSdx\nivAEuKKyqO5ACXydTarrrk7z+ZR/suOU/zgdZdCLgPhvfOz5pZiECQKvTTus\ndWWGpI/I785tckfpEediJzUuIhVWv1INi21o12oYW8sQlJxNooldV14twV6g\nyVbDRr9Z/Rvu+q/gbE+4uEd5FZMd7UWwZd0jr7yvCr6BlJEJCelXjBIoTzPx\nIaxnUZU5Yt5jpFGDMGs/f1cwzmKoC1ZWnK59mNVI++3raG0jcQ0aqWAOnn9N\nbxIV5dsopymDO0BpsUxY/4H+IpZibDH3wUuzbUzEAj/c2JkiqQT5BJUjpQdZ\nNhzL\r\n=xVE4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCqgvmTS2Gua3LOVEMvA+EIBTkUrHkwnIapiI4zVYZlPAIgMnL6uTNe2z7WE1n+a1AXQUdtZbMCRVbSEla5UDGZ16A="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.39_1611067026007_0.4664919966862928"},"_hasShrinkwrap":false},"0.3.0-alpha.40":{"name":"fracture","version":"0.3.0-alpha.40","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.55","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.45","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"ef7bfb09e88f2b21d7f17ec430c8fe7cd90fe077","_id":"fracture@0.3.0-alpha.40","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-jIujaI0kb3wdUTVuUBdr/L1piNWeMBcwVdIjvyGfnWaO1pV9UF+2QSelL/otC7uGm9arJY+LN5s6aAasdJKaYA==","shasum":"b683589b3139f971dcb95b7fa1bae67ae1b206f9","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.40.tgz","fileCount":6,"unpackedSize":48448,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgBwSxCRA9TVsSAnZWagAApb8P/3UbgQ+0a+fclzbA6Ibn\nTw/LfbNdFg4PESWjTh9R/HZ6oe1A4NF9NKkW8YSgoyENW3SCCnWGGQf40ADI\nqHeujkSf/dEi//nB9J1OEW/Ma6nFFaK6JJQ3UP+oM6uJTqyNGJM5CK/QtG17\nQ+LezEr8jVY0JBnL7ZqNsPjDGbaDGI2ICN+WkFiGXgPZmLHytkrpIeoXU9iK\n+0YXUi6cbCQyBNVXZewj0CzXOybARGLJSMoIaCxN3kxvYiPuE+SlPjnjszAs\nhvOWEQfB+gXruVBBfOHsCAzscFTEUpzGYKXhMrVEjvx7IX+wej+Q/MzVGtWg\nNzP3SwZPfm2V++Y94n0crqSU4NoXpH9TDwGliCdSr9OSshZq/HqCKk1c0vv2\nIfgOj0AFrGephdrVWo4SNX1Ogc6vANVCXJBaPjVBMR8dofBWKfn5iiXv4Ww9\nve9NSSJg5+Hr9eyUPcFfwS6l52W8lRLjSjZe7IGrJLgS9rtcjcgK+hYA5WP+\nOPDN3o1Q+0Rz/WsrFY2F1iPO7WufO29v3oj+ZqQryIWIRY8XYOu6Z3eWbpXy\nYEC/2RKf0mJ5RjdhiUtauSxA7FazFqlba0Q+8pgZUPI6LiVMakWBV5aLTUDA\nc2+918f2GYrBy4HrS8zPMP1ZDqY2xaxQgPpSBmUzw8UL+m0mlOzApFjDhzB0\nkrAS\r\n=0kdw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCRgLKJt7FP9nHNnryQUYpyU7MBqvuZpzPgwiglfO86/wIhALf4r9QdlSTH4g3cNRM7F3+GkbKhSyLO/soniGXq6t/n"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.40_1611072688567_0.9135361168454976"},"_hasShrinkwrap":false},"0.3.0-alpha.41":{"name":"fracture","version":"0.3.0-alpha.41","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.55","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.45","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"0d5ff0b4b5f74ef9c6a2005779edaaeb9af3ecc4","_id":"fracture@0.3.0-alpha.41","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-AGuwg+rZmhmT7ZPnwWx2MjsGH9H0obUoYxG9IOkXY4JaSi2i2T7qmPy5eRw04t4hUSSrTXyWetwqtgcY4h+8uA==","shasum":"93521bb4cf0ec538750336d02e947d2dc03cb15b","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.41.tgz","fileCount":6,"unpackedSize":48117,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgB8JvCRA9TVsSAnZWagAAus4QAINS0h5UNWWebU+gH3zh\ngR1gzz4vNKRYFf5CLtFC4JDfSEgo2IdEGWiXxyeJn+KGLlNJU5NzASN9PXwx\nP8hZinJbQ1cBdYXWl3dBOIyzIadYlWU0030XTrID6oIihtuQTMGzj8XPMvr0\nGfpQf3tbg7VOOqLAP7m7Sb3UsJ5W+/htI25/IbVMR92zfwae3VsHhpiY206p\nL5bTz8eOfnFEwf+XKdaBtHvMsOswrqCUh6Gjc7Oum8febt+o5D0gqsYgo1aw\nhVk+1w1JEWeSEe82esDj9L6R7zZ8SVhB9+AwtOqiP3sj5AbhUwPqviC+Dpwp\nzb4vufYbT8UawxNa6S7UpCL5kekmIZ3aJ5YOiDywZrSAt/wERxuS09i08MBk\npf2030ofInd+DqeIYsdDm6mC9lFaBlP9mGF1TyntHCG/qKlMhX5kFdiPWsc9\njoftHETmcRvxLDEm+ui5zChPwttFhMeH/hiw4gQoj1El3qGu259uY9Zw66gh\n6INAr1GXKueqe0bJCDzl21KolqdlDLLqfu5BKTT+CkjS7O2l8Sl9KmxSmkOK\nFQTENTsH4jnBDIK55ziFdLuIYRbXtAV5xiO+TSg6aHIHma2qURF2C0cHag+Z\nWW3+I75QKzxz6VXjWaojXb03oiBn50BiKdkICNfCEdk1AS2wcOkct+AqHCs4\neywz\r\n=lc1Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDgulav3x3mdiFZNC6y1TsTskWCKkyjCoEF98E5cWMFYQIgWHHwBDORzBCHhlLl/KsEt1qXtW3FRGMrpRbUTOqSI6k="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.41_1611121263376_0.6674994144901314"},"_hasShrinkwrap":false},"0.3.0-alpha.42":{"name":"fracture","version":"0.3.0-alpha.42","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.56","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.46","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"dfdf4a38f68e7577415af2402dda68f99b591dd4","_id":"fracture@0.3.0-alpha.42","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-OJGHu3/3kwW7satd6ksGp7DU6odyF3Zn28nToflA827PUvCTT0BoEs0y1TjJGsIqMnYfViskXJ4KtjMo1nwe5A==","shasum":"ff36a26766f3d3eaf4f6754c34d0c56946780bbd","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.42.tgz","fileCount":6,"unpackedSize":48117,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgCeKgCRA9TVsSAnZWagAA5RgQAI/40yNZjBvnigYhSmMS\nQg9jrEtLKps7WDSbbHx379p2couMUDWYacGVo8DgwfOLFowTez/grJXvUBfd\nH57Pnj5WApM+3oCYdRD8SDuKqCFfOq+gE7PwP09DnOHMbg7BDEU1LmZU8bie\nCtpWyly1HP9dOKtVsHOsQW/yv6sCUx2jecXWtGm/1MVPXP84c8hMyvUCqxgA\nxwSJT6uACw6GXTZo7yRWrLckIIDiWl6tu5gLgRSVe1aXUdOxIhNESLtNbWv4\nXLMQ3droRifKNzB/Pzols/q+AH2kTFZobWnoFHwstl0KbGQO7MxYPbAO5S55\nxEuBEfRUg0qPPBB7YmLKgbX/0d/S3Hrd64PaOp2JM4yiLD/eZGSKVX9bTxHp\n5Ezuj4wUUGwyULGXuwN0PKmVstABc5TUJt918TGBItXl32uiZjaKuZxF8UKd\nJ6wVTdnXAUZv03l5+r1J1KqW20J9SZWB0lBsAD3ygj3ysoaeewUprKGny0x5\nT0x4wgMAWsxgIyXMkxUqzpgIVrsbhR3ZVwKj9HeXbOfmvKac9EPzb9mrX8x6\n9AJ8Ss6OWf3hpaM+++2ScNZl+Xj+XYLXHmigkApBZsa+3wI4MBmlQPy2tYGV\n3b9bwP2XRexIiZ6ZfnUrvLdLG+wFPc7nJagDa3JX6cWel0+rUW+jp1LCr01x\nb4YS\r\n=3RMP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFkYOgUBNHpicvNTNn8XUfBhR6+RKVMpuEuhifcnF5syAiB5usTGYS7LJVQgAf7yuIg0l5Eoktp8+UtIezjHvPtK5g=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.42_1611260575667_0.7456266014285606"},"_hasShrinkwrap":false},"0.3.0-alpha.43":{"name":"fracture","version":"0.3.0-alpha.43","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.56","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.46","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"194c887c4655b43b9217c798acf86647c0236342","_id":"fracture@0.3.0-alpha.43","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-rsBYeWaA2K50J2+FgnvEuIvKqXcZn2NEPNiI+hkuae6W3sDwK4qIeh4dpfVIQlpxwC+Nv9yCQo0GnbGD22MGTQ==","shasum":"261ac7bd64fc90d0d1b503ea3afaf04b3a84156a","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.43.tgz","fileCount":6,"unpackedSize":51747,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgC3YdCRA9TVsSAnZWagAAhs0P/2Dmagcd34jRiPr2iszf\nlpt5I9nYbadwmVPz2R4PDa0RaRts9ooqNAX1D3ZkZwUopVGFL4uLuwHIOvhN\nPHY/KwyUozsVE0Tyvj/Ac39lLuBPzwwNA5ZUodiW4fbdnZ7APd/b/T6iYt0A\nlxoq62drD2tcP7Rr6EsB38de8iMGpFCJeufjXHhj+XHjZVWMp4DwWTfynRWu\nIMF92+9S6puHc9Rc9j//E/zIihIKYuaeOnD8fE2oEMCUGi/sUnTY11/P7inC\nKfVwX9CpkwmxdJIPs/ri6liwTxaqS6bD0WtwYufa5NYdVATbrhPq0KUoBFEU\n8VMKgZXffwsqZcEGRzipEV/0gNU3G0s2RPaWMCR5X64H+tSQGrazRvK+h92y\nFRu4dQoiQTqImKIasakLpKs7PRHq0QtThNW7EnZCQGpPwCuY9undauracbRV\nVSFBUT3U26yrpsBapG48EjYG+z86yDsg8dqXFEL0Cl0AK6RFS/9pMak58u35\nCDDBA8WWWyK3FUecmYLTxC/72rbTZd3cYzu1JFFBYP8bShHRQKKat/mXeXH6\nA7RjeZ5VTaGNPA6zzMe8rJCzRDdNySB0XieTXP2QWGSkyGoIMn5Sggt8eFG0\nNbTnTnNcifLFNFplkFl+E2Ru456Gt2QH1U+mbFbxfqPXDq2F5ExyVDPYxkIr\nmgV7\r\n=9Ffl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCrY9EzgdrsDqVihpRCPgKWwb2kTwDjlKUa9+KtCkynlgIge1p9aIIRC60lln389AREIRWX06RsYn1kNfpyfR3hKlk="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.43_1611363868634_0.8678893839069735"},"_hasShrinkwrap":false},"0.3.0-alpha.44":{"name":"fracture","version":"0.3.0-alpha.44","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.56","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.46","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"6dcad719954bdee2399836c73e7c5ddb01868df9","_id":"fracture@0.3.0-alpha.44","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-v662YWoij8ZbtT98zoBApdAF4B2COz9kwQRtt0ebJSj3RelTb+A/MIeDC2FV1pmZmX+9pf06eecw0pyUhaXEJg==","shasum":"045d42532d351bca2ddd0e71a342953b402953b1","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.44.tgz","fileCount":6,"unpackedSize":51747,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgC3YxCRA9TVsSAnZWagAA9lMP+QHYsxyDw3KjRWb3Sfz6\nAfEb1P6O4ZtiinGbErHWNGuSenCa5+FW/zdY7enIAAC4B0rmr5NasuJzM9eb\nfAYYu2Gw7iYnHjUh7hYUuaombzc6//E6VVhHl6SDqXN75DjwOxKZQG/FkDcp\nAiSWu8E3ICnf6R47S7QNFd4hQfCLB6FbhTx9EJUYrFhu7iYok+C1h9Kikn2d\n09OVthtAUD4XbbtS2AEfiEF9s9H6n8jJ046i2ERxTMSZ94vrkIkct+yXhfr4\nAI36wIxhs9FrpTqqGgBvnQ134gi0+sijtajNMA4M8EGJB1DI9ITWMSWO2oAX\nqJB0Y7TPHPZovKqlktNkbfR21dtzRQgtL+uRsV86fCYOyKgHTNvZCQiozl69\nFyq3+zKXK0HgLX3bHjtpruoi0VK9U9lYniQNbURPQ0TwSaODo2NZ3lnKgn/m\nLNiKEiOIIg/Pb6ixZ/8JYkxNbTxVcNp3XFFwIuex06hmIzlevPdo5gz77Amx\neQz1CC9oMfbEKvRieQvMkw4WZs/t3tM/gm0GBdDk8w5VB3Ju8f7F+e8+6CaU\nGkcGjM1TtCWdaRGdHPGToy1g8Otaq0pB87LcVLwLJ+pF1R3nKKJUu9mp5tiQ\nkcY91NHZZedmW/7XEbBUiztie6iq62xnAUo9tauf+YVASPJzOIaQ4CvxVCri\nG344\r\n=/6Qv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHAThNQmDOpvhpMNOSa8AShKmUnpoa9dTSd1n2hB+5PsAiEA7YY9MbHdnGbuEbTow6bC8mchc5f7gvAWGdMM97KcJt4="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.44_1611363888961_0.4524994350791465"},"_hasShrinkwrap":false},"0.3.0-alpha.45":{"name":"fracture","version":"0.3.0-alpha.45","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.56","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.46","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\n//{ \"mode\": \"text\" }\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\n//{ \"code\": { \"tests\": 16 }, \"text\": { \"tests\": 4  } }\nrequire('proof')(%(tests)d, async okay => {\n    //{ \"include\": \"testRequire\" }\n    //{ \"include\": \"test\" }\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\nThe `'fracture'` module exports a single `Fracture` object.\n\n\n```javascript\n//{ \"name\": \"displayedRequire\", \"mode\": \"text\" }\nconst Fracture = require('fracture')\n```\n\n```javascript\n//{ \"name\": \"testRequire\", \"mode\": \"code\" }\nconst Fracture = require('..')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a').work.push(1)\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a').work.push(2)\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n}\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => {\n            return { work: [] }\n        },\n        worker: ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a').work.push(1)\n    fracture.enqueue('a').work.push(2)\n    fracture.enqueue('b').work.push(3)\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n}\n```\n\n```\n//{ \"mode\": \"code\", \"name\": \"test\" }\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n\nawait destructible.rescue(async () => {\n    //{ \"include\": \"test\" }\n\n    destructible.destroy()\n})\n\nawait destructible.promise\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    const first = fracture.enqueue('a')\n    first.work.push(50)\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    const second = fracture.enqueue('a')\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    okay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\n    await fracture.destructible.destroy().promise\n}\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b')\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused entry.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.entered, 'paused work was resumed')\n}\n```\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n}\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\n//{ \"unblock\": true, \"name\": \"test\" }\n{\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n}\n```\n\n\n```javascript\n//{ \"mode\": \"none\" }\n    // **TODO** Define the strand concept in `Destructible`.\n\n    // Our dependencies are as follows.\n\n    // An `async`/`await` work queue. It is the foundation of Fracture.\n\n    //\n    const Turnstile = require('turnstile')\n    //\n\n    // Manage a tree of `async`/`await` code execution paths, with mechanism for\n    // `Promise` cancellation.\n\n    //\n    const Destructible = require('destructible')\n    //\n\n\n    //\n    {\n        // When we create a Fracture we must create a `Turnstile`. To create a\n        // Turnstile we must create a `Destructible`.\n\n        //\n        const destructible = new Destructible($ => $(), 'fracture')\n        const turnstile = new Turnstile(destructible)\n        //\n\n        //\n        // **TODO** Rename `turnstiles` to `strands` in `Turnstile`.\n        //\n\n        // Here is a worker class that we're going to automate with Fracture.\n\n        //\n\n        class Worker {\n            constructor () {\n                this.gathered = []\n                this.called = 0\n            }\n\n            async work ({ key, value }) {\n                const called = ++this.called\n                for (const work of value) {\n                    this.gathered.push({ called, key, work })\n                }\n            }\n        }\n\n        const worker = new Worker\n        //\n\n        // Our worker class expects an object with a key and value property. The\n        // value is an array of values. For our example we'll just gather up the\n        // values.\n\n        //\n        //\n        // To create a Fracture you give it a Turnstile to use to queue its\n        // work.\n        //\n        // Fracture will divide your work up by a key. For each key it will\n        // create a queue entry. You will need to give Fracture a constructor\n        // function to construct the value for each queue entry. The value is\n        // whatever you want it to be. We are going to simply construct an empty\n        // array.\n\n        // You must also provide an asynchronous function that perform work on\n        // the queue entry. You can optionally provide an object that will be\n        // the `this` property of the function when it is called.\n\n        //\n        const fracture = new Fracture(destructible.durable($ => $(), 'fracture'), turnstile, () => [], worker.work, worker)\n        //\n\n        // Now we can queue some work. When we call enqueue we will get back an\n        // instance of an object created using our constructor function.\n\n        // Now this is important...\n\n        // Whatever we do with this object, we must do it _synchronously_. You\n        // cannot hold onto this object beyond an `async` call or a call that\n        // will return you to the Node.js event loop.\n\n        //\n        const array = fracture.enqueue('a')\n        array.push(1, 2, 3)\n        //\n\n        // Fracture needs you to be aware of how JavaScript works. You have a\n        // synchronous window in which to add work to your user object. After\n        // that window closes the object could be in in the user function\n        // getting worked through, or it could be out of the queue entirely.\n\n        // If you where to enqueue the same key immediately, you would get the\n        // same user object.\n\n        //\n        okay(array === fracture.enqueue('a'), 'adding work to same user object')\n\n        fracture.enqueue('a').push(4)\n\n        okay(array, [ 1, 2, 3, 4 ], 'work piling up in the user object')\n\n        //\n\n        // You're not supposed to rely on this in your application, it's just to\n        // illustrate that this object is going to gather up work from your\n        // application until it enters your worker function.\n\n        // When you use a different key, you will get a different user object.\n\n        //\n        fracture.enqueue('b').push(5)\n        //\n\n        // Now if we chill out for just a little bit, we'll probably see that\n        // our work has been completed.\n\n        //\n        await new Promise(resolve => setTimeout(resolve, 50))\n\n        okay(worker.gathered, [{\n            called: 1, key: 'a', work: 1\n        }, {\n            called: 1, key: 'a', work: 2\n        }, {\n            called: 1, key: 'a', work: 3\n        }, {\n            called: 1, key: 'a', work: 4\n        }, {\n            called: 2, key: 'b', work: 5\n        }], 'worker received all our queued work')\n        //\n\n        // You'll note that the first call to the worker function processed our\n        // user object for the key `'a'` which was an array with four items.\n        // Then a second call to the worker function processed the user object\n        // for the key `'b'` which was an array with a single item.\n\n        // We can now shutdown our Turnstile.\n\n        // And wait for our Destructible to confirm that everything has been\n        // shut down.\n\n        //\n        await destructible.destroy().promise\n    }\n```\n","readmeFilename":"README.in.md","gitHead":"a490c49dadc624cdab890e132a8365cd5ca124c5","_id":"fracture@0.3.0-alpha.45","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-QSEv2Jm4HJAUlNG9KhCU3JCx2c1SwZ58Jvf73Jr1oUoo3amS7fiZTY9lDfHhIEAMC+kyarxPKwluqz/gUFocYw==","shasum":"e1cb7c144ab7910a6f9276a9f48b677713ea0f8c","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.45.tgz","fileCount":5,"unpackedSize":51605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgC3bMCRA9TVsSAnZWagAA2ZEP/RwCL6sqtjVx6eERAUal\na7gx0NIWIoBjYPnQVFxCN4jtWmWcCZRdItD5rNpjOz0KXRV6PUhXdnkITC9e\nfqBo86lDWOICn2Q4lEYEizQyjU5taDdX8PzAviSCFzAKwligfeOAUXv5WXF7\nRYuwuRsEZxv7Yj7DZXffbKf2fXuPjIE+BSmqMz8aNMDzjSQsGjipEU08XRPf\nFEmnDZmuHX3W5CSrN1bx9eXK1NYAztV2Y5tnxK30rNx0aBU1/31DaxjxhpUb\nZFGf45CJLs2TJ+8BKiz1ayM9GM80PzPtL90cbRa9gRCKo9UQHslRe0krux3v\nL9X0adAuhahC+jJOFMkzRMlZlWcrAFbhGXbVNlQGtZi1A90RG9sUvPud2TC2\ngPHvNUhIWAQNqIe0KEmVqEx66nLkOI/1UUhKF9Lwjin9gMGcaQxPSkx1YPCv\nYQViB8eKpgNGahdzpASob91Wn7O1QxkvofXq72ikwdxgs2q3VS9PcEPob7wx\n+oHqmvugh9CoE+EQEUTX3M8TecUNT3ImTm9K76gu7bw79aj7hpqvSpaKpRE7\nrwsguTjlky3DiR5qCo6HNtw9k2PZZw105utO2g2Rs2Y4PXvc8p+cF+0po9N6\nIWPKVjrN4E3vBFy7U0Oj+c15rb19hG/oN0rj1pwO5j56EkGZIvLjCjLb/Zu3\n6dNk\r\n=3iE/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDDmifiby+HefW2RMfPlz3AP42pi49+RIn8SDWQb3uWOQIgHsteMHjdAl/YgezbtZMOiUzwJ4h+azYaB67YXzjEZ58="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.45_1611364044379_0.13933635372484132"},"_hasShrinkwrap":false},"0.3.0-alpha.46":{"name":"fracture","version":"0.3.0-alpha.46","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"extant":"^1.0.20","hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.56","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.46","proof":"^9.0.2","rescue":"7.0.0-alpha.9"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally 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\nThe `'fracture'` module exports a single `Fracture` object.\n\n```javascript\nconst Fracture = require('fracture')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue('a').work.push(1)\n\n// Push more work into the queue for the same key.\nfracture.enqueue('a').work.push(2)\n\n// Push work into the queue for a different key.\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue('a').work.push(1)\nfracture.enqueue('a').work.push(2)\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nconst first = fracture.enqueue('a')\nfirst.work.push(50)\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nconst second = fracture.enqueue('a')\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nokay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\nawait fracture.destructible.destroy().promise\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b')\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused entry.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"e90dbf0875f53455b1ff1a1a5866f83a778ec9be","_id":"fracture@0.3.0-alpha.46","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-6x3+YK8nsHRigTQgTlJKp4ay4xDsFBbnFD/ygOvyR8YOLTIzk4NrnoSGFXJYpuXNZM+DQTVwzoOgAoFHApeTQw==","shasum":"9bd491321a5b8d75aa21416f90ce6dc336ebb7f0","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.46.tgz","fileCount":4,"unpackedSize":30027,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgC3eJCRA9TVsSAnZWagAA0N4P/3pFkPzauvtOdDCsG8l5\nZhkxmbBc2vD6QjeyemT368cCsDAFKX3Ndvi4QAF6VpvNka0/S5cJBUztOSpp\nPDLUKPdiSV/eXZbM6Dj2ffsmjMlubAPtYdkaz364pRcy61LuyxTLm8fyIHl+\nIQcmPk1+Rf2kr2Epf74oAazLzdqnMzwL1uT/sXYc3p/dipppuybZyl+Tp90Q\n5q/SlGGKKfVe+zrjAMxi5HmZbmRuK3JhKqBVIrCYTkNR5kvJxWeTBw2sordF\nMuZ77n6ZMgpDCEOfMnHL7Wz2+AB36ylaeeYcewcR1+Gk0nadOrMgKRZqScBH\n5TrAOcdd6Dfm7au1XlhE59/aFL4JbFKbTm8nqRDuwY7MUP9JcHuWWPyR1OBn\nao5cY0nrb9ymHzvGtG0697qC8vHBk/xK2xheZ8NKyN1lZbsDUgyv0bd/3JUR\nbhCl9h6JQFZfgHvhIsAukGaINSzFzbmQqrFQ7aPRVs0wSMyPQkjGZbGcY16W\nYwBsKi5B4lBZmVcr5trnj1JsbTahY+2GVOmdl6kH+QaO4UFlnZEiXgB/aBg1\nfBzVu15O4c2FtP3Y/zVYZpB4iN+XRWFz2Xhc+AC+snS5OWlPdC/ZkxiubIoD\ngzBAZWksR7ZTywg/qVvJKCEzQ9bSCZKlhyJHATskSiyjg29fTKsAZVksiY+6\nHs2a\r\n=K75X\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDtut0EYEV4RkhaLw4XQalUUqvhJ6lRG+WpNLEpGXrcdgIhAL4eSktGkdVMSPIEw4MxB5vz9QFEExx44ObjJsOKInal"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.46_1611364233512_0.8665765617756978"},"_hasShrinkwrap":false},"0.3.0-alpha.47":{"name":"fracture","version":"0.3.0-alpha.47","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.57","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.48","proof":"^9.0.2","rescue":"7.0.0-alpha.11"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally 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\nThe `'fracture'` module exports a single `Fracture` object.\n\n```javascript\nconst Fracture = require('fracture')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue('a').work.push(1)\n\n// Push more work into the queue for the same key.\nfracture.enqueue('a').work.push(2)\n\n// Push work into the queue for a different key.\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue('a').work.push(1)\nfracture.enqueue('a').work.push(2)\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nconst first = fracture.enqueue('a')\nfirst.work.push(50)\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nconst second = fracture.enqueue('a')\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nokay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\nawait fracture.destructible.destroy().promise\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b')\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused entry.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"33bc97401c6432d40c0e7e0f5d5accd4c70cc359","_id":"fracture@0.3.0-alpha.47","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-STKpAFwD7DnLip1flHeS2cse7BF03g7q68lKEHLacLPDk7NBgCKgbPzH62CV+KM+mPd74piidHzs2kNn4eU6eg==","shasum":"a51b31805bbe6945b622672633bb1215f9166cd1","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.47.tgz","fileCount":4,"unpackedSize":29957,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJ2UHCRA9TVsSAnZWagAAKwwP/0BoKbg2JK+wjzjppWVS\nPgZGXmbvzmK0RRH0aLjzaMws8b/voDhDPuyoz8QmjsgmyhbWOF3URVdDPvwo\nbPGKw+aCXS5N9RBP/P1IRgwcrpjAEqlmmXx9k2V1xJ3I1GlYrtQwDndS8Gno\nh3H0PQZlLQWJ0EFW5X+K9lVZ1ikOgcrsxZJDV6ZloduWO8ZQySPWPoUdVIbE\nPAvDqjKE4kGcf/YcbmKojxJxNUqa/wI9iectpqBtqkVKDZH+KF5SYqZQYmNM\nNcBRnnIo5o/VDCsf7bPx7PfQmCo2HVoC7l9QWqrbycsRBaXoJR+jjrQTDovI\nwpM9ZkmhDTFPpQo4x4zpv+Lp/BiTp0BW6J95Ibm3VUQ7CDbMWBpcIXK4INNz\nmA/oNfSY+Ofn2SEN2GGDH/k3degVMfVrB72NesYYXhTh7cdmPburbkNhl4Ag\nupXMDa3aBObxCDyGTGgbMhqn4SYUeVjpR9l+09S7lM+WJBxUFXxxVVvWNGxD\nGeDvqf7h5GrOwDNA0/v3b1d78mfoP694laiWQEuulsAczKDzA9bxvO75+e1k\n7mDwylV9MgnNzuKIeHBrCDCcEKnQoOtWh2/3MNEw6wc8pdZguZ43C4L2yYBg\n8QID/4slomE1pRZFS9me+Z7YmftHTZfOlk0y5fGshKSTa24MUbrKpWt1/ciT\n+bE2\r\n=V4xh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG6zeVT4rrZ7GvpbVN3zXXGjRY0QbPdxzXp0fqsiYU1EAiA9n52TX6u+KaJp0uxfFu/DAMZC42558IIh/CejTdOoDg=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.47_1613194502777_0.21046434357581179"},"_hasShrinkwrap":false},"0.3.0-alpha.48":{"name":"fracture","version":"0.3.0-alpha.48","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.57","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.48","proof":"^9.0.2","rescue":"7.0.0-alpha.11"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally 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\nThe `'fracture'` module exports a single `Fracture` object.\n\n```javascript\nconst Fracture = require('fracture')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue('a').work.push(1)\n\n// Push more work into the queue for the same key.\nfracture.enqueue('a').work.push(2)\n\n// Push work into the queue for a different key.\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue('a').work.push(1)\nfracture.enqueue('a').work.push(2)\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nconst first = fracture.enqueue('a')\nfirst.work.push(50)\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nconst second = fracture.enqueue('a')\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nokay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\nawait fracture.destructible.destroy().promise\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b')\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused entry.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"380581decaafa48c9c19ce668c7bc0255b11a712","_id":"fracture@0.3.0-alpha.48","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-D5rALavegWqsL1Q1/iNZX6Yq1jeXpIEwjS2ltPcSc0RHOQyKg/URbZdtZljVr/ledz433qnShMsUX+i/GSorDg==","shasum":"6047cc43f99627e7107d7c34fcd3b9758896abc5","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.48.tgz","fileCount":4,"unpackedSize":29867,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgPg5NCRA9TVsSAnZWagAAToQP/2AN8szj9kQBgMsAb+N7\nVe0sgVFqLL+120Ei7vqknCg7dIkXRxiKbp5rgrINKhGogi9HubkTXeNlV1iH\nTN5AjRoAnZ7fJiqwdJbx0+MnchqXdOqmUWtgl5yXLwmJYk7EYG8q3LZrddMo\n0EaTbM0hKAF9M7+pP60mF7ynhhx3zp9cnY3Ajnf7hHQ0BlqtvZmLIq9mAc+E\nqudnPWy/8iwNxg/ralBXo/LPUutgN4V37Qq37It1TYCzS6JwTJHoZAlE6QXs\nkx50uaCbLBRwI/LwMs9jcbHxs92Hg7EsjtMg5GlMxf/TgTXE/CGFlkRhHs7X\nYSwf60vZz38JJxgVUXbvaKtxEJMla50kaLfD3pdr/wxP5kBEakgoppvlzt9e\nYve5/ykUy36y7pirO11II2fEluTIUUJR2zzJA+Sn3j5dGtMaXmJkVVzDgyng\nbYsX7C74E9YLoeZh4V1inu9K+qnQDtl8ygSjAwczSlRwvp3UK0JpWOLfm8Ae\nFHrwPmjaAgFAt8gMR3JBcRgaL0IGncX2JlT6M+Wp8qwip/xJI0RO6no7UDRl\nCQJoZQ3kTRsBvkSoVCn57GxtJxnfd4WWDICzdzu1SjyYt83QoQGi6yGHwfgu\nmJLxDSZUGyA4nuDgj9wFqCUSsdElT5V6m/1ILhwDItEZDqEZn0MLhQKPUQpV\n13jM\r\n=3X9o\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD7Ksmlo0rdJRXJguSd4ZSloh06kIVreY9gKleHXRvYngIhAJc1z5nUDG0qJ3K/1RJi6Onfnn/imnS+OXGNXJ3OH1d+"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.48_1614679629061_0.4216612409227152"},"_hasShrinkwrap":false},"0.3.0-alpha.49":{"name":"fracture","version":"0.3.0-alpha.49","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.57","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.48","proof":"^9.0.2","rescue":"7.0.0-alpha.11"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally 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\nThe `'fracture'` module exports a single `Fracture` object.\n\n```javascript\nconst Fracture = require('fracture')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue('a').work.push(1)\n\n// Push more work into the queue for the same key.\nfracture.enqueue('a').work.push(2)\n\n// Push work into the queue for a different key.\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue('a').work.push(1)\nfracture.enqueue('a').work.push(2)\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nconst first = fracture.enqueue('a')\nfirst.work.push(50)\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nconst second = fracture.enqueue('a')\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nokay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\nawait fracture.destructible.destroy().promise\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b')\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused entry.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"b2bf2240ec1ab98bcbfd7704cf5cb7f7bfa1f725","_id":"fracture@0.3.0-alpha.49","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-pZXSTdGR48KaficPtemc2wLdLHpkpDkRWnb4FKaRA7hWlXZLlzu87MD57y8fwb+r658qcLMXSsKRYnjBX7qJmw==","shasum":"273ac15587cbd3bffc2c5dc4da6d607733672123","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.49.tgz","fileCount":4,"unpackedSize":29790,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgPu1eCRA9TVsSAnZWagAApdoP/ilIyHvCoVXLSjAa6ISP\nJcb/DdNUpHcC1QOTsUrgSpr4IarzlF1mtG7xEsYInMqfrx8715JEk+XhAMCy\noihstqgYIuNpkewsTHu1qRKY/BpSL4elc5XzUrx8/Wl9yiGN45qdA3UlHpJc\nPU9k4sEL+1PGXUk4r1fMEYi//Pxsx3O1J9STU1hJdAv/602mqPPKeRMUi1FR\nHBVB6gr9UlZyIu5Gciizyx1iDfUIPhsIt0MfhuNSkvqTWxz5kiJi6Mpi9Lc2\nlH9RKeFegy829gTChX+ztc+R0HtcFds0mkNpFmx1CpKOQUvM/h1albvS3Fmf\nryYMkeVUU0IJKCbLn43FX0V1HBqknQ+DDsgJk50hN/iAJH3GHAugoymcTkRY\n3lCNYQHsPN5XvW5RcFDeXbMem4DM52lPDApwBMI9oY2XxBE7LergxfP1BNrg\nEU00dyxGs3rTlSPHoz7Ka1tbxoD1i71+Iz8KQcxBSeCxruiKYdUGSS7MBe2o\nv/wRFfnS93gSYNaHXU/DiDu1hi/h9/gqd+lllqSTGWLX8GebDvCVuH02NASK\nS4rlkfVFwY+b3JAXn8xntr4SP0GBj9COVoHKOpYppEKmvP/t5gK8epXXS5Zg\nosZayY33J1z4w7ZhHd5DPeh5KP8c23Y/SKnvRd96T1kVLS++e4Ci7Mx2H0A0\nI17n\r\n=jsBq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAw7z8BarbAgWf9y9umZ98WhgQFieyYCgs3EWKxoZi34AiBno2AcmVLPWQAtVsdzIHpuVRMtA0sx5w3MY+bupwu8Vw=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.49_1614736734002_0.8478630951184394"},"_hasShrinkwrap":false},"0.3.0-alpha.50":{"name":"fracture","version":"0.3.0-alpha.50","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.58","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.49","proof":"^9.0.2","rescue":"7.0.0-alpha.11"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally 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\nThe `'fracture'` module exports a single `Fracture` object.\n\n```javascript\nconst Fracture = require('fracture')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue('a').work.push(1)\n\n// Push more work into the queue for the same key.\nfracture.enqueue('a').work.push(2)\n\n// Push work into the queue for a different key.\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue('a').work.push(1)\nfracture.enqueue('a').work.push(2)\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nconst first = fracture.enqueue('a')\nfirst.work.push(50)\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nconst second = fracture.enqueue('a')\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nokay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\nawait fracture.destructible.destroy().promise\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b')\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused entry.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"7da89afa4ff75192f6e6bae1cc093540d0d81bc5","_id":"fracture@0.3.0-alpha.50","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-JxecMDExANrrY/UWf9hO7DGnbvyp9loe1po7jjiwqfCFQED+UulfGAEm1ejHfMYG47LRAhjG3rOBdMsLCe7RJg==","shasum":"33513fd7ed6ff4a3600b061d7134a4b9cc192254","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.50.tgz","fileCount":4,"unpackedSize":29790,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgPu+wCRA9TVsSAnZWagAAowgQAJArsMHsfBrNmahsj6mi\nNYfJoS0WrikpNKSOiF8KZwd/V8hBky1tIzz914dX0Z6KXKPlN9dBJPjPpwuz\nwnY840KUkzaz+tQ/vuOGls8NwjDtI1usRWk0PnEpDOvQBcYn1Hg5xG5MoK0s\nXMZmIqzCn2pBt4AgGoVuBOkDo3K2e6KxAK4kbLpF9L/ec3vPWu5QndBmNuCk\n0vEKCieEldY5J0tFuT/RoQQRUpmf1h1d26w4WJU5zkYANy+fs+05mzwTMFFL\nXKoRXyfm4Zg5Z0u5o+PV6cInq4lF2WzZ7CMWgajnyIF/bYOopIIgFi/eyDN0\nZfq1dy0g1iXQyjGMPUfn2g7vASMjyJkhA68Ke0QRzNYgTzSbPf7pmoAw2YD9\n3I+JHvxNLVZoJhSm/DuBOKKXQTRSUPeUVjbG/AJWH0v05mnVF6Ljx8fiV9QO\nLRqT2DPuODlQqwj+Jw7MAmSY0IEicYYTlgWz9aOC0h3ww1XKluYB/J962Zss\n5vB6FkwKRMvoj521DWkaQEOd2zGngzb9WycezoZ4lSwY35gX5ooiSen9hJSH\nfpjO9Yd/k6QYx82SJp7XQUZlMJO5fx4ZQi6N3ByNebkGcEFSlG0dJGYC6tB4\nl7gYPeWsjp6gAx3ZxErTxP6C+DMU+uSiCZq8Mn9nmRVCpMtjh1NdcCojz9aE\naxgC\r\n=00XO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCqkbSBESJ/F7OKTgOT2wFlTe8E4u0kh6Y6BU5CK80JTwIhANpMqZifgtwfvM96C7OgnY3ak99SiMi2La50jDSvnM56"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.50_1614737328189_0.011702331364020102"},"_hasShrinkwrap":false},"0.3.0-alpha.51":{"name":"fracture","version":"0.3.0-alpha.51","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.60","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.50","proof":"^9.0.2","rescue":"7.0.0-alpha.11"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally 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\nThe `'fracture'` module exports a single `Fracture` object.\n\n```javascript\nconst Fracture = require('fracture')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue('a').work.push(1)\n\n// Push more work into the queue for the same key.\nfracture.enqueue('a').work.push(2)\n\n// Push work into the queue for a different key.\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue('a').work.push(1)\nfracture.enqueue('a').work.push(2)\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nconst first = fracture.enqueue('a')\nfirst.work.push(50)\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nconst second = fracture.enqueue('a')\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nokay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\nawait fracture.destructible.destroy().promise\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b')\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused entry.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"34f4fcf73b330392f2f05721fbcb1cf7950d41e6","_id":"fracture@0.3.0-alpha.51","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-FiUUtg5AfzeN+1q2FdWMoGzk6K1qjJ/AOpkwDgVPZ8DapuIOc9k+FggigdOuqh2ducVRfbkrY0SNdLTnJSH9zw==","shasum":"12d3d93a292f08f7ff763e29e74609316e00bee9","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.51.tgz","fileCount":5,"unpackedSize":34353,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgP8p+CRA9TVsSAnZWagAA75YP/1RDQh5LzKUllIFz7LuW\nORH277rqptkpIhOre3DB6bZS06sK2FaaFyCOVYRrSP9NeHmgyRygnBJhlnzP\nuBsoObz8R/ykBS9j3ysT0JkMXXmx4LkkbS6t6K1Ly9oSB+h1Zr/rGGGXtZGw\nN3MFaoJtR98RVmoO8ujoSrgjXdJFbVimH9n68434RtdqioepAwk5TnvVAcr3\nDh45qnoiM0PlEA1YZx2wGSD6DXCvVk6/Vc2Jdd/vLnMtTjFAHeuyNxCdYhNu\n+/Pti75vaF44NgdTFNdhhzyYYnUvvw5fppavNP858iYFVt85hsGZ3oqsAE5E\nHFWgDc3HIzkqjDgCiNGQ4eoLFNiN/L1hE/LEbM6sQyVFMczpbGB5OYHQylTz\nUhwOTskoPxQV6+2zq2Kw9CZivMwDD3orcdsAOdHeBZejnCGFW/9CIkWcfDRN\nZOgn9rIkHmFzgF9LkVqIJZGyxRNiekBWTzioPoLWHbc6LWwEPzc2TRd0Q1+W\nNM6Kksz+5EXyLXDXTmbmGx7xY7ciLdz6n1RiCyUinVwdV8VxVnPyi1OLoATp\nSwxN0PIsVJhwGMinpv46jo9SDAKgglFFPX06gbcDOoNwm2DeiMD1zAk/19RB\nCZas2+r7wAFMU9/vb3+PlS0FDCjnwoPSpJk6NkoZLj2Lcw4VnBeyhibdcxCm\n0rbW\r\n=2V3b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCC1KWyK0vLodzk9OX6o5I23PUenz3R4M3mxYd8EOKyzAIhAMq/WOE9nUTP1T4eDS6Fm39MbMT+RFjtEf8Pm0zgsC8z"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.51_1614793342219_0.6249023636321729"},"_hasShrinkwrap":false},"0.3.0-alpha.52":{"name":"fracture","version":"0.3.0-alpha.52","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.60","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.50","proof":"^9.0.2","rescue":"7.0.0-alpha.11"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally 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\nThe `'fracture'` module exports a single `Fracture` object.\n\n```javascript\nconst Fracture = require('fracture')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is construced when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue('a').work.push(1)\n\n// Push more work into the queue for the same key.\nfracture.enqueue('a').work.push(2)\n\n// Push work into the queue for a different key.\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceding entry to consume the proceding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => {\n        return { work: [] }\n    },\n    worker: ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue('a').work.push(1)\nfracture.enqueue('a').work.push(2)\nfracture.enqueue('b').work.push(3)\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nconst first = fracture.enqueue('a')\nfirst.work.push(50)\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nconst second = fracture.enqueue('a')\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nokay(second === fracture.enqueue('a'), 'we continue to get the same second object until we do something asynchronous')\n\nawait fracture.destructible.destroy().promise\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause entry')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b')\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused entry.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"98e954437083d3b3dffb849acdc9e21773503621","_id":"fracture@0.3.0-alpha.52","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-KWw4nC0MbJIHIeKW0/d7M/tDppF1nJC8kaKL+sNMo8fm5gxh5maQB+YJItPwyx9V7cDAxUq21mwTaCSSqrt2Mg==","shasum":"a98e564e221f9b78487c2f0d5afa889188b2cd0e","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.52.tgz","fileCount":5,"unpackedSize":34520,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgREeYCRA9TVsSAnZWagAA1yAP/1N7se89FjSMRaiyZNZT\n2ivcZLRbP4Kd40CbAI1KIhgDUtV0YU2x7ek21PsaMe8swT9c30R9GNvIKNDa\n6x/ZSK6PHE5OQOMszOzVHahXwrU80HIHKEKYEu36fqJ9OZxAYtv2c+oBv8dC\nCBtLm9DY4Hhi5mq3S1ZI8doHTo0ABYjEUvJCOrfEn8F+wfvZJz5oYZyT7gdk\nTi3My6vhK4TlL6p/mxhhbO6kzZ5npKs8GR9sxtPA/65fu3b/6eZw+wNDx5ua\nSVY1w7dp+3wvzQQewyTYGcx9GRp9NHqGTSaopSS1Rb5k5XsOiusr2YjqN21P\nizqsVLwNCGxlCKezRiP2qakwQE4Owy3RwgHTlmPxToidNJuA1j9wlLIYOjbw\nz8d/Hd2L4cWr1fZHo4a3zmeZQB4DtpWCtqJKziqzNcSyB9nnTFeZLNJXQzzt\nCE5r99pM+DOQt7Abh2Ib+kBLQt4puAW3i2e5GnGnPByuNd0A2MVxRfb1vRhn\nqp3h4bNeEKlZM+mxbtNmUABTbMyyj+TMDErdGqF9r4PVZGooGGy4GtOfaXhM\nPHBcjwX7iQeh4ln7DIRKEnjDf+m9GMRdMu6Y/hrktHIOKBZqwZyA3QE4xkFm\nWIj06efnQwC9iQ3uiMU6OzSqGQYCC24EDruKGhYJJs6zhnbreUsaoGSjnE07\nVGD1\r\n=P+ki\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDQjXiNwH0VxPnvFfD6gk1MsOzROAbZx0KiRmdeWde7RAIgAyiwz3TUrN2NbwLqcDsOQ4BZwtewvgwT9G8/RaBp65Q="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.52_1615087511110_0.4956434309109914"},"_hasShrinkwrap":false},"0.3.0-alpha.53":{"name":"fracture","version":"0.3.0-alpha.53","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.65","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.54","proof":"^9.0.2","rescue":"7.0.0-alpha.13"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"2ee6962114ab1e8deec8249def616a6834e1cfd5","_id":"fracture@0.3.0-alpha.53","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-uKeOCw+2DL/hBDHvzWbX2iNKVTUXNR1gIP05/WLE2sSTsWhY4IKHWTu1sJb+cm/bwKcWmJD/Ve9KCu0OrSztxQ==","shasum":"10c004c504eaa9751d30ef304db124de07d4e03c","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.53.tgz","fileCount":6,"unpackedSize":35957,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTEgSCRA9TVsSAnZWagAAtLcQAIsUZ43qqjuTdiDQ94eu\n9YTjuRDGxxcA4Wu69pFBmQwuOg0n5VkjfilwIb8Kwf25kBu7B+VYShngIXIC\nvIhCdvEaDPeedY2i4DGniCGhm64rzS97eMi4dyU9rEN6Nsk5f81B76673Rz3\n3zQBKnrAwdFuNQdh4LSBsumYbo2eMEENzzflIinR0h3xFbkTcbu3tl5ecnO7\nYMAivOFwN+zElTdioc5cFgkWMcLSgfEka4uVe3G/0wzeKl3VQab95mzJJ8Qi\n0kE8wCvvmxmpdDxLQ2HfjJ3DmwWThEuhvL/VHOzHi3fNKuozmYFITCBva6ba\npJAgH+k31UtqGhKt/t0thHT4W9YRjZoFikPtwZ8jo0FZd89lXHburSSBWzpx\nl82qL4xD2Y3BzAM8Ai6MkS6qGI4j/d0RtlK1WKFl3EcwKPQATy2sLrqJPSih\nOie6R59UEUYz8WlI79S2UBZyEZP2DB5uGUQa0DV2wLeVkBDfsVesp39FiHGq\nWhovY3xHCLSJXjA59aHNSZ9bcaKiFf5Kymvxy4JVp5oH3yP9lC9h0sMoA3ia\nty08DTlkA6tzeQ3dD9wn796FEJsnmPM7JSO4awgXdkwoZ9NCF8MO54+FzMp3\nDsbxw9Y4ghXI2Gp5vnDLBHMLHGlCFzAO9sE9atbope//vlAJIqT6GZOVPsvs\nj426\r\n=udvP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCeZyYHwBjxZyyCeuNRLIZEN02VPcg5kECvNodIN2C2OgIhAMcpLL42fvOPV7yVCgGlg9yco3Rvsi9gWjY+bJ2dYpIT"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.53_1615611921570_0.6139513940723365"},"_hasShrinkwrap":false},"0.3.0-alpha.54":{"name":"fracture","version":"0.3.0-alpha.54","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.65","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.54","proof":"^9.0.2","rescue":"7.0.0-alpha.13"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"deb4a96d27affdbbdc7c55e3507208f2a64206b7","_id":"fracture@0.3.0-alpha.54","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-+P/UmV0/Lxxs1avgdCo/OB+pVBIv3a9OkqTJWrqxqznX6OVoySbvSBfTjzPxSXEYK4ICbIygno+t36Wt6C1PAw==","shasum":"6edbfa94202fe5a4b2061ddb8e4e5a77dd204efd","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.54.tgz","fileCount":6,"unpackedSize":36021,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTFzOCRA9TVsSAnZWagAAmqsP/jwF/rYkoofo27qRcnHM\nfDB2paSPuM0Q9SBmQgNQLIc2vJILhA6ms7EuFUtkF6StWPbL5u/0EM6HU1Oq\nTtQZzN3CH7RLOzD6rl8zYdkaxMurc37vaiyp3ilVCOgj82QW1BWsWaZv7uLo\nhiVWXDFmCsbAHHBZYked315lSVgG1LXvXmVeYwD7UFO8I6agU9vsVfpzdBQt\nwPF5BNb9tHzin1senApu8hg5sBz4tL0x705Iz26Af4Y7Za6w/vIY9uIHNW4d\noad6ZwHNhPn07oQRiSbQmuJhDUq2Av5M5H3dkRYZzj60eExrlXh2upIRe+Fa\n4wvX/2G5s+XLHjK2TqCUye+A/V2rt7/6i2UqwRNlENLZf5d/7E5S9F6yIUn/\nFe3bQBvIXnTWvtnSAumtqyEfH91rXp3t0XSYVgBrU8/R1S/+NihHVCtN9bOa\nqj5/G4va9nS8IZcUqOdRc+dDEAh6V5lAYc2nhVxqbKud/goMn+30dqzRmqfY\n8VPhzY4/H/0i6t+wuPUxc3TW0a23H9tzCxz7XXcMd/CfJplR8uUdcTNGXgKP\n9D7+Bwb5zDG0N0obM0GuhSuSFfDnMVGQcmiUS/ddXitbWm5Odr9++Uz4WpEq\nGwm5qCKxwdXxoJ9guBeMqQ9V5yhdf3PdqNoZqUt7wFZdDNrdBb1wh0i3Ksjz\n5m72\r\n=brLk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHwtFwReHuEz/ez5GVWg5A6aE+dQZtO704DwvSDJ8+MPAiBO3a3maRiWHzEexpjYh8CH9idwDL2SYUtlTBRH2+7VLw=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.54_1615617229679_0.13513910752676295"},"_hasShrinkwrap":false},"0.3.0-alpha.55":{"name":"fracture","version":"0.3.0-alpha.55","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.65","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.54","proof":"^9.0.2","rescue":"7.0.0-alpha.13"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"7d4af24b11a3c1471056fe9d1243ec5f92ed96db","_id":"fracture@0.3.0-alpha.55","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-HuRK1Qv/t9kA1M2jYiKgQNlmfQSLSeVgLBaGEwjrB0Gfgog7To6WxSls0DZC+cvIdwrgbjYy84UjZBpbofgknw==","shasum":"8474a3ad277aedc080800058fde32dd7d2267e31","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.55.tgz","fileCount":6,"unpackedSize":36324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTH47CRA9TVsSAnZWagAAsHQP/0+2WPsKKIt74dZboLIb\naPqIO+5u9Y/i+IUH645oZm5y6jQj7SfA8gdDqkhFKHomnNlMXws1X3xSSs8o\nGu4znOG8vl2ykOI3+Yijis84xZQHFf0/tjDhcQCsUWff78RK2eBpNn3x+aZB\npPwzbixC/IX+CuhYe/ptDW//gWcogfGm8SobPO2wwQc0Tkmu4ISd1lIdTpp5\n59OCM10oPJ66FEjfmR7jqWOlWCZO7KTHFsgCu1ODLoCMUKjIecwJOAuiMm1P\nLOd1Q0KAnQq511M3VPV7/wPYLrZnbQxP68d37mlqmQPyHKM/WruZ/Pt4nl5V\nvojs/AtjZ+2yfIqNbRUQhw/k+QPf/xXnVkVaI9LMakvw8//WDfFlKbCscUx0\nGDd0A9csmSP7EUo6EiUZPGlrJAwCsY8VXrI1gFj2cgc29WumsCe4GhLbO0Bs\n8lz9t75EYx1txgljTm/Xg+oArPgRp3kgIUIqT34lmE74kJBgs2GDr7sTqAbR\nGqMfaknkUT0TuviaOGaHsypbxv8LPLDCXwEj7/9Qf16N3VFchdEsBdhHnRTS\nnGm+316/x/hq2RhvVPNe7+78Y5eD5j6ZEwSLnNa6oITmtVl/h259r6Vkjdvl\nsFEHHeYRadDKKthcN0Xop37w92NeO2KJ0HLDPzPety+TEQq7hhZFnKHTWKJJ\nnojl\r\n=o5LU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDygR29GCPxq0eHWhm+al2uosjqT659EVMR0oE9pXpd0gIgVPLAa3sWcNq3BqVCmA3rWbJWQ2iaRbML4qOoUPl9YNU="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.55_1615625786916_0.031599062010236656"},"_hasShrinkwrap":false},"0.3.0-alpha.56":{"name":"fracture","version":"0.3.0-alpha.56","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.65","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.54","proof":"^9.0.2","rescue":"7.0.0-alpha.13"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"718b0566142c50b2d0ac23c14703c22473c8b651","_id":"fracture@0.3.0-alpha.56","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-hRpr3DWfI/Dlpr8aXHlj0mwrAiXJ4jfTraSqHtswb6otuF/5nrCAnO8QBORGzMswowUyY39U0AK6Xs1Gm2dDuw==","shasum":"167142c8cff2c122c5c3ea50370f409ff8972964","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.56.tgz","fileCount":6,"unpackedSize":36667,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTTEeCRA9TVsSAnZWagAAH3AP/ibGE6KkygjjTmY5yXGS\nJgZVPYy53JzdGgbaAGxq9vADF+UiGnhC/Pv4YKqaWYeQWYmXDagkQQmHShSX\nuXpCwr+haZk9WZvEQKN6elKzv4X/SxIGQd2MAtPKwgkoki/ab0vK0tbGb0Mm\n5OaFMUI2R6JLFMjrFCQU0MrekExyBzeBAi05J1Vn2Okora0+sf/NT61/INIn\nbTp5PwhWXymvM1rdB3xZwz76KZCdaK1ow7zO+zwBLLi3jGL6+/xbf3n0doWB\nVeCoZQWdOa27uJ1S5caz0eDsKBkM4ejDZTLmsl4huL1o92VBc0DEMowoR1tP\nKsQvrtQ2NssTpJA2Qrp35GDobTZteIySyZ5Xi32ev4WyA8QxWhKLmkPbNUp2\nes5kZz4RnHj5UweALXaGtrgsojHQiTOA1q0JW8HpJF7rS/DMGmNARUPgcavS\nR1C0kzwm2zIJ6jacyzLXb3QTOICNtUzGgqj3EvUQVEuHLzgJ9xKWkx5WWU8C\nrO/X53e359EK3AKK9b2Ay/m3AyqquMCcrX0qATNKWZwVy0PdkXBxuO/U16Jk\njy2NVH6Ri+Q/3KcGgIDVE71rNmk/bY09maMicXPCgdR0Lt0cDagV7F3V31Sp\n3b3OatRWv4TzI8Uko2l2znYhNFui14aVmwbEmNhTV2el2cTlW/xzw7PEN5dN\nJ9MA\r\n=eRr+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRcLPzjxDVqdz6VqLi2ANHTyN3KQpjWTvqVsp8Fnq3SwIhAPv5MK9h3Ujv9P4dkcTeQmf7qDIRX6HyL4HPRYqCPGZ2"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.56_1615671581873_0.22378955954739066"},"_hasShrinkwrap":false},"0.3.0-alpha.57":{"name":"fracture","version":"0.3.0-alpha.57","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.69","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.56","proof":"^9.0.2","rescue":"7.0.0-alpha.13"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"c2b2c5d43f2ae9492abe301152f4a440cd940e58","_id":"fracture@0.3.0-alpha.57","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-0w0hJFpymwv2zU3zBMV9Qq9mJkNh3oZ3rhAp8camssqOTdfgMmuYPgQxe9CePEedOPIl0wvS6/jtTESqxhW14g==","shasum":"3d6239133bc37d1feff86a3f47613c15c8f4f219","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.57.tgz","fileCount":4,"unpackedSize":33821,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTaUFCRA9TVsSAnZWagAAi50P/2cY+DtbAA2pkMBaINqi\n9L5X30nh8/ikEcOpiJXBdlD4xHeN2FFmgU4YuulyunPN9sJ+unfUMbZBPJzC\nskx+uj0XkgMhxN2St6zDu6caE23Vmy8f0bW4Up4TTJM1CuWRkGY+3L4lsfUD\nRnakTJKIK7nOPb9odEHVczujdDa2p6eYl9yl1N32HLZWzo4KXf2PMvlA/3oD\nTEE5tryJBEy7px9FEriIgBYiwNh4dZmIEkn2e83Y50ynkPp2gJmsS1gDBSDF\nEXqfrwp4hMN7p64AFq1zjSE92EBoW3Y9/CzdvW9dpcmCUKSvwpDZ0fSAI93V\nXP0HLmVxOxdx5j/451SJdyG5sN1JyHin5KrxH9GHZ6WP6QjVAfQWwWn3BUdE\ncV6UrsE+Lrjk5vBhUuTJh2elEYrZ21GC8uZLqO9abFEN+WPYq28ZIRmnJmLL\nEkjYz6t+a7GTsHkS86opH7nnGUBvGNuuTnBd3DJow4+vQjPEn5D6MEp7XRsW\n44MuN7FLe97lyNzLbegCywsVYd1pIdbBmVzm8gCbFCX27prnLpYSJnwARxla\nSuczoRNmOowhvnNftYPzf4Pi6TkXRP4m60AJLmQcJzv323ekg4+VzvaUzBhD\nYbrdTmCf83tYM9o7gdMfXRS2nP+Q4asqX1LIyrNKcyl8qxqRwhHiJ5gCdaSF\nULDD\r\n=VZui\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDiaVyArkGS/XvaBS6EHlLD451pkRCVe5MNIeOiu5I4tQIgRTzSs+6zl6rzuonKvp9EpG01+ivd3G3kM1D6kFtIdcc="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.57_1615701253390_0.8947717367520185"},"_hasShrinkwrap":false},"0.3.0-alpha.58":{"name":"fracture","version":"0.3.0-alpha.58","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.69","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.56","proof":"^9.0.2","rescue":"7.0.0-alpha.13"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"8e524dedb4c20ff9dcf2be8d7db6d14539c6eb7f","_id":"fracture@0.3.0-alpha.58","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-6Rk2I2gVvK23nzEmnlLf06sbl64i1f5XdHpol58C3LqKR7gQUaf8z+slme+h/IgQlFykyQld2ezwYiXQU4epXQ==","shasum":"8fd1007cf1d43df97c9df43c366047054f462db8","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.58.tgz","fileCount":4,"unpackedSize":34013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTbj7CRA9TVsSAnZWagAAm/UP/R91vDCGBZnOWL/lRbxi\nC5U7+oQECm1oDKzkWjlKyawNI8gXiVvtso0E9HZOrF2Lc2X5DEYD0jEgoEjy\n8xJTsFvE9Tzyko5m8lJqIpuxkc9c8N6Pi6ePv5Es/kkxCwJPeb8p8deojsAa\nOBD1C4/XUYW1JSlqMcMCiDy54JkjmIE/TS2YmA8ghws2WAm2tV3EmRfCgQPE\nZp6tnHFgTZordrCGTAhswgAmqklqaU6DwVEMApcfjIqcCJbX9lLE/vt2gAz9\nFcMN4Cv7KjyHi3o6ev+HZLhapBX2Seing6hbTz31cegzTHyW8TMI6InGr5O6\nweUZz7caXiV0VVruA/bi6LOy2XO9Lo+wf2iibh5HRAJ1kyJPD9CWqkQtXb9o\nUNgGZEh3cSs6glERUP2cgVMFeu/VI9ZaOz/hDh7RcxsQNe5JCyqXD0LH/Mt1\nq5yKjByy9ItlCwEaIzNgg+Zrm9voV+gmTUxFCcObIAacvS+gPHRBd7v+M27/\n25y70t2fQnQrUQ+EdSdIbXTOTdh/iXnv3FTHl9vQlNS8MN93du3P0FedAUyd\nYgDQUlteSVyzjoBNqFaatHiEfkWtVgyEAWqaLq+nJ1F1XreNeN6pB/iZ56Wm\ntEcLmo1b0U5gZGAW91QY7d1QbgwOHNHvnxkp5qMWh1TyjsMqI2ek5W9z+HJi\n6qDm\r\n=vjL1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbW0Fys/qAV40J9OrC1WIMPVRIQTyoosL13TccsI7mrQIhAJxmQQ7KKnfg6AuGFUgBNWXloOd2JjDuWDpnsZr0R+e2"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.58_1615706362577_0.7135116696280361"},"_hasShrinkwrap":false},"0.3.0-alpha.59":{"name":"fracture","version":"0.3.0-alpha.59","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.6","turnstile":"6.0.0-alpha.70","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.57","proof":"^9.0.2","rescue":"7.0.0-alpha.13"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"b51cb544a5b7f6f76d8785394497f785eecd1fcb","_id":"fracture@0.3.0-alpha.59","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-N0Jfc6Si599nwKkaVHy1PunwiglyBoE4H5iLvkzDrlSNI3yqNptsTk0DUEuHs5s1K2oauGZNqf/VNg5ARgv8oQ==","shasum":"ecb3d3da6689cdd38b4fa4c925e4f9d6f6d6042a","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.59.tgz","fileCount":4,"unpackedSize":34013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTb7kCRA9TVsSAnZWagAA8R8P/A045HJcZLUS/FrxLCUT\nMftnoReiUyRtBgBer66fEvuuWFpfExjry6Edrogvb4FSgVwAelKaxa2356qd\nckPiJJTL94uvNCT8kHnuJhdSbIoKMjnXDIp1D1svHT8YvZGfJAKl3z5PKd20\ncok34I0exGHf0KV9+jqUDB5/7M9GPtVChlT/erKQtUXx9jO2+IwRkPQeDA/u\nn1U49xOCM08oGnJOl5QGjz37FICRA4XMcOg/qV2WxPIk9JWkEQSdssgjIuqN\nR/niPMR084VBgSg+DtU2genDF27/EyDptL0ch08LkP/on/MQtfwATnb3+UrV\nB55yYCcjEUYTIKN18ElNff7EcFD2gjb2wM/mO6NJi3tPOaGSSDi+0BvjFOvf\notY3DU8gSMxDyCd6227HQDcPn46v3liiZ3cHrygGuE/IlpGA7BqSjLy1wAvM\nx6SuEBzkv+p4dU2zvcQQo+5JpiGHeQ79rFU3Gw1sqvgXphCvkCXe8sUN2zio\nSjNdk6j2mbaRmq+ZJxHZaciJ7O8kVckw9bEVy1qtrJbaSj9oX7kAPMNPdOEa\nht2O+yTag39QAuVRvUJSsge+ySa/qtjeZxdf4K+M9mXe4OyMEgRg3g+XFdvS\n6pYXyL/JLDFEAScJMNmKneGiV22gE6mtUP7ru/AaWeWhdqZMz+rWw8V2mfpR\nS0Gh\r\n=uTXE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDV9M2DGy+p9/chyoUrKWmRByf76lIO6NAjSyWFsfRWNwIhAIFqE1MgXD+Bypd6A1Q6eZray7J9+L5/gNDbOsFiahPs"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.59_1615707875657_0.934573142910025"},"_hasShrinkwrap":false},"0.3.0-alpha.60":{"name":"fracture","version":"0.3.0-alpha.60","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.9","turnstile":"6.0.0-alpha.72","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.61","proof":"^9.0.2","rescue":"7.0.0-alpha.13"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"4a7018333b41dd81139bb87c0647c3934566be1a","_id":"fracture@0.3.0-alpha.60","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-8Dg9TtHsB2VJ1h/WUu/kjdgfUQMCVzPLxzc7FeYCwHGD77+MRtaAUSHHs1nC1BOnpetjr5uzh0kCKJd4K5bhvg==","shasum":"7be365460f24b00e0f761c3f907c9aa14afd198b","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.60.tgz","fileCount":4,"unpackedSize":33866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTpkpCRA9TVsSAnZWagAAOi0P/iVSLTrWiwGqZNWv/3yG\n6pbbe36sJr2J/ex8HItLsw8PWzwqbkK8rr53/iqleWxu5H6YpTmzblJbdkoY\nAQjODVNC0Tm4yCFDUgZ0V/3PKUNTCETXotc6HQDak0QBXeFDi0ca+L9NUHhB\nfjzVuGJLH6ndPGosaerTlrctTn3ludIPzzCwiYwyN5FiN4B7E6QcyyxXZVeb\n2x19JT2GbNyMCT8LSl3kUxl2lMw9YVYwnzc3Bi1krjg2Y6q3Lg1YMQd6Tomi\nDHXuGy0UItvDAV/bxSZI7KIprSf88L+/8ydCqL9x63QVibUezPcdsRtA52e8\nxwptDxcoD35bRj72zyOkynod17jxXBqsk1w2nwuNWWBI5jinNK/BADczsULj\nVQfHdKS1uFLvQFqGsgmajDQ9KLVIlVKfHk3CSRQYwAlBT6GRj5qbBbJy9YrJ\nLAvqak7cuyzkc+KDsy6BJ94+kLHJif6Rpuc2wgiN0Yk4IMybXpsI8iWXAwo4\nHHGQP05w42XRErUJMdndOqnBtv+V+IFlZSA40C///6Iw+FbWa6HUb1CKSDsD\nxYwLs/5zXQp+wGD8b1vrH5bNRK+5CntzjPoV/ROwFVSjiRO6fbQp40txVs0J\nyXlgpHl536VfAECtNvYZfOB0DK082DLWCsMEe7mW0XWeIxof3xHbbmghVAwf\n4j7X\r\n=q+Sf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBfSwUiuUeEXur+QBQThQb1Tt8AV65uCvk2N7CNbK4KNAiAzgi8E5HCs4H73R1LSaGk25d8lk04+ZET3eVuWfC3UcQ=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.60_1615763752813_0.7605489174351916"},"_hasShrinkwrap":false},"0.3.0-alpha.61":{"name":"fracture","version":"0.3.0-alpha.61","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.9","turnstile":"6.0.0-alpha.73","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.62","proof":"^9.0.2","rescue":"7.0.0-alpha.13"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"e7b9d6b25895274371b6770e29dbecb5a2519c9d","_id":"fracture@0.3.0-alpha.61","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-knAifC4lzNC5KjKSyyWX8LWVFDhQ1Rbx4zMYPUMt1vJFLAQ/OGYy56XKlBE0pP1LwVxd9AeQNKTz31MZXkOvyA==","shasum":"10421b512e2359bb2c90e6458fef9ef3226185fd","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.61.tgz","fileCount":4,"unpackedSize":33866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTqDgCRA9TVsSAnZWagAAb0EP/jepzkU4vth/zbSU3JAr\natQosD5IbLIGMQ5aBLdZWdGecmS9jXxJEvmsJlXQIJ4X6fN9OaZQ6R8yxlFo\n9J0sAJqsWrn89ehSjuj1viUH8EIXFOvgc+EbjJf9iFt8xfhVHrB1ik0pm8CU\nRkNf0gWpjh+stTtiXVDc//a2LujtnvSw5kLnONTDYfc0AKcUN+8gKDYG+Kq1\nNR5Goa+PMVlK2YODnpYDFxsMidLZsLh/BouLkTHdw1d/ryd62Gw43lb/Rxof\ncZ9bxrBMEw7coHYiHC1kkv1WwN6Q4Z7B8KXAyi4JCdnFyb6Nb64QLM+tHRfF\ne9FXhxj4fTGVX2+DyuplA0L+MOVh+Tj7tDeH5n9AedHhGzPg6j3vNq2J/qBF\nlvWmJ+somdLJ6aUuS9J5Eb11e18rZyOIEFVQYQ2kiV+wVaV0Q6px+GeznXU2\n43fZz4RXPCgYHw6Zxh+8SX3uKSUuI9s6uDhUbx2A9E9J5rZ0SDgnu6sZv/oj\nA1QQjH1fqNQX+TVraMYMe4SD+reWu7bG814YqRST76SQ202wkhNsT+AhBw8U\nNSmuo4aewZHbASIA/RAmjhdZYWSTgBsAaz4HfbWmVWeWNYEbQCDw95T/n/MZ\nT6+ZBeInQUMMp0nSqpbOGiLu2bJdIz5406kmzlSokvAXGCQHmkJVxkoIwPke\nKPq5\r\n=apw0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCiEiAsu/Ni/BOdGVzNTKhCr2umaoD5R/1MYrOra1s6PQIgXlSe5Ce89RV1x+Enll70Yxv/BT5msTnmufyreNOT3DQ="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.61_1615765728378_0.2469609870763143"},"_hasShrinkwrap":false},"0.3.0-alpha.62":{"name":"fracture","version":"0.3.0-alpha.62","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.9","turnstile":"6.0.0-alpha.74","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.63","proof":"^9.0.2","rescue":"7.0.0-alpha.14"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"3d97e98fdfaa6d73089a47a248985970412cc1fc","_id":"fracture@0.3.0-alpha.62","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-DdwvwZBb9RHaYzlImWbWKeSNHesWv1cFyuaVokyQwrxqUqU0N4cfbl1pGQ3/118QlIDbZLxipV47NBoiMN+Qcw==","shasum":"123050be9b829a625a88eada3d677be233bb28b7","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.62.tgz","fileCount":4,"unpackedSize":33866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgWVyCCRA9TVsSAnZWagAADc4QAJeMpes9lgn6+PkUsCZn\nbx6+j4mCmE3/k63I73lf0+9yp0e38LBFMU8EHrNJ3dVGO36zd3w209Jc/kLA\n+Q+nwtKmJUBsjQP4yE2riOwFMGZ1IRpiX39WJ173MivsEHGRuuTaqQ9RzrRD\nM0B20DDeTjPzF7JdDM9CCLt03G5LUCmasiW+DKXotRar6Wz2lBt0IUIDxxi+\nBbnfp4FINIP7VczTpvS8mFMMgBsKDuBK7jFxMDe9K8aMqD4jfLIfFNUHhy2n\nE6oM9K6LQMrsvb+WUMl6ZEGasK5kTs65B3Rw/X757OZyg6hpBIM20P8y1p/y\nqTSJR+1kfwtr9Dr4hiPFAnLpanGDAj2Y19URFTaNnp/c9bqON5Ujlanj2Vaa\nzOqvDHuZF52bvZwyAO1yLy3lkbCnNSKuQrhUvHC9p0MkM/3AbuVAiCsY8p3R\nOpVLwygvgCeqt+lByHhVsrAxIxzBZLKHFAABz+GCt5IGrcgwstLPcd+GAB9n\nGbB73hzMq+tAOpeS6JWWrNfvc7xVq2tbMfUX0Ykqj+lJguHtrl5o1K9zsR+E\nf5FCvIY27AZh2aWDPPK4T6zIo2JFhum9Psup5S+7PUNyJPupglBlPCDI1DSV\nLbsSzKI+J3m4dmVQa04r6dZpmSOXorTXiv0YPQwOEGm8IozbtaP5hPX2P0Gq\nHw6j\r\n=Da/J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHElw3EgmWmHy46MDHlPeAOA+WuHRGtFIG8QAchGV0ceAiEAsIL3R9z/CiRu5nOWkIUL5xwUVU9UQcxYClTgc2q5Lio="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.62_1616469121905_0.6495228289030639"},"_hasShrinkwrap":false},"0.3.0-alpha.63":{"name":"fracture","version":"0.3.0-alpha.63","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.10","turnstile":"6.0.0-alpha.75","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.64","proof":"^9.0.2","rescue":"7.0.0-alpha.14"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is construced when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceding entry to consume the proceding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"68271a992ccecaed132023452abed347d1a279cd","_id":"fracture@0.3.0-alpha.63","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-BoXovCUxRCVS9vfKsY4Lmhd7+y2iwai+kWB4UixAFt9f3BRoOCtzuAtUXCsOjtUANl7MBlDjPZX8NpzByaWPGg==","shasum":"056204811ff191889f4acc1aae1d03e322023e70","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.63.tgz","fileCount":4,"unpackedSize":33871,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgX1huCRA9TVsSAnZWagAAesQP+wXp3xWjKStCWN7wRrDh\nmiHHXIoYk0j9Kszeana5vzXHG0quZ58871R/WhCfHSPBN8R9ghYR+hYRJ3cn\nJN8yhtwsRs+Bz6FffNtcwpmF0HTRDhzILKCux7EKEd83rYH5ewqppLIbjzy0\nvGlOo+muAH3CKDiUKd2BwAUTJ654TthQnklEfT7i2wc0D5+hFdBB/RU2TONF\nLAvCkZjr+mzmlJTuPC4ZAPe+sevM3WZTJV/PVmbEb+7Qwk5rZ4GNHl6eJ2cf\nK1CemUrYRxENS7lOKvNW3fwa3r066IqlzYTHC02uo7/4/kYnIAx2Bw7hTa4P\nRRlL9x3q4iTjYzGdEe98MUVW0PwYiDOPYqtSNM5N0WIeV/sCJnw3dLv18jBg\nc98WJpWmOnyhR9FAkSxB3mAmbTtcOp9U4n9L3iqIobX+sykVHhbcyImDTgOb\nemtzKCJw6GV5oJO6vyBJ4iqiPhf/oa1YhTjtA+ufo8AH6YDm6eCDMq7hWcyg\ndaaSVE9NSVCNZme6bY8j4zFNMGEUdiuG7UuK3FB9FELxhJJzYjeQR+JVT5Qd\n8Ctk4Ohd4aBMrdz2NnfbERQDHri4GW8y6JuYikkuUrxTNeIAHFBv8FPeBfr7\nG5mirQr/hkMlkZ0A/75mYzgLyuu6gf+BxkQxXX4JH8tUubNx0Xn2veltsrID\n5jss\r\n=Fv1E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEFG0awnP6A/gSc8qQQWthZZm3V19yyOzx9LJfToHnl8AiEA8N0jvM1bvN31e5IbmZh6gRah9VA0g7pqes+n8XHdqZU="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.63_1616861294053_0.557312175373349"},"_hasShrinkwrap":false},"0.3.0-alpha.64":{"name":"fracture","version":"0.3.0-alpha.64","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.10","turnstile":"6.0.0-alpha.76","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.65","proof":"^9.0.2","rescue":"7.0.0-alpha.14"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is constructed when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceeding entry to consume the proceeding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"e06df9a8a52a57eab118c64ecd038463171f8064","_id":"fracture@0.3.0-alpha.64","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-z1qcVvGtQC0SvUB2lPdNhGqJdWz4RAnL5SmKO5tdpk11A/WX4IqaJJfOoDbERlZTas2PlLqLDYNdpcUs+szrqQ==","shasum":"1c769e145513b08fb7127e05f8f650afa5f40abb","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.64.tgz","fileCount":4,"unpackedSize":33875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgmrRFCRA9TVsSAnZWagAA1/AP/j0FpVDy7Rl0Roy9Vnf3\n9W8TyQmdSqNn1h9vJZJSXgdHXzaE6f0GrrMTkc0zCj1PwD6ZArBIs7eKaE0a\nEbQxRWtHMZ6PJy/euGrB+3BAgKeo9gcLOfYjCWI76BQvlQrGtowhHvyeWAG8\ntpM9EgwOXCwz1AIl+T8/bhWN04bxYfXZgYnU4vkDe0XGfbpEiyte8lwnXGEw\ng7y03pBdIAmj0raqSSnbLoKfVRFcYH8adoIq61DE0FegZWu4ilJbWj0/GKSe\n+7j/vJnoThOuYsaWJxY6YhxtuuCjjVaZocXqlNEw8F1z703QvT2l0Y8/0JRR\nrZrjxAqmIw6Jn28qohnpRZT9FMQJupmWUF1dqpIm1BCP+em0FrcxBD+Rd1v0\n/aTUdCvoeIfbTpKx2so0vReI5tumQTcmle2ZZqJmvCoCfjn//MeGjVdkB+7R\nbD9CoZDZnAW95ZaUOsJueV8qqdQmbKdFN8mMl6tpJHVKxvpbIIBe2LIcCpQh\nkykXkG1ePvF0mx0FBZo5SAorQBeOiuU8dIIK4qKxKqi+FLdVldcElX24/bP0\nYWjGAIkCI3/rc8Sv9bLTDbpbml/bfrMdmqlgyhmQdZsxD6+V/7CortbyzFI4\nVXxx0v+3VHI9XkLdaMW7CxI1K79kkHUQfn8xxpHS4mz60IeZYrIaobtzFI2b\nddmV\r\n=GQ+U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDoZxsuzuOq8haqOATu5wqCaUUrrfY6+Sn8X77bNm3MEgIhAIoA/Evl7zaLuRBkeqUm4PCG7VLevLklbgWjDc+bqV/o"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.64_1620751429418_0.14888760444329896"},"_hasShrinkwrap":false},"0.3.0-alpha.65":{"name":"fracture","version":"0.3.0-alpha.65","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.10","turnstile":"6.0.0-alpha.76","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.65","proof":"^9.0.2","rescue":"7.0.0-alpha.14"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is constructed when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceeding entry to consume the proceeding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"cfb0926dfcff31d8a3793036b9d0d174b9220958","_id":"fracture@0.3.0-alpha.65","_nodeVersion":"15.3.0","_npmVersion":"7.0.14","dist":{"integrity":"sha512-g52A/spmP4IybjHNE9CikE9o6SsBb5l+SrDFsuen3oGTGSdDWrXE1E+F311sPj5TY0+Hh8ZVBEpBqC3Wv20XOQ==","shasum":"8ee3aa73173e809ee91b607d3dd7d02118142d0d","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.65.tgz","fileCount":4,"unpackedSize":33875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgm0K/CRA9TVsSAnZWagAAa9sP/30Pgaazq620tlACaD1A\nb3rlt4WxwbTMFtQmFdTKHIJARpjXa7PHTlX82BplviI70+2EX40iR9mdV6SL\nIE8kNzRYnLG0XIvG3elXdCoP8cgDecu90bdb5xqfXS06xuOSAbVWFPYGmPVM\ndPHECw7S6f4o44uacL1QRiQskAIyRkfi9kMLTAHFHLHPgdASy3dpnv0kv3w8\nNou4TppX1UIzT/d9v62PE8AlcMKG6nsRVJjQgFLv2/HEP6SINJK05eY1q+W2\n+8lWaCOg4a6v3rgTSxWzK7YAnaWl1FB3tS8NrAM8NAb9Ngfn55UD3qBFZAuy\nbQagVAaGwom1EAADw8SwCJ62uzRmp8VXhMb2ktjAdFCE2hYmTx+rwHZ4s+bz\nErx/ZosLtQQEzQCyf4rGxOO+6Y+vHoq+RquQIknzJIjYq3Z0EbD6pbedC0BW\niOLWTkTntzgh4/yJ59QKxrV64DwkqB1QbTmxnfZpiMbAHWWcZ3rjUM8w+MMG\nQP3hfLLHE4OXOgQNiIB62wGJoXBcViS1jHiGFL9jfYFo9dnhRBj3UWDyk3k+\n1JVuZmU2W1uBC5EOUnCN/zKC+25KydQghfqC6vQATxjSKfA0w8wP/G4tCWH2\n3GHsHRrZdb8EqshW9ji2MPkW5rQGtZSHjGrhFrfsYfGkKrSuJbeAkVFLSuL8\nqfgq\r\n=U/Dd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBMKicfaXsjucHaZ4N17iclB8WO6MFadVolsk1++U3awIhALix5QAd73qcZDmsr688fXctIfpKS+sJ+eQo6s1ef52H"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.65_1620787903106_0.2661857374098664"},"_hasShrinkwrap":false},"0.3.0-alpha.66":{"name":"fracture","version":"0.3.0-alpha.66","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.10","turnstile":"6.0.0-alpha.76","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.65","proof":"^9.0.2","rescue":"7.0.0-alpha.14"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is constructed when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceeding entry to consume the proceeding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"b08a750e07c65db10021229dd5c3e2004b463f7c","_id":"fracture@0.3.0-alpha.66","_nodeVersion":"12.16.2","_npmVersion":"6.14.4","dist":{"integrity":"sha512-c8f+qLYZ0d7TU2RQ6W9T99nhiYncgGLwR1KV6FLdaxvquWFpT6kvM5OOf3qRAaJI6c7oLs5l/qx3zxXj7SfCWg==","shasum":"b377f170e81024d0c89c1bd4b6d51a88f1ab2dc6","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.66.tgz","fileCount":4,"unpackedSize":33875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+dPdCRA9TVsSAnZWagAAsEoQAJkgBanhlYhwr/HrlD5N\nh3zwP06OKzxYPZ6HKrGSa40oQ8+7H72xIUQ9CA0h/SAh3iaEvu0APQnUWeU4\n+wUSSSTVvyv2FKs90hhj9vuEtk0iWBop6PD3ovSkU7uYT0GKq956iA9fbeSw\nWMO8qAZDxDLl1UTFxSZFgXQ3MVmFNq9o/zaBgZrN4Z8eULEAB5Gv4kfc9B5F\nWuaUX13In8uRSkRcHu0AB8DgTuYfeQB0iHfUJb5Y1Ire2tZugpUpn+5wZuJv\nSsxpVi2T8g0CKmS3OZXxS9dDUFXIoM9A6D2Ca7vsw7rtGsf50+tMx4UoJyoO\n5b2v7xOrqZS5h5KXP25jD5kSgkQ/EgGHZdHzcfI0o7XvNxVvFxMkM2BDrsoz\nl2AxWrrHWX+onGMzURFXA1DBtSNchacnvNiynkKTQ1hsFkxVAGRH/jTdWRFH\nxYYsJLnTTnYmwkly40SPbvM+DUCfnHK78iVr9a0YSq43TQMxDLFEntI5Xa7p\nYS6JYPy6MvsxQ65coLfsX0l6VqryuuyLtROZod41o846d/qh4OGpYdT0Z0Qc\nPdlwipTWBL8f9OaBY3ZbOUwA0GhzXPeft8iA1t6tyqB2EjRlw15udTdL/f1o\n44Zb3EZDCgCQJdzZLkPIztX2tJee+s9dSMcUbzFZ/FtSNXZ6PcqbhDvbrfZR\nJm/b\r\n=XV6Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBJrfahjJMnRuXzoUVwFRd/R/ZprQkgCcKuLI90kbxISAiEArxXcQedjNzvs8a8A8Pub/cfZzStz6PmmAL2bj0F0hjw="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.66_1626985437811_0.5039842595871538"},"_hasShrinkwrap":false},"0.3.0-alpha.67":{"name":"fracture","version":"0.3.0-alpha.67","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.11","turnstile":"6.0.0-alpha.77","vivifyer":"^2.1.0"},"devDependencies":{"destructible":"7.0.0-alpha.66","proof":"^9.0.2","rescue":"7.0.0-alpha.15"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is constructed when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceeding entry to consume the proceeding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"297335a184cfbd5d64c8e37d35044ac63a0cdf24","_id":"fracture@0.3.0-alpha.67","_nodeVersion":"12.16.2","_npmVersion":"6.14.4","dist":{"integrity":"sha512-VwpQEUcE/zdfeXRWhDhJDynLx1PJMK2KRVx1fR/01I5ExEc73GbGWdePi1tNgy+Z+oz5hSNGhb/jpfEQ/rMeIw==","shasum":"ade19bb2b01b83fe7566479d59a00157af175127","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.67.tgz","fileCount":4,"unpackedSize":33875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+dRFCRA9TVsSAnZWagAACv0P+wdWJiAIdXZ2ypy99LMC\nI7KX/TwJu4wLGXP/XpKCe2KCF04RtxBQWmwIswUpOAOhd8uQs+4SXg5gl4vv\nUDGRH4A7sBEHENigcBKTRSR5+pn7gizjlouRam+R0ZFAeeyDPA07XP3KlrN4\n0QiksiWRR94xwgnyyKaaj779Ivir8fTG6FZA0lwgIxJ+eQvufSAmL0IBBvJ2\nyzMk5VEICDLNdIUQO7YBi/g5l99ptD6aw9+vcyPthhhlNPr01fu6ZzLstUMc\nD3YN22raq8//NvSaETHBW3p39K86f3Fogz1DxoncI6cT4jlzeOY1oEDC1+IF\nqoAaK/MdXI0eUdUeUuQdftotDyIJiGAC6QcxmFiVDIEoqjPEcwNQpfZLI5ur\nwBds5uKz5SP6BIkmELQp5F1IiDH4fq3QCCom7fd8SaOHC7RdrAhwPRSwf8H7\nVc71VlVGuIkWkn6C5uI3Csr2ymsAvl75UfiRqZkhaQxQMzc1cWr14qh8sUvq\nNZblq5sp7rNd6/ZPm7N/pqudDFe2mFADQZ7ZlZA4Sv+05ySBZwJlnMRhbJ/C\n//tdIJPOLlj5afRLlrnRUIQBQ1na69rZtotz9LHEKP4CHGx0KyEmmhDGUpR8\nqa754HUGFZqlbYGJLHeQWt52DkHFEflMtrDDFYCpge6Ei4mdMxDNxI5k9pYr\njfBy\r\n=auLH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBYUWd5Ias3tQyccXszWGpkp8HgDhpzFQHjWhDp/CgPsAiEAlSmM81U1nMWMgBIBi9RDTX2o2EZmZsiGmEwM0sU3aRY="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.67_1626985541818_0.6166990808694808"},"_hasShrinkwrap":false},"0.3.0-alpha.68":{"name":"fracture","version":"0.3.0-alpha.68","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.11","turnstile":"6.0.0-alpha.77","vivifyer":"^3.0.0"},"devDependencies":{"destructible":"7.0.0-alpha.67","proof":"^9.0.2","rescue":"7.0.0-alpha.16"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is constructed when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceeding entry to consume the proceeding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"0291f4537eaf0f0e2d7d0f1505bacc16c03dafe7","_id":"fracture@0.3.0-alpha.68","_nodeVersion":"16.5.0","_npmVersion":"7.19.1","dist":{"integrity":"sha512-OuiUHdby7UxkAqVqz1qGuZNZNJC8zWUF6Jf0WzFcUqAhtfCmh21GCxtIknBj+vvng/R2wYTT1TuzwOXXaMqy/w==","shasum":"f9174bf0108cd61f3a19ab788e00e8231f37c3e7","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.68.tgz","fileCount":4,"unpackedSize":33875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+lNeCRA9TVsSAnZWagAATA4P/3l/iRs83WEeanPvaTxy\nLqxsIZu5RR/5/gNyBAa++tnAuORTY9ZvTh4Vcx9Q0q3tTSisg8J/TwapnlLD\nwfzSEywmdJyQLjAtQ6kFa8Tadz3gp42CUwNhLu6xwL19DV7f/48k2KN5FaeP\nSpJ442QbfiZeyuTg+fpbVePO4EWssqd8H7n1/6+6AHMWsyBLTpXLkbBYerR2\nuB2reqZd8z9JNpxkv/2HKQPOEgDaLgs8khPSKCNYDQzVP7jxBsk8agz7ZtYC\nVhUM7AFn687h50pCvBvH67Ccu9w2G0Q2NKBpIrsS0pHuMlDKvILK7OdyQVoZ\nUwL/T2AxcoDqHE5vE0yAHxjQPNL9Y1R5CxBDpuTHQR/lL9oGpBN5SopMexJX\ndwdkGE8cnAmUVkUx7NKL2chKgMOCJujCEIB69BWJ2KJVWFnaUaygqUaMZ1ua\nkMHavJMEAXSMX/WOvD7zcoid1VaSEVFgPLQo04FQBQZWHIO6NyH8AneBv2cT\nvBl6MZFs2Iseud3T2TVtVMkq44cmdJ3ECrWYZrGyjHDL7hLHrFWHL+YO1DOT\nQ31fqJRhxXBU9U3JZmw0La5VCP0obL6c/S1bVOArCbmANMCL1jNoJxTgwipE\nMIvgV6E3ZIFKX3BelVa5zoR4cLanW5ElppkikQjoSURCN+7M9uDBRDCCoEEj\nGKIe\r\n=SGen\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBomOS00cuAVGmEpZmPTs0gNFEyUwg07yguJNIpYHkNMAiEA6VNurWQffVKnElBObAOrw4MSQPFVMEYx7Xm+f3rc8BI="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.68_1627018078807_0.3306054427310625"},"_hasShrinkwrap":false},"0.3.0-alpha.69":{"name":"fracture","version":"0.3.0-alpha.69","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.11","turnstile":"6.0.0-alpha.78","vivifyer":"^3.0.0"},"devDependencies":{"destructible":"7.0.0-alpha.67","proof":"^9.0.2","rescue":"7.0.0-alpha.16"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is constructed when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceeding entry to consume the proceeding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"c87cf5fdeb4506be6a0269f38f639b2f374bfcae","_id":"fracture@0.3.0-alpha.69","_nodeVersion":"16.5.0","_npmVersion":"7.19.1","dist":{"integrity":"sha512-6ERiyY1pKrTSx0Q9wUBS4kdWliM6n+/qeNStEgZPrPNjMe/43sZmhPpnwrH0Uo5Fc5vhsXj8GILng0kjj0waeg==","shasum":"6c749214d225dfecfc5601f1691a6dd892521103","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.69.tgz","fileCount":4,"unpackedSize":33875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+lkcCRA9TVsSAnZWagAAsicP/11dxVVzAubTeQccrJE9\n7qvs/rmZ+SWcUDpigCDEFVLgkUrXJ5G+e6tgEeasj3nYRiYV7idDahGMP0Zc\nWIeE82l+wIOpMrhhLg1IbETjfBAOph5MppetpQO858y3S5+WYvaQkT0IGrif\nNncAJGWxPbDDCqxWwVEiU0it+G12Czk9nlHbf+vuAy6g/8PJ0Y541p6ELgO+\nk0uyefP4LFlIozfivfUEoKbu71pc+jBzGMXBNfyO9BOspbMtpslpqwHN/cG7\nY8YBQBkAEpRAc0s6RjiaHiKU6VU7v3HUdlMa1Nnxy7ifZmVy+jDLSvkMViXg\nOLJIRdUPP5AF5aCfTJ3/4psJCukcAooViQzq3umMbVdNGQ4qgJrTd8kEFArq\n5iE6N4TXCtmvx2Ut44WmbghmWIo6dUpEn1NQm62YtVv4hz9t/l55oBkh4BxM\n4Y/HX3Yd96RB0YIHE9+fz3wmE7SmnWHjERn9jAKFxFuH8PcGPODrHKHO8b+c\nFResc+4/HyHi1+U3vd5CzQrC0cKXjxveC/83CK3n1m9fU0Zv1vdO5FtdHGM6\nHpNUJHS5qRAPeV93mDSpuX3Y71tRE6sD8/ZNOI3KBPrEzWzngva7AOZ8iQPq\nN6a0fdzkvVJH9Z8nQQ76xOtvcqZiF34RbYr5wvpID/Z6l75AycvPGeYPt1kg\nIiak\r\n=mPJq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHLlzLOW8AyrEUgMMkcwcGJFoseeORLhdfeJKXwsI9MtAiAGjiZLdwFJuEDofNeIH7GytElAyegcY4RMJ7EALC7RNw=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.69_1627019548647_0.6763730752779782"},"_hasShrinkwrap":false},"0.3.0-alpha.70":{"name":"fracture","version":"0.3.0-alpha.70","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.11","turnstile":"6.0.0-alpha.79","vivifyer":"^3.0.0"},"devDependencies":{"destructible":"7.0.0-alpha.68","proof":"^9.0.2","rescue":"7.0.0-alpha.17"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally looks like this.\n\n```javascript\nrequire('proof')(4, async okay => {\n    The `'fracture'` module exports a single `Fracture` object.\n\n    const Fracture = require('fracture')\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    Fracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\n    is a an `async`/`await` work queue that manages parallel asynchronous call\n    stacks, or strands. You enter work into the work queue providing a worker\n    function and an object of some sort. The queue is consumed by a fixed number of\n    of one or more work loops which run in parallel. They pull work off the queue\n    and call the worker functions.\n\n    Fracture allows a Turnstile to be shared across sub-systems, hence the name\n    \"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\n    conditions you'll face when you use a queue as a concurrency construct.\n\n    Turnstile depends on [Destructible](https://github.com/bigeasy/destructible)\n    manages a tree of asynchronous code paths, or strands as they're called by\n    Destructible. It provides for catching and reporting errors from multiple\n    concurrent `Promise`s as well as `Promise` cancellation.\n\n    To use Fracture you must provide a `Destructible` and `Turnstile`.\n\n    You provide an entry constructor function. It will create a queue of your design\n    entry specific to your application. You provide a worker function that will\n    process the entry.\n\n    Fracture divides work up by keys. When you enqueue work into Fracture it will\n    return an entry constructed by your entry constructor. This is the pending\n    entry, the one you add your work to. It is constructed when you call `enqueue`\n    and there is no entry available for the key. Until the entry is consumed by the\n    worker function `enqeue` will return the same entry. When the entry is consumed\n    by the worker function `enqueue` will create a new entry.\n\n    There can be no entries for a key, a single pending entry for a key, or a\n    working entry and a pending entry. There will only ever be at most two entries\n    for each key in Fracture.\n\n    In order to implement a work queue that has more than two entries, you add an\n    array to the application specific entry and you process each array in your\n    worker function.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // worker function.\n    const gathered = []\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Push work into the queue for a particular key.\n    fracture.enqueue('a', entry => entry.work.push(1))\n\n    // Push more work into the queue for the same key.\n    fracture.enqueue('a', entry => entry.work.push(2))\n\n    // Push work into the queue for a different key.\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    In the example above, `work` is the application specific work queue. When we\n    call `enqueue` a new application specific entry is created.\n\n    Deadlock occurs when an entry in the queue depends on a result of an entry in\n    the queue that precedes it and there are not enough strands available for the\n    proceeding entry to consume the proceeding entry.\n\n    Race conditions are more difficult to describe and are Node.js specific. More on\n    those later.\n\n    A user can specify a number of concurrent strands to run in the application. You\n    create a Turnstile with the specified number of strands. You can then use\n    Fracture to share those strands across multiple sub-systems, each sub-system\n    doing whatever sort of work it needs to do inside the Turnstile.\n\n    If you wanted to make a hard partition between sub-systems so that the user can\n    specify a number of strands for each, you would divide those sub-systems between\n    Turnstiles.\n\n    For example, if you had a wrote database server, you could have a Turnstile for\n    network requests and a Turnstile for file system operations. The user could\n    configure the number of strands for each. You then use Fracture to create work\n    queues, however many you need on each set of strands.\n\n    Above we configured Fracture to last the lifetime of the Turnstile and the\n    Destructible, but a Fracture can end during the life of the program.\n\n    // Import Destructible and Turnstile.\n    const Destructible = require('destructible')\n    const Turnstile = require('turnstile')\n\n    // Create a Destructible and Turnstile.\n    const destructible = new Destructible('fracture')\n    const turnstile = new Turnstile(destructible.durable('turnstile'))\n\n    // Create a Fracture with a Destructible, Turnstile, entry constructor and\n    // work function.\n    const gathered = []\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => {\n            return { work: [] }\n        },\n        worker: async ({ key, value: { work } }) => {\n            gathered.push({ key, work })\n        }\n    })\n\n    // Add work to `fracture`.\n    fracture.enqueue('a', entry => entry.work.push(1))\n    fracture.enqueue('a', entry => entry.work.push(2))\n    fracture.enqueue('b', entry => entry.work.push(3))\n\n    // Destroy the destructible and wait for everything to wind down.\n    await fracture.destructible.destroy().promise\n\n    // We should have gathered all the work into the `gathered` array.\n    okay(gathered, [{\n        work: [ 1, 2 ], key: 'a'\n    }, {\n        work: [ 3 ], key: 'b'\n    }], 'okay')\n\n    // The `destructible` given to `fracture` is destroyed.\n    okay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n    // The root `destructible` is still operational.\n    okay(! destructible.destroyed, 'root destructible operational')\n\n    We'll now pretend we declared a `destructible` and `turnstile` in our examples\n    and that we're reusing them.\n\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        value: () => ({ work: [], entered: false }),\n        worker: async ({ value }) => {\n            value.entered = true\n            for (const timeout of value.work) {\n                await new Promise(resolve => setTimeout(resolve, timeout))\n            }\n        }\n    })\n\n    // Add some \"work\", which is just a timeout duration.\n    let first\n    fracture.enqueue('a', entry => {\n        entry.work.push(50)\n        first = entry\n    })\n\n    // Let's go to the Node.js event loop for a moment so our work queue can\n    // start.\n    await new Promise(resolve => setImmediate(resolve))\n\n    // Now when we enqueue we're going to get a new user object. Our current\n    // object is in the work queue. We cannot add more work to it. We held\n    // on to it just to show that a new user object has been created.\n    let second\n    fracture.enqueue('a', entry => second = entry)\n\n    okay(second !== first, 'new user object created for future work')\n    okay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\n    okay(!second.entered, 'our second user object has not entered the work queue')\n\n    fracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\n    await fracture.destructible.destroy().promise\n\n    Pause is used to pull work out of the queue. It is how we avoid deadlock.\n    Sometimes work must be done across multiple keys. The keys allow us to order our\n    work for a given key. Sometimes work must be done across a given key.\n\n    In a database you might want to write to a database page. If you use the page\n    file name as a key, you can be assured that all your writes will be in order.\n    One write or set of writes after another.\n\n    At some point you might need to merge two database pages. You can queue an\n    operation that will merge the pages, but that operation should wait until any\n    outstanding writes to those pages are written. If you only have one strand and\n    it enters your merge function, and then your merge function waits on outstanding\n    writes to the two merging pages to finish, it will deadlock. There is only one\n    strand so the queued writes to the merging pages will not be able to make\n    progress.\n\n    This is where pause comes into play. The merge operation can pause the writes to\n    the two pages. When it does so it will pull their entries out of the queue and\n    it will process them itself, flushing the writes, then merging the pages. Then\n    when it resumes those entries will get processed but the merge operation will\n    have cleared the entries of writes that it flushed.\n\n    This allows us to have complicated concurrent operations that can run in one or\n    more strands. The underlying Turnstile has many stands and the merge operation\n    pauses the queue for a page that is currently in its flush operation, the pause\n    will block until the operation completes. We know this cannot deadlock. Both the\n    merge operation and the flush operation have been assigned an available strand\n    are both capable of making progress even though one is waiting on the other.\n\n    We can still get deadlock the old fashioned way. If the merge operation pauses\n    the flush operation and the flush operation pauses the merge operation, that is\n    going to deadlock eventually. Otherwise, you don't have to concern yourself with\n    a deadlock due to resource starvation, i.e. there not enough strands to handle a\n    fan-out of work.\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n    const fracture = new Fracture(destructible.ephemeral('fracture'), {\n        turnstile: turnstile,\n        work: () => ({ entered: false, number: 0 }),\n        worker: async ({ key, value, pause }) => {\n            /*\n            switch (key) {\n            case 'a': {\n                    const b = await pause('b')\n                    okay(b.entries, [], 'paused b')\n                    b.resume()\n                }\n                break\n            case 'b': {\n                    await pause('a')\n                    okay(a.entries, [], 'paused a')\n                    // auto-resume\n                }\n                break\n            }\n            */\n            value.entered = true\n        }\n    })\n    //\n\n    // Add some work, take note of the user object.\n\n    //\n    const willPause = fracture.enqueue('a')\n    willPause.value.number = 7\n    //\n\n    // Pause immediately. We will get a pause object with an `entries`\n    // property. The entries property will always have two user work\n    // entries.\n\n    //\n    const pause = await fracture.pause('a')\n    okay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n    //\n\n    //\n\n    // Using a different key, we can add work to the queue and it will make\n    // progress. We are not blocking the queue with our pause.\n\n    //\n    const unblocked = fracture.enqueue('b').value\n    await new Promise(resolve => setImmediate(resolve))\n    okay(unblocked.entered, 'pausing does not block the queue')\n    //\n\n    // We now resume our paused work.\n\n    //\n    pause.resume()\n    //\n\n    // If we wait for our Fracture to drain we will see that our paused work\n    // was completed.\n    await fracture.destructible.destroy().promise\n\n    okay(willPause.value.entered, 'paused work was resumed')\n\n    function latch () {\n        let capture\n        return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n    }\n    //\n\n    // A very basic user object that just marks that the work entered the\n    // work function.\n\n    //\n    let sum = 0\n\n    const parallel = destructible.ephemeral('parallel')\n    const turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\n    const fracture = new Fracture(parallel.durable('fracture'), {\n        turnstile: turnstile,\n        value: () => ({\n            entered: latch(), block: null, work: 0\n        }),\n        worker: async ({ key, value }) => {\n            value.entered.resolve()\n            if (value.block != null) {\n                await value.block.promise\n            }\n            value.entered = true\n            if (key == 'a') {\n                const pause = await fracture.pause('b')\n                for (const entry in pause.entries) {\n                    sum += entry.work\n                    entry.work = 0\n                }\n                pause.resume()\n            }\n            sum += value.work\n        }\n    })\n\n    const a = fracture.enqueue('a')\n    const b = fracture.enqueue('b')\n\n    a.work = 1\n    a.block = latch()\n    b.work = 2\n    b.block = latch()\n\n    await a.entered.promise\n    await b.entered.promise\n\n    fracture.enqueue('b').work = 3\n\n    a.block.resolve()\n    await 1\n    b.block.resolve()\n\n    // Proceed with an orderly shutdown.\n\n    //\n    await parallel.destroy().promise\n\n    Deadlock can also be resolved by the caller pausing itself.\n\n    const fracture = new Fracture(destructible.durable('fracture'), {\n        turnstile: turnstile,\n        entry: () => ({\n            latch: latch(), value: null\n        }),\n        worker: async ({ key, value, promise }) => {\n            switch (key) {\n            case 'calculate': {\n                    if (promise == null) {\n                        const entry = fracture.enqueue(value.method)\n                        entry.value = value.value\n                        return () => entry.latch.promise\n                    }\n                    value.latch.resolve(await promise)\n                }\n                break\n            case 'increment': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            case 'decrement': {\n                    value.latch.resolve(value.value + 1)\n                }\n                break\n            }\n        }\n    })\n    const entry = fracture.enqueue('calculate')\n    entry.value = 1\n    entry.method = 'increment'\n    okay(await entry.latch.promise, 2, 'continuation')\n    await fracture.destructible.destroy().promise\n})\n```\n","readmeFilename":"README.md","gitHead":"be28ea0016967ce1e3eafca67bcad3c804e4cc9f","_id":"fracture@0.3.0-alpha.70","_nodeVersion":"16.4.2","_npmVersion":"7.18.1","dist":{"integrity":"sha512-ly2bCFihOejWD62Atxtd89JSuNWSrWpbsTyXgUsY/VOLMpniQs92cZ3LF1oBHsbqA4ogLf4Pcys+XchFI5kdAA==","shasum":"99f1efeb993e9ec4eb6d6ef3413e4232b93cf859","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.70.tgz","fileCount":4,"unpackedSize":33875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhCQ0pCRA9TVsSAnZWagAAJv8P/RQBDRq/jE3R9ihQN5TR\nZtLhshfxqlfJNrlwTDJ6vqFW2TpIKX3BO1HRPxclTZwEs0eFaPt7lqDEtl4W\n0nJ+g1VuOZvwwUfS7re1HddtVXnQ9ZdjQ8FLRvFir4MpaXMdeb1uEEEynnRo\n9zAxSf+Cz/jfdip8YGFzgfOmZ8Z+nWOUHUf5jNxJu+wrn3GRjjTfGJKfWGiS\nlscNMeuMRtS9h1EkHK5QEW1kzVLxUwkIWbM5tS5mqjjmz7nCGGkhGmG0bHsa\n13s1aKz3r1Hm9ax4q1rCR+dWrtFjC9Hsacfat/Nt2vaBO0MoXyngRCFoSzyZ\nlJAr1Kc+FpuUW7YxdwNspbfVLVOMxkj8hFJyiWRMQXYX4M4f4KDEp9tm3QkE\nrGB+IDTXM69tFaehffAm72SOq9gvZa7Cr3cnqs+uDGUnXG79Uor6MoT/7rUt\nkcggQO1oKJ/bGMWHMzpJuY8RY07ik3FxX5C5m23aa5AtGgSIILnf3/0nexhT\nxVbAQgvM4Yq5M0SBM4/930rioxn07Rq17CafsrOX+IUPwBBVy/YJSQoO1hv8\n+fv+m7kkd2w9tXo8SU98RoYEcIR65QWNVEPTNaJKU0qdu1lgk86QP1PAxNF9\nFXSIjAj13J/LUzdNposylQyssnnc7gZtpI9FRNzoAJhYRcIjZ/paH5AElqXD\nNPO3\r\n=y1aB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCv8+ZOUdpONemmPk46WX5nXymshFlvLJlU4hS7q3yMsgIgHD8xyQR902qPTI4IwU6fy4KxWwG3ghv8BxFBat7bn+o="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.70_1627983145421_0.41100903680571377"},"_hasShrinkwrap":false},"0.3.0-alpha.71":{"name":"fracture","version":"0.3.0-alpha.71","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.11","turnstile":"6.0.0-alpha.80","vivifyer":"^3.0.0"},"devDependencies":{"destructible":"7.0.0-alpha.69","proof":"^9.0.2","rescue":"7.0.0-alpha.18"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally 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\nThe `'fracture'` module exports a single `Fracture` object.\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is constructed when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    value: () => {\n        return { work: [] }\n    },\n    worker: async ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue('a', entry => entry.work.push(1))\n\n// Push more work into the queue for the same key.\nfracture.enqueue('a', entry => entry.work.push(2))\n\n// Push work into the queue for a different key.\nfracture.enqueue('b', entry => entry.work.push(3))\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceeding entry to consume the proceeding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    value: () => {\n        return { work: [] }\n    },\n    worker: async ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue('a', entry => entry.work.push(1))\nfracture.enqueue('a', entry => entry.work.push(2))\nfracture.enqueue('b', entry => entry.work.push(3))\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    value: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nlet first\nfracture.enqueue('a', entry => {\n    entry.work.push(50)\n    first = entry\n})\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nlet second\nfracture.enqueue('a', entry => second = entry)\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nfracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\nawait fracture.destructible.destroy().promise\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    work: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.value.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b').value\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused work.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.value.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    value: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"85aaadc73b15b58749806a1927b50c9c5d2c20ea","_id":"fracture@0.3.0-alpha.71","_nodeVersion":"16.4.2","_npmVersion":"7.18.1","dist":{"integrity":"sha512-4bz9n6Nv2evuvPryicAAEo1ETLdbiBiuZIXuk132TAMNiyeA22UloEEZvth2s7mBk/0fXlZ4zpzyyL+6eQIRPQ==","shasum":"4a420cf1e80bc1a39d4c142be8a4535c9e3ed3c0","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.71.tgz","fileCount":4,"unpackedSize":32698,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDHRQ4NxFNPLEr61eLIEM4uenHH0obbOAjjClFaudaycgIhAL6U+jsa+/4h/M6NlFv7sMdgQ9eKbj9Brme5VdnjXxiI"}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.71_1632162914638_0.43889312576827755"},"_hasShrinkwrap":false},"0.3.0-alpha.72":{"name":"fracture","version":"0.3.0-alpha.72","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.12","turnstile":"6.0.0-alpha.81","vivifyer":"^3.0.1"},"devDependencies":{"destructible":"7.0.0-alpha.70","proof":"^9.0.2","rescue":"7.0.0-alpha.18"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n```text\nnpm install fracture\n```\n\nThis `README.md` is also a unit test using the Proof unit test framework. We'll\nuse the Proof `okay` function to assert out statements in the readme. A Proof\nunit test generally 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\nThe `'fracture'` module exports a single `Fracture` object.\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is constructed when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    value: () => {\n        return { work: [] }\n    },\n    worker: async ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue('a', entry => entry.work.push(1))\n\n// Push more work into the queue for the same key.\nfracture.enqueue('a', entry => entry.work.push(2))\n\n// Push work into the queue for a different key.\nfracture.enqueue('b', entry => entry.work.push(3))\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceeding entry to consume the proceeding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    value: () => {\n        return { work: [] }\n    },\n    worker: async ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue('a', entry => entry.work.push(1))\nfracture.enqueue('a', entry => entry.work.push(2))\nfracture.enqueue('b', entry => entry.work.push(3))\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\nWe'll now pretend we declared a `destructible` and `turnstile` in our examples\nand that we're reusing them.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    value: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nlet first\nfracture.enqueue('a', entry => {\n    entry.work.push(50)\n    first = entry\n})\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nlet second\nfracture.enqueue('a', entry => second = entry)\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nfracture.enqueue('a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\nawait fracture.destructible.destroy().promise\n```\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    work: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.value.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b').value\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused work.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.value.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    value: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"b2d9c371a2a82a8d5e81f9debb4ffc2d1cb64610","_id":"fracture@0.3.0-alpha.72","_nodeVersion":"16.4.2","_npmVersion":"7.18.1","dist":{"integrity":"sha512-P+qABbjhn/Yr2iZ9Urs4lxXNwxlJeeP1Oo9Mw1v+1FpbttW3ez6HhGZe0GEW4QlqgGj7D1c3q9blWn8TBcWUMA==","shasum":"8c275374822efa96080e426e60b2597a4ea57a6d","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.72.tgz","fileCount":4,"unpackedSize":32698,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDo0gecpMG7G2Q5LLNSCRed3/fyoss2sA65GT87T+mi/AiAhEt3ca69Qmfq7JGZuJiH3g1un2vyy+81+R7S7FyJAqQ=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.72_1632164107447_0.25519449381135706"},"_hasShrinkwrap":false},"0.3.0-alpha.73":{"name":"fracture","version":"0.3.0-alpha.73","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.12","turnstile":"6.0.0-alpha.82","vivifyer":"^3.0.1"},"devDependencies":{"destructible":"7.0.0-alpha.71","proof":"^9.0.2","rescue":"7.0.0-alpha.18"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n\nFracture installs from NPM.\n\n```\nnpm install fracture\n```\n\n## Living `README.md`\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/fracture.git\ncd fracture\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\n## Overview\n\nThe `'fracture'` module exports a single `Fracture` object.\n\n```javascript\nconst Fracture = require('ascension')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n```\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is constructed when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    value: () => {\n        return { work: [] }\n    },\n    worker: async ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue(Fracture.stack(), 'a', entry => entry.work.push(1))\n\n// Push more work into the queue for the same key.\nfracture.enqueue(Fracture.stack(), 'a', entry => entry.work.push(2))\n\n// Push work into the queue for a different key.\nfracture.enqueue(Fracture.stack(), 'b', entry => entry.work.push(3))\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceeding entry to consume the proceeding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    value: () => {\n        return { work: [] }\n    },\n    worker: async ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue(Fracture.stack(), 'a', entry => entry.work.push(1))\nfracture.enqueue(Fracture.stack(), 'a', entry => entry.work.push(2))\nfracture.enqueue(Fracture.stack(), 'b', entry => entry.work.push(3))\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\n## Queueing\n\nLet's create a our `Destructible` and `Turnstile`.\n\n```\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n```\n\nTODO Should I rename `value` to `entry`? Didn't I already rename it from `entry`\nto `value`?\n\nWe create a `Fracture` that creates a work queue entry with a `work` array and a\nflag to indicate whether the entry has entered the worker function.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    value: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nlet first\nfracture.enqueue(Fracture.stack(), 'a', entry => {\n    entry.work.push(50)\n    first = entry\n})\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nlet second\nfracture.enqueue(Fracture.stack(), 'a', entry => second = entry)\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nfracture.enqueue(Fracture.stack(), 'a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\nawait fracture.destructible.destroy().promise\n```\n\n## Pause\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    work: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.value.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b').value\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused work.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.value.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    value: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"ca5e372bdc2978670304b925a6478d5b0d0a2064","_id":"fracture@0.3.0-alpha.73","_nodeVersion":"16.4.2","_npmVersion":"7.18.1","dist":{"integrity":"sha512-ZXoAOhmOcU78QBc0LZF3OBecfB5ikOW9+gn5TqOqArKvMUjmEfjRBw99VEHklT9rbR/0O/ptzJ8Gio3cI4TRUg==","shasum":"58762a9bbf11dffe5cdef95f4162ca32f8cd2348","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.73.tgz","fileCount":4,"unpackedSize":33625,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICKGiznA/qvnrnVr2umoKMXiOHXPealZMOFmwoANl/ZWAiEAi4nfdViOvtUtUCD73p3HwnUrar0nWcV76EVAX3Fmycg="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.73_1634311524047_0.06261471696464582"},"_hasShrinkwrap":false},"0.3.0-alpha.74":{"name":"fracture","version":"0.3.0-alpha.74","description":"A error-first callback work queue that splits work on a hashed key.","keywords":["queue","events","callback","control","flow","cadence"],"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"homepage":"https://github.com/bigeasy/fracture","bugs":{"url":"https://github.com/bigeasy/fracture/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"dependencies":{"hash.fnv":"^1.0.8","nop":"1.0.0","perhaps":"0.0.13","turnstile":"6.0.0-alpha.83","vivifyer":"^3.0.1"},"devDependencies":{"destructible":"7.0.0-alpha.72","proof":"^9.0.2","rescue":"7.0.0-alpha.19"},"main":"fracture","scripts":{"test":"proof test/*.t.js"},"readme":"[![Actions Status](https://github.com/bigeasy/fracture/workflows/Node%20CI/badge.svg)](https://github.com/bigeasy/fracture/actions)\n[![codecov](https://codecov.io/gh/bigeasy/fracture/branch/master/graph/badge.svg)](https://codecov.io/gh/bigeasy/fracture)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn `async`/`await` work queue that groups work by key.\n\n| What          | Where                                         |\n| --- | --- |\n| Discussion    | https://github.com/bigeasy/fracture/issues/1  |\n| Documentation | https://bigeasy.github.io/fracture            |\n| Source        | https://github.com/bigeasy/fracture           |\n| Issues        | https://github.com/bigeasy/fracture/issues    |\n| CI            | https://travis-ci.org/bigeasy/fracture        |\n| Coverage:     | https://codecov.io/gh/bigeasy/fracture        |\n| License:      | MIT                                           |\n\n\nFracture installs from NPM.\n\n```\nnpm install fracture\n```\n\n## Living `README.md`\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/fracture.git\ncd fracture\nnpm install --no-package-lock --no-save\nnode test/readme.t.js\n```\n\n## Overview\n\nThe `'fracture'` module exports a single `Fracture` object.\n\n```javascript\nconst Fracture = require('ascension')\n```\n\nFracture depends on [Turnstile](https://github.com/bigeasy/turnstile). Turnstile\nis a an `async`/`await` work queue that manages parallel asynchronous call\nstacks, or strands. You enter work into the work queue providing a worker\nfunction and an object of some sort. The queue is consumed by a fixed number of\nof one or more work loops which run in parallel. They pull work off the queue\nand call the worker functions.\n\nFracture allows a Turnstile to be shared across sub-systems, hence the name\n\"Fracture.\" It provides mechanisms to resolve the deadlock issues and race\nconditions you'll face when you use a queue as a concurrency construct.\n\nTurnstile depends on [Destructible](https://github.com/bigeasy/destructible)\nmanages a tree of asynchronous code paths, or strands as they're called by\nDestructible. It provides for catching and reporting errors from multiple\nconcurrent `Promise`s as well as `Promise` cancellation.\n\nTo use Fracture you must provide a `Destructible` and `Turnstile`.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n```\n\nYou provide an entry constructor function. It will create a queue of your design\nentry specific to your application. You provide a worker function that will\nprocess the entry.\n\nFracture divides work up by keys. When you enqueue work into Fracture it will\nreturn an entry constructed by your entry constructor. This is the pending\nentry, the one you add your work to. It is constructed when you call `enqueue`\nand there is no entry available for the key. Until the entry is consumed by the\nworker function `enqeue` will return the same entry. When the entry is consumed\nby the worker function `enqueue` will create a new entry.\n\nThere can be no entries for a key, a single pending entry for a key, or a\nworking entry and a pending entry. There will only ever be at most two entries\nfor each key in Fracture.\n\nIn order to implement a work queue that has more than two entries, you add an\narray to the application specific entry and you process each array in your\nworker function.\n\n```javascript\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// worker function.\nconst gathered = []\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    value: () => {\n        return { work: [] }\n    },\n    worker: async ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Push work into the queue for a particular key.\nfracture.enqueue(Fracture.stack(), 'a', entry => entry.work.push(1))\n\n// Push more work into the queue for the same key.\nfracture.enqueue(Fracture.stack(), 'a', entry => entry.work.push(2))\n\n// Push work into the queue for a different key.\nfracture.enqueue(Fracture.stack(), 'b', entry => entry.work.push(3))\n\n// Destroy the destructible and wait for everything to wind down.\nawait destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n```\n\nIn the example above, `work` is the application specific work queue. When we\ncall `enqueue` a new application specific entry is created.\n\nDeadlock occurs when an entry in the queue depends on a result of an entry in\nthe queue that precedes it and there are not enough strands available for the\nproceeding entry to consume the proceeding entry.\n\nRace conditions are more difficult to describe and are Node.js specific. More on\nthose later.\n\nA user can specify a number of concurrent strands to run in the application. You\ncreate a Turnstile with the specified number of strands. You can then use\nFracture to share those strands across multiple sub-systems, each sub-system\ndoing whatever sort of work it needs to do inside the Turnstile.\n\nIf you wanted to make a hard partition between sub-systems so that the user can\nspecify a number of strands for each, you would divide those sub-systems between\nTurnstiles.\n\nFor example, if you had a wrote database server, you could have a Turnstile for\nnetwork requests and a Turnstile for file system operations. The user could\nconfigure the number of strands for each. You then use Fracture to create work\nqueues, however many you need on each set of strands.\n\nAbove we configured Fracture to last the lifetime of the Turnstile and the\nDestructible, but a Fracture can end during the life of the program.\n\n```javascript\n// Import Destructible and Turnstile.\nconst Destructible = require('destructible')\nconst Turnstile = require('turnstile')\n\n// Create a Destructible and Turnstile.\nconst destructible = new Destructible('fracture')\nconst turnstile = new Turnstile(destructible.durable('turnstile'))\n\n// Create a Fracture with a Destructible, Turnstile, entry constructor and\n// work function.\nconst gathered = []\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    value: () => {\n        return { work: [] }\n    },\n    worker: async ({ key, value: { work } }) => {\n        gathered.push({ key, work })\n    }\n})\n\n// Add work to `fracture`.\nfracture.enqueue(Fracture.stack(), 'a', entry => entry.work.push(1))\nfracture.enqueue(Fracture.stack(), 'a', entry => entry.work.push(2))\nfracture.enqueue(Fracture.stack(), 'b', entry => entry.work.push(3))\n\n// Destroy the destructible and wait for everything to wind down.\nawait fracture.destructible.destroy().promise\n\n// We should have gathered all the work into the `gathered` array.\nokay(gathered, [{\n    work: [ 1, 2 ], key: 'a'\n}, {\n    work: [ 3 ], key: 'b'\n}], 'okay')\n\n// The `destructible` given to `fracture` is destroyed.\nokay(fracture.destructible.destroyed, 'fracture destructible destroyed')\n\n// The root `destructible` is still operational.\nokay(! destructible.destroyed, 'root destructible operational')\n```\n\n## Queueing\n\nLet's create a our `Destructible` and `Turnstile`.\n\n```\nconst destructible = new Destructible($ => $(), 'fracture.t')\nconst turnstile = new Turnstile(destructible.durable($ => $(), 'turnstile'))\n```\n\nTODO Should I rename `value` to `entry`? Didn't I already rename it from `entry`\nto `value`?\n\nWe create a `Fracture` that creates a work queue entry with a `work` array and a\nflag to indicate whether the entry has entered the worker function.\n\n```javascript\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    value: () => ({ work: [], entered: false }),\n    worker: async ({ value }) => {\n        value.entered = true\n        for (const timeout of value.work) {\n            await new Promise(resolve => setTimeout(resolve, timeout))\n        }\n    }\n})\n\n// Add some \"work\", which is just a timeout duration.\nlet first\nfracture.enqueue(Fracture.stack(), 'a', entry => {\n    entry.work.push(50)\n    first = entry\n})\n\n// Let's go to the Node.js event loop for a moment so our work queue can\n// start.\nawait new Promise(resolve => setImmediate(resolve))\n\n// Now when we enqueue we're going to get a new user object. Our current\n// object is in the work queue. We cannot add more work to it. We held\n// on to it just to show that a new user object has been created.\nlet second\nfracture.enqueue(Fracture.stack(), 'a', entry => second = entry)\n\nokay(second !== first, 'new user object created for future work')\nokay(first.entered, 'our first user object has entered the work queue (and could well have left it)')\nokay(!second.entered, 'our second user object has not entered the work queue')\n\nfracture.enqueue(Fracture.stack(), 'a', entry => okay(entry == second, 'we continue to get the same second object until we do something asynchronous'))\n\nawait fracture.destructible.destroy().promise\n```\n\n## Pause\n\nPause is used to pull work out of the queue. It is how we avoid deadlock.\nSometimes work must be done across multiple keys. The keys allow us to order our\nwork for a given key. Sometimes work must be done across a given key.\n\nIn a database you might want to write to a database page. If you use the page\nfile name as a key, you can be assured that all your writes will be in order.\nOne write or set of writes after another.\n\nAt some point you might need to merge two database pages. You can queue an\noperation that will merge the pages, but that operation should wait until any\noutstanding writes to those pages are written. If you only have one strand and\nit enters your merge function, and then your merge function waits on outstanding\nwrites to the two merging pages to finish, it will deadlock. There is only one\nstrand so the queued writes to the merging pages will not be able to make\nprogress.\n\nThis is where pause comes into play. The merge operation can pause the writes to\nthe two pages. When it does so it will pull their entries out of the queue and\nit will process them itself, flushing the writes, then merging the pages. Then\nwhen it resumes those entries will get processed but the merge operation will\nhave cleared the entries of writes that it flushed.\n\nThis allows us to have complicated concurrent operations that can run in one or\nmore strands. The underlying Turnstile has many stands and the merge operation\npauses the queue for a page that is currently in its flush operation, the pause\nwill block until the operation completes. We know this cannot deadlock. Both the\nmerge operation and the flush operation have been assigned an available strand\nare both capable of making progress even though one is waiting on the other.\n\nWe can still get deadlock the old fashioned way. If the merge operation pauses\nthe flush operation and the flush operation pauses the merge operation, that is\ngoing to deadlock eventually. Otherwise, you don't have to concern yourself with\na deadlock due to resource starvation, i.e. there not enough strands to handle a\nfan-out of work.\n\n```javascript\n// A very basic user object that just marks that the work entered the\n// work function.\nconst fracture = new Fracture(destructible.ephemeral('fracture'), {\n    turnstile: turnstile,\n    work: () => ({ entered: false, number: 0 }),\n    worker: async ({ key, value, pause }) => {\n        /*\n        switch (key) {\n        case 'a': {\n                const b = await pause('b')\n                okay(b.entries, [], 'paused b')\n                b.resume()\n            }\n            break\n        case 'b': {\n                await pause('a')\n                okay(a.entries, [], 'paused a')\n                // auto-resume\n            }\n            break\n        }\n        */\n        value.entered = true\n    }\n})\n//\n\n// Add some work, take note of the user object.\n\n//\nconst willPause = fracture.enqueue('a')\nwillPause.value.number = 7\n//\n\n// Pause immediately. We will get a pause object with an `entries`\n// property. The entries property will always have two user work\n// entries.\n\n//\nconst pause = await fracture.pause('a')\nokay(pause.entries[0], { entered: false, number: 7 }, 'first pause work')\n//\n\n//\n\n// Using a different key, we can add work to the queue and it will make\n// progress. We are not blocking the queue with our pause.\n\n//\nconst unblocked = fracture.enqueue('b').value\nawait new Promise(resolve => setImmediate(resolve))\nokay(unblocked.entered, 'pausing does not block the queue')\n//\n\n// We now resume our paused work.\n\n//\npause.resume()\n//\n\n// If we wait for our Fracture to drain we will see that our paused work\n// was completed.\nawait fracture.destructible.destroy().promise\n\nokay(willPause.value.entered, 'paused work was resumed')\n```\n\n```javascript\nfunction latch () {\n    let capture\n    return { promise: new Promise(resolve => capture = { resolve }), ...capture }\n}\n//\n\n// A very basic user object that just marks that the work entered the\n// work function.\n\n//\nlet sum = 0\n\nconst parallel = destructible.ephemeral('parallel')\nconst turnstile = new Turnstile(parallel.durable('turnstile'), { strands: 2 })\nconst fracture = new Fracture(parallel.durable('fracture'), {\n    turnstile: turnstile,\n    value: () => ({\n        entered: latch(), block: null, work: 0\n    }),\n    worker: async ({ key, value }) => {\n        value.entered.resolve()\n        if (value.block != null) {\n            await value.block.promise\n        }\n        value.entered = true\n        if (key == 'a') {\n            const pause = await fracture.pause('b')\n            for (const entry in pause.entries) {\n                sum += entry.work\n                entry.work = 0\n            }\n            pause.resume()\n        }\n        sum += value.work\n    }\n})\n\nconst a = fracture.enqueue('a')\nconst b = fracture.enqueue('b')\n\na.work = 1\na.block = latch()\nb.work = 2\nb.block = latch()\n\nawait a.entered.promise\nawait b.entered.promise\n\nfracture.enqueue('b').work = 3\n\na.block.resolve()\nawait 1\nb.block.resolve()\n\n// Proceed with an orderly shutdown.\n\n//\nawait parallel.destroy().promise\n```\n\nDeadlock can also be resolved by the caller pausing itself.\n\n```javascript\nconst fracture = new Fracture(destructible.durable('fracture'), {\n    turnstile: turnstile,\n    entry: () => ({\n        latch: latch(), value: null\n    }),\n    worker: async ({ key, value, promise }) => {\n        switch (key) {\n        case 'calculate': {\n                if (promise == null) {\n                    const entry = fracture.enqueue(value.method)\n                    entry.value = value.value\n                    return () => entry.latch.promise\n                }\n                value.latch.resolve(await promise)\n            }\n            break\n        case 'increment': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        case 'decrement': {\n                value.latch.resolve(value.value + 1)\n            }\n            break\n        }\n    }\n})\nconst entry = fracture.enqueue('calculate')\nentry.value = 1\nentry.method = 'increment'\nokay(await entry.latch.promise, 2, 'continuation')\nawait fracture.destructible.destroy().promise\n```\n","readmeFilename":"README.md","gitHead":"db406fac5a22d9f3035dc910573e6cec89d8e938","_id":"fracture@0.3.0-alpha.74","_nodeVersion":"12.16.2","_npmVersion":"6.14.4","dist":{"integrity":"sha512-CK6O0NhL0M3SQ/VsBzVPq4Rj3QsVSDAyLGg5e5RbWvyPU7Y5Kc2Iu6fGerIeGRfXK1mOr5WxVMXSO+NZ9Bq4Og==","shasum":"e23a0b0f36a73d0839e744af4daee96698c5490e","tarball":"https://registry.npmjs.org/fracture/-/fracture-0.3.0-alpha.74.tgz","fileCount":4,"unpackedSize":33625,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiAMkSCRA9TVsSAnZWagAAIEgP/ig//FTcZX3eQO7uKmRT\n47sxvMKhXbcMIRZYx7dDgtVQcqRBeQk8HYV7otTCT+dc3e6iPttI9EA5nqt7\nvDkc4w5k2sYnL8aXnq9Ft2qbN/SoVxmyV4bOfbeRMkjKJ5UUje4AcHBoqixs\n62Mo57fsDdnqivLLdmIFIk9yssHv7HK9JYTo22L48DWIdSCilEkN1BmSw6nZ\n95CVLD/0QVKwfiveKweQ8xP+DeTCwoUy38eKfeOMvrEOoKDZwy1BcDXKKXfB\n+ohPWMu3OnyRrfUnB8CjQUZ81lst/6O8jbkgv/3xlv5OZKqgCas+TYkqmc92\ndPzfmwJDLzbZ6Y4sWFO5bprN07ecbQ0BHVLMwvP8vpkDksnum7LPNSwXIbY8\n6+WdRvo4joE7CnXLEIo4EfbBaHt8lWDmc4hk/WsQewlUz+Plh9wnvieHJBRP\nXbG0byb5YwdcnziaMavPPSYPkblemdEXU/VYbkKkcnXqNTjjoEog4toINs7K\nETtfMVeiBb5Ngkc7Itzr2j3w9DskOPH7XxXFh9VnfExkXEden9i6SO3c3YQ5\nE28spW1so+dXNRBgXxslXvSOqnKz/9hAPn0EZRQv0pXHkx6IFR612LNwE3sG\nQtNJniyEo9TiD5aPQjYNO6HIwhgQVeXBieDfdri2rsOxQucq5PaEY8Zcww8Q\n42oy\r\n=HmH4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGD0KWFCBxdsc8Tk3eMYNhErAzwZuld07QkhqPrxQI7TAiApdVwB7ICZ1V1TZqMAHbqrwzWjKgnn8M6wi2Zba68gBQ=="}]},"_npmUser":{"name":"bigeasy","email":"alan@prettyrobots.com"},"directories":{},"maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fracture_0.3.0-alpha.74_1644218642832_0.6891701836799655"},"_hasShrinkwrap":false}},"readme":"","maintainers":[{"name":"bigeasy","email":"alan@prettyrobots.com"}],"time":{"modified":"2022-06-18T03:25:38.454Z","created":"2016-11-26T15:06:25.963Z","0.0.0":"2016-11-26T15:06:25.963Z","0.0.2":"2017-03-20T13:16:02.371Z","0.0.3":"2017-03-20T17:46:47.306Z","0.1.0":"2017-10-26T15:03:13.273Z","0.1.1":"2018-02-15T12:45:22.760Z","0.1.2":"2019-04-05T03:10:24.941Z","0.2.0":"2019-08-25T04:49:02.723Z","0.3.0-alpha.0":"2019-09-21T20:39:53.809Z","0.3.0-alpha.1":"2020-03-20T06:17:18.465Z","0.3.0-alpha.2":"2020-03-20T07:12:11.851Z","0.3.0-alpha.3":"2020-03-21T06:53:22.893Z","0.3.0-alpha.4":"2020-03-21T07:47:00.110Z","0.3.0-alpha.5":"2020-03-21T07:59:53.874Z","0.3.0-alpha.6":"2020-03-21T09:11:50.639Z","0.3.0-alpha.7":"2020-03-22T12:19:24.829Z","0.3.0-alpha.8":"2020-12-20T18:14:50.982Z","0.3.0-alpha.9":"2020-12-21T07:13:53.430Z","0.3.0-alpha.10":"2020-12-21T19:14:49.489Z","0.3.0-alpha.11":"2020-12-22T20:40:02.324Z","0.3.0-alpha.12":"2020-12-23T00:03:35.721Z","0.3.0-alpha.13":"2020-12-23T19:41:27.422Z","0.3.0-alpha.14":"2020-12-24T03:25:19.077Z","0.3.0-alpha.15":"2020-12-24T05:18:48.628Z","0.3.0-alpha.16":"2020-12-24T05:33:59.840Z","0.3.0-alpha.17":"2020-12-24T07:23:18.329Z","0.3.0-alpha.18":"2020-12-24T09:32:24.234Z","0.3.0-alpha.19":"2020-12-24T09:33:21.532Z","0.3.0-alpha.20":"2020-12-24T11:01:05.466Z","0.3.0-alpha.21":"2020-12-25T03:36:27.270Z","0.3.0-alpha.22":"2020-12-26T07:08:32.857Z","0.3.0-alpha.23":"2020-12-30T04:12:58.800Z","0.3.0-alpha.24":"2020-12-31T10:49:26.339Z","0.3.0-alpha.25":"2021-01-02T05:46:02.185Z","0.3.0-alpha.26":"2021-01-02T08:12:10.997Z","0.3.0-alpha.27":"2021-01-03T10:45:38.834Z","0.3.0-alpha.28":"2021-01-05T09:26:51.947Z","0.3.0-alpha.29":"2021-01-08T18:19:13.008Z","0.3.0-alpha.30":"2021-01-10T19:23:54.111Z","0.3.0-alpha.31":"2021-01-11T12:38:29.542Z","0.3.0-alpha.32":"2021-01-12T08:54:15.209Z","0.3.0-alpha.33":"2021-01-14T04:38:26.450Z","0.3.0-alpha.34":"2021-01-16T15:34:44.985Z","0.3.0-alpha.35":"2021-01-17T04:28:42.808Z","0.3.0-alpha.36":"2021-01-17T04:36:02.633Z","0.3.0-alpha.37":"2021-01-17T13:19:13.750Z","0.3.0-alpha.38":"2021-01-17T14:42:28.713Z","0.3.0-alpha.39":"2021-01-19T14:37:06.126Z","0.3.0-alpha.40":"2021-01-19T16:11:28.731Z","0.3.0-alpha.41":"2021-01-20T05:41:03.530Z","0.3.0-alpha.42":"2021-01-21T20:22:55.822Z","0.3.0-alpha.43":"2021-01-23T01:04:28.734Z","0.3.0-alpha.44":"2021-01-23T01:04:49.137Z","0.3.0-alpha.45":"2021-01-23T01:07:24.491Z","0.3.0-alpha.46":"2021-01-23T01:10:33.645Z","0.3.0-alpha.47":"2021-02-13T05:35:02.912Z","0.3.0-alpha.48":"2021-03-02T10:07:09.350Z","0.3.0-alpha.49":"2021-03-03T01:58:54.192Z","0.3.0-alpha.50":"2021-03-03T02:08:48.398Z","0.3.0-alpha.51":"2021-03-03T17:42:22.411Z","0.3.0-alpha.52":"2021-03-07T03:25:11.367Z","0.3.0-alpha.53":"2021-03-13T05:05:21.756Z","0.3.0-alpha.54":"2021-03-13T06:33:49.867Z","0.3.0-alpha.55":"2021-03-13T08:56:27.301Z","0.3.0-alpha.56":"2021-03-13T21:39:42.079Z","0.3.0-alpha.57":"2021-03-14T05:54:13.592Z","0.3.0-alpha.58":"2021-03-14T07:19:22.761Z","0.3.0-alpha.59":"2021-03-14T07:44:35.805Z","0.3.0-alpha.60":"2021-03-14T23:15:53.012Z","0.3.0-alpha.61":"2021-03-14T23:48:48.551Z","0.3.0-alpha.62":"2021-03-23T03:12:02.061Z","0.3.0-alpha.63":"2021-03-27T16:08:14.210Z","0.3.0-alpha.64":"2021-05-11T16:43:49.626Z","0.3.0-alpha.65":"2021-05-12T02:51:43.314Z","0.3.0-alpha.66":"2021-07-22T20:23:57.927Z","0.3.0-alpha.67":"2021-07-22T20:25:41.980Z","0.3.0-alpha.68":"2021-07-23T05:27:58.971Z","0.3.0-alpha.69":"2021-07-23T05:52:28.802Z","0.3.0-alpha.70":"2021-08-03T09:32:25.596Z","0.3.0-alpha.71":"2021-09-20T18:35:14.789Z","0.3.0-alpha.72":"2021-09-20T18:55:07.584Z","0.3.0-alpha.73":"2021-10-15T15:25:24.271Z","0.3.0-alpha.74":"2022-02-07T07:24:02.969Z"},"homepage":"https://github.com/bigeasy/fracture","repository":{"type":"git","url":"git+https://github.com/bigeasy/fracture.git"},"author":{"name":"Alan Gutierrez","email":"alan@prettyrobots.com"},"license":"MIT","readmeFilename":"","keywords":["queue","events","callback","control","flow","cadence"],"bugs":{"url":"https://github.com/bigeasy/fracture/issues"}}