{"_id":"better-all","_rev":"9-7f94a066a68fc6daea17b2f1a9ef49f7","name":"better-all","dist-tags":{"latest":"0.0.9"},"versions":{"0.0.1":{"name":"better-all","version":"0.0.1","keywords":["promise","async","parallel","dependency","optimization"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"better-all@0.0.1","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"dist":{"shasum":"56fc2a506cd94356a8733d93783082df84d18020","tarball":"https://registry.npmjs.org/better-all/-/better-all-0.0.1.tgz","fileCount":7,"integrity":"sha512-c9HIuKvvo5gU1jqisZNZrbsna5mhyiQBXNTKrw6Kog4Mi2z01b/oRKo1IETzyIc4L0PJZpHI7T1ROLcGKcH/6g==","signatures":[{"sig":"MEUCIGK/f10crn4Y+/WTYyWR/J0SNjQr7RtTDsZOXymwqf8lAiEA7DUrX+PbK94yYjAgLSKPxF6TLTWB6jmQEaQxEAxhiNU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17518},"main":"./dist/es/index.js","type":"module","types":"./dist/es/index.d.ts","module":"./dist/es/index.js","exports":{".":{"types":"./dist/es/index.d.ts","import":"./dist/es/index.js","require":"./dist/index.cjs"}},"gitHead":"fd78524cfa27b75c60a19025065fd13bafac2788","scripts":{"test":"vitest run","build":"bunchee","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"_npmVersion":"11.6.2","description":"Better Promise.all with automatic dependency optimization","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.5","bunchee":"^6.3.1","typescript":"^5.9.3","@types/node":"^22.10.5","@vitest/coverage-v8":"^3.0.5"},"_npmOperationalInternal":{"tmp":"tmp/better-all_0.0.1_1767806897647_0.03356249506176456","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"better-all","version":"0.0.2","keywords":["promise","async","parallel","dependency","optimization"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"better-all@0.0.2","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"dist":{"shasum":"07817512f6dfd64e82c3d2d767e323f6e0f44559","tarball":"https://registry.npmjs.org/better-all/-/better-all-0.0.2.tgz","fileCount":7,"integrity":"sha512-+okGTsDgSLiFjoJXUdJYGJmPuXk4lA1kW9COjOLzK0K0PCeJnhDYELOHnHwn6KUANOPSFE4l/d01FWZmLASRXg==","signatures":[{"sig":"MEQCICGTrKYBv2VqCiHwuUo3bM0x3/7OU2RtFZpdauCtLA1kAiArU9ObLy748dt6bK20mfuSHXKIyXjh2QJD5v/6pbdM+A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17693},"main":"./dist/es/index.js","type":"module","types":"./dist/es/index.d.ts","module":"./dist/es/index.js","exports":{".":{"types":"./dist/es/index.d.ts","import":"./dist/es/index.js","require":"./dist/index.cjs"}},"gitHead":"13000bd8ffb634ab0d09cb6bee1cd71e9f55a947","scripts":{"test":"vitest run","build":"bunchee","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"_npmVersion":"11.6.2","description":"Better Promise.all with automatic dependency optimization","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.5","bunchee":"^6.3.1","typescript":"^5.9.3","@types/node":"^22.10.5","@vitest/coverage-v8":"^3.0.5"},"_npmOperationalInternal":{"tmp":"tmp/better-all_0.0.2_1768248531740_0.7293611356998164","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"better-all","version":"0.0.3","keywords":["promise","async","parallel","dependency","optimization"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"better-all@0.0.3","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"dist":{"shasum":"300db0ff2bf20d173fd369cbd6279d3660a07a0d","tarball":"https://registry.npmjs.org/better-all/-/better-all-0.0.3.tgz","fileCount":7,"integrity":"sha512-k2JpHj8DJvRMnoGgAhmjs784q15eTLu5uATS5YAg0HTL+JlnAger4Hzdr7+//mwnhASuDIY3dCF49Fs/Q5dgTQ==","signatures":[{"sig":"MEUCIEp0FLRnPuay/ylGWFoxNb5LKps5+RWUxvKKszT+Q/8BAiEAwYzefVPoRr7zV/PofwgB64X7Q+xDfhbYms7+tG5n77s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27030},"main":"./dist/es/index.js","type":"module","types":"./dist/es/index.d.ts","module":"./dist/es/index.js","exports":{".":{"types":"./dist/es/index.d.ts","import":"./dist/es/index.js","require":"./dist/index.cjs"}},"gitHead":"f831a1c6e212d4d599a3427cf8f0d1c6b164698f","scripts":{"test":"vitest run","build":"bunchee","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"_npmVersion":"11.6.2","description":"Better Promise.all with automatic dependency optimization","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.5","bunchee":"^6.3.1","typescript":"^5.9.3","@types/node":"^22.10.5","@vitest/coverage-v8":"^3.0.5"},"_npmOperationalInternal":{"tmp":"tmp/better-all_0.0.3_1768327983602_0.09028334668452564","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"better-all","version":"0.0.4","keywords":["promise","async","parallel","dependency","optimization"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"better-all@0.0.4","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"dist":{"shasum":"351a3b634c8dab937e7e3c885248e19cdaf0eb63","tarball":"https://registry.npmjs.org/better-all/-/better-all-0.0.4.tgz","fileCount":7,"integrity":"sha512-UQEgOHUBEhP8G0Th3Lrc9Kpdd37J6rEXpiYbjGefZWQlwK8swtRTsWMBhqJd+M5ZaJwyvFg3riShpJEkJy2sGQ==","signatures":[{"sig":"MEUCIQD8wqrhSJp+CGnqNrWz+mu30y3Ab2UBqh7Ua8qUDBF+wAIgQ2l+k28KCnnkK9ZNkq/sPqgrbBKN9eKeh4j7oU35vDs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54973},"main":"./dist/es/index.js","type":"module","types":"./dist/es/index.d.ts","module":"./dist/es/index.js","exports":{".":{"types":"./dist/es/index.d.ts","import":"./dist/es/index.js","require":"./dist/index.cjs"}},"gitHead":"0e575ae0c3b0f16504c56fb7da01cbd044a3bbff","scripts":{"test":"vitest run","build":"bunchee","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"_npmVersion":"11.6.2","description":"Better Promise.all with automatic dependency optimization","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.5","bunchee":"^6.3.1","typescript":"^5.9.3","@types/node":"^22.10.5","@vitest/coverage-v8":"^3.0.5"},"_npmOperationalInternal":{"tmp":"tmp/better-all_0.0.4_1768930944680_0.6269037892864848","host":"s3://npm-registry-packages-npm-production"}},"0.0.5":{"name":"better-all","version":"0.0.5","keywords":["promise","async","parallel","dependency","optimization"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"better-all@0.0.5","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"dist":{"shasum":"e0fa09f2772c85f31737c760d3e1d615ef8eb269","tarball":"https://registry.npmjs.org/better-all/-/better-all-0.0.5.tgz","fileCount":7,"integrity":"sha512-m5T7MNB8Zyu1klrjaZIKBVfNHwnNkBB0/u+iy5bojdpGBkJMtswq83CzZOC1eMHkRCF+1D8Nsu9D/Zv8hea+zg==","signatures":[{"sig":"MEUCIFiwt92012kGt2EZg3j/hAgQccd7mftK0M6sq8mRYvpbAiEA2Rv4RAH9mMt9reokuAYBSH5HhdG3Pt8tLYrP+Rtp7Cw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":61981},"main":"./dist/es/index.js","type":"module","types":"./dist/es/index.d.ts","module":"./dist/es/index.js","exports":{".":{"types":"./dist/es/index.d.ts","import":"./dist/es/index.js","require":"./dist/index.cjs"}},"gitHead":"f0a0ce2f35dde2f8323c3b3ea24f2fda56258c5e","scripts":{"test":"vitest run","build":"bunchee","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"_npmVersion":"11.6.2","description":"Better Promise.all with automatic dependency optimization","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.5","bunchee":"^6.3.1","typescript":"^5.9.3","@types/node":"^22.10.5","@vitest/coverage-v8":"^3.0.5"},"_npmOperationalInternal":{"tmp":"tmp/better-all_0.0.5_1769004488974_0.3961107769181573","host":"s3://npm-registry-packages-npm-production"}},"0.0.6":{"name":"better-all","version":"0.0.6","keywords":["promise","async","parallel","dependency","optimization"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"better-all@0.0.6","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"homepage":"https://github.com/shuding/better-all#readme","bugs":{"url":"https://github.com/shuding/better-all/issues"},"dist":{"shasum":"8ae23db190be97d94276b48fe5efd2d2fc8d04fd","tarball":"https://registry.npmjs.org/better-all/-/better-all-0.0.6.tgz","fileCount":7,"integrity":"sha512-AfTEeUP0eySNtm4RUx9nec47+uLbPLmGrBHjGEsvpDsuFYArazsY4G8kiKwlpFPUov8f6PNJ/WjU4PgUld6miw==","signatures":[{"sig":"MEQCIGGMMYJGe6Xlg16eAQab55ueO3TwpYLqsc1DTtzEOgmFAiAECAO8tuZz0oB5KWwQxF7X6/AHNlPe7hTzeczNbpv0lA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62073},"main":"./dist/es/index.js","type":"module","types":"./dist/es/index.d.ts","module":"./dist/es/index.js","exports":{".":{"types":"./dist/es/index.d.ts","import":"./dist/es/index.js","require":"./dist/index.cjs"}},"gitHead":"4cd661000b2336aa64696b4f2a34609cc1b935b0","scripts":{"test":"vitest run","build":"bunchee","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"repository":{"url":"git+https://github.com/shuding/better-all.git","type":"git"},"_npmVersion":"11.6.2","description":"Better Promise.all with automatic dependency optimization","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.5","bunchee":"^6.3.1","typescript":"^5.9.3","@types/node":"^22.10.5","@vitest/coverage-v8":"^3.0.5"},"_npmOperationalInternal":{"tmp":"tmp/better-all_0.0.6_1769784809012_0.5724776947589738","host":"s3://npm-registry-packages-npm-production"}},"0.0.7":{"name":"better-all","version":"0.0.7","keywords":["promise","async","parallel","dependency","optimization"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"better-all@0.0.7","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"homepage":"https://github.com/shuding/better-all#readme","bugs":{"url":"https://github.com/shuding/better-all/issues"},"dist":{"shasum":"a075db5c925ac0089443f794937561c44df2c187","tarball":"https://registry.npmjs.org/better-all/-/better-all-0.0.7.tgz","fileCount":7,"integrity":"sha512-xgdUMWb88Qhz6RtC/PY/7QtR61K/JabCcbKhNYYuXC/IoY3c/bgYdIjVxyTHXR41KP8JQeP+6dg4zH4KOpwJzw==","signatures":[{"sig":"MEUCIQDRaRbLe81bgga9YCZBtxE7qR7w0zdwpH3uKBT5KPTOAAIgOQgnoyJO4lv5FB//F4uIeWgzpZZbV8eKRxpf1sw6+Ew=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":80051},"main":"./dist/es/index.js","type":"module","_from":"file:better-all-0.0.7.tgz","types":"./dist/es/index.d.ts","module":"./dist/es/index.js","exports":{".":{"types":"./dist/es/index.d.ts","import":"./dist/es/index.js","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"bunchee","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"_resolved":"/private/var/folders/fc/2k25gs0945xcslbfrd9w_mtm0000gn/T/5fae4a4c7268cb9523fa69519172c966/better-all-0.0.7.tgz","_integrity":"sha512-xgdUMWb88Qhz6RtC/PY/7QtR61K/JabCcbKhNYYuXC/IoY3c/bgYdIjVxyTHXR41KP8JQeP+6dg4zH4KOpwJzw==","repository":{"url":"git+https://github.com/shuding/better-all.git","type":"git"},"_npmVersion":"11.6.2","description":"Better Promise.all with automatic dependency optimization","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.5","bunchee":"^6.3.1","typescript":"^5.9.3","@types/node":"^22.10.5","@vitest/coverage-v8":"^3.0.5"},"_npmOperationalInternal":{"tmp":"tmp/better-all_0.0.7_1769935518500_0.9254623909796904","host":"s3://npm-registry-packages-npm-production"}},"0.0.8":{"name":"better-all","version":"0.0.8","keywords":["promise","async","parallel","dependency","optimization"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"better-all@0.0.8","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"homepage":"https://github.com/shuding/better-all#readme","bugs":{"url":"https://github.com/shuding/better-all/issues"},"dist":{"shasum":"5bb89a40eda5e47d41596cd4d9bfc05548def6af","tarball":"https://registry.npmjs.org/better-all/-/better-all-0.0.8.tgz","fileCount":7,"integrity":"sha512-d43wsd1dg3CgF4F3wAmd4j0tSEMSRbHJ5pgsKCqI7PCygiciq3deuuEz0XhZCcim+owAouSgikvGMGvFTjFcOg==","signatures":[{"sig":"MEUCIHgp47iVc1/K/bP3MbFTEXn4NtgctNvf58oB3y0CDVBxAiEAvjsQWmxH8K+Lal7ww03DTJyNhiegeNEUc+zoqyfymJo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":80525},"main":"./dist/es/index.js","type":"module","types":"./dist/es/index.d.ts","module":"./dist/es/index.js","exports":{".":{"types":"./dist/es/index.d.ts","import":"./dist/es/index.js","require":"./dist/index.cjs"}},"gitHead":"0b1fac0893840c0ff097b63ededc2a1cb670d9af","scripts":{"test":"vitest run","build":"bunchee","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"repository":{"url":"git+https://github.com/shuding/better-all.git","type":"git"},"_npmVersion":"11.6.2","description":"Better Promise.all with automatic dependency optimization","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.5","bunchee":"^6.3.1","typescript":"^5.9.3","@types/node":"^22.10.5","@vitest/coverage-v8":"^3.0.5"},"_npmOperationalInternal":{"tmp":"tmp/better-all_0.0.8_1784648489740_0.31529507816788827","host":"s3://npm-registry-packages-npm-production"}},"0.0.9":{"name":"better-all","version":"0.0.9","description":"Better Promise.all with automatic dependency optimization","type":"module","main":"./dist/es/index.js","module":"./dist/es/index.js","types":"./dist/es/index.d.ts","author":{"name":"Shu Ding","email":"g@shud.in"},"repository":{"type":"git","url":"git+https://github.com/shuding/better-all.git"},"license":"MIT","exports":{".":{"types":"./dist/es/index.d.ts","import":"./dist/es/index.js","require":"./dist/index.cjs"}},"scripts":{"build":"bunchee","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"keywords":["promise","async","parallel","dependency","optimization"],"devDependencies":{"@types/node":"^22.10.5","@vitest/coverage-v8":"^3.0.5","bunchee":"^6.3.1","typescript":"^5.9.3","vitest":"^3.0.5"},"gitHead":"51b703ababd4ffdd9b61691a5ca3e324b42b9d9a","_id":"better-all@0.0.9","bugs":{"url":"https://github.com/shuding/better-all/issues"},"homepage":"https://github.com/shuding/better-all#readme","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-RTddaak6W8VR6/SBVTzyCGEQBCkm0uMSRfQ+AvMAHxoYlZISjr1bKelXK+lFmde6GFlAFdl78XU9LYYCKw0eyg==","shasum":"7e03e2749463ac9ee970e41ecf5fd05ec0cf5715","tarball":"https://registry.npmjs.org/better-all/-/better-all-0.0.9.tgz","fileCount":7,"unpackedSize":80628,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC7DMOJKs2Zjgunzr7DsaPYxDGyl3DoJTWWgoUTVdVAAwIhAJ044dFCZwBu2qDyJByM75BD61zYrcIIT1SGYRs87Iob"}]},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"directories":{},"maintainers":[{"name":"quietshu","email":"g@shud.in"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/better-all_0.0.9_1787152697579_0.39789219304557344"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-07T17:28:17.646Z","modified":"2026-08-19T15:18:17.895Z","0.0.1":"2026-01-07T17:28:17.800Z","0.0.2":"2026-01-12T20:08:51.861Z","0.0.3":"2026-01-13T18:13:03.751Z","0.0.4":"2026-01-20T17:42:24.818Z","0.0.5":"2026-01-21T14:08:09.110Z","0.0.6":"2026-01-30T14:53:29.161Z","0.0.7":"2026-02-01T08:45:18.626Z","0.0.8":"2026-07-21T15:41:29.873Z","0.0.9":"2026-08-19T15:18:17.759Z"},"bugs":{"url":"https://github.com/shuding/better-all/issues"},"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","homepage":"https://github.com/shuding/better-all#readme","keywords":["promise","async","parallel","dependency","optimization"],"repository":{"type":"git","url":"git+https://github.com/shuding/better-all.git"},"description":"Better Promise.all with automatic dependency optimization","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"readme":"# better-all\n\nPromise.all with automatic dependency optimization and full type inference.\n\n## Why?\n\nWhen you have tasks with dependencies, the common `Promise.all` pattern is sometimes inefficient:\n\n```typescript\n// Common pattern: Sequential execution wastes time\nconst [a, b] = await Promise.all([getA(), getB()])  // a: 1s, b: 10s → takes 10s\nconst c = await getC(a)                             // c: 10s → takes 10s\n// Total: 20 seconds\n```\n\nYou could optimize this manually by parallelizing `b` and `c`:\n\n```typescript\nconst a = await getA()               // a: 1s -> takes 1s\nconst [b, c] = await Promise.all([   // b: 10s, c: 10s -> takes 10s\n  getB(),\n  getC(a)\n])\n// Total: 11 seconds\n```\n\nBut what if the durations of these methods change (i.e. unstable network latency)? Say `getA()` now takes 10 seconds and `getC()` takes 1 second. The previous manual optimization becomes suboptimal again, compared to the naive approach:\n\n```typescript\nconst a = await getA()              // a: 10s -> takes 10s\nconst [b, c] = await Promise.all([  // b: 10s, c: 1s -> takes 10s\n  getB(),\n  getC(a)\n])\n// Total: 20 seconds\n\n// Naive approach:\nconst [a, b] = await Promise.all([getA(), getB()])  // a: 10s, b: 10s → takes 10s\nconst c = await getC(a)                             // c: 1s → takes 1s\n// Total: 11 seconds\n```\n\nTo correctly optimize such cases using `Promise.all`, you'd have to _manually analyze and declare the dependency graph_:\n\n```typescript\nconst [[a, c], b] = await Promise.all([\n  getA().then(a => getC(a).then(c => [a, c])),\n  getB()\n])\n```\n\nThis quickly becomes unmanageable in real-world scenarios with many tasks and complex dependencies, not to mention the loss of readability.\n\nIn real-world application code, there are more downsides of the naive approach and ad-hoc promise adjustments.\n[Give this a read](https://github.com/shuding/better-all/discussions/3) if you are still not convinced.\n\n## Better `Promise.all`\n\n**This library solves it automatically:**\n\n```typescript\nimport { all } from 'better-all'\n\nconst { a, b, c } = await all({\n  async a() { return getA() },               // 1s\n  async b() { return getB() },               // 10s\n  async c() { return getC(await this.$.a) }  // 10s (waits for a)\n})\n// Total: 11 seconds - optimal parallelization!\n```\n\n`all` automatically kicks off all tasks immediately, and when hitting an `await this.$.dependency`, it waits for that specific task to complete.\n\nThe magical `this.$` object gives you access to all other task results as promises, allowing you to express dependencies naturally.\n\nThe library ensures maximal parallelization automatically.\n\n## Installation\n\n```bash\nnpm install better-all\n# or\npnpm add better-all\n# or\nbun add better-all\n# or\nyarn add better-all\n```\n\n## Features\n\n- **Full type inference**: Both results and dependencies are fully typed\n- **Automatic maximal parallelization**: Independent tasks run in parallel\n- **Object-based API**: Minimal cognitive load, easy to read\n- **No hanging promises**: Avoids the uncaught dangling promises problem often seen in manual optimization\n- **Auto-abort on failure**: Cancel remaining tasks when one fails via `this.$signal`\n- **Debug mode with waterfall visualization**: See exactly how tasks execute with ASCII waterfall charts\n- **Early exit support**: Exit flows early when a result is determined\n- **Lightweight**: Minimal dependencies and small bundle size\n\n## API\n\n### `all(tasks, options?)`\n\nExecute tasks with automatic dependency resolution.\n\n- `tasks`: Object of async task functions\n- `options`: Optional configuration object\n  - `debug`: Set to `true` to output a waterfall chart showing task execution timeline\n  - `signal`: An `AbortSignal` to abort all tasks externally\n- Each task function receives:\n  - `this.$` - an object with promises for all task results\n  - `this.$signal` - an `AbortSignal` that aborts when any sibling task fails\n- Returns a promise that resolves to an object with all task results\n- Rejects if any task fails (like `Promise.all`)\n\n### `allSettled(tasks, options?)`\n\nExecute tasks with automatic dependency resolution, returning settled results for all tasks.\n\n- `tasks`: Object of async task functions\n- `options`: Optional configuration object\n  - `debug`: Set to `true` to output a waterfall chart showing task execution timeline\n  - `signal`: An `AbortSignal` to abort all tasks externally\n- Each task function receives:\n  - `this.$` - an object with promises for all task results\n  - `this.$signal` - an `AbortSignal` (only aborts on external signal, not on sibling failure)\n- Returns a promise that resolves to an object with all task results as `{ status: 'fulfilled', value }` or `{ status: 'rejected', reason }`\n- Never rejects - failed tasks are included in the result (like `Promise.allSettled`)\n- If a task depends on a failed task, the dependent task will also fail unless it catches the error\n\n### `flow<R>(tasks, options?)`\n\nExecute tasks with automatic dependency resolution and early exit support.\n\n- **Type parameter `<R>`**: **Required**. Specifies the return type that `$end()` must accept\n- `tasks`: Object of async task functions\n- `options`: Same as `all()` - optional configuration object\n- Each task function receives:\n  - `this.$` - an object with promises for all task results\n  - `this.$signal` - an `AbortSignal` for resource cleanup\n  - `this.$end(value: R)` - function to exit the entire flow early with a return value of type `R`\n- Returns a promise that resolves to `R | undefined`\n  - Returns the value passed to the first `$end()` call\n  - Returns `undefined` if no task calls `$end()`\n- See [Early Exit Flow](#early-exit-flow) for detailed usage\n\n## Examples\n\n### Basic Parallel Execution\n\n```typescript\nconst { a, b, c } = await all({\n  async a() { await sleep(1000); return 1 },\n  async b() { await sleep(1000); return 2 },\n  async c() { await sleep(1000); return 3 }\n})\n\n// All three run in parallel\n// Returns { a: 1, b: 2, c: 3 }\n```\n\n### With Dependencies\n\n```typescript\nconst { user, profile, settings } = await all({\n  async user() { return fetchUser(1) },\n  async profile() { return fetchProfile((await this.$.user).id) },\n  async settings() { return fetchSettings((await this.$.user).id) }\n})\n\n// User runs first, then profile and settings run in parallel\n```\n\n## Type Safety\n\nFull TypeScript support with automatic type inference:\n\n```typescript\nconst result = await all({\n  async num() { return 42 },\n  async str() { return 'hello' },\n  async combined() {\n    const n = await this.$.num  // n: number (auto-inferred!)\n    const s = await this.$.str  // s: string (auto-inferred!)\n    return `${s}: ${n}`\n  }\n})\n\nresult.num       // number\nresult.str       // string\nresult.combined  // string\n```\n\n### Complex Dependency Graph\n\n```typescript\nconst { a, b, c, d, e } = await all({\n  async a() { return 1 },\n  async b() { return 2 },\n  async c() { return (await this.$.a) + 10 },\n  async d() { return (await this.$.b) + 20 },\n  async e() { return (await this.$.c) + (await this.$.d) }\n})\n\n// a and b run in parallel\n// c waits for a, d waits for b (c and d can overlap)\n// e waits for both c and d\n\n// { a: 1, b: 2, c: 11, d: 22, e: 33 }\nconsole.log({ a, b, c, d, e })\n```\n\n### Stepped Dependency Chain\n\nIn this example, the `postsWithAuthor` task calls `await this.$.user` and `await this.$.posts` sequentially but there won't be any actual delays. The `all` function will always kick off all tasks as early as possible, so `posts` was already running while we awaited `this.$.user`:\n\n```typescript\nconst result = await all({\n  async user() {\n    return fetchUser(1)\n  },\n  async posts() {\n    return fetchPosts((await this.$.user).id)\n  },\n  async postsWithAuthor() {\n    const user = await this.$.user\n    console.log(`Fetched user: ${user.name}`)\n    const posts = await this.$.posts\n    return posts.map(post => ({ ...post, author: user.name }))\n  },\n})\n```\n\nThis still gives optimal parallelization.\n\n## Debug Mode\n\nEnable debug mode to visualize task execution with a waterfall chart:\n\n```typescript\nconst result = await all({\n  async config() {\n    await sleep(50)\n    return { apiUrl: 'https://api.example.com' }\n  },\n  async user() {\n    await sleep(120)\n    return { id: 1, name: 'Alice' }\n  },\n  async posts() {\n    const user = await this.$.user\n    await sleep(200)\n    return fetchPosts(user.id)\n  },\n  async profile() {\n    const user = await this.$.user\n    const config = await this.$.config\n    await sleep(80)\n    return fetchProfile(user.id, config.apiUrl)\n  },\n  async analytics() {\n    const posts = await this.$.posts\n    const profile = await this.$.profile\n    await sleep(40)\n    return computeAnalytics(posts, profile)\n  }\n}, { debug: true })\n```\n\nThis outputs an ASCII waterfall chart showing:\n- Task execution timeline\n- Task duration in milliseconds\n- Dependencies for each task\n- Visual representation of parallel vs sequential execution\n\nExample output:\n\n```\n╔════════════════════════════════════════════════════════════════════════════════╗\n║                           Task Execution Waterfall                             ║\n╠════════════════════════════════════════════════════════════════════════════════╣\n║ Total Duration: 364.54ms                                                       ║\n╚════════════════════════════════════════════════════════════════════════════════╝\n\nTask      │ Deps           │ Duration │ Timeline\n──────────┼────────────────┼──────────┼──────────────────────────────────────────────────────────────────\nconfig    │ -              │   51.4ms │ ████████                                                         \nuser      │ -              │  121.4ms │ ████████████████████                                             \nposts     │ user           │  322.6ms │ ░░░░░░░░░░░░░░░░░░░░██████████████████████████████████████       \nprofile   │ user, config   │  202.9ms │ ░░░░░░░░░░░░░░░░░░░░███████████████████                          \nanalytics │ posts, profile │  364.4ms │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░███████\n\nLegend: █ = active (fulfilled), ▓ = active (rejected), ░ = waiting on dependency\n```\n\nThe enhanced waterfall visualization shows:\n- **█** (solid bars) = Active execution time when the task is running its code\n- **░** (light shade) = Waiting time when the task is blocked on a dependency\n- **▓** (dashed bars) = Active execution for tasks that failed\n\nThis makes it easy to:\n- Distinguish between active execution vs waiting on dependencies\n- Identify which tasks are running in parallel\n- See exactly how long each task actively executes vs waits\n- Understand the dependency chain and blocking relationships\n- Spot opportunities for optimization (e.g., tasks with long wait times)\n\n## Error Handling\n\n### With `all()`\n\nErrors propagate to dependent tasks automatically, similar to `Promise.all`:\n\n```typescript\ntry {\n  await all({\n    async a() { throw new Error('Failed') },\n    async b() { return (await this.$.a) + 1 }\n  })\n} catch (err) {\n  console.error(err) // Error: Failed\n}\n```\n\n### With `allSettled()`\n\nAll tasks complete and return their settled state, never rejecting:\n\n```typescript\nconst result = await allSettled({\n  async a() { return 1 },\n  async b() { throw new Error('Task b failed') },\n  async c() { return 3 }\n})\n\n// result.a: { status: 'fulfilled', value: 1 }\n// result.b: { status: 'rejected', reason: Error('Task b failed') }\n// result.c: { status: 'fulfilled', value: 3 }\n\nif (result.a.status === 'fulfilled') {\n  console.log(result.a.value) // 1\n}\n\nif (result.b.status === 'rejected') {\n  console.error(result.b.reason) // Error: Task b failed\n}\n```\n\n### Handling Dependency Failures with `allSettled()`\n\nWhen a task depends on a failed task, it will also fail unless the error is caught:\n\n```typescript\nconst result = await allSettled({\n  async a() { throw new Error('a failed') },\n  async b() {\n    // This will fail because 'a' failed\n    const aValue = await this.$.a\n    return aValue + 10\n  },\n  async c() {\n    // This handles the error and succeeds\n    try {\n      const aValue = await this.$.a\n      return aValue + 10\n    } catch (err) {\n      return 'fallback value'\n    }\n  }\n})\n\n// result.a: { status: 'rejected', reason: Error('a failed') }\n// result.b: { status: 'rejected', reason: Error('a failed') }\n// result.c: { status: 'fulfilled', value: 'fallback value' }\n```\n\n## Abort Signal\n\nWhen a task fails in `all()`, you may want to cancel other running tasks to avoid wasting resources (e.g., API calls, LLM requests).\n\nEach task receives `this.$signal` - an `AbortSignal` that gets aborted when any sibling task fails:\n\n```typescript\nconst result = await all({\n  async fetchUser() {\n    const res = await fetch('/api/user', { signal: this.$signal })\n    return res.json()\n  },\n  async fetchPosts() {\n    // If fetchUser fails, this.$signal will be aborted\n    const res = await fetch('/api/posts', { signal: this.$signal })\n    return res.json()\n  }\n})\n```\n\nYou can also pass an external signal to respect parent abort controllers:\n\n```typescript\nconst controller = new AbortController()\n\nconst result = await all({\n  async a() { return fetchData(this.$signal) },\n  async b() { return fetchMoreData(this.$signal) }\n}, { signal: controller.signal })\n```\n\n**Note:** `allSettled()` does NOT auto-abort on task failure (to preserve its \"wait for all\" behavior), but external signal abort still works.\n\n## Early Exit Flow\n\n`flow` allows you to exit early from complex async flows when a task determines the final result. This is useful for optimization patterns like:\n\n- **Cache checks**: Exit early if cached data is available\n- **Racing operations**: Return the first successful result\n- **Conditional computations**: Skip remaining work based on intermediate results\n\n### `flow(tasks, options?)`\n\nExecute tasks with automatic dependency resolution, but allow any task to end the entire flow early by calling `this.$end(value)`.\n\n**Key behaviors:**\n- All tasks start together in parallel (same as `all()`)\n- First task to call `this.$end(value)` determines the return value\n- After `$end()` is called, other tasks that try to access dependencies will receive errors (caught silently)\n- Real errors (not from `$end`) still propagate to the caller\n- Integrates with `this.$signal` for resource cleanup\n\n### Early Exit from Cache\n\n```typescript\nimport { flow } from 'better-all'\n\nconst data = await flow<YourDataType>({\n  async checkCache() {\n    const cached = await getFromCache('key')\n    if (cached) this.$end(cached)  // Exit early with cached data\n    return null\n  },\n  async fetchFromApi() {\n    const user = await this.$.checkCache  // Will throw if cache hit\n    return await fetchExpensiveData()\n  },\n  async processData() {\n    const apiData = await this.$.fetchFromApi\n    this.$end(transform(apiData))\n  }\n})\n```\n\n### Racing Operations\n\n```typescript\nconst result = await flow<ResponseData>({\n  async fetchFromPrimary() {\n    await sleep(100)\n    const data = await fetch('/api/primary')\n    this.$end(await data.json())\n  },\n  async fetchFromBackup() {\n    await sleep(500)\n    const data = await fetch('/api/backup')\n    this.$end(await data.json())\n  }\n})\n// Returns data from whichever endpoint responds first\n```\n\n### Conditional Early Exit\n\n```typescript\nconst result = await flow<{ error: string } | ProcessedData>({\n  async validateInput() {\n    const isValid = await validate(input)\n    if (!isValid) this.$end({ error: 'Invalid input' })\n    return input\n  },\n  async processData() {\n    const validInput = await this.$.validateInput\n    const processed = await heavyComputation(validInput)\n    this.$end({ success: true, data: processed })\n  }\n})\n```\n\n### Return Type\n\nYou must specify the return type as a type parameter to `flow`:\n\n```typescript\nconst result = await flow<number | string>({\n  async task1() {\n    this.$end(42)  // number\n    return 1\n  },\n  async task2() {\n    this.$end('hello')  // string\n    return 'world'\n  }\n})\n// result: number | string | undefined\n```\n\nTo explicitly allow `undefined` as a return value:\n\n```typescript\n// Explicitly allow undefined\nconst result = await flow<string | undefined>({\n  async task1() {\n    const data = await getData()\n    if (!data) this.$end(undefined)  // ✅ OK: undefined is in the type parameter\n    this.$end(data)\n  }\n})\n// result: string | undefined\n```\n\n**⚠️ Important Notes:**\n- The type parameter `<R>` is **required** and specifies what type `$end()` accepts\n- If you want to call `$end(undefined)`, you must explicitly include `undefined` in `R` (e.g., `flow<undefined>` or `flow<string | undefined>`)\n- If no task calls `this.$end()`, the flow will return `undefined`\n- Once `$end()` is called, subsequent dependency accesses will fail (but are caught silently)\n- `$end()` stops the current task execution (throws internally)\n\n## Development\n\n```bash\npnpm install     # Install dependencies\npnpm test        # Run tests\npnpm build       # Build\n```\n\n## Author\n\n[Shu Ding](https://shud.in)\n\n## License\n\nMIT\n","readmeFilename":"README.md"}