@backblaze-labs/b2-sdk 0.3.0 → 0.4.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 +45 -1
- package/README.md +18 -5
- package/dist/_virtual/_b2-sdk-version-json.cjs +1 -1
- package/dist/_virtual/_b2-sdk-version-json.js +1 -1
- package/dist/auth/reauth-coalescer.cjs +65 -0
- package/dist/auth/reauth-coalescer.cjs.map +1 -0
- package/dist/auth/reauth-coalescer.d.cts +34 -0
- package/dist/auth/reauth-coalescer.d.cts.map +1 -0
- package/dist/auth/reauth-coalescer.d.ts +34 -0
- package/dist/auth/reauth-coalescer.d.ts.map +1 -0
- package/dist/auth/reauth-coalescer.js +65 -0
- package/dist/auth/reauth-coalescer.js.map +1 -0
- package/dist/backup/raw.cjs +37 -15
- package/dist/backup/raw.cjs.map +1 -1
- package/dist/backup/raw.d.cts +2 -2
- package/dist/backup/raw.d.cts.map +1 -1
- package/dist/backup/raw.d.ts +2 -2
- package/dist/backup/raw.d.ts.map +1 -1
- package/dist/backup/raw.js +37 -15
- package/dist/backup/raw.js.map +1 -1
- package/dist/bucket-validation.cjs +111 -0
- package/dist/bucket-validation.cjs.map +1 -0
- package/dist/bucket-validation.d.cts +27 -0
- package/dist/bucket-validation.d.cts.map +1 -0
- package/dist/bucket-validation.d.ts +27 -0
- package/dist/bucket-validation.d.ts.map +1 -0
- package/dist/bucket-validation.js +109 -0
- package/dist/bucket-validation.js.map +1 -0
- package/dist/bucket.cjs +123 -73
- package/dist/bucket.cjs.map +1 -1
- package/dist/bucket.d.cts +156 -61
- package/dist/bucket.d.cts.map +1 -1
- package/dist/bucket.d.ts +156 -61
- package/dist/bucket.d.ts.map +1 -1
- package/dist/bucket.js +123 -73
- package/dist/bucket.js.map +1 -1
- package/dist/client.cjs +29 -8
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +30 -9
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.ts +30 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +29 -8
- package/dist/client.js.map +1 -1
- package/dist/download/single.cjs +38 -13
- package/dist/download/single.cjs.map +1 -1
- package/dist/download/single.d.cts +10 -4
- package/dist/download/single.d.cts.map +1 -1
- package/dist/download/single.d.ts +10 -4
- package/dist/download/single.d.ts.map +1 -1
- package/dist/download/single.js +38 -13
- package/dist/download/single.js.map +1 -1
- package/dist/errors/index.cjs +27 -4
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +15 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +15 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +27 -5
- package/dist/errors/index.js.map +1 -1
- package/dist/http/non-idempotent-mutations.cjs +38 -0
- package/dist/http/non-idempotent-mutations.cjs.map +1 -0
- package/dist/http/non-idempotent-mutations.d.cts +25 -0
- package/dist/http/non-idempotent-mutations.d.cts.map +1 -0
- package/dist/http/non-idempotent-mutations.d.ts +25 -0
- package/dist/http/non-idempotent-mutations.d.ts.map +1 -0
- package/dist/http/non-idempotent-mutations.js +35 -0
- package/dist/http/non-idempotent-mutations.js.map +1 -0
- package/dist/http/transport.cjs +9 -8
- package/dist/http/transport.cjs.map +1 -1
- package/dist/http/transport.d.cts +5 -0
- package/dist/http/transport.d.cts.map +1 -1
- package/dist/http/transport.d.ts +5 -0
- package/dist/http/transport.d.ts.map +1 -1
- package/dist/http/transport.js +9 -8
- package/dist/http/transport.js.map +1 -1
- package/dist/http/user-agent.cjs +12 -1
- package/dist/http/user-agent.cjs.map +1 -1
- package/dist/http/user-agent.d.cts +8 -0
- package/dist/http/user-agent.d.cts.map +1 -1
- package/dist/http/user-agent.d.ts +8 -0
- package/dist/http/user-agent.d.ts.map +1 -1
- package/dist/http/user-agent.js +13 -3
- package/dist/http/user-agent.js.map +1 -1
- package/dist/index.cjs +25 -0
- package/dist/index.d.cts +10 -9
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/notifications/index.cjs +3 -0
- package/dist/notifications/index.d.cts +2 -0
- package/dist/notifications/index.d.cts.map +1 -1
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +2 -1
- package/dist/object.cjs +12 -1
- package/dist/object.cjs.map +1 -1
- package/dist/object.d.cts +7 -1
- package/dist/object.d.cts.map +1 -1
- package/dist/object.d.ts +7 -1
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +12 -1
- package/dist/object.js.map +1 -1
- package/dist/partner/auth-core.cjs +4 -23
- package/dist/partner/auth-core.cjs.map +1 -1
- package/dist/partner/auth-core.d.cts +1 -1
- package/dist/partner/auth-core.d.cts.map +1 -1
- package/dist/partner/auth-core.d.ts +1 -1
- package/dist/partner/auth-core.d.ts.map +1 -1
- package/dist/partner/auth-core.js +5 -24
- package/dist/partner/auth-core.js.map +1 -1
- package/dist/partner/client.cjs +25 -10
- package/dist/partner/client.cjs.map +1 -1
- package/dist/partner/client.d.cts +32 -10
- package/dist/partner/client.d.cts.map +1 -1
- package/dist/partner/client.d.ts +32 -10
- package/dist/partner/client.d.ts.map +1 -1
- package/dist/partner/client.js +25 -10
- package/dist/partner/client.js.map +1 -1
- package/dist/partner/endpoint-url.cjs +1 -0
- package/dist/partner/endpoint-url.cjs.map +1 -1
- package/dist/partner/endpoint-url.d.cts +2 -0
- package/dist/partner/endpoint-url.d.cts.map +1 -1
- package/dist/partner/endpoint-url.d.ts +2 -0
- package/dist/partner/endpoint-url.d.ts.map +1 -1
- package/dist/partner/endpoint-url.js +1 -0
- package/dist/partner/endpoint-url.js.map +1 -1
- package/dist/partner/in-memory.cjs.map +1 -1
- package/dist/partner/in-memory.js.map +1 -1
- package/dist/partner/index.cjs +9 -0
- package/dist/partner/index.d.cts +11 -2
- package/dist/partner/index.d.cts.map +1 -1
- package/dist/partner/index.d.ts +11 -2
- package/dist/partner/index.d.ts.map +1 -1
- package/dist/partner/index.js +3 -1
- package/dist/partner/raw.cjs +52 -44
- package/dist/partner/raw.cjs.map +1 -1
- package/dist/partner/raw.d.cts +16 -19
- package/dist/partner/raw.d.cts.map +1 -1
- package/dist/partner/raw.d.ts +16 -19
- package/dist/partner/raw.d.ts.map +1 -1
- package/dist/partner/raw.js +52 -44
- package/dist/partner/raw.js.map +1 -1
- package/dist/partner/redaction.cjs +133 -108
- package/dist/partner/redaction.cjs.map +1 -1
- package/dist/partner/redaction.d.cts +47 -13
- package/dist/partner/redaction.d.cts.map +1 -1
- package/dist/partner/redaction.d.ts +47 -13
- package/dist/partner/redaction.d.ts.map +1 -1
- package/dist/partner/redaction.js +133 -109
- package/dist/partner/redaction.js.map +1 -1
- package/dist/raw/encoding.cjs +29 -10
- package/dist/raw/encoding.cjs.map +1 -1
- package/dist/raw/encoding.d.cts +3 -2
- package/dist/raw/encoding.d.cts.map +1 -1
- package/dist/raw/encoding.d.ts +3 -2
- package/dist/raw/encoding.d.ts.map +1 -1
- package/dist/raw/encoding.js +29 -10
- package/dist/raw/encoding.js.map +1 -1
- package/dist/raw/index.cjs +64 -39
- package/dist/raw/index.cjs.map +1 -1
- package/dist/raw/index.d.cts +21 -19
- package/dist/raw/index.d.cts.map +1 -1
- package/dist/raw/index.d.ts +21 -19
- package/dist/raw/index.d.ts.map +1 -1
- package/dist/raw/index.js +47 -40
- package/dist/raw/index.js.map +1 -1
- package/dist/raw/url.cjs.map +1 -1
- package/dist/raw/url.d.cts +3 -3
- package/dist/raw/url.d.ts +3 -3
- package/dist/raw/url.js.map +1 -1
- package/dist/simulator/index.cjs +456 -125
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.cts +16 -1
- package/dist/simulator/index.d.cts.map +1 -1
- package/dist/simulator/index.d.ts +16 -1
- package/dist/simulator/index.d.ts.map +1 -1
- package/dist/simulator/index.js +449 -130
- package/dist/simulator/index.js.map +1 -1
- package/dist/simulator/partner-capabilities.cjs +77 -0
- package/dist/simulator/partner-capabilities.cjs.map +1 -0
- package/dist/simulator/partner-capabilities.d.cts +53 -0
- package/dist/simulator/partner-capabilities.d.cts.map +1 -0
- package/dist/simulator/partner-capabilities.d.ts +53 -0
- package/dist/simulator/partner-capabilities.d.ts.map +1 -0
- package/dist/simulator/partner-capabilities.js +73 -0
- package/dist/simulator/partner-capabilities.js.map +1 -0
- package/dist/simulator/partner.cjs +55 -61
- package/dist/simulator/partner.cjs.map +1 -1
- package/dist/simulator/partner.d.cts +5 -2
- package/dist/simulator/partner.d.cts.map +1 -1
- package/dist/simulator/partner.d.ts +5 -2
- package/dist/simulator/partner.d.ts.map +1 -1
- package/dist/simulator/partner.js +56 -61
- package/dist/simulator/partner.js.map +1 -1
- package/dist/simulator/validation.cjs +160 -30
- package/dist/simulator/validation.cjs.map +1 -1
- package/dist/simulator/validation.d.cts +28 -6
- package/dist/simulator/validation.d.cts.map +1 -1
- package/dist/simulator/validation.d.ts +28 -6
- package/dist/simulator/validation.d.ts.map +1 -1
- package/dist/simulator/validation.js +148 -31
- package/dist/simulator/validation.js.map +1 -1
- package/dist/streams/file-source.cjs +1 -1
- package/dist/streams/file-source.cjs.map +1 -1
- package/dist/streams/file-source.js +1 -1
- package/dist/streams/file-source.js.map +1 -1
- package/dist/streams/hash.cjs +246 -13
- package/dist/streams/hash.cjs.map +1 -1
- package/dist/streams/hash.d.cts +37 -12
- package/dist/streams/hash.d.cts.map +1 -1
- package/dist/streams/hash.d.ts +37 -12
- package/dist/streams/hash.d.ts.map +1 -1
- package/dist/streams/hash.js +246 -15
- package/dist/streams/hash.js.map +1 -1
- package/dist/streams/index.cjs +2 -0
- package/dist/streams/index.d.cts +1 -1
- package/dist/streams/index.d.cts.map +1 -1
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.d.ts.map +1 -1
- package/dist/streams/index.js +2 -2
- package/dist/streams/progress.cjs +63 -4
- package/dist/streams/progress.cjs.map +1 -1
- package/dist/streams/progress.d.cts +28 -2
- package/dist/streams/progress.d.cts.map +1 -1
- package/dist/streams/progress.d.ts +28 -2
- package/dist/streams/progress.d.ts.map +1 -1
- package/dist/streams/progress.js +63 -4
- package/dist/streams/progress.js.map +1 -1
- package/dist/sync/download-staging.cjs +147 -35
- package/dist/sync/download-staging.cjs.map +1 -1
- package/dist/sync/download-staging.d.cts +25 -2
- package/dist/sync/download-staging.d.cts.map +1 -1
- package/dist/sync/download-staging.d.ts +25 -2
- package/dist/sync/download-staging.d.ts.map +1 -1
- package/dist/sync/download-staging.js +146 -36
- package/dist/sync/download-staging.js.map +1 -1
- package/dist/sync/index.d.cts +1 -1
- package/dist/sync/index.d.cts.map +1 -1
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/local-file-io.cjs +5 -1
- package/dist/sync/local-file-io.cjs.map +1 -1
- package/dist/sync/local-file-io.d.cts +2 -0
- package/dist/sync/local-file-io.d.cts.map +1 -1
- package/dist/sync/local-file-io.d.ts +2 -0
- package/dist/sync/local-file-io.d.ts.map +1 -1
- package/dist/sync/local-file-io.js +5 -1
- package/dist/sync/local-file-io.js.map +1 -1
- package/dist/sync/pairing.cjs +1 -0
- package/dist/sync/pairing.cjs.map +1 -1
- package/dist/sync/pairing.js +1 -0
- package/dist/sync/pairing.js.map +1 -1
- package/dist/sync/scanners/b2.cjs +2 -0
- package/dist/sync/scanners/b2.cjs.map +1 -1
- package/dist/sync/scanners/b2.d.cts.map +1 -1
- package/dist/sync/scanners/b2.d.ts.map +1 -1
- package/dist/sync/scanners/b2.js +2 -0
- package/dist/sync/scanners/b2.js.map +1 -1
- package/dist/sync/scanners/local.cjs +150 -33
- package/dist/sync/scanners/local.cjs.map +1 -1
- package/dist/sync/scanners/local.d.cts +6 -0
- package/dist/sync/scanners/local.d.cts.map +1 -1
- package/dist/sync/scanners/local.d.ts +6 -0
- package/dist/sync/scanners/local.d.ts.map +1 -1
- package/dist/sync/scanners/local.js +150 -33
- package/dist/sync/scanners/local.js.map +1 -1
- package/dist/sync/synchronizer.cjs +76 -29
- package/dist/sync/synchronizer.cjs.map +1 -1
- package/dist/sync/synchronizer.d.cts +1 -0
- package/dist/sync/synchronizer.d.cts.map +1 -1
- package/dist/sync/synchronizer.d.ts +1 -0
- package/dist/sync/synchronizer.d.ts.map +1 -1
- package/dist/sync/synchronizer.js +76 -29
- package/dist/sync/synchronizer.js.map +1 -1
- package/dist/sync/types.d.cts +19 -3
- package/dist/sync/types.d.cts.map +1 -1
- package/dist/sync/types.d.ts +19 -3
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/types/backup.d.cts +6 -1
- package/dist/types/backup.d.cts.map +1 -1
- package/dist/types/backup.d.ts +6 -1
- package/dist/types/backup.d.ts.map +1 -1
- package/dist/types/bucket.cjs +81 -11
- package/dist/types/bucket.cjs.map +1 -1
- package/dist/types/bucket.d.cts +190 -39
- package/dist/types/bucket.d.cts.map +1 -1
- package/dist/types/bucket.d.ts +190 -39
- package/dist/types/bucket.d.ts.map +1 -1
- package/dist/types/bucket.js +67 -12
- package/dist/types/bucket.js.map +1 -1
- package/dist/types/download.cjs.map +1 -1
- package/dist/types/download.d.cts +3 -17
- package/dist/types/download.d.cts.map +1 -1
- package/dist/types/download.d.ts +3 -17
- package/dist/types/download.d.ts.map +1 -1
- package/dist/types/download.js.map +1 -1
- package/dist/types/encryption.cjs +18 -0
- package/dist/types/encryption.cjs.map +1 -1
- package/dist/types/encryption.d.cts +21 -11
- package/dist/types/encryption.d.cts.map +1 -1
- package/dist/types/encryption.d.ts +21 -11
- package/dist/types/encryption.d.ts.map +1 -1
- package/dist/types/encryption.js +18 -0
- package/dist/types/encryption.js.map +1 -1
- package/dist/types/file.cjs +3 -0
- package/dist/types/file.cjs.map +1 -1
- package/dist/types/file.d.cts +125 -16
- package/dist/types/file.d.cts.map +1 -1
- package/dist/types/file.d.ts +125 -16
- package/dist/types/file.d.ts.map +1 -1
- package/dist/types/file.js +3 -1
- package/dist/types/file.js.map +1 -1
- package/dist/types/index.d.cts +7 -7
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/key.d.cts +4 -3
- package/dist/types/key.d.cts.map +1 -1
- package/dist/types/key.d.ts +4 -3
- package/dist/types/key.d.ts.map +1 -1
- package/dist/types/notifications.cjs +30 -0
- package/dist/types/notifications.cjs.map +1 -1
- package/dist/types/notifications.d.cts +26 -1
- package/dist/types/notifications.d.cts.map +1 -1
- package/dist/types/notifications.d.ts +26 -1
- package/dist/types/notifications.d.ts.map +1 -1
- package/dist/types/notifications.js +29 -1
- package/dist/types/notifications.js.map +1 -1
- package/dist/types/partner.cjs.map +1 -1
- package/dist/types/partner.d.cts +28 -36
- package/dist/types/partner.d.cts.map +1 -1
- package/dist/types/partner.d.ts +28 -36
- package/dist/types/partner.d.ts.map +1 -1
- package/dist/types/partner.js.map +1 -1
- package/dist/types/upload.d.cts +4 -0
- package/dist/types/upload.d.cts.map +1 -1
- package/dist/types/upload.d.ts +4 -0
- package/dist/types/upload.d.ts.map +1 -1
- package/dist/upload/large.cjs +10 -7
- package/dist/upload/large.cjs.map +1 -1
- package/dist/upload/large.d.cts +15 -6
- package/dist/upload/large.d.cts.map +1 -1
- package/dist/upload/large.d.ts +15 -6
- package/dist/upload/large.d.ts.map +1 -1
- package/dist/upload/large.js +10 -7
- package/dist/upload/large.js.map +1 -1
- package/dist/upload/options.cjs.map +1 -1
- package/dist/upload/options.d.cts +12 -0
- package/dist/upload/options.d.cts.map +1 -1
- package/dist/upload/options.d.ts +12 -0
- package/dist/upload/options.d.ts.map +1 -1
- package/dist/upload/options.js.map +1 -1
- package/dist/upload/resume.cjs +4 -3
- package/dist/upload/resume.cjs.map +1 -1
- package/dist/upload/resume.d.cts +6 -4
- package/dist/upload/resume.d.cts.map +1 -1
- package/dist/upload/resume.d.ts +6 -4
- package/dist/upload/resume.d.ts.map +1 -1
- package/dist/upload/resume.js +4 -3
- package/dist/upload/resume.js.map +1 -1
- package/dist/upload/retry.cjs +17 -10
- package/dist/upload/retry.cjs.map +1 -1
- package/dist/upload/retry.d.cts.map +1 -1
- package/dist/upload/retry.d.ts.map +1 -1
- package/dist/upload/retry.js +17 -10
- package/dist/upload/retry.js.map +1 -1
- package/dist/upload/single.cjs +35 -31
- package/dist/upload/single.cjs.map +1 -1
- package/dist/upload/single.d.cts +6 -0
- package/dist/upload/single.d.cts.map +1 -1
- package/dist/upload/single.d.ts +6 -0
- package/dist/upload/single.d.ts.map +1 -1
- package/dist/upload/single.js +35 -31
- package/dist/upload/single.js.map +1 -1
- package/dist/upload/stream.cjs +12 -1
- package/dist/upload/stream.cjs.map +1 -1
- package/dist/upload/stream.d.cts +6 -0
- package/dist/upload/stream.d.cts.map +1 -1
- package/dist/upload/stream.d.ts +6 -0
- package/dist/upload/stream.d.ts.map +1 -1
- package/dist/upload/stream.js +12 -1
- package/dist/upload/stream.js.map +1 -1
- package/dist/version.cjs +56 -4
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +53 -4
- package/dist/version.d.cts.map +1 -1
- package/dist/version.d.ts +53 -4
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +52 -5
- package/dist/version.js.map +1 -1
- package/package.json +11 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"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 { encodeFileName } from '../raw/encoding.ts'\nimport { type B2ApiVersion, b2Url, isB2ApiVersion } from '../raw/url.ts'\nimport { sha1Hex } from '../streams/hash.ts'\nimport { Capability } from '../types/auth.ts'\nimport { type BucketInfo, BucketRetentionMode, type BucketType } from '../types/bucket.ts'\nimport { DownloadClientUnauthorizedToReadMarker, DownloadHeaderName } from '../types/download.ts'\nimport {\n EncryptionAlgorithm,\n EncryptionMode,\n type EncryptionSetting,\n type PublicEncryptionSetting,\n} from '../types/encryption.ts'\nimport { FileAction, type FileVersion, type ReplicationStatus } from '../types/file.ts'\nimport {\n type AuthToken,\n accountId as accountIdOf,\n type BucketId,\n bucketId as bucketIdOf,\n fileId as fileIdOf,\n} from '../types/ids.ts'\nimport { type FileRetentionValue, LegalHoldValue, RetentionMode } from '../types/lock.ts'\nimport type { EventNotificationRule } from '../types/notifications.ts'\nimport type { ReplicationRule } from '../types/replication.ts'\nimport { hexEncode, hmacSha256 } from '../util/crypto.ts'\nimport { md5Base64, md5Base64Sync } from '../util/md5.ts'\nimport { utf8Decoder, utf8Encoder } from '../util/text-codec.ts'\nimport { toError } from '../util/to-error.ts'\nimport { isPartnerQueryEndpoint, PartnerSimulator } from './partner.ts'\n\nconst UPLOAD_TOKEN_SIGNING_KEY = ['b2 sdk typescript', 'simulator upload authorization', 'v1'].join(\n ':',\n)\nconst SIMULATOR_MASTER_APPLICATION_KEY_ID = 'master-key-id'\nconst SIMULATOR_MASTER_APPLICATION_KEY = 'master-key'\nconst SIMULATOR_TEST_APPLICATION_KEY_ID = 'test-key-id'\nconst SIMULATOR_TEST_APPLICATION_KEY = 'test-key'\nconst SIMULATOR_APPLICATION_KEY_ID_PREFIX = 'sim_key_'\n// Master capabilities granted to the simulator's implicit storage credentials.\n// Object Lock capabilities are intentionally omitted: real B2 does not\n// auto-grant them; tests that need them create an explicit key.\nconst SIMULATOR_MASTER_CAPABILITIES: readonly Capability[] = [\n Capability.ListBuckets,\n Capability.ListAllBucketNames,\n Capability.ReadBuckets,\n Capability.WriteBuckets,\n Capability.DeleteBuckets,\n Capability.ReadBucketEncryption,\n Capability.WriteBucketEncryption,\n Capability.ReadBucketReplications,\n Capability.WriteBucketReplications,\n Capability.ReadBucketNotifications,\n Capability.WriteBucketNotifications,\n Capability.ReadBucketLogging,\n Capability.WriteBucketLogging,\n Capability.ReadBucketLifecycleRules,\n Capability.WriteBucketLifecycleRules,\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]\n\nfunction apiPathParts(path: string): { endpoint: string; version: B2ApiVersion } {\n const segments = path.split('/').filter((segment) => segment.length > 0)\n const candidate = segments.at(-2)\n return {\n endpoint: segments.at(-1) ?? '',\n version: candidate !== undefined && isB2ApiVersion(candidate) ? candidate : 'v3',\n }\n}\n\ninterface BasicCredentials {\n readonly applicationKeyId: string\n readonly applicationKey: string\n}\n\nfunction parseBasicAuthorizationHeader(authzHeader: string | undefined): BasicCredentials | null {\n if (!authzHeader?.startsWith('Basic ')) return null\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 <= 0 || idx === decoded.length - 1) return null\n return {\n applicationKeyId: decoded.slice(0, idx),\n applicationKey: decoded.slice(idx + 1),\n }\n}\n\nconst B2_MIN_PART_NUMBER = 1\nconst B2_MAX_PART_NUMBER = 10_000\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = ''\n for (const byte of bytes) {\n binary += String.fromCharCode(byte)\n }\n return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '')\n}\n\nfunction base64UrlDecode(encoded: string): Uint8Array | null {\n const base64 = encoded.replaceAll('-', '+').replaceAll('_', '/')\n const padding = (4 - (base64.length % 4)) % 4\n try {\n const binary = atob(base64.padEnd(base64.length + padding, '='))\n return Uint8Array.from(binary, (char) => char.charCodeAt(0))\n } catch {\n return null\n }\n}\n\nfunction timingSafeStringEqual(left: string, right: string): boolean {\n const max = Math.max(left.length, right.length)\n let mismatch = left.length === right.length ? 0 : 1\n for (let i = 0; i < max; i++) {\n mismatch |= (left.charCodeAt(i) || 0) ^ (right.charCodeAt(i) || 0)\n }\n return mismatch === 0\n}\n\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\nconst MAX_JS_DATE_MS = 8.64e15\n\nfunction isValidRetentionTimestamp(timestamp: number, now = Date.now()): boolean {\n return Number.isFinite(timestamp) && timestamp <= MAX_JS_DATE_MS && timestamp > now\n}\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}\n\nfunction decodeHeaderValue(value: string): string {\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n}\n\n/**\n * Compares B2 file names with deterministic JS string order, not locale collation.\n *\n * @param a - First file name.\n * @param b - Second file name.\n *\n * @returns `-1` when `a` sorts first, `1` when `b` sorts first, otherwise `0`.\n */\nfunction compareB2FileNames(a: string, b: string): number {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n}\n\nimport { missingCapabilitiesFor } from './capabilities.ts'\nimport {\n normalizeCreateKeyCapabilities,\n type ValidationError,\n validateBucketInfo,\n validateBucketName,\n validateBucketTypes,\n validateCorsRules,\n validateCreateKeyCapabilities,\n validateCreateKeyName,\n validateDefaultRetention,\n validateDownloadAuthorizationDuration,\n validateDownloadAuthorizationPrefix,\n validateFileInfo,\n validateFileName,\n validateLifecycleRules,\n validateMaxCount,\n validateNotificationRules,\n validateReplicationConfiguration,\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 DOWNLOAD_AUTH_DURATION_MAX_SECONDS,\n DOWNLOAD_AUTH_DURATION_MIN_SECONDS,\n FILE_INFO_TOTAL_MAX,\n FILE_INFO_VALUE_MAX,\n FILE_NAME_MAX_BYTES,\n KEY_NAME_MAX,\n KEY_NAME_MIN,\n LIST_ENDPOINT_CAPS,\n} from './validation.ts'\n\nconst DOWNLOAD_AUTH_TOKEN_BYTES = 32\nconst DOWNLOAD_AUTH_TOKEN_PREFIX = 'sim_dl_auth_'\nconst DOWNLOAD_AUTH_PURGE_BATCH_SIZE = 128\n\nconst DOWNLOAD_RESPONSE_OVERRIDE_PARAMS = [\n 'b2ContentDisposition',\n 'b2ContentLanguage',\n 'b2ContentEncoding',\n 'b2ContentType',\n 'b2CacheControl',\n 'b2Expires',\n] as const\n\ntype DownloadResponseOverrideParam = (typeof DOWNLOAD_RESPONSE_OVERRIDE_PARAMS)[number]\ntype DownloadResponseOverrides = Partial<Record<DownloadResponseOverrideParam, string>>\n\nconst DOWNLOAD_RESPONSE_OVERRIDE_HEADERS: Record<DownloadResponseOverrideParam, string> = {\n b2ContentDisposition: DownloadHeaderName.ContentDisposition,\n b2ContentLanguage: DownloadHeaderName.ContentLanguage,\n b2ContentEncoding: DownloadHeaderName.ContentEncoding,\n b2ContentType: DownloadHeaderName.ContentType,\n b2CacheControl: DownloadHeaderName.CacheControl,\n b2Expires: DownloadHeaderName.Expires,\n}\n\ntype DownloadAuthorizationRequestBody = {\n bucketId: string\n fileNamePrefix: string\n validDurationInSeconds: number\n} & Partial<Record<DownloadResponseOverrideParam, unknown>>\n\nfunction randomDownloadAuthorizationToken(): string {\n const crypto = globalThis.crypto\n if (typeof crypto?.getRandomValues !== 'function') {\n throw new Error('Download authorization tokens require globalThis.crypto.getRandomValues')\n }\n const bytes = new Uint8Array(DOWNLOAD_AUTH_TOKEN_BYTES)\n crypto.getRandomValues(bytes)\n return `${DOWNLOAD_AUTH_TOKEN_PREFIX}${hexEncode(bytes)}`\n}\n\nfunction pickDownloadResponseOverrides(\n req: Partial<Record<DownloadResponseOverrideParam, unknown>>,\n): DownloadResponseOverrides {\n const overrides: DownloadResponseOverrides = {}\n for (const param of DOWNLOAD_RESPONSE_OVERRIDE_PARAMS) {\n const value = req[param]\n if (typeof value === 'string') overrides[param] = value\n }\n return overrides\n}\n\nfunction validateDownloadResponseOverrides(\n req: Partial<Record<DownloadResponseOverrideParam, unknown>>,\n): ValidationError | null {\n for (const param of DOWNLOAD_RESPONSE_OVERRIDE_PARAMS) {\n const value = req[param]\n if (value === undefined || typeof value === 'string') continue\n return { code: 'bad_request', message: `${param} must be a string` }\n }\n return null\n}\n\ninterface StoredFile {\n readonly fileVersion: FileVersion\n readonly data: Uint8Array\n readonly serverSideEncryption: StoredServerSideEncryption\n}\n\ntype StoredServerSideEncryption =\n | { readonly mode: 'none' }\n | { readonly mode: 'SSE-B2'; readonly algorithm: EncryptionAlgorithm }\n | {\n readonly mode: 'SSE-C'\n readonly algorithm: EncryptionAlgorithm\n readonly customerKeyDigest: string\n readonly customerKeyMd5: string\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 fileRetention: FileRetentionValue | null\n readonly legalHold: LegalHoldValue | null\n readonly replicationStatus?: ReplicationStatus\n readonly serverSideEncryption: StoredServerSideEncryption\n readonly uploadTimestamp: number\n readonly parts: Map<number, StoredLargeFilePart>\n}\n\ninterface StoredLargeFilePart {\n readonly data: Uint8Array\n readonly sha1: string\n readonly contentMd5: string | null\n readonly uploadTimestamp: number\n}\n\ninterface StoredKey {\n readonly applicationKeyId: string\n readonly keyName: string\n readonly capabilities: readonly Capability[]\n readonly accountId: string\n readonly applicationKey: string\n readonly bucketIds: readonly string[] | null\n readonly namePrefix: string | null\n readonly expirationTimestamp: number | null\n}\n\ninterface AuthorizationGrant {\n readonly accountId: string\n readonly capabilities: readonly Capability[]\n readonly bucketIds: readonly string[] | null\n readonly namePrefix: string | null\n readonly applicationKeyId: string | null\n readonly expirationTimestamp: number | null\n}\n\ntype AuthorizationGrantResolution =\n | { readonly kind: 'ok'; readonly grant: AuthorizationGrant }\n | { readonly kind: 'invalid' }\n | { readonly kind: 'expired' }\n\ninterface IssuedToken {\n readonly accountId: string\n readonly capabilities: readonly Capability[]\n readonly bucketIds: readonly string[] | null\n readonly namePrefix: string | null\n readonly 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 B2Simulator.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 readonly applicationKeyId: string | null\n}\n\ntype UploadTokenKind = 'file' | 'part'\n\ninterface UploadTokenPayload {\n readonly v: 1\n readonly kind: UploadTokenKind\n readonly fileName: string | null\n readonly uploadUrl: string\n readonly namePrefix: string | null\n readonly applicationKeyId: string | null\n readonly expiresAt: number\n}\n\ninterface StoredUploadToken {\n readonly kind: UploadTokenKind\n readonly fileName: string | null\n readonly uploadUrl: string\n readonly namePrefix: string | null\n readonly applicationKeyId: string | null\n expiresAt: number\n readonly cleanupAt: number\n invalidated: boolean\n}\n\ninterface DownloadAuthorizationToken {\n readonly bucketId: string\n readonly fileNamePrefix: string\n readonly expiresAt: number\n readonly responseHeaderOverrides: DownloadResponseOverrides\n}\n\ninterface DownloadAuthorizationExpiry {\n readonly token: string\n readonly expiresAt: number\n}\n\ninterface RequestScope {\n readonly bucketIds: readonly string[]\n readonly fileNames?: readonly string[]\n readonly requiresBucketScope: boolean\n readonly requiresAccountLevelBucketAccess?: boolean\n}\n\nfunction normalizeKeyBucketIds(req: {\n bucketIds?: readonly string[] | null\n}): readonly string[] | null {\n return req.bucketIds === undefined || req.bucketIds === null\n ? null\n : Object.freeze([...req.bucketIds])\n}\n\nfunction singleBucketId(bucketIds: readonly string[] | null | undefined): string | null {\n return bucketIds?.length === 1 ? (bucketIds[0] ?? null) : null\n}\n\nfunction cloneBucketIds(bucketIds: readonly string[] | null): readonly string[] | null {\n return bucketIds === null ? null : [...bucketIds]\n}\n\nfunction cloneCapabilities(capabilities: readonly Capability[]): readonly Capability[] {\n return [...capabilities]\n}\n\nfunction isImplicitStorageMasterCredential(credentials: BasicCredentials): boolean {\n return (\n (credentials.applicationKeyId === SIMULATOR_MASTER_APPLICATION_KEY_ID &&\n credentials.applicationKey === SIMULATOR_MASTER_APPLICATION_KEY) ||\n (credentials.applicationKeyId === SIMULATOR_TEST_APPLICATION_KEY_ID &&\n credentials.applicationKey === SIMULATOR_TEST_APPLICATION_KEY)\n )\n}\n\nfunction isImplicitPartnerMasterCredential(credentials: BasicCredentials): boolean {\n return (\n credentials.applicationKeyId === SIMULATOR_MASTER_APPLICATION_KEY_ID &&\n credentials.applicationKey === SIMULATOR_MASTER_APPLICATION_KEY\n )\n}\n\nfunction isSimulatorApplicationKeyId(applicationKeyId: string): boolean {\n return applicationKeyId.startsWith(SIMULATOR_APPLICATION_KEY_ID_PREFIX)\n}\n\nfunction hasOwnField(body: unknown, field: string): boolean {\n return typeof body === 'object' && body !== null && Object.hasOwn(body, field)\n}\n\nfunction requestStringField(body: unknown, field: string): string | undefined {\n if (typeof body !== 'object' || body === null) return undefined\n const value = (body as Record<string, unknown>)[field]\n return typeof value === 'string' ? value : undefined\n}\n\nfunction requestRecord(body: unknown): Record<string, unknown> | null {\n return typeof body === 'object' && body !== null && !Array.isArray(body)\n ? (body as Record<string, unknown>)\n : null\n}\n\nfunction replicationDestinationBucketIds(body: unknown): readonly string[] {\n const req = requestRecord(body)\n const replicationConfiguration = requestRecord(req?.['replicationConfiguration'])\n const source = requestRecord(replicationConfiguration?.['asReplicationSource'])\n const rules = source?.['replicationRules']\n if (!Array.isArray(rules)) return []\n\n const bucketIds: string[] = []\n for (const rule of rules) {\n const destinationBucketId = requestRecord(rule)?.['destinationBucketId']\n if (typeof destinationBucketId === 'string') bucketIds.push(destinationBucketId)\n }\n return [...new Set(bucketIds)]\n}\n\nfunction normalizeReplicationConfiguration(\n config: BucketInfo['replicationConfiguration'],\n): BucketInfo['replicationConfiguration'] {\n return {\n asReplicationSource: config.asReplicationSource ?? null,\n asReplicationDestination: config.asReplicationDestination ?? null,\n }\n}\n\ninterface BucketConfigurationFields {\n readonly bucketInfo?: unknown\n readonly corsRules?: unknown\n readonly defaultRetention?: unknown\n readonly lifecycleRules?: unknown\n readonly replicationConfiguration?: unknown\n}\n\nfunction validateBucketConfigurationFields(\n fields: BucketConfigurationFields,\n options: { readonly objectLockEnabled: boolean },\n): ValidationError | null {\n if (fields.bucketInfo !== undefined) {\n const infoError = validateBucketInfo(fields.bucketInfo as Record<string, string>)\n if (infoError) return infoError\n }\n if (fields.corsRules !== undefined) {\n const corsError = validateCorsRules(fields.corsRules)\n if (corsError) return corsError\n }\n if (fields.lifecycleRules !== undefined) {\n const lifecycleError = validateLifecycleRules(fields.lifecycleRules)\n if (lifecycleError) return lifecycleError\n }\n if (fields.replicationConfiguration !== undefined) {\n const replicationError = validateReplicationConfiguration(fields.replicationConfiguration)\n if (replicationError) return replicationError\n }\n if (fields.defaultRetention !== undefined) {\n const retentionError = validateDefaultRetention(fields.defaultRetention)\n if (retentionError) return retentionError\n if (\n requestRecord(fields.defaultRetention)?.['mode'] !== BucketRetentionMode.None &&\n !options.objectLockEnabled\n ) {\n return {\n code: 'file_lock_not_enabled',\n message: 'Bucket must have Object Lock enabled to set defaultRetention',\n }\n }\n }\n return null\n}\n\nfunction nextObjectLockEnabled(current: boolean, requested: unknown): boolean | ValidationError {\n if (requested === undefined) return current\n if (typeof requested !== 'boolean') {\n return { code: 'bad_request', message: 'fileLockEnabled must be a boolean' }\n }\n if (current && requested === false) {\n return { code: 'file_lock_conflict', message: 'Object Lock cannot be disabled' }\n }\n return current || requested\n}\n\nfunction requestHeaderValue(headers: Record<string, string>, name: string): string | undefined {\n const exact = headers[name]\n if (exact !== undefined) return exact\n const lowerName = name.toLowerCase()\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) return value\n }\n return undefined\n}\n\ntype ContentLengthHeader =\n | { readonly kind: 'absent' }\n | { readonly kind: 'ok'; readonly expectedLength: number }\n | { readonly kind: 'error'; readonly message: string }\n\nfunction parseContentLengthHeader(headers: Record<string, string>): ContentLengthHeader {\n const header = requestHeaderValue(headers, 'content-length')\n if (header === undefined) return { kind: 'absent' }\n if (!/^\\d+$/.test(header)) {\n return { kind: 'error', message: `Content-Length must be a byte count: ${header}` }\n }\n const expectedLength = Number(header)\n if (!Number.isSafeInteger(expectedLength)) {\n return { kind: 'error', message: `Content-Length is too large: ${header}` }\n }\n return { kind: 'ok', expectedLength }\n}\n\nfunction contentLengthMismatchMessage(expectedLength: number, actualLength: number): string {\n return `Content-Length ${expectedLength} does not match request body length ${actualLength}`\n}\n\nfunction fileNames(...names: readonly (string | undefined)[]): readonly string[] | undefined {\n const present = names.filter((name): name is string => name !== undefined)\n return present.length > 0 ? present : undefined\n}\n\nfunction queryParamsBody(params: URLSearchParams): Record<string, string> | null {\n const body = Object.create(null) as Record<string, string>\n let hasParams = false\n for (const [key, value] of params) {\n if (body[key] !== undefined) continue\n body[key] = value\n hasParams = true\n }\n return hasParams ? body : null\n}\n\nconst JSON_GET_ENDPOINTS = new Set<string>([\n 'b2_authorize_account',\n 'b2_list_groups',\n 'b2_list_group_members',\n 'bz_list_computers',\n])\n\nconst JSON_POST_ENDPOINTS = new Set<string>([\n 'b2_create_group_member',\n 'b2_eject_group_member',\n 'b2_reserve_trial_create_account',\n 'bz_delete_computer',\n 'b2_create_bucket',\n 'b2_list_buckets',\n 'b2_delete_bucket',\n 'b2_update_bucket',\n 'b2_get_upload_url',\n 'b2_list_file_names',\n 'b2_list_file_versions',\n 'b2_get_file_info',\n 'b2_hide_file',\n 'b2_delete_file_version',\n 'b2_copy_file',\n 'b2_start_large_file',\n 'b2_get_upload_part_url',\n 'b2_finish_large_file',\n 'b2_cancel_large_file',\n 'b2_list_unfinished_large_files',\n 'b2_list_parts',\n 'b2_copy_part',\n 'b2_get_download_authorization',\n 'b2_create_key',\n 'b2_list_keys',\n 'b2_delete_key',\n 'b2_update_file_retention',\n 'b2_update_file_legal_hold',\n 'b2_get_bucket_notification_rules',\n 'b2_set_bucket_notification_rules',\n])\n\nfunction jsonEndpointAllowsMethod(method: string, endpoint: string): boolean {\n const normalizedMethod = method.toUpperCase()\n if (JSON_GET_ENDPOINTS.has(endpoint)) return normalizedMethod === 'GET'\n if (JSON_POST_ENDPOINTS.has(endpoint)) return normalizedMethod === 'POST'\n return true\n}\n\nfunction notificationRulePrefixes(body: unknown): readonly string[] | undefined {\n if (typeof body !== 'object' || body === null) return undefined\n const rules = (body as Record<string, unknown>)['eventNotificationRules']\n if (!Array.isArray(rules)) return undefined\n return rules.map((rule) => {\n if (typeof rule !== 'object' || rule === null) return ''\n const objectNamePrefix = (rule as Record<string, unknown>)['objectNamePrefix']\n return typeof objectNamePrefix === 'string' ? objectNamePrefix : ''\n })\n}\n\nfunction storedNotificationRulePrefixes(\n rules: readonly EventNotificationRule[] | undefined,\n): readonly string[] {\n return (rules ?? []).map((rule) =>\n typeof rule.objectNamePrefix === 'string' ? rule.objectNamePrefix : '',\n )\n}\n\nfunction hasKeyManagementCapability(capabilities: readonly Capability[]): boolean {\n return capabilities.some(\n (capability) =>\n capability === Capability.ListKeys ||\n capability === Capability.WriteKeys ||\n capability === Capability.DeleteKeys,\n )\n}\n\nfunction missingStoredKeyCapabilities(\n key: StoredKey,\n required: readonly Capability[],\n): readonly Capability[] {\n const capabilities = new Set(key.capabilities)\n return required.filter((capability) => !capabilities.has(capability))\n}\n\nfunction storedKeyAllowsBucket(key: StoredKey, bucketId: string): boolean {\n return key.bucketIds === null || key.bucketIds.includes(bucketId)\n}\n\nfunction publicServerSideEncryption(\n encryption: EncryptionSetting | StoredServerSideEncryption,\n): PublicEncryptionSetting {\n if (encryption.mode === EncryptionMode.SseC) {\n return { mode: encryption.mode, algorithm: encryption.algorithm }\n }\n if (encryption.mode === EncryptionMode.None) {\n return { mode: null, algorithm: null }\n }\n return encryption\n}\n\nfunction customerKeyDigest(customerKey: Uint8Array): string {\n let first = 0x811c9dc5\n let second = 0x27d4eb2d\n for (const byte of customerKey) {\n first = Math.imul(first ^ byte, 0x01000193) >>> 0\n second = Math.imul(second ^ byte, 0x85ebca6b) >>> 0\n }\n return `${first.toString(16).padStart(8, '0')}${second.toString(16).padStart(8, '0')}`\n}\n\nconst BASE64_ALPHABET = /^[A-Za-z0-9+/]+$/\n\nfunction normalizeSizedBase64(value: string, decodedByteLength: number): string | null {\n const paddedLength = Math.ceil(decodedByteLength / 3) * 4\n const noPaddingLength = Math.ceil((decodedByteLength * 8) / 6)\n const padding = '='.repeat(paddedLength - noPaddingLength)\n\n if (value.length === noPaddingLength && BASE64_ALPHABET.test(value)) {\n return value.padEnd(paddedLength, '=')\n }\n\n if (\n value.length === paddedLength &&\n value.endsWith(padding) &&\n BASE64_ALPHABET.test(value.slice(0, noPaddingLength))\n ) {\n return value\n }\n\n return null\n}\n\nfunction base64ToBytes(value: string, decodedByteLength: number): Uint8Array | null {\n const normalized = normalizeSizedBase64(value, decodedByteLength)\n if (normalized === null) return null\n try {\n const decoded = atob(normalized)\n if (decoded.length !== decodedByteLength) return null\n const bytes = new Uint8Array(decoded.length)\n for (let i = 0; i < decoded.length; i++) bytes[i] = decoded.charCodeAt(i)\n return bytes\n } catch {\n return null\n }\n}\n\nfunction encryptionValidationError(message: string): SimulatorJsonResponse {\n return { status: 400, body: { status: 400, code: 'bad_request', message } }\n}\n\nfunction sameStoredServerSideEncryption(\n a: StoredServerSideEncryption,\n b: StoredServerSideEncryption,\n): boolean {\n if (a.mode !== b.mode) return false\n if (a.mode === EncryptionMode.None) return true\n if (a.mode === EncryptionMode.SseB2 && b.mode === EncryptionMode.SseB2) {\n return a.algorithm === b.algorithm\n }\n if (a.mode === EncryptionMode.SseC && b.mode === EncryptionMode.SseC) {\n return (\n a.algorithm === b.algorithm &&\n a.customerKeyDigest === b.customerKeyDigest &&\n a.customerKeyMd5 === b.customerKeyMd5\n )\n }\n return false\n}\n\nfunction hasCustomerEncryptionFields(encryption: Record<string, unknown>): boolean {\n return (\n encryption['customerKey'] !== undefined ||\n encryption['customerKeyMd5'] !== undefined ||\n encryption['customerAlgorithm'] !== undefined\n )\n}\n\nfunction hasCustomerEncryptionHeaders(headers: Record<string, string>): boolean {\n return (\n headers['x-bz-server-side-encryption-customer-algorithm'] !== undefined ||\n headers['x-bz-server-side-encryption-customer-key'] !== undefined ||\n headers['x-bz-server-side-encryption-customer-key-md5'] !== undefined\n )\n}\n\nasync function storedServerSideEncryption(\n encryption: EncryptionSetting,\n): Promise<StoredServerSideEncryption | SimulatorJsonResponse> {\n const runtimeEncryption = encryption as unknown as Record<string, unknown>\n if (encryption.mode === EncryptionMode.SseC) {\n if (runtimeEncryption['algorithm'] === undefined) {\n return encryptionValidationError('SSE-C customer algorithm is required')\n }\n if (encryption.algorithm !== EncryptionAlgorithm.Aes256) {\n return encryptionValidationError('SSE-C customer algorithm must be AES256')\n }\n const customerKey = encryption.customerKey\n const customerKeyMd5 = encryption.customerKeyMd5\n if (typeof customerKey !== 'string' || customerKey === '') {\n return encryptionValidationError('SSE-C customer key is required')\n }\n if (typeof customerKeyMd5 !== 'string' || customerKeyMd5 === '') {\n return encryptionValidationError('SSE-C customer key MD5 is required')\n }\n const customerKeyBytes = base64ToBytes(customerKey, 32)\n if (customerKeyBytes === null) {\n return encryptionValidationError('SSE-C customer key must be a base64-encoded 256-bit key')\n }\n const normalizedCustomerKeyMd5 = normalizeSizedBase64(customerKeyMd5, 16)\n if (normalizedCustomerKeyMd5 === null) {\n return encryptionValidationError(\n 'SSE-C customer key MD5 must be a base64-encoded 128-bit digest',\n )\n }\n const actualMd5 = await md5Base64(customerKeyBytes)\n if (actualMd5 !== normalizedCustomerKeyMd5) {\n return encryptionValidationError('SSE-C customer key MD5 does not match the key')\n }\n return {\n mode: encryption.mode,\n algorithm: encryption.algorithm,\n customerKeyDigest: customerKeyDigest(customerKeyBytes),\n customerKeyMd5: normalizedCustomerKeyMd5,\n }\n }\n if (encryption.mode === EncryptionMode.SseB2) {\n if (hasCustomerEncryptionFields(runtimeEncryption)) {\n return encryptionValidationError('SSE-B2 settings must not include SSE-C customer keys')\n }\n if (encryption.algorithm !== EncryptionAlgorithm.Aes256) {\n return encryptionValidationError('SSE-B2 algorithm must be AES256')\n }\n return { mode: encryption.mode, algorithm: encryption.algorithm }\n }\n if (encryption.mode === EncryptionMode.None) {\n if (hasCustomerEncryptionFields(runtimeEncryption)) {\n return encryptionValidationError(\n 'No-encryption settings must not include SSE-C customer keys',\n )\n }\n return { mode: EncryptionMode.None }\n }\n\n return encryptionValidationError(\n `Unsupported server-side encryption mode: ${String(runtimeEncryption['mode'])}`,\n )\n}\n\nasync function uploadServerSideEncryption(\n headers: Record<string, string>,\n fallback: EncryptionSetting,\n): Promise<StoredServerSideEncryption | SimulatorJsonResponse> {\n const customerAlgorithm = headers['x-bz-server-side-encryption-customer-algorithm']\n const customerKey = headers['x-bz-server-side-encryption-customer-key']\n const customerKeyMd5 = headers['x-bz-server-side-encryption-customer-key-md5']\n const managedEncryption = headers['x-bz-server-side-encryption']\n if (hasCustomerEncryptionHeaders(headers)) {\n if (managedEncryption !== undefined) {\n return encryptionValidationError('SSE-B2 settings must not include SSE-C customer keys')\n }\n if (customerAlgorithm === undefined) {\n return encryptionValidationError('SSE-C customer algorithm is required')\n }\n if (customerAlgorithm !== EncryptionAlgorithm.Aes256) {\n return encryptionValidationError('SSE-C customer algorithm must be AES256')\n }\n if (!customerKey || !customerKeyMd5) {\n return encryptionValidationError('SSE-C customer key and MD5 are required')\n }\n return await storedServerSideEncryption({\n mode: EncryptionMode.SseC,\n algorithm: EncryptionAlgorithm.Aes256,\n customerKey,\n customerKeyMd5,\n })\n }\n\n if (managedEncryption !== undefined) {\n if (managedEncryption !== EncryptionAlgorithm.Aes256) {\n return encryptionValidationError('SSE-B2 algorithm must be AES256')\n }\n return { mode: EncryptionMode.SseB2, algorithm: EncryptionAlgorithm.Aes256 }\n }\n\n return await storedServerSideEncryption(fallback)\n}\n\nfunction defaultFileRetention(\n policy: BucketInfo['defaultRetention'],\n uploadTimestamp: number,\n): FileRetentionValue | null {\n if (policy.mode === BucketRetentionMode.None || policy.period === null) return null\n const days = policy.period.unit === 'days' ? policy.period.duration : policy.period.duration * 365\n const retainUntilTimestamp = uploadTimestamp + days * MS_PER_DAY\n if (!isValidRetentionTimestamp(retainUntilTimestamp, uploadTimestamp)) return null\n return {\n mode: policy.mode as RetentionMode,\n retainUntilTimestamp,\n }\n}\n\nfunction isRetentionMode(value: unknown): value is RetentionMode {\n return value === RetentionMode.Compliance || value === RetentionMode.Governance\n}\n\nfunction parseFileRetentionValue(value: unknown, now: number): FileRetentionValue | null {\n if (typeof value !== 'object' || value === null) return null\n const candidate = value as Record<string, unknown>\n const mode = candidate['mode']\n const retainUntilTimestamp = candidate['retainUntilTimestamp']\n\n if (mode === null && retainUntilTimestamp === null) {\n return { mode, retainUntilTimestamp }\n }\n if (isRetentionMode(mode) && typeof retainUntilTimestamp === 'number') {\n if (!isValidRetentionTimestamp(retainUntilTimestamp, now)) return null\n return { mode, retainUntilTimestamp }\n }\n return null\n}\n\nfunction isRetentionWeakened(\n current: FileRetentionValue | null | undefined,\n next: FileRetentionValue,\n): boolean {\n if (current === null || current === undefined) return false\n if (current.mode === null) return false\n if (next.mode === null) return true\n if (current.mode === RetentionMode.Compliance && next.mode !== RetentionMode.Compliance) {\n return true\n }\n if (current.retainUntilTimestamp === null) return next.mode !== current.mode\n if (next.retainUntilTimestamp === null) return true\n return next.retainUntilTimestamp < current.retainUntilTimestamp\n}\n\nfunction requiresGovernanceBypass(\n current: FileRetentionValue | null | undefined,\n next: FileRetentionValue,\n): boolean {\n if (current?.mode !== RetentionMode.Governance) return false\n if (next.mode === RetentionMode.Compliance) return true\n return isRetentionWeakened(current, next)\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: { kind: 'webhook' | 'replication'; error: Error }) => 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 * for account/application-key authorization (matching its\n * long-standing behaviour): any well-formed Basic credential whose\n * applicationKeyId is not a simulator-created key receives the\n * implicit master grant, so tests do not have to set up keys with\n * the right capabilities. Created keys always authorize with their\n * stored capabilities, bucket scope, name prefix, and expiration;\n * a wrong or expired created-key secret is rejected.\n *\n * Upload authorization tokens returned by `b2_get_upload_url` and\n * `b2_get_upload_part_url` are always enforced, regardless of this\n * option. Upload handlers reject missing, unknown, expired, or\n * wrong-URL upload tokens in both permissive and strict modes.\n *\n * In strict mode:\n *\n * - `b2_authorize_account` still accepts the documented implicit\n * master credentials `test-key-id:test-key` and\n * `master-key-id:master-key`.\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 bucketIds / 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. The simulator also uses this TTL for upload\n * authorization tokens issued via `b2_get_upload_url` and\n * `b2_get_upload_part_url`. Defaults to 24 hours (real B2). Tests\n * that want to exercise the 401/reauth retry path or stale upload\n * URL handling can lower this and use {@link B2Simulator.advanceTime}\n * to move simulator time past account-token expiry. Upload tokens are\n * rejected at the exact expiry boundary.\n */\n authTokenTtlMs?: number\n /**\n * When `true`, v3 `b2_authorize_account` responses include Partner and\n * Computer Backup suites and issue Partner authorization tokens. Partner\n * and Backup endpoint calls then require one of those issued tokens, even\n * when `strictAuth` is otherwise disabled, so SDK auth-error paths can test\n * documented 401 responses.\n *\n * Defaults to `false`: direct Partner/Backup endpoint tests remain\n * permissive and accept any non-empty, whitespace-free Partner token without\n * requiring an authorize call first.\n */\n partnerAuthorize?: boolean\n /**\n * Whether Partner API endpoints should accept issued Partner authorization\n * tokens. Defaults to `true`. Set to `false` to exercise documented\n * `403 access_denied` prerequisite failures.\n */\n partnerApiEnabled?: boolean\n /**\n * Whether the simulated partner administrator has a valid phone number.\n * Defaults to `true`; `false` produces Partner prerequisite failures\n * such as `403 access_denied` or `401 invalid_sms_phone` depending on\n * the endpoint's documented error shape.\n */\n partnerAccountHasValidPhone?: boolean\n /**\n * Whether the simulated partner administrator account is in good standing.\n * Defaults to `true`; `false` produces `403 access_denied` on Partner calls.\n */\n partnerAccountInGoodStanding?: boolean\n /**\n * Whether the simulated account may set custom upload timestamps on\n * `b2_upload_file` and `b2_start_large_file`. Defaults to `false`, matching\n * production accounts without the restricted feature enabled.\n */\n customUploadTimestampsEnabled?: boolean\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 *\n * `test-key-id:test-key` is the documented implicit full-access\n * storage credential. `master-key-id:master-key` is also accepted for\n * tests that share storage and Partner simulator credentials. In the\n * default permissive mode, other well-formed Basic credentials whose\n * applicationKeyId was not minted by `b2_create_key` receive the same\n * implicit master grant for backward compatibility. A simulator-created\n * key always authorizes with its own capabilities, bucket restrictions,\n * name prefix, and expiration.\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 private readonly customUploadTimestampsEnabled: boolean\n private readonly partner: PartnerSimulator\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<string, IssuedToken>()\n /**\n * Mutable upload-token overrides for tokens minted by `b2_get_upload_url`\n * and `b2_get_upload_part_url`. The token string is self-describing\n * and signed so another simulator instance can validate issued-token\n * state, while this map records local invalidation / forced-expiry\n * state for tests.\n */\n private readonly uploadTokens = new Map<string, StoredUploadToken>()\n private readonly downloadAuthorizationTokens = new Map<string, DownloadAuthorizationToken>()\n private readonly downloadAuthorizationExpiryHeap: DownloadAuthorizationExpiry[] = []\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 this.customUploadTimestampsEnabled = options.customUploadTimestampsEnabled ?? 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 this.partner = new PartnerSimulator(\n {\n accountId: this.accountId,\n authTokenTtlMs: this.authTokenTtlMs,\n minimumPartSize: this.minimumPartSize,\n recommendedPartSize: this.recommendedPartSize,\n canAuthorize: (authzHeader) => this.canAuthorizePartner(authzHeader),\n createBucket: (req) => this.createBucket(req),\n error: (status, code, message) => this.error(status, code, message),\n genId: (prefix) => this.genId(prefix),\n monotonicTimestamp: () => this.monotonicTimestamp(),\n now: () => this.now(),\n storeKey: (key) => {\n this.keys.set(key.applicationKeyId, key)\n },\n },\n options,\n )\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 * Expire an upload authorization token previously returned by\n * `b2_get_upload_url` or `b2_get_upload_part_url`. Tests can use this\n * to simulate a stale upload URL and verify retry paths that fetch a\n * fresh URL/token pair.\n *\n * @param authorizationToken - The upload authorization token to expire.\n *\n * @returns `true` when the token existed and was expired, otherwise `false`.\n */\n expireUploadToken(authorizationToken: string): boolean {\n const now = this.now()\n const token = this.uploadTokens.get(authorizationToken)\n if (token === undefined || now >= token.cleanupAt) return false\n token.expiresAt = now - 1\n this.uploadTokens.set(authorizationToken, token)\n return true\n }\n\n /**\n * Invalidate an upload authorization token previously returned by\n * `b2_get_upload_url` or `b2_get_upload_part_url`.\n *\n * @param authorizationToken - The upload authorization token to invalidate.\n *\n * @returns `true` when the token existed and was invalidated, otherwise `false`.\n */\n invalidateUploadToken(authorizationToken: string): boolean {\n const now = this.now()\n const token = this.uploadTokens.get(authorizationToken)\n if (token === undefined || now >= token.cleanupAt) {\n this.pruneExpiredUploadTokens(now)\n return false\n }\n token.invalidated = true\n this.uploadTokens.set(authorizationToken, token)\n this.pruneExpiredUploadTokens()\n return true\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 private isStoredKeyExpired(key: StoredKey): boolean {\n return key.expirationTimestamp !== null && key.expirationTimestamp <= this.now()\n }\n\n private pruneExpiredUploadTokens(now = this.now()): void {\n for (const [authorizationToken, token] of this.uploadTokens.entries()) {\n if (now >= token.cleanupAt) {\n this.uploadTokens.delete(authorizationToken)\n }\n }\n }\n\n private async uploadTokenState(authorizationToken: string): Promise<StoredUploadToken | null> {\n const stored = this.uploadTokens.get(authorizationToken)\n if (stored !== undefined) return stored\n return await this.decodeUploadAuthorizationToken(authorizationToken)\n }\n\n private async encodeUploadAuthorizationToken(token: StoredUploadToken): Promise<string> {\n const payload: UploadTokenPayload = {\n v: 1,\n kind: token.kind,\n fileName: token.fileName,\n uploadUrl: token.uploadUrl,\n namePrefix: token.namePrefix,\n applicationKeyId: token.applicationKeyId,\n expiresAt: token.expiresAt,\n }\n const encoded = base64UrlEncode(utf8Encoder.encode(JSON.stringify(payload)))\n const signature = await this.signUploadTokenPayload(encoded)\n const prefix = token.kind === 'file' ? 'sim_upload_auth' : 'sim_part_auth'\n return `${prefix}_${encoded}.${signature}`\n }\n\n private async decodeUploadAuthorizationToken(\n authorizationToken: string,\n ): Promise<StoredUploadToken | null> {\n const filePrefix = 'sim_upload_auth_'\n const partPrefix = 'sim_part_auth_'\n const expectedKind = authorizationToken.startsWith(filePrefix)\n ? 'file'\n : authorizationToken.startsWith(partPrefix)\n ? 'part'\n : null\n if (expectedKind === null) return null\n\n const encoded = authorizationToken.slice(\n expectedKind === 'file' ? filePrefix.length : partPrefix.length,\n )\n const tokenParts = encoded.split('.')\n if (tokenParts.length !== 2) return null\n const [payloadBytes, signature] = tokenParts as [string, string]\n const expectedSignature = await this.signUploadTokenPayload(payloadBytes)\n if (!timingSafeStringEqual(signature, expectedSignature)) return null\n\n let parsed: unknown\n try {\n const bytes = base64UrlDecode(payloadBytes)\n if (bytes === null) return null\n parsed = JSON.parse(utf8Decoder.decode(bytes)) as unknown\n } catch {\n return null\n }\n if (typeof parsed !== 'object' || parsed === null) return null\n const payload = parsed as Record<string, unknown>\n if (\n payload['v'] !== 1 ||\n payload['kind'] !== expectedKind ||\n typeof payload['uploadUrl'] !== 'string' ||\n typeof payload['expiresAt'] !== 'number' ||\n !Number.isFinite(payload['expiresAt']) ||\n !(payload['namePrefix'] === null || typeof payload['namePrefix'] === 'string') ||\n !(payload['applicationKeyId'] === null || typeof payload['applicationKeyId'] === 'string')\n ) {\n return null\n }\n let fileName: string | null\n if (expectedKind === 'part') {\n if (typeof payload['fileName'] !== 'string') return null\n fileName = payload['fileName']\n } else {\n if (payload['fileName'] !== null) return null\n fileName = null\n }\n return {\n kind: expectedKind,\n fileName,\n uploadUrl: payload['uploadUrl'],\n namePrefix: payload['namePrefix'],\n applicationKeyId: payload['applicationKeyId'],\n expiresAt: payload['expiresAt'],\n cleanupAt: payload['expiresAt'],\n invalidated: false,\n }\n }\n\n private async signUploadTokenPayload(encodedPayload: string): Promise<string> {\n return base64UrlEncode(await hmacSha256(UPLOAD_TOKEN_SIGNING_KEY, encodedPayload))\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 scope - Optional effective bucket and file-name scope derived from the request.\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 scope?: RequestScope,\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 if (token.applicationKeyId !== null) {\n const key = this.keys.get(token.applicationKeyId)\n if (key === undefined) {\n return this.error(401, 'bad_auth_token', 'application key has been deleted; reauthorize')\n }\n if (this.isStoredKeyExpired(key)) {\n return this.error(401, 'expired_auth_token', 'application key has expired; reauthorize')\n }\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 return this.authorizeScopeGrant(scope, {\n bucketIds: token.bucketIds,\n namePrefix: token.namePrefix,\n bucketScopeRequiredMessage: () =>\n `application key is scoped to buckets ${token.bucketIds?.join(', ')}; bucket scope is required`,\n bucketMismatchMessage: (bucketId) =>\n `application key is scoped to buckets ${token.bucketIds?.join(', ')}; cannot access ${bucketId}`,\n prefixMismatchMessage: (fileName) =>\n `application key is scoped to prefix \"${token.namePrefix}\"; \"${fileName}\" is outside scope`,\n })\n }\n\n private authorizeScopeGrant(\n scope: RequestScope | undefined,\n grant: {\n readonly bucketIds: readonly string[] | null\n readonly namePrefix: string | null\n readonly bucketScopeRequiredMessage: () => string\n readonly bucketMismatchMessage: (bucketId: string) => string\n readonly prefixMismatchMessage: (fileName: string) => string\n },\n ): SimulatorJsonResponse | null {\n if (grant.bucketIds !== null) {\n if (scope?.requiresAccountLevelBucketAccess === true) {\n return this.error(403, 'unauthorized', grant.bucketScopeRequiredMessage())\n }\n if ((scope?.bucketIds.length ?? 0) === 0 && scope?.requiresBucketScope === true) {\n return this.error(403, 'unauthorized', grant.bucketScopeRequiredMessage())\n }\n for (const bucketId of scope?.bucketIds ?? []) {\n if (grant.bucketIds.includes(bucketId)) continue\n return this.error(403, 'unauthorized', grant.bucketMismatchMessage(bucketId))\n }\n }\n if (grant.namePrefix !== null) {\n for (const fileName of scope?.fileNames ?? []) {\n if (fileName.startsWith(grant.namePrefix)) continue\n return this.error(403, 'unauthorized', grant.prefixMismatchMessage(fileName))\n }\n }\n return null\n }\n\n private authorizeDownloadRequest(\n authToken: string | undefined,\n endpoint: 'b2_download_file_by_id' | 'b2_download_file_by_name',\n scope: RequestScope,\n url: URL,\n ): SimulatorJsonResponse | null {\n if (authToken === undefined || authToken === '') {\n return this.error(401, 'bad_auth_token', 'missing authorization token')\n }\n\n const downloadAuth = this.downloadAuthorizationTokens.get(authToken)\n if (downloadAuth === undefined) {\n this.purgeExpiredDownloadAuthorizationTokens()\n return this.authorizeRequest(authToken, endpoint, scope)\n }\n if (this.isExpiredDownloadAuthorizationToken(downloadAuth)) {\n this.downloadAuthorizationTokens.delete(authToken)\n this.purgeExpiredDownloadAuthorizationTokens()\n return this.error(401, 'expired_auth_token', 'download authorization token has expired')\n }\n if (endpoint === 'b2_download_file_by_id') {\n return this.error(\n 403,\n 'unauthorized',\n 'download authorization tokens cannot be used with b2_download_file_by_id',\n )\n }\n const overrideError = this.authorizeDownloadResponseOverrides(downloadAuth, url)\n if (overrideError !== null) return overrideError\n return this.authorizeScopeGrant(scope, {\n bucketIds: [downloadAuth.bucketId],\n namePrefix: downloadAuth.fileNamePrefix,\n bucketScopeRequiredMessage: () =>\n `download authorization is scoped to bucket ${downloadAuth.bucketId}; bucket scope is required`,\n bucketMismatchMessage: (bucketId) =>\n `download authorization is scoped to bucket ${downloadAuth.bucketId}; cannot access ${bucketId}`,\n prefixMismatchMessage: (fileName) =>\n `download authorization is scoped to prefix \"${downloadAuth.fileNamePrefix}\"; \"${fileName}\" is outside scope`,\n })\n }\n\n private isExpiredDownloadAuthorizationToken(\n token: DownloadAuthorizationToken,\n now = this.now(),\n ): boolean {\n return now >= token.expiresAt\n }\n\n private purgeExpiredDownloadAuthorizationTokens(): void {\n const now = this.now()\n for (let i = 0; i < DOWNLOAD_AUTH_PURGE_BATCH_SIZE; i++) {\n const next = this.downloadAuthorizationExpiryHeap[0]\n if (next === undefined || next.expiresAt > now) return\n this.popDownloadAuthorizationExpiry()\n const current = this.downloadAuthorizationTokens.get(next.token)\n if (current?.expiresAt === next.expiresAt) {\n this.downloadAuthorizationTokens.delete(next.token)\n }\n }\n }\n\n private pushDownloadAuthorizationExpiry(entry: DownloadAuthorizationExpiry): void {\n const heap = this.downloadAuthorizationExpiryHeap\n heap.push(entry)\n let idx = heap.length - 1\n while (idx > 0) {\n const parentIdx = Math.floor((idx - 1) / 2)\n const parent = heap[parentIdx] as DownloadAuthorizationExpiry\n if (parent.expiresAt <= entry.expiresAt) break\n heap[idx] = parent\n idx = parentIdx\n }\n heap[idx] = entry\n }\n\n private popDownloadAuthorizationExpiry(): DownloadAuthorizationExpiry | undefined {\n const heap = this.downloadAuthorizationExpiryHeap\n const root = heap[0]\n const last = heap.pop()\n if (root === undefined || last === undefined || heap.length === 0) return root\n\n let idx = 0\n while (true) {\n const leftIdx = idx * 2 + 1\n const rightIdx = leftIdx + 1\n const left = heap[leftIdx]\n const right = heap[rightIdx]\n if (left === undefined) break\n\n const childIdx = right !== undefined && right.expiresAt < left.expiresAt ? rightIdx : leftIdx\n const child = heap[childIdx] as DownloadAuthorizationExpiry\n if (child.expiresAt >= last.expiresAt) break\n heap[idx] = child\n idx = childIdx\n }\n heap[idx] = last\n return root\n }\n\n private authorizeDownloadResponseOverrides(\n downloadAuth: DownloadAuthorizationToken,\n url: URL,\n ): SimulatorJsonResponse | null {\n for (const param of DOWNLOAD_RESPONSE_OVERRIDE_PARAMS) {\n const expected = downloadAuth.responseHeaderOverrides[param]\n if (expected === undefined) continue\n const actual = url.searchParams.get(param)\n if (actual === expected) continue\n const expectedText = JSON.stringify(expected)\n const actualText = actual === null ? 'absent' : JSON.stringify(actual)\n return this.error(\n 403,\n 'unauthorized',\n `download authorization requires ${param}=${expectedText}; got ${actualText}`,\n )\n }\n return null\n }\n\n private downloadAuthorizationFromRequest(\n headers: Record<string, string>,\n url: URL,\n options: { readonly allowQueryAuthorization?: boolean } = {},\n ): string | undefined {\n const headerToken = requestHeaderValue(headers, 'authorization')\n if (headerToken !== undefined) return headerToken\n if (options.allowQueryAuthorization !== true) return undefined\n const queryToken =\n url.searchParams.get('Authorization') ?? url.searchParams.get('authorization')\n return queryToken?.startsWith(DOWNLOAD_AUTH_TOKEN_PREFIX) ? queryToken : undefined\n }\n\n private requestHasCapability(authToken: string | undefined, capability: Capability): boolean {\n if (!this.strictAuth) return true\n if (authToken === undefined) return false\n return this.issuedTokens.get(authToken)?.capabilities.includes(capability) === true\n }\n\n private requireFileLockEnabled(bucket: StoredBucket): SimulatorJsonResponse | null {\n if (bucket.info.fileLockConfiguration.value?.isFileLockEnabled === true) return null\n return this.error(400, 'file_lock_not_enabled', 'Bucket does not have file lock enabled')\n }\n\n private requireGovernanceBypass(\n req: { bypassGovernance?: boolean },\n authToken: string | undefined,\n message: string,\n ): SimulatorJsonResponse | null {\n if (req.bypassGovernance !== true) {\n return this.error(400, 'file_lock_governance_protected', message)\n }\n if (!this.requestHasCapability(authToken, Capability.BypassGovernance)) {\n return this.error(\n 403,\n 'unauthorized',\n `application key lacks required capabilities: ${Capability.BypassGovernance}`,\n )\n }\n return null\n }\n\n private validateReplicationApplicationKey(\n applicationKeyId: string,\n requiredCapabilities: readonly Capability[],\n options: { readonly bucketId?: string | undefined; readonly role: string },\n ): SimulatorJsonResponse | null {\n const key = this.keys.get(applicationKeyId)\n if (key === undefined) {\n return this.invalidReplicationApplicationKey(options.role)\n }\n const missing = missingStoredKeyCapabilities(key, requiredCapabilities)\n if (missing.length > 0) {\n return this.invalidReplicationApplicationKey(options.role)\n }\n if (options.bucketId !== undefined && !storedKeyAllowsBucket(key, options.bucketId)) {\n return this.invalidReplicationApplicationKey(options.role)\n }\n return null\n }\n\n private invalidReplicationApplicationKey(role: string): SimulatorJsonResponse {\n return this.error(\n 400,\n 'bad_request',\n `${role} application key is invalid or not authorized for this replication configuration`,\n )\n }\n\n private invalidReplicationDestinationBucket(): SimulatorJsonResponse {\n return this.error(\n 400,\n 'bad_request',\n 'replication destination bucket is invalid or not configured for this source application key',\n )\n }\n\n private validateReplicationDestinationBuckets(\n sourceApplicationKeyId: string,\n rules: readonly unknown[],\n ): SimulatorJsonResponse | null {\n for (const rule of rules) {\n const destinationBucketId = requestRecord(rule)?.['destinationBucketId']\n if (typeof destinationBucketId !== 'string') continue\n\n const destinationBucket = this.buckets.get(destinationBucketId)\n const destinationApplicationKeyId =\n destinationBucket?.info.replicationConfiguration.asReplicationDestination\n ?.sourceToDestinationKeyMapping[sourceApplicationKeyId]\n if (destinationApplicationKeyId === undefined) {\n return this.invalidReplicationDestinationBucket()\n }\n\n const destinationKeyError = this.validateReplicationApplicationKey(\n destinationApplicationKeyId,\n [Capability.WriteFiles],\n { bucketId: destinationBucketId, role: 'replication destination' },\n )\n if (destinationKeyError !== null) return destinationKeyError\n }\n return null\n }\n\n private validateReplicationApplicationKeys(\n config: unknown,\n bucketId: string | undefined,\n ): SimulatorJsonResponse | null {\n if (!this.strictAuth || config === undefined) return null\n\n const replicationConfiguration = requestRecord(config)\n const source = requestRecord(replicationConfiguration?.['asReplicationSource'])\n if (source !== null) {\n const sourceApplicationKeyId = source['sourceApplicationKeyId']\n if (typeof sourceApplicationKeyId === 'string') {\n const sourceKeyError = this.validateReplicationApplicationKey(\n sourceApplicationKeyId,\n [Capability.ReadFiles, Capability.ListFiles],\n { bucketId, role: 'replication source' },\n )\n if (sourceKeyError !== null) return sourceKeyError\n }\n const rules = source['replicationRules']\n if (typeof sourceApplicationKeyId === 'string' && Array.isArray(rules)) {\n const destinationError = this.validateReplicationDestinationBuckets(\n sourceApplicationKeyId,\n rules,\n )\n if (destinationError !== null) return destinationError\n }\n }\n\n const destination = requestRecord(replicationConfiguration?.['asReplicationDestination'])\n const mapping = requestRecord(destination?.['sourceToDestinationKeyMapping'])\n if (mapping !== null) {\n for (const [sourceApplicationKeyId, destinationApplicationKeyId] of Object.entries(mapping)) {\n const sourceKeyError = this.validateReplicationApplicationKey(\n sourceApplicationKeyId,\n [Capability.ReadFiles, Capability.ListFiles],\n { role: 'replication source' },\n )\n if (sourceKeyError !== null) return sourceKeyError\n\n if (typeof destinationApplicationKeyId === 'string') {\n const destinationKeyError = this.validateReplicationApplicationKey(\n destinationApplicationKeyId,\n [Capability.WriteFiles],\n { bucketId, role: 'replication destination' },\n )\n if (destinationKeyError !== null) return destinationKeyError\n }\n }\n }\n\n return null\n }\n\n private requestScope(endpoint: string, body: unknown): RequestScope | undefined {\n const directBucketId = requestStringField(body, 'bucketId')\n const directFileName = requestStringField(body, 'fileName')\n\n switch (endpoint) {\n case 'b2_create_bucket':\n return this.createBucketScope(body)\n case 'b2_list_buckets':\n return this.listBucketsScope(body, directBucketId)\n case 'b2_list_file_names':\n case 'b2_list_file_versions':\n return {\n bucketIds: directBucketId === undefined ? [] : [directBucketId],\n fileNames: [requestStringField(body, 'prefix') ?? ''],\n requiresBucketScope: true,\n }\n case 'b2_get_file_info':\n return (\n this.fileIdScope(requestStringField(body, 'fileId')) ?? {\n bucketIds: [],\n requiresBucketScope: true,\n }\n )\n case 'b2_delete_file_version':\n case 'b2_update_file_retention':\n case 'b2_update_file_legal_hold':\n return this.fileVersionScope(body, directFileName)\n case 'b2_get_upload_part_url':\n case 'b2_finish_large_file':\n case 'b2_cancel_large_file':\n case 'b2_list_parts':\n return (\n this.largeFileScope(requestStringField(body, 'fileId')) ?? {\n bucketIds: [],\n requiresBucketScope: true,\n }\n )\n case 'b2_copy_file':\n return this.copyFileScope(body, directFileName)\n case 'b2_copy_part':\n return this.copyPartScope(body)\n case 'b2_list_unfinished_large_files':\n return {\n bucketIds: directBucketId === undefined ? [] : [directBucketId],\n fileNames: [requestStringField(body, 'namePrefix') ?? ''],\n requiresBucketScope: true,\n }\n case 'b2_get_download_authorization':\n return {\n bucketIds: directBucketId === undefined ? [] : [directBucketId],\n fileNames: [requestStringField(body, 'fileNamePrefix') ?? ''],\n requiresBucketScope: true,\n }\n case 'b2_get_bucket_notification_rules':\n return this.notificationRulesScope(body, directBucketId, false)\n case 'b2_set_bucket_notification_rules':\n return this.notificationRulesScope(body, directBucketId, true)\n case 'b2_update_bucket':\n return this.updateBucketScope(body, directBucketId)\n default:\n return this.defaultRequestScope(directBucketId, directFileName)\n }\n }\n\n private createBucketScope(body: unknown): RequestScope {\n return {\n bucketIds: replicationDestinationBucketIds(body),\n requiresBucketScope: true,\n requiresAccountLevelBucketAccess: true,\n }\n }\n\n private updateBucketScope(body: unknown, directBucketId: string | undefined): RequestScope {\n return {\n bucketIds: [\n ...(directBucketId === undefined ? [] : [directBucketId]),\n ...replicationDestinationBucketIds(body),\n ],\n requiresBucketScope: true,\n }\n }\n\n private listBucketsScope(body: unknown, directBucketId: string | undefined): RequestScope {\n if (directBucketId !== undefined) {\n return { bucketIds: [directBucketId], requiresBucketScope: true }\n }\n const bucketName = requestStringField(body, 'bucketName')\n if (bucketName !== undefined) {\n const bucket = [...this.buckets.values()].find((b) => b.info.bucketName === bucketName)\n return {\n bucketIds: bucket === undefined ? [] : [bucket.info.bucketId as string],\n requiresBucketScope: true,\n }\n }\n return { bucketIds: [], requiresBucketScope: true }\n }\n\n private fileVersionScope(body: unknown, directFileName: string | undefined): RequestScope {\n const fileScope = this.fileIdScope(requestStringField(body, 'fileId'))\n if (fileScope !== undefined) return fileScope\n return {\n bucketIds: [],\n ...(directFileName !== undefined ? { fileNames: [directFileName] } : {}),\n requiresBucketScope: true,\n }\n }\n\n private copyFileScope(body: unknown, directFileName: string | undefined): RequestScope {\n const sourceScope = this.fileIdScope(requestStringField(body, 'sourceFileId'))\n const destinationBucketId = requestStringField(body, 'destinationBucketId')\n const bucketIds = [\n ...(sourceScope?.bucketIds ?? []),\n ...(destinationBucketId !== undefined ? [destinationBucketId] : []),\n ]\n const scopedFileNames = fileNames(...(sourceScope?.fileNames ?? []), directFileName)\n return {\n bucketIds,\n ...(scopedFileNames !== undefined ? { fileNames: scopedFileNames } : {}),\n requiresBucketScope: true,\n }\n }\n\n private copyPartScope(body: unknown): RequestScope {\n const sourceScope = this.fileIdScope(requestStringField(body, 'sourceFileId'))\n const largeScope = this.largeFileScope(requestStringField(body, 'largeFileId'))\n const scopedFileNames = fileNames(\n ...(sourceScope?.fileNames ?? []),\n ...(largeScope?.fileNames ?? []),\n )\n return {\n bucketIds: [...(sourceScope?.bucketIds ?? []), ...(largeScope?.bucketIds ?? [])],\n ...(scopedFileNames !== undefined ? { fileNames: scopedFileNames } : {}),\n requiresBucketScope: true,\n }\n }\n\n private notificationRulesScope(\n body: unknown,\n directBucketId: string | undefined,\n includeRequestRules: boolean,\n ): RequestScope {\n const existingPrefixes =\n directBucketId === undefined\n ? []\n : storedNotificationRulePrefixes(this.notificationRules.get(directBucketId))\n const requestPrefixes = includeRequestRules ? (notificationRulePrefixes(body) ?? []) : []\n const prefixes = [...existingPrefixes, ...requestPrefixes]\n return {\n bucketIds: directBucketId === undefined ? [] : [directBucketId],\n ...(prefixes.length > 0 ? { fileNames: prefixes } : {}),\n requiresBucketScope: true,\n }\n }\n\n private defaultRequestScope(\n directBucketId: string | undefined,\n directFileName: string | undefined,\n ): RequestScope | undefined {\n if (directBucketId !== undefined) {\n return {\n bucketIds: [directBucketId],\n ...(directFileName !== undefined ? { fileNames: [directFileName] } : {}),\n requiresBucketScope: true,\n }\n }\n return directFileName !== undefined\n ? { bucketIds: [], fileNames: [directFileName], requiresBucketScope: false }\n : undefined\n }\n\n private fileIdScope(fileId: string | undefined): RequestScope | undefined {\n if (fileId === undefined) return undefined\n const found = this.findFile(fileId)\n if (found === null) return undefined\n return {\n bucketIds: [found.bucketId],\n fileNames: [found.stored.fileVersion.fileName],\n requiresBucketScope: true,\n }\n }\n\n private largeFileScope(fileId: string | undefined): RequestScope | undefined {\n if (fileId === undefined) return undefined\n const large = this.largeFiles.get(fileId)\n if (large === undefined) return undefined\n return { bucketIds: [large.bucketId], fileNames: [large.fileName], requiresBucketScope: true }\n }\n\n private async issueUploadAuthorization(\n options:\n | {\n kind: 'file'\n sourceAuthToken: string | undefined\n bucketId: string\n }\n | {\n kind: 'part'\n sourceAuthToken: string | undefined\n fileId: string\n fileName: string\n },\n ): Promise<{ uploadUrl: string; authorizationToken: string }> {\n const now = this.now()\n this.pruneExpiredUploadTokens(now)\n const endpoint = options.kind === 'file' ? 'b2_upload_file' : 'b2_upload_part'\n const idParam = options.kind === 'file' ? 'bucketId' : 'fileId'\n const scopedId = options.kind === 'file' ? options.bucketId : options.fileId\n const uploadId = this.genId(options.kind === 'file' ? 'upload_file' : 'upload_part')\n const uploadUrl = new URL(b2Url('http://localhost:0', { version: 'v3', endpoint }))\n uploadUrl.searchParams.set(idParam, scopedId)\n uploadUrl.searchParams.set('uploadId', uploadId)\n\n const sourceToken =\n options.sourceAuthToken === undefined\n ? undefined\n : this.issuedTokens.get(options.sourceAuthToken)\n const token: StoredUploadToken = {\n kind: options.kind,\n fileName: options.kind === 'part' ? options.fileName : null,\n uploadUrl: uploadUrl.toString(),\n namePrefix: this.strictAuth ? (sourceToken?.namePrefix ?? null) : null,\n applicationKeyId: sourceToken?.applicationKeyId ?? null,\n expiresAt: now + this.authTokenTtlMs,\n cleanupAt: now + this.authTokenTtlMs,\n invalidated: false,\n }\n const authorizationToken = await this.encodeUploadAuthorizationToken(token)\n this.uploadTokens.set(authorizationToken, token)\n return { uploadUrl: uploadUrl.toString(), authorizationToken }\n }\n\n private async validateUploadAuthorization(\n kind: UploadTokenKind,\n uploadUrl: string,\n authToken: string | undefined,\n fileName: string | undefined,\n ): Promise<SimulatorJsonResponse | null> {\n if (authToken === undefined || authToken === '') {\n return this.error(401, 'bad_auth_token', 'missing upload Authorization header')\n }\n\n const token = await this.uploadTokenState(authToken)\n if (token === null) {\n this.pruneExpiredUploadTokens()\n return this.error(401, 'bad_auth_token', 'unknown upload authorization token')\n }\n const now = this.now()\n if (now >= token.expiresAt) {\n if (now >= token.cleanupAt) {\n this.uploadTokens.delete(authToken)\n } else {\n this.uploadTokens.set(authToken, token)\n }\n this.pruneExpiredUploadTokens(now)\n return this.error(\n 401,\n 'expired_auth_token',\n 'upload authorization token has expired; get a new upload URL',\n )\n }\n if (token.invalidated) {\n this.uploadTokens.set(authToken, token)\n this.pruneExpiredUploadTokens(now)\n return this.error(401, 'bad_auth_token', 'upload authorization token has been invalidated')\n }\n if (token.kind !== kind) {\n this.pruneExpiredUploadTokens(now)\n return this.error(401, 'bad_auth_token', 'upload authorization token type mismatch')\n }\n this.pruneExpiredUploadTokens(now)\n if (!this.uploadUrlMatches(token, uploadUrl)) {\n return this.error(\n 401,\n 'bad_auth_token',\n 'upload authorization token is not valid for this upload URL',\n )\n }\n const scopedName = kind === 'file' ? fileName : (token.fileName ?? undefined)\n if (token.namePrefix !== null && scopedName !== undefined) {\n if (!scopedName.startsWith(token.namePrefix)) {\n return this.error(\n 403,\n 'unauthorized',\n `application key is scoped to prefix \"${token.namePrefix}\"; \"${scopedName}\" is outside scope`,\n )\n }\n }\n return null\n }\n\n private uploadUrlMatches(token: StoredUploadToken, uploadUrl: string): boolean {\n let expected: URL\n let actual: URL\n try {\n expected = new URL(token.uploadUrl)\n actual = new URL(uploadUrl)\n } catch {\n return false\n }\n\n if (actual.origin !== expected.origin || actual.pathname !== expected.pathname) return false\n const scopeParam = token.kind === 'file' ? 'bucketId' : 'fileId'\n return (\n actual.searchParams.get(scopeParam) === expected.searchParams.get(scopeParam) &&\n actual.searchParams.get('uploadId') === expected.searchParams.get('uploadId')\n )\n }\n\n private allowedBuckets(\n bucketIds: readonly string[] | null | undefined,\n ): readonly { readonly id: BucketId; readonly name: string | null }[] | null {\n if (bucketIds === undefined || bucketIds === null) return null\n return bucketIds.map((id) => ({\n id: bucketIdOf(id),\n name: this.buckets.get(id)?.info.bucketName ?? null,\n }))\n }\n\n /**\n * Resolve 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 * The simulator has two implicit full-access storage credentials:\n * the documented `test-key-id:test-key` pair used by SDK tests and\n * `master-key-id:master-key`. In default permissive mode, any other\n * well-formed Basic credential whose applicationKeyId is not a stored\n * key also receives that implicit master grant for backward\n * compatibility. A credential whose applicationKeyId matches a stored\n * key must use the stored secret and must not be expired.\n *\n * @param authzHeader - Raw HTTP `Authorization` header value.\n *\n * @returns The authorizing key's grant scope or the rejection reason.\n */\n private findAuthorizationGrant(authzHeader: string | undefined): AuthorizationGrantResolution {\n const credentials = parseBasicAuthorizationHeader(authzHeader)\n if (credentials === null) return { kind: 'invalid' }\n const stored = this.keys.get(credentials.applicationKeyId)\n if (stored !== undefined) {\n if (!timingSafeStringEqual(stored.applicationKey, credentials.applicationKey)) {\n return { kind: 'invalid' }\n }\n if (this.isStoredKeyExpired(stored)) return { kind: 'expired' }\n return {\n kind: 'ok',\n grant: {\n capabilities: stored.capabilities,\n bucketIds: stored.bucketIds,\n namePrefix: stored.namePrefix,\n applicationKeyId: credentials.applicationKeyId,\n accountId: stored.accountId,\n expirationTimestamp: stored.expirationTimestamp,\n },\n }\n }\n if (isSimulatorApplicationKeyId(credentials.applicationKeyId)) return { kind: 'invalid' }\n if (isImplicitStorageMasterCredential(credentials) || !this.strictAuth) {\n return {\n kind: 'ok',\n grant: {\n capabilities: SIMULATOR_MASTER_CAPABILITIES,\n bucketIds: null,\n namePrefix: null,\n applicationKeyId: null,\n accountId: this.accountId,\n expirationTimestamp: null,\n },\n }\n }\n return { kind: 'invalid' }\n }\n\n private canAuthorizePartner(authzHeader: string | undefined): boolean {\n const credentials = parseBasicAuthorizationHeader(authzHeader)\n if (credentials === null) return false\n if (isImplicitPartnerMasterCredential(credentials)) return true\n const stored = this.keys.get(credentials.applicationKeyId)\n return (\n stored !== undefined &&\n stored.accountId === this.accountId &&\n !this.isStoredKeyExpired(stored) &&\n timingSafeStringEqual(stored.applicationKey, credentials.applicationKey)\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.\n * @param origin - The request URL origin used for simulator-issued endpoints.\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 origin: string,\n path: string,\n headers: Record<string, string>,\n body: unknown,\n ): Promise<SimulatorJsonResponse> {\n const { endpoint, version } = apiPathParts(path)\n if (!jsonEndpointAllowsMethod(method, endpoint)) {\n return this.error(\n 405,\n 'method_not_allowed',\n `${endpoint} does not support ${method.toUpperCase()}`,\n )\n }\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 // capability and scope checks.\n if (this.strictAuth && !this.partner.isEndpoint(endpoint)) {\n const authError = this.authorizeRequest(\n headers['authorization'],\n endpoint,\n this.requestScope(endpoint, body),\n )\n if (authError !== null) return authError\n }\n\n switch (endpoint) {\n case 'b2_authorize_account':\n if (version === 'v3' && this.partner.isAuthorizeEnabled()) {\n return this.partner.authorize(headers['authorization'], origin)\n }\n return this.authorize(headers['authorization'], origin)\n case 'b2_create_group_member':\n return this.partner.createGroupMember(body, headers['authorization'])\n case 'b2_eject_group_member':\n return this.partner.ejectGroupMember(body, headers['authorization'])\n case 'b2_list_groups':\n return this.partner.listGroups(body, headers['authorization'])\n case 'b2_list_group_members':\n return this.partner.listGroupMembers(body, headers['authorization'])\n case 'b2_reserve_trial_create_account':\n return this.partner.reserveTrialCreateAccount(body, headers['authorization'])\n case 'bz_list_computers':\n return this.partner.listComputers(body, headers['authorization'])\n case 'bz_delete_computer':\n return this.partner.deleteComputer(body, 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 body as {\n bucketId?: string\n bucketName?: string\n bucketTypes?: readonly BucketType[]\n },\n )\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 await this.getUploadUrl(body as { bucketId: string }, headers['authorization'])\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 headers['authorization'],\n )\n case 'b2_copy_file':\n return await this.copyFile(\n body as {\n sourceFileId: string\n fileName: string\n destinationBucketId?: string\n range?: string\n metadataDirective?: string\n contentType?: string\n fileInfo?: Record<string, string>\n sourceServerSideEncryption?: EncryptionSetting\n destinationServerSideEncryption?: EncryptionSetting\n },\n )\n case 'b2_start_large_file':\n return await this.startLargeFile(\n body as {\n bucketId: string\n fileName: string\n contentType: string\n customUploadTimestamp?: string | null\n fileInfo?: Record<string, string>\n fileRetention?: FileRetentionValue\n legalHold?: LegalHoldValue\n serverSideEncryption?: EncryptionSetting\n },\n )\n case 'b2_get_upload_part_url':\n return await this.getUploadPartUrl(body as { fileId: string }, headers['authorization'])\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 sourceServerSideEncryption?: EncryptionSetting\n destinationServerSideEncryption?: EncryptionSetting\n },\n )\n case 'b2_get_download_authorization':\n return this.getDownloadAuthorization(body as DownloadAuthorizationRequestBody)\n case 'b2_create_key':\n return this.createKey(\n body as {\n accountId: string\n capabilities: unknown\n keyName: unknown\n validDurationInSeconds?: number\n bucketIds?: readonly string[] | null\n bucketId?: string\n namePrefix?: string\n },\n version,\n headers['authorization'],\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?: unknown\n retainUntilTimestamp?: unknown\n }\n bypassGovernance?: boolean\n },\n headers['authorization'],\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 A promise resolving to an object with HTTP status and JSON response body.\n */\n async handleUpload(\n url: string,\n headers: Record<string, string>,\n data: Uint8Array,\n ): Promise<SimulatorJsonResponse> {\n const endpoint = new URL(url).pathname.split('/').pop() ?? ''\n const kind: UploadTokenKind | null =\n endpoint === 'b2_upload_file' ? 'file' : endpoint === 'b2_upload_part' ? 'part' : null\n if (kind === null) return this.error(400, 'bad_request', `Unknown upload endpoint: ${endpoint}`)\n\n const fileName =\n kind === 'part' || headers['x-bz-file-name'] === undefined\n ? undefined\n : decodeHeaderValue(headers['x-bz-file-name'])\n const authError = await this.validateUploadAuthorization(\n kind,\n url,\n headers['authorization'],\n fileName,\n )\n if (authError !== null) return authError\n\n const contentLengthError = this.validateContentLength(headers, data.byteLength)\n if (contentLengthError !== null) return contentLengthError\n\n if (kind === 'part') {\n return await this.handleUploadPart(url, headers, data)\n }\n return await this.handleUploadFile(url, headers, data)\n }\n\n private validateContentLength(\n headers: Record<string, string>,\n actualLength: number,\n ): SimulatorJsonResponse | null {\n const parsed = parseContentLengthHeader(headers)\n if (parsed.kind === 'absent') return null\n if (parsed.kind === 'error') return this.error(400, 'bad_request', parsed.message)\n if (parsed.expectedLength !== actualLength) {\n return this.error(\n 400,\n 'bad_request',\n contentLengthMismatchMessage(parsed.expectedLength, actualLength),\n )\n }\n return null\n }\n\n private parseCustomUploadTimestamp(\n value: unknown,\n ): { readonly timestamp: number | null } | SimulatorJsonResponse {\n if (value === undefined || value === null) return { timestamp: null }\n if (!this.customUploadTimestampsEnabled) {\n return this.error(\n 400,\n 'custom_timestamp_not_allowed',\n 'Custom upload timestamps are not enabled for this account.',\n )\n }\n const timestamp = typeof value === 'string' && /^\\d+$/.test(value) ? Number(value) : Number.NaN\n\n if (!Number.isSafeInteger(timestamp) || timestamp < 0 || timestamp > this.now()) {\n return this.error(\n 400,\n 'custom_timestamp_invalid',\n 'The request has an invalid custom upload timestamp.',\n )\n }\n return { timestamp }\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 const url = new URL(`http://localhost${path}`)\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.authorizeDownloadRequest(\n this.downloadAuthorizationFromRequest(headers, url, { allowQueryAuthorization: false }),\n 'b2_download_file_by_id',\n this.fileIdScope(url.searchParams.get('fileId') ?? undefined) ?? {\n bucketIds: [],\n requiresBucketScope: true,\n },\n url,\n )\n if (authError !== null) return this.errorAsDownload(authError)\n }\n const fileId = url.searchParams.get('fileId') ?? ''\n return this.finalizeDownload(\n this.downloadById(fileId, headers, requestHeaderValue(headers, 'range')),\n url,\n method,\n )\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 const url = new URL(`http://localhost${path}`)\n if (this.strictAuth) {\n const bucket = [...this.buckets.values()].find((b) => b.info.bucketName === bucketName)\n const authError = this.authorizeDownloadRequest(\n this.downloadAuthorizationFromRequest(headers, url, { allowQueryAuthorization: true }),\n 'b2_download_file_by_name',\n {\n bucketIds: bucket === undefined ? [] : [bucket.info.bucketId as string],\n fileNames: [fileName],\n requiresBucketScope: true,\n },\n url,\n )\n if (authError !== null) return this.errorAsDownload(authError)\n }\n return this.finalizeDownload(\n this.downloadByName(bucketName, fileName, headers, requestHeaderValue(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 newHeaders = { ...response.headers }\n for (const [param, header] of Object.entries(DOWNLOAD_RESPONSE_OVERRIDE_HEADERS)) {\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 async handleUploadFile(\n url: string,\n headers: Record<string, string>,\n data: Uint8Array,\n ): Promise<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 = decodeHeaderValue(headers['x-bz-file-name'] ?? '')\n const contentType = headers['content-type'] ?? 'application/octet-stream'\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 // SHA-1 verification, matching real B2 semantics for X-Bz-Content-Sha1:\n // - 'none' / 'do_not_verify' (or missing) -> stored as 'none', no check\n // - 'unverified:<hex>' -> store <hex>, no check\n // - 'hex_digits_at_end' -> verify the trailing digest,\n // store body without trailer\n // - 40-char hex -> verify against the bytes,\n // 400 bad_request on mismatch\n // Without this, the simulator stored the client's claimed hash verbatim, so\n // a wrong digest or corrupted bytes passed every test.\n const resolved = await this.resolveUploadSha1(headers['x-bz-content-sha1'], data)\n if ('status' in resolved) return resolved\n const { sha1: contentSha1, data: storedData } = resolved\n\n const serverSideEncryption = await uploadServerSideEncryption(\n headers,\n bucket.info.defaultServerSideEncryption,\n )\n if ('status' in serverSideEncryption) return serverSideEncryption\n const customUploadTimestamp = this.parseCustomUploadTimestamp(\n requestHeaderValue(headers, 'x-bz-custom-upload-timestamp'),\n )\n if ('status' in customUploadTimestamp) {\n return customUploadTimestamp\n }\n const fileVersion = this.makeFileVersion({\n bucketId,\n fileName,\n contentType,\n contentLength: storedData.byteLength,\n contentSha1,\n fileInfo,\n action: FileAction.Upload,\n serverSideEncryption,\n ...this.newReplicationStatusField(bucketId, fileName),\n ...(customUploadTimestamp.timestamp !== null\n ? { uploadTimestamp: customUploadTimestamp.timestamp }\n : {}),\n })\n const stored: StoredFile = { fileVersion, data: storedData, serverSideEncryption }\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 /**\n * Resolve the `X-Bz-Content-Sha1` header into the SHA-1 to store, verifying\n * it against the uploaded bytes where B2 would. Returns the SHA-1 string to\n * persist, or a `400 bad_request` response when a verifiable hash does not\n * match the data received.\n *\n * @param header - The raw `X-Bz-Content-Sha1` header value, if any.\n * @param data - The uploaded bytes.\n *\n * @returns The SHA-1 string to store, or an error response on mismatch.\n */\n private async resolveUploadSha1(\n header: string | undefined,\n data: Uint8Array,\n ): Promise<{ sha1: string; data: Uint8Array } | SimulatorJsonResponse> {\n const value = header ?? 'none'\n if (value === 'none' || value === 'do_not_verify') return { sha1: 'none', data }\n if (value.startsWith('unverified:')) {\n return { sha1: value.slice('unverified:'.length).toLowerCase(), data }\n }\n if (value === 'hex_digits_at_end') {\n // B2 trailing-SHA mode: the final 40 bytes of the body are the hex digest,\n // not file content. Split them off, verify the rest, and store only the\n // body so contentLength and downloaded bytes match real B2.\n if (data.byteLength < 40) {\n return this.error(400, 'bad_request', 'Sha1 did not match data received')\n }\n const body = data.subarray(0, data.byteLength - 40)\n const trailer = utf8Decoder.decode(data.subarray(data.byteLength - 40)).toLowerCase()\n const actual = await sha1Hex(body)\n if (actual !== trailer) {\n return this.error(400, 'bad_request', 'Sha1 did not match data received')\n }\n return { sha1: actual, data: body }\n }\n const expected = value.toLowerCase()\n const actual = await sha1Hex(data)\n if (actual !== expected) {\n // Match real B2's error string exactly (no digests) for fidelity.\n return this.error(400, 'bad_request', 'Sha1 did not match data received')\n }\n return { sha1: expected, data }\n }\n\n private async validateUploadPartEncryption(\n large: LargeFileInProgress,\n headers: Record<string, string>,\n ): Promise<SimulatorJsonResponse | null> {\n if (large.serverSideEncryption.mode !== EncryptionMode.SseC) {\n if (!hasCustomerEncryptionHeaders(headers)) return null\n return this.error(400, 'bad_request', 'SSE-C upload part headers require an SSE-C large file')\n }\n\n const supplied = await storedServerSideEncryption({\n mode: EncryptionMode.SseC,\n algorithm: headers['x-bz-server-side-encryption-customer-algorithm'] as EncryptionAlgorithm,\n customerKey: headers['x-bz-server-side-encryption-customer-key'] as string,\n customerKeyMd5: headers['x-bz-server-side-encryption-customer-key-md5'] as string,\n })\n if ('status' in supplied) return supplied\n if (!sameStoredServerSideEncryption(large.serverSideEncryption, supplied)) {\n return this.error(\n 400,\n 'bad_request',\n 'SSE-C upload parts require matching customer encryption headers',\n )\n }\n\n return null\n }\n\n private async handleUploadPart(\n url: string,\n headers: Record<string, string>,\n data: Uint8Array,\n ): Promise<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 = this.parsePartNumberHeader(requestHeaderValue(headers, 'x-bz-part-number'))\n if (typeof partNumber !== 'number') return partNumber\n\n const encryptionError = await this.validateUploadPartEncryption(large, headers)\n if (encryptionError !== null) return encryptionError\n\n // Verify the part bytes against X-Bz-Content-Sha1, same as b2_upload_file.\n // Parts are always sent with a real (or unverified:) sha1 by the SDK.\n const resolved = await this.resolveUploadSha1(headers['x-bz-content-sha1'], data)\n if ('status' in resolved) return resolved\n const { sha1, data: partData } = resolved\n\n const uploadTimestamp = this.monotonicTimestamp()\n large.parts.set(partNumber, { data: partData, sha1, contentMd5: null, uploadTimestamp })\n\n return {\n status: 200,\n body: {\n fileId: large.fileId,\n partNumber,\n contentLength: partData.byteLength,\n contentSha1: sha1,\n contentMd5: null,\n serverSideEncryption: publicServerSideEncryption(large.serverSideEncryption),\n uploadTimestamp,\n },\n }\n }\n\n private parsePartNumberHeader(header: string | undefined): number | SimulatorJsonResponse {\n if (header === undefined) {\n return this.error(400, 'bad_request', 'X-Bz-Part-Number header is required')\n }\n if (!/^\\d+$/.test(header)) {\n return this.error(\n 400,\n 'bad_request',\n `X-Bz-Part-Number must be an integer between ${B2_MIN_PART_NUMBER} and ${B2_MAX_PART_NUMBER}; received ${header}`,\n )\n }\n const partNumber = Number(header)\n return this.validatePartNumber(partNumber) ?? partNumber\n }\n\n private validatePartNumber(partNumber: number): SimulatorJsonResponse | null {\n if (\n Number.isInteger(partNumber) &&\n partNumber >= B2_MIN_PART_NUMBER &&\n partNumber <= B2_MAX_PART_NUMBER\n ) {\n return null\n }\n return this.error(\n 400,\n 'bad_request',\n `partNumber must be an integer between ${B2_MIN_PART_NUMBER} and ${B2_MAX_PART_NUMBER}; received ${String(partNumber)}`,\n )\n }\n\n private downloadById(\n fileId: string,\n headers: Record<string, string>,\n range?: string,\n ): SimulatorDownloadResponse {\n const found = this.findFile(fileId)\n if (found === null) return { status: 404, headers: {}, data: null }\n return this.serveFile(found.stored, headers, range)\n }\n\n private downloadByName(\n bucketName: string,\n fileName: string,\n headers: Record<string, 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, headers, range)\n }\n return { status: 404, headers: {}, data: null }\n }\n\n private validateDownloadEncryption(\n stored: StoredFile,\n headers: Record<string, string>,\n ): SimulatorDownloadResponse | null {\n const encryption = stored.serverSideEncryption\n if (encryption.mode !== EncryptionMode.SseC) return null\n\n const algorithm = headers['x-bz-server-side-encryption-customer-algorithm']\n const customerKey = headers['x-bz-server-side-encryption-customer-key']\n const customerKeyMd5 = headers['x-bz-server-side-encryption-customer-key-md5']\n const customerKeyBytes = customerKey === undefined ? null : base64ToBytes(customerKey, 32)\n const normalizedCustomerKeyMd5 =\n customerKeyMd5 === undefined ? null : normalizeSizedBase64(customerKeyMd5, 16)\n if (\n algorithm !== encryption.algorithm ||\n customerKeyBytes === null ||\n normalizedCustomerKeyMd5 === null ||\n md5Base64Sync(customerKeyBytes) !== normalizedCustomerKeyMd5\n ) {\n return this.errorAsDownload(\n this.error(\n 400,\n 'bad_request',\n 'SSE-C downloads require matching customer encryption headers',\n ),\n )\n }\n if (\n normalizedCustomerKeyMd5 !== encryption.customerKeyMd5 ||\n customerKeyDigest(customerKeyBytes) !== encryption.customerKeyDigest\n ) {\n return this.errorAsDownload(\n this.error(\n 403,\n 'access_denied',\n 'SSE-C customer encryption key does not match the stored file',\n ),\n )\n }\n\n return null\n }\n\n private async validateSourceServerSideEncryption(\n stored: StoredFile,\n encryption: EncryptionSetting | undefined,\n ): Promise<SimulatorJsonResponse | null> {\n const expected = stored.serverSideEncryption\n if (expected.mode !== EncryptionMode.SseC) return null\n if (encryption?.mode !== EncryptionMode.SseC) {\n return this.error(400, 'bad_request', 'SSE-C source copies require customer encryption')\n }\n\n const supplied = await storedServerSideEncryption(encryption)\n if ('status' in supplied) return supplied\n if (!sameStoredServerSideEncryption(expected, supplied)) {\n return this.error(400, 'bad_request', 'SSE-C source customer encryption does not match')\n }\n return null\n }\n\n private async resolveCopyDestinationEncryption(\n destinationBucket: StoredBucket,\n requested: EncryptionSetting | undefined,\n ): Promise<StoredServerSideEncryption | SimulatorJsonResponse> {\n if (requested !== undefined) return await storedServerSideEncryption(requested)\n return await storedServerSideEncryption(destinationBucket.info.defaultServerSideEncryption)\n }\n\n private async validateCopyPartDestinationEncryption(\n large: LargeFileInProgress,\n encryption: EncryptionSetting | undefined,\n ): Promise<SimulatorJsonResponse | null> {\n if (encryption === undefined) {\n return large.serverSideEncryption.mode === EncryptionMode.SseC\n ? this.error(400, 'bad_request', 'SSE-C destination copies require customer encryption')\n : null\n }\n\n const supplied = await storedServerSideEncryption(encryption)\n if ('status' in supplied) return supplied\n if (!sameStoredServerSideEncryption(large.serverSideEncryption, supplied)) {\n return this.error(400, 'bad_request', 'Destination server-side encryption does not match')\n }\n return null\n }\n\n private serveFile(\n stored: StoredFile,\n requestHeaders: Record<string, string>,\n range?: string,\n ): SimulatorDownloadResponse {\n const encryptionError = this.validateDownloadEncryption(stored, requestHeaders)\n if (encryptionError !== null) return encryptionError\n\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': encodeFileName(fv.fileName),\n 'X-Bz-Content-Sha1': fv.contentSha1 ?? 'none',\n 'X-Bz-Upload-Timestamp': String(fv.uploadTimestamp),\n }\n // Serialize stored fileInfo as X-Bz-Info-* response headers so custom\n // metadata round-trips through download(), not just getFileInfo/list. Use\n // the same B2 wire encoding (encodeFileName) the SDK's download parser\n // decodes with (decodeFileName), rather than encodeURIComponent.\n for (const [key, value] of Object.entries(fv.fileInfo)) {\n headers[`X-Bz-Info-${encodeFileName(key)}`] = encodeFileName(value)\n }\n // Preserve the synthetic last-modified default only when the upload didn't\n // set one explicitly.\n if (!('src_last_modified_millis' in fv.fileInfo)) {\n headers['X-Bz-Info-src_last_modified_millis'] = String(fv.uploadTimestamp)\n }\n const unauthorizedToRead: DownloadClientUnauthorizedToReadMarker[] = []\n this.addDownloadEncryptionHeaders(\n headers,\n stored.serverSideEncryption,\n requestHeaders,\n unauthorizedToRead,\n )\n this.addDownloadObjectLockHeaders(headers, fv, requestHeaders, unauthorizedToRead)\n if (unauthorizedToRead.length > 0) {\n headers[DownloadHeaderName.ClientUnauthorizedToRead] = unauthorizedToRead.join(',')\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 private addDownloadEncryptionHeaders(\n headers: Record<string, string>,\n encryption: StoredServerSideEncryption,\n requestHeaders: Record<string, string>,\n unauthorizedToRead: DownloadClientUnauthorizedToReadMarker[],\n ): void {\n if (encryption.mode === EncryptionMode.None) return\n if (\n !this.requestHasCapability(\n requestHeaderValue(requestHeaders, 'authorization'),\n Capability.ReadBucketEncryption,\n )\n ) {\n if (encryption.mode === EncryptionMode.SseB2) {\n unauthorizedToRead.push(DownloadClientUnauthorizedToReadMarker.ServerSideEncryption)\n } else {\n unauthorizedToRead.push(\n DownloadClientUnauthorizedToReadMarker.ServerSideEncryptionCustomerAlgorithm,\n DownloadClientUnauthorizedToReadMarker.ServerSideEncryptionCustomerKeyMd5,\n )\n }\n return\n }\n\n if (encryption.mode === EncryptionMode.SseB2) {\n headers[DownloadHeaderName.ServerSideEncryption] = encryption.algorithm\n return\n }\n headers[DownloadHeaderName.ServerSideEncryptionCustomerAlgorithm] = encryption.algorithm\n headers[DownloadHeaderName.ServerSideEncryptionCustomerKeyMd5] = encryption.customerKeyMd5\n }\n\n private addDownloadObjectLockHeaders(\n headers: Record<string, string>,\n fileVersion: FileVersion,\n requestHeaders: Record<string, string>,\n unauthorizedToRead: DownloadClientUnauthorizedToReadMarker[],\n ): void {\n const authToken = requestHeaderValue(requestHeaders, 'authorization')\n const retention = fileVersion.fileRetention.value\n if (retention !== null && retention.mode !== null && retention.retainUntilTimestamp !== null) {\n if (this.requestHasCapability(authToken, Capability.ReadFileRetentions)) {\n headers[DownloadHeaderName.FileRetentionMode] = retention.mode\n headers[DownloadHeaderName.FileRetentionRetainUntilTimestamp] = String(\n retention.retainUntilTimestamp,\n )\n } else {\n unauthorizedToRead.push(\n DownloadClientUnauthorizedToReadMarker.FileRetentionMode,\n DownloadClientUnauthorizedToReadMarker.FileRetentionRetainUntilTimestamp,\n )\n }\n }\n\n const legalHold = fileVersion.legalHold.value\n if (legalHold === null) return\n if (this.requestHasCapability(authToken, Capability.ReadFileLegalHolds)) {\n headers[DownloadHeaderName.FileLegalHold] = legalHold\n return\n }\n unauthorizedToRead.push(DownloadClientUnauthorizedToReadMarker.FileLegalHold)\n }\n\n // --- API handlers ---\n\n private authorize(authzHeader?: string, origin = 'http://localhost:0'): SimulatorJsonResponse {\n // Token validity: real B2 = 24h; configurable via `authTokenTtlMs`.\n // The issued token and response body both inherit the exact grant\n // resolved from the Basic credentials used for this authorize call.\n const resolution = this.findAuthorizationGrant(authzHeader)\n if (resolution.kind === 'invalid') {\n return this.error(401, 'unauthorized', 'application key is not valid')\n }\n if (resolution.kind === 'expired') {\n return this.error(401, 'unauthorized', 'application key is expired')\n }\n const { grant } = resolution\n const allowedBuckets = this.allowedBuckets(grant.bucketIds)\n const legacyBucketId = singleBucketId(grant.bucketIds)\n const legacyBucketName =\n legacyBucketId === null ? null : (this.buckets.get(legacyBucketId)?.info.bucketName ?? null)\n const tokenStr = `sim_auth_token_${this.nextId++}`\n this.issuedTokens.set(tokenStr, {\n accountId: grant.accountId,\n capabilities: grant.capabilities,\n bucketIds: grant.bucketIds,\n namePrefix: grant.namePrefix,\n expiresAt: this.now() + this.authTokenTtlMs,\n applicationKeyId: grant.applicationKeyId,\n })\n return {\n status: 200,\n body: {\n accountId: accountIdOf(grant.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: origin,\n bucketId: legacyBucketId === null ? null : bucketIdOf(legacyBucketId),\n bucketName: legacyBucketName,\n downloadUrl: origin,\n infoType: 'storageApi',\n namePrefix: grant.namePrefix,\n recommendedPartSize: this.recommendedPartSize,\n s3ApiUrl: origin,\n allowed: {\n capabilities: cloneCapabilities(grant.capabilities),\n buckets: allowedBuckets,\n bucketId: legacyBucketId === null ? null : bucketIdOf(legacyBucketId),\n bucketName: legacyBucketName,\n namePrefix: grant.namePrefix,\n },\n },\n },\n applicationKeyExpirationTimestamp: grant.expirationTimestamp,\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 const objectLockEnabled = nextObjectLockEnabled(false, req.fileLockEnabled)\n if (typeof objectLockEnabled !== 'boolean') {\n return this.error(400, objectLockEnabled.code, objectLockEnabled.message)\n }\n const configError = validateBucketConfigurationFields(req, { objectLockEnabled })\n if (configError) return this.error(400, configError.code, configError.message)\n const replicationKeyError = this.validateReplicationApplicationKeys(\n req.replicationConfiguration,\n undefined,\n )\n if (replicationKeyError !== null) return replicationKeyError\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: objectLockEnabled,\n defaultRetention,\n },\n },\n lifecycleRules: req.lifecycleRules ?? [],\n options: [],\n revision: 1,\n defaultRetention,\n replicationConfiguration:\n req.replicationConfiguration === undefined\n ? { asReplicationSource: null, asReplicationDestination: null }\n : normalizeReplicationConfiguration(req.replicationConfiguration),\n }\n this.buckets.set(bid, { info, files: new Map() })\n return { status: 200, body: info }\n }\n\n private listBuckets(req: {\n bucketId?: string\n bucketName?: string\n bucketTypes?: readonly BucketType[]\n }): SimulatorJsonResponse {\n const bucketTypesError = validateBucketTypes(req.bucketTypes)\n if (bucketTypesError) return this.error(400, bucketTypesError.code, bucketTypesError.message)\n const buckets = [...this.buckets.values()]\n .map((b) => b.info)\n .filter((bucket) => req.bucketId === undefined || bucket.bucketId === req.bucketId)\n .filter((bucket) => req.bucketName === undefined || bucket.bucketName === req.bucketName)\n .filter(\n (bucket) => req.bucketTypes === undefined || req.bucketTypes.includes(bucket.bucketType),\n )\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 if (this.bucketHasContents(bucket)) {\n return this.error(\n 400,\n 'cannot_delete_non_empty_bucket',\n 'Bucket is not empty and cannot be deleted',\n )\n }\n this.buckets.delete(req.bucketId)\n return { status: 200, body: bucket.info }\n }\n\n private bucketHasContents(bucket: StoredBucket): boolean {\n for (const versions of bucket.files.values()) {\n if (versions.length > 0) return true\n }\n for (const large of this.largeFiles.values()) {\n if (large.bucketId === bucket.info.bucketId) return true\n }\n return false\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 const revisionGuard = req['ifRevisionIs']\n if (revisionGuard !== undefined) {\n if (typeof revisionGuard !== 'number' || !Number.isInteger(revisionGuard)) {\n return this.error(400, 'bad_request', 'ifRevisionIs must be an integer')\n }\n if (revisionGuard !== bucket.info.revision) {\n return this.error(409, 'conflict', 'ifRevisionIs test failed')\n }\n }\n const currentObjectLockEnabled =\n bucket.info.fileLockConfiguration.value?.isFileLockEnabled ?? false\n const objectLockEnabled = nextObjectLockEnabled(\n currentObjectLockEnabled,\n req['fileLockEnabled'],\n )\n if (typeof objectLockEnabled !== 'boolean') {\n return this.error(400, objectLockEnabled.code, objectLockEnabled.message)\n }\n const configError = validateBucketConfigurationFields(\n {\n bucketInfo: req['bucketInfo'],\n corsRules: req['corsRules'],\n defaultRetention: req['defaultRetention'],\n lifecycleRules: req['lifecycleRules'],\n replicationConfiguration: req['replicationConfiguration'],\n },\n { objectLockEnabled },\n )\n if (configError) return this.error(400, configError.code, configError.message)\n const replicationKeyError = this.validateReplicationApplicationKeys(\n req['replicationConfiguration'],\n req['bucketId'] as string,\n )\n if (replicationKeyError !== null) return replicationKeyError\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: normalizeReplicationConfiguration(\n req['replicationConfiguration'] as BucketInfo['replicationConfiguration'],\n ),\n }\n : {}),\n ...(req['defaultRetention'] !== undefined\n ? {\n defaultRetention: req['defaultRetention'] as BucketInfo['defaultRetention'],\n fileLockConfiguration: {\n isClientAuthorizedToRead: true,\n value: {\n isFileLockEnabled: objectLockEnabled,\n defaultRetention: req['defaultRetention'] as BucketInfo['defaultRetention'],\n },\n },\n }\n : {}),\n ...(req['fileLockEnabled'] !== undefined && req['defaultRetention'] === undefined\n ? {\n fileLockConfiguration: {\n isClientAuthorizedToRead: true,\n value: {\n isFileLockEnabled: objectLockEnabled,\n defaultRetention: bucket.info.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 async getUploadUrl(\n req: { bucketId: string },\n authToken?: string,\n ): Promise<SimulatorJsonResponse> {\n const bucket = this.buckets.get(req.bucketId)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n const uploadAuth = await this.issueUploadAuthorization({\n kind: 'file',\n sourceAuthToken: authToken,\n bucketId: req.bucketId,\n })\n return {\n status: 200,\n body: {\n bucketId: req.bucketId,\n uploadUrl: uploadAuth.uploadUrl,\n authorizationToken: uploadAuth.authorizationToken,\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) => compareB2FileNames(a.fileName, 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 = compareB2FileNames(a.fileName, 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 = {\n fileVersion,\n data: new Uint8Array(0),\n serverSideEncryption: { mode: EncryptionMode.None },\n }\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(\n req: {\n fileId: string\n fileName: string\n bypassGovernance?: boolean\n },\n authToken?: string,\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 === LegalHoldValue.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 === RetentionMode.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 === RetentionMode.Governance &&\n retention.retainUntilTimestamp !== null &&\n retention.retainUntilTimestamp > now\n ) {\n const bypassError = this.requireGovernanceBypass(\n req,\n authToken,\n 'File is under governance-mode retention; pass bypassGovernance: true to delete',\n )\n if (bypassError !== null) return bypassError\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 async copyFile(req: {\n sourceFileId: string\n fileName: string\n destinationBucketId?: string\n range?: string\n metadataDirective?: string\n contentType?: string\n fileInfo?: Record<string, string>\n sourceServerSideEncryption?: EncryptionSetting\n destinationServerSideEncryption?: EncryptionSetting\n }): Promise<{ status: number; body: unknown }> {\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 sourceEncryptionError = await this.validateSourceServerSideEncryption(\n sourceStored,\n req.sourceServerSideEncryption,\n )\n if (sourceEncryptionError !== null) return sourceEncryptionError\n\n const destinationEncryption = await this.resolveCopyDestinationEncryption(\n destBucket,\n req.destinationServerSideEncryption,\n )\n if ('status' in destinationEncryption) return destinationEncryption\n\n // Honor an optional byte range: copy only the requested slice. The copied\n // content differs from the source, so its SHA-1 is recomputed; a full copy\n // preserves the source's stored SHA-1 (including 'none' for large files).\n let data = sourceStored.data\n let contentSha1 = sourceStored.fileVersion.contentSha1 ?? 'none'\n if (req.range !== undefined) {\n const parsed = parseRangeHeader(req.range, sourceStored.data.byteLength)\n // B2 returns 416 for a well-formed-but-unsatisfiable range; a malformed\n // range is an invalid request field, so 400 bad_request.\n if (parsed.kind === 'malformed') {\n return this.error(400, 'bad_request', `Malformed copy range: ${req.range}`)\n }\n if (parsed.kind === 'unsatisfiable') {\n return this.error(416, 'range_not_satisfiable', `Unsatisfiable copy range: ${req.range}`)\n }\n data = sourceStored.data.subarray(parsed.start, parsed.end + 1)\n contentSha1 = await sha1Hex(data)\n }\n\n // Metadata directive: COPY (default) preserves the source's contentType +\n // fileInfo and forbids replacement metadata; REPLACE applies the request's\n // (contentType required, fileInfo validated). Real B2 rejects an unknown\n // directive, and rejects contentType/fileInfo supplied in COPY mode.\n const directive = req.metadataDirective\n if (directive !== undefined && directive !== 'COPY' && directive !== 'REPLACE') {\n return this.error(400, 'bad_request', `Invalid metadataDirective: ${directive}`)\n }\n let contentType: string\n let fileInfo: Record<string, string>\n if (directive === 'REPLACE') {\n if (req.contentType === undefined) {\n return this.error(\n 400,\n 'bad_request',\n 'contentType is required when metadataDirective is REPLACE',\n )\n }\n const replaceFileInfo = req.fileInfo ?? {}\n const fileInfoError = validateFileInfo(replaceFileInfo)\n if (fileInfoError) return this.error(400, fileInfoError.code, fileInfoError.message)\n contentType = req.contentType\n fileInfo = replaceFileInfo\n } else {\n if (req.contentType !== undefined || req.fileInfo !== undefined) {\n return this.error(\n 400,\n 'bad_request',\n 'contentType and fileInfo may only be set when metadataDirective is REPLACE',\n )\n }\n contentType = sourceStored.fileVersion.contentType\n fileInfo = sourceStored.fileVersion.fileInfo\n }\n\n const fileVersion = this.makeFileVersion({\n bucketId: destBucketId,\n fileName: req.fileName,\n contentType,\n contentLength: data.byteLength,\n contentSha1,\n fileInfo,\n action: FileAction.Copy,\n serverSideEncryption: destinationEncryption,\n ...this.newReplicationStatusField(destBucketId, req.fileName),\n })\n const copied: StoredFile = {\n fileVersion,\n data: new Uint8Array(data),\n serverSideEncryption: destinationEncryption,\n }\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 async startLargeFile(req: {\n bucketId: string\n fileName: string\n contentType: string\n customUploadTimestamp?: string | null\n fileInfo?: Record<string, string>\n fileRetention?: FileRetentionValue\n legalHold?: LegalHoldValue\n serverSideEncryption?: EncryptionSetting\n }): Promise<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 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 customUploadTimestamp = this.parseCustomUploadTimestamp(req.customUploadTimestamp)\n if ('status' in customUploadTimestamp) return customUploadTimestamp\n\n const fid = this.genId('4_z')\n const uploadTimestamp = customUploadTimestamp.timestamp ?? this.monotonicTimestamp()\n const serverSideEncryption = await storedServerSideEncryption(\n req.serverSideEncryption ?? bucket.info.defaultServerSideEncryption,\n )\n if ('status' in serverSideEncryption) return serverSideEncryption\n const replicationStatus = this.replicationStatusForNewFile(req.bucketId, req.fileName)\n const large: LargeFileInProgress = {\n fileId: fid,\n bucketId: req.bucketId,\n fileName: req.fileName,\n contentType: req.contentType,\n fileInfo: req.fileInfo ?? {},\n fileRetention:\n req.fileRetention ?? defaultFileRetention(bucket.info.defaultRetention, uploadTimestamp),\n legalHold: req.legalHold ?? null,\n ...this.replicationStatusMetadataFor(replicationStatus),\n serverSideEncryption,\n uploadTimestamp,\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 action: FileAction.Start,\n contentLength: 0,\n contentSha1: 'none',\n contentMd5: null,\n fileRetention: {\n isClientAuthorizedToRead: true,\n value: large.fileRetention,\n },\n legalHold: {\n isClientAuthorizedToRead: true,\n value: large.legalHold,\n },\n ...this.replicationStatusMetadataFor(large.replicationStatus),\n serverSideEncryption: publicServerSideEncryption(large.serverSideEncryption),\n uploadTimestamp: large.uploadTimestamp,\n },\n }\n }\n\n private async getUploadPartUrl(\n req: { fileId: string },\n authToken?: string,\n ): Promise<SimulatorJsonResponse> {\n const large = this.largeFiles.get(req.fileId)\n if (large === undefined) return this.error(400, 'bad_request', 'Large file not found')\n const uploadAuth = await this.issueUploadAuthorization({\n kind: 'part',\n sourceAuthToken: authToken,\n fileId: req.fileId,\n fileName: large.fileName,\n })\n return {\n status: 200,\n body: {\n fileId: req.fileId,\n uploadUrl: uploadAuth.uploadUrl,\n authorizationToken: uploadAuth.authorizationToken,\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 > B2_MAX_PART_NUMBER) {\n return this.error(\n 400,\n 'bad_request',\n `multipart upload has ${sortedParts.length} parts; B2 caps at ${B2_MAX_PART_NUMBER}`,\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 or older simulator\n // state already contains invalid part numbers.\n for (const [partNumber] of sortedParts) {\n if (\n !Number.isInteger(partNumber) ||\n partNumber < B2_MIN_PART_NUMBER ||\n partNumber > B2_MAX_PART_NUMBER\n ) {\n return this.error(\n 400,\n 'bad_request',\n `partNumber ${partNumber} is outside the [${B2_MIN_PART_NUMBER}, ${B2_MAX_PART_NUMBER}] 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: partSha1Array is the ordered checksum list that\n // confirms the right parts were uploaded in the right order. Compare each\n // entry against the stored part's SHA-1; real B2 rejects a mismatch with\n // `bad_request`.\n for (const [i, [partNumber, part]] of sortedParts.entries()) {\n if (req.partSha1Array[i]?.toLowerCase() !== part.sha1) {\n return this.error(\n 400,\n 'bad_request',\n `part ${partNumber} SHA-1 does not match the uploaded part`,\n )\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 (const [partNumber, part] of sortedParts.slice(0, -1)) {\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 fileInfo: large.fileInfo,\n action: FileAction.Upload,\n fileRetention: large.fileRetention,\n legalHold: large.legalHold,\n serverSideEncryption: large.serverSideEncryption,\n uploadTimestamp: large.uploadTimestamp,\n ...this.replicationStatusMetadataFor(large.replicationStatus),\n })\n const stored: StoredFile = {\n fileVersion,\n data: combined,\n serverSideEncryption: large.serverSideEncryption,\n }\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 // Keep listing order deterministic; resume sorts scanned exact-name\n // matches by uploadTimestamp before selecting one.\n const candidates = [...this.largeFiles.values()]\n .filter((f) => f.bucketId === req.bucketId)\n .filter((f) => f.fileName.startsWith(prefix))\n .sort((a, b) => compareB2FileNames(a.fileName, b.fileName))\n\n // `startFileId` is the inclusive cursor returned from a prior page.\n // When present in the current listing, that entry is returned first.\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 action: FileAction.Start,\n contentLength: 0,\n contentSha1: 'none',\n contentMd5: null,\n fileInfo: f.fileInfo,\n fileRetention: {\n isClientAuthorizedToRead: true,\n value: f.fileRetention,\n },\n legalHold: {\n isClientAuthorizedToRead: true,\n value: f.legalHold,\n },\n ...this.replicationStatusMetadataFor(f.replicationStatus),\n serverSideEncryption: publicServerSideEncryption(f.serverSideEncryption),\n uploadTimestamp: f.uploadTimestamp,\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 contentMd5: part.contentMd5,\n serverSideEncryption: publicServerSideEncryption(large.serverSideEncryption),\n uploadTimestamp: part.uploadTimestamp,\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 sourceServerSideEncryption?: EncryptionSetting\n destinationServerSideEncryption?: EncryptionSetting\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 const partNumberError = this.validatePartNumber(req.partNumber)\n if (partNumberError !== null) return partNumberError\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 const sourceEncryptionError = await this.validateSourceServerSideEncryption(\n sourceStored,\n req.sourceServerSideEncryption,\n )\n if (sourceEncryptionError !== null) return sourceEncryptionError\n\n const destinationEncryptionError = await this.validateCopyPartDestinationEncryption(\n large,\n req.destinationServerSideEncryption,\n )\n if (destinationEncryptionError !== null) return destinationEncryptionError\n\n let partData = sourceStored.data\n if (req.range !== undefined) {\n const parsed = parseRangeHeader(req.range, sourceStored.data.byteLength)\n if (parsed.kind === 'malformed') {\n return this.error(400, 'bad_request', `Malformed copy range: ${req.range}`)\n }\n if (parsed.kind === 'unsatisfiable') {\n return this.error(416, 'range_not_satisfiable', `Unsatisfiable copy range: ${req.range}`)\n }\n partData = sourceStored.data.subarray(parsed.start, parsed.end + 1)\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 const uploadTimestamp = this.monotonicTimestamp()\n large.parts.set(req.partNumber, {\n data: new Uint8Array(partData),\n sha1,\n contentMd5: null,\n uploadTimestamp,\n })\n\n return {\n status: 200,\n body: {\n fileId: req.largeFileId,\n partNumber: req.partNumber,\n contentLength: partData.byteLength,\n contentSha1: sha1,\n contentMd5: null,\n serverSideEncryption: publicServerSideEncryption(large.serverSideEncryption),\n uploadTimestamp,\n },\n }\n }\n\n private getDownloadAuthorization(req: DownloadAuthorizationRequestBody): SimulatorJsonResponse {\n this.purgeExpiredDownloadAuthorizationTokens()\n if (!this.buckets.has(req.bucketId)) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n const prefixError = validateDownloadAuthorizationPrefix(req.fileNamePrefix)\n if (prefixError) return this.error(400, prefixError.code, prefixError.message)\n const durationError = validateDownloadAuthorizationDuration(req.validDurationInSeconds)\n if (durationError) return this.error(400, durationError.code, durationError.message)\n const overrideError = validateDownloadResponseOverrides(req)\n if (overrideError) return this.error(400, overrideError.code, overrideError.message)\n let authorizationToken = randomDownloadAuthorizationToken()\n while (this.downloadAuthorizationTokens.has(authorizationToken)) {\n authorizationToken = randomDownloadAuthorizationToken()\n }\n const expiresAt = this.now() + req.validDurationInSeconds * 1000\n this.downloadAuthorizationTokens.set(authorizationToken, {\n bucketId: req.bucketId,\n fileNamePrefix: req.fileNamePrefix,\n expiresAt,\n responseHeaderOverrides: pickDownloadResponseOverrides(req),\n })\n this.pushDownloadAuthorizationExpiry({ token: authorizationToken, expiresAt })\n return {\n status: 200,\n body: {\n bucketId: req.bucketId,\n fileNamePrefix: req.fileNamePrefix,\n authorizationToken,\n },\n }\n }\n\n // --- Keys ---\n\n private createKeyCapabilitiesOutsideGrant(\n authToken: string | undefined,\n requested: readonly Capability[],\n ): readonly Capability[] {\n // In default mode, unknown auth tokens keep the simulator permissive. When the\n // token is known, preserve the creator's real grant so delegated keys cannot\n // exceed it even without full strict-auth request enforcement.\n const creator = authToken === undefined ? undefined : this.issuedTokens.get(authToken)\n if (creator === undefined) return this.strictAuth ? requested : []\n // The simulator's implicit master credential may mint any valid capability.\n if (creator.applicationKeyId === null) return []\n const granted = new Set(creator.capabilities)\n return requested.filter((capability) => !granted.has(capability))\n }\n\n private createKeyCreatorAccountId(authToken: string | undefined): string {\n const creator = authToken === undefined ? undefined : this.issuedTokens.get(authToken)\n return creator?.accountId ?? this.accountId\n }\n\n private createKey(\n req: {\n accountId: string\n capabilities: unknown\n keyName: unknown\n validDurationInSeconds?: number\n bucketIds?: readonly string[] | null\n bucketId?: string\n namePrefix?: string\n },\n version: B2ApiVersion,\n authToken: string | undefined,\n ): SimulatorJsonResponse {\n if (version === 'v4' && hasOwnField(req, 'bucketId')) {\n return this.error(\n 400,\n 'bad_request',\n 'bucketId is not accepted by v4 b2_create_key; use bucketIds',\n )\n }\n if (version !== 'v4' && req.bucketId !== undefined && req.bucketIds !== undefined) {\n return this.error(400, 'bad_request', 'b2_create_key accepts either bucketIds or bucketId')\n }\n const bucketIds =\n version !== 'v4' && req.bucketId !== undefined\n ? Object.freeze([req.bucketId])\n : normalizeKeyBucketIds(req)\n const namePrefix = req.namePrefix === undefined || req.namePrefix === '' ? null : req.namePrefix\n const creatorAccountId = this.createKeyCreatorAccountId(authToken)\n if (req.accountId !== creatorAccountId) {\n return this.error(400, 'bad_request', 'accountId must match authorized account')\n }\n const keyNameError = validateCreateKeyName(req.keyName)\n if (keyNameError) return this.error(400, keyNameError.code, keyNameError.message)\n const keyName = req.keyName as string\n const capabilitiesError = validateCreateKeyCapabilities(req.capabilities)\n if (capabilitiesError) return this.error(400, capabilitiesError.code, capabilitiesError.message)\n const capabilities = normalizeCreateKeyCapabilities(req.capabilities as readonly Capability[])\n const unauthorizedCapabilities = this.createKeyCapabilitiesOutsideGrant(authToken, capabilities)\n if (unauthorizedCapabilities.length > 0) {\n return this.error(\n 400,\n 'bad_request',\n `requested capabilities exceed creator grant: ${unauthorizedCapabilities.join(', ')}`,\n )\n }\n if (hasKeyManagementCapability(capabilities) && (bucketIds !== null || namePrefix !== null)) {\n return this.error(\n 400,\n 'bad_request',\n 'key-management capabilities are account-level and cannot be bucket or name-prefix scoped',\n )\n }\n const kid = this.genId('sim_key')\n const appKey = this.genId('sim_secret')\n const expiration =\n req.validDurationInSeconds !== undefined\n ? this.now() + req.validDurationInSeconds * 1000\n : null\n const stored: StoredKey = {\n applicationKeyId: kid,\n keyName,\n capabilities,\n accountId: creatorAccountId,\n applicationKey: appKey,\n bucketIds,\n namePrefix,\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: cloneCapabilities(stored.capabilities),\n accountId: stored.accountId,\n expirationTimestamp: stored.expirationTimestamp,\n bucketIds: cloneBucketIds(stored.bucketIds),\n bucketId: singleBucketId(stored.bucketIds),\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: cloneCapabilities(k.capabilities),\n accountId: k.accountId,\n expirationTimestamp: k.expirationTimestamp,\n bucketIds: cloneBucketIds(k.bucketIds),\n bucketId: singleBucketId(k.bucketIds),\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 for (const [token, scope] of this.uploadTokens.entries()) {\n if (scope.applicationKeyId === req.applicationKeyId) {\n scope.invalidated = true\n this.uploadTokens.set(token, scope)\n }\n }\n return {\n status: 200,\n body: {\n keyName: key.keyName,\n applicationKeyId: key.applicationKeyId,\n capabilities: cloneCapabilities(key.capabilities),\n accountId: key.accountId,\n expirationTimestamp: key.expirationTimestamp,\n bucketIds: cloneBucketIds(key.bucketIds),\n bucketId: singleBucketId(key.bucketIds),\n namePrefix: key.namePrefix,\n options: [],\n },\n }\n }\n\n // --- File lock ---\n\n private updateFileRetention(\n req: {\n fileName: string\n fileId: string\n fileRetention: unknown\n bypassGovernance?: boolean\n },\n authToken?: 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 const fileLockError = this.requireFileLockEnabled(found.bucket)\n if (fileLockError !== null) return fileLockError\n\n const fileRetention = parseFileRetentionValue(req.fileRetention, Date.now())\n if (fileRetention === null) {\n return this.error(\n 400,\n 'bad_request',\n 'fileRetention must use a valid mode and retainUntilTimestamp pair',\n )\n }\n\n const current = found.stored.fileVersion.fileRetention?.value\n if (\n current?.mode === RetentionMode.Compliance &&\n (fileRetention.mode !== RetentionMode.Compliance ||\n isRetentionWeakened(current, fileRetention))\n ) {\n return this.error(\n 400,\n 'file_lock_compliance_protected',\n 'Compliance-mode retention cannot be shortened or removed',\n )\n }\n if (requiresGovernanceBypass(current, fileRetention)) {\n const bypassError = this.requireGovernanceBypass(\n req,\n authToken,\n 'Governance-mode retention cannot be shortened, removed, or converted without bypassGovernance',\n )\n if (bypassError !== null) return bypassError\n }\n\n found.versions[found.index] = {\n fileVersion: {\n ...found.stored.fileVersion,\n fileRetention: { isClientAuthorizedToRead: true, value: fileRetention },\n },\n data: found.stored.data,\n serverSideEncryption: found.stored.serverSideEncryption,\n }\n return {\n status: 200,\n body: {\n fileName: req.fileName,\n fileId: req.fileId,\n 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 const fileLockError = this.requireFileLockEnabled(found.bucket)\n if (fileLockError !== null) return fileLockError\n\n if (req.legalHold !== LegalHoldValue.On && req.legalHold !== LegalHoldValue.Off) {\n return this.error(400, 'bad_request', 'legalHold must be \"on\" or \"off\"')\n }\n found.versions[found.index] = {\n fileVersion: {\n ...found.stored.fileVersion,\n legalHold: {\n isClientAuthorizedToRead: true,\n value: req.legalHold,\n },\n },\n data: found.stored.data,\n serverSideEncryption: found.stored.serverSideEncryption,\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 const rulesError = validateNotificationRules(req.eventNotificationRules)\n if (rulesError) return this.error(400, rulesError.code, rulesError.message)\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 readonly fileInfo?: Record<string, string>\n readonly fileRetention?: FileRetentionValue | null\n readonly legalHold?: LegalHoldValue | null\n readonly replicationStatus?: ReplicationStatus\n readonly serverSideEncryption?: EncryptionSetting | StoredServerSideEncryption\n readonly uploadTimestamp?: number\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: params.fileInfo ?? {},\n fileName: params.fileName,\n fileRetention: { isClientAuthorizedToRead: true, value: params.fileRetention ?? null },\n legalHold: { isClientAuthorizedToRead: true, value: params.legalHold ?? null },\n ...this.replicationStatusMetadataFor(params.replicationStatus),\n serverSideEncryption: publicServerSideEncryption(\n params.serverSideEncryption ?? { mode: EncryptionMode.None },\n ),\n uploadTimestamp: params.uploadTimestamp ?? this.monotonicTimestamp(),\n }\n }\n\n private newReplicationStatusField(\n bucketId: string,\n fileName: string,\n ): { readonly replicationStatus?: ReplicationStatus } {\n return this.replicationStatusMetadataFor(this.replicationStatusForNewFile(bucketId, fileName))\n }\n\n private replicationStatusMetadataFor(replicationStatus: ReplicationStatus | undefined): {\n readonly replicationStatus?: ReplicationStatus\n } {\n return replicationStatus === undefined ? {} : { replicationStatus }\n }\n\n private replicationStatusForNewFile(\n bucketId: string,\n fileName: string,\n ): ReplicationStatus | undefined {\n return this.matchingEnabledReplicationRules(bucketId, fileName).length > 0\n ? 'PENDING'\n : undefined\n }\n\n private matchingEnabledReplicationRules(\n bucketId: string,\n fileName: string,\n ): readonly ReplicationRule[] {\n const bucket = this.buckets.get(bucketId)\n const sourceRules = bucket?.info.replicationConfiguration.asReplicationSource?.replicationRules\n return (\n sourceRules?.filter((rule) => rule.isEnabled && fileName.startsWith(rule.fileNamePrefix)) ??\n []\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 for (const rule of this.matchingEnabledReplicationRules(bucketId, fileVersion.fileName)) {\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\nfunction rawUrlPathContainsLiteralBackslash(rawUrl: string): boolean {\n const schemeEnd = rawUrl.indexOf('://')\n const authorityStart = schemeEnd === -1 ? 0 : schemeEnd + 3\n\n const queryStart = rawUrl.indexOf('?', authorityStart)\n const hashStart = rawUrl.indexOf('#', authorityStart)\n const urlEndCandidates = [queryStart, hashStart].filter((index) => index !== -1)\n const urlEnd = urlEndCandidates.length === 0 ? rawUrl.length : Math.min(...urlEndCandidates)\n\n const slashStart = rawUrl.indexOf('/', authorityStart)\n const backslashStart = rawUrl.indexOf('\\\\', authorityStart)\n const pathStartCandidates = [slashStart, backslashStart].filter(\n (index) => index !== -1 && index < urlEnd,\n )\n if (pathStartCandidates.length === 0) return false\n\n const pathStart = Math.min(...pathStartCandidates)\n const queryStartAfterPath = rawUrl.indexOf('?', pathStart)\n const hashStartAfterPath = rawUrl.indexOf('#', pathStart)\n const pathEndCandidates = [queryStartAfterPath, hashStartAfterPath].filter(\n (index) => index !== -1,\n )\n const pathEnd = pathEndCandidates.length === 0 ? rawUrl.length : Math.min(...pathEndCandidates)\n return rawUrl.slice(pathStart, pathEnd).includes('\\\\')\n}\n\nfunction badRequestJson(message: string): SimulatorJsonResponse {\n return { status: 400, body: { status: 400, code: 'bad_request', message } }\n}\n\ntype UploadBodyReadResult =\n | { readonly data: Uint8Array }\n | { readonly error: SimulatorJsonResponse }\n\nfunction checkedUploadBody(data: Uint8Array, expectedLength: number | null): UploadBodyReadResult {\n if (expectedLength !== null && data.byteLength !== expectedLength) {\n return { error: badRequestJson(contentLengthMismatchMessage(expectedLength, data.byteLength)) }\n }\n return { data }\n}\n\nfunction copyBytes(bytes: Uint8Array): Uint8Array {\n const copy = new Uint8Array(bytes.byteLength)\n copy.set(bytes)\n return copy\n}\n\nfunction concatenateChunks(chunks: readonly Uint8Array[], totalLength: number): Uint8Array {\n if (chunks.length === 0) return new Uint8Array(0)\n if (chunks.length === 1) return copyBytes(chunks[0] as Uint8Array)\n const data = new Uint8Array(totalLength)\n let offset = 0\n for (const chunk of chunks) {\n data.set(chunk, offset)\n offset += chunk.byteLength\n }\n return data\n}\n\nasync function readStreamingUploadBody(\n stream: ReadableStream<Uint8Array>,\n expectedLength: number | null,\n): Promise<UploadBodyReadResult> {\n const reader = stream.getReader()\n const chunks: Uint8Array[] = []\n let totalLength = 0\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n if (value === undefined) continue\n const chunk = value instanceof Uint8Array ? value : new Uint8Array(value)\n totalLength += chunk.byteLength\n if (expectedLength !== null && totalLength > expectedLength) {\n await reader.cancel().catch(() => undefined)\n return {\n error: badRequestJson(contentLengthMismatchMessage(expectedLength, totalLength)),\n }\n }\n chunks.push(chunk)\n }\n } finally {\n reader.releaseLock()\n }\n if (expectedLength !== null && totalLength !== expectedLength) {\n return { error: badRequestJson(contentLengthMismatchMessage(expectedLength, totalLength)) }\n }\n return { data: concatenateChunks(chunks, totalLength) }\n}\n\nasync function readUploadBody(\n body: BodyInit,\n expectedLength: number | null,\n): Promise<UploadBodyReadResult> {\n if (body instanceof ReadableStream) return readStreamingUploadBody(body, expectedLength)\n if (body instanceof ArrayBuffer) {\n return checkedUploadBody(new Uint8Array(body), expectedLength)\n }\n if (ArrayBuffer.isView(body)) {\n const view = body as ArrayBufferView\n return checkedUploadBody(\n copyBytes(new Uint8Array(view.buffer, view.byteOffset, view.byteLength)),\n expectedLength,\n )\n }\n if (typeof Blob !== 'undefined' && body instanceof Blob) {\n if (expectedLength !== null && body.size !== expectedLength) {\n return { error: badRequestJson(contentLengthMismatchMessage(expectedLength, body.size)) }\n }\n }\n return checkedUploadBody(new Uint8Array(await new Response(body).arrayBuffer()), expectedLength)\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 if (rawUrlPathContainsLiteralBackslash(url)) {\n return buildFaultResponse({\n on: '\\\\',\n status: 400,\n code: 'bad_request',\n message: 'URL path must not contain literal backslashes',\n })\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 parsedUrl = new URL(url)\n const endpoint = parsedUrl.pathname.split('/').pop() ?? ''\n const isUpload = endpoint === 'b2_upload_file' || endpoint === 'b2_upload_part'\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 = await this.sim.handleDownload(\n parsedUrl.pathname + parsedUrl.search,\n headers,\n method,\n )\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 let decodedText: string | null = null\n const text = () => {\n decodedText ??= utf8Decoder.decode(data)\n return decodedText\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: <T>() => {\n try {\n return Promise.resolve(JSON.parse(text()) as T)\n } catch {\n return Promise.reject(new Error('Download response is not JSON'))\n }\n },\n text: () => Promise.resolve(text()),\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) {\n const parsedContentLength = parseContentLengthHeader(headers)\n if (parsedContentLength.kind === 'error') {\n result = badRequestJson(parsedContentLength.message)\n } else {\n const uploadBody = await readUploadBody(\n request.body ?? new Uint8Array(0),\n parsedContentLength.kind === 'ok' ? parsedContentLength.expectedLength : null,\n )\n result =\n 'error' in uploadBody\n ? uploadBody.error\n : await this.sim.handleUpload(url, headers, uploadBody.data)\n }\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 } else if (\n request.method.toUpperCase() === 'GET' &&\n isPartnerQueryEndpoint(apiPathParts(parsedUrl.pathname).endpoint)\n ) {\n body = queryParamsBody(parsedUrl.searchParams)\n }\n result = await this.sim.handleRequest(\n request.method,\n parsedUrl.origin,\n parsedUrl.pathname,\n headers,\n body,\n )\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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"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 { encodeFileName } from '../raw/encoding.ts'\nimport { type B2ApiVersion, b2Url, isB2ApiVersion } from '../raw/url.ts'\nimport { sha1Hex } from '../streams/hash.ts'\nimport { Capability } from '../types/auth.ts'\nimport {\n type BucketDefaultRetention,\n type BucketDefaultServerSideEncryption,\n type BucketDefaultServerSideEncryptionSetting,\n type BucketInfo,\n BucketRetentionMode,\n type BucketRetentionPolicy,\n type BucketType,\n type BucketTypesFilter,\n type ReadableReplicationConfiguration,\n} from '../types/bucket.ts'\nimport { DownloadClientUnauthorizedToReadMarker, DownloadHeaderName } from '../types/download.ts'\nimport {\n EncryptionAlgorithm,\n EncryptionMode,\n type EncryptionSetting,\n type PublicEncryptionSetting,\n} from '../types/encryption.ts'\nimport {\n type ConcreteFileAction,\n FileAction,\n type FileVersion,\n type FileVersionListEntry,\n type FolderFileVersion,\n HIDE_MARKER_CONTENT_TYPE,\n type ListedFileVersion,\n type ReplicationStatus,\n} from '../types/file.ts'\nimport {\n type AuthToken,\n accountId as accountIdOf,\n type BucketId,\n bucketId as bucketIdOf,\n fileId as fileIdOf,\n} from '../types/ids.ts'\nimport {\n type FileRetentionValue,\n LegalHoldValue,\n type ReadableFileRetention,\n type ReadableLegalHold,\n RetentionMode,\n} from '../types/lock.ts'\nimport type { EventNotificationRule } from '../types/notifications.ts'\nimport type { PartnerCapability } from '../types/partner.ts'\nimport type { ReplicationConfiguration, ReplicationRule } from '../types/replication.ts'\nimport { hexEncode, hmacSha256 } from '../util/crypto.ts'\nimport { md5Base64, md5Base64Sync } from '../util/md5.ts'\nimport { utf8Decoder, utf8Encoder } from '../util/text-codec.ts'\nimport { toError } from '../util/to-error.ts'\nimport { PartnerSimulator } from './partner.ts'\nimport { PartnerEndpoint } from './partner-capabilities.ts'\n\nconst UPLOAD_TOKEN_SIGNING_KEY = ['b2 sdk typescript', 'simulator upload authorization', 'v1'].join(\n ':',\n)\nconst SIMULATOR_MASTER_APPLICATION_KEY_ID = 'master-key-id'\nconst SIMULATOR_MASTER_APPLICATION_KEY = 'master-key'\nconst SIMULATOR_TEST_APPLICATION_KEY_ID = 'test-key-id'\nconst SIMULATOR_TEST_APPLICATION_KEY = 'test-key'\nconst SIMULATOR_APPLICATION_KEY_ID_PREFIX = 'sim_key_'\n// Master capabilities granted to the simulator's implicit storage credentials.\n// Object Lock capabilities are intentionally omitted: real B2 does not\n// auto-grant them; tests that need them create an explicit key.\nconst SIMULATOR_MASTER_CAPABILITIES: readonly Capability[] = [\n Capability.ListBuckets,\n Capability.ListAllBucketNames,\n Capability.ReadBuckets,\n Capability.WriteBuckets,\n Capability.DeleteBuckets,\n Capability.ReadBucketEncryption,\n Capability.WriteBucketEncryption,\n Capability.ReadBucketReplications,\n Capability.WriteBucketReplications,\n Capability.ReadBucketNotifications,\n Capability.WriteBucketNotifications,\n Capability.ReadBucketLogging,\n Capability.WriteBucketLogging,\n Capability.ReadBucketLifecycleRules,\n Capability.WriteBucketLifecycleRules,\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]\n\ntype SimulatorJsonApiVersion = 'v3' | 'v4'\n\ninterface ApiPathParts {\n readonly endpoint: string\n readonly version: SimulatorJsonApiVersion\n readonly unsupportedVersion?: B2ApiVersion\n}\n\nfunction apiPathParts(path: string): ApiPathParts {\n const segments = path.split('/').filter((segment) => segment.length > 0)\n const candidate = segments.at(-2)\n const endpoint = segments.at(-1) ?? ''\n const isB2ApiPath = segments.at(-3) === 'b2api'\n if (candidate !== undefined && isB2ApiVersion(candidate)) {\n if (candidate === 'v3' || candidate === 'v4') return { endpoint, version: candidate }\n if (isB2ApiPath) return { endpoint, version: 'v3', unsupportedVersion: candidate }\n }\n return {\n endpoint,\n version: 'v3',\n }\n}\n\ninterface BasicCredentials {\n readonly applicationKeyId: string\n readonly applicationKey: string\n}\n\nfunction parseBasicAuthorizationHeader(authzHeader: string | undefined): BasicCredentials | null {\n if (!authzHeader?.startsWith('Basic ')) return null\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 <= 0 || idx === decoded.length - 1) return null\n return {\n applicationKeyId: decoded.slice(0, idx),\n applicationKey: decoded.slice(idx + 1),\n }\n}\n\nconst B2_MIN_PART_NUMBER = 1\nconst B2_MAX_PART_NUMBER = 10_000\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = ''\n for (const byte of bytes) {\n binary += String.fromCharCode(byte)\n }\n return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '')\n}\n\nfunction base64UrlDecode(encoded: string): Uint8Array | null {\n const base64 = encoded.replaceAll('-', '+').replaceAll('_', '/')\n const padding = (4 - (base64.length % 4)) % 4\n try {\n const binary = atob(base64.padEnd(base64.length + padding, '='))\n return Uint8Array.from(binary, (char) => char.charCodeAt(0))\n } catch {\n return null\n }\n}\n\nfunction timingSafeStringEqual(left: string, right: string): boolean {\n const max = Math.max(left.length, right.length)\n let mismatch = left.length === right.length ? 0 : 1\n for (let i = 0; i < max; i++) {\n mismatch |= (left.charCodeAt(i) || 0) ^ (right.charCodeAt(i) || 0)\n }\n return mismatch === 0\n}\n\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\nconst MAX_JS_DATE_MS = 8.64e15\n\nfunction isValidRetentionTimestamp(timestamp: number, now = Date.now()): boolean {\n return Number.isFinite(timestamp) && timestamp <= MAX_JS_DATE_MS && timestamp > now\n}\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}\n\nfunction decodeHeaderValue(value: string): string {\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n}\n\n/**\n * Compares B2 file names with deterministic JS string order, not locale collation.\n *\n * @param a - First file name.\n * @param b - Second file name.\n *\n * @returns `-1` when `a` sorts first, `1` when `b` sorts first, otherwise `0`.\n */\nfunction compareB2FileNames(a: string, b: string): number {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n}\n\nfunction compareFileVersionListEntries(a: FileVersionListEntry, b: FileVersionListEntry): number {\n const nameCmp = compareB2FileNames(a.fileName, b.fileName)\n if (nameCmp !== 0) return nameCmp\n return b.uploadTimestamp - a.uploadTimestamp\n}\n\nimport { missingCapabilitiesFor } from './capabilities.ts'\nimport {\n normalizeCreateKeyCapabilities,\n type ValidationError,\n validateBucketInfo,\n validateBucketName,\n validateBucketType,\n validateBucketTypes,\n validateCorsRules,\n validateCreateKeyCapabilities,\n validateCreateKeyName,\n validateDefaultRetention,\n validateDownloadAuthorizationDuration,\n validateDownloadAuthorizationPrefix,\n validateFileInfo,\n validateFileName,\n validateLifecycleRules,\n validateMaxCount,\n validateNotificationRules,\n validateReplicationConfiguration,\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_KEY_MAX_BYTES,\n BUCKET_INFO_KEY_MIN_BYTES,\n BUCKET_INFO_KEY_PATTERN,\n BUCKET_INFO_MAX_KEYS,\n BUCKET_INFO_RESERVED_PREFIX,\n BUCKET_INFO_VALUE_MAX,\n BUCKET_INFO_VALUES_MAX_BYTES,\n BUCKET_NAME_MAX,\n BUCKET_NAME_MIN,\n CORS_MAX_AGE_SECONDS_MAX,\n CORS_RULE_MAX_BYTES,\n CORS_RULE_NAME_MAX_LENGTH,\n CORS_RULE_NAME_MIN_LENGTH,\n CORS_RULE_NAME_PATTERN,\n CORS_RULE_NAME_RESERVED_PREFIX,\n CORS_RULES_MAX_COUNT,\n DOWNLOAD_AUTH_DURATION_MAX_SECONDS,\n DOWNLOAD_AUTH_DURATION_MIN_SECONDS,\n FILE_INFO_TOTAL_MAX,\n FILE_INFO_VALUE_MAX,\n FILE_NAME_MAX_BYTES,\n KEY_NAME_MAX,\n KEY_NAME_MIN,\n LIST_ENDPOINT_CAPS,\n} from './validation.ts'\n\nconst DOWNLOAD_AUTH_TOKEN_BYTES = 32\nconst DOWNLOAD_AUTH_TOKEN_PREFIX = 'sim_dl_auth_'\nconst DOWNLOAD_AUTH_PURGE_BATCH_SIZE = 128\n\nconst DOWNLOAD_RESPONSE_OVERRIDE_PARAMS = [\n 'b2ContentDisposition',\n 'b2ContentLanguage',\n 'b2ContentEncoding',\n 'b2ContentType',\n 'b2CacheControl',\n 'b2Expires',\n] as const\n\ntype DownloadResponseOverrideParam = (typeof DOWNLOAD_RESPONSE_OVERRIDE_PARAMS)[number]\ntype DownloadResponseOverrides = Partial<Record<DownloadResponseOverrideParam, string>>\n\nconst DOWNLOAD_RESPONSE_OVERRIDE_HEADERS: Record<DownloadResponseOverrideParam, string> = {\n b2ContentDisposition: DownloadHeaderName.ContentDisposition,\n b2ContentLanguage: DownloadHeaderName.ContentLanguage,\n b2ContentEncoding: DownloadHeaderName.ContentEncoding,\n b2ContentType: DownloadHeaderName.ContentType,\n b2CacheControl: DownloadHeaderName.CacheControl,\n b2Expires: DownloadHeaderName.Expires,\n}\n\ntype DownloadAuthorizationRequestBody = {\n bucketId: string\n fileNamePrefix: string\n validDurationInSeconds: number\n} & Partial<Record<DownloadResponseOverrideParam, unknown>>\n\nfunction randomDownloadAuthorizationToken(): string {\n const crypto = globalThis.crypto\n if (typeof crypto?.getRandomValues !== 'function') {\n throw new Error('Download authorization tokens require globalThis.crypto.getRandomValues')\n }\n const bytes = new Uint8Array(DOWNLOAD_AUTH_TOKEN_BYTES)\n crypto.getRandomValues(bytes)\n return `${DOWNLOAD_AUTH_TOKEN_PREFIX}${hexEncode(bytes)}`\n}\n\nfunction pickDownloadResponseOverrides(\n req: Partial<Record<DownloadResponseOverrideParam, unknown>>,\n): DownloadResponseOverrides {\n const overrides: DownloadResponseOverrides = {}\n for (const param of DOWNLOAD_RESPONSE_OVERRIDE_PARAMS) {\n const value = req[param]\n if (typeof value === 'string') overrides[param] = value\n }\n return overrides\n}\n\nfunction validateDownloadResponseOverrides(\n req: Partial<Record<DownloadResponseOverrideParam, unknown>>,\n): ValidationError | null {\n for (const param of DOWNLOAD_RESPONSE_OVERRIDE_PARAMS) {\n const value = req[param]\n if (value === undefined || typeof value === 'string') continue\n return { code: 'bad_request', message: `${param} must be a string` }\n }\n return null\n}\n\ninterface StoredFile {\n readonly fileVersion: FileVersion\n readonly data: Uint8Array\n readonly serverSideEncryption: StoredServerSideEncryption\n}\n\ntype StoredServerSideEncryption =\n | { readonly mode: 'none' }\n | { readonly mode: 'SSE-B2'; readonly algorithm: EncryptionAlgorithm }\n | {\n readonly mode: 'SSE-C'\n readonly algorithm: EncryptionAlgorithm\n readonly customerKeyDigest: string\n readonly customerKeyMd5: string\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 fileRetention: FileRetentionValue | null\n readonly legalHold: LegalHoldValue | null\n readonly replicationStatus?: ReplicationStatus\n readonly serverSideEncryption: StoredServerSideEncryption\n readonly uploadTimestamp: number\n readonly parts: Map<number, StoredLargeFilePart>\n}\n\ninterface StoredLargeFilePart {\n readonly data: Uint8Array\n readonly sha1: string\n readonly contentMd5: string | null\n readonly uploadTimestamp: number\n}\n\ninterface StoredKey {\n readonly applicationKeyId: string\n readonly keyName: string\n readonly capabilities: readonly Capability[]\n readonly accountId: string\n readonly applicationKey: string\n readonly bucketIds: readonly string[] | null\n readonly namePrefix: string | null\n readonly expirationTimestamp: number | null\n}\n\ninterface AuthorizationGrant {\n readonly accountId: string\n readonly capabilities: readonly Capability[]\n readonly bucketIds: readonly string[] | null\n readonly namePrefix: string | null\n readonly applicationKeyId: string | null\n readonly expirationTimestamp: number | null\n}\n\ntype AuthorizationGrantResolution =\n | { readonly kind: 'ok'; readonly grant: AuthorizationGrant }\n | { readonly kind: 'invalid' }\n | { readonly kind: 'expired' }\n\ninterface IssuedToken {\n readonly accountId: string\n readonly capabilities: readonly Capability[]\n readonly bucketIds: readonly string[] | null\n readonly namePrefix: string | null\n readonly 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 B2Simulator.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 readonly applicationKeyId: string | null\n}\n\ntype UploadTokenKind = 'file' | 'part'\n\ninterface UploadTokenPayload {\n readonly v: 1\n readonly kind: UploadTokenKind\n readonly fileName: string | null\n readonly uploadUrl: string\n readonly namePrefix: string | null\n readonly applicationKeyId: string | null\n readonly expiresAt: number\n}\n\ninterface StoredUploadToken {\n readonly kind: UploadTokenKind\n readonly fileName: string | null\n readonly uploadUrl: string\n readonly namePrefix: string | null\n readonly applicationKeyId: string | null\n expiresAt: number\n readonly cleanupAt: number\n invalidated: boolean\n}\n\ninterface DownloadAuthorizationToken {\n readonly bucketId: string\n readonly fileNamePrefix: string\n readonly expiresAt: number\n readonly responseHeaderOverrides: DownloadResponseOverrides\n}\n\ninterface DownloadAuthorizationExpiry {\n readonly token: string\n readonly expiresAt: number\n}\n\ninterface RequestScope {\n readonly bucketIds: readonly string[]\n readonly fileNames?: readonly string[]\n readonly requiresBucketScope: boolean\n readonly requiresAccountLevelBucketAccess?: boolean\n}\n\nfunction normalizeKeyBucketIds(req: {\n bucketIds?: readonly string[] | null\n}): readonly string[] | null {\n return req.bucketIds === undefined || req.bucketIds === null\n ? null\n : Object.freeze([...req.bucketIds])\n}\n\nfunction singleBucketId(bucketIds: readonly string[] | null | undefined): string | null {\n return bucketIds?.length === 1 ? (bucketIds[0] ?? null) : null\n}\n\nfunction cloneBucketIds(bucketIds: readonly string[] | null): readonly string[] | null {\n return bucketIds === null ? null : [...bucketIds]\n}\n\nfunction cloneCapabilities(capabilities: readonly Capability[]): readonly Capability[] {\n return [...capabilities]\n}\n\nfunction isImplicitStorageMasterCredential(credentials: BasicCredentials): boolean {\n return (\n (credentials.applicationKeyId === SIMULATOR_MASTER_APPLICATION_KEY_ID &&\n credentials.applicationKey === SIMULATOR_MASTER_APPLICATION_KEY) ||\n (credentials.applicationKeyId === SIMULATOR_TEST_APPLICATION_KEY_ID &&\n credentials.applicationKey === SIMULATOR_TEST_APPLICATION_KEY)\n )\n}\n\nfunction isImplicitPartnerMasterCredential(credentials: BasicCredentials): boolean {\n return (\n credentials.applicationKeyId === SIMULATOR_MASTER_APPLICATION_KEY_ID &&\n credentials.applicationKey === SIMULATOR_MASTER_APPLICATION_KEY\n )\n}\n\nfunction isSimulatorApplicationKeyId(applicationKeyId: string): boolean {\n return applicationKeyId.startsWith(SIMULATOR_APPLICATION_KEY_ID_PREFIX)\n}\n\nfunction hasOwnField(body: unknown, field: string): boolean {\n return typeof body === 'object' && body !== null && Object.hasOwn(body, field)\n}\n\nfunction requestStringField(body: unknown, field: string): string | undefined {\n if (typeof body !== 'object' || body === null) return undefined\n const value = (body as Record<string, unknown>)[field]\n return typeof value === 'string' ? value : undefined\n}\n\nfunction requestRecord(body: unknown): Record<string, unknown> | null {\n return typeof body === 'object' && body !== null && !Array.isArray(body)\n ? (body as Record<string, unknown>)\n : null\n}\n\nfunction replicationDestinationBucketIds(body: unknown): readonly string[] {\n const req = requestRecord(body)\n const replicationConfiguration = requestRecord(req?.['replicationConfiguration'])\n const source = requestRecord(replicationConfiguration?.['asReplicationSource'])\n const rules = source?.['replicationRules']\n if (!Array.isArray(rules)) return []\n\n const bucketIds: string[] = []\n for (const rule of rules) {\n const destinationBucketId = requestRecord(rule)?.['destinationBucketId']\n if (typeof destinationBucketId === 'string') bucketIds.push(destinationBucketId)\n }\n return [...new Set(bucketIds)]\n}\n\nfunction normalizeReplicationConfiguration(\n config: ReplicationConfiguration,\n): ReplicationConfiguration {\n return {\n asReplicationSource: config.asReplicationSource ?? null,\n asReplicationDestination: config.asReplicationDestination ?? null,\n }\n}\n\nfunction wrapReplicationConfiguration(\n config: ReplicationConfiguration | undefined,\n): ReadableReplicationConfiguration {\n if (config === undefined) {\n return { isClientAuthorizedToRead: true, value: null }\n }\n\n const normalized = normalizeReplicationConfiguration(config)\n const value =\n normalized.asReplicationSource === null && normalized.asReplicationDestination === null\n ? null\n : normalized\n return { isClientAuthorizedToRead: true, value }\n}\n\nfunction filterReadableReplicationConfiguration(\n config: ReadableReplicationConfiguration,\n canRead: boolean,\n): ReadableReplicationConfiguration {\n return canRead\n ? { isClientAuthorizedToRead: true, value: config.value }\n : { isClientAuthorizedToRead: false, value: null }\n}\n\nfunction readableFileRetention(\n value: FileRetentionValue | null,\n canRead: boolean,\n): ReadableFileRetention {\n return canRead\n ? { isClientAuthorizedToRead: true, value }\n : { isClientAuthorizedToRead: false, value: null }\n}\n\nfunction readableLegalHold(value: LegalHoldValue | null, canRead: boolean): ReadableLegalHold {\n return canRead\n ? { isClientAuthorizedToRead: true, value }\n : { isClientAuthorizedToRead: false, value: null }\n}\n\ninterface BucketConfigurationFields {\n readonly bucketInfo?: unknown\n readonly corsRules?: unknown\n readonly defaultServerSideEncryption?: unknown\n readonly defaultRetention?: unknown\n readonly lifecycleRules?: unknown\n readonly replicationConfiguration?: unknown\n}\n\nfunction validateBucketConfigurationFields(\n fields: BucketConfigurationFields,\n options: { readonly objectLockEnabled: boolean },\n): ValidationError | null {\n if (fields.bucketInfo !== undefined) {\n const infoError = validateBucketInfo(fields.bucketInfo as Record<string, string>)\n if (infoError) return infoError\n }\n if (fields.corsRules !== undefined) {\n const corsError = validateCorsRules(fields.corsRules)\n if (corsError) return corsError\n }\n if (fields.lifecycleRules !== undefined) {\n const lifecycleError = validateLifecycleRules(fields.lifecycleRules)\n if (lifecycleError) return lifecycleError\n }\n if (fields.defaultServerSideEncryption !== undefined) {\n const encryptionError = validateBucketDefaultServerSideEncryption(\n fields.defaultServerSideEncryption,\n )\n if (encryptionError) return encryptionError\n }\n if (fields.replicationConfiguration !== undefined) {\n const replicationError = validateReplicationConfiguration(fields.replicationConfiguration)\n if (replicationError) return replicationError\n }\n if (fields.defaultRetention !== undefined) {\n const retentionError = validateDefaultRetention(fields.defaultRetention)\n if (retentionError) return retentionError\n if (\n requestRecord(fields.defaultRetention)?.['mode'] !== BucketRetentionMode.None &&\n !options.objectLockEnabled\n ) {\n return {\n code: 'file_lock_not_enabled',\n message: 'Bucket must have Object Lock enabled to set defaultRetention',\n }\n }\n }\n return null\n}\n\nfunction validateBucketDefaultServerSideEncryption(encryption: unknown): ValidationError | null {\n const runtimeEncryption = requestRecord(encryption)\n if (runtimeEncryption === null) {\n return { code: 'bad_request', message: 'defaultServerSideEncryption must be an object' }\n }\n if (hasCustomerEncryptionFields(runtimeEncryption)) {\n return {\n code: 'bad_request',\n message: 'Bucket default server-side encryption cannot use SSE-C',\n }\n }\n const mode = runtimeEncryption['mode']\n if (mode === EncryptionMode.None) return null\n if (mode === EncryptionMode.SseB2) {\n if (runtimeEncryption['algorithm'] !== EncryptionAlgorithm.Aes256) {\n return { code: 'bad_request', message: 'SSE-B2 algorithm must be AES256' }\n }\n return null\n }\n if (mode === EncryptionMode.SseC) {\n return {\n code: 'bad_request',\n message: 'Bucket default server-side encryption cannot use SSE-C',\n }\n }\n return {\n code: 'bad_request',\n message: `Unsupported bucket default server-side encryption mode: ${String(mode)}`,\n }\n}\n\nfunction nextObjectLockEnabled(current: boolean, requested: unknown): boolean | ValidationError {\n if (requested === undefined) return current\n if (typeof requested !== 'boolean') {\n return { code: 'bad_request', message: 'fileLockEnabled must be a boolean' }\n }\n if (current && requested === false) {\n return { code: 'file_lock_conflict', message: 'Object Lock cannot be disabled' }\n }\n return current || requested\n}\n\nfunction requestHeaderValue(headers: Record<string, string>, name: string): string | undefined {\n const exact = headers[name]\n if (exact !== undefined) return exact\n const lowerName = name.toLowerCase()\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) return value\n }\n return undefined\n}\n\ntype ContentLengthHeader =\n | { readonly kind: 'absent' }\n | { readonly kind: 'ok'; readonly expectedLength: number }\n | { readonly kind: 'error'; readonly message: string }\n\nfunction parseContentLengthHeader(headers: Record<string, string>): ContentLengthHeader {\n const header = requestHeaderValue(headers, 'content-length')\n if (header === undefined) return { kind: 'absent' }\n if (!/^\\d+$/.test(header)) {\n return { kind: 'error', message: `Content-Length must be a byte count: ${header}` }\n }\n const expectedLength = Number(header)\n if (!Number.isSafeInteger(expectedLength)) {\n return { kind: 'error', message: `Content-Length is too large: ${header}` }\n }\n return { kind: 'ok', expectedLength }\n}\n\nfunction contentLengthMismatchMessage(expectedLength: number, actualLength: number): string {\n return `Content-Length ${expectedLength} does not match request body length ${actualLength}`\n}\n\nfunction fileNames(...names: readonly (string | undefined)[]): readonly string[] | undefined {\n const present = names.filter((name): name is string => name !== undefined)\n return present.length > 0 ? present : undefined\n}\n\nfunction queryParamsBody(\n params: URLSearchParams,\n endpoint: string,\n): Record<string, string | number> {\n const metadata = jsonEndpointMetadata(endpoint)\n const queryFields = new Set(metadata?.queryFields ?? [])\n const numericFields = new Set(metadata?.numericQueryFields ?? [])\n const body = Object.create(null) as Record<string, string | number>\n for (const [key, value] of params) {\n if (!queryFields.has(key)) continue\n if (body[key] !== undefined) continue\n body[key] = queryParamValue(value, numericFields.has(key))\n }\n return body\n}\n\nfunction queryParamValue(value: string, numeric: boolean): string | number {\n if (!numeric || !/^-?\\d+$/.test(value)) return value\n const numericValue = Number(value)\n return Number.isSafeInteger(numericValue) ? numericValue : value\n}\n\ntype JsonEndpointMethod = 'GET' | 'POST'\n\ninterface JsonEndpointMetadata {\n readonly methods: readonly JsonEndpointMethod[]\n readonly queryFields?: readonly string[]\n readonly numericQueryFields?: readonly string[]\n}\n\nconst JSON_ENDPOINTS = {\n b2_authorize_account: { methods: ['GET'] },\n [PartnerEndpoint.ListGroups]: {\n methods: ['GET'],\n queryFields: ['adminAccountId', 'groupName', 'startGroupId', 'maxGroupCount'],\n numericQueryFields: ['maxGroupCount'],\n },\n [PartnerEndpoint.ListGroupMembers]: {\n methods: ['GET'],\n queryFields: ['adminAccountId', 'groupId', 'startEmail', 'maxMemberCount'],\n numericQueryFields: ['maxMemberCount'],\n },\n [PartnerEndpoint.ListComputers]: {\n methods: ['GET'],\n queryFields: ['accountId', 'startComputerId', 'maxComputerCount'],\n numericQueryFields: ['maxComputerCount'],\n },\n b2_get_upload_url: {\n methods: ['GET', 'POST'],\n queryFields: ['bucketId'],\n },\n b2_list_file_names: {\n methods: ['GET', 'POST'],\n queryFields: ['bucketId', 'delimiter', 'maxFileCount', 'prefix', 'startFileName'],\n numericQueryFields: ['maxFileCount'],\n },\n b2_list_file_versions: {\n methods: ['GET', 'POST'],\n queryFields: [\n 'bucketId',\n 'delimiter',\n 'maxFileCount',\n 'prefix',\n 'startFileName',\n 'startFileId',\n ],\n numericQueryFields: ['maxFileCount'],\n },\n b2_get_file_info: {\n methods: ['GET', 'POST'],\n queryFields: ['fileId'],\n },\n b2_copy_file: {\n methods: ['GET', 'POST'],\n queryFields: [\n 'sourceFileId',\n 'fileName',\n 'destinationBucketId',\n 'range',\n 'metadataDirective',\n 'contentType',\n ],\n },\n b2_get_upload_part_url: {\n methods: ['GET', 'POST'],\n queryFields: ['fileId'],\n },\n b2_list_unfinished_large_files: {\n methods: ['GET', 'POST'],\n queryFields: ['bucketId', 'namePrefix', 'startFileId', 'maxFileCount'],\n numericQueryFields: ['maxFileCount'],\n },\n b2_list_parts: {\n methods: ['GET', 'POST'],\n queryFields: ['fileId', 'startPartNumber', 'maxPartCount'],\n numericQueryFields: ['startPartNumber', 'maxPartCount'],\n },\n b2_copy_part: {\n methods: ['GET', 'POST'],\n queryFields: ['sourceFileId', 'largeFileId', 'partNumber', 'range'],\n numericQueryFields: ['partNumber'],\n },\n b2_get_download_authorization: {\n methods: ['GET', 'POST'],\n queryFields: [\n 'bucketId',\n 'fileNamePrefix',\n 'validDurationInSeconds',\n 'b2ContentDisposition',\n 'b2ContentLanguage',\n 'b2ContentEncoding',\n 'b2ContentType',\n 'b2CacheControl',\n 'b2Expires',\n ],\n numericQueryFields: ['validDurationInSeconds'],\n },\n b2_list_keys: {\n methods: ['GET', 'POST'],\n queryFields: ['accountId', 'maxKeyCount', 'startApplicationKeyId'],\n numericQueryFields: ['maxKeyCount'],\n },\n b2_get_bucket_notification_rules: {\n methods: ['GET', 'POST'],\n queryFields: ['bucketId'],\n },\n [PartnerEndpoint.CreateGroupMember]: { methods: ['POST'] },\n [PartnerEndpoint.EjectGroupMember]: { methods: ['POST'] },\n [PartnerEndpoint.ReserveTrialCreateAccount]: { methods: ['POST'] },\n [PartnerEndpoint.DeleteComputer]: { methods: ['POST'] },\n b2_create_bucket: { methods: ['POST'] },\n b2_list_buckets: { methods: ['POST'] },\n b2_delete_bucket: { methods: ['POST'] },\n b2_update_bucket: { methods: ['POST'] },\n b2_hide_file: { methods: ['POST'] },\n b2_delete_file_version: { methods: ['POST'] },\n b2_start_large_file: { methods: ['POST'] },\n b2_finish_large_file: { methods: ['POST'] },\n b2_cancel_large_file: { methods: ['POST'] },\n b2_create_key: { methods: ['POST'] },\n b2_delete_key: { methods: ['POST'] },\n b2_update_file_retention: { methods: ['POST'] },\n b2_update_file_legal_hold: { methods: ['POST'] },\n b2_set_bucket_notification_rules: { methods: ['POST'] },\n} as const satisfies Record<string, JsonEndpointMetadata>\n\nfunction jsonEndpointMetadata(endpoint: string): JsonEndpointMetadata | undefined {\n return JSON_ENDPOINTS[endpoint as keyof typeof JSON_ENDPOINTS]\n}\n\nfunction jsonEndpointAllowsMethod(method: string, endpoint: string): boolean {\n const metadata = jsonEndpointMetadata(endpoint)\n if (metadata === undefined) return true\n const normalizedMethod = method.toUpperCase()\n if (normalizedMethod !== 'GET' && normalizedMethod !== 'POST') return false\n return metadata.methods.includes(normalizedMethod)\n}\n\nfunction isJsonQueryEndpoint(endpoint: string): boolean {\n return jsonEndpointMetadata(endpoint)?.queryFields !== undefined\n}\n\nfunction notificationRulePrefixes(body: unknown): readonly string[] | undefined {\n if (typeof body !== 'object' || body === null) return undefined\n const rules = (body as Record<string, unknown>)['eventNotificationRules']\n if (!Array.isArray(rules)) return undefined\n return rules.map((rule) => {\n if (typeof rule !== 'object' || rule === null) return ''\n const objectNamePrefix = (rule as Record<string, unknown>)['objectNamePrefix']\n return typeof objectNamePrefix === 'string' ? objectNamePrefix : ''\n })\n}\n\nfunction storedNotificationRulePrefixes(\n rules: readonly EventNotificationRule[] | undefined,\n): readonly string[] {\n return (rules ?? []).map((rule) =>\n typeof rule.objectNamePrefix === 'string' ? rule.objectNamePrefix : '',\n )\n}\n\nfunction hasKeyManagementCapability(capabilities: readonly Capability[]): boolean {\n return capabilities.some(\n (capability) =>\n capability === Capability.ListKeys ||\n capability === Capability.WriteKeys ||\n capability === Capability.DeleteKeys,\n )\n}\n\nfunction missingStoredKeyCapabilities(\n key: StoredKey,\n required: readonly Capability[],\n): readonly Capability[] {\n const capabilities = new Set(key.capabilities)\n return required.filter((capability) => !capabilities.has(capability))\n}\n\nfunction storedKeyAllowsBucket(key: StoredKey, bucketId: string): boolean {\n return key.bucketIds === null || key.bucketIds.includes(bucketId)\n}\n\nfunction publicServerSideEncryption(\n encryption: EncryptionSetting | StoredServerSideEncryption,\n): PublicEncryptionSetting {\n if (encryption.mode === EncryptionMode.SseC) {\n return { mode: encryption.mode, algorithm: encryption.algorithm }\n }\n if (encryption.mode === EncryptionMode.None) {\n return { mode: null, algorithm: null }\n }\n return encryption\n}\n\nfunction publicBucketDefaultServerSideEncryption(\n encryption: BucketDefaultServerSideEncryptionSetting | undefined,\n): BucketInfo['defaultServerSideEncryption'] {\n return {\n isClientAuthorizedToRead: true,\n value: publicBucketDefaultServerSideEncryptionValue(encryption),\n }\n}\n\nfunction publicBucketDefaultServerSideEncryptionValue(\n encryption: BucketDefaultServerSideEncryptionSetting | undefined,\n): BucketDefaultServerSideEncryption {\n if (encryption?.mode === EncryptionMode.SseB2) return encryption\n return { mode: null, algorithm: null }\n}\n\nfunction bucketDefaultServerSideEncryptionForStorage(\n info: BucketInfo,\n): BucketDefaultServerSideEncryption {\n const encryption = info.defaultServerSideEncryption\n if (!encryption.isClientAuthorizedToRead) return { mode: null, algorithm: null }\n return encryption.value ?? { mode: null, algorithm: null }\n}\n\nfunction customerKeyDigest(customerKey: Uint8Array): string {\n let first = 0x811c9dc5\n let second = 0x27d4eb2d\n for (const byte of customerKey) {\n first = Math.imul(first ^ byte, 0x01000193) >>> 0\n second = Math.imul(second ^ byte, 0x85ebca6b) >>> 0\n }\n return `${first.toString(16).padStart(8, '0')}${second.toString(16).padStart(8, '0')}`\n}\n\nconst BASE64_ALPHABET = /^[A-Za-z0-9+/]+$/\n\nfunction normalizeSizedBase64(value: string, decodedByteLength: number): string | null {\n const paddedLength = Math.ceil(decodedByteLength / 3) * 4\n const noPaddingLength = Math.ceil((decodedByteLength * 8) / 6)\n const padding = '='.repeat(paddedLength - noPaddingLength)\n\n if (value.length === noPaddingLength && BASE64_ALPHABET.test(value)) {\n return value.padEnd(paddedLength, '=')\n }\n\n if (\n value.length === paddedLength &&\n value.endsWith(padding) &&\n BASE64_ALPHABET.test(value.slice(0, noPaddingLength))\n ) {\n return value\n }\n\n return null\n}\n\nfunction base64ToBytes(value: string, decodedByteLength: number): Uint8Array | null {\n const normalized = normalizeSizedBase64(value, decodedByteLength)\n if (normalized === null) return null\n try {\n const decoded = atob(normalized)\n if (decoded.length !== decodedByteLength) return null\n const bytes = new Uint8Array(decoded.length)\n for (let i = 0; i < decoded.length; i++) bytes[i] = decoded.charCodeAt(i)\n return bytes\n } catch {\n return null\n }\n}\n\nfunction encryptionValidationError(message: string): SimulatorJsonResponse {\n return { status: 400, body: { status: 400, code: 'bad_request', message } }\n}\n\nfunction sameStoredServerSideEncryption(\n a: StoredServerSideEncryption,\n b: StoredServerSideEncryption,\n): boolean {\n if (a.mode !== b.mode) return false\n if (a.mode === EncryptionMode.None) return true\n if (a.mode === EncryptionMode.SseB2 && b.mode === EncryptionMode.SseB2) {\n return a.algorithm === b.algorithm\n }\n if (a.mode === EncryptionMode.SseC && b.mode === EncryptionMode.SseC) {\n return (\n a.algorithm === b.algorithm &&\n a.customerKeyDigest === b.customerKeyDigest &&\n a.customerKeyMd5 === b.customerKeyMd5\n )\n }\n return false\n}\n\nfunction hasCustomerEncryptionFields(encryption: Record<string, unknown>): boolean {\n return (\n encryption['customerKey'] !== undefined ||\n encryption['customerKeyMd5'] !== undefined ||\n encryption['customerAlgorithm'] !== undefined\n )\n}\n\nfunction hasCustomerEncryptionHeaders(headers: Record<string, string>): boolean {\n return (\n headers['x-bz-server-side-encryption-customer-algorithm'] !== undefined ||\n headers['x-bz-server-side-encryption-customer-key'] !== undefined ||\n headers['x-bz-server-side-encryption-customer-key-md5'] !== undefined\n )\n}\n\nfunction isExactNoEncryptionWireSetting(encryption: Record<string, unknown>): boolean {\n return (\n encryption['mode'] === null &&\n encryption['algorithm'] === null &&\n Object.keys(encryption).length === 2\n )\n}\n\nasync function storedServerSideEncryption(\n encryption: EncryptionSetting | BucketDefaultServerSideEncryption,\n): Promise<StoredServerSideEncryption | SimulatorJsonResponse> {\n const runtimeEncryption = encryption as unknown as Record<string, unknown>\n if (encryption.mode === null) {\n if (isExactNoEncryptionWireSetting(runtimeEncryption)) return { mode: EncryptionMode.None }\n if (hasCustomerEncryptionFields(runtimeEncryption)) {\n return encryptionValidationError(\n 'No-encryption settings must not include SSE-C customer keys',\n )\n }\n return encryptionValidationError(\n 'No-encryption wire settings must be exactly { mode: null, algorithm: null }',\n )\n }\n if (encryption.mode === EncryptionMode.SseC) {\n if (runtimeEncryption['algorithm'] === undefined) {\n return encryptionValidationError('SSE-C customer algorithm is required')\n }\n if (encryption.algorithm !== EncryptionAlgorithm.Aes256) {\n return encryptionValidationError('SSE-C customer algorithm must be AES256')\n }\n const customerKey = 'customerKey' in encryption ? encryption.customerKey : undefined\n const customerKeyMd5 = 'customerKeyMd5' in encryption ? encryption.customerKeyMd5 : undefined\n if (typeof customerKey !== 'string' || customerKey === '') {\n return encryptionValidationError('SSE-C customer key is required')\n }\n if (typeof customerKeyMd5 !== 'string' || customerKeyMd5 === '') {\n return encryptionValidationError('SSE-C customer key MD5 is required')\n }\n const customerKeyBytes = base64ToBytes(customerKey, 32)\n if (customerKeyBytes === null) {\n return encryptionValidationError('SSE-C customer key must be a base64-encoded 256-bit key')\n }\n const normalizedCustomerKeyMd5 = normalizeSizedBase64(customerKeyMd5, 16)\n if (normalizedCustomerKeyMd5 === null) {\n return encryptionValidationError(\n 'SSE-C customer key MD5 must be a base64-encoded 128-bit digest',\n )\n }\n const actualMd5 = await md5Base64(customerKeyBytes)\n if (actualMd5 !== normalizedCustomerKeyMd5) {\n return encryptionValidationError('SSE-C customer key MD5 does not match the key')\n }\n return {\n mode: encryption.mode,\n algorithm: encryption.algorithm,\n customerKeyDigest: customerKeyDigest(customerKeyBytes),\n customerKeyMd5: normalizedCustomerKeyMd5,\n }\n }\n if (encryption.mode === EncryptionMode.SseB2) {\n if (hasCustomerEncryptionFields(runtimeEncryption)) {\n return encryptionValidationError('SSE-B2 settings must not include SSE-C customer keys')\n }\n if (encryption.algorithm !== EncryptionAlgorithm.Aes256) {\n return encryptionValidationError('SSE-B2 algorithm must be AES256')\n }\n return { mode: encryption.mode, algorithm: encryption.algorithm }\n }\n if (encryption.mode === EncryptionMode.None) {\n if (hasCustomerEncryptionFields(runtimeEncryption)) {\n return encryptionValidationError(\n 'No-encryption settings must not include SSE-C customer keys',\n )\n }\n return { mode: EncryptionMode.None }\n }\n\n return encryptionValidationError(\n `Unsupported server-side encryption mode: ${String(runtimeEncryption['mode'])}`,\n )\n}\n\nasync function uploadServerSideEncryption(\n headers: Record<string, string>,\n fallback: EncryptionSetting | BucketDefaultServerSideEncryption,\n): Promise<StoredServerSideEncryption | SimulatorJsonResponse> {\n const customerAlgorithm = headers['x-bz-server-side-encryption-customer-algorithm']\n const customerKey = headers['x-bz-server-side-encryption-customer-key']\n const customerKeyMd5 = headers['x-bz-server-side-encryption-customer-key-md5']\n const managedEncryption = headers['x-bz-server-side-encryption']\n if (hasCustomerEncryptionHeaders(headers)) {\n if (managedEncryption !== undefined) {\n return encryptionValidationError('SSE-B2 settings must not include SSE-C customer keys')\n }\n if (customerAlgorithm === undefined) {\n return encryptionValidationError('SSE-C customer algorithm is required')\n }\n if (customerAlgorithm !== EncryptionAlgorithm.Aes256) {\n return encryptionValidationError('SSE-C customer algorithm must be AES256')\n }\n if (!customerKey || !customerKeyMd5) {\n return encryptionValidationError('SSE-C customer key and MD5 are required')\n }\n return await storedServerSideEncryption({\n mode: EncryptionMode.SseC,\n algorithm: EncryptionAlgorithm.Aes256,\n customerKey,\n customerKeyMd5,\n })\n }\n\n if (managedEncryption !== undefined) {\n if (managedEncryption !== EncryptionAlgorithm.Aes256) {\n return encryptionValidationError('SSE-B2 algorithm must be AES256')\n }\n return { mode: EncryptionMode.SseB2, algorithm: EncryptionAlgorithm.Aes256 }\n }\n\n return await storedServerSideEncryption(fallback)\n}\n\nfunction noBucketDefaultRetention(): BucketDefaultRetention {\n return { mode: null, period: null }\n}\n\nfunction bucketDefaultRetentionForResponse(\n policy: BucketRetentionPolicy | undefined,\n): BucketDefaultRetention {\n if (policy === undefined || policy.mode === BucketRetentionMode.None) {\n return noBucketDefaultRetention()\n }\n return policy\n}\n\nfunction bucketDefaultRetention(info: BucketInfo): BucketDefaultRetention | undefined {\n return info.fileLockConfiguration.value?.defaultRetention\n}\n\nfunction defaultFileRetention(\n policy: BucketDefaultRetention | undefined,\n uploadTimestamp: number,\n): FileRetentionValue | null {\n if (policy === undefined) return null\n if (policy.mode === null) return null\n if (policy.mode === BucketRetentionMode.None || policy.period === null) return null\n const days = policy.period.unit === 'days' ? policy.period.duration : policy.period.duration * 365\n const retainUntilTimestamp = uploadTimestamp + days * MS_PER_DAY\n if (!isValidRetentionTimestamp(retainUntilTimestamp, uploadTimestamp)) return null\n return {\n mode: policy.mode as RetentionMode,\n retainUntilTimestamp,\n }\n}\n\nfunction isRetentionMode(value: unknown): value is RetentionMode {\n return value === RetentionMode.Compliance || value === RetentionMode.Governance\n}\n\nfunction parseFileRetentionValue(value: unknown, now: number): FileRetentionValue | null {\n if (typeof value !== 'object' || value === null) return null\n const candidate = value as Record<string, unknown>\n const mode = candidate['mode']\n const retainUntilTimestamp = candidate['retainUntilTimestamp']\n\n if (mode === null && retainUntilTimestamp === null) {\n return { mode, retainUntilTimestamp }\n }\n if (isRetentionMode(mode) && typeof retainUntilTimestamp === 'number') {\n if (!isValidRetentionTimestamp(retainUntilTimestamp, now)) return null\n return { mode, retainUntilTimestamp }\n }\n return null\n}\n\nfunction isRetentionWeakened(\n current: FileRetentionValue | null | undefined,\n next: FileRetentionValue,\n): boolean {\n if (current === null || current === undefined) return false\n if (current.mode === null) return false\n if (next.mode === null) return true\n if (current.mode === RetentionMode.Compliance && next.mode !== RetentionMode.Compliance) {\n return true\n }\n if (current.retainUntilTimestamp === null) return next.mode !== current.mode\n if (next.retainUntilTimestamp === null) return true\n return next.retainUntilTimestamp < current.retainUntilTimestamp\n}\n\nfunction requiresGovernanceBypass(\n current: FileRetentionValue | null | undefined,\n next: FileRetentionValue,\n): boolean {\n if (current?.mode !== RetentionMode.Governance) return false\n if (next.mode === RetentionMode.Compliance) return true\n return isRetentionWeakened(current, next)\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: { kind: 'webhook' | 'replication'; error: Error }) => 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 * for account/application-key authorization (matching its\n * long-standing behaviour): any well-formed Basic credential whose\n * applicationKeyId is not a simulator-created key receives the\n * implicit master grant, so tests do not have to set up keys with\n * the right capabilities. Created keys always authorize with their\n * stored capabilities, bucket scope, name prefix, and expiration;\n * a wrong or expired created-key secret is rejected.\n *\n * Upload authorization tokens returned by `b2_get_upload_url` and\n * `b2_get_upload_part_url` are always enforced, regardless of this\n * option. Upload handlers reject missing, unknown, expired, or\n * wrong-URL upload tokens in both permissive and strict modes.\n *\n * In strict mode:\n *\n * - `b2_authorize_account` still accepts the documented implicit\n * master credentials `test-key-id:test-key` and\n * `master-key-id:master-key`.\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 bucketIds / 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. The simulator also uses this TTL for upload\n * authorization tokens issued via `b2_get_upload_url` and\n * `b2_get_upload_part_url`. Defaults to 24 hours (real B2). Tests\n * that want to exercise the 401/reauth retry path or stale upload\n * URL handling can lower this and use {@link B2Simulator.advanceTime}\n * to move simulator time past account-token expiry. Upload tokens are\n * rejected at the exact expiry boundary.\n */\n authTokenTtlMs?: number\n /**\n * When `true`, v3 `b2_authorize_account` responses include Partner and\n * Computer Backup suites and issue Partner authorization tokens. Partner\n * and Backup endpoint calls then require one of those issued tokens, even\n * when `strictAuth` is otherwise disabled, so SDK auth-error paths can test\n * documented 401 responses.\n *\n * Defaults to `false`: direct Partner/Backup endpoint tests remain\n * permissive and accept any non-empty, whitespace-free Partner token without\n * requiring an authorize call first.\n */\n partnerAuthorize?: boolean\n /**\n * Partner API suite capabilities granted by simulator-issued Partner\n * authorization tokens. Defaults to `['all']`.\n */\n partnerGroupsCapabilities?: readonly PartnerCapability[]\n /**\n * Computer Backup API suite capabilities granted by simulator-issued\n * Partner authorization tokens. Defaults to `['all']`.\n */\n partnerBackupCapabilities?: readonly PartnerCapability[]\n /**\n * Whether Partner API endpoints should accept issued Partner authorization\n * tokens. Defaults to `true`. Set to `false` to exercise documented\n * `403 access_denied` prerequisite failures.\n */\n partnerApiEnabled?: boolean\n /**\n * Whether the simulated partner administrator has a valid phone number.\n * Defaults to `true`; `false` produces Partner prerequisite failures\n * such as `403 access_denied` or `401 invalid_sms_phone` depending on\n * the endpoint's documented error shape.\n */\n partnerAccountHasValidPhone?: boolean\n /**\n * Whether the simulated partner administrator account is in good standing.\n * Defaults to `true`; `false` produces `403 access_denied` on Partner calls.\n */\n partnerAccountInGoodStanding?: boolean\n /**\n * Whether the simulated account may set custom upload timestamps on\n * `b2_upload_file` and `b2_start_large_file`. Defaults to `false`, matching\n * production accounts without the restricted feature enabled.\n */\n customUploadTimestampsEnabled?: boolean\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 *\n * `test-key-id:test-key` is the documented implicit full-access\n * storage credential. `master-key-id:master-key` is also accepted for\n * tests that share storage and Partner simulator credentials. In the\n * default permissive mode, other well-formed Basic credentials whose\n * applicationKeyId was not minted by `b2_create_key` receive the same\n * implicit master grant for backward compatibility. A simulator-created\n * key always authorizes with its own capabilities, bucket restrictions,\n * name prefix, and expiration.\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 private readonly customUploadTimestampsEnabled: boolean\n private readonly partner: PartnerSimulator\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<string, IssuedToken>()\n /**\n * Mutable upload-token overrides for tokens minted by `b2_get_upload_url`\n * and `b2_get_upload_part_url`. The token string is self-describing\n * and signed so another simulator instance can validate issued-token\n * state, while this map records local invalidation / forced-expiry\n * state for tests.\n */\n private readonly uploadTokens = new Map<string, StoredUploadToken>()\n private readonly downloadAuthorizationTokens = new Map<string, DownloadAuthorizationToken>()\n private readonly downloadAuthorizationExpiryHeap: DownloadAuthorizationExpiry[] = []\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 this.customUploadTimestampsEnabled = options.customUploadTimestampsEnabled ?? 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 this.partner = new PartnerSimulator(\n {\n accountId: this.accountId,\n authTokenTtlMs: this.authTokenTtlMs,\n minimumPartSize: this.minimumPartSize,\n recommendedPartSize: this.recommendedPartSize,\n canAuthorize: (authzHeader) => this.canAuthorizePartner(authzHeader),\n createBucket: (req) => this.createBucket(req),\n error: (status, code, message) => this.error(status, code, message),\n genId: (prefix) => this.genId(prefix),\n monotonicTimestamp: () => this.monotonicTimestamp(),\n now: () => this.now(),\n storeKey: (key) => {\n this.keys.set(key.applicationKeyId, key)\n },\n },\n options,\n )\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 * Expire an upload authorization token previously returned by\n * `b2_get_upload_url` or `b2_get_upload_part_url`. Tests can use this\n * to simulate a stale upload URL and verify retry paths that fetch a\n * fresh URL/token pair.\n *\n * @param authorizationToken - The upload authorization token to expire.\n *\n * @returns `true` when the token existed and was expired, otherwise `false`.\n */\n expireUploadToken(authorizationToken: string): boolean {\n const now = this.now()\n const token = this.uploadTokens.get(authorizationToken)\n if (token === undefined || now >= token.cleanupAt) return false\n token.expiresAt = now - 1\n this.uploadTokens.set(authorizationToken, token)\n return true\n }\n\n /**\n * Invalidate an upload authorization token previously returned by\n * `b2_get_upload_url` or `b2_get_upload_part_url`.\n *\n * @param authorizationToken - The upload authorization token to invalidate.\n *\n * @returns `true` when the token existed and was invalidated, otherwise `false`.\n */\n invalidateUploadToken(authorizationToken: string): boolean {\n const now = this.now()\n const token = this.uploadTokens.get(authorizationToken)\n if (token === undefined || now >= token.cleanupAt) {\n this.pruneExpiredUploadTokens(now)\n return false\n }\n token.invalidated = true\n this.uploadTokens.set(authorizationToken, token)\n this.pruneExpiredUploadTokens()\n return true\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 private isStoredKeyExpired(key: StoredKey): boolean {\n return key.expirationTimestamp !== null && key.expirationTimestamp <= this.now()\n }\n\n private pruneExpiredUploadTokens(now = this.now()): void {\n for (const [authorizationToken, token] of this.uploadTokens.entries()) {\n if (now >= token.cleanupAt) {\n this.uploadTokens.delete(authorizationToken)\n }\n }\n }\n\n private async uploadTokenState(authorizationToken: string): Promise<StoredUploadToken | null> {\n const stored = this.uploadTokens.get(authorizationToken)\n if (stored !== undefined) return stored\n return await this.decodeUploadAuthorizationToken(authorizationToken)\n }\n\n private async encodeUploadAuthorizationToken(token: StoredUploadToken): Promise<string> {\n const payload: UploadTokenPayload = {\n v: 1,\n kind: token.kind,\n fileName: token.fileName,\n uploadUrl: token.uploadUrl,\n namePrefix: token.namePrefix,\n applicationKeyId: token.applicationKeyId,\n expiresAt: token.expiresAt,\n }\n const encoded = base64UrlEncode(utf8Encoder.encode(JSON.stringify(payload)))\n const signature = await this.signUploadTokenPayload(encoded)\n const prefix = token.kind === 'file' ? 'sim_upload_auth' : 'sim_part_auth'\n return `${prefix}_${encoded}.${signature}`\n }\n\n private async decodeUploadAuthorizationToken(\n authorizationToken: string,\n ): Promise<StoredUploadToken | null> {\n const filePrefix = 'sim_upload_auth_'\n const partPrefix = 'sim_part_auth_'\n const expectedKind = authorizationToken.startsWith(filePrefix)\n ? 'file'\n : authorizationToken.startsWith(partPrefix)\n ? 'part'\n : null\n if (expectedKind === null) return null\n\n const encoded = authorizationToken.slice(\n expectedKind === 'file' ? filePrefix.length : partPrefix.length,\n )\n const tokenParts = encoded.split('.')\n if (tokenParts.length !== 2) return null\n const [payloadBytes, signature] = tokenParts as [string, string]\n const expectedSignature = await this.signUploadTokenPayload(payloadBytes)\n if (!timingSafeStringEqual(signature, expectedSignature)) return null\n\n let parsed: unknown\n try {\n const bytes = base64UrlDecode(payloadBytes)\n if (bytes === null) return null\n parsed = JSON.parse(utf8Decoder.decode(bytes)) as unknown\n } catch {\n return null\n }\n if (typeof parsed !== 'object' || parsed === null) return null\n const payload = parsed as Record<string, unknown>\n if (\n payload['v'] !== 1 ||\n payload['kind'] !== expectedKind ||\n typeof payload['uploadUrl'] !== 'string' ||\n typeof payload['expiresAt'] !== 'number' ||\n !Number.isFinite(payload['expiresAt']) ||\n !(payload['namePrefix'] === null || typeof payload['namePrefix'] === 'string') ||\n !(payload['applicationKeyId'] === null || typeof payload['applicationKeyId'] === 'string')\n ) {\n return null\n }\n let fileName: string | null\n if (expectedKind === 'part') {\n if (typeof payload['fileName'] !== 'string') return null\n fileName = payload['fileName']\n } else {\n if (payload['fileName'] !== null) return null\n fileName = null\n }\n return {\n kind: expectedKind,\n fileName,\n uploadUrl: payload['uploadUrl'],\n namePrefix: payload['namePrefix'],\n applicationKeyId: payload['applicationKeyId'],\n expiresAt: payload['expiresAt'],\n cleanupAt: payload['expiresAt'],\n invalidated: false,\n }\n }\n\n private async signUploadTokenPayload(encodedPayload: string): Promise<string> {\n return base64UrlEncode(await hmacSha256(UPLOAD_TOKEN_SIGNING_KEY, encodedPayload))\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 scope - Optional effective bucket and file-name scope derived from the request.\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 scope?: RequestScope,\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 if (token.applicationKeyId !== null) {\n const key = this.keys.get(token.applicationKeyId)\n if (key === undefined) {\n return this.error(401, 'bad_auth_token', 'application key has been deleted; reauthorize')\n }\n if (this.isStoredKeyExpired(key)) {\n return this.error(401, 'expired_auth_token', 'application key has expired; reauthorize')\n }\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 return this.authorizeScopeGrant(scope, {\n bucketIds: token.bucketIds,\n namePrefix: token.namePrefix,\n bucketScopeRequiredMessage: () =>\n `application key is scoped to buckets ${token.bucketIds?.join(', ')}; bucket scope is required`,\n bucketMismatchMessage: (bucketId) =>\n `application key is scoped to buckets ${token.bucketIds?.join(', ')}; cannot access ${bucketId}`,\n prefixMismatchMessage: (fileName) =>\n `application key is scoped to prefix \"${token.namePrefix}\"; \"${fileName}\" is outside scope`,\n })\n }\n\n private authorizeScopeGrant(\n scope: RequestScope | undefined,\n grant: {\n readonly bucketIds: readonly string[] | null\n readonly namePrefix: string | null\n readonly bucketScopeRequiredMessage: () => string\n readonly bucketMismatchMessage: (bucketId: string) => string\n readonly prefixMismatchMessage: (fileName: string) => string\n },\n ): SimulatorJsonResponse | null {\n if (grant.bucketIds !== null) {\n if (scope?.requiresAccountLevelBucketAccess === true) {\n return this.error(403, 'unauthorized', grant.bucketScopeRequiredMessage())\n }\n if ((scope?.bucketIds.length ?? 0) === 0 && scope?.requiresBucketScope === true) {\n return this.error(403, 'unauthorized', grant.bucketScopeRequiredMessage())\n }\n for (const bucketId of scope?.bucketIds ?? []) {\n if (grant.bucketIds.includes(bucketId)) continue\n return this.error(403, 'unauthorized', grant.bucketMismatchMessage(bucketId))\n }\n }\n if (grant.namePrefix !== null) {\n for (const fileName of scope?.fileNames ?? []) {\n if (fileName.startsWith(grant.namePrefix)) continue\n return this.error(403, 'unauthorized', grant.prefixMismatchMessage(fileName))\n }\n }\n return null\n }\n\n private authorizeDownloadRequest(\n authToken: string | undefined,\n endpoint: 'b2_download_file_by_id' | 'b2_download_file_by_name',\n scope: RequestScope,\n url: URL,\n ): SimulatorJsonResponse | null {\n if (authToken === undefined || authToken === '') {\n return this.error(401, 'bad_auth_token', 'missing authorization token')\n }\n\n const downloadAuth = this.downloadAuthorizationTokens.get(authToken)\n if (downloadAuth === undefined) {\n this.purgeExpiredDownloadAuthorizationTokens()\n return this.authorizeRequest(authToken, endpoint, scope)\n }\n if (this.isExpiredDownloadAuthorizationToken(downloadAuth)) {\n this.downloadAuthorizationTokens.delete(authToken)\n this.purgeExpiredDownloadAuthorizationTokens()\n return this.error(401, 'expired_auth_token', 'download authorization token has expired')\n }\n if (endpoint === 'b2_download_file_by_id') {\n return this.error(\n 403,\n 'unauthorized',\n 'download authorization tokens cannot be used with b2_download_file_by_id',\n )\n }\n const overrideError = this.authorizeDownloadResponseOverrides(downloadAuth, url)\n if (overrideError !== null) return overrideError\n return this.authorizeScopeGrant(scope, {\n bucketIds: [downloadAuth.bucketId],\n namePrefix: downloadAuth.fileNamePrefix,\n bucketScopeRequiredMessage: () =>\n `download authorization is scoped to bucket ${downloadAuth.bucketId}; bucket scope is required`,\n bucketMismatchMessage: (bucketId) =>\n `download authorization is scoped to bucket ${downloadAuth.bucketId}; cannot access ${bucketId}`,\n prefixMismatchMessage: (fileName) =>\n `download authorization is scoped to prefix \"${downloadAuth.fileNamePrefix}\"; \"${fileName}\" is outside scope`,\n })\n }\n\n private isExpiredDownloadAuthorizationToken(\n token: DownloadAuthorizationToken,\n now = this.now(),\n ): boolean {\n return now >= token.expiresAt\n }\n\n private purgeExpiredDownloadAuthorizationTokens(): void {\n const now = this.now()\n for (let i = 0; i < DOWNLOAD_AUTH_PURGE_BATCH_SIZE; i++) {\n const next = this.downloadAuthorizationExpiryHeap[0]\n if (next === undefined || next.expiresAt > now) return\n this.popDownloadAuthorizationExpiry()\n const current = this.downloadAuthorizationTokens.get(next.token)\n if (current?.expiresAt === next.expiresAt) {\n this.downloadAuthorizationTokens.delete(next.token)\n }\n }\n }\n\n private pushDownloadAuthorizationExpiry(entry: DownloadAuthorizationExpiry): void {\n const heap = this.downloadAuthorizationExpiryHeap\n heap.push(entry)\n let idx = heap.length - 1\n while (idx > 0) {\n const parentIdx = Math.floor((idx - 1) / 2)\n const parent = heap[parentIdx] as DownloadAuthorizationExpiry\n if (parent.expiresAt <= entry.expiresAt) break\n heap[idx] = parent\n idx = parentIdx\n }\n heap[idx] = entry\n }\n\n private popDownloadAuthorizationExpiry(): DownloadAuthorizationExpiry | undefined {\n const heap = this.downloadAuthorizationExpiryHeap\n const root = heap[0]\n const last = heap.pop()\n if (root === undefined || last === undefined || heap.length === 0) return root\n\n let idx = 0\n while (true) {\n const leftIdx = idx * 2 + 1\n const rightIdx = leftIdx + 1\n const left = heap[leftIdx]\n const right = heap[rightIdx]\n if (left === undefined) break\n\n const childIdx = right !== undefined && right.expiresAt < left.expiresAt ? rightIdx : leftIdx\n const child = heap[childIdx] as DownloadAuthorizationExpiry\n if (child.expiresAt >= last.expiresAt) break\n heap[idx] = child\n idx = childIdx\n }\n heap[idx] = last\n return root\n }\n\n private authorizeDownloadResponseOverrides(\n downloadAuth: DownloadAuthorizationToken,\n url: URL,\n ): SimulatorJsonResponse | null {\n for (const param of DOWNLOAD_RESPONSE_OVERRIDE_PARAMS) {\n const expected = downloadAuth.responseHeaderOverrides[param]\n if (expected === undefined) continue\n const actual = url.searchParams.get(param)\n if (actual === expected) continue\n const expectedText = JSON.stringify(expected)\n const actualText = actual === null ? 'absent' : JSON.stringify(actual)\n return this.error(\n 403,\n 'unauthorized',\n `download authorization requires ${param}=${expectedText}; got ${actualText}`,\n )\n }\n return null\n }\n\n private downloadAuthorizationFromRequest(\n headers: Record<string, string>,\n url: URL,\n options: { readonly allowQueryAuthorization?: boolean } = {},\n ): string | undefined {\n const headerToken = requestHeaderValue(headers, 'authorization')\n if (headerToken !== undefined) return headerToken\n if (options.allowQueryAuthorization !== true) return undefined\n const queryToken =\n url.searchParams.get('Authorization') ?? url.searchParams.get('authorization')\n return queryToken?.startsWith(DOWNLOAD_AUTH_TOKEN_PREFIX) ? queryToken : undefined\n }\n\n private requestHasCapability(authToken: string | undefined, capability: Capability): boolean {\n if (!this.strictAuth) return true\n if (authToken === undefined) return false\n return this.issuedTokens.get(authToken)?.capabilities.includes(capability) === true\n }\n\n private bucketInfoForResponse(info: BucketInfo, authToken: string | undefined): BucketInfo {\n return {\n ...info,\n defaultServerSideEncryption: this.requestHasCapability(\n authToken,\n Capability.ReadBucketEncryption,\n )\n ? info.defaultServerSideEncryption\n : { isClientAuthorizedToRead: false, value: null },\n replicationConfiguration: filterReadableReplicationConfiguration(\n info.replicationConfiguration,\n this.requestHasCapability(authToken, Capability.ReadBucketReplications),\n ),\n }\n }\n\n private requireFileLockEnabled(bucket: StoredBucket): SimulatorJsonResponse | null {\n if (bucket.info.fileLockConfiguration.value?.isFileLockEnabled === true) return null\n return this.error(400, 'file_lock_not_enabled', 'Bucket does not have file lock enabled')\n }\n\n private requireGovernanceBypass(\n req: { bypassGovernance?: boolean },\n authToken: string | undefined,\n message: string,\n ): SimulatorJsonResponse | null {\n if (req.bypassGovernance !== true) {\n return this.error(400, 'file_lock_governance_protected', message)\n }\n if (!this.requestHasCapability(authToken, Capability.BypassGovernance)) {\n return this.error(\n 403,\n 'unauthorized',\n `application key lacks required capabilities: ${Capability.BypassGovernance}`,\n )\n }\n return null\n }\n\n private validateReplicationApplicationKey(\n applicationKeyId: string,\n requiredCapabilities: readonly Capability[],\n options: { readonly bucketId?: string | undefined; readonly role: string },\n ): SimulatorJsonResponse | null {\n const key = this.keys.get(applicationKeyId)\n if (key === undefined) {\n return this.invalidReplicationApplicationKey(options.role)\n }\n const missing = missingStoredKeyCapabilities(key, requiredCapabilities)\n if (missing.length > 0) {\n return this.invalidReplicationApplicationKey(options.role)\n }\n if (options.bucketId !== undefined && !storedKeyAllowsBucket(key, options.bucketId)) {\n return this.invalidReplicationApplicationKey(options.role)\n }\n return null\n }\n\n private invalidReplicationApplicationKey(role: string): SimulatorJsonResponse {\n return this.error(\n 400,\n 'bad_request',\n `${role} application key is invalid or not authorized for this replication configuration`,\n )\n }\n\n private invalidReplicationDestinationBucket(): SimulatorJsonResponse {\n return this.error(\n 400,\n 'bad_request',\n 'replication destination bucket is invalid or not configured for this source application key',\n )\n }\n\n private validateReplicationDestinationBuckets(\n sourceApplicationKeyId: string,\n rules: readonly unknown[],\n ): SimulatorJsonResponse | null {\n for (const rule of rules) {\n const destinationBucketId = requestRecord(rule)?.['destinationBucketId']\n if (typeof destinationBucketId !== 'string') continue\n\n const destinationBucket = this.buckets.get(destinationBucketId)\n const destinationApplicationKeyId =\n destinationBucket?.info.replicationConfiguration.value?.asReplicationDestination\n ?.sourceToDestinationKeyMapping[sourceApplicationKeyId]\n if (destinationApplicationKeyId === undefined) {\n return this.invalidReplicationDestinationBucket()\n }\n\n const destinationKeyError = this.validateReplicationApplicationKey(\n destinationApplicationKeyId,\n [Capability.WriteFiles],\n { bucketId: destinationBucketId, role: 'replication destination' },\n )\n if (destinationKeyError !== null) return destinationKeyError\n }\n return null\n }\n\n private validateReplicationApplicationKeys(\n config: unknown,\n bucketId: string | undefined,\n ): SimulatorJsonResponse | null {\n if (!this.strictAuth || config === undefined) return null\n\n const replicationConfiguration = requestRecord(config)\n const source = requestRecord(replicationConfiguration?.['asReplicationSource'])\n if (source !== null) {\n const sourceApplicationKeyId = source['sourceApplicationKeyId']\n if (typeof sourceApplicationKeyId === 'string') {\n const sourceKeyError = this.validateReplicationApplicationKey(\n sourceApplicationKeyId,\n [Capability.ReadFiles, Capability.ListFiles],\n { bucketId, role: 'replication source' },\n )\n if (sourceKeyError !== null) return sourceKeyError\n }\n const rules = source['replicationRules']\n if (typeof sourceApplicationKeyId === 'string' && Array.isArray(rules)) {\n const destinationError = this.validateReplicationDestinationBuckets(\n sourceApplicationKeyId,\n rules,\n )\n if (destinationError !== null) return destinationError\n }\n }\n\n const destination = requestRecord(replicationConfiguration?.['asReplicationDestination'])\n const mapping = requestRecord(destination?.['sourceToDestinationKeyMapping'])\n if (mapping !== null) {\n for (const [sourceApplicationKeyId, destinationApplicationKeyId] of Object.entries(mapping)) {\n const sourceKeyError = this.validateReplicationApplicationKey(\n sourceApplicationKeyId,\n [Capability.ReadFiles, Capability.ListFiles],\n { role: 'replication source' },\n )\n if (sourceKeyError !== null) return sourceKeyError\n\n if (typeof destinationApplicationKeyId === 'string') {\n const destinationKeyError = this.validateReplicationApplicationKey(\n destinationApplicationKeyId,\n [Capability.WriteFiles],\n { bucketId, role: 'replication destination' },\n )\n if (destinationKeyError !== null) return destinationKeyError\n }\n }\n }\n\n return null\n }\n\n private requestScope(endpoint: string, body: unknown): RequestScope | undefined {\n const directBucketId = requestStringField(body, 'bucketId')\n const directFileName = requestStringField(body, 'fileName')\n\n switch (endpoint) {\n case 'b2_create_bucket':\n return this.createBucketScope(body)\n case 'b2_list_buckets':\n return this.listBucketsScope(body, directBucketId)\n case 'b2_list_file_names':\n case 'b2_list_file_versions':\n return {\n bucketIds: directBucketId === undefined ? [] : [directBucketId],\n fileNames: [requestStringField(body, 'prefix') ?? ''],\n requiresBucketScope: true,\n }\n case 'b2_get_file_info':\n return (\n this.fileIdScope(requestStringField(body, 'fileId')) ?? {\n bucketIds: [],\n requiresBucketScope: true,\n }\n )\n case 'b2_delete_file_version':\n case 'b2_update_file_retention':\n case 'b2_update_file_legal_hold':\n return this.fileVersionScope(body, directFileName)\n case 'b2_get_upload_part_url':\n case 'b2_finish_large_file':\n case 'b2_cancel_large_file':\n case 'b2_list_parts':\n return (\n this.largeFileScope(requestStringField(body, 'fileId')) ?? {\n bucketIds: [],\n requiresBucketScope: true,\n }\n )\n case 'b2_copy_file':\n return this.copyFileScope(body, directFileName)\n case 'b2_copy_part':\n return this.copyPartScope(body)\n case 'b2_list_unfinished_large_files':\n return {\n bucketIds: directBucketId === undefined ? [] : [directBucketId],\n fileNames: [requestStringField(body, 'namePrefix') ?? ''],\n requiresBucketScope: true,\n }\n case 'b2_get_download_authorization':\n return {\n bucketIds: directBucketId === undefined ? [] : [directBucketId],\n fileNames: [requestStringField(body, 'fileNamePrefix') ?? ''],\n requiresBucketScope: true,\n }\n case 'b2_get_bucket_notification_rules':\n return this.notificationRulesScope(body, directBucketId, false)\n case 'b2_set_bucket_notification_rules':\n return this.notificationRulesScope(body, directBucketId, true)\n case 'b2_update_bucket':\n return this.updateBucketScope(body, directBucketId)\n default:\n return this.defaultRequestScope(directBucketId, directFileName)\n }\n }\n\n private createBucketScope(body: unknown): RequestScope {\n return {\n bucketIds: replicationDestinationBucketIds(body),\n requiresBucketScope: true,\n requiresAccountLevelBucketAccess: true,\n }\n }\n\n private updateBucketScope(body: unknown, directBucketId: string | undefined): RequestScope {\n return {\n bucketIds: [\n ...(directBucketId === undefined ? [] : [directBucketId]),\n ...replicationDestinationBucketIds(body),\n ],\n requiresBucketScope: true,\n }\n }\n\n private listBucketsScope(body: unknown, directBucketId: string | undefined): RequestScope {\n if (directBucketId !== undefined) {\n return { bucketIds: [directBucketId], requiresBucketScope: true }\n }\n const bucketName = requestStringField(body, 'bucketName')\n if (bucketName !== undefined) {\n const bucket = [...this.buckets.values()].find((b) => b.info.bucketName === bucketName)\n return {\n bucketIds: bucket === undefined ? [] : [bucket.info.bucketId as string],\n requiresBucketScope: true,\n }\n }\n return { bucketIds: [], requiresBucketScope: true }\n }\n\n private fileVersionScope(body: unknown, directFileName: string | undefined): RequestScope {\n const fileScope = this.fileIdScope(requestStringField(body, 'fileId'))\n if (fileScope !== undefined) return fileScope\n return {\n bucketIds: [],\n ...(directFileName !== undefined ? { fileNames: [directFileName] } : {}),\n requiresBucketScope: true,\n }\n }\n\n private copyFileScope(body: unknown, directFileName: string | undefined): RequestScope {\n const sourceScope = this.fileIdScope(requestStringField(body, 'sourceFileId'))\n const destinationBucketId = requestStringField(body, 'destinationBucketId')\n const bucketIds = [\n ...(sourceScope?.bucketIds ?? []),\n ...(destinationBucketId !== undefined ? [destinationBucketId] : []),\n ]\n const scopedFileNames = fileNames(...(sourceScope?.fileNames ?? []), directFileName)\n return {\n bucketIds,\n ...(scopedFileNames !== undefined ? { fileNames: scopedFileNames } : {}),\n requiresBucketScope: true,\n }\n }\n\n private copyPartScope(body: unknown): RequestScope {\n const sourceScope = this.fileIdScope(requestStringField(body, 'sourceFileId'))\n const largeScope = this.largeFileScope(requestStringField(body, 'largeFileId'))\n const scopedFileNames = fileNames(\n ...(sourceScope?.fileNames ?? []),\n ...(largeScope?.fileNames ?? []),\n )\n return {\n bucketIds: [...(sourceScope?.bucketIds ?? []), ...(largeScope?.bucketIds ?? [])],\n ...(scopedFileNames !== undefined ? { fileNames: scopedFileNames } : {}),\n requiresBucketScope: true,\n }\n }\n\n private notificationRulesScope(\n body: unknown,\n directBucketId: string | undefined,\n includeRequestRules: boolean,\n ): RequestScope {\n const existingPrefixes =\n directBucketId === undefined\n ? []\n : storedNotificationRulePrefixes(this.notificationRules.get(directBucketId))\n const requestPrefixes = includeRequestRules ? (notificationRulePrefixes(body) ?? []) : []\n const prefixes = [...existingPrefixes, ...requestPrefixes]\n return {\n bucketIds: directBucketId === undefined ? [] : [directBucketId],\n ...(prefixes.length > 0 ? { fileNames: prefixes } : {}),\n requiresBucketScope: true,\n }\n }\n\n private defaultRequestScope(\n directBucketId: string | undefined,\n directFileName: string | undefined,\n ): RequestScope | undefined {\n if (directBucketId !== undefined) {\n return {\n bucketIds: [directBucketId],\n ...(directFileName !== undefined ? { fileNames: [directFileName] } : {}),\n requiresBucketScope: true,\n }\n }\n return directFileName !== undefined\n ? { bucketIds: [], fileNames: [directFileName], requiresBucketScope: false }\n : undefined\n }\n\n private fileIdScope(fileId: string | undefined): RequestScope | undefined {\n if (fileId === undefined) return undefined\n const found = this.findFile(fileId)\n if (found === null) return undefined\n return {\n bucketIds: [found.bucketId],\n fileNames: [found.stored.fileVersion.fileName],\n requiresBucketScope: true,\n }\n }\n\n private largeFileScope(fileId: string | undefined): RequestScope | undefined {\n if (fileId === undefined) return undefined\n const large = this.largeFiles.get(fileId)\n if (large === undefined) return undefined\n return { bucketIds: [large.bucketId], fileNames: [large.fileName], requiresBucketScope: true }\n }\n\n private async issueUploadAuthorization(\n options:\n | {\n kind: 'file'\n sourceAuthToken: string | undefined\n bucketId: string\n }\n | {\n kind: 'part'\n sourceAuthToken: string | undefined\n fileId: string\n fileName: string\n },\n ): Promise<{ uploadUrl: string; authorizationToken: string }> {\n const now = this.now()\n this.pruneExpiredUploadTokens(now)\n const endpoint = options.kind === 'file' ? 'b2_upload_file' : 'b2_upload_part'\n const idParam = options.kind === 'file' ? 'bucketId' : 'fileId'\n const scopedId = options.kind === 'file' ? options.bucketId : options.fileId\n const uploadId = this.genId(options.kind === 'file' ? 'upload_file' : 'upload_part')\n const uploadUrl = new URL(b2Url('http://localhost:0', { version: 'v3', endpoint }))\n uploadUrl.searchParams.set(idParam, scopedId)\n uploadUrl.searchParams.set('uploadId', uploadId)\n\n const sourceToken =\n options.sourceAuthToken === undefined\n ? undefined\n : this.issuedTokens.get(options.sourceAuthToken)\n const token: StoredUploadToken = {\n kind: options.kind,\n fileName: options.kind === 'part' ? options.fileName : null,\n uploadUrl: uploadUrl.toString(),\n namePrefix: this.strictAuth ? (sourceToken?.namePrefix ?? null) : null,\n applicationKeyId: sourceToken?.applicationKeyId ?? null,\n expiresAt: now + this.authTokenTtlMs,\n cleanupAt: now + this.authTokenTtlMs,\n invalidated: false,\n }\n const authorizationToken = await this.encodeUploadAuthorizationToken(token)\n this.uploadTokens.set(authorizationToken, token)\n return { uploadUrl: uploadUrl.toString(), authorizationToken }\n }\n\n private async validateUploadAuthorization(\n kind: UploadTokenKind,\n uploadUrl: string,\n authToken: string | undefined,\n fileName: string | undefined,\n ): Promise<SimulatorJsonResponse | null> {\n if (authToken === undefined || authToken === '') {\n return this.error(401, 'bad_auth_token', 'missing upload Authorization header')\n }\n\n const token = await this.uploadTokenState(authToken)\n if (token === null) {\n this.pruneExpiredUploadTokens()\n return this.error(401, 'bad_auth_token', 'unknown upload authorization token')\n }\n const now = this.now()\n if (now >= token.expiresAt) {\n if (now >= token.cleanupAt) {\n this.uploadTokens.delete(authToken)\n } else {\n this.uploadTokens.set(authToken, token)\n }\n this.pruneExpiredUploadTokens(now)\n return this.error(\n 401,\n 'expired_auth_token',\n 'upload authorization token has expired; get a new upload URL',\n )\n }\n if (token.invalidated) {\n this.uploadTokens.set(authToken, token)\n this.pruneExpiredUploadTokens(now)\n return this.error(401, 'bad_auth_token', 'upload authorization token has been invalidated')\n }\n if (token.kind !== kind) {\n this.pruneExpiredUploadTokens(now)\n return this.error(401, 'bad_auth_token', 'upload authorization token type mismatch')\n }\n this.pruneExpiredUploadTokens(now)\n if (!this.uploadUrlMatches(token, uploadUrl)) {\n return this.error(\n 401,\n 'bad_auth_token',\n 'upload authorization token is not valid for this upload URL',\n )\n }\n const scopedName = kind === 'file' ? fileName : (token.fileName ?? undefined)\n if (token.namePrefix !== null && scopedName !== undefined) {\n if (!scopedName.startsWith(token.namePrefix)) {\n return this.error(\n 403,\n 'unauthorized',\n `application key is scoped to prefix \"${token.namePrefix}\"; \"${scopedName}\" is outside scope`,\n )\n }\n }\n return null\n }\n\n private uploadUrlMatches(token: StoredUploadToken, uploadUrl: string): boolean {\n let expected: URL\n let actual: URL\n try {\n expected = new URL(token.uploadUrl)\n actual = new URL(uploadUrl)\n } catch {\n return false\n }\n\n if (actual.origin !== expected.origin || actual.pathname !== expected.pathname) return false\n const scopeParam = token.kind === 'file' ? 'bucketId' : 'fileId'\n return (\n actual.searchParams.get(scopeParam) === expected.searchParams.get(scopeParam) &&\n actual.searchParams.get('uploadId') === expected.searchParams.get('uploadId')\n )\n }\n\n private allowedBuckets(\n bucketIds: readonly string[] | null | undefined,\n ): readonly { readonly id: BucketId; readonly name: string | null }[] | null {\n if (bucketIds === undefined || bucketIds === null) return null\n return bucketIds.map((id) => ({\n id: bucketIdOf(id),\n name: this.buckets.get(id)?.info.bucketName ?? null,\n }))\n }\n\n /**\n * Resolve 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 * The simulator has two implicit full-access storage credentials:\n * the documented `test-key-id:test-key` pair used by SDK tests and\n * `master-key-id:master-key`. In default permissive mode, any other\n * well-formed Basic credential whose applicationKeyId is not a stored\n * key also receives that implicit master grant for backward\n * compatibility. A credential whose applicationKeyId matches a stored\n * key must use the stored secret and must not be expired.\n *\n * @param authzHeader - Raw HTTP `Authorization` header value.\n *\n * @returns The authorizing key's grant scope or the rejection reason.\n */\n private findAuthorizationGrant(authzHeader: string | undefined): AuthorizationGrantResolution {\n const credentials = parseBasicAuthorizationHeader(authzHeader)\n if (credentials === null) return { kind: 'invalid' }\n const stored = this.keys.get(credentials.applicationKeyId)\n if (stored !== undefined) {\n if (!timingSafeStringEqual(stored.applicationKey, credentials.applicationKey)) {\n return { kind: 'invalid' }\n }\n if (this.isStoredKeyExpired(stored)) return { kind: 'expired' }\n return {\n kind: 'ok',\n grant: {\n capabilities: stored.capabilities,\n bucketIds: stored.bucketIds,\n namePrefix: stored.namePrefix,\n applicationKeyId: credentials.applicationKeyId,\n accountId: stored.accountId,\n expirationTimestamp: stored.expirationTimestamp,\n },\n }\n }\n if (isSimulatorApplicationKeyId(credentials.applicationKeyId)) return { kind: 'invalid' }\n if (isImplicitStorageMasterCredential(credentials) || !this.strictAuth) {\n return {\n kind: 'ok',\n grant: {\n capabilities: SIMULATOR_MASTER_CAPABILITIES,\n bucketIds: null,\n namePrefix: null,\n applicationKeyId: null,\n accountId: this.accountId,\n expirationTimestamp: null,\n },\n }\n }\n return { kind: 'invalid' }\n }\n\n private canAuthorizePartner(authzHeader: string | undefined): boolean {\n const credentials = parseBasicAuthorizationHeader(authzHeader)\n if (credentials === null) return false\n if (isImplicitPartnerMasterCredential(credentials)) return true\n const stored = this.keys.get(credentials.applicationKeyId)\n return (\n stored !== undefined &&\n stored.accountId === this.accountId &&\n !this.isStoredKeyExpired(stored) &&\n timingSafeStringEqual(stored.applicationKey, credentials.applicationKey)\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.\n * @param origin - The request URL origin used for simulator-issued endpoints.\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 origin: string,\n path: string,\n headers: Record<string, string>,\n body: unknown,\n ): Promise<SimulatorJsonResponse> {\n const { endpoint, version, unsupportedVersion } = apiPathParts(path)\n if (unsupportedVersion !== undefined) {\n return this.error(\n 400,\n 'unsupported_api_version',\n `B2 API version ${unsupportedVersion} is not supported by the simulator; use v4 or v3`,\n )\n }\n if (!jsonEndpointAllowsMethod(method, endpoint)) {\n return this.error(\n 405,\n 'method_not_allowed',\n `${endpoint} does not support ${method.toUpperCase()}`,\n )\n }\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 // capability and scope checks.\n if (this.strictAuth && !this.partner.isEndpoint(endpoint)) {\n const authError = this.authorizeRequest(\n headers['authorization'],\n endpoint,\n this.requestScope(endpoint, body),\n )\n if (authError !== null) return authError\n }\n\n switch (endpoint) {\n case 'b2_authorize_account':\n if (version === 'v3' && this.partner.isAuthorizeEnabled()) {\n return this.partner.authorize(headers['authorization'], origin)\n }\n return this.authorize(headers['authorization'], origin)\n case PartnerEndpoint.CreateGroupMember:\n return this.partner.createGroupMember(body, headers['authorization'])\n case PartnerEndpoint.EjectGroupMember:\n return this.partner.ejectGroupMember(body, headers['authorization'])\n case PartnerEndpoint.ListGroups:\n return this.partner.listGroups(body, headers['authorization'])\n case PartnerEndpoint.ListGroupMembers:\n return this.partner.listGroupMembers(body, headers['authorization'])\n case PartnerEndpoint.ReserveTrialCreateAccount:\n return this.partner.reserveTrialCreateAccount(body, headers['authorization'])\n case PartnerEndpoint.ListComputers:\n return this.partner.listComputers(body, headers['authorization'])\n case PartnerEndpoint.DeleteComputer:\n return this.partner.deleteComputer(body, headers['authorization'])\n case 'b2_create_bucket':\n return this.createBucket(\n body as { bucketName: string; bucketType: BucketType; accountId: string },\n headers['authorization'],\n )\n case 'b2_list_buckets':\n return this.listBuckets(\n body as {\n bucketId?: string\n bucketName?: string\n bucketTypes?: BucketTypesFilter\n },\n headers['authorization'],\n )\n case 'b2_delete_bucket':\n return this.deleteBucket(body as { bucketId: string }, headers['authorization'])\n case 'b2_update_bucket':\n return this.updateBucket(body as Record<string, unknown>, headers['authorization'])\n case 'b2_get_upload_url':\n return await this.getUploadUrl(body as { bucketId: string }, headers['authorization'])\n case 'b2_list_file_names':\n return this.listFileNames(\n body as {\n bucketId: string\n delimiter?: 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 delimiter?: 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 headers['authorization'],\n )\n case 'b2_copy_file':\n return await this.copyFile(\n body as {\n sourceFileId: string\n fileName: string\n destinationBucketId?: string\n range?: string\n metadataDirective?: string\n contentType?: string\n fileInfo?: Record<string, string>\n sourceServerSideEncryption?: EncryptionSetting\n destinationServerSideEncryption?: EncryptionSetting\n },\n )\n case 'b2_start_large_file':\n return await this.startLargeFile(\n body as {\n bucketId: string\n fileName: string\n contentType: string\n customUploadTimestamp?: string | null\n fileInfo?: Record<string, string>\n fileRetention?: FileRetentionValue\n legalHold?: LegalHoldValue\n serverSideEncryption?: EncryptionSetting\n },\n headers['authorization'],\n )\n case 'b2_get_upload_part_url':\n return await this.getUploadPartUrl(body as { fileId: string }, headers['authorization'])\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 headers['authorization'],\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 sourceServerSideEncryption?: EncryptionSetting\n destinationServerSideEncryption?: EncryptionSetting\n },\n )\n case 'b2_get_download_authorization':\n return this.getDownloadAuthorization(body as DownloadAuthorizationRequestBody)\n case 'b2_create_key':\n return this.createKey(\n body as {\n accountId: string\n capabilities: unknown\n keyName: unknown\n validDurationInSeconds?: number\n bucketIds?: readonly string[] | null\n bucketId?: string\n namePrefix?: string\n },\n version,\n headers['authorization'],\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?: unknown\n retainUntilTimestamp?: unknown\n }\n bypassGovernance?: boolean\n },\n headers['authorization'],\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 A promise resolving to an object with HTTP status and JSON response body.\n */\n async handleUpload(\n url: string,\n headers: Record<string, string>,\n data: Uint8Array,\n ): Promise<SimulatorJsonResponse> {\n const endpoint = new URL(url).pathname.split('/').pop() ?? ''\n const kind: UploadTokenKind | null =\n endpoint === 'b2_upload_file' ? 'file' : endpoint === 'b2_upload_part' ? 'part' : null\n if (kind === null) return this.error(400, 'bad_request', `Unknown upload endpoint: ${endpoint}`)\n\n const fileName =\n kind === 'part' || headers['x-bz-file-name'] === undefined\n ? undefined\n : decodeHeaderValue(headers['x-bz-file-name'])\n const authError = await this.validateUploadAuthorization(\n kind,\n url,\n headers['authorization'],\n fileName,\n )\n if (authError !== null) return authError\n\n const contentLengthError = this.validateContentLength(headers, data.byteLength)\n if (contentLengthError !== null) return contentLengthError\n\n if (kind === 'part') {\n return await this.handleUploadPart(url, headers, data)\n }\n return await this.handleUploadFile(url, headers, data)\n }\n\n private validateContentLength(\n headers: Record<string, string>,\n actualLength: number,\n ): SimulatorJsonResponse | null {\n const parsed = parseContentLengthHeader(headers)\n if (parsed.kind === 'absent') return null\n if (parsed.kind === 'error') return this.error(400, 'bad_request', parsed.message)\n if (parsed.expectedLength !== actualLength) {\n return this.error(\n 400,\n 'bad_request',\n contentLengthMismatchMessage(parsed.expectedLength, actualLength),\n )\n }\n return null\n }\n\n private parseCustomUploadTimestamp(\n value: unknown,\n ): { readonly timestamp: number | null } | SimulatorJsonResponse {\n if (value === undefined || value === null) return { timestamp: null }\n if (!this.customUploadTimestampsEnabled) {\n return this.error(\n 400,\n 'custom_timestamp_not_allowed',\n 'Custom upload timestamps are not enabled for this account.',\n )\n }\n const timestamp = typeof value === 'string' && /^\\d+$/.test(value) ? Number(value) : Number.NaN\n\n if (!Number.isSafeInteger(timestamp) || timestamp < 0 || timestamp > this.now()) {\n return this.error(\n 400,\n 'custom_timestamp_invalid',\n 'The request has an invalid custom upload timestamp.',\n )\n }\n return { timestamp }\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 const url = new URL(`http://localhost${path}`)\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.authorizeDownloadRequest(\n this.downloadAuthorizationFromRequest(headers, url, { allowQueryAuthorization: false }),\n 'b2_download_file_by_id',\n this.fileIdScope(url.searchParams.get('fileId') ?? undefined) ?? {\n bucketIds: [],\n requiresBucketScope: true,\n },\n url,\n )\n if (authError !== null) return this.errorAsDownload(authError)\n }\n const fileId = url.searchParams.get('fileId') ?? ''\n return this.finalizeDownload(\n this.downloadById(fileId, headers, requestHeaderValue(headers, 'range')),\n url,\n method,\n )\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 const url = new URL(`http://localhost${path}`)\n if (this.strictAuth) {\n const bucket = [...this.buckets.values()].find((b) => b.info.bucketName === bucketName)\n const authError = this.authorizeDownloadRequest(\n this.downloadAuthorizationFromRequest(headers, url, { allowQueryAuthorization: true }),\n 'b2_download_file_by_name',\n {\n bucketIds: bucket === undefined ? [] : [bucket.info.bucketId as string],\n fileNames: [fileName],\n requiresBucketScope: true,\n },\n url,\n )\n if (authError !== null) return this.errorAsDownload(authError)\n }\n return this.finalizeDownload(\n this.downloadByName(bucketName, fileName, headers, requestHeaderValue(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 newHeaders = { ...response.headers }\n for (const [param, header] of Object.entries(DOWNLOAD_RESPONSE_OVERRIDE_HEADERS)) {\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 async handleUploadFile(\n url: string,\n headers: Record<string, string>,\n data: Uint8Array,\n ): Promise<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 = decodeHeaderValue(headers['x-bz-file-name'] ?? '')\n const contentType = headers['content-type'] ?? 'application/octet-stream'\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 // SHA-1 verification, matching real B2 semantics for X-Bz-Content-Sha1:\n // - 'none' / 'do_not_verify' (or missing) -> stored as 'none', no check\n // - 'unverified:<hex>' -> store <hex>, no check\n // - 'hex_digits_at_end' -> verify the trailing digest,\n // store body without trailer\n // - 40-char hex -> verify against the bytes,\n // 400 bad_request on mismatch\n // Without this, the simulator stored the client's claimed hash verbatim, so\n // a wrong digest or corrupted bytes passed every test.\n const resolved = await this.resolveUploadSha1(headers['x-bz-content-sha1'], data)\n if ('status' in resolved) return resolved\n const { sha1: contentSha1, data: storedData } = resolved\n\n const serverSideEncryption = await uploadServerSideEncryption(\n headers,\n bucketDefaultServerSideEncryptionForStorage(bucket.info),\n )\n if ('status' in serverSideEncryption) return serverSideEncryption\n const customUploadTimestamp = this.parseCustomUploadTimestamp(\n requestHeaderValue(headers, 'x-bz-custom-upload-timestamp'),\n )\n if ('status' in customUploadTimestamp) {\n return customUploadTimestamp\n }\n const fileVersion = this.makeFileVersion({\n bucketId,\n fileName,\n contentType,\n contentLength: storedData.byteLength,\n contentSha1,\n fileInfo,\n action: FileAction.Upload,\n serverSideEncryption,\n ...this.newReplicationStatusField(bucketId, fileName),\n ...(customUploadTimestamp.timestamp !== null\n ? { uploadTimestamp: customUploadTimestamp.timestamp }\n : {}),\n })\n const stored: StoredFile = { fileVersion, data: storedData, serverSideEncryption }\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 /**\n * Resolve the `X-Bz-Content-Sha1` header into the SHA-1 to store, verifying\n * it against the uploaded bytes where B2 would. Returns the SHA-1 string to\n * persist, or a `400 bad_request` response when a verifiable hash does not\n * match the data received.\n *\n * @param header - The raw `X-Bz-Content-Sha1` header value, if any.\n * @param data - The uploaded bytes.\n *\n * @returns The SHA-1 string to store, or an error response on mismatch.\n */\n private async resolveUploadSha1(\n header: string | undefined,\n data: Uint8Array,\n ): Promise<{ sha1: string; data: Uint8Array } | SimulatorJsonResponse> {\n const value = header ?? 'none'\n if (value === 'none' || value === 'do_not_verify') return { sha1: 'none', data }\n if (value.startsWith('unverified:')) {\n return { sha1: value.slice('unverified:'.length).toLowerCase(), data }\n }\n if (value === 'hex_digits_at_end') {\n // B2 trailing-SHA mode: the final 40 bytes of the body are the hex digest,\n // not file content. Split them off, verify the rest, and store only the\n // body so contentLength and downloaded bytes match real B2.\n if (data.byteLength < 40) {\n return this.error(400, 'bad_request', 'Sha1 did not match data received')\n }\n const body = data.subarray(0, data.byteLength - 40)\n const trailer = utf8Decoder.decode(data.subarray(data.byteLength - 40)).toLowerCase()\n const actual = await sha1Hex(body)\n if (actual !== trailer) {\n return this.error(400, 'bad_request', 'Sha1 did not match data received')\n }\n return { sha1: actual, data: body }\n }\n const expected = value.toLowerCase()\n const actual = await sha1Hex(data)\n if (actual !== expected) {\n // Match real B2's error string exactly (no digests) for fidelity.\n return this.error(400, 'bad_request', 'Sha1 did not match data received')\n }\n return { sha1: expected, data }\n }\n\n private async validateUploadPartEncryption(\n large: LargeFileInProgress,\n headers: Record<string, string>,\n ): Promise<SimulatorJsonResponse | null> {\n if (large.serverSideEncryption.mode !== EncryptionMode.SseC) {\n if (!hasCustomerEncryptionHeaders(headers)) return null\n return this.error(400, 'bad_request', 'SSE-C upload part headers require an SSE-C large file')\n }\n\n const supplied = await storedServerSideEncryption({\n mode: EncryptionMode.SseC,\n algorithm: headers['x-bz-server-side-encryption-customer-algorithm'] as EncryptionAlgorithm,\n customerKey: headers['x-bz-server-side-encryption-customer-key'] as string,\n customerKeyMd5: headers['x-bz-server-side-encryption-customer-key-md5'] as string,\n })\n if ('status' in supplied) return supplied\n if (!sameStoredServerSideEncryption(large.serverSideEncryption, supplied)) {\n return this.error(\n 400,\n 'bad_request',\n 'SSE-C upload parts require matching customer encryption headers',\n )\n }\n\n return null\n }\n\n private async handleUploadPart(\n url: string,\n headers: Record<string, string>,\n data: Uint8Array,\n ): Promise<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 = this.parsePartNumberHeader(requestHeaderValue(headers, 'x-bz-part-number'))\n if (typeof partNumber !== 'number') return partNumber\n\n const encryptionError = await this.validateUploadPartEncryption(large, headers)\n if (encryptionError !== null) return encryptionError\n\n // Verify the part bytes against X-Bz-Content-Sha1, same as b2_upload_file.\n // Parts are always sent with a real (or unverified:) sha1 by the SDK.\n const resolved = await this.resolveUploadSha1(headers['x-bz-content-sha1'], data)\n if ('status' in resolved) return resolved\n const { sha1, data: partData } = resolved\n\n const uploadTimestamp = this.monotonicTimestamp()\n large.parts.set(partNumber, { data: partData, sha1, contentMd5: null, uploadTimestamp })\n\n return {\n status: 200,\n body: {\n fileId: large.fileId,\n partNumber,\n contentLength: partData.byteLength,\n contentSha1: sha1,\n contentMd5: null,\n serverSideEncryption: publicServerSideEncryption(large.serverSideEncryption),\n uploadTimestamp,\n },\n }\n }\n\n private parsePartNumberHeader(header: string | undefined): number | SimulatorJsonResponse {\n if (header === undefined) {\n return this.error(400, 'bad_request', 'X-Bz-Part-Number header is required')\n }\n if (!/^\\d+$/.test(header)) {\n return this.error(\n 400,\n 'bad_request',\n `X-Bz-Part-Number must be an integer between ${B2_MIN_PART_NUMBER} and ${B2_MAX_PART_NUMBER}; received ${header}`,\n )\n }\n const partNumber = Number(header)\n return this.validatePartNumber(partNumber) ?? partNumber\n }\n\n private validatePartNumber(partNumber: number): SimulatorJsonResponse | null {\n if (\n Number.isInteger(partNumber) &&\n partNumber >= B2_MIN_PART_NUMBER &&\n partNumber <= B2_MAX_PART_NUMBER\n ) {\n return null\n }\n return this.error(\n 400,\n 'bad_request',\n `partNumber must be an integer between ${B2_MIN_PART_NUMBER} and ${B2_MAX_PART_NUMBER}; received ${String(partNumber)}`,\n )\n }\n\n private downloadById(\n fileId: string,\n headers: Record<string, string>,\n range?: string,\n ): SimulatorDownloadResponse {\n const found = this.findFile(fileId)\n if (found === null) return { status: 404, headers: {}, data: null }\n return this.serveFile(found.stored, headers, range)\n }\n\n private downloadByName(\n bucketName: string,\n fileName: string,\n headers: Record<string, 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, headers, range)\n }\n return { status: 404, headers: {}, data: null }\n }\n\n private validateDownloadEncryption(\n stored: StoredFile,\n headers: Record<string, string>,\n ): SimulatorDownloadResponse | null {\n const encryption = stored.serverSideEncryption\n if (encryption.mode !== EncryptionMode.SseC) return null\n\n const algorithm = headers['x-bz-server-side-encryption-customer-algorithm']\n const customerKey = headers['x-bz-server-side-encryption-customer-key']\n const customerKeyMd5 = headers['x-bz-server-side-encryption-customer-key-md5']\n const customerKeyBytes = customerKey === undefined ? null : base64ToBytes(customerKey, 32)\n const normalizedCustomerKeyMd5 =\n customerKeyMd5 === undefined ? null : normalizeSizedBase64(customerKeyMd5, 16)\n if (\n algorithm !== encryption.algorithm ||\n customerKeyBytes === null ||\n normalizedCustomerKeyMd5 === null ||\n md5Base64Sync(customerKeyBytes) !== normalizedCustomerKeyMd5\n ) {\n return this.errorAsDownload(\n this.error(\n 400,\n 'bad_request',\n 'SSE-C downloads require matching customer encryption headers',\n ),\n )\n }\n if (\n normalizedCustomerKeyMd5 !== encryption.customerKeyMd5 ||\n customerKeyDigest(customerKeyBytes) !== encryption.customerKeyDigest\n ) {\n return this.errorAsDownload(\n this.error(\n 403,\n 'access_denied',\n 'SSE-C customer encryption key does not match the stored file',\n ),\n )\n }\n\n return null\n }\n\n private async validateSourceServerSideEncryption(\n stored: StoredFile,\n encryption: EncryptionSetting | undefined,\n ): Promise<SimulatorJsonResponse | null> {\n const expected = stored.serverSideEncryption\n if (expected.mode !== EncryptionMode.SseC) return null\n if (encryption?.mode !== EncryptionMode.SseC) {\n return this.error(400, 'bad_request', 'SSE-C source copies require customer encryption')\n }\n\n const supplied = await storedServerSideEncryption(encryption)\n if ('status' in supplied) return supplied\n if (!sameStoredServerSideEncryption(expected, supplied)) {\n return this.error(400, 'bad_request', 'SSE-C source customer encryption does not match')\n }\n return null\n }\n\n private async resolveCopyDestinationEncryption(\n destinationBucket: StoredBucket,\n requested: EncryptionSetting | undefined,\n ): Promise<StoredServerSideEncryption | SimulatorJsonResponse> {\n if (requested !== undefined) return await storedServerSideEncryption(requested)\n return await storedServerSideEncryption(\n bucketDefaultServerSideEncryptionForStorage(destinationBucket.info),\n )\n }\n\n private async validateCopyPartDestinationEncryption(\n large: LargeFileInProgress,\n encryption: EncryptionSetting | undefined,\n ): Promise<SimulatorJsonResponse | null> {\n if (encryption === undefined) {\n return large.serverSideEncryption.mode === EncryptionMode.SseC\n ? this.error(400, 'bad_request', 'SSE-C destination copies require customer encryption')\n : null\n }\n\n const supplied = await storedServerSideEncryption(encryption)\n if ('status' in supplied) return supplied\n if (!sameStoredServerSideEncryption(large.serverSideEncryption, supplied)) {\n return this.error(400, 'bad_request', 'Destination server-side encryption does not match')\n }\n return null\n }\n\n private serveFile(\n stored: StoredFile,\n requestHeaders: Record<string, string>,\n range?: string,\n ): SimulatorDownloadResponse {\n const encryptionError = this.validateDownloadEncryption(stored, requestHeaders)\n if (encryptionError !== null) return encryptionError\n\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': encodeFileName(fv.fileName),\n 'X-Bz-Content-Sha1': fv.contentSha1 ?? 'none',\n 'X-Bz-Upload-Timestamp': String(fv.uploadTimestamp),\n }\n // Serialize stored fileInfo as X-Bz-Info-* response headers so custom\n // metadata round-trips through download(), not just getFileInfo/list. Use\n // the same B2 wire encoding (encodeFileName) the SDK's download parser\n // decodes with (decodeFileName), rather than encodeURIComponent.\n for (const [key, value] of Object.entries(fv.fileInfo)) {\n headers[`X-Bz-Info-${encodeFileName(key)}`] = encodeFileName(value)\n }\n // Preserve the synthetic last-modified default only when the upload didn't\n // set one explicitly.\n if (!('src_last_modified_millis' in fv.fileInfo)) {\n headers['X-Bz-Info-src_last_modified_millis'] = String(fv.uploadTimestamp)\n }\n const unauthorizedToRead: DownloadClientUnauthorizedToReadMarker[] = []\n this.addDownloadEncryptionHeaders(\n headers,\n stored.serverSideEncryption,\n requestHeaders,\n unauthorizedToRead,\n )\n this.addDownloadObjectLockHeaders(headers, fv, requestHeaders, unauthorizedToRead)\n if (unauthorizedToRead.length > 0) {\n headers[DownloadHeaderName.ClientUnauthorizedToRead] = unauthorizedToRead.join(',')\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 private addDownloadEncryptionHeaders(\n headers: Record<string, string>,\n encryption: StoredServerSideEncryption,\n requestHeaders: Record<string, string>,\n unauthorizedToRead: DownloadClientUnauthorizedToReadMarker[],\n ): void {\n if (encryption.mode === EncryptionMode.None) return\n if (\n !this.requestHasCapability(\n requestHeaderValue(requestHeaders, 'authorization'),\n Capability.ReadBucketEncryption,\n )\n ) {\n if (encryption.mode === EncryptionMode.SseB2) {\n unauthorizedToRead.push(DownloadClientUnauthorizedToReadMarker.ServerSideEncryption)\n } else {\n unauthorizedToRead.push(\n DownloadClientUnauthorizedToReadMarker.ServerSideEncryptionCustomerAlgorithm,\n DownloadClientUnauthorizedToReadMarker.ServerSideEncryptionCustomerKeyMd5,\n )\n }\n return\n }\n\n if (encryption.mode === EncryptionMode.SseB2) {\n headers[DownloadHeaderName.ServerSideEncryption] = encryption.algorithm\n return\n }\n headers[DownloadHeaderName.ServerSideEncryptionCustomerAlgorithm] = encryption.algorithm\n headers[DownloadHeaderName.ServerSideEncryptionCustomerKeyMd5] = encryption.customerKeyMd5\n }\n\n private addDownloadObjectLockHeaders(\n headers: Record<string, string>,\n fileVersion: FileVersion,\n requestHeaders: Record<string, string>,\n unauthorizedToRead: DownloadClientUnauthorizedToReadMarker[],\n ): void {\n const authToken = requestHeaderValue(requestHeaders, 'authorization')\n const retention = fileVersion.fileRetention.value\n if (retention !== null && retention.mode !== null && retention.retainUntilTimestamp !== null) {\n if (this.requestHasCapability(authToken, Capability.ReadFileRetentions)) {\n headers[DownloadHeaderName.FileRetentionMode] = retention.mode\n headers[DownloadHeaderName.FileRetentionRetainUntilTimestamp] = String(\n retention.retainUntilTimestamp,\n )\n } else {\n unauthorizedToRead.push(\n DownloadClientUnauthorizedToReadMarker.FileRetentionMode,\n DownloadClientUnauthorizedToReadMarker.FileRetentionRetainUntilTimestamp,\n )\n }\n }\n\n const legalHold = fileVersion.legalHold.value\n if (legalHold === null) return\n if (this.requestHasCapability(authToken, Capability.ReadFileLegalHolds)) {\n headers[DownloadHeaderName.FileLegalHold] = legalHold\n return\n }\n unauthorizedToRead.push(DownloadClientUnauthorizedToReadMarker.FileLegalHold)\n }\n\n // --- API handlers ---\n\n private authorize(authzHeader?: string, origin = 'http://localhost:0'): SimulatorJsonResponse {\n // Token validity: real B2 = 24h; configurable via `authTokenTtlMs`.\n // The issued token and response body both inherit the exact grant\n // resolved from the Basic credentials used for this authorize call.\n const resolution = this.findAuthorizationGrant(authzHeader)\n if (resolution.kind === 'invalid') {\n return this.error(401, 'unauthorized', 'application key is not valid')\n }\n if (resolution.kind === 'expired') {\n return this.error(401, 'unauthorized', 'application key is expired')\n }\n const { grant } = resolution\n const allowedBuckets = this.allowedBuckets(grant.bucketIds)\n const legacyBucketId = singleBucketId(grant.bucketIds)\n const legacyBucketName =\n legacyBucketId === null ? null : (this.buckets.get(legacyBucketId)?.info.bucketName ?? null)\n const tokenStr = `sim_auth_token_${this.nextId++}`\n this.issuedTokens.set(tokenStr, {\n accountId: grant.accountId,\n capabilities: grant.capabilities,\n bucketIds: grant.bucketIds,\n namePrefix: grant.namePrefix,\n expiresAt: this.now() + this.authTokenTtlMs,\n applicationKeyId: grant.applicationKeyId,\n })\n return {\n status: 200,\n body: {\n accountId: accountIdOf(grant.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: origin,\n bucketId: legacyBucketId === null ? null : bucketIdOf(legacyBucketId),\n bucketName: legacyBucketName,\n downloadUrl: origin,\n infoType: 'storageApi',\n namePrefix: grant.namePrefix,\n recommendedPartSize: this.recommendedPartSize,\n s3ApiUrl: origin,\n allowed: {\n capabilities: cloneCapabilities(grant.capabilities),\n buckets: allowedBuckets,\n bucketId: legacyBucketId === null ? null : bucketIdOf(legacyBucketId),\n bucketName: legacyBucketName,\n namePrefix: grant.namePrefix,\n },\n },\n },\n applicationKeyExpirationTimestamp: grant.expirationTimestamp,\n },\n }\n }\n\n private createBucket(\n req: {\n bucketName: string\n bucketType: BucketType\n accountId: string\n bucketInfo?: Record<string, string>\n corsRules?: BucketInfo['corsRules']\n defaultServerSideEncryption?: BucketDefaultServerSideEncryptionSetting\n defaultRetention?: BucketRetentionPolicy\n fileLockEnabled?: boolean\n lifecycleRules?: BucketInfo['lifecycleRules']\n replicationConfiguration?: ReplicationConfiguration\n },\n authToken?: string,\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 const bucketTypeError = validateBucketType(req.bucketType)\n if (bucketTypeError) return this.error(400, bucketTypeError.code, bucketTypeError.message)\n const objectLockEnabled = nextObjectLockEnabled(false, req.fileLockEnabled)\n if (typeof objectLockEnabled !== 'boolean') {\n return this.error(400, objectLockEnabled.code, objectLockEnabled.message)\n }\n const configError = validateBucketConfigurationFields(req, { objectLockEnabled })\n if (configError) return this.error(400, configError.code, configError.message)\n const replicationKeyError = this.validateReplicationApplicationKeys(\n req.replicationConfiguration,\n undefined,\n )\n if (replicationKeyError !== null) return replicationKeyError\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 = bucketDefaultRetentionForResponse(req.defaultRetention)\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: publicBucketDefaultServerSideEncryption(\n req.defaultServerSideEncryption,\n ),\n fileLockConfiguration: {\n isClientAuthorizedToRead: true,\n value: {\n isFileLockEnabled: objectLockEnabled,\n defaultRetention,\n },\n },\n lifecycleRules: req.lifecycleRules ?? [],\n options: [],\n revision: 1,\n replicationConfiguration: wrapReplicationConfiguration(req.replicationConfiguration),\n }\n this.buckets.set(bid, { info, files: new Map() })\n return { status: 200, body: this.bucketInfoForResponse(info, authToken) }\n }\n\n private listBuckets(\n req: {\n bucketId?: string\n bucketName?: string\n bucketTypes?: BucketTypesFilter\n },\n authToken?: string,\n ): SimulatorJsonResponse {\n const bucketTypesError = validateBucketTypes(req.bucketTypes)\n if (bucketTypesError) return this.error(400, bucketTypesError.code, bucketTypesError.message)\n const buckets = [...this.buckets.values()]\n .map((b) => b.info)\n .filter((bucket) => req.bucketId === undefined || bucket.bucketId === req.bucketId)\n .filter((bucket) => req.bucketName === undefined || bucket.bucketName === req.bucketName)\n .filter(\n (bucket) =>\n req.bucketTypes === undefined ||\n req.bucketTypes[0] === 'all' ||\n req.bucketTypes.some((bucketType) => bucketType === bucket.bucketType),\n )\n .map((bucket) => this.bucketInfoForResponse(bucket, authToken))\n return { status: 200, body: { buckets } }\n }\n\n private deleteBucket(req: { bucketId: string }, authToken?: string): SimulatorJsonResponse {\n const bucket = this.buckets.get(req.bucketId)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n if (this.bucketHasContents(bucket)) {\n return this.error(\n 400,\n 'cannot_delete_non_empty_bucket',\n 'Bucket is not empty and cannot be deleted',\n )\n }\n this.buckets.delete(req.bucketId)\n return { status: 200, body: this.bucketInfoForResponse(bucket.info, authToken) }\n }\n\n private bucketHasContents(bucket: StoredBucket): boolean {\n for (const versions of bucket.files.values()) {\n if (versions.length > 0) return true\n }\n for (const large of this.largeFiles.values()) {\n if (large.bucketId === bucket.info.bucketId) return true\n }\n return false\n }\n\n private updateBucket(req: Record<string, unknown>, authToken?: string): 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 const revisionGuard = req['ifRevisionIs']\n if (revisionGuard !== undefined) {\n if (typeof revisionGuard !== 'number' || !Number.isInteger(revisionGuard)) {\n return this.error(400, 'bad_request', 'ifRevisionIs must be an integer')\n }\n if (revisionGuard !== bucket.info.revision) {\n return this.error(409, 'conflict', 'ifRevisionIs test failed')\n }\n }\n const currentObjectLockEnabled =\n bucket.info.fileLockConfiguration.value?.isFileLockEnabled ?? false\n const objectLockEnabled = nextObjectLockEnabled(\n currentObjectLockEnabled,\n req['fileLockEnabled'],\n )\n if (typeof objectLockEnabled !== 'boolean') {\n return this.error(400, objectLockEnabled.code, objectLockEnabled.message)\n }\n if (req['bucketType'] !== undefined) {\n const bucketTypeError = validateBucketType(req['bucketType'])\n if (bucketTypeError) return this.error(400, bucketTypeError.code, bucketTypeError.message)\n }\n const configError = validateBucketConfigurationFields(\n {\n bucketInfo: req['bucketInfo'],\n corsRules: req['corsRules'],\n defaultServerSideEncryption: req['defaultServerSideEncryption'],\n defaultRetention: req['defaultRetention'],\n lifecycleRules: req['lifecycleRules'],\n replicationConfiguration: req['replicationConfiguration'],\n },\n { objectLockEnabled },\n )\n if (configError) return this.error(400, configError.code, configError.message)\n const replicationKeyError = this.validateReplicationApplicationKeys(\n req['replicationConfiguration'],\n req['bucketId'] as string,\n )\n if (replicationKeyError !== null) return replicationKeyError\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: wrapReplicationConfiguration(\n req['replicationConfiguration'] as ReplicationConfiguration,\n ),\n }\n : {}),\n ...(req['defaultRetention'] !== undefined\n ? {\n fileLockConfiguration: {\n isClientAuthorizedToRead: true,\n value: {\n isFileLockEnabled: objectLockEnabled,\n defaultRetention: bucketDefaultRetentionForResponse(\n req['defaultRetention'] as BucketRetentionPolicy,\n ),\n },\n },\n }\n : {}),\n ...(req['fileLockEnabled'] !== undefined && req['defaultRetention'] === undefined\n ? {\n fileLockConfiguration: {\n isClientAuthorizedToRead: true,\n value: {\n isFileLockEnabled: objectLockEnabled,\n defaultRetention: bucketDefaultRetention(bucket.info) ?? noBucketDefaultRetention(),\n },\n },\n }\n : {}),\n ...(req['defaultServerSideEncryption'] !== undefined\n ? {\n defaultServerSideEncryption: publicBucketDefaultServerSideEncryption(\n req['defaultServerSideEncryption'] as BucketDefaultServerSideEncryptionSetting,\n ),\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: this.bucketInfoForResponse(updated, authToken) }\n }\n\n private async getUploadUrl(\n req: { bucketId: string },\n authToken?: string,\n ): Promise<SimulatorJsonResponse> {\n const bucket = this.buckets.get(req.bucketId)\n if (!bucket) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n const uploadAuth = await this.issueUploadAuthorization({\n kind: 'file',\n sourceAuthToken: authToken,\n bucketId: req.bucketId,\n })\n return {\n status: 200,\n body: {\n bucketId: req.bucketId,\n uploadUrl: uploadAuth.uploadUrl,\n authorizationToken: uploadAuth.authorizationToken,\n },\n }\n }\n\n private listFileNames(req: {\n bucketId: string\n delimiter?: 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 only visible latest versions.\n // Hide markers remain available through `b2_list_file_versions`, but do\n // not appear directly here and do not create delimiter folder rows.\n const currentVersions = [...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 .filter((v) => v.action !== FileAction.Hide)\n .sort((a, b) => compareB2FileNames(a.fileName, b.fileName))\n\n let allFiles = this.applyListDelimiter({\n bucketId: req.bucketId,\n delimiter: req.delimiter,\n entries: currentVersions,\n prefix,\n })\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 delimiter?: 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 versions = [...bucket.files.entries()]\n .filter(([name]) => name.startsWith(prefix))\n .flatMap(([_, versions]) => versions.map((v) => v.fileVersion))\n .sort((a, b) => {\n const nameCmp = compareB2FileNames(a.fileName, b.fileName)\n if (nameCmp !== 0) return nameCmp\n return b.uploadTimestamp - a.uploadTimestamp\n })\n const allVersions = this.applyListDelimiter({\n bucketId: req.bucketId,\n delimiter: req.delimiter,\n entries: versions,\n prefix,\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 applyListDelimiter(params: {\n bucketId: string\n delimiter: string | undefined\n entries: readonly FileVersion[]\n prefix: string\n }): FileVersionListEntry[] {\n if (params.delimiter === undefined || params.delimiter === '') {\n return params.entries.map((entry) => this.toListEntry(entry))\n }\n\n const grouped: FileVersionListEntry[] = []\n const seenFolders = new Set<string>()\n for (const entry of params.entries) {\n const delimiterIndex = entry.fileName.indexOf(params.delimiter, params.prefix.length)\n if (delimiterIndex === -1) {\n grouped.push(this.toListEntry(entry))\n continue\n }\n\n const folderName = entry.fileName.slice(0, delimiterIndex + params.delimiter.length)\n if (!seenFolders.has(folderName)) {\n seenFolders.add(folderName)\n grouped.push(this.makeFolderListEntry(params.bucketId, folderName))\n }\n }\n\n return grouped.sort(compareFileVersionListEntries)\n }\n\n private toListEntry(entry: FileVersion): FileVersionListEntry {\n if (entry.action !== FileAction.Hide) return entry as ListedFileVersion\n const {\n contentType: _contentType,\n fileRetention,\n legalHold,\n serverSideEncryption,\n ...listed\n } = entry\n void _contentType\n void fileRetention\n void legalHold\n void serverSideEncryption\n return { ...listed, action: FileAction.Hide, contentType: HIDE_MARKER_CONTENT_TYPE }\n }\n\n private makeFolderListEntry(bucketId: string, fileName: string): FolderFileVersion {\n return {\n accountId: accountIdOf(this.accountId),\n action: FileAction.Folder,\n bucketId: bucketIdOf(bucketId),\n contentLength: 0,\n contentMd5: null,\n contentSha1: null,\n contentType: null,\n fileId: null,\n fileInfo: {},\n fileName,\n uploadTimestamp: 0,\n }\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: HIDE_MARKER_CONTENT_TYPE,\n contentLength: 0,\n contentSha1: 'none',\n action: FileAction.Hide,\n })\n const existing = bucket.files.get(req.fileName)\n const stored: StoredFile = {\n fileVersion,\n data: new Uint8Array(0),\n serverSideEncryption: { mode: EncryptionMode.None },\n }\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(\n req: {\n fileId: string\n fileName: string\n bypassGovernance?: boolean\n },\n authToken?: string,\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 === LegalHoldValue.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 === RetentionMode.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 === RetentionMode.Governance &&\n retention.retainUntilTimestamp !== null &&\n retention.retainUntilTimestamp > now\n ) {\n const bypassError = this.requireGovernanceBypass(\n req,\n authToken,\n 'File is under governance-mode retention; pass bypassGovernance: true to delete',\n )\n if (bypassError !== null) return bypassError\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 async copyFile(req: {\n sourceFileId: string\n fileName: string\n destinationBucketId?: string\n range?: string\n metadataDirective?: string\n contentType?: string\n fileInfo?: Record<string, string>\n sourceServerSideEncryption?: EncryptionSetting\n destinationServerSideEncryption?: EncryptionSetting\n }): Promise<{ status: number; body: unknown }> {\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 sourceEncryptionError = await this.validateSourceServerSideEncryption(\n sourceStored,\n req.sourceServerSideEncryption,\n )\n if (sourceEncryptionError !== null) return sourceEncryptionError\n\n const destinationEncryption = await this.resolveCopyDestinationEncryption(\n destBucket,\n req.destinationServerSideEncryption,\n )\n if ('status' in destinationEncryption) return destinationEncryption\n\n // Honor an optional byte range: copy only the requested slice. The copied\n // content differs from the source, so its SHA-1 is recomputed; a full copy\n // preserves the source's stored SHA-1 (including 'none' for large files).\n let data = sourceStored.data\n let contentSha1 = sourceStored.fileVersion.contentSha1 ?? 'none'\n if (req.range !== undefined) {\n const parsed = parseRangeHeader(req.range, sourceStored.data.byteLength)\n // B2 returns 416 for a well-formed-but-unsatisfiable range; a malformed\n // range is an invalid request field, so 400 bad_request.\n if (parsed.kind === 'malformed') {\n return this.error(400, 'bad_request', `Malformed copy range: ${req.range}`)\n }\n if (parsed.kind === 'unsatisfiable') {\n return this.error(416, 'range_not_satisfiable', `Unsatisfiable copy range: ${req.range}`)\n }\n data = sourceStored.data.subarray(parsed.start, parsed.end + 1)\n contentSha1 = await sha1Hex(data)\n }\n\n // Metadata directive: COPY (default) preserves the source's contentType +\n // fileInfo and forbids replacement metadata; REPLACE applies the request's\n // (contentType required, fileInfo validated). Real B2 rejects an unknown\n // directive, and rejects contentType/fileInfo supplied in COPY mode.\n const directive = req.metadataDirective\n if (directive !== undefined && directive !== 'COPY' && directive !== 'REPLACE') {\n return this.error(400, 'bad_request', `Invalid metadataDirective: ${directive}`)\n }\n let contentType: string\n let fileInfo: Record<string, string>\n if (directive === 'REPLACE') {\n if (req.contentType === undefined) {\n return this.error(\n 400,\n 'bad_request',\n 'contentType is required when metadataDirective is REPLACE',\n )\n }\n const replaceFileInfo = req.fileInfo ?? {}\n const fileInfoError = validateFileInfo(replaceFileInfo)\n if (fileInfoError) return this.error(400, fileInfoError.code, fileInfoError.message)\n contentType = req.contentType\n fileInfo = replaceFileInfo\n } else {\n if (req.contentType !== undefined || req.fileInfo !== undefined) {\n return this.error(\n 400,\n 'bad_request',\n 'contentType and fileInfo may only be set when metadataDirective is REPLACE',\n )\n }\n contentType = sourceStored.fileVersion.contentType\n fileInfo = sourceStored.fileVersion.fileInfo\n }\n\n const fileVersion = this.makeFileVersion({\n bucketId: destBucketId,\n fileName: req.fileName,\n contentType,\n contentLength: data.byteLength,\n contentSha1,\n fileInfo,\n action: FileAction.Copy,\n serverSideEncryption: destinationEncryption,\n ...this.newReplicationStatusField(destBucketId, req.fileName),\n })\n const copied: StoredFile = {\n fileVersion,\n data: new Uint8Array(data),\n serverSideEncryption: destinationEncryption,\n }\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 async startLargeFile(\n req: {\n bucketId: string\n fileName: string\n contentType: string\n customUploadTimestamp?: string | null\n fileInfo?: Record<string, string>\n fileRetention?: FileRetentionValue\n legalHold?: LegalHoldValue\n serverSideEncryption?: EncryptionSetting\n },\n authToken?: string,\n ): Promise<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 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 customUploadTimestamp = this.parseCustomUploadTimestamp(req.customUploadTimestamp)\n if ('status' in customUploadTimestamp) return customUploadTimestamp\n\n const fid = this.genId('4_z')\n const uploadTimestamp = customUploadTimestamp.timestamp ?? this.monotonicTimestamp()\n const serverSideEncryption = await storedServerSideEncryption(\n req.serverSideEncryption ?? bucketDefaultServerSideEncryptionForStorage(bucket.info),\n )\n if ('status' in serverSideEncryption) return serverSideEncryption\n const replicationStatus = this.replicationStatusForNewFile(req.bucketId, req.fileName)\n const large: LargeFileInProgress = {\n fileId: fid,\n bucketId: req.bucketId,\n fileName: req.fileName,\n contentType: req.contentType,\n fileInfo: req.fileInfo ?? {},\n fileRetention:\n req.fileRetention ??\n defaultFileRetention(bucketDefaultRetention(bucket.info), uploadTimestamp),\n legalHold: req.legalHold ?? null,\n ...this.replicationStatusMetadataFor(replicationStatus),\n serverSideEncryption,\n uploadTimestamp,\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 action: FileAction.Start,\n contentLength: 0,\n contentSha1: 'none',\n contentMd5: null,\n fileRetention: readableFileRetention(\n large.fileRetention,\n this.requestHasCapability(authToken, Capability.ReadFileRetentions),\n ),\n legalHold: readableLegalHold(\n large.legalHold,\n this.requestHasCapability(authToken, Capability.ReadFileLegalHolds),\n ),\n ...this.replicationStatusMetadataFor(large.replicationStatus),\n serverSideEncryption: publicServerSideEncryption(large.serverSideEncryption),\n uploadTimestamp: large.uploadTimestamp,\n },\n }\n }\n\n private async getUploadPartUrl(\n req: { fileId: string },\n authToken?: string,\n ): Promise<SimulatorJsonResponse> {\n const large = this.largeFiles.get(req.fileId)\n if (large === undefined) return this.error(400, 'bad_request', 'Large file not found')\n const uploadAuth = await this.issueUploadAuthorization({\n kind: 'part',\n sourceAuthToken: authToken,\n fileId: req.fileId,\n fileName: large.fileName,\n })\n return {\n status: 200,\n body: {\n fileId: req.fileId,\n uploadUrl: uploadAuth.uploadUrl,\n authorizationToken: uploadAuth.authorizationToken,\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 > B2_MAX_PART_NUMBER) {\n return this.error(\n 400,\n 'bad_request',\n `multipart upload has ${sortedParts.length} parts; B2 caps at ${B2_MAX_PART_NUMBER}`,\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 or older simulator\n // state already contains invalid part numbers.\n for (const [partNumber] of sortedParts) {\n if (\n !Number.isInteger(partNumber) ||\n partNumber < B2_MIN_PART_NUMBER ||\n partNumber > B2_MAX_PART_NUMBER\n ) {\n return this.error(\n 400,\n 'bad_request',\n `partNumber ${partNumber} is outside the [${B2_MIN_PART_NUMBER}, ${B2_MAX_PART_NUMBER}] 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: partSha1Array is the ordered checksum list that\n // confirms the right parts were uploaded in the right order. Compare each\n // entry against the stored part's SHA-1; real B2 rejects a mismatch with\n // `bad_request`.\n for (const [i, [partNumber, part]] of sortedParts.entries()) {\n if (req.partSha1Array[i]?.toLowerCase() !== part.sha1) {\n return this.error(\n 400,\n 'bad_request',\n `part ${partNumber} SHA-1 does not match the uploaded part`,\n )\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 (const [partNumber, part] of sortedParts.slice(0, -1)) {\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 fileInfo: large.fileInfo,\n action: FileAction.Upload,\n fileRetention: large.fileRetention,\n legalHold: large.legalHold,\n serverSideEncryption: large.serverSideEncryption,\n uploadTimestamp: large.uploadTimestamp,\n ...this.replicationStatusMetadataFor(large.replicationStatus),\n })\n const stored: StoredFile = {\n fileVersion,\n data: combined,\n serverSideEncryption: large.serverSideEncryption,\n }\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(\n req: {\n bucketId: string\n namePrefix?: string\n startFileId?: string\n maxFileCount?: number\n },\n authToken?: string,\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 // Keep listing order deterministic; resume sorts scanned exact-name\n // matches by uploadTimestamp before selecting one.\n const candidates = [...this.largeFiles.values()]\n .filter((f) => f.bucketId === req.bucketId)\n .filter((f) => f.fileName.startsWith(prefix))\n .sort((a, b) => compareB2FileNames(a.fileName, b.fileName))\n\n // `startFileId` is the inclusive cursor returned from a prior page.\n // When present in the current listing, that entry is returned first.\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 canReadFileRetentions = this.requestHasCapability(\n authToken,\n Capability.ReadFileRetentions,\n )\n const canReadFileLegalHolds = this.requestHasCapability(\n authToken,\n Capability.ReadFileLegalHolds,\n )\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 action: FileAction.Start,\n contentLength: 0,\n contentSha1: 'none',\n contentMd5: null,\n fileInfo: f.fileInfo,\n fileRetention: readableFileRetention(f.fileRetention, canReadFileRetentions),\n legalHold: readableLegalHold(f.legalHold, canReadFileLegalHolds),\n ...this.replicationStatusMetadataFor(f.replicationStatus),\n serverSideEncryption: publicServerSideEncryption(f.serverSideEncryption),\n uploadTimestamp: f.uploadTimestamp,\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 contentMd5: part.contentMd5,\n serverSideEncryption: publicServerSideEncryption(large.serverSideEncryption),\n uploadTimestamp: part.uploadTimestamp,\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 sourceServerSideEncryption?: EncryptionSetting\n destinationServerSideEncryption?: EncryptionSetting\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 const partNumberError = this.validatePartNumber(req.partNumber)\n if (partNumberError !== null) return partNumberError\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 const sourceEncryptionError = await this.validateSourceServerSideEncryption(\n sourceStored,\n req.sourceServerSideEncryption,\n )\n if (sourceEncryptionError !== null) return sourceEncryptionError\n\n const destinationEncryptionError = await this.validateCopyPartDestinationEncryption(\n large,\n req.destinationServerSideEncryption,\n )\n if (destinationEncryptionError !== null) return destinationEncryptionError\n\n let partData = sourceStored.data\n if (req.range !== undefined) {\n const parsed = parseRangeHeader(req.range, sourceStored.data.byteLength)\n if (parsed.kind === 'malformed') {\n return this.error(400, 'bad_request', `Malformed copy range: ${req.range}`)\n }\n if (parsed.kind === 'unsatisfiable') {\n return this.error(416, 'range_not_satisfiable', `Unsatisfiable copy range: ${req.range}`)\n }\n partData = sourceStored.data.subarray(parsed.start, parsed.end + 1)\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 const uploadTimestamp = this.monotonicTimestamp()\n large.parts.set(req.partNumber, {\n data: new Uint8Array(partData),\n sha1,\n contentMd5: null,\n uploadTimestamp,\n })\n\n return {\n status: 200,\n body: {\n fileId: req.largeFileId,\n partNumber: req.partNumber,\n contentLength: partData.byteLength,\n contentSha1: sha1,\n contentMd5: null,\n serverSideEncryption: publicServerSideEncryption(large.serverSideEncryption),\n uploadTimestamp,\n },\n }\n }\n\n private getDownloadAuthorization(req: DownloadAuthorizationRequestBody): SimulatorJsonResponse {\n this.purgeExpiredDownloadAuthorizationTokens()\n if (!this.buckets.has(req.bucketId)) return this.error(400, 'bad_bucket_id', 'Bucket not found')\n const prefixError = validateDownloadAuthorizationPrefix(req.fileNamePrefix)\n if (prefixError) return this.error(400, prefixError.code, prefixError.message)\n const durationError = validateDownloadAuthorizationDuration(req.validDurationInSeconds)\n if (durationError) return this.error(400, durationError.code, durationError.message)\n const overrideError = validateDownloadResponseOverrides(req)\n if (overrideError) return this.error(400, overrideError.code, overrideError.message)\n let authorizationToken = randomDownloadAuthorizationToken()\n while (this.downloadAuthorizationTokens.has(authorizationToken)) {\n authorizationToken = randomDownloadAuthorizationToken()\n }\n const expiresAt = this.now() + req.validDurationInSeconds * 1000\n this.downloadAuthorizationTokens.set(authorizationToken, {\n bucketId: req.bucketId,\n fileNamePrefix: req.fileNamePrefix,\n expiresAt,\n responseHeaderOverrides: pickDownloadResponseOverrides(req),\n })\n this.pushDownloadAuthorizationExpiry({ token: authorizationToken, expiresAt })\n return {\n status: 200,\n body: {\n bucketId: req.bucketId,\n fileNamePrefix: req.fileNamePrefix,\n authorizationToken,\n },\n }\n }\n\n // --- Keys ---\n\n private createKeyCapabilitiesOutsideGrant(\n authToken: string | undefined,\n requested: readonly Capability[],\n ): readonly Capability[] {\n // In default mode, unknown auth tokens keep the simulator permissive. When the\n // token is known, preserve the creator's real grant so delegated keys cannot\n // exceed it even without full strict-auth request enforcement.\n const creator = authToken === undefined ? undefined : this.issuedTokens.get(authToken)\n if (creator === undefined) return this.strictAuth ? requested : []\n // The simulator's implicit master credential may mint any valid capability.\n if (creator.applicationKeyId === null) return []\n const granted = new Set(creator.capabilities)\n return requested.filter((capability) => !granted.has(capability))\n }\n\n private createKeyCreatorAccountId(authToken: string | undefined): string {\n const creator = authToken === undefined ? undefined : this.issuedTokens.get(authToken)\n return creator?.accountId ?? this.accountId\n }\n\n private createKey(\n req: {\n accountId: string\n capabilities: unknown\n keyName: unknown\n validDurationInSeconds?: number\n bucketIds?: readonly string[] | null\n bucketId?: string\n namePrefix?: string\n },\n version: B2ApiVersion,\n authToken: string | undefined,\n ): SimulatorJsonResponse {\n if (version === 'v4' && hasOwnField(req, 'bucketId')) {\n return this.error(\n 400,\n 'bad_request',\n 'bucketId is not accepted by v4 b2_create_key; use bucketIds',\n )\n }\n if (version !== 'v4' && req.bucketId !== undefined && req.bucketIds !== undefined) {\n return this.error(400, 'bad_request', 'b2_create_key accepts either bucketIds or bucketId')\n }\n const bucketIds =\n version !== 'v4' && req.bucketId !== undefined\n ? Object.freeze([req.bucketId])\n : normalizeKeyBucketIds(req)\n const namePrefix = req.namePrefix === undefined || req.namePrefix === '' ? null : req.namePrefix\n const creatorAccountId = this.createKeyCreatorAccountId(authToken)\n if (req.accountId !== creatorAccountId) {\n return this.error(400, 'bad_request', 'accountId must match authorized account')\n }\n const keyNameError = validateCreateKeyName(req.keyName)\n if (keyNameError) return this.error(400, keyNameError.code, keyNameError.message)\n const keyName = req.keyName as string\n const capabilitiesError = validateCreateKeyCapabilities(req.capabilities)\n if (capabilitiesError) return this.error(400, capabilitiesError.code, capabilitiesError.message)\n const capabilities = normalizeCreateKeyCapabilities(req.capabilities as readonly Capability[])\n const unauthorizedCapabilities = this.createKeyCapabilitiesOutsideGrant(authToken, capabilities)\n if (unauthorizedCapabilities.length > 0) {\n return this.error(\n 400,\n 'bad_request',\n `requested capabilities exceed creator grant: ${unauthorizedCapabilities.join(', ')}`,\n )\n }\n if (hasKeyManagementCapability(capabilities) && (bucketIds !== null || namePrefix !== null)) {\n return this.error(\n 400,\n 'bad_request',\n 'key-management capabilities are account-level and cannot be bucket or name-prefix scoped',\n )\n }\n const kid = this.genId('sim_key')\n const appKey = this.genId('sim_secret')\n const expiration =\n req.validDurationInSeconds !== undefined\n ? this.now() + req.validDurationInSeconds * 1000\n : null\n const stored: StoredKey = {\n applicationKeyId: kid,\n keyName,\n capabilities,\n accountId: creatorAccountId,\n applicationKey: appKey,\n bucketIds,\n namePrefix,\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: cloneCapabilities(stored.capabilities),\n accountId: stored.accountId,\n expirationTimestamp: stored.expirationTimestamp,\n bucketIds: cloneBucketIds(stored.bucketIds),\n bucketId: singleBucketId(stored.bucketIds),\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: cloneCapabilities(k.capabilities),\n accountId: k.accountId,\n expirationTimestamp: k.expirationTimestamp,\n bucketIds: cloneBucketIds(k.bucketIds),\n bucketId: singleBucketId(k.bucketIds),\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 for (const [token, scope] of this.uploadTokens.entries()) {\n if (scope.applicationKeyId === req.applicationKeyId) {\n scope.invalidated = true\n this.uploadTokens.set(token, scope)\n }\n }\n return {\n status: 200,\n body: {\n keyName: key.keyName,\n applicationKeyId: key.applicationKeyId,\n capabilities: cloneCapabilities(key.capabilities),\n accountId: key.accountId,\n expirationTimestamp: key.expirationTimestamp,\n bucketIds: cloneBucketIds(key.bucketIds),\n bucketId: singleBucketId(key.bucketIds),\n namePrefix: key.namePrefix,\n options: [],\n },\n }\n }\n\n // --- File lock ---\n\n private updateFileRetention(\n req: {\n fileName: string\n fileId: string\n fileRetention: unknown\n bypassGovernance?: boolean\n },\n authToken?: 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 const fileLockError = this.requireFileLockEnabled(found.bucket)\n if (fileLockError !== null) return fileLockError\n\n const fileRetention = parseFileRetentionValue(req.fileRetention, Date.now())\n if (fileRetention === null) {\n return this.error(\n 400,\n 'bad_request',\n 'fileRetention must use a valid mode and retainUntilTimestamp pair',\n )\n }\n\n const current = found.stored.fileVersion.fileRetention?.value\n if (\n current?.mode === RetentionMode.Compliance &&\n (fileRetention.mode !== RetentionMode.Compliance ||\n isRetentionWeakened(current, fileRetention))\n ) {\n return this.error(\n 400,\n 'file_lock_compliance_protected',\n 'Compliance-mode retention cannot be shortened or removed',\n )\n }\n if (requiresGovernanceBypass(current, fileRetention)) {\n const bypassError = this.requireGovernanceBypass(\n req,\n authToken,\n 'Governance-mode retention cannot be shortened, removed, or converted without bypassGovernance',\n )\n if (bypassError !== null) return bypassError\n }\n\n found.versions[found.index] = {\n fileVersion: {\n ...found.stored.fileVersion,\n fileRetention: { isClientAuthorizedToRead: true, value: fileRetention },\n },\n data: found.stored.data,\n serverSideEncryption: found.stored.serverSideEncryption,\n }\n return {\n status: 200,\n body: {\n fileName: req.fileName,\n fileId: req.fileId,\n 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 const fileLockError = this.requireFileLockEnabled(found.bucket)\n if (fileLockError !== null) return fileLockError\n\n if (req.legalHold !== LegalHoldValue.On && req.legalHold !== LegalHoldValue.Off) {\n return this.error(400, 'bad_request', 'legalHold must be \"on\" or \"off\"')\n }\n found.versions[found.index] = {\n fileVersion: {\n ...found.stored.fileVersion,\n legalHold: {\n isClientAuthorizedToRead: true,\n value: req.legalHold,\n },\n },\n data: found.stored.data,\n serverSideEncryption: found.stored.serverSideEncryption,\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 const rulesError = validateNotificationRules(req.eventNotificationRules)\n if (rulesError) return this.error(400, rulesError.code, rulesError.message)\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: ConcreteFileAction\n readonly fileInfo?: Record<string, string>\n readonly fileRetention?: FileRetentionValue | null\n readonly legalHold?: LegalHoldValue | null\n readonly replicationStatus?: ReplicationStatus\n readonly serverSideEncryption?: EncryptionSetting | StoredServerSideEncryption\n readonly uploadTimestamp?: number\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: params.fileInfo ?? {},\n fileName: params.fileName,\n fileRetention: { isClientAuthorizedToRead: true, value: params.fileRetention ?? null },\n legalHold: { isClientAuthorizedToRead: true, value: params.legalHold ?? null },\n ...this.replicationStatusMetadataFor(params.replicationStatus),\n serverSideEncryption: publicServerSideEncryption(\n params.serverSideEncryption ?? { mode: EncryptionMode.None },\n ),\n uploadTimestamp: params.uploadTimestamp ?? this.monotonicTimestamp(),\n }\n }\n\n private newReplicationStatusField(\n bucketId: string,\n fileName: string,\n ): { readonly replicationStatus?: ReplicationStatus } {\n return this.replicationStatusMetadataFor(this.replicationStatusForNewFile(bucketId, fileName))\n }\n\n private replicationStatusMetadataFor(replicationStatus: ReplicationStatus | undefined): {\n readonly replicationStatus?: ReplicationStatus\n } {\n return replicationStatus === undefined ? {} : { replicationStatus }\n }\n\n private replicationStatusForNewFile(\n bucketId: string,\n fileName: string,\n ): ReplicationStatus | undefined {\n return this.matchingEnabledReplicationRules(bucketId, fileName).length > 0\n ? 'PENDING'\n : undefined\n }\n\n private matchingEnabledReplicationRules(\n bucketId: string,\n fileName: string,\n ): readonly ReplicationRule[] {\n const bucket = this.buckets.get(bucketId)\n const sourceRules =\n bucket?.info.replicationConfiguration.value?.asReplicationSource?.replicationRules\n return (\n sourceRules?.filter((rule) => rule.isEnabled && fileName.startsWith(rule.fileNamePrefix)) ??\n []\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 for (const rule of this.matchingEnabledReplicationRules(bucketId, fileVersion.fileName)) {\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\nfunction rawUrlPathContainsLiteralBackslash(rawUrl: string): boolean {\n const schemeEnd = rawUrl.indexOf('://')\n const authorityStart = schemeEnd === -1 ? 0 : schemeEnd + 3\n\n const queryStart = rawUrl.indexOf('?', authorityStart)\n const hashStart = rawUrl.indexOf('#', authorityStart)\n const urlEndCandidates = [queryStart, hashStart].filter((index) => index !== -1)\n const urlEnd = urlEndCandidates.length === 0 ? rawUrl.length : Math.min(...urlEndCandidates)\n\n const slashStart = rawUrl.indexOf('/', authorityStart)\n const backslashStart = rawUrl.indexOf('\\\\', authorityStart)\n const pathStartCandidates = [slashStart, backslashStart].filter(\n (index) => index !== -1 && index < urlEnd,\n )\n if (pathStartCandidates.length === 0) return false\n\n const pathStart = Math.min(...pathStartCandidates)\n const queryStartAfterPath = rawUrl.indexOf('?', pathStart)\n const hashStartAfterPath = rawUrl.indexOf('#', pathStart)\n const pathEndCandidates = [queryStartAfterPath, hashStartAfterPath].filter(\n (index) => index !== -1,\n )\n const pathEnd = pathEndCandidates.length === 0 ? rawUrl.length : Math.min(...pathEndCandidates)\n return rawUrl.slice(pathStart, pathEnd).includes('\\\\')\n}\n\nfunction badRequestJson(message: string): SimulatorJsonResponse {\n return { status: 400, body: { status: 400, code: 'bad_request', message } }\n}\n\ntype UploadBodyReadResult =\n | { readonly data: Uint8Array }\n | { readonly error: SimulatorJsonResponse }\n\nfunction checkedUploadBody(data: Uint8Array, expectedLength: number | null): UploadBodyReadResult {\n if (expectedLength !== null && data.byteLength !== expectedLength) {\n return { error: badRequestJson(contentLengthMismatchMessage(expectedLength, data.byteLength)) }\n }\n return { data }\n}\n\nfunction copyBytes(bytes: Uint8Array): Uint8Array {\n const copy = new Uint8Array(bytes.byteLength)\n copy.set(bytes)\n return copy\n}\n\nfunction concatenateChunks(chunks: readonly Uint8Array[], totalLength: number): Uint8Array {\n if (chunks.length === 0) return new Uint8Array(0)\n if (chunks.length === 1) return copyBytes(chunks[0] as Uint8Array)\n const data = new Uint8Array(totalLength)\n let offset = 0\n for (const chunk of chunks) {\n data.set(chunk, offset)\n offset += chunk.byteLength\n }\n return data\n}\n\nasync function readStreamingUploadBody(\n stream: ReadableStream<Uint8Array>,\n expectedLength: number | null,\n): Promise<UploadBodyReadResult> {\n const reader = stream.getReader()\n const chunks: Uint8Array[] = []\n let totalLength = 0\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n if (value === undefined) continue\n const chunk = value instanceof Uint8Array ? value : new Uint8Array(value)\n totalLength += chunk.byteLength\n if (expectedLength !== null && totalLength > expectedLength) {\n await reader.cancel().catch(() => undefined)\n return {\n error: badRequestJson(contentLengthMismatchMessage(expectedLength, totalLength)),\n }\n }\n chunks.push(chunk)\n }\n } finally {\n reader.releaseLock()\n }\n if (expectedLength !== null && totalLength !== expectedLength) {\n return { error: badRequestJson(contentLengthMismatchMessage(expectedLength, totalLength)) }\n }\n return { data: concatenateChunks(chunks, totalLength) }\n}\n\nasync function readUploadBody(\n body: BodyInit,\n expectedLength: number | null,\n): Promise<UploadBodyReadResult> {\n if (body instanceof ReadableStream) return readStreamingUploadBody(body, expectedLength)\n if (body instanceof ArrayBuffer) {\n return checkedUploadBody(new Uint8Array(body), expectedLength)\n }\n if (ArrayBuffer.isView(body)) {\n const view = body as ArrayBufferView\n return checkedUploadBody(\n copyBytes(new Uint8Array(view.buffer, view.byteOffset, view.byteLength)),\n expectedLength,\n )\n }\n if (typeof Blob !== 'undefined' && body instanceof Blob) {\n if (expectedLength !== null && body.size !== expectedLength) {\n return { error: badRequestJson(contentLengthMismatchMessage(expectedLength, body.size)) }\n }\n }\n return checkedUploadBody(new Uint8Array(await new Response(body).arrayBuffer()), expectedLength)\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 if (rawUrlPathContainsLiteralBackslash(url)) {\n return buildFaultResponse({\n on: '\\\\',\n status: 400,\n code: 'bad_request',\n message: 'URL path must not contain literal backslashes',\n })\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 parsedUrl = new URL(url)\n const endpoint = parsedUrl.pathname.split('/').pop() ?? ''\n const isUpload = endpoint === 'b2_upload_file' || endpoint === 'b2_upload_part'\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 = await this.sim.handleDownload(\n parsedUrl.pathname + parsedUrl.search,\n headers,\n method,\n )\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 let decodedText: string | null = null\n const text = () => {\n decodedText ??= utf8Decoder.decode(data)\n return decodedText\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: <T>() => {\n try {\n return Promise.resolve(JSON.parse(text()) as T)\n } catch {\n return Promise.reject(new Error('Download response is not JSON'))\n }\n },\n text: () => Promise.resolve(text()),\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) {\n const parsedContentLength = parseContentLengthHeader(headers)\n if (parsedContentLength.kind === 'error') {\n result = badRequestJson(parsedContentLength.message)\n } else {\n const uploadBody = await readUploadBody(\n request.body ?? new Uint8Array(0),\n parsedContentLength.kind === 'ok' ? parsedContentLength.expectedLength : null,\n )\n result =\n 'error' in uploadBody\n ? uploadBody.error\n : await this.sim.handleUpload(url, headers, uploadBody.data)\n }\n } else {\n const requestPath = apiPathParts(parsedUrl.pathname)\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 } else if (\n request.method.toUpperCase() === 'GET' &&\n isJsonQueryEndpoint(requestPath.endpoint)\n ) {\n body = queryParamsBody(parsedUrl.searchParams, requestPath.endpoint)\n }\n result = await this.sim.handleRequest(\n request.method,\n parsedUrl.origin,\n parsedUrl.pathname,\n headers,\n body,\n )\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"],"mappings":"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"}
|