@backblaze-labs/b2-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +121 -0
- package/LICENSE +21 -0
- package/README.md +664 -0
- package/dist/auth/account-info.d.cts +51 -0
- package/dist/auth/account-info.d.cts.map +1 -0
- package/dist/auth/account-info.d.ts +51 -0
- package/dist/auth/account-info.d.ts.map +1 -0
- package/dist/auth/file.cjs +210 -0
- package/dist/auth/file.cjs.map +1 -0
- package/dist/auth/file.d.cts +157 -0
- package/dist/auth/file.d.cts.map +1 -0
- package/dist/auth/file.d.ts +157 -0
- package/dist/auth/file.d.ts.map +1 -0
- package/dist/auth/file.js +210 -0
- package/dist/auth/file.js.map +1 -0
- package/dist/auth/in-memory.cjs +184 -0
- package/dist/auth/in-memory.cjs.map +1 -0
- package/dist/auth/in-memory.d.cts +147 -0
- package/dist/auth/in-memory.d.cts.map +1 -0
- package/dist/auth/in-memory.d.ts +147 -0
- package/dist/auth/in-memory.d.ts.map +1 -0
- package/dist/auth/in-memory.js +184 -0
- package/dist/auth/in-memory.js.map +1 -0
- package/dist/auth/index.cjs +10 -0
- package/dist/auth/index.cjs.map +1 -0
- package/dist/auth/index.d.cts +6 -0
- package/dist/auth/index.d.cts.map +1 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +10 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/realms.cjs +12 -0
- package/dist/auth/realms.cjs.map +1 -0
- package/dist/auth/realms.d.cts +12 -0
- package/dist/auth/realms.d.cts.map +1 -0
- package/dist/auth/realms.d.ts +12 -0
- package/dist/auth/realms.d.ts.map +1 -0
- package/dist/auth/realms.js +12 -0
- package/dist/auth/realms.js.map +1 -0
- package/dist/auth/upload-url-pool.cjs +52 -0
- package/dist/auth/upload-url-pool.cjs.map +1 -0
- package/dist/auth/upload-url-pool.d.cts +35 -0
- package/dist/auth/upload-url-pool.d.cts.map +1 -0
- package/dist/auth/upload-url-pool.d.ts +35 -0
- package/dist/auth/upload-url-pool.d.ts.map +1 -0
- package/dist/auth/upload-url-pool.js +52 -0
- package/dist/auth/upload-url-pool.js.map +1 -0
- package/dist/bucket.cjs +789 -0
- package/dist/bucket.cjs.map +1 -0
- package/dist/bucket.d.cts +659 -0
- package/dist/bucket.d.cts.map +1 -0
- package/dist/bucket.d.ts +659 -0
- package/dist/bucket.d.ts.map +1 -0
- package/dist/bucket.js +789 -0
- package/dist/bucket.js.map +1 -0
- package/dist/client.cjs +230 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +222 -0
- package/dist/client.d.cts.map +1 -0
- package/dist/client.d.ts +222 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +230 -0
- package/dist/client.js.map +1 -0
- package/dist/copy/large.cjs +74 -0
- package/dist/copy/large.cjs.map +1 -0
- package/dist/copy/large.d.cts +53 -0
- package/dist/copy/large.d.cts.map +1 -0
- package/dist/copy/large.d.ts +53 -0
- package/dist/copy/large.d.ts.map +1 -0
- package/dist/copy/large.js +74 -0
- package/dist/copy/large.js.map +1 -0
- package/dist/download/parallel.cjs +122 -0
- package/dist/download/parallel.cjs.map +1 -0
- package/dist/download/parallel.d.cts +42 -0
- package/dist/download/parallel.d.cts.map +1 -0
- package/dist/download/parallel.d.ts +42 -0
- package/dist/download/parallel.d.ts.map +1 -0
- package/dist/download/parallel.js +122 -0
- package/dist/download/parallel.js.map +1 -0
- package/dist/download/single.cjs +117 -0
- package/dist/download/single.cjs.map +1 -0
- package/dist/download/single.d.cts +152 -0
- package/dist/download/single.d.cts.map +1 -0
- package/dist/download/single.d.ts +152 -0
- package/dist/download/single.d.ts.map +1 -0
- package/dist/download/single.js +117 -0
- package/dist/download/single.js.map +1 -0
- package/dist/errors/index.cjs +268 -0
- package/dist/errors/index.cjs.map +1 -0
- package/dist/errors/index.d.cts +264 -0
- package/dist/errors/index.d.cts.map +1 -0
- package/dist/errors/index.d.ts +264 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +268 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/http/retry.cjs +36 -0
- package/dist/http/retry.cjs.map +1 -0
- package/dist/http/retry.d.cts +34 -0
- package/dist/http/retry.d.cts.map +1 -0
- package/dist/http/retry.d.ts +34 -0
- package/dist/http/retry.d.ts.map +1 -0
- package/dist/http/retry.js +36 -0
- package/dist/http/retry.js.map +1 -0
- package/dist/http/transport.cjs +143 -0
- package/dist/http/transport.cjs.map +1 -0
- package/dist/http/transport.d.cts +122 -0
- package/dist/http/transport.d.cts.map +1 -0
- package/dist/http/transport.d.ts +122 -0
- package/dist/http/transport.d.ts.map +1 -0
- package/dist/http/transport.js +143 -0
- package/dist/http/transport.js.map +1 -0
- package/dist/http/url-guard.cjs +97 -0
- package/dist/http/url-guard.cjs.map +1 -0
- package/dist/http/url-guard.d.cts +88 -0
- package/dist/http/url-guard.d.cts.map +1 -0
- package/dist/http/url-guard.d.ts +88 -0
- package/dist/http/url-guard.d.ts.map +1 -0
- package/dist/http/url-guard.js +97 -0
- package/dist/http/url-guard.js.map +1 -0
- package/dist/http/user-agent.cjs +51 -0
- package/dist/http/user-agent.cjs.map +1 -0
- package/dist/http/user-agent.d.cts +28 -0
- package/dist/http/user-agent.d.cts.map +1 -0
- package/dist/http/user-agent.d.ts +28 -0
- package/dist/http/user-agent.d.ts.map +1 -0
- package/dist/http/user-agent.js +51 -0
- package/dist/http/user-agent.js.map +1 -0
- package/dist/index.cjs +78 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +39 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications/index.cjs +7 -0
- package/dist/notifications/index.cjs.map +1 -0
- package/dist/notifications/index.d.cts +3 -0
- package/dist/notifications/index.d.cts.map +1 -0
- package/dist/notifications/index.d.ts +3 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +7 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/webhook.cjs +106 -0
- package/dist/notifications/webhook.cjs.map +1 -0
- package/dist/notifications/webhook.d.cts +146 -0
- package/dist/notifications/webhook.d.cts.map +1 -0
- package/dist/notifications/webhook.d.ts +146 -0
- package/dist/notifications/webhook.d.ts.map +1 -0
- package/dist/notifications/webhook.js +106 -0
- package/dist/notifications/webhook.js.map +1 -0
- package/dist/object.cjs +212 -0
- package/dist/object.cjs.map +1 -0
- package/dist/object.d.cts +263 -0
- package/dist/object.d.cts.map +1 -0
- package/dist/object.d.ts +263 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/object.js +212 -0
- package/dist/object.js.map +1 -0
- package/dist/package.json.cjs +9 -0
- package/dist/package.json.cjs.map +1 -0
- package/dist/package.json.js +9 -0
- package/dist/package.json.js.map +1 -0
- package/dist/raw/encoding.cjs +47 -0
- package/dist/raw/encoding.cjs.map +1 -0
- package/dist/raw/encoding.d.cts +46 -0
- package/dist/raw/encoding.d.cts.map +1 -0
- package/dist/raw/encoding.d.ts +46 -0
- package/dist/raw/encoding.d.ts.map +1 -0
- package/dist/raw/encoding.js +47 -0
- package/dist/raw/encoding.js.map +1 -0
- package/dist/raw/index.cjs +600 -0
- package/dist/raw/index.cjs.map +1 -0
- package/dist/raw/index.d.cts +381 -0
- package/dist/raw/index.d.cts.map +1 -0
- package/dist/raw/index.d.ts +381 -0
- package/dist/raw/index.d.ts.map +1 -0
- package/dist/raw/index.js +601 -0
- package/dist/raw/index.js.map +1 -0
- package/dist/s3/index.cjs +23 -0
- package/dist/s3/index.cjs.map +1 -0
- package/dist/s3/index.d.cts +74 -0
- package/dist/s3/index.d.cts.map +1 -0
- package/dist/s3/index.d.ts +74 -0
- package/dist/s3/index.d.ts.map +1 -0
- package/dist/s3/index.js +23 -0
- package/dist/s3/index.js.map +1 -0
- package/dist/simulator/capabilities.cjs +53 -0
- package/dist/simulator/capabilities.cjs.map +1 -0
- package/dist/simulator/capabilities.d.cts +15 -0
- package/dist/simulator/capabilities.d.cts.map +1 -0
- package/dist/simulator/capabilities.d.ts +15 -0
- package/dist/simulator/capabilities.d.ts.map +1 -0
- package/dist/simulator/capabilities.js +53 -0
- package/dist/simulator/capabilities.js.map +1 -0
- package/dist/simulator/index.cjs +1559 -0
- package/dist/simulator/index.cjs.map +1 -0
- package/dist/simulator/index.d.cts +495 -0
- package/dist/simulator/index.d.cts.map +1 -0
- package/dist/simulator/index.d.ts +495 -0
- package/dist/simulator/index.d.ts.map +1 -0
- package/dist/simulator/index.js +1560 -0
- package/dist/simulator/index.js.map +1 -0
- package/dist/simulator/validation.cjs +174 -0
- package/dist/simulator/validation.cjs.map +1 -0
- package/dist/simulator/validation.d.cts +104 -0
- package/dist/simulator/validation.d.cts.map +1 -0
- package/dist/simulator/validation.d.ts +104 -0
- package/dist/simulator/validation.d.ts.map +1 -0
- package/dist/simulator/validation.js +174 -0
- package/dist/simulator/validation.js.map +1 -0
- package/dist/streams/collect.cjs +26 -0
- package/dist/streams/collect.cjs.map +1 -0
- package/dist/streams/collect.d.cts +18 -0
- package/dist/streams/collect.d.cts.map +1 -0
- package/dist/streams/collect.d.ts +18 -0
- package/dist/streams/collect.d.ts.map +1 -0
- package/dist/streams/collect.js +26 -0
- package/dist/streams/collect.js.map +1 -0
- package/dist/streams/hash.cjs +123 -0
- package/dist/streams/hash.cjs.map +1 -0
- package/dist/streams/hash.d.cts +42 -0
- package/dist/streams/hash.d.cts.map +1 -0
- package/dist/streams/hash.d.ts +42 -0
- package/dist/streams/hash.d.ts.map +1 -0
- package/dist/streams/hash.js +101 -0
- package/dist/streams/hash.js.map +1 -0
- package/dist/streams/index.cjs +13 -0
- package/dist/streams/index.cjs.map +1 -0
- package/dist/streams/index.d.cts +7 -0
- package/dist/streams/index.d.cts.map +1 -0
- package/dist/streams/index.d.ts +7 -0
- package/dist/streams/index.d.ts.map +1 -0
- package/dist/streams/index.js +13 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams/progress.cjs +47 -0
- package/dist/streams/progress.cjs.map +1 -0
- package/dist/streams/progress.d.cts +53 -0
- package/dist/streams/progress.d.cts.map +1 -0
- package/dist/streams/progress.d.ts +53 -0
- package/dist/streams/progress.d.ts.map +1 -0
- package/dist/streams/progress.js +47 -0
- package/dist/streams/progress.js.map +1 -0
- package/dist/streams/source.cjs +155 -0
- package/dist/streams/source.cjs.map +1 -0
- package/dist/streams/source.d.cts +140 -0
- package/dist/streams/source.d.cts.map +1 -0
- package/dist/streams/source.d.ts +140 -0
- package/dist/streams/source.d.ts.map +1 -0
- package/dist/streams/source.js +155 -0
- package/dist/streams/source.js.map +1 -0
- package/dist/sync/actions/index.cjs +191 -0
- package/dist/sync/actions/index.cjs.map +1 -0
- package/dist/sync/actions/index.d.cts +167 -0
- package/dist/sync/actions/index.d.cts.map +1 -0
- package/dist/sync/actions/index.d.ts +167 -0
- package/dist/sync/actions/index.d.ts.map +1 -0
- package/dist/sync/actions/index.js +191 -0
- package/dist/sync/actions/index.js.map +1 -0
- package/dist/sync/index.cjs +23 -0
- package/dist/sync/index.cjs.map +1 -0
- package/dist/sync/index.d.cts +21 -0
- package/dist/sync/index.d.cts.map +1 -0
- package/dist/sync/index.d.ts +21 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +23 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/pairing.cjs +31 -0
- package/dist/sync/pairing.cjs.map +1 -0
- package/dist/sync/pairing.d.cts +13 -0
- package/dist/sync/pairing.d.cts.map +1 -0
- package/dist/sync/pairing.d.ts +13 -0
- package/dist/sync/pairing.d.ts.map +1 -0
- package/dist/sync/pairing.js +31 -0
- package/dist/sync/pairing.js.map +1 -0
- package/dist/sync/policies/compare.cjs +14 -0
- package/dist/sync/policies/compare.cjs.map +1 -0
- package/dist/sync/policies/compare.d.cts +13 -0
- package/dist/sync/policies/compare.d.cts.map +1 -0
- package/dist/sync/policies/compare.d.ts +13 -0
- package/dist/sync/policies/compare.d.ts.map +1 -0
- package/dist/sync/policies/compare.js +14 -0
- package/dist/sync/policies/compare.js.map +1 -0
- package/dist/sync/policies/index.cjs +74 -0
- package/dist/sync/policies/index.cjs.map +1 -0
- package/dist/sync/policies/index.d.cts +42 -0
- package/dist/sync/policies/index.d.cts.map +1 -0
- package/dist/sync/policies/index.d.ts +42 -0
- package/dist/sync/policies/index.d.ts.map +1 -0
- package/dist/sync/policies/index.js +74 -0
- package/dist/sync/policies/index.js.map +1 -0
- package/dist/sync/scanners/b2.cjs +57 -0
- package/dist/sync/scanners/b2.cjs.map +1 -0
- package/dist/sync/scanners/b2.d.cts +20 -0
- package/dist/sync/scanners/b2.d.cts.map +1 -0
- package/dist/sync/scanners/b2.d.ts +20 -0
- package/dist/sync/scanners/b2.d.ts.map +1 -0
- package/dist/sync/scanners/b2.js +57 -0
- package/dist/sync/scanners/b2.js.map +1 -0
- package/dist/sync/scanners/local.cjs +58 -0
- package/dist/sync/scanners/local.cjs.map +1 -0
- package/dist/sync/scanners/local.d.cts +24 -0
- package/dist/sync/scanners/local.d.cts.map +1 -0
- package/dist/sync/scanners/local.d.ts +24 -0
- package/dist/sync/scanners/local.d.ts.map +1 -0
- package/dist/sync/scanners/local.js +58 -0
- package/dist/sync/scanners/local.js.map +1 -0
- package/dist/sync/synchronizer.cjs +203 -0
- package/dist/sync/synchronizer.cjs.map +1 -0
- package/dist/sync/synchronizer.d.cts +52 -0
- package/dist/sync/synchronizer.d.cts.map +1 -0
- package/dist/sync/synchronizer.d.ts +52 -0
- package/dist/sync/synchronizer.d.ts.map +1 -0
- package/dist/sync/synchronizer.js +181 -0
- package/dist/sync/synchronizer.js.map +1 -0
- package/dist/sync/types.d.cts +121 -0
- package/dist/sync/types.d.cts.map +1 -0
- package/dist/sync/types.d.ts +121 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/types/auth.cjs +58 -0
- package/dist/types/auth.cjs.map +1 -0
- package/dist/types/auth.d.cts +150 -0
- package/dist/types/auth.d.cts.map +1 -0
- package/dist/types/auth.d.ts +150 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/auth.js +58 -0
- package/dist/types/auth.js.map +1 -0
- package/dist/types/bucket.cjs +44 -0
- package/dist/types/bucket.cjs.map +1 -0
- package/dist/types/bucket.d.cts +245 -0
- package/dist/types/bucket.d.cts.map +1 -0
- package/dist/types/bucket.d.ts +245 -0
- package/dist/types/bucket.d.ts.map +1 -0
- package/dist/types/bucket.js +44 -0
- package/dist/types/bucket.js.map +1 -0
- package/dist/types/download.d.cts +110 -0
- package/dist/types/download.d.cts.map +1 -0
- package/dist/types/download.d.ts +110 -0
- package/dist/types/download.d.ts.map +1 -0
- package/dist/types/encryption.cjs +343 -0
- package/dist/types/encryption.cjs.map +1 -0
- package/dist/types/encryption.d.cts +132 -0
- package/dist/types/encryption.d.cts.map +1 -0
- package/dist/types/encryption.d.ts +132 -0
- package/dist/types/encryption.d.ts.map +1 -0
- package/dist/types/encryption.js +321 -0
- package/dist/types/encryption.js.map +1 -0
- package/dist/types/errors.d.cts +16 -0
- package/dist/types/errors.d.cts.map +1 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/file.cjs +23 -0
- package/dist/types/file.cjs.map +1 -0
- package/dist/types/file.d.cts +220 -0
- package/dist/types/file.d.cts.map +1 -0
- package/dist/types/file.d.ts +220 -0
- package/dist/types/file.d.ts.map +1 -0
- package/dist/types/file.js +23 -0
- package/dist/types/file.js.map +1 -0
- package/dist/types/ids.cjs +27 -0
- package/dist/types/ids.cjs.map +1 -0
- package/dist/types/ids.d.cts +84 -0
- package/dist/types/ids.d.cts.map +1 -0
- package/dist/types/ids.d.ts +84 -0
- package/dist/types/ids.d.ts.map +1 -0
- package/dist/types/ids.js +27 -0
- package/dist/types/ids.js.map +1 -0
- package/dist/types/index.d.cts +25 -0
- package/dist/types/index.d.cts.map +1 -0
- package/dist/types/index.d.ts +25 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/key.d.cts +84 -0
- package/dist/types/key.d.cts.map +1 -0
- package/dist/types/key.d.ts +84 -0
- package/dist/types/key.d.ts.map +1 -0
- package/dist/types/lock.cjs +17 -0
- package/dist/types/lock.cjs.map +1 -0
- package/dist/types/lock.d.cts +92 -0
- package/dist/types/lock.d.cts.map +1 -0
- package/dist/types/lock.d.ts +92 -0
- package/dist/types/lock.d.ts.map +1 -0
- package/dist/types/lock.js +17 -0
- package/dist/types/lock.js.map +1 -0
- package/dist/types/notifications.cjs +24 -0
- package/dist/types/notifications.cjs.map +1 -0
- package/dist/types/notifications.d.cts +97 -0
- package/dist/types/notifications.d.cts.map +1 -0
- package/dist/types/notifications.d.ts +97 -0
- package/dist/types/notifications.d.ts.map +1 -0
- package/dist/types/notifications.js +24 -0
- package/dist/types/notifications.js.map +1 -0
- package/dist/types/replication.d.cts +42 -0
- package/dist/types/replication.d.cts.map +1 -0
- package/dist/types/replication.d.ts +42 -0
- package/dist/types/replication.d.ts.map +1 -0
- package/dist/types/upload.d.cts +219 -0
- package/dist/types/upload.d.cts.map +1 -0
- package/dist/types/upload.d.ts +219 -0
- package/dist/types/upload.d.ts.map +1 -0
- package/dist/upload/cancel.cjs +10 -0
- package/dist/upload/cancel.cjs.map +1 -0
- package/dist/upload/cancel.d.cts +23 -0
- package/dist/upload/cancel.d.cts.map +1 -0
- package/dist/upload/cancel.d.ts +23 -0
- package/dist/upload/cancel.d.ts.map +1 -0
- package/dist/upload/cancel.js +10 -0
- package/dist/upload/cancel.js.map +1 -0
- package/dist/upload/concurrency.cjs +54 -0
- package/dist/upload/concurrency.cjs.map +1 -0
- package/dist/upload/concurrency.d.cts +46 -0
- package/dist/upload/concurrency.d.cts.map +1 -0
- package/dist/upload/concurrency.d.ts +46 -0
- package/dist/upload/concurrency.d.ts.map +1 -0
- package/dist/upload/concurrency.js +54 -0
- package/dist/upload/concurrency.js.map +1 -0
- package/dist/upload/large.cjs +222 -0
- package/dist/upload/large.cjs.map +1 -0
- package/dist/upload/large.d.cts +71 -0
- package/dist/upload/large.d.cts.map +1 -0
- package/dist/upload/large.d.ts +71 -0
- package/dist/upload/large.d.ts.map +1 -0
- package/dist/upload/large.js +222 -0
- package/dist/upload/large.js.map +1 -0
- package/dist/upload/resume.cjs +34 -0
- package/dist/upload/resume.cjs.map +1 -0
- package/dist/upload/resume.d.cts +33 -0
- package/dist/upload/resume.d.cts.map +1 -0
- package/dist/upload/resume.d.ts +33 -0
- package/dist/upload/resume.d.ts.map +1 -0
- package/dist/upload/resume.js +34 -0
- package/dist/upload/resume.js.map +1 -0
- package/dist/upload/single.cjs +47 -0
- package/dist/upload/single.cjs.map +1 -0
- package/dist/upload/single.d.cts +51 -0
- package/dist/upload/single.d.cts.map +1 -0
- package/dist/upload/single.d.ts +51 -0
- package/dist/upload/single.d.ts.map +1 -0
- package/dist/upload/single.js +47 -0
- package/dist/upload/single.js.map +1 -0
- package/dist/upload/stream.cjs +198 -0
- package/dist/upload/stream.cjs.map +1 -0
- package/dist/upload/stream.d.cts +61 -0
- package/dist/upload/stream.d.cts.map +1 -0
- package/dist/upload/stream.d.ts +61 -0
- package/dist/upload/stream.d.ts.map +1 -0
- package/dist/upload/stream.js +198 -0
- package/dist/upload/stream.js.map +1 -0
- package/dist/util/best-effort.cjs +10 -0
- package/dist/util/best-effort.cjs.map +1 -0
- package/dist/util/best-effort.d.cts +31 -0
- package/dist/util/best-effort.d.cts.map +1 -0
- package/dist/util/best-effort.d.ts +31 -0
- package/dist/util/best-effort.d.ts.map +1 -0
- package/dist/util/best-effort.js +10 -0
- package/dist/util/best-effort.js.map +1 -0
- package/dist/util/defaults.cjs +11 -0
- package/dist/util/defaults.cjs.map +1 -0
- package/dist/util/defaults.d.cts +54 -0
- package/dist/util/defaults.d.cts.map +1 -0
- package/dist/util/defaults.d.ts +54 -0
- package/dist/util/defaults.d.ts.map +1 -0
- package/dist/util/defaults.js +11 -0
- package/dist/util/defaults.js.map +1 -0
- package/dist/util/normalize.cjs +16 -0
- package/dist/util/normalize.cjs.map +1 -0
- package/dist/util/normalize.d.cts +62 -0
- package/dist/util/normalize.d.cts.map +1 -0
- package/dist/util/normalize.d.ts +62 -0
- package/dist/util/normalize.d.ts.map +1 -0
- package/dist/util/normalize.js +16 -0
- package/dist/util/normalize.js.map +1 -0
- package/dist/util/paginator.cjs +20 -0
- package/dist/util/paginator.cjs.map +1 -0
- package/dist/util/paginator.d.cts +120 -0
- package/dist/util/paginator.d.cts.map +1 -0
- package/dist/util/paginator.d.ts +120 -0
- package/dist/util/paginator.d.ts.map +1 -0
- package/dist/util/paginator.js +20 -0
- package/dist/util/paginator.js.map +1 -0
- package/dist/util/plan-ranges.cjs +28 -0
- package/dist/util/plan-ranges.cjs.map +1 -0
- package/dist/util/plan-ranges.d.cts +56 -0
- package/dist/util/plan-ranges.d.cts.map +1 -0
- package/dist/util/plan-ranges.d.ts +56 -0
- package/dist/util/plan-ranges.d.ts.map +1 -0
- package/dist/util/plan-ranges.js +28 -0
- package/dist/util/plan-ranges.js.map +1 -0
- package/dist/util/text-codec.cjs +7 -0
- package/dist/util/text-codec.cjs.map +1 -0
- package/dist/util/text-codec.d.cts +26 -0
- package/dist/util/text-codec.d.cts.map +1 -0
- package/dist/util/text-codec.d.ts +26 -0
- package/dist/util/text-codec.d.ts.map +1 -0
- package/dist/util/text-codec.js +7 -0
- package/dist/util/text-codec.js.map +1 -0
- package/dist/util/to-error.cjs +7 -0
- package/dist/util/to-error.cjs.map +1 -0
- package/dist/util/to-error.d.cts +15 -0
- package/dist/util/to-error.d.cts.map +1 -0
- package/dist/util/to-error.d.ts +15 -0
- package/dist/util/to-error.d.ts.map +1 -0
- package/dist/util/to-error.js +7 -0
- package/dist/util/to-error.js.map +1 -0
- package/dist/version.cjs +6 -0
- package/dist/version.cjs.map +1 -0
- package/dist/version.d.cts +14 -0
- package/dist/version.d.cts.map +1 -0
- package/dist/version.d.ts +14 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +198 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/simulator/index.ts"],"sourcesContent":["/**\n * In-memory B2 simulator for testing without network I/O.\n *\n * {@link B2Simulator} implements 25+ B2 native API operations at the\n * request/response level. Create a simulator, call {@link B2Simulator.transport}\n * to get an {@link HttpTransport}, and pass it to `B2Client`. Ideal for\n * unit tests, CI pipelines, and local development.\n *\n * @packageDocumentation\n */\n\nimport type { HttpRequest, HttpResponse, HttpTransport } from '../http/transport.ts'\nimport { sha1Hex } from '../streams/hash.ts'\nimport { type AuthorizeAccountResponse, Capability } from '../types/auth.ts'\nimport { type BucketInfo, BucketRetentionMode, type BucketType } from '../types/bucket.ts'\nimport { EncryptionMode } from '../types/encryption.ts'\nimport { FileAction, type FileVersion } from '../types/file.ts'\nimport {\n type AuthToken,\n type BucketId,\n accountId as accountIdOf,\n bucketId as bucketIdOf,\n fileId as fileIdOf,\n} from '../types/ids.ts'\nimport type { RetentionMode } from '../types/lock.ts'\nimport type { EventNotificationRule } from '../types/notifications.ts'\nimport { utf8Decoder, utf8Encoder } from '../util/text-codec.ts'\nimport { toError } from '../util/to-error.ts'\n\n/**\n * Result of {@link parseRangeHeader}. `'ok'` is satisfiable,\n * `'unsatisfiable'` is well-formed but cannot be served (e.g. range\n * against an empty file, or a start offset at or past EOF), and\n * `'malformed'` means the header could not be parsed at all and is\n * ignored (RFC 7233 §3.1 allows servers to treat malformed headers as\n * absent). Real B2 returns `416 Range Not Satisfiable` for the\n * `'unsatisfiable'` case.\n */\ntype RangeParseResult =\n | { kind: 'ok'; start: number; end: number }\n | { kind: 'unsatisfiable' }\n | { kind: 'malformed' }\n\n/**\n * Parse an RFC 7233 `Range` header value into inclusive start/end byte\n * offsets clamped to the file size. Supports closed (`bytes=0-999`),\n * open-ended (`bytes=1000-`), and suffix (`bytes=-500`) forms.\n *\n * @param header - The raw header value (e.g. `'bytes=0-999'`).\n * @param totalSize - The full file size in bytes (for clamping + suffix).\n *\n * @returns A {@link RangeParseResult} tagged with how the simulator\n * should respond: serve the range, send 416, or ignore the header.\n *\n * @see https://www.rfc-editor.org/rfc/rfc7233#section-2.1\n */\nfunction parseRangeHeader(header: string, totalSize: number): RangeParseResult {\n const m = header.match(/^bytes=(\\d*)-(\\d*)$/)\n if (!m) return { kind: 'malformed' }\n const [, startStr, endStr] = m\n const hasStart = startStr !== ''\n const hasEnd = endStr !== ''\n if (!hasStart && !hasEnd) return { kind: 'malformed' }\n\n // Empty file with any well-formed range request is unsatisfiable —\n // real B2 returns 416. Previously the simulator returned 200 + the\n // empty body, which masked range-aware caller bugs.\n if (totalSize === 0) return { kind: 'unsatisfiable' }\n\n let start: number\n let end: number\n if (!hasStart) {\n // `bytes=-N` form: the last N bytes.\n const suffixLen = Number.parseInt(endStr ?? '0', 10)\n if (!Number.isFinite(suffixLen) || suffixLen <= 0) return { kind: 'malformed' }\n start = Math.max(0, totalSize - suffixLen)\n end = totalSize - 1\n } else if (!hasEnd) {\n // `bytes=N-` form: from offset N to end of file.\n start = Number.parseInt(startStr ?? '0', 10)\n end = totalSize - 1\n } else {\n start = Number.parseInt(startStr ?? '0', 10)\n end = Number.parseInt(endStr ?? '0', 10)\n }\n if (!Number.isFinite(start) || !Number.isFinite(end) || start < 0 || start > end) {\n return { kind: 'malformed' }\n }\n // Start past EOF is unsatisfiable per RFC 7233 §4.4.\n if (start >= totalSize) return { kind: 'unsatisfiable' }\n // Clamp end to the actual file size — RFC 7233 says the server SHOULD\n // satisfy a partially-valid range rather than rejecting.\n end = Math.min(end, totalSize - 1)\n return { kind: 'ok', start, end }\n}\n\n/**\n * Parse `X-Bz-Info-*` headers (lowercased Map keys) into a plain\n * fileInfo record. Mirrors the SDK's `parseFileInfoHeaders` in\n * `raw/encoding.ts` but operates on a `Record<string, string>` rather\n * than a `Headers` object so the simulator can reuse the same\n * extraction logic without converting back to `Headers`.\n *\n * @param headers - Lowercased header map.\n *\n * @returns Plain `Record<string, string>` of `fileInfo` keys/values.\n */\nfunction parseFileInfoHeaders(headers: Record<string, string>): Record<string, string> {\n const info: Record<string, string> = {}\n const prefix = 'x-bz-info-'\n for (const [key, value] of Object.entries(headers)) {\n if (!key.startsWith(prefix)) continue\n const fileInfoKey = decodeURIComponent(key.slice(prefix.length))\n info[fileInfoKey] = decodeURIComponent(value)\n }\n return info\n}\nimport { missingCapabilitiesFor } from './capabilities.ts'\nimport {\n validateBucketInfo,\n validateBucketName,\n validateFileInfo,\n validateFileName,\n validateMaxCount,\n} from './validation.ts'\n\n// Re-export the documented B2 spec limit constants so callers of\n// `@backblaze-labs/b2-sdk/simulator` can parameterise tests against the\n// real caps without reaching into the validation submodule.\nexport {\n BUCKET_INFO_MAX_KEYS,\n BUCKET_INFO_VALUE_MAX,\n BUCKET_NAME_MAX,\n BUCKET_NAME_MIN,\n FILE_INFO_TOTAL_MAX,\n FILE_INFO_VALUE_MAX,\n FILE_NAME_MAX_BYTES,\n LIST_ENDPOINT_CAPS,\n} from './validation.ts'\n\ninterface StoredFile {\n readonly fileVersion: FileVersion\n readonly data: Uint8Array\n}\n\ninterface StoredBucket {\n readonly info: BucketInfo\n readonly files: Map<string, StoredFile[]>\n}\n\ninterface LargeFileInProgress {\n readonly fileId: string\n readonly bucketId: string\n readonly fileName: string\n readonly contentType: string\n readonly fileInfo: Record<string, string>\n readonly parts: Map<number, { data: Uint8Array; sha1: string }>\n}\n\ninterface StoredKey {\n readonly applicationKeyId: string\n readonly keyName: string\n readonly capabilities: readonly string[]\n readonly accountId: string\n readonly applicationKey: string\n readonly bucketId: string | null\n readonly namePrefix: string | null\n readonly expirationTimestamp: number | null\n}\n\n/** JSON response returned by {@link B2Simulator.handleRequest} and {@link B2Simulator.handleUpload}. */\nexport interface SimulatorJsonResponse {\n /** HTTP status code. */\n readonly status: number\n /** JSON response body. */\n readonly body: unknown\n}\n\n/** Download response returned by {@link B2Simulator.handleDownload}. */\nexport interface SimulatorDownloadResponse {\n /** HTTP status code. */\n readonly status: number\n /** B2 response headers (content type, SHA-1, file info, etc.). */\n readonly headers: Record<string, string>\n /** Raw file bytes, or null if the file was not found. */\n readonly data: Uint8Array | null\n}\n\n/**\n * Specification for a synthetic failure to return from the simulator's\n * transport. See {@link B2Simulator.injectFailure}.\n */\nexport interface FaultSpec {\n /**\n * URL substring matched against `request.url`. The fault triggers on\n * every request whose URL contains this substring. Typically a B2\n * endpoint name like `'b2_upload_part'`, `'b2_authorize_account'`,\n * `'b2_download_file_by_id'`, or `'/file/'` for download-by-name.\n */\n readonly on: string\n /** HTTP status to return. Defaults to `503`. */\n readonly status?: number\n /** B2 error code to return in the JSON body. Defaults to `'service_unavailable'`. */\n readonly code?: string\n /** Human-readable message. Defaults to `'simulated failure'`. */\n readonly message?: string\n /**\n * Number of matched requests to fail before the fault retires. Defaults\n * to `Number.POSITIVE_INFINITY` (every matched request fails until\n * cleared). Set to e.g. `3` to fail the next 3 matched requests then\n * stop.\n */\n readonly count?: number\n /**\n * Number of matched requests to let through before failures start.\n * Defaults to `0` (fail from the first matched request). Set to e.g.\n * `2` to let the first 2 succeed and start failing on the 3rd.\n */\n readonly skip?: number\n /**\n * If set, the synthetic response includes a `Retry-After: <n>` header\n * (in seconds). Used to exercise the retry transport's\n * `Retry-After`-respecting backoff path.\n */\n readonly retryAfter?: number\n}\n\n/**\n * Handle returned by {@link B2Simulator.injectFailure} so a specific\n * fault registration can be torn down without affecting other faults.\n */\nexport interface FaultHandle {\n /**\n * Remove this fault registration. Idempotent: calling twice is a no-op.\n * Faults whose `count` budget has already been exhausted retire\n * automatically and do not need to be cleared explicitly.\n */\n clear(): void\n}\n\n/**\n * Internal book-keeping for an active {@link FaultSpec}. Tracks the\n * remaining skip/count budget across matched requests and a unique id so\n * the registration can be torn down individually.\n */\ninterface ActiveFault {\n readonly id: number\n readonly spec: FaultSpec\n remainingSkip: number\n remainingCount: number\n}\n\n/**\n * Options for constructing a {@link B2Simulator}.\n */\nexport interface B2SimulatorOptions {\n /**\n * The minimum part size the simulator advertises in `b2_authorize_account`\n * responses (`apiInfo.storageApi.absoluteMinimumPartSize`). Defaults to\n * `5_000_000` to mirror production B2. Lower this in tests that exercise\n * multipart control-flow branches but don't need realistic part sizes,\n * because v8 coverage instrumentation pushes 5 MB+ part hashing past 60 s\n * on the slowest CI runners, which trips vitest's IPC RPC timeout.\n */\n minimumPartSize?: number\n /**\n * The recommended part size the simulator advertises in\n * `b2_authorize_account` responses (`apiInfo.storageApi.recommendedPartSize`).\n * Defaults to `100_000_000` to mirror production B2. Lower this when a test\n * needs to exercise the SDK's \"use the recommended size when the caller\n * omits `partSize`\" default-branch without uploading 100 MB of bytes.\n */\n recommendedPartSize?: number\n /**\n * Pluggable hook: invoked after every successful upload, copy, or\n * `finishLargeFile` on a bucket with a matching event-notification\n * rule. Tests can register a hook to assert the SDK's webhook\n * publishing path without spinning up a real HTTP listener.\n *\n * Receives the freshly-stored `FileVersion`, the bucket the upload\n * landed in, and the rule that matched. Returns a promise so async\n * hook implementations are allowed; the simulator never blocks on it\n * (errors thrown from the hook are surfaced via `bestEffort` to\n * avoid masking the underlying API call's success).\n */\n onWebhookDeliver?: (event: {\n rule: EventNotificationRule\n fileVersion: FileVersion\n bucketId: string\n }) => Promise<void> | void\n /**\n * Pluggable hook: invoked after every successful upload on a bucket\n * configured as a replication source. Receives the source `FileVersion`\n * and the destination bucket ID. Tests can register a hook to\n * verify replication intent without actually copying bytes inside\n * the simulator.\n */\n onReplicate?: (event: {\n sourceFileVersion: FileVersion\n sourceBucketId: string\n destinationBucketId: string\n }) => Promise<void> | void\n /**\n * Diagnostic hook: invoked with any error thrown or rejected by\n * `onWebhookDeliver` / `onReplicate`. Without this, errors thrown by\n * user-supplied hooks are silently swallowed (intentional: a buggy\n * hook must not corrupt an otherwise-successful upload), which makes\n * test debugging hard when a hook quietly stops firing. Register\n * `onHookError` to surface what would otherwise be invisible.\n */\n onHookError?: (event: {\n kind: 'webhook' | 'replication'\n error: Error\n }) => void\n /**\n * When `true`, the simulator enforces application-key capability\n * checks, bucket scoping, prefix scoping, and auth-token expiry on\n * every request. The default `false` keeps the simulator permissive\n * (matching its long-standing behaviour) so the existing test suite\n * doesn't have to set up keys with the right capabilities.\n *\n * In strict mode:\n *\n * - Unknown auth tokens return HTTP 401 with code `bad_auth_token`.\n * - Expired tokens (per {@link B2Simulator.advanceTime}) return HTTP 401 with code `expired_auth_token`.\n * - Calls without the required capability for the endpoint return HTTP 403 `unauthorized`.\n * - Calls outside the key's bucketId / namePrefix scope return HTTP 403 `unauthorized`.\n *\n * Each test can opt in: `new B2Simulator({ strictAuth: true })`.\n */\n strictAuth?: boolean\n /**\n * How long auth tokens issued via `b2_authorize_account` are valid\n * for, in milliseconds. Defaults to 24 hours (real B2). Tests that\n * want to exercise the 401/reauth retry path can lower this so a\n * single call to {@link B2Simulator.advanceTime} expires the token.\n */\n authTokenTtlMs?: number\n}\n\n/**\n * In-memory B2 simulator for testing. Implements the B2 native API at the\n * request/response level without any network I/O. Supports 25+ operations\n * including buckets, files, large files, keys, and notifications.\n *\n * @example\n * ```ts\n * const sim = new B2Simulator()\n * const client = new B2Client({\n * applicationKeyId: 'test-key-id',\n * applicationKey: 'test-key',\n * transport: sim.transport(),\n * })\n * await client.authorize()\n * ```\n */\nexport class B2Simulator {\n private readonly buckets = new Map<string, StoredBucket>()\n private readonly accountId = 'sim_account_0001'\n private readonly largeFiles = new Map<string, LargeFileInProgress>()\n private readonly keys = new Map<string, StoredKey>()\n private readonly notificationRules = new Map<string, EventNotificationRule[]>()\n private readonly minimumPartSize: number\n private readonly recommendedPartSize: number\n private readonly faults: ActiveFault[] = []\n private nextFaultId = 1\n private readonly onWebhookDeliver?: B2SimulatorOptions['onWebhookDeliver']\n private readonly onReplicate?: B2SimulatorOptions['onReplicate']\n private readonly onHookError?: B2SimulatorOptions['onHookError']\n private readonly strictAuth: boolean\n private readonly authTokenTtlMs: number\n /**\n * Issued auth tokens with their associated grant scope + expiry. In\n * permissive mode (`strictAuth: false`) this is still populated by\n * `authorize` but never consulted on subsequent requests. In strict\n * mode each request looks up its `Authorization` header here.\n */\n private readonly issuedTokens = new Map<\n string,\n {\n capabilities: readonly Capability[]\n bucketId: string | null\n namePrefix: string | null\n expiresAt: number\n /**\n * The application-key ID this token was minted for, or `null`\n * for tokens minted from the implicit master credential. Set so\n * {@link deleteKey} can evict every outstanding token whose\n * underlying key was just revoked — without this back-pointer\n * deleted keys keep working until the token TTL expires.\n */\n applicationKeyId: string | null\n }\n >()\n /**\n * Virtual-clock offset applied to `Date.now()` for expiry checks.\n * Defaults to 0. Tests advance via {@link advanceTime} to fast-forward\n * past auth-token expiry without sleeping.\n */\n private clockOffsetMs = 0\n /**\n * Per-instance monotonic counter used to mint realistic-looking\n * IDs (`b2_bucket_<24-hex>`, `4_z<24-hex>`, etc.) and auth tokens.\n * Module-global previously, which leaked state across `B2Simulator`\n * instances in the same Vitest worker — concurrent tests could see\n * colliding tokens. Per-instance ensures determinism within a single\n * simulator's lifetime.\n */\n private nextId = 1\n /**\n * Last-issued upload timestamp. The simulator enforces strict\n * monotonicity so version ordering is deterministic for tests; if\n * two writes hit the same `Date.now()` millisecond, the second\n * receives `lastTimestamp + 1`.\n */\n private lastTimestamp = 0\n /**\n * Outstanding fire-and-forget hook invocations. Tracked so tests\n * (and future {@link flushHooks} callers) can wait for every\n * dispatched `onWebhookDeliver` / `onReplicate` callback to settle\n * before asserting against observable side effects. The microtask\n * dance in fidelity tests was previously brittle: positive cases\n * flushed twice, negative cases flushed once, and any addition to\n * the hook dispatch chain quietly broke the negative path.\n */\n private readonly pendingHooks = new Set<Promise<void>>()\n\n /**\n * Constructs a new in-memory B2 simulator.\n * @param options - Optional simulator overrides. See {@link B2SimulatorOptions}.\n */\n constructor(options: B2SimulatorOptions = {}) {\n this.minimumPartSize = options.minimumPartSize ?? 5_000_000\n this.recommendedPartSize = options.recommendedPartSize ?? 100_000_000\n if (options.onWebhookDeliver !== undefined) this.onWebhookDeliver = options.onWebhookDeliver\n if (options.onReplicate !== undefined) this.onReplicate = options.onReplicate\n if (options.onHookError !== undefined) this.onHookError = options.onHookError\n this.strictAuth = options.strictAuth ?? false\n // Real B2 tokens last 24h. Default matches production; tests that\n // want to exercise the reauth path can lower this knob.\n this.authTokenTtlMs = options.authTokenTtlMs ?? 24 * 60 * 60 * 1000\n }\n\n /**\n * Advance the simulator's virtual clock by `ms` milliseconds. Used in\n * conjunction with `strictAuth: true` + a finite `authTokenTtlMs` to\n * force token expiry without `setTimeout`-based delays.\n *\n * @param ms - Milliseconds to advance. Negative values rewind (rarely useful).\n */\n advanceTime(ms: number): void {\n this.clockOffsetMs += ms\n }\n\n /**\n * Current simulator time. Equal to `Date.now() + clockOffsetMs`.\n *\n * @returns Unix milliseconds.\n */\n private now(): number {\n return Date.now() + this.clockOffsetMs\n }\n\n /**\n * Generate a deterministic-but-realistic-looking B2 identifier of the\n * shape `<prefix>_<24-hex-counter>`, which approximates the visual\n * width of real B2 wire IDs. The counter is monotonic per instance\n * so test fixtures are deterministic across runs of the same\n * simulator.\n *\n * @param prefix - B2-style prefix (`'b2_bucket'`, `'4_z'`, etc.).\n *\n * @returns A simulator-issued identifier that looks like a B2 wire ID.\n */\n private genId(prefix: string): string {\n const n = this.nextId++\n return `${prefix}_${n.toString(16).padStart(24, '0')}`\n }\n\n /**\n * Return a strictly-increasing upload timestamp, even when multiple\n * writes land in the same `Date.now()` millisecond. Tests rely on\n * `uploadTimestamp` ordering to discriminate file versions; ties from\n * coarse system-clock resolution would otherwise make version\n * selection nondeterministic.\n *\n * @returns Unix milliseconds.\n */\n private monotonicTimestamp(): number {\n const now = Date.now()\n if (now <= this.lastTimestamp) {\n this.lastTimestamp += 1\n } else {\n this.lastTimestamp = now\n }\n return this.lastTimestamp\n }\n\n /**\n * Wait for every fire-and-forget hook (`onWebhookDeliver`,\n * `onReplicate`) currently in flight to settle. Use in tests after\n * an upload/copy/finish-large-file call to deterministically observe\n * hook side effects without microtask-flush guesswork.\n *\n * @returns A promise that resolves once every pending hook callback\n * has either resolved or rejected.\n */\n async flushHooks(): Promise<void> {\n while (this.pendingHooks.size > 0) {\n // Snapshot to avoid mutating the set while iterating; new hooks\n // can be enqueued by the very promises we're awaiting.\n const snapshot = [...this.pendingHooks]\n await Promise.allSettled(snapshot)\n }\n }\n\n /**\n * Authorize a request against the strict-auth bookkeeping. Returns\n * a `SimulatorJsonResponse` error on failure or `null` on success.\n * Only consulted when `strictAuth: true`.\n *\n * @param authToken - The `Authorization` header value from the request.\n * @param endpoint - B2 endpoint name being invoked.\n * @param bucketId - Optional bucket ID the request operates on, for scope check.\n * @param fileName - Optional file name the request operates on, for prefix check.\n *\n * @returns `null` when the request is authorized, otherwise a 401/403 response.\n */\n private authorizeRequest(\n authToken: string | undefined,\n endpoint: string,\n bucketId?: string,\n fileName?: string,\n ): SimulatorJsonResponse | null {\n // Endpoints that don't require auth (just b2_authorize_account today).\n const required = missingCapabilitiesFor(endpoint, [])\n const noCheckNeeded = required.length === 0 && endpoint === 'b2_authorize_account'\n if (noCheckNeeded) return null\n\n if (authToken === undefined || authToken === '') {\n return this.error(401, 'bad_auth_token', 'missing Authorization header')\n }\n const token = this.issuedTokens.get(authToken)\n if (!token) {\n return this.error(401, 'bad_auth_token', 'unknown auth token')\n }\n if (this.now() > token.expiresAt) {\n return this.error(401, 'expired_auth_token', 'auth token has expired; reauthorize')\n }\n const missing = missingCapabilitiesFor(endpoint, token.capabilities)\n if (missing.length > 0) {\n return this.error(\n 403,\n 'unauthorized',\n `application key lacks required capabilities: ${missing.join(', ')}`,\n )\n }\n if (token.bucketId !== null && bucketId !== undefined && bucketId !== token.bucketId) {\n return this.error(\n 403,\n 'unauthorized',\n `application key is scoped to bucket ${token.bucketId}; cannot access ${bucketId}`,\n )\n }\n if (\n token.namePrefix !== null &&\n fileName !== undefined &&\n !fileName.startsWith(token.namePrefix)\n ) {\n return this.error(\n 403,\n 'unauthorized',\n `application key is scoped to prefix \"${token.namePrefix}\"; \"${fileName}\" is outside scope`,\n )\n }\n return null\n }\n\n /**\n * Look up the application key matching the `Authorization` header on\n * an `authorize_account` request. The header is in the form\n * `Basic base64(applicationKeyId:applicationKey)`.\n *\n * Returns `null` for the implicit master credential (anything that\n * does not match a key minted via `b2_create_key`); in that case\n * `authorize` grants the full master capability set.\n *\n * @param authzHeader - Raw HTTP `Authorization` header value.\n *\n * @returns The matching key's grant scope, or `null` for the master.\n */\n private findKeyForAuthHeader(authzHeader: string | undefined): {\n capabilities: readonly Capability[]\n bucketId: string | null\n namePrefix: string | null\n applicationKeyId: string\n } | null {\n if (!authzHeader || !authzHeader.startsWith('Basic ')) return null\n // `atob` is standard on Node 16+, browsers, and modern edge runtimes.\n // Wrapped in a try because malformed base64 throws.\n const decoded = (() => {\n try {\n return atob(authzHeader.slice(6))\n } catch {\n return null\n }\n })()\n if (decoded === null) return null\n const idx = decoded.indexOf(':')\n if (idx === -1) return null\n const applicationKeyId = decoded.slice(0, idx)\n const applicationKey = decoded.slice(idx + 1)\n const stored = this.keys.get(applicationKeyId)\n if (!stored || stored.applicationKey !== applicationKey) return null\n return {\n capabilities: stored.capabilities as readonly Capability[],\n bucketId: stored.bucketId,\n namePrefix: stored.namePrefix,\n applicationKeyId,\n }\n }\n\n /**\n * Creates an {@link HttpTransport} that routes requests to this simulator.\n * @returns A transport instance backed by this in-memory simulator.\n */\n transport(): HttpTransport {\n return new SimulatorTransport(this)\n }\n\n /**\n * Register a synthetic failure to inject on requests whose URL contains\n * `spec.on`. Use this to exercise retry / backoff / error-handling\n * paths in tests without hand-rolling a wrapping `HttpTransport`. The\n * fault is consumed in registration order on each matched request;\n * once its `count` budget is exhausted it auto-retires.\n *\n * Faults are checked BEFORE the simulator's real handlers run, so a\n * matched request never touches in-memory state — failed uploads\n * don't create partial parts, failed deletes don't remove anything.\n *\n * @param spec - The failure to inject. See {@link FaultSpec}.\n *\n * @returns A handle whose `clear()` method removes this specific\n * fault registration (other faults remain in effect).\n *\n * @example\n * ```ts\n * // Fail the next 2 b2_upload_part calls with 503, then succeed.\n * sim.injectFailure({ on: 'b2_upload_part', status: 503, count: 2 })\n *\n * // Fail every b2_authorize_account with 401 + Retry-After: 5.\n * const handle = sim.injectFailure({\n * on: 'b2_authorize_account',\n * status: 401,\n * code: 'expired_auth_token',\n * retryAfter: 5,\n * })\n * // ... later\n * handle.clear()\n * ```\n */\n injectFailure(spec: FaultSpec): FaultHandle {\n const id = this.nextFaultId++\n const fault: ActiveFault = {\n id,\n spec,\n remainingSkip: spec.skip ?? 0,\n remainingCount: spec.count ?? Number.POSITIVE_INFINITY,\n }\n this.faults.push(fault)\n return {\n clear: () => {\n const idx = this.faults.findIndex((f) => f.id === id)\n if (idx !== -1) this.faults.splice(idx, 1)\n },\n }\n }\n\n /**\n * Remove every registered fault. Equivalent to calling `.clear()` on\n * every handle returned by {@link injectFailure}, plus a defensive\n * reset for tests that re-use a simulator across cases.\n */\n clearFaults(): void {\n this.faults.length = 0\n }\n\n /**\n * Internal: checks the registered faults for a match on the given URL\n * and consumes one if it should fire. Called from\n * {@link SimulatorTransport.send} before any real handler runs.\n *\n * @param url - The request URL to match against each fault's `on`\n * substring.\n *\n * @returns The fault to apply, or `null` if no fault should fire.\n *\n * @internal\n */\n consumeMatchingFault(url: string): FaultSpec | null {\n for (let i = 0; i < this.faults.length; i++) {\n const fault = this.faults[i] as ActiveFault\n if (!url.includes(fault.spec.on)) continue\n if (fault.remainingSkip > 0) {\n fault.remainingSkip -= 1\n continue\n }\n if (fault.remainingCount <= 0) continue\n fault.remainingCount -= 1\n if (fault.remainingCount <= 0) {\n // Auto-retire when the count budget is spent so subsequent\n // requests see the next-matching fault (or no fault).\n this.faults.splice(i, 1)\n }\n return fault.spec\n }\n return null\n }\n\n /**\n * Dispatches a JSON API request to the appropriate handler.\n * @param _method - The HTTP method (unused).\n * @param path - The request URL path containing the B2 endpoint name.\n * @param headers - The HTTP request headers; consulted by the\n * strict-auth gate to look up the issued auth token.\n * @param body - The parsed JSON request body.\n *\n * @returns An object with HTTP status and JSON response body.\n */\n async handleRequest(\n _method: string,\n path: string,\n headers: Record<string, string>,\n body: unknown,\n ): Promise<SimulatorJsonResponse> {\n const endpoint = path.split('/').pop() ?? ''\n\n // Strict-mode auth gate runs BEFORE the dispatch so even endpoints\n // that don't otherwise consult headers (e.g. b2_list_buckets) get\n // the capability check. Bucket / file-name scope checks happen\n // inside the per-endpoint handlers where the relevant fields are\n // actually accessible.\n if (this.strictAuth) {\n const authError = this.authorizeRequest(headers['authorization'], endpoint)\n if (authError !== null) return authError\n }\n\n switch (endpoint) {\n case 'b2_authorize_account':\n return this.authorize(headers['authorization'])\n case 'b2_create_bucket':\n return this.createBucket(\n body as { bucketName: string; bucketType: BucketType; accountId: string },\n )\n case 'b2_list_buckets':\n return this.listBuckets()\n case 'b2_delete_bucket':\n return this.deleteBucket(body as { bucketId: string })\n case 'b2_update_bucket':\n return this.updateBucket(body as Record<string, unknown>)\n case 'b2_get_upload_url':\n return this.getUploadUrl(body as { bucketId: string })\n case 'b2_list_file_names':\n return this.listFileNames(\n body as {\n bucketId: string\n maxFileCount?: number\n prefix?: string\n startFileName?: string\n },\n )\n case 'b2_list_file_versions':\n return this.listFileVersions(\n body as {\n bucketId: string\n maxFileCount?: number\n startFileName?: string\n startFileId?: string\n prefix?: string\n },\n )\n case 'b2_get_file_info':\n return this.getFileInfo(body as { fileId: string })\n case 'b2_hide_file':\n return this.hideFile(body as { bucketId: string; fileName: string })\n case 'b2_delete_file_version':\n return this.deleteFileVersion(\n body as { fileId: string; fileName: string; bypassGovernance?: boolean },\n )\n case 'b2_copy_file':\n return this.copyFile(\n body as { sourceFileId: string; fileName: string; destinationBucketId?: string },\n )\n case 'b2_start_large_file':\n return this.startLargeFile(\n body as {\n bucketId: string\n fileName: string\n contentType: string\n fileInfo?: Record<string, string>\n },\n )\n case 'b2_get_upload_part_url':\n return this.getUploadPartUrl(body as { fileId: string })\n case 'b2_finish_large_file':\n return this.finishLargeFile(body as { fileId: string; partSha1Array: string[] })\n case 'b2_cancel_large_file':\n return this.cancelLargeFile(body as { fileId: string })\n case 'b2_list_unfinished_large_files':\n return this.listUnfinishedLargeFiles(\n body as {\n bucketId: string\n namePrefix?: string\n startFileId?: string\n maxFileCount?: number\n },\n )\n case 'b2_list_parts':\n return this.listParts(\n body as { fileId: string; startPartNumber?: number; maxPartCount?: number },\n )\n case 'b2_copy_part':\n return await this.copyPart(\n body as {\n sourceFileId: string\n largeFileId: string\n partNumber: number\n range?: string\n },\n )\n case 'b2_get_download_authorization':\n return this.getDownloadAuthorization(\n body as { bucketId: string; fileNamePrefix: string; validDurationInSeconds: number },\n )\n case 'b2_create_key':\n return this.createKey(\n body as {\n accountId: string\n capabilities: string[]\n keyName: string\n validDurationInSeconds?: number\n bucketId?: string\n namePrefix?: string\n },\n )\n case 'b2_list_keys':\n return this.listKeys(\n body as { accountId: string; maxKeyCount?: number; startApplicationKeyId?: string },\n )\n case 'b2_delete_key':\n return this.deleteKey(body as { applicationKeyId: string })\n case 'b2_update_file_retention':\n return this.updateFileRetention(\n body as {\n fileName: string\n fileId: string\n fileRetention: {\n mode: RetentionMode | null\n retainUntilTimestamp: number | null\n }\n },\n )\n case 'b2_update_file_legal_hold':\n return this.updateFileLegalHold(\n body as { fileName: string; fileId: string; legalHold: string },\n )\n case 'b2_get_bucket_notification_rules':\n return this.getBucketNotificationRules(body as { bucketId: string })\n case 'b2_set_bucket_notification_rules':\n return this.setBucketNotificationRules(\n body as { bucketId: string; eventNotificationRules: EventNotificationRule[] },\n )\n default:\n return {\n status: 400,\n body: { status: 400, code: 'bad_request', message: `Unknown endpoint: ${endpoint}` },\n }\n }\n }\n\n /**\n * Handles file and part upload requests (`b2_upload_file`, `b2_upload_part`).\n * Dispatches to the appropriate internal handler based on the URL.\n * @param url - The upload endpoint URL used to determine the upload type.\n * @param headers - The HTTP headers containing file metadata and authorization.\n * @param data - The raw file or part content as bytes.\n *\n * @returns An object with HTTP status and JSON response body.\n */\n handleUpload(\n url: string,\n headers: Record<string, string>,\n data: Uint8Array,\n ): SimulatorJsonResponse {\n // Strict-mode auth gate also covers upload endpoints. Without\n // this, an expired or unknown auth token still successfully wrote\n // bytes because `handleUpload` bypassed `handleRequest`'s dispatch\n // gate. The endpoint name is sniffed from the URL since uploads\n // use opaque per-upload URLs rather than the canonical\n // `/b2api/v3/<endpoint>` shape.\n const isPart = url.includes('b2_upload_part')\n if (this.strictAuth) {\n const endpoint = isPart ? 'b2_upload_part' : 'b2_upload_file'\n const authError = this.authorizeRequest(headers['authorization'], endpoint)\n if (authError !== null) return authError\n }\n if (isPart) {\n return this.handleUploadPart(url, headers, data)\n }\n return this.handleUploadFile(url, headers, data)\n }\n\n /**\n * Handles file download requests (`b2_download_file_by_id`, `/file/` by name).\n * Returns the file data along with B2 response headers.\n * @param path - The request URL path identifying the file to download.\n * @param headers - The HTTP request headers for range or authorization.\n * @param method - The HTTP method; `'HEAD'` suppresses the response body.\n *\n * @returns The download response containing file data and B2 headers.\n */\n handleDownload(\n path: string,\n headers: Record<string, string>,\n method: 'GET' | 'HEAD' = 'GET',\n ): SimulatorDownloadResponse {\n if (path.includes('b2_download_file_by_id')) {\n // Strict-mode auth gate for download-by-id. Mirrors the gate in\n // `handleRequest`. Returns a synthetic JSON error body in the\n // download response shape so the transport renders the right\n // status code.\n if (this.strictAuth) {\n const authError = this.authorizeRequest(headers['authorization'], 'b2_download_file_by_id')\n if (authError !== null) return this.errorAsDownload(authError)\n }\n const url = new URL(`http://localhost${path}`)\n const fileId = url.searchParams.get('fileId') ?? ''\n return this.finalizeDownload(this.downloadById(fileId, headers['range']), url, method)\n }\n\n const fileMatch = path.match(/^([^?]+)/)?.[1]?.match(/\\/file\\/([^/]+)\\/(.+)/)\n if (fileMatch) {\n const bucketName = decodeURIComponent(fileMatch[1] ?? '')\n const fileName = decodeURIComponent(fileMatch[2] ?? '')\n if (this.strictAuth) {\n const authError = this.authorizeRequest(\n headers['authorization'],\n 'b2_download_file_by_name',\n undefined,\n fileName,\n )\n if (authError !== null) return this.errorAsDownload(authError)\n }\n const url = new URL(`http://localhost${path}`)\n return this.finalizeDownload(\n this.downloadByName(bucketName, fileName, headers['range']),\n url,\n method,\n )\n }\n\n return { status: 404, headers: {}, data: null }\n }\n\n /**\n * Convert a synthetic JSON error body into a `SimulatorDownloadResponse`\n * shape so the strict-auth gate on download paths can surface 401/403s\n * through the same transport plumbing as a regular failed download.\n *\n * @param json - The JSON error from {@link authorizeRequest}.\n *\n * @returns A download-shaped response with the error body bytes inline.\n */\n private errorAsDownload(json: SimulatorJsonResponse): SimulatorDownloadResponse {\n return {\n status: json.status,\n headers: { 'Content-Type': 'application/json' },\n data: utf8Encoder.encode(JSON.stringify(json.body)),\n }\n }\n\n /**\n * Applies HEAD-method body suppression and `b2Content*` response-header\n * overrides parsed from the download URL's query string. Mirrors what the\n * real B2 service does: any `b2Content*` query parameter is echoed back as\n * the corresponding response header.\n *\n * @param response - The download response produced by {@link downloadById} or {@link downloadByName}.\n * @param url - The parsed download URL (used to read `b2Content*` query params).\n * @param method - The HTTP method of the originating request.\n *\n * @returns The response with overrides applied.\n */\n private finalizeDownload(\n response: SimulatorDownloadResponse,\n url: URL,\n method: 'GET' | 'HEAD',\n ): SimulatorDownloadResponse {\n const overrideMap: Record<string, string> = {\n b2ContentDisposition: 'Content-Disposition',\n b2ContentLanguage: 'Content-Language',\n b2ContentEncoding: 'Content-Encoding',\n b2ContentType: 'Content-Type',\n b2CacheControl: 'Cache-Control',\n b2Expires: 'Expires',\n }\n const newHeaders = { ...response.headers }\n for (const [param, header] of Object.entries(overrideMap)) {\n const value = url.searchParams.get(param)\n if (value !== null) newHeaders[header] = value\n }\n const data = method === 'HEAD' ? null : response.data\n return { status: response.status, headers: newHeaders, data }\n }\n\n private handleUploadFile(\n url: string,\n headers: Record<string, string>,\n data: Uint8Array,\n ): SimulatorJsonResponse {\n const bucketId = new URL(url).searchParams.get('bucketId')\n if (!bucketId) return this.error(400, 'bad_request', 'Missing bucketId')\n\n const bucket = this.buckets.get(bucketId)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n\n const fileName = decodeURIComponent(headers['x-bz-file-name'] ?? '')\n const contentType = headers['content-type'] ?? 'application/octet-stream'\n const sha1 = headers['x-bz-content-sha1'] ?? 'none'\n\n // B2 spec-compliance: validate file name and optional X-Bz-Info-*\n // headers before storing. Real B2 rejects with `400 invalid_file_name`\n // or `400 invalid_file_info`; the simulator used to store anything.\n const fileNameError = validateFileName(fileName)\n if (fileNameError) return this.error(400, fileNameError.code, fileNameError.message)\n const fileInfo = parseFileInfoHeaders(headers)\n const fileInfoError = validateFileInfo(fileInfo)\n if (fileInfoError) return this.error(400, fileInfoError.code, fileInfoError.message)\n\n const fileVersion = this.makeFileVersion({\n bucketId,\n fileName,\n contentType,\n contentLength: data.byteLength,\n contentSha1: sha1,\n action: FileAction.Upload,\n })\n const stored: StoredFile = { fileVersion, data }\n const existing = bucket.files.get(fileName)\n if (existing) {\n existing.push(stored)\n } else {\n bucket.files.set(fileName, [stored])\n }\n\n this.firePostUploadHooks(fileVersion, bucketId, 'b2:ObjectCreated:Upload')\n return { status: 200, body: fileVersion }\n }\n\n private handleUploadPart(\n url: string,\n headers: Record<string, string>,\n data: Uint8Array,\n ): SimulatorJsonResponse {\n const fileId = new URL(url).searchParams.get('fileId')\n if (!fileId) return this.error(400, 'bad_request', 'Missing fileId')\n\n const large = this.largeFiles.get(fileId)\n if (!large) return this.error(400, 'bad_request', 'Large file not found')\n\n const partNumber = Number.parseInt(headers['x-bz-part-number'] ?? '0', 10)\n const sha1 = headers['x-bz-content-sha1'] ?? 'none'\n\n large.parts.set(partNumber, { data, sha1 })\n\n return {\n status: 200,\n body: {\n fileId: large.fileId,\n partNumber,\n contentLength: data.byteLength,\n contentSha1: sha1,\n serverSideEncryption: { mode: EncryptionMode.None },\n uploadTimestamp: Date.now(),\n },\n }\n }\n\n private downloadById(fileId: string, range?: string): SimulatorDownloadResponse {\n const found = this.findFile(fileId)\n if (found === null) return { status: 404, headers: {}, data: null }\n return this.serveFile(found.stored, range)\n }\n\n private downloadByName(\n bucketName: string,\n fileName: string,\n range?: string,\n ): SimulatorDownloadResponse {\n for (const bucket of this.buckets.values()) {\n if (bucket.info.bucketName !== bucketName) continue\n const versions = bucket.files.get(fileName)\n if (!versions || versions.length === 0) break\n const latest = versions[versions.length - 1]\n if (!latest || latest.fileVersion.action === FileAction.Hide) {\n return { status: 404, headers: {}, data: null }\n }\n return this.serveFile(latest, range)\n }\n return { status: 404, headers: {}, data: null }\n }\n\n private serveFile(\n stored: StoredFile,\n range?: string,\n ): { status: number; headers: Record<string, string>; data: Uint8Array } {\n const fullData = stored.data\n let data = fullData\n let status = 200\n let contentRange: string | null = null\n\n if (range) {\n const parsed = parseRangeHeader(range, fullData.byteLength)\n if (parsed.kind === 'ok') {\n data = fullData.subarray(parsed.start, parsed.end + 1)\n status = 206\n contentRange = `bytes ${parsed.start}-${parsed.end}/${fullData.byteLength}`\n } else if (parsed.kind === 'unsatisfiable') {\n // RFC 7233 §4.4: 416 with a `Content-Range: bytes */<total>`\n // hint telling the client the legitimate size.\n return {\n status: 416,\n headers: {\n 'Content-Type': stored.fileVersion.contentType,\n 'Content-Range': `bytes */${fullData.byteLength}`,\n },\n data: new Uint8Array(0),\n }\n }\n // `kind === 'malformed'`: treat as absent and serve the full body.\n }\n\n const fv = stored.fileVersion\n const headers: Record<string, string> = {\n 'Content-Type': fv.contentType,\n 'Content-Length': String(data.byteLength),\n 'X-Bz-File-Id': fv.fileId,\n 'X-Bz-File-Name': encodeURIComponent(fv.fileName),\n 'X-Bz-Content-Sha1': fv.contentSha1 ?? 'none',\n 'X-Bz-Upload-Timestamp': String(fv.uploadTimestamp),\n 'X-Bz-Info-src_last_modified_millis': String(fv.uploadTimestamp),\n }\n if (contentRange !== null) {\n // B2 spec-compliance: 206 Partial Content responses MUST carry a\n // `Content-Range: bytes <start>-<end>/<total>` header per RFC\n // 7233 §4.2. The simulator used to return 206 with the partial\n // body but no Content-Range, leaving range-aware callers with no\n // way to verify they got the bytes they asked for.\n headers['Content-Range'] = contentRange\n }\n return { status, headers, data }\n }\n\n // --- API handlers ---\n\n private authorize(authzHeader?: string): { status: number; body: AuthorizeAccountResponse } {\n // Master capabilities granted to the implicit \"test\" credential.\n // Real B2 derives the capability list from the application key the\n // caller authorized with; in permissive mode every auth call gets\n // the full set so existing tests don't have to construct keys\n // first. Strict-mode tests that need a restricted scope authorize\n // with a specific app-key first via b2_create_key, then call\n // authorize-with-that-key (today's simulator returns this full\n // set regardless — strict-mode test seam is in `authorizeRequest`\n // which consults the issued-token map, not the response body).\n // Note: object-lock-related capabilities (BypassGovernance,\n // WriteFileLegalHolds, WriteFileRetentions) are intentionally\n // omitted from the master grant. Real B2 doesn't auto-grant these\n // either — they're opt-in scopes set via b2_create_key. Tests that\n // need them explicit-issue a key via the simulator's createKey\n // handler and reauth with that key.\n const capabilities: readonly Capability[] = [\n Capability.ListBuckets,\n Capability.ReadBuckets,\n Capability.WriteBuckets,\n Capability.DeleteBuckets,\n Capability.ListFiles,\n Capability.ReadFiles,\n Capability.WriteFiles,\n Capability.DeleteFiles,\n Capability.ListKeys,\n Capability.WriteKeys,\n Capability.DeleteKeys,\n Capability.ShareFiles,\n Capability.ReadBucketNotifications,\n Capability.WriteBucketNotifications,\n ]\n // Token validity: real B2 = 24h; configurable via `authTokenTtlMs`.\n // If a key was previously authorized via `authorizeAsKey` (test\n // seam, see `authorizeAsKey` below), the auth header identifies\n // it and the issued token inherits that key's scope.\n const keyForAuth = this.findKeyForAuthHeader(authzHeader)\n const tokenStr = `sim_auth_token_${this.nextId++}`\n this.issuedTokens.set(tokenStr, {\n capabilities: keyForAuth?.capabilities ?? capabilities,\n bucketId: keyForAuth?.bucketId ?? null,\n namePrefix: keyForAuth?.namePrefix ?? null,\n expiresAt: this.now() + this.authTokenTtlMs,\n applicationKeyId: keyForAuth?.applicationKeyId ?? null,\n })\n return {\n status: 200,\n body: {\n accountId: accountIdOf(this.accountId),\n // `AuthToken` has no public factory by design — auth tokens are\n // minted by B2, not constructed by user code. The simulator is\n // the only legitimate place that needs to forge one.\n authorizationToken: tokenStr as unknown as AuthToken,\n apiInfo: {\n storageApi: {\n absoluteMinimumPartSize: this.minimumPartSize,\n apiUrl: 'http://localhost:0',\n bucketId: (keyForAuth?.bucketId ?? null) as BucketId | null,\n bucketName: null,\n downloadUrl: 'http://localhost:0',\n infoType: 'storageApi',\n namePrefix: keyForAuth?.namePrefix ?? null,\n recommendedPartSize: this.recommendedPartSize,\n s3ApiUrl: 'http://localhost:0',\n allowed: {\n capabilities: keyForAuth?.capabilities ?? capabilities,\n bucketId: (keyForAuth?.bucketId ?? null) as BucketId | null,\n bucketName: null,\n namePrefix: keyForAuth?.namePrefix ?? null,\n },\n },\n },\n applicationKeyExpirationTimestamp: null,\n },\n }\n }\n\n private createBucket(req: {\n bucketName: string\n bucketType: BucketType\n accountId: string\n bucketInfo?: Record<string, string>\n corsRules?: BucketInfo['corsRules']\n defaultServerSideEncryption?: BucketInfo['defaultServerSideEncryption']\n defaultRetention?: BucketInfo['defaultRetention']\n fileLockEnabled?: boolean\n lifecycleRules?: BucketInfo['lifecycleRules']\n replicationConfiguration?: BucketInfo['replicationConfiguration']\n }): {\n status: number\n body: unknown\n } {\n // B2 spec-compliance: validate bucket name regex + length, plus the\n // optional bucketInfo byte budget. Real B2 rejects bad names with\n // `400 invalid_bucket_name`; the simulator used to accept anything.\n const nameError = validateBucketName(req.bucketName)\n if (nameError) return this.error(400, nameError.code, nameError.message)\n if (req.bucketInfo !== undefined) {\n const infoError = validateBucketInfo(req.bucketInfo)\n if (infoError) return this.error(400, infoError.code, infoError.message)\n }\n for (const b of this.buckets.values()) {\n if (b.info.bucketName === req.bucketName) {\n return this.error(400, 'duplicate_bucket_name', 'Bucket name already in use')\n }\n }\n const bid = bucketIdOf(this.genId('b2_bucket'))\n // Honor optional fields supplied in the create request so callers\n // that construct a bucket with e.g. `fileLockEnabled: true` see the\n // flag reflected back in the returned `BucketInfo` (and in every\n // subsequent `listBuckets` response). Previously these were\n // hardcoded to defaults, forcing tests to mutate `bucket.info`\n // post-create to simulate a non-vanilla bucket.\n const defaultRetention = req.defaultRetention ?? {\n mode: BucketRetentionMode.None,\n period: null,\n }\n const info: BucketInfo = {\n accountId: accountIdOf(req.accountId),\n bucketId: bid,\n bucketName: req.bucketName,\n bucketType: req.bucketType,\n bucketInfo: req.bucketInfo ?? {},\n corsRules: req.corsRules ?? [],\n defaultServerSideEncryption: req.defaultServerSideEncryption ?? { mode: EncryptionMode.None },\n fileLockConfiguration: {\n isClientAuthorizedToRead: true,\n value: {\n isFileLockEnabled: req.fileLockEnabled ?? false,\n defaultRetention,\n },\n },\n lifecycleRules: req.lifecycleRules ?? [],\n options: [],\n revision: 1,\n defaultRetention,\n replicationConfiguration: req.replicationConfiguration ?? {\n asReplicationSource: null,\n asReplicationDestination: null,\n },\n }\n this.buckets.set(bid, { info, files: new Map() })\n return { status: 200, body: info }\n }\n\n private listBuckets(): SimulatorJsonResponse {\n const buckets = [...this.buckets.values()].map((b) => b.info)\n return { status: 200, body: { buckets } }\n }\n\n private deleteBucket(req: { bucketId: string }): SimulatorJsonResponse {\n const bucket = this.buckets.get(req.bucketId)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n this.buckets.delete(req.bucketId)\n return { status: 200, body: bucket.info }\n }\n\n private updateBucket(req: Record<string, unknown>): SimulatorJsonResponse {\n const bucket = this.buckets.get(req['bucketId'] as string)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n // Validate bucketInfo budget on update too — real B2 rejects\n // oversized info maps on the update path, not just on create.\n if (req['bucketInfo'] !== undefined) {\n const infoError = validateBucketInfo(req['bucketInfo'] as Record<string, string>)\n if (infoError) return this.error(400, infoError.code, infoError.message)\n }\n const updated: BucketInfo = {\n ...bucket.info,\n ...(req['bucketType'] !== undefined ? { bucketType: req['bucketType'] as BucketType } : {}),\n ...(req['bucketInfo'] !== undefined\n ? { bucketInfo: req['bucketInfo'] as Record<string, string> }\n : {}),\n ...(req['lifecycleRules'] !== undefined\n ? { lifecycleRules: req['lifecycleRules'] as BucketInfo['lifecycleRules'] }\n : {}),\n ...(req['corsRules'] !== undefined\n ? { corsRules: req['corsRules'] as BucketInfo['corsRules'] }\n : {}),\n ...(req['replicationConfiguration'] !== undefined\n ? {\n replicationConfiguration: req[\n 'replicationConfiguration'\n ] as BucketInfo['replicationConfiguration'],\n }\n : {}),\n ...(req['defaultRetention'] !== undefined\n ? {\n defaultRetention: req['defaultRetention'] as BucketInfo['defaultRetention'],\n fileLockConfiguration: {\n isClientAuthorizedToRead: true,\n value: {\n isFileLockEnabled:\n bucket.info.fileLockConfiguration.value?.isFileLockEnabled ?? false,\n defaultRetention: req['defaultRetention'] as BucketInfo['defaultRetention'],\n },\n },\n }\n : {}),\n ...(req['defaultServerSideEncryption'] !== undefined\n ? {\n defaultServerSideEncryption: req[\n 'defaultServerSideEncryption'\n ] as BucketInfo['defaultServerSideEncryption'],\n }\n : {}),\n revision: bucket.info.revision + 1,\n }\n this.buckets.set(req['bucketId'] as string, { info: updated, files: bucket.files })\n return { status: 200, body: updated }\n }\n\n private getUploadUrl(req: { bucketId: string }): SimulatorJsonResponse {\n if (!this.buckets.has(req.bucketId)) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n return {\n status: 200,\n body: {\n bucketId: req.bucketId,\n uploadUrl: `http://localhost:0/b2api/v3/b2_upload_file?bucketId=${req.bucketId}`,\n authorizationToken: 'sim_upload_token',\n },\n }\n }\n\n private listFileNames(req: {\n bucketId: string\n maxFileCount?: number\n prefix?: string\n startFileName?: string\n }): SimulatorJsonResponse {\n const bucket = this.buckets.get(req.bucketId)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n\n const countError = validateMaxCount(req.maxFileCount, 'b2_list_file_names')\n if (countError) return this.error(400, countError.code, countError.message)\n const max = req.maxFileCount ?? 1000\n const prefix = req.prefix ?? ''\n // Real B2: `b2_list_file_names` returns the most recent version per\n // file name. If that most-recent version is a hide marker (created via\n // `b2_hide_file`), it IS the row that gets returned, with\n // `action: 'hide'` and `contentLength: 0`. Filtering hide markers out\n // of the listing would diverge from production behaviour and hide a\n // real test seam: the action / SDK consumer must skip hide-action\n // entries when iterating over \"live\" files.\n let allFiles = [...bucket.files.entries()]\n .filter(([name]) => name.startsWith(prefix))\n .map(([_, versions]) => versions[versions.length - 1])\n .filter((v): v is StoredFile => v !== undefined)\n .map((v) => v.fileVersion)\n .sort((a, b) => a.fileName.localeCompare(b.fileName))\n\n if (req.startFileName) {\n const start = req.startFileName\n allFiles = allFiles.filter((f) => f.fileName >= start)\n }\n\n const files = allFiles.slice(0, max)\n const nextFileName = allFiles.length > max ? (allFiles[max]?.fileName ?? null) : null\n\n return { status: 200, body: { files, nextFileName } }\n }\n\n private listFileVersions(req: {\n bucketId: string\n maxFileCount?: number\n startFileName?: string\n startFileId?: string\n prefix?: string\n }): SimulatorJsonResponse {\n const bucket = this.buckets.get(req.bucketId)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n\n const countError = validateMaxCount(req.maxFileCount, 'b2_list_file_versions')\n if (countError) return this.error(400, countError.code, countError.message)\n const max = req.maxFileCount ?? 1000\n const prefix = req.prefix ?? ''\n const allVersions = [...bucket.files.entries()]\n .filter(([name]) => name.startsWith(prefix))\n .flatMap(([_, versions]) => versions.map((v) => v.fileVersion))\n .sort((a, b) => {\n const nameCmp = a.fileName.localeCompare(b.fileName)\n if (nameCmp !== 0) return nameCmp\n return b.uploadTimestamp - a.uploadTimestamp\n })\n\n // Pagination cursor: `(startFileName, startFileId)` is composite. B2\n // returns BOTH at a page boundary and expects the client to pass BOTH\n // back. Using only `startFileName` would miss intervening versions of\n // a file with many versions (page 2 would replay page 1's last entry\n // instead of resuming at the next version). The cursor is inclusive\n // on the start: callers replay the boundary entry as page N+1's first\n // item.\n let startIdx = 0\n if (req.startFileName !== undefined) {\n const startName = req.startFileName\n const startId = req.startFileId\n // Walk forward to the first entry that matches the cursor. Two\n // sub-cases: (a) `startFileId` was supplied — advance to the exact\n // (name, id) pair, falling back to the first entry of that name if\n // the id has been deleted; (b) no `startFileId` — advance to the\n // first entry whose name is >= `startFileName`.\n const nameIdx = allVersions.findIndex((f) => f.fileName >= startName)\n if (nameIdx === -1) {\n startIdx = allVersions.length\n } else if (startId !== undefined) {\n const exactIdx = allVersions.findIndex((f, i) => i >= nameIdx && f.fileId === startId)\n startIdx = exactIdx !== -1 ? exactIdx : nameIdx\n } else {\n startIdx = nameIdx\n }\n }\n\n const sliced = allVersions.slice(startIdx, startIdx + max)\n const hasMore = startIdx + max < allVersions.length\n const nextFileName = hasMore ? (allVersions[startIdx + max]?.fileName ?? null) : null\n const nextFileId = hasMore ? (allVersions[startIdx + max]?.fileId ?? null) : null\n\n return { status: 200, body: { files: sliced, nextFileName, nextFileId } }\n }\n\n private getFileInfo(req: { fileId: string }): SimulatorJsonResponse {\n const found = this.findFile(req.fileId)\n if (found === null) return this.error(404, 'file_not_present', 'File not found')\n return { status: 200, body: found.stored.fileVersion }\n }\n\n private hideFile(req: { bucketId: string; fileName: string }): SimulatorJsonResponse {\n const bucket = this.buckets.get(req.bucketId)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n\n const nameError = validateFileName(req.fileName)\n if (nameError) return this.error(400, nameError.code, nameError.message)\n\n const fileVersion = this.makeFileVersion({\n bucketId: req.bucketId,\n fileName: req.fileName,\n contentType: 'application/octet-stream',\n contentLength: 0,\n contentSha1: 'none',\n action: FileAction.Hide,\n })\n const existing = bucket.files.get(req.fileName)\n const stored: StoredFile = { fileVersion, data: new Uint8Array(0) }\n if (existing) {\n existing.push(stored)\n } else {\n bucket.files.set(req.fileName, [stored])\n }\n return { status: 200, body: fileVersion }\n }\n\n private deleteFileVersion(req: {\n fileId: string\n fileName: string\n bypassGovernance?: boolean\n }): {\n status: number\n body: unknown\n } {\n const found = this.findFile(req.fileId)\n if (found === null || found.stored.fileVersion.fileName !== req.fileName) {\n return this.error(400, 'file_not_present', 'File version not found')\n }\n\n // Object Lock enforcement. Real B2 surfaces three distinct error\n // codes for protected file versions; the simulator returns the same\n // shapes so test code exercising the typed `B2Error` hierarchy hits\n // realistic responses.\n const fv = found.stored.fileVersion\n const retention = fv.fileRetention?.value\n const legalHold = fv.legalHold?.value\n const now = Date.now()\n\n if (legalHold === 'on') {\n return this.error(\n 400,\n 'file_lock_legal_hold_protected',\n 'File is on legal hold and cannot be deleted',\n )\n }\n if (\n retention?.mode === 'compliance' &&\n retention.retainUntilTimestamp !== null &&\n retention.retainUntilTimestamp > now\n ) {\n return this.error(\n 400,\n 'file_lock_compliance_protected',\n `File is under compliance-mode retention and cannot be deleted until ${new Date(retention.retainUntilTimestamp).toISOString()}`,\n )\n }\n if (\n retention?.mode === 'governance' &&\n retention.retainUntilTimestamp !== null &&\n retention.retainUntilTimestamp > now &&\n req.bypassGovernance !== true\n ) {\n return this.error(\n 400,\n 'file_lock_governance_protected',\n 'File is under governance-mode retention; pass bypassGovernance: true to delete',\n )\n }\n\n found.versions.splice(found.index, 1)\n if (found.versions.length === 0) found.bucket.files.delete(req.fileName)\n return { status: 200, body: { fileId: req.fileId, fileName: req.fileName } }\n }\n\n private copyFile(req: { sourceFileId: string; fileName: string; destinationBucketId?: string }): {\n status: number\n body: unknown\n } {\n const nameError = validateFileName(req.fileName)\n if (nameError) return this.error(400, nameError.code, nameError.message)\n const found = this.findFile(req.sourceFileId)\n if (found === null) return this.error(404, 'file_not_present', 'Source file not found')\n const sourceStored = found.stored\n const destBucketId = req.destinationBucketId ?? found.bucketId\n const destBucket = this.buckets.get(destBucketId)\n if (!destBucket) return this.error(400, 'bad_bucket_id', 'Destination bucket not found')\n\n const fileVersion = this.makeFileVersion({\n bucketId: destBucketId,\n fileName: req.fileName,\n contentType: sourceStored.fileVersion.contentType,\n contentLength: sourceStored.data.byteLength,\n contentSha1: sourceStored.fileVersion.contentSha1 ?? 'none',\n action: FileAction.Copy,\n })\n const copied: StoredFile = { fileVersion, data: new Uint8Array(sourceStored.data) }\n const existing = destBucket.files.get(req.fileName)\n if (existing) {\n existing.push(copied)\n } else {\n destBucket.files.set(req.fileName, [copied])\n }\n\n this.firePostUploadHooks(fileVersion, destBucketId, 'b2:ObjectCreated:Copy')\n return { status: 200, body: fileVersion }\n }\n\n private startLargeFile(req: {\n bucketId: string\n fileName: string\n contentType: string\n fileInfo?: Record<string, string>\n }): SimulatorJsonResponse {\n if (!this.buckets.has(req.bucketId)) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n\n const nameError = validateFileName(req.fileName)\n if (nameError) return this.error(400, nameError.code, nameError.message)\n if (req.fileInfo !== undefined) {\n const infoError = validateFileInfo(req.fileInfo)\n if (infoError) return this.error(400, infoError.code, infoError.message)\n }\n\n const fid = this.genId('4_z')\n const large: LargeFileInProgress = {\n fileId: fid,\n bucketId: req.bucketId,\n fileName: req.fileName,\n contentType: req.contentType,\n fileInfo: req.fileInfo ?? {},\n parts: new Map(),\n }\n this.largeFiles.set(fid, large)\n\n return {\n status: 200,\n body: {\n fileId: fid,\n fileName: req.fileName,\n accountId: this.accountId,\n bucketId: req.bucketId,\n contentType: req.contentType,\n fileInfo: large.fileInfo,\n },\n }\n }\n\n private getUploadPartUrl(req: { fileId: string }): SimulatorJsonResponse {\n if (!this.largeFiles.has(req.fileId))\n return this.error(400, 'bad_request', 'Large file not found')\n return {\n status: 200,\n body: {\n fileId: req.fileId,\n uploadUrl: `http://localhost:0/b2api/v3/b2_upload_part?fileId=${req.fileId}`,\n authorizationToken: 'sim_part_token',\n },\n }\n }\n\n private finishLargeFile(req: { fileId: string; partSha1Array: string[] }): {\n status: number\n body: unknown\n } {\n const large = this.largeFiles.get(req.fileId)\n if (!large) return this.error(400, 'bad_request', 'Large file not found')\n\n const bucket = this.buckets.get(large.bucketId)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n\n const sortedParts = [...large.parts.entries()].sort((a, b) => a[0] - b[0])\n\n // B2 spec-compliance: hard cap of 10000 parts per multipart upload.\n // Real B2 rejects with `400 bad_request`; the simulator used to\n // accept any number of parts.\n if (sortedParts.length > 10_000) {\n return this.error(\n 400,\n 'bad_request',\n `multipart upload has ${sortedParts.length} parts; B2 caps at 10000`,\n )\n }\n // B2 spec-compliance: every part number must be in [1, 10000].\n // Real B2 rejects a part upload with an out-of-range partNumber\n // server-side; we enforce here at finish time as a backstop in case\n // a caller bypassed the upload-side validation (the simulator's\n // own `handleUploadPart` stores whatever number the client sends).\n for (const [partNumber] of sortedParts) {\n if (!Number.isInteger(partNumber) || partNumber < 1 || partNumber > 10_000) {\n return this.error(\n 400,\n 'bad_request',\n `partNumber ${partNumber} is outside the [1, 10000] range B2 accepts`,\n )\n }\n }\n // partSha1Array length must match the parts uploaded — real B2\n // rejects mismatches with `bad_request`.\n if (req.partSha1Array.length !== sortedParts.length) {\n return this.error(\n 400,\n 'bad_request',\n `partSha1Array has ${req.partSha1Array.length} entries but ${sortedParts.length} parts were uploaded`,\n )\n }\n // B2 spec-compliance: every non-last part must be at least\n // `absoluteMinimumPartSize`. The last part (highest part number)\n // may be smaller. We enforce here rather than at b2_upload_part\n // time because the simulator can't otherwise know which part is\n // the last until finish_large_file is called.\n for (let i = 0; i < sortedParts.length - 1; i++) {\n const [partNumber, part] = sortedParts[i] as [number, { data: Uint8Array; sha1: string }]\n if (part.data.byteLength < this.minimumPartSize) {\n return this.error(\n 400,\n 'bad_request',\n `part ${partNumber} (${part.data.byteLength} bytes) is smaller than the minimum part size of ${this.minimumPartSize}`,\n )\n }\n }\n\n let totalSize = 0\n for (const [_, part] of sortedParts) totalSize += part.data.byteLength\n const combined = new Uint8Array(totalSize)\n let offset = 0\n for (const [_, part] of sortedParts) {\n combined.set(part.data, offset)\n offset += part.data.byteLength\n }\n\n const fileVersion = this.makeFileVersion({\n bucketId: large.bucketId,\n fileName: large.fileName,\n contentType: large.contentType,\n contentLength: totalSize,\n contentSha1: 'none',\n action: FileAction.Upload,\n })\n const stored: StoredFile = { fileVersion, data: combined }\n const existing = bucket.files.get(large.fileName)\n if (existing) {\n existing.push(stored)\n } else {\n bucket.files.set(large.fileName, [stored])\n }\n\n this.largeFiles.delete(req.fileId)\n this.firePostUploadHooks(fileVersion, large.bucketId, 'b2:ObjectCreated:Upload')\n return { status: 200, body: fileVersion }\n }\n\n private cancelLargeFile(req: { fileId: string }): SimulatorJsonResponse {\n const large = this.largeFiles.get(req.fileId)\n if (!large) return this.error(400, 'bad_request', 'Large file not found')\n this.largeFiles.delete(req.fileId)\n return {\n status: 200,\n body: {\n fileId: large.fileId,\n accountId: this.accountId,\n bucketId: large.bucketId,\n fileName: large.fileName,\n },\n }\n }\n\n private listUnfinishedLargeFiles(req: {\n bucketId: string\n namePrefix?: string\n startFileId?: string\n maxFileCount?: number\n }): SimulatorJsonResponse {\n const countError = validateMaxCount(req.maxFileCount, 'b2_list_unfinished_large_files')\n if (countError) return this.error(400, countError.code, countError.message)\n const prefix = req.namePrefix ?? ''\n const max = req.maxFileCount ?? 100\n\n // Real B2 orders unfinished large files by fileName ascending. Sort\n // here so pagination (via startFileId) is deterministic.\n const candidates = [...this.largeFiles.values()]\n .filter((f) => f.bucketId === req.bucketId)\n .filter((f) => f.fileName.startsWith(prefix))\n .sort((a, b) => a.fileName.localeCompare(b.fileName))\n\n // `startFileId` is the cursor returned from a prior page. Skip past\n // (and including) the entry with that fileId.\n let startIndex = 0\n if (req.startFileId !== undefined) {\n const found = candidates.findIndex((f) => f.fileId === req.startFileId)\n startIndex = found >= 0 ? found : 0\n }\n\n const slice = candidates.slice(startIndex, startIndex + max)\n const files = slice.map((f) => ({\n fileId: f.fileId,\n fileName: f.fileName,\n accountId: this.accountId,\n bucketId: f.bucketId,\n contentType: f.contentType,\n fileInfo: f.fileInfo,\n }))\n const hasMore = startIndex + max < candidates.length\n const nextFileId = hasMore ? (candidates[startIndex + max]?.fileId ?? null) : null\n return { status: 200, body: { files, nextFileId } }\n }\n\n private listParts(req: {\n fileId: string\n startPartNumber?: number\n maxPartCount?: number\n }): SimulatorJsonResponse {\n const large = this.largeFiles.get(req.fileId)\n if (!large) return this.error(400, 'bad_request', 'Large file not found')\n\n const countError = validateMaxCount(req.maxPartCount, 'b2_list_parts')\n if (countError) return this.error(400, countError.code, countError.message)\n const start = req.startPartNumber ?? 1\n const max = req.maxPartCount ?? 1000\n\n const allParts = [...large.parts.entries()]\n .filter(([n]) => n >= start)\n .sort((a, b) => a[0] - b[0])\n .map(([partNumber, part]) => ({\n fileId: req.fileId,\n partNumber,\n contentLength: part.data.byteLength,\n contentSha1: part.sha1,\n uploadTimestamp: Date.now(),\n }))\n\n const parts = allParts.slice(0, max)\n const nextPartNumber = allParts.length > max ? (allParts[max]?.partNumber ?? null) : null\n\n return { status: 200, body: { parts, nextPartNumber } }\n }\n\n private async copyPart(req: {\n sourceFileId: string\n largeFileId: string\n partNumber: number\n range?: string\n }): Promise<SimulatorJsonResponse> {\n const large = this.largeFiles.get(req.largeFileId)\n if (!large) return this.error(400, 'bad_request', 'Large file not found')\n\n const found = this.findFile(req.sourceFileId)\n if (found === null) return this.error(404, 'file_not_present', 'Source file not found')\n const sourceStored = found.stored\n\n let partData = sourceStored.data\n if (req.range) {\n const match = req.range.match(/bytes=(\\d+)-(\\d+)?/)\n if (match) {\n const rs = Number.parseInt(match[1] ?? '0', 10)\n const re =\n match[2] !== undefined ? Number.parseInt(match[2], 10) : sourceStored.data.byteLength - 1\n partData = sourceStored.data.slice(rs, re + 1)\n }\n }\n\n // Hash the part data so list_parts can return a real SHA-1.\n // sha1Hex is isomorphic (node:crypto in Node, WebCrypto in browsers).\n const sha1 = await sha1Hex(partData)\n large.parts.set(req.partNumber, { data: new Uint8Array(partData), sha1 })\n\n return {\n status: 200,\n body: {\n fileId: req.largeFileId,\n partNumber: req.partNumber,\n contentLength: partData.byteLength,\n contentSha1: sha1,\n },\n }\n }\n\n private getDownloadAuthorization(req: {\n bucketId: string\n fileNamePrefix: string\n validDurationInSeconds: number\n }): SimulatorJsonResponse {\n if (!this.buckets.has(req.bucketId)) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n return {\n status: 200,\n body: {\n bucketId: req.bucketId,\n fileNamePrefix: req.fileNamePrefix,\n authorizationToken: `sim_dl_auth_${this.genId('tok')}`,\n },\n }\n }\n\n // --- Keys ---\n\n private createKey(req: {\n accountId: string\n capabilities: string[]\n keyName: string\n validDurationInSeconds?: number\n bucketId?: string\n namePrefix?: string\n }): SimulatorJsonResponse {\n const kid = this.genId('sim_key')\n const appKey = this.genId('sim_secret')\n const expiration =\n req.validDurationInSeconds !== undefined\n ? Date.now() + req.validDurationInSeconds * 1000\n : null\n const stored: StoredKey = {\n applicationKeyId: kid,\n keyName: req.keyName,\n capabilities: req.capabilities,\n accountId: req.accountId,\n applicationKey: appKey,\n bucketId: req.bucketId ?? null,\n namePrefix: req.namePrefix ?? null,\n expirationTimestamp: expiration,\n }\n this.keys.set(kid, stored)\n\n return {\n status: 200,\n body: {\n keyName: stored.keyName,\n applicationKeyId: stored.applicationKeyId,\n applicationKey: stored.applicationKey,\n capabilities: stored.capabilities,\n accountId: stored.accountId,\n expirationTimestamp: stored.expirationTimestamp,\n bucketId: stored.bucketId,\n namePrefix: stored.namePrefix,\n options: [],\n },\n }\n }\n\n private listKeys(req: {\n accountId: string\n maxKeyCount?: number\n startApplicationKeyId?: string\n }): SimulatorJsonResponse {\n const countError = validateMaxCount(req.maxKeyCount, 'b2_list_keys')\n if (countError) return this.error(400, countError.code, countError.message)\n const max = req.maxKeyCount ?? 1000\n let allKeys = [...this.keys.values()].sort((a, b) =>\n a.applicationKeyId.localeCompare(b.applicationKeyId),\n )\n\n if (req.startApplicationKeyId) {\n const start = req.startApplicationKeyId\n allKeys = allKeys.filter((k) => k.applicationKeyId >= start)\n }\n\n const keys = allKeys.slice(0, max).map((k) => ({\n keyName: k.keyName,\n applicationKeyId: k.applicationKeyId,\n capabilities: k.capabilities,\n accountId: k.accountId,\n expirationTimestamp: k.expirationTimestamp,\n bucketId: k.bucketId,\n namePrefix: k.namePrefix,\n options: [],\n }))\n\n const nextId = allKeys.length > max ? (allKeys[max]?.applicationKeyId ?? null) : null\n\n return { status: 200, body: { keys, nextApplicationKeyId: nextId } }\n }\n\n private deleteKey(req: { applicationKeyId: string }): SimulatorJsonResponse {\n const key = this.keys.get(req.applicationKeyId)\n if (!key) return this.error(400, 'bad_request', 'Key not found')\n this.keys.delete(req.applicationKeyId)\n // Evict every issued auth token whose backing application key was\n // just revoked. Real B2 invalidates the token immediately on\n // `b2_delete_key`; before this fix, tokens minted from a deleted\n // key kept passing strict-auth until their TTL expired.\n for (const [token, scope] of this.issuedTokens.entries()) {\n if (scope.applicationKeyId === req.applicationKeyId) {\n this.issuedTokens.delete(token)\n }\n }\n return {\n status: 200,\n body: {\n keyName: key.keyName,\n applicationKeyId: key.applicationKeyId,\n capabilities: key.capabilities,\n accountId: key.accountId,\n expirationTimestamp: key.expirationTimestamp,\n bucketId: key.bucketId,\n namePrefix: key.namePrefix,\n options: [],\n },\n }\n }\n\n // --- File lock ---\n\n private updateFileRetention(req: {\n fileName: string\n fileId: string\n fileRetention: { mode: RetentionMode | null; retainUntilTimestamp: number | null }\n }): SimulatorJsonResponse {\n const found = this.findFile(req.fileId)\n if (found === null || found.stored.fileVersion.fileName !== req.fileName) {\n return this.error(404, 'file_not_present', 'File not found')\n }\n found.versions[found.index] = {\n fileVersion: {\n ...found.stored.fileVersion,\n fileRetention: { isClientAuthorizedToRead: true, value: req.fileRetention },\n },\n data: found.stored.data,\n }\n return {\n status: 200,\n body: {\n fileName: req.fileName,\n fileId: req.fileId,\n fileRetention: req.fileRetention,\n },\n }\n }\n\n private updateFileLegalHold(req: {\n fileName: string\n fileId: string\n legalHold: string\n }): SimulatorJsonResponse {\n const found = this.findFile(req.fileId)\n if (found === null || found.stored.fileVersion.fileName !== req.fileName) {\n return this.error(404, 'file_not_present', 'File not found')\n }\n found.versions[found.index] = {\n fileVersion: {\n ...found.stored.fileVersion,\n legalHold: {\n isClientAuthorizedToRead: true,\n value: req.legalHold as 'on' | 'off',\n },\n },\n data: found.stored.data,\n }\n return {\n status: 200,\n body: {\n fileName: req.fileName,\n fileId: req.fileId,\n legalHold: req.legalHold,\n },\n }\n }\n\n // --- Notifications ---\n\n private getBucketNotificationRules(req: { bucketId: string }): {\n status: number\n body: unknown\n } {\n if (!this.buckets.has(req.bucketId)) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n const rules = this.notificationRules.get(req.bucketId) ?? []\n return { status: 200, body: { bucketId: req.bucketId, eventNotificationRules: rules } }\n }\n\n private setBucketNotificationRules(req: {\n bucketId: string\n eventNotificationRules: EventNotificationRule[]\n }): SimulatorJsonResponse {\n if (!this.buckets.has(req.bucketId)) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n this.notificationRules.set(req.bucketId, req.eventNotificationRules)\n return {\n status: 200,\n body: { bucketId: req.bucketId, eventNotificationRules: req.eventNotificationRules },\n }\n }\n\n // --- Helpers ---\n\n /**\n * Locates a stored file version by its `fileId`, scanning every bucket.\n *\n * Returns enough context to support read-only inspection (`stored`,\n * `bucketId`) AND in-place mutation (`versions`, `index`) so callers\n * that need to splice the version out can do so without re-scanning.\n *\n * Real B2 fileIds embed the bucketId, so production lookups are O(1);\n * the simulator's flat ID generator (`this.genId('4_z')`) doesn't, so this\n * is O(buckets × files × versions). Acceptable for tests.\n *\n * @param fileId - The file version ID to locate.\n *\n * @returns The location of the matching version, or `null` if not found.\n */\n private findFile(fileId: string): {\n stored: StoredFile\n bucketId: string\n bucket: StoredBucket\n versions: StoredFile[]\n index: number\n } | null {\n for (const [bid, bucket] of this.buckets.entries()) {\n for (const versions of bucket.files.values()) {\n const idx = versions.findIndex((v) => v.fileVersion.fileId === fileId)\n if (idx !== -1) {\n // Non-null asserted via the findIndex guard above.\n const stored = versions[idx] as StoredFile\n return { stored, bucketId: bid, bucket, versions, index: idx }\n }\n }\n }\n return null\n }\n\n private makeFileVersion(params: {\n readonly bucketId: string\n readonly fileName: string\n readonly contentType: string\n readonly contentLength: number\n readonly contentSha1: string\n readonly action: FileAction\n }): FileVersion {\n return {\n accountId: accountIdOf(this.accountId),\n action: params.action,\n bucketId: bucketIdOf(params.bucketId),\n contentLength: params.contentLength,\n contentMd5: null,\n contentSha1: params.contentSha1,\n contentType: params.contentType,\n fileId: fileIdOf(this.genId('4_z')),\n fileInfo: {},\n fileName: params.fileName,\n fileRetention: { isClientAuthorizedToRead: true, value: null },\n legalHold: { isClientAuthorizedToRead: true, value: null },\n replicationStatus: null,\n serverSideEncryption: { mode: EncryptionMode.None },\n uploadTimestamp: this.monotonicTimestamp(),\n }\n }\n\n private error(status: number, code: string, message: string): SimulatorJsonResponse {\n return { status, body: { status, code, message } }\n }\n\n /**\n * Fire the pluggable post-upload hooks for a file that just landed in\n * a bucket: matching event-notification rules → `onWebhookDeliver`,\n * configured replication source rules → `onReplicate`. Errors thrown\n * from user-supplied hooks are swallowed so a buggy listener never\n * masks an otherwise-successful API response.\n *\n * Fired-and-forgotten (no await) by the handler so the synthetic\n * response is returned to the caller as fast as production B2 would\n * acknowledge the write.\n *\n * @param fileVersion - The freshly-stored file metadata.\n * @param bucketId - The bucket the upload landed in.\n * @param eventType - The B2 event-type tag (e.g. `'b2:ObjectCreated:Upload'`)\n * used to match against `EventNotificationRule.eventTypes` globs.\n */\n private firePostUploadHooks(fileVersion: FileVersion, bucketId: string, eventType: string): void {\n const bucket = this.buckets.get(bucketId)\n if (!bucket) return\n if (this.onWebhookDeliver !== undefined) {\n const rules = this.notificationRules.get(bucketId) ?? []\n for (const rule of rules) {\n if (!rule.isEnabled) continue\n const matches = rule.eventTypes.some((pattern) => eventTypeMatches(pattern, eventType))\n if (!matches) continue\n const hook = this.onWebhookDeliver\n this.dispatchHook('webhook', () => hook({ rule, fileVersion, bucketId }))\n }\n }\n if (this.onReplicate !== undefined) {\n const replConfig = bucket.info.replicationConfiguration\n const sourceRules = replConfig.asReplicationSource?.replicationRules ?? []\n for (const rule of sourceRules) {\n if (!rule.isEnabled) continue\n if (rule.fileNamePrefix && !fileVersion.fileName.startsWith(rule.fileNamePrefix)) continue\n const hook = this.onReplicate\n this.dispatchHook('replication', () =>\n hook({\n sourceFileVersion: fileVersion,\n sourceBucketId: bucketId,\n destinationBucketId: rule.destinationBucketId,\n }),\n )\n }\n }\n }\n\n /**\n * Schedule a user-supplied hook callback. Errors are routed to\n * `onHookError` if configured, swallowed otherwise. The returned\n * promise is tracked in {@link pendingHooks} so `flushHooks()` can\n * wait for every dispatched hook to settle.\n *\n * @param kind - Which hook this is (`'webhook'` or `'replication'`).\n * @param fn - Thunk that performs the hook invocation.\n */\n private dispatchHook(kind: 'webhook' | 'replication', fn: () => Promise<void> | void): void {\n const task = Promise.resolve()\n .then(() => fn())\n .catch((err) => {\n if (this.onHookError !== undefined) {\n this.onHookError({ kind, error: toError(err) })\n }\n })\n .finally(() => {\n this.pendingHooks.delete(task)\n })\n this.pendingHooks.add(task)\n }\n}\n\n/**\n * Module-local cache of compiled glob regexes. Avoids recompiling the\n * same `eventTypes` pattern on every upload's hook dispatch — patterns\n * are stable for the lifetime of a `setBucketNotificationRules` call\n * and typically reused across thousands of uploads.\n */\nconst eventTypeRegexCache = new Map<string, RegExp>()\n\n/**\n * Wildcard match for B2 event-type globs. Supports the prefix-glob form\n * the B2 docs document: `b2:ObjectCreated:*` matches\n * `b2:ObjectCreated:Upload`, `b2:ObjectCreated:Copy`, etc. Exact\n * matches without `*` match literally.\n *\n * @param pattern - Glob from an `EventNotificationRule.eventTypes` entry.\n * @param eventType - Concrete event type produced by the simulator.\n *\n * @returns `true` when the pattern matches the event type.\n */\nfunction eventTypeMatches(pattern: string, eventType: string): boolean {\n if (!pattern.includes('*')) return pattern === eventType\n let compiled = eventTypeRegexCache.get(pattern)\n if (compiled === undefined) {\n // Escape regex metacharacters, then replace literal `*` with `.*`.\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*')\n compiled = new RegExp(`^${escaped}$`)\n eventTypeRegexCache.set(pattern, compiled)\n }\n return compiled.test(eventType)\n}\n\n/**\n * Build a synthetic {@link HttpResponse} from a consumed {@link FaultSpec}.\n * Mirrors the shape of real B2 error responses so the SDK's\n * `RetryTransport` / `classifyError` paths see realistic input.\n *\n * @param fault - The fault spec to render.\n *\n * @returns An `HttpResponse` ready to return from `transport.send`.\n */\nfunction buildFaultResponse(fault: FaultSpec): HttpResponse {\n const status = fault.status ?? 503\n const code = fault.code ?? 'service_unavailable'\n const message = fault.message ?? 'simulated failure'\n const body = JSON.stringify({ status, code, message })\n const headers = new Headers({ 'Content-Type': 'application/json' })\n if (fault.retryAfter !== undefined) {\n headers.set('Retry-After', String(fault.retryAfter))\n }\n return {\n status,\n headers,\n body: new ReadableStream({\n start(controller) {\n controller.enqueue(utf8Encoder.encode(body))\n controller.close()\n },\n }),\n json: <T>() => Promise.resolve(JSON.parse(body) as T),\n text: () => Promise.resolve(body),\n arrayBuffer: () => Promise.resolve(utf8Encoder.encode(body).buffer as ArrayBuffer),\n }\n}\n\nclass SimulatorTransport implements HttpTransport {\n constructor(private readonly sim: B2Simulator) {}\n\n async send(request: HttpRequest): Promise<HttpResponse> {\n const url = request.url\n\n // Fault injection: synthetic failures registered via\n // `B2Simulator.injectFailure()` run BEFORE any real handler, so a\n // matched request never reaches in-memory state. This is what\n // exercises the SDK's retry / classification paths against\n // realistic error responses in tests.\n const fault = this.sim.consumeMatchingFault(url)\n if (fault !== null) {\n return buildFaultResponse(fault)\n }\n\n const headers: Record<string, string> = {}\n if (request.headers) {\n for (const [k, v] of Object.entries(request.headers)) {\n headers[k.toLowerCase()] = v\n }\n }\n\n const isUpload = url.includes('b2_upload_file') || url.includes('b2_upload_part')\n const parsedUrl = new URL(url)\n const isDownload =\n parsedUrl.pathname.includes('b2_download_file_by_id') || parsedUrl.pathname.includes('/file/')\n\n if (isDownload) {\n const method = request.method === 'HEAD' ? 'HEAD' : 'GET'\n const result = this.sim.handleDownload(parsedUrl.pathname + parsedUrl.search, headers, method)\n const data = result.data ?? new Uint8Array(0)\n const responseHeaders = new Headers(result.headers)\n responseHeaders.set(\n 'Content-Type',\n result.headers['Content-Type'] ?? 'application/octet-stream',\n )\n\n // HEAD responses have no body but keep all headers (matches HTTP semantics).\n const body =\n method === 'HEAD' || result.data === null\n ? null\n : new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(data)\n controller.close()\n },\n })\n\n return {\n status: result.status,\n headers: responseHeaders,\n body,\n json: () => Promise.reject(new Error('Download response is not JSON')),\n text: () => Promise.resolve(utf8Decoder.decode(data)),\n arrayBuffer: () =>\n Promise.resolve(\n data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer,\n ),\n }\n }\n\n let result: { status: number; body: unknown }\n\n if (isUpload && request.body) {\n const data = new Uint8Array(\n request.body instanceof ArrayBuffer\n ? request.body\n : request.body instanceof Uint8Array\n ? request.body.buffer.slice(\n request.body.byteOffset,\n request.body.byteOffset + request.body.byteLength,\n )\n : await new Response(request.body).arrayBuffer(),\n )\n result = this.sim.handleUpload(url, headers, data)\n } else {\n let body: unknown = null\n if (request.body) {\n const text =\n typeof request.body === 'string' ? request.body : await new Response(request.body).text()\n try {\n body = JSON.parse(text)\n } catch {\n body = text\n }\n }\n result = await this.sim.handleRequest(request.method, parsedUrl.pathname, headers, body)\n }\n\n const responseBody = JSON.stringify(result.body)\n return {\n status: result.status,\n headers: new Headers({ 'Content-Type': 'application/json' }),\n body: new ReadableStream({\n start(controller) {\n controller.enqueue(utf8Encoder.encode(responseBody))\n controller.close()\n },\n }),\n json: <T>() => Promise.resolve(result.body as T),\n text: () => Promise.resolve(responseBody),\n arrayBuffer: () => Promise.resolve(utf8Encoder.encode(responseBody).buffer as ArrayBuffer),\n }\n }\n}\n"],"names":["missingCapabilitiesFor","utf8Encoder","bucket","validateFileName","validateFileInfo","FileAction","EncryptionMode","capabilities","Capability","accountIdOf","validateBucketName","validateBucketInfo","bucketIdOf","BucketRetentionMode","validateMaxCount","sha1Hex","fileIdOf","toError","result","utf8Decoder"],"mappings":"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"}
|