{"_id":"@k2b/sync","_rev":"13-467f4c539a4030e31169eef585c21226","name":"@k2b/sync","dist-tags":{"latest":"6.5.0"},"versions":{"5.7.0-rc.0":{"name":"@k2b/sync","version":"5.7.0-rc.0","keywords":["bun","redis","valkey","ratelimit","mutex","jobs","queue","topic","scheduler","ephemeral","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@5.7.0-rc.0","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"18c82dcc2739768eaebd40c5b91d004cd39c73d0","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-5.7.0-rc.0.tgz","fileCount":51,"integrity":"sha512-b4Avh8B1z1uyalOnlnRn7JeZc403s0xvG2UjGyAoFjR4VdPtns6RbnlE28kgBRSy7p2Cd8ynFd/9tsGH7SwZxw==","signatures":[{"sig":"MEUCIQDTSvdQyUO08cZOPjkQaak5Tw3cPAYjYol1vNRtUynkQQIgIG5NLU8IT7K7uhSB4KIpzFbjguPW+iWvPF2gvglNbZI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":255343},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./browser":{"types":"./browser/index.d.ts","import":"./browser/index.js"}},"_npmUser":{"name":"valentinkolb","email":"mail@valentin-kolb.com"},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"12.0.1","description":"Distributed sync primitives for TypeScript and Bun — ratelimit, mutex, queue, topic, job, scheduler, ephemeral, retry","directories":{},"_nodeVersion":"24.15.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_5.7.0-rc.0_1785087129613_0.7415007112423486","host":"s3://npm-registry-packages-npm-production"}},"5.7.0":{"name":"@k2b/sync","version":"5.7.0","keywords":["bun","redis","valkey","ratelimit","mutex","jobs","queue","topic","scheduler","ephemeral","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@5.7.0","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"f7cd48ad7bcc5efdf8faa06a62ec2e620d214d69","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-5.7.0.tgz","fileCount":51,"integrity":"sha512-AxnG5ht5wpfJ6gjEw7KRES9xkstHOqZysNr/RG8869gs0khxLydSVh2VRZL4D/2w27StYGXsFVTtPWtalxDy/A==","signatures":[{"sig":"MEUCIHEebYluYBihIu6QGTR9pU1BPk9dn39bFeuyAq4ZTpV6AiEAyCUKmoxz96Q2qdWo/cCXJ5VaBzRZKZbuTx6kUoNvVXo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@5.7.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":255290},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./browser":{"types":"./browser/index.d.ts","import":"./browser/index.js"}},"gitHead":"71ccbab1a18b410ab9c4dd482db85d2d1dd8e90a","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"Distributed sync primitives for TypeScript and Bun — ratelimit, mutex, queue, topic, job, scheduler, ephemeral, retry","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_5.7.0_1785087212890_0.4605473036972978","host":"s3://npm-registry-packages-npm-production"}},"5.8.0":{"name":"@k2b/sync","version":"5.8.0","keywords":["bun","redis","valkey","ratelimit","mutex","jobs","queue","topic","pump","scheduler","ephemeral","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@5.8.0","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"2770d158216265340047279d79f2a528b3d0e5fb","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-5.8.0.tgz","fileCount":54,"integrity":"sha512-Ku7Z1RxVMof7oy8qQY9cPQkboG+hImAIqt7661db/fIAHQQ7t/BRCRsdc1o7foMphj6whAUJHMQzQE/5c6a0+Q==","signatures":[{"sig":"MEUCIEx3HcmD+XWSTpWjtanjovo6LCOn6w3knxqMXXaD6IM4AiEAo9U4nrJZUq8K0Q3+x3SogaYv1qEB3PJQq4NbOXPa1+k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@5.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":305700},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./browser":{"types":"./browser/index.d.ts","import":"./browser/index.js"}},"gitHead":"3139cd0c8ad75663386a8fbdb805677563fdbddd","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"Distributed sync primitives for TypeScript and Bun — ratelimit, mutex, queue, topic, job, pump, scheduler, ephemeral, retry","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_5.8.0_1785236945448_0.1886416653003562","host":"s3://npm-registry-packages-npm-production"}},"5.9.0":{"name":"@k2b/sync","version":"5.9.0","keywords":["bun","redis","valkey","ratelimit","mutex","jobs","queue","topic","pump","scheduler","ephemeral","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@5.9.0","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"1435e91286322c50069bbf7397a00efbca0fc895","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-5.9.0.tgz","fileCount":57,"integrity":"sha512-0k4cl3trYKKWJVIX9NPa1yrYkMkcx6DN+jiIWQFNJDOwDHco2ViTdhipRkrjgXO6NO8ytwucSQ32Qo6UMBE/OA==","signatures":[{"sig":"MEQCIDxvcJJuXzT6CeqsQyuZcYnxLIGen2gTPBCSHx2kdvubAiBC9zTEbD1NAn8v9xqhF5N1vCM7YJT316xxRy/KwERLsg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@5.9.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":506009},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./browser":{"types":"./browser/index.d.ts","import":"./browser/index.js"}},"gitHead":"de487a75f1f0b1d48c7b8985bf85e6fa887ca4dd","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"Distributed sync primitives for TypeScript and Bun — ratelimit, mutex, queue, topic, job, pump, scheduler, ephemeral, retry","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_5.9.0_1785509795150_0.9844086093067717","host":"s3://npm-registry-packages-npm-production"}},"5.9.1":{"name":"@k2b/sync","version":"5.9.1","keywords":["bun","redis","valkey","ratelimit","mutex","jobs","queue","topic","pump","scheduler","ephemeral","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@5.9.1","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"ab47e6f799c50cc60f48ca63ab1e6365abafc6f8","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-5.9.1.tgz","fileCount":57,"integrity":"sha512-dBOJ5bD2U57Wyhyr7jgSGPp6oB0KE89YzwTL3ccmUuim4bGPhlpTlEIr9bBOyHhl1D7Y0LCeC/5kxpSlAdrVkw==","signatures":[{"sig":"MEYCIQCAxE29u5bsi4HzNEJRC86kfymqcpB0Ri1nXPMSz6yxiQIhAJl2xQV/xbeWUSiNoh3fhMry8BAqLDhWfkztYfCF8UZc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@5.9.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":507120},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./browser":{"types":"./browser/index.d.ts","import":"./browser/index.js"}},"gitHead":"e2b7f8fdb9616d6c9aeede040431d0eb6dcdc794","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"Distributed sync primitives for TypeScript and Bun — ratelimit, mutex, queue, topic, job, pump, scheduler, ephemeral, retry","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_5.9.1_1785536845633_0.8691154528569836","host":"s3://npm-registry-packages-npm-production"}},"6.0.0":{"name":"@k2b/sync","version":"6.0.0","keywords":["bun","nats","jetstream","queue","topic","jobs","scheduler","mutex","ephemeral","object-store","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@6.0.0","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"93a4c92abedadaa9d696e46a075b4624c49bb775","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-6.0.0.tgz","fileCount":29,"integrity":"sha512-2Ct8Ko+B9ULPncWG0J/jW9DwW9ZRxCubxQdFlrdH9GaBuY3ygtq2sPqFOfDB/rXRwr83TfYRUeV3EdgfeDWLGw==","signatures":[{"sig":"MEQCICQ2t+hkaIc8klrmLwjOKJM1P8c3RP90Wo07lEb8caMVAiBBZArgTmYH9Y9mHuTs8IcZ5W1uIbo9Msf4pJOT0UdMBg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@6.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":232083},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./retry":{"types":"./retry.d.ts","import":"./retry.js"}},"gitHead":"8506072671223cd0a66ecc76dd8fb3f62389c4e8","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"NATS-native distributed sync primitives for TypeScript and Bun — queue, topic, job, pump, scheduler, mutex, ephemeral, object store, retry","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@nats-io/kv":"3.4.0","@nats-io/obj":"3.4.0","@nats-io/jetstream":"3.4.0","@nats-io/nats-core":"3.4.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_6.0.0_1787445002205_0.41405965540486034","host":"s3://npm-registry-packages-npm-production"}},"6.1.0":{"name":"@k2b/sync","version":"6.1.0","keywords":["bun","nats","jetstream","queue","topic","jobs","scheduler","mutex","ephemeral","object-store","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@6.1.0","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"d64401ea8936803e9328b3bb3ce337676d4e4a61","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-6.1.0.tgz","fileCount":29,"integrity":"sha512-2HazFZNUzBgEZGTXYRGWAAPks/6O0FxjZrWLnWRTYx+pW9kWw0ZJZSFGHbtU3FwdSFjLiEYlBd3S8MwzQdGK6w==","signatures":[{"sig":"MEUCIBoBoamCxAwtHqD+C048YfBAn6rVFBARy2GBtYrMoW3nAiEAhCmcpfcNrnZBwH35UA+0MtGlQ3rwxOYzbyKnTwfUn90=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@6.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":239472},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./retry":{"types":"./retry.d.ts","import":"./retry.js"}},"gitHead":"689425ebb5a476481921cc1891d295951a137aa7","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"NATS-native distributed sync primitives for TypeScript and Bun — queue, topic, job, pump, scheduler, mutex, ephemeral, object store, retry","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@nats-io/kv":"3.4.0","@nats-io/obj":"3.4.0","@nats-io/jetstream":"3.4.0","@nats-io/nats-core":"3.4.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_6.1.0_1787599378376_0.6426014624339751","host":"s3://npm-registry-packages-npm-production"}},"6.2.0":{"name":"@k2b/sync","version":"6.2.0","keywords":["bun","nats","jetstream","queue","topic","jobs","scheduler","mutex","ephemeral","object-store","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@6.2.0","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"3b0c9a267b90e95479a4f6fe0c0ddc619e0da04e","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-6.2.0.tgz","fileCount":29,"integrity":"sha512-CpI71sCGOFaG5J4NcT1nb31miSblwU9az7AEX+/iW+eVJ4myIz6iHDwgv/VYKxIEBZz0afek6nuPDGRHQy2ZFw==","signatures":[{"sig":"MEUCIDrrkv41ZxamxVlm0oXtMynPy1ANAuBL3ZQRi68XB538AiEAlVkqgcj6H1uT4OnVdzuwEhtd+S5wScbPB7s2lJMWD98=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@6.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":242680},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./retry":{"types":"./retry.d.ts","import":"./retry.js"}},"gitHead":"f4f998d58dd57bbfafec86adb600478ebcb96d57","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"NATS-native distributed sync primitives for TypeScript and Bun — queue, topic, job, pump, scheduler, mutex, ephemeral, object store, retry","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@nats-io/kv":"3.4.0","@nats-io/obj":"3.4.0","@nats-io/jetstream":"3.4.0","@nats-io/nats-core":"3.4.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_6.2.0_1788798546733_0.2008932414626734","host":"s3://npm-registry-packages-npm-production"}},"6.3.1":{"name":"@k2b/sync","version":"6.3.1","keywords":["bun","nats","jetstream","queue","topic","jobs","scheduler","mutex","ephemeral","object-store","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@6.3.1","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"12470e007f2bee406c40c7e345e806a549047101","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-6.3.1.tgz","fileCount":29,"integrity":"sha512-CPgYkt+BnQau/+NT7HvT4N7nzzvuF6WUi/VK9r8d3lUrzm4yeTHPEpHNcys4n2s/SeASUYytjK15b3NyUIPoLg==","signatures":[{"sig":"MEUCIQDcyv8rhTP8Rl3ZauY0QQCohiZO1V0gwZYY+acVU6mXbAIgAzcUkTPEH1B5eDrCWoewCaUEL7u3cVjuH6sGbiR3VKg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@6.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":260109},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./retry":{"types":"./retry.d.ts","import":"./retry.js"}},"gitHead":"4438fd20494b0fa72c069198e112b5ba6be648e0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"NATS-native distributed sync primitives for TypeScript and Bun — queue, topic, job, pump, scheduler, mutex, ephemeral, object store, retry","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@nats-io/kv":"3.4.0","@nats-io/obj":"3.4.0","@nats-io/jetstream":"3.4.0","@nats-io/nats-core":"3.4.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_6.3.1_1788820270162_0.8067231286890226","host":"s3://npm-registry-packages-npm-production"}},"6.3.2":{"name":"@k2b/sync","version":"6.3.2","keywords":["bun","nats","jetstream","queue","topic","jobs","scheduler","mutex","ephemeral","object-store","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@6.3.2","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"841586573ef61779ff06b2adbf6355cc91f29bc1","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-6.3.2.tgz","fileCount":29,"integrity":"sha512-ZMJG8zUjBZgnS3jFyvXk/9tTD9ZaWRQRQ4FnC18DSE+ZKgBZTNCt8Bhq3Ajg0KoY4CQ6qgQnRvLskmxZEHowYA==","signatures":[{"sig":"MEUCIQDhziGOT88QKEh475QsgLJ2QzQFbM09lEc7TtBnFTZpTAIgbBUK14mDoyi+FV7T6L7be4rqsrUv8thaww8qqTnmne4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@6.3.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":262203},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./retry":{"types":"./retry.d.ts","import":"./retry.js"}},"gitHead":"587def15dee7e973e8277a8bda74a369eb8a7563","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"NATS-native distributed sync primitives for TypeScript and Bun — queue, topic, job, pump, scheduler, mutex, ephemeral, object store, retry","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@nats-io/kv":"3.4.0","@nats-io/obj":"3.4.0","@nats-io/jetstream":"3.4.0","@nats-io/nats-core":"3.4.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_6.3.2_1788826341265_0.6449463766163832","host":"s3://npm-registry-packages-npm-production"}},"6.4.0":{"name":"@k2b/sync","version":"6.4.0","keywords":["bun","nats","jetstream","queue","topic","jobs","scheduler","mutex","ephemeral","object-store","distributed"],"author":{"name":"Valentin Kolb"},"license":"MIT","_id":"@k2b/sync@6.4.0","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"homepage":"https://github.com/k2b-dev/sync#readme","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"d21c08618c3ced8e1feb62f4a7e4650e743d2e4f","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-6.4.0.tgz","fileCount":29,"integrity":"sha512-EkoaVKZ6V34lOx+zk3huGNqpRnWY21s00Z/pFHPCBHDeGUFTtmLspdx8ejyM+rFpPs9z6opIS1Efy8JFCLJjgg==","signatures":[{"sig":"MEYCIQDY4QerVoLdVpmceNZy94nIwA/tkdAPx+xWCWllOvenXQIhAM1AarN3E4I+zZjiF21Q1jrTeLpI7oI4mze9zHiZxi2i","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@6.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":276888},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./retry":{"types":"./retry.d.ts","import":"./retry.js"}},"gitHead":"b4d651f55da0cb9d4f87be638926fac6e849f009","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"NATS-native distributed sync primitives for TypeScript and Bun — queue, topic, job, pump, scheduler, mutex, ephemeral, object store, retry","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@nats-io/kv":"3.4.0","@nats-io/obj":"3.4.0","@nats-io/jetstream":"3.4.0","@nats-io/nats-core":"3.4.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sync_6.4.0_1788881595320_0.32412274659441254","host":"s3://npm-registry-packages-npm-production"}},"6.5.0":{"_id":"@k2b/sync@6.5.0","bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"dist":{"shasum":"4339899f48ac1f12a684340e4eed7708b4330142","tarball":"https://registry.npmjs.org/@k2b/sync/-/sync-6.5.0.tgz","fileCount":29,"integrity":"sha512-6pLVb+emG2z4oRYZtDyzY6gD7nOHmKul0vSjlsoDd9nzpENizLbU2wAh/lQii04rgDehQr2LyUQpwb9oOoTovA==","signatures":[{"sig":"MEQCIGLwgzmnFUwTaPbRjPL48uVIDOoufEhZN3epg3J5WgBtAiBVO7EelwBM0wEVLZkLpOD48q/Cp7zhtsxZfESi7dTanw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHLOwt7OziKce7nDzeXJZakxzNrMtWjWer7HxaJHt46+AiAgF35CggeU4Iv4v/NMbDPsyoLUkTSGhSbZGFXz8b+Afw=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@k2b%2fsync@6.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":284745},"main":"index.js","name":"@k2b/sync","type":"module","types":"index.d.ts","author":{"name":"Valentin Kolb"},"module":"index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./retry":{"types":"./retry.d.ts","import":"./retry.js"}},"gitHead":"0f5134b1b084957cf7058d17e6810e89f77ca885","license":"MIT","version":"6.5.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c78d80d0-d29e-4397-885d-446445560d71"}},"homepage":"https://github.com/k2b-dev/sync#readme","keywords":["bun","nats","jetstream","queue","topic","jobs","scheduler","mutex","ephemeral","object-store","distributed"],"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"_npmVersion":"11.5.1","description":"NATS-native distributed sync primitives for TypeScript and Bun — queue, topic, job, pump, scheduler, mutex, ephemeral, object store, retry","directories":{},"maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"_nodeVersion":"22.23.2","dependencies":{"@nats-io/kv":"3.4.0","@nats-io/obj":"3.4.0","@nats-io/jetstream":"3.4.0","@nats-io/nats-core":"3.4.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sync_6.5.0_1790164686595_0.58075988324032"}}},"time":{"created":"2026-07-26T17:32:09.434Z","modified":"2026-09-23T11:58:07.029Z","5.7.0-rc.0":"2026-07-26T17:32:09.809Z","5.7.0":"2026-07-26T17:33:33.018Z","5.8.0":"2026-07-28T11:09:05.601Z","5.9.0":"2026-07-31T14:56:35.315Z","5.9.1":"2026-07-31T22:27:25.800Z","6.0.0":"2026-08-23T00:30:02.351Z","6.1.0":"2026-08-24T19:22:58.528Z","6.2.0":"2026-09-07T16:29:06.873Z","6.3.1":"2026-09-07T22:31:10.378Z","6.3.2":"2026-09-08T00:12:21.403Z","6.4.0":"2026-09-08T15:33:15.464Z","6.5.0":"2026-09-23T11:58:06.682Z"},"bugs":{"url":"https://github.com/k2b-dev/sync/issues"},"author":{"name":"Valentin Kolb"},"license":"MIT","homepage":"https://github.com/k2b-dev/sync#readme","keywords":["bun","nats","jetstream","queue","topic","jobs","scheduler","mutex","ephemeral","object-store","distributed"],"repository":{"url":"git+https://github.com/k2b-dev/sync.git","type":"git"},"description":"NATS-native distributed sync primitives for TypeScript and Bun — queue, topic, job, pump, scheduler, mutex, ephemeral, object store, retry","maintainers":[{"name":"valentinkolb","email":"mail@valentin-kolb.com"}],"readme":"# @k2b/sync\n\nNATS-native distributed synchronization primitives for TypeScript and [Bun](https://bun.sh).\n\nSync v6 is a small, explicit layer over [NATS](https://nats.io) Core, JetStream, KV, and Object Store. It does not hide the distributed semantics: durable work is at-least-once, handlers must be idempotent, ordering exists only where you declare it, and resource configuration drift fails loudly instead of being patched silently.\n\nNine primitives: **queue**, **topic**, **job**, **pump**, **scheduler**, **mutex**, **ephemeral**, **objectStore**, and the local **retry** helper.\n\n## Requirements\n\n- NATS Server **2.14+** with JetStream enabled (three-node cluster recommended for production)\n- Bun (or Node 22+) with the official NATS.js client for the connection (Sync pins its own `@nats-io/*` dependencies to exactly 3.4.0)\n- An **already connected, caller-owned** `NatsConnection` — Sync never reads environment variables, loads credentials, or creates infrastructure\n\n## Installation\n\n```bash\nbun add @k2b/sync @nats-io/transport-node\n```\n\n## Getting started\n\n```ts\nimport { connect } from \"@nats-io/transport-node\";\nimport { createSync } from \"@k2b/sync\";\n\nconst connection = await connect({ servers: [\"nats://nats-0:4222\", \"nats://nats-1:4222\", \"nats://nats-2:4222\"] });\n\nconst sync = createSync({\n  connection,\n  namespace: \"cloud-prod\",   // deployment isolation (dev / staging / prod)\n  application: \"notebooks\",  // ownership + diagnostics metadata\n});\n\nconst runs = sync.job<{ runId: string }>({ id: \"workflow-runs\" });\n\nawait sync.ready(); // verifies the server, provisions resources, fails on drift\n```\n\n`createSync()` and the primitive factories perform no I/O. `ready()` verifies the connection and server version, creates missing resources, and compares every existing resource against its declaration — an incompatible difference throws `ResourceDriftError` with a field diff and mutates nothing.\n\nShutdown order:\n\n```ts\nawait sync.drain({ timeoutMs: 30_000 }); // stop pulls, finish handlers, nak leftovers\nawait connection.drain();                // the connection is yours\n```\n\n## The two concurrency knobs\n\nThese have exactly one meaning everywhere:\n\n- **`process({ concurrency: 64 })`** — at most 64 handlers run simultaneously *in this worker handle in this process*. Never a cluster limit.\n- **`delivery.maxInFlight: 512`** — the durable NATS consumer's global unacknowledged-delivery ceiling (`MaxAckPending`), shared by all pods.\n\nFour pods × `concurrency: 64` with `maxInFlight: 512` ⇒ at most `min(256, 512)` running handlers. A dead pod occupies its unacknowledged slots until `ackWaitMs` expires, then the work is redelivered elsewhere.\n\nSync pulls messages only for currently free local slots — there is no hidden prefetch buffer, so unclaimed work stays on the server for other pods.\n\n## Queue\n\nDurable work with at-least-once delivery, competing consumers, delay, retries, and a DLQ.\n\n```ts\nconst emails = sync.queue<{ to: string }>({\n  id: \"emails\",\n  delivery: { ackWaitMs: 30_000, maxAttempts: 5, maxInFlight: 1_000, backoffMs: [1_000, 5_000, 30_000] },\n  retention: { maxAgeMs: 7 * 24 * 3_600_000, maxBytes: 1024 ** 3 },\n});\n\nawait emails.send({ data: { to: \"a@example.com\" }, idempotencyKey: \"welcome:42\" });\nawait emails.send({ data: { to: \"b@example.com\" }, delayMs: 60_000 }); // broker-side delay\n\nconst worker = await emails.process({ concurrency: 8 }, async (message) => {\n  await deliver(message.data);        // resolves → acked; throws → retried, then DLQ\n  await message.heartbeat();          // long handlers reset ackWait\n});\n\nconst dead = await emails.deadLetters.list();\nawait emails.deadLetters.requeue({ messageId: dead[0].messageId, idempotencyKey: \"retry-1\" });\n\n// Admin inspection: bounded oldest-first pages, including deleted-cursor recovery.\nconst page = await emails.deadLetters.page({ limit: 20 });\nconst next = page.nextCursor\n  ? await emails.deadLetters.page({ limit: 20, cursor: page.nextCursor })\n  : null;\nconst first = page.entries[0];\nconst detail = first\n  ? await emails.deadLetters.get({ messageId: first.messageId, streamSequence: first.streamSequence })\n  : null;\n```\n\n- `idempotencyKey` deduplicates within `dedupeWindowMs` (default 2 min), scoped per tenant.\n- `delayMs`/`at` use one-shot NATS message schedules — no consumer slot is occupied while waiting.\n- `reader()` gives manual `ack()` / `retry()` / `deadLetter()` settlement per message.\n- `sendBatch([...])` enqueues up to 1000 messages atomically (all-or-nothing; no dedupe ids — NATS batches exclude them). `pause()`/`resume()` stop and restart global delivery without touching publishers. `send({ ttlMs })` makes work expire if not settled in time.\n- `ordering: { mode: \"partitioned\", partitions: 64 }` hashes `orderingKey` to a stable partition with strictly serial per-partition delivery — including across handler failures: partitioned retries happen in place (the delivery is held with heartbeats through the backoff) so younger messages can never overtake a retrying one. The partition count becomes the global in-flight ceiling; this is for per-aggregate processing, not general fan-out.\n- Retention limits (`maxAgeMs`/`maxBytes`) are a hard loss boundary: NATS forbids reject-new on streams with message schedules, so at the limits the **oldest pending work is dropped**. Size them generously.\n\nQueue, job, topic-consumer, and scheduler pause/resume calls wait for the broker to apply the requested state. Already delivered work can finish. A concurrent control request can prevent confirmation and cause the call to fail.\n\n## Job\n\nThe normal shape for background tasks: a queue plus a **required idempotent key**, retry policy, and bounded fan-out.\n\n```ts\nconst runs = sync.job<{ runId: string }>({ id: \"workflow-runs\" });\n\nawait runs.submit({ key: `run:${runId}`, input: { runId } }); // duplicate keys dedupe (windowed)\nawait runs.submit({ key: \"reindex\", input, coalesce: true }); // ≤1 queued-or-running; key frees on completion\n\nawait runs.submitMany(\n  runIds.map((runId) => ({ key: `run:${runId}`, input: { runId } })),\n  { publishConcurrency: 128, maxPendingBytes: 8 * 1024 * 1024 }, // bounded promises AND bytes\n);\n\nawait runs.process(\n  {\n    concurrency: 64,\n    onError: async ({ context, error }) => {\n      await persistFailure(context.jobId, error);\n      return context.failureCount < 2 ? { action: \"retry\", delayMs: 5_000 } : { action: \"dead_letter\", reason: \"gave up\" };\n    },\n  },\n  async (context) => {\n    await runWorkflow(context.input.runId, { signal: context.signal });\n  },\n);\n```\n\nJobs do not store results or expose `join()` — durable domain status belongs in your database. `submitMany` is not atomic: on failure a `BatchSubmitError` reports the accepted and duplicate counts and prior accepted items stay accepted.\n\n`coalesce: true` keeps the first accepted input for each tenant and key. Both `submit` and `submitMany` use the same claim: duplicates join that job and do not replace its input. If publication fails with an unknown outcome, retrying the submission repairs the original pending job. Handlers still need to be idempotent and call `heartbeat()` during long work.\n\n`context.resubmit({ input, delayMs })` requests a continuation after the handler succeeds. Coalesced continuations retain the key across the handoff and preserve the original ordering key and metadata. Dead-letter requeue also respects an active coalesced key.\n\nAn accepted coalesced message whose claim record is missing (lost or expired) adopts a fresh claim and runs instead of being acknowledged silently; the run is reported as a `redelivery` event with `detail.orphanClaimAdopted`.\n\n**Upgrading existing 6.2.0 deployments:** the coalescing repair (6.2.1+) requires stopping all 6.2.0 producers and workers before starting the corrected code. Do not run old and corrected writers together: old workers can overwrite or delete a newer claim. Existing queued jobs can adopt their legacy claims. A legacy pending claim without a queued message contains no recoverable input; a submission reports `SyncUsageError` instead of inventing a receipt. Reconcile that specific job with application-owned state before clearing its old claim and submitting it again. No streams or claims are automatically reset.\n\n## Topic\n\nA retained event log with four deliberately different reads:\n\n```ts\nconst events = sync.topic<NotebookEvent>({\n  id: \"notebook-events\",\n  retention: { maxAgeMs: 24 * 3_600_000, maxBytes: 256 * 1024 * 1024 },\n});\n\nconst receipt = await events.publish({ data: event, tenantId: workspaceId });\n\n// Optimistic per-tenant event sourcing: append only if nothing was written since.\nawait events.publish({ data: event, tenantId: workspaceId, expectedAfter: receipt.cursor }); // ConflictError on lost races\nawait events.publishBatch({ tenantId: workspaceId, events: [...], expectedAfter: receipt.cursor }); // atomic multi-event append\n\n// 1. live(): core NATS broadcast — best-effort, no replay, every listener sees it.\nfor await (const event of events.live({ tenantId: workspaceId })) notifySockets(event);\n\n// 2. replay(): from a cursor to the head captured at start, then ends.\nfor await (const event of events.replay({ tenantId: workspaceId, after: cursor })) apply(event);\n\n// 3. follow(): like replay but stays open for new events.\nfor await (const event of events.follow({ tenantId: workspaceId, after: cursor })) apply(event);\n\n// 4. process(): named durable consumer — pods with the same name compete,\n//    different names own independent cursors.\nawait events.process({ consumer: \"search-indexer\", concurrency: 4 }, async (event) => index(event));\n```\n\nCursors are opaque and resource-bound (`CursorMismatchError` elsewhere). If a cursor points below the retained window — in `replay()`, `follow()`, or a fresh `process({ start: { after } })` consumer — Sync throws `RetentionGapError` instead of silently skipping; its `resumeAfter` cursor resumes from the first retained event without losing it. `live()` events carry no cursor and are suitable for invalidate-then-read, not as durable acceptance evidence.\n\n`tenantId` on replay/follow is filtered on the server: a tenant-scoped read receives only that tenant's events, however many other tenants share the topic. That makes **one topic with `tenantId` per entity** the recommended shape for per-entity logs (documents, records, mailboxes). JetStream reserves every stream's `maxBytes` (times replicas) against the account, so a topic per entity multiplies the reservation by the number of entities; a shared topic keeps it constant.\n\nGap detection on a shared topic: a limits stream only loses its front (age, bytes, message count). A tenant read reports `RetentionGapError` when the stream no longer retains the position right after your cursor, because Sync cannot tell whether the removed events belonged to your tenant. Other tenants' events between yours are never a gap, and an idle `follow()` keeps its position current while others write. A cursor that is only persisted when *its own tenant* changes therefore goes stale once the window passes it. Keep persisted per-tenant cursors fresh: read `head()` **before** `latestCursor({ tenantId })`; once you have applied the tenant's events up to that latest cursor, you may persist the `head()` cursor instead (the tenant has no events in between). Refresh idle tenants the same way before the retained window reaches their cursor.\n\n`sync.listTopics({ idPrefix })` lists topics of the namespace that exist on the broker (not only the ones declared in this process), and `topic.destroy()` deletes a topic's event and dead-letter streams without provisioning anything. Together they retire old per-entity topics after their events are captured elsewhere.\n\n`deadLetterRetention: { maxAgeMs?, maxBytes? }` sizes the consumer dead-letter stream independently; both default to `retention`. Its `maxBytes` must hold one dead letter (payload limit plus 4 KiB).\n\nTopic DLQ entries can be inspected and deleted through `deadLetters`; opt-in recovery targets only the original consumer.\n\n## Pump\n\nCheckpointed draining of a finite source (imports, backfills, reindexing). The KV run record is the truth; per-item checkpoints mean a crash repeats only ambiguous items.\n\n```ts\nconst reindex = sync.pump<{ mailbox: string }, string, { key: string }>({\n  id: \"mail-reindex\",\n  batchSize: 100,\n  dispatchConcurrency: 16,\n  pull: async ({ input, cursor, limit }) => fetchPage(input.mailbox, cursor, limit),\n  dispatch: async ({ item }) => indexItem(item.key), // must be idempotent by item.key\n});\n\nawait reindex.start({ key: \"mailbox:42\", input: { mailbox: \"42\" } });\nawait reindex.process({ concurrency: 4 });\nconst state = await reindex.get({ key: \"mailbox:42\" }); // queued | running | waiting | completed | failed | canceled\n```\n\nWake-ups are repairable: `process()` reconciles lost wake-ups from KV state on start, and `reconcile()` is callable explicitly.\n\n## Scheduler\n\nNATS 2.14 message schedules are the clock: the broker produces durable ticks **even while every application process is offline**.\n\n```ts\nconst cron = sync.scheduler({\n  id: \"maintenance\",\n  // Tick retention is per schedule (age + count). Global byte limits are\n  // deliberately not configurable: with discard-old they would eventually\n  // evict the broker-side schedule definitions and silently stop the clock.\n  retention: { maxAgeMs: 7 * 24 * 3_600_000, maxTicksPerSchedule: 10_000 },\n});\n\nawait cron.create({\n  id: \"cleanup\",\n  cron: \"0 3 * * *\",            // five-field cron, minute resolution\n  timezone: \"Europe/Berlin\",\n  misfire: \"latest\",             // or \"all\": execute every retained slot\n  process: async (context) => {\n    await cleanup({ signal: context.signal });\n  },\n});\n\nawait cron.process({ concurrency: 4 }); // runs of one schedule never overlap\nawait cron.runNow({ id: \"cleanup\", requestId: \"manual-1\" }); // durably accepted\n```\n\n`misfire: \"latest\"` coalesces ticks that accumulated during downtime and executes only the newest retained slot — the newest accepted slot is never lost. `runNow` returning means the run is durably accepted, not that it started or finished; repeating a `requestId` deduplicates within the 120 s duplicate window (like every other idempotency key in v6). `delete()` cancels the broker schedule and drops its retained ticks; a later `create()` starts fresh.\n\n## Mutex\n\nKV compare-and-set leases with monotonic fencing.\n\n```ts\nconst locks = sync.mutex({ id: \"provider-refresh\", ttlMs: 10_000 });\n\nconst result = await locks.withLock({ resource: \"tenant:42\" }, async (lock) => {\n  // lock.fence is a monotonic bigint — persist and compare it if stale\n  // writes to external systems after lease expiry must be excluded.\n  return refresh(lock.fence);\n});\n```\n\nA lease alone cannot stop an expired owner from writing to PostgreSQL afterwards. Consumers needing strict exclusion compare the `fence` or make effects idempotent.\n\n## Ephemeral\n\nPresence, service registry, and transient state on NATS KV with per-key TTL.\n\n```ts\nconst registry = sync.ephemeral<{ url: string }>({ id: \"services\", ttlMs: 15_000 });\n\nawait registry.upsert({ key: \"api/pod-1\", value: { url } });\nawait registry.touch({ key: \"api/pod-1\" });          // heartbeat: refresh TTL\n\nconst snap = await registry.snapshot({ prefix: \"api/\" });\nfor await (const event of registry.watch({ after: snap.revision })) {\n  // upsert | delete | expire | resync_required\n}\n```\n\nA watch without `after` starts by replaying the current entries as upserts, then streams changes. If the watch revision fell out of history, one explicit `resync_required` event is emitted and the watch ends — take a fresh snapshot; Sync never silently skips ahead. TTLs round up to whole seconds (NATS minimum 1s); entries read back via snapshot/watch report `updatedAt` and omit `expiresAt` (a reader cannot know custom per-key TTLs).\n\n## Object store\n\nExplicit large-artifact storage. Sync **never** auto-offloads oversized payloads — you upload explicitly and pass the returned `ObjectRef` (a plain JSON value) through queues and jobs.\n\n```ts\nconst artifacts = sync.objectStore({\n  id: \"workflow-artifacts\",\n  retention: { maxAgeMs: 7 * 24 * 3_600_000, maxBytes: 100 * 1024 ** 3 },\n  maxObjectBytes: 512 * 1024 ** 2,\n});\n\nconst ref = await artifacts.put({ key: `runs/${runId}/input`, body: readableStream });\nawait runs.submit({ key: runId, input: { runId, artifact: ref } });\n\nconst stored = await artifacts.get(ref); // null if deleted or replaced since\n// get() bounds the wait between body chunks (idleTimeoutMs, default 30 s):\n// an object purged mid-read errors the stream instead of hanging it.\n```\n\nStreaming both ways, digest-verified, byte-limited mid-stream (`ObjectTooLargeError`). References do not pin objects: choose bucket retention larger than your maximum queue residence plus retry window, and `delete()` explicitly when an artifact is no longer shared. Permanent end-user files belong in your application's object storage, not here.\n\n## Retry\n\nA local, transport-free helper — also importable from the browser-safe subpath `@k2b/sync/retry`.\n\n```ts\nimport { retry, expBackoff, isRetryableTransportError } from \"@k2b/sync/retry\";\n\nconst result = await retry({\n  run: async () => fetchThing(),\n  after: async ({ ctx }) => {\n    if (ctx.error && isRetryableTransportError(ctx.error) && ctx.attempt < 5) {\n      ctx.reschedule({ delayMs: ctx.expBackoff() });\n    }\n  },\n});\n```\n\n## Diagnostics\n\n```ts\nsync.health();            // { state, connection, pendingResources, driftedResources, activeWorkers, activeHandlers, droppedEvents }\nawait sync.resources();   // sanitized per-resource summaries (messages, bytes, consumers, DLQ depth, ...)\nfor await (const event of sync.events()) { ... } // bounded structured events; slow readers drop events, never block work\n```\n\nObservers are contained: a throwing or slow observer can never alter transport settlement.\n\n`sync.controls()` returns typed controls for queue, job, and scheduler resources\nand topics whose `process()` has started. Topic controls retain inspect/delete\naccess after the worker stops; direct recovery requires an active handler\nregistered with `recoverDeadLetters: true`. It preserves the original event\nidentity and only invokes that consumer, never republishing the topic. Historical\nDLQ entries without original cursor metadata remain inspect/delete-only. See the\n[topic recovery contract](skills/sync/references/api.md#topic) for timeout and\nat-least-once behavior.\n\nThe inventory includes resources already declared in this process. Reading the inventory performs no I/O and\ndoes not provision resources or start workers. Queue and job controls expose\ntheir existing `deadLetters` store; scheduler controls expose `list()`,\n`runNow()`, and `awaitRun()`.\n\nIdentify controls by `{ namespace, kind, id }`; a queue and job may share an ID.\nRepeated declarations produce one inventory entry. Scheduler inspection reports\n`handlerAvailable` across all local handles of that scheduler, without merging\ntheir handlers or workers. Controls remain discoverable after workers stop.\nThe inventory is process-local, not broker-wide, and supplies no authorization:\napplications must protect any administrative route that exposes it.\n\n## Resource model\n\nUser-provided ids never become raw NATS names. Every resource is identified by `{ namespace, kind, id }`, hashed into stable stream/KV/bucket names (`S6_Q_…`, `KV_S6_E_…`) and lower-case subject tokens (`sync.v6.<ns>.queue.<hash>.t.<tenant>.work`). The full identity, owner, and API version are stamped into resource metadata.\n\n- `owner` defaults to `application`; every application opening a shared resource must declare the same configuration **and** owner.\n- Drift (any semantic difference between declaration and live resource) throws `ResourceDriftError` and never mutates the resource.\n- Two conflicting declarations of one resource in the same process fail before any I/O.\n\n## Semantics you must build on\n\n- Durable delivery (queue, job, durable topic consumers, pump, scheduler) is **at-least-once**. Handlers and sinks must be idempotent.\n- A successful publish/submit means the stream quorum accepted the message — not that a handler ran.\n- A late ack of a delivery that was already redelivered and settled elsewhere is accepted idempotently by NATS; it is not detectable as \"stale\". `StaleDeliveryError` is thrown when an ack cannot be confirmed at all (e.g. the consumer was deleted).\n- Ordering exists only in partitioned queues (per key) and within a single topic reader.\n- Payload limits are enforced locally on the complete encoded envelope (default 128 KiB; ephemeral values 4 KiB) before publish.\n\n## Development\n\n```bash\ndocker compose -f compose.nats.yml up -d --wait  # persistent 3-node NATS 2.14 cluster (ports 14222-14224)\ncd packages/sync\nbun run test         # parallel suite against the real cluster\nbun run test:serial  # fault suite (node restarts) — must run alone\nbun run typecheck\n```\n\n## Migrating from v5\n\nv6 is a hard cut: Redis is gone, `@k2b/sync/browser` is gone, `ratelimit` is gone, and no v5 state is migrated. See [MIGRATION.md](./MIGRATION.md).\n\n## License\n\nMIT\n","readmeFilename":"README.md"}