@backblaze-labs/b2-sdk 0.2.0 → 0.3.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 +42 -1
- package/README.md +20 -2
- package/dist/_virtual/_b2-sdk-version-json.cjs +1 -1
- package/dist/_virtual/_b2-sdk-version-json.js +1 -1
- package/dist/auth/file.cjs.map +1 -1
- package/dist/backup/client.cjs +206 -0
- package/dist/backup/client.cjs.map +1 -0
- package/dist/backup/client.d.cts +238 -0
- package/dist/backup/client.d.cts.map +1 -0
- package/dist/backup/client.d.ts +238 -0
- package/dist/backup/client.d.ts.map +1 -0
- package/dist/backup/client.js +206 -0
- package/dist/backup/client.js.map +1 -0
- package/dist/backup/index.cjs +15 -0
- package/dist/backup/index.d.cts +22 -0
- package/dist/backup/index.d.cts.map +1 -0
- package/dist/backup/index.d.ts +22 -0
- package/dist/backup/index.d.ts.map +1 -0
- package/dist/backup/index.js +7 -0
- package/dist/backup/raw.cjs +146 -0
- package/dist/backup/raw.cjs.map +1 -0
- package/dist/backup/raw.d.cts +122 -0
- package/dist/backup/raw.d.cts.map +1 -0
- package/dist/backup/raw.d.ts +122 -0
- package/dist/backup/raw.d.ts.map +1 -0
- package/dist/backup/raw.js +146 -0
- package/dist/backup/raw.js.map +1 -0
- package/dist/bucket.cjs +2 -1
- package/dist/bucket.cjs.map +1 -1
- package/dist/bucket.js +2 -1
- package/dist/bucket.js.map +1 -1
- package/dist/copy/large.cjs +5 -3
- package/dist/copy/large.cjs.map +1 -1
- package/dist/copy/large.d.cts.map +1 -1
- package/dist/copy/large.d.ts.map +1 -1
- package/dist/copy/large.js +5 -3
- package/dist/copy/large.js.map +1 -1
- package/dist/download/checksum.cjs +6 -2
- package/dist/download/checksum.cjs.map +1 -1
- package/dist/download/checksum.js +6 -2
- package/dist/download/checksum.js.map +1 -1
- package/dist/download/parallel.cjs +52 -15
- package/dist/download/parallel.cjs.map +1 -1
- package/dist/download/parallel.d.cts +8 -1
- package/dist/download/parallel.d.cts.map +1 -1
- package/dist/download/parallel.d.ts +8 -1
- package/dist/download/parallel.d.ts.map +1 -1
- package/dist/download/parallel.js +52 -15
- package/dist/download/parallel.js.map +1 -1
- package/dist/download/single.cjs +141 -6
- package/dist/download/single.cjs.map +1 -1
- package/dist/download/single.d.cts.map +1 -1
- package/dist/download/single.d.ts.map +1 -1
- package/dist/download/single.js +141 -6
- package/dist/download/single.js.map +1 -1
- package/dist/errors/index.cjs +173 -3
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +119 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +119 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +161 -4
- package/dist/errors/index.js.map +1 -1
- package/dist/http/retry.cjs +1 -1
- package/dist/http/retry.cjs.map +1 -1
- package/dist/http/retry.js +1 -1
- package/dist/http/retry.js.map +1 -1
- package/dist/http/transport.cjs +61 -23
- package/dist/http/transport.cjs.map +1 -1
- package/dist/http/transport.d.cts +25 -3
- package/dist/http/transport.d.cts.map +1 -1
- package/dist/http/transport.d.ts +25 -3
- package/dist/http/transport.d.ts.map +1 -1
- package/dist/http/transport.js +59 -22
- package/dist/http/transport.js.map +1 -1
- package/dist/index.cjs +25 -2
- package/dist/index.d.cts +6 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/object.cjs +1 -1
- package/dist/object.cjs.map +1 -1
- package/dist/object.d.cts +4 -2
- package/dist/object.d.cts.map +1 -1
- package/dist/object.d.ts +4 -2
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +1 -1
- package/dist/object.js.map +1 -1
- package/dist/partner/account-info.d.cts +43 -0
- package/dist/partner/account-info.d.cts.map +1 -0
- package/dist/partner/account-info.d.ts +43 -0
- package/dist/partner/account-info.d.ts.map +1 -0
- package/dist/partner/auth-clone.cjs +64 -0
- package/dist/partner/auth-clone.cjs.map +1 -0
- package/dist/partner/auth-clone.d.cts +27 -0
- package/dist/partner/auth-clone.d.cts.map +1 -0
- package/dist/partner/auth-clone.d.ts +27 -0
- package/dist/partner/auth-clone.d.ts.map +1 -0
- package/dist/partner/auth-clone.js +63 -0
- package/dist/partner/auth-clone.js.map +1 -0
- package/dist/partner/auth-core.cjs +175 -0
- package/dist/partner/auth-core.cjs.map +1 -0
- package/dist/partner/auth-core.d.cts +111 -0
- package/dist/partner/auth-core.d.cts.map +1 -0
- package/dist/partner/auth-core.d.ts +111 -0
- package/dist/partner/auth-core.d.ts.map +1 -0
- package/dist/partner/auth-core.js +175 -0
- package/dist/partner/auth-core.js.map +1 -0
- package/dist/partner/auth-shape.cjs +36 -0
- package/dist/partner/auth-shape.cjs.map +1 -0
- package/dist/partner/auth-shape.d.cts +12 -0
- package/dist/partner/auth-shape.d.cts.map +1 -0
- package/dist/partner/auth-shape.d.ts +12 -0
- package/dist/partner/auth-shape.d.ts.map +1 -0
- package/dist/partner/auth-shape.js +36 -0
- package/dist/partner/auth-shape.js.map +1 -0
- package/dist/partner/client.cjs +255 -0
- package/dist/partner/client.cjs.map +1 -0
- package/dist/partner/client.d.cts +335 -0
- package/dist/partner/client.d.cts.map +1 -0
- package/dist/partner/client.d.ts +335 -0
- package/dist/partner/client.d.ts.map +1 -0
- package/dist/partner/client.js +255 -0
- package/dist/partner/client.js.map +1 -0
- package/dist/partner/endpoint-url.cjs +108 -0
- package/dist/partner/endpoint-url.cjs.map +1 -0
- package/dist/partner/endpoint-url.d.cts +72 -0
- package/dist/partner/endpoint-url.d.cts.map +1 -0
- package/dist/partner/endpoint-url.d.ts +72 -0
- package/dist/partner/endpoint-url.d.ts.map +1 -0
- package/dist/partner/endpoint-url.js +105 -0
- package/dist/partner/endpoint-url.js.map +1 -0
- package/dist/partner/in-memory.cjs +135 -0
- package/dist/partner/in-memory.cjs.map +1 -0
- package/dist/partner/in-memory.d.cts +99 -0
- package/dist/partner/in-memory.d.cts.map +1 -0
- package/dist/partner/in-memory.d.ts +99 -0
- package/dist/partner/in-memory.d.ts.map +1 -0
- package/dist/partner/in-memory.js +135 -0
- package/dist/partner/in-memory.js.map +1 -0
- package/dist/partner/index.cjs +18 -0
- package/dist/partner/index.d.cts +23 -0
- package/dist/partner/index.d.cts.map +1 -0
- package/dist/partner/index.d.ts +23 -0
- package/dist/partner/index.d.ts.map +1 -0
- package/dist/partner/index.js +7 -0
- package/dist/partner/raw.cjs +392 -0
- package/dist/partner/raw.cjs.map +1 -0
- package/dist/partner/raw.d.cts +223 -0
- package/dist/partner/raw.d.cts.map +1 -0
- package/dist/partner/raw.d.ts +223 -0
- package/dist/partner/raw.d.ts.map +1 -0
- package/dist/partner/raw.js +390 -0
- package/dist/partner/raw.js.map +1 -0
- package/dist/partner/redaction-placeholders.cjs +10 -0
- package/dist/partner/redaction-placeholders.cjs.map +1 -0
- package/dist/partner/redaction-placeholders.d.cts +5 -0
- package/dist/partner/redaction-placeholders.d.cts.map +1 -0
- package/dist/partner/redaction-placeholders.d.ts +5 -0
- package/dist/partner/redaction-placeholders.d.ts.map +1 -0
- package/dist/partner/redaction-placeholders.js +9 -0
- package/dist/partner/redaction-placeholders.js.map +1 -0
- package/dist/partner/redaction.cjs +267 -0
- package/dist/partner/redaction.cjs.map +1 -0
- package/dist/partner/redaction.d.cts +124 -0
- package/dist/partner/redaction.d.cts.map +1 -0
- package/dist/partner/redaction.d.ts +124 -0
- package/dist/partner/redaction.d.ts.map +1 -0
- package/dist/partner/redaction.js +257 -0
- package/dist/partner/redaction.js.map +1 -0
- package/dist/raw/index.cjs +81 -19
- package/dist/raw/index.cjs.map +1 -1
- package/dist/raw/index.d.cts +6 -2
- package/dist/raw/index.d.cts.map +1 -1
- package/dist/raw/index.d.ts +6 -2
- package/dist/raw/index.d.ts.map +1 -1
- package/dist/raw/index.js +81 -19
- package/dist/raw/index.js.map +1 -1
- package/dist/raw/url.cjs +111 -0
- package/dist/raw/url.cjs.map +1 -0
- package/dist/raw/url.d.cts +41 -0
- package/dist/raw/url.d.cts.map +1 -0
- package/dist/raw/url.d.ts +41 -0
- package/dist/raw/url.d.ts.map +1 -0
- package/dist/raw/url.js +110 -0
- package/dist/raw/url.js.map +1 -0
- package/dist/s3/sigv4.cjs +15 -10
- package/dist/s3/sigv4.cjs.map +1 -1
- package/dist/s3/sigv4.js +15 -10
- package/dist/s3/sigv4.js.map +1 -1
- package/dist/simulator/index.cjs +1197 -206
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.cts +109 -11
- package/dist/simulator/index.d.cts.map +1 -1
- package/dist/simulator/index.d.ts +109 -11
- package/dist/simulator/index.d.ts.map +1 -1
- package/dist/simulator/index.js +1196 -209
- package/dist/simulator/index.js.map +1 -1
- package/dist/simulator/partner.cjs +719 -0
- package/dist/simulator/partner.cjs.map +1 -0
- package/dist/simulator/partner.d.cts +183 -0
- package/dist/simulator/partner.d.cts.map +1 -0
- package/dist/simulator/partner.d.ts +183 -0
- package/dist/simulator/partner.d.ts.map +1 -0
- package/dist/simulator/partner.js +718 -0
- package/dist/simulator/partner.js.map +1 -0
- package/dist/simulator/validation.cjs +577 -0
- package/dist/simulator/validation.cjs.map +1 -1
- package/dist/simulator/validation.d.cts +117 -0
- package/dist/simulator/validation.d.cts.map +1 -1
- package/dist/simulator/validation.d.ts +117 -0
- package/dist/simulator/validation.d.ts.map +1 -1
- package/dist/simulator/validation.js +563 -1
- 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/index.cjs +1 -1
- package/dist/streams/index.js +1 -1
- package/dist/streams/source.cjs +2 -1
- package/dist/streams/source.cjs.map +1 -1
- package/dist/streams/source.js +2 -1
- package/dist/streams/source.js.map +1 -1
- package/dist/sync/b2-sha1-reader.cjs +6 -2
- package/dist/sync/b2-sha1-reader.cjs.map +1 -1
- package/dist/sync/b2-sha1-reader.d.cts.map +1 -1
- package/dist/sync/b2-sha1-reader.d.ts.map +1 -1
- package/dist/sync/b2-sha1-reader.js +6 -2
- package/dist/sync/b2-sha1-reader.js.map +1 -1
- package/dist/sync/download-staging.cjs +11 -10
- package/dist/sync/download-staging.cjs.map +1 -1
- package/dist/sync/download-staging.d.cts +2 -1
- package/dist/sync/download-staging.d.cts.map +1 -1
- package/dist/sync/download-staging.d.ts +2 -1
- package/dist/sync/download-staging.d.ts.map +1 -1
- package/dist/sync/download-staging.js +11 -10
- package/dist/sync/download-staging.js.map +1 -1
- package/dist/sync/filters.cjs.map +1 -1
- package/dist/sync/filters.js.map +1 -1
- package/dist/sync/local-file-io.cjs +11 -6
- package/dist/sync/local-file-io.cjs.map +1 -1
- package/dist/sync/local-file-io.d.cts +1 -0
- package/dist/sync/local-file-io.d.cts.map +1 -1
- package/dist/sync/local-file-io.d.ts +1 -0
- package/dist/sync/local-file-io.d.ts.map +1 -1
- package/dist/sync/local-file-io.js +11 -6
- package/dist/sync/local-file-io.js.map +1 -1
- package/dist/sync/local-sha1.cjs +6 -15
- package/dist/sync/local-sha1.cjs.map +1 -1
- package/dist/sync/local-sha1.d.cts +0 -8
- package/dist/sync/local-sha1.d.cts.map +1 -1
- package/dist/sync/local-sha1.d.ts +0 -8
- package/dist/sync/local-sha1.d.ts.map +1 -1
- package/dist/sync/local-sha1.js +7 -15
- package/dist/sync/local-sha1.js.map +1 -1
- package/dist/sync/policies/compare.cjs +5 -3
- package/dist/sync/policies/compare.cjs.map +1 -1
- package/dist/sync/policies/compare.d.cts.map +1 -1
- package/dist/sync/policies/compare.d.ts.map +1 -1
- package/dist/sync/policies/compare.js +6 -4
- package/dist/sync/policies/compare.js.map +1 -1
- package/dist/sync/policies/index.cjs +4 -10
- package/dist/sync/policies/index.cjs.map +1 -1
- package/dist/sync/policies/index.js +4 -10
- package/dist/sync/policies/index.js.map +1 -1
- package/dist/sync/scanners/b2.cjs +2 -2
- package/dist/sync/scanners/b2.cjs.map +1 -1
- package/dist/sync/scanners/b2.js +2 -2
- package/dist/sync/scanners/b2.js.map +1 -1
- package/dist/sync/scanners/local.cjs.map +1 -1
- package/dist/sync/scanners/local.js.map +1 -1
- package/dist/sync/synchronizer.cjs +3 -2
- package/dist/sync/synchronizer.cjs.map +1 -1
- package/dist/sync/synchronizer.js +3 -2
- package/dist/sync/synchronizer.js.map +1 -1
- package/dist/types/auth.cjs +4 -0
- package/dist/types/auth.cjs.map +1 -1
- package/dist/types/auth.d.cts +4 -0
- package/dist/types/auth.d.cts.map +1 -1
- package/dist/types/auth.d.ts +4 -0
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/auth.js +4 -0
- package/dist/types/auth.js.map +1 -1
- package/dist/types/backup.d.cts +80 -0
- package/dist/types/backup.d.cts.map +1 -0
- package/dist/types/backup.d.ts +80 -0
- package/dist/types/backup.d.ts.map +1 -0
- package/dist/types/bucket.cjs.map +1 -1
- package/dist/types/bucket.d.cts +4 -2
- package/dist/types/bucket.d.cts.map +1 -1
- package/dist/types/bucket.d.ts +4 -2
- package/dist/types/bucket.d.ts.map +1 -1
- package/dist/types/bucket.js.map +1 -1
- package/dist/types/download.cjs +62 -0
- package/dist/types/download.cjs.map +1 -0
- package/dist/types/download.d.cts +98 -0
- package/dist/types/download.d.cts.map +1 -1
- package/dist/types/download.d.ts +98 -0
- package/dist/types/download.d.ts.map +1 -1
- package/dist/types/download.js +61 -0
- package/dist/types/download.js.map +1 -0
- package/dist/types/encryption.cjs +58 -229
- package/dist/types/encryption.cjs.map +1 -1
- package/dist/types/encryption.d.cts +32 -0
- package/dist/types/encryption.d.cts.map +1 -1
- package/dist/types/encryption.d.ts +32 -0
- package/dist/types/encryption.d.ts.map +1 -1
- package/dist/types/encryption.js +56 -229
- package/dist/types/encryption.js.map +1 -1
- package/dist/types/errors.cjs +9 -0
- package/dist/types/errors.cjs.map +1 -1
- package/dist/types/errors.d.cts +1 -1
- package/dist/types/errors.d.cts.map +1 -1
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +9 -0
- package/dist/types/errors.js.map +1 -1
- package/dist/types/file.cjs.map +1 -1
- package/dist/types/file.d.cts +15 -2
- package/dist/types/file.d.cts.map +1 -1
- package/dist/types/file.d.ts +15 -2
- package/dist/types/file.d.ts.map +1 -1
- package/dist/types/file.js.map +1 -1
- package/dist/types/ids.cjs +30 -0
- package/dist/types/ids.cjs.map +1 -1
- package/dist/types/ids.d.cts +32 -0
- package/dist/types/ids.d.cts.map +1 -1
- package/dist/types/ids.d.ts +32 -0
- package/dist/types/ids.d.ts.map +1 -1
- package/dist/types/ids.js +28 -1
- package/dist/types/ids.js.map +1 -1
- package/dist/types/index.d.cts +10 -6
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +10 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lock.cjs.map +1 -1
- package/dist/types/lock.d.cts +14 -0
- package/dist/types/lock.d.cts.map +1 -1
- package/dist/types/lock.d.ts +14 -0
- package/dist/types/lock.d.ts.map +1 -1
- package/dist/types/lock.js.map +1 -1
- package/dist/types/notifications.cjs +1 -1
- package/dist/types/notifications.cjs.map +1 -1
- package/dist/types/notifications.d.cts +5 -3
- package/dist/types/notifications.d.cts.map +1 -1
- package/dist/types/notifications.d.ts +5 -3
- package/dist/types/notifications.d.ts.map +1 -1
- package/dist/types/notifications.js +1 -1
- package/dist/types/notifications.js.map +1 -1
- package/dist/types/partner.cjs +32 -0
- package/dist/types/partner.cjs.map +1 -0
- package/dist/types/partner.d.cts +394 -0
- package/dist/types/partner.d.cts.map +1 -0
- package/dist/types/partner.d.ts +394 -0
- package/dist/types/partner.d.ts.map +1 -0
- package/dist/types/partner.js +29 -0
- package/dist/types/partner.js.map +1 -0
- package/dist/types/upload.d.cts +31 -18
- package/dist/types/upload.d.cts.map +1 -1
- package/dist/types/upload.d.ts +31 -18
- package/dist/types/upload.d.ts.map +1 -1
- package/dist/upload/abort-scope.cjs +0 -45
- package/dist/upload/abort-scope.cjs.map +1 -1
- package/dist/upload/abort-scope.d.cts +0 -22
- package/dist/upload/abort-scope.d.cts.map +1 -1
- package/dist/upload/abort-scope.d.ts +0 -22
- package/dist/upload/abort-scope.d.ts.map +1 -1
- package/dist/upload/abort-scope.js +1 -44
- package/dist/upload/abort-scope.js.map +1 -1
- package/dist/upload/finish.cjs +3 -8
- package/dist/upload/finish.cjs.map +1 -1
- package/dist/upload/finish.d.cts.map +1 -1
- package/dist/upload/finish.d.ts.map +1 -1
- package/dist/upload/finish.js +1 -6
- package/dist/upload/finish.js.map +1 -1
- package/dist/upload/large.cjs +5 -1
- package/dist/upload/large.cjs.map +1 -1
- package/dist/upload/large.d.cts +16 -2
- package/dist/upload/large.d.cts.map +1 -1
- package/dist/upload/large.d.ts +16 -2
- package/dist/upload/large.d.ts.map +1 -1
- package/dist/upload/large.js +5 -1
- 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 +2 -1
- package/dist/upload/resume.cjs.map +1 -1
- package/dist/upload/resume.d.cts +3 -1
- package/dist/upload/resume.d.cts.map +1 -1
- package/dist/upload/resume.d.ts +3 -1
- package/dist/upload/resume.d.ts.map +1 -1
- package/dist/upload/resume.js +2 -1
- package/dist/upload/resume.js.map +1 -1
- package/dist/upload/retry.cjs +8 -3
- package/dist/upload/retry.cjs.map +1 -1
- package/dist/upload/retry.js +8 -3
- package/dist/upload/retry.js.map +1 -1
- package/dist/upload/single.cjs +2 -1
- 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 +2 -1
- package/dist/upload/single.js.map +1 -1
- package/dist/upload/stream.cjs +6 -4
- 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 +3 -1
- package/dist/upload/stream.js.map +1 -1
- package/dist/util/abort.cjs +114 -0
- package/dist/util/abort.cjs.map +1 -0
- package/dist/util/abort.d.cts +65 -0
- package/dist/util/abort.d.cts.map +1 -0
- package/dist/util/abort.d.ts +65 -0
- package/dist/util/abort.d.ts.map +1 -0
- package/dist/util/abort.js +109 -0
- package/dist/util/abort.js.map +1 -0
- package/dist/util/defaults.cjs +3 -0
- package/dist/util/defaults.cjs.map +1 -1
- package/dist/util/defaults.d.cts +2 -0
- package/dist/util/defaults.d.cts.map +1 -1
- package/dist/util/defaults.d.ts +2 -0
- package/dist/util/defaults.d.ts.map +1 -1
- package/dist/util/defaults.js +3 -1
- package/dist/util/defaults.js.map +1 -1
- package/dist/util/md5.cjs +211 -0
- package/dist/util/md5.cjs.map +1 -0
- package/dist/util/md5.d.cts +43 -0
- package/dist/util/md5.d.cts.map +1 -0
- package/dist/util/md5.d.ts +43 -0
- package/dist/util/md5.d.ts.map +1 -0
- package/dist/util/md5.js +209 -0
- package/dist/util/md5.js.map +1 -0
- package/dist/util/normalize.cjs +5 -5
- package/dist/util/normalize.cjs.map +1 -1
- package/dist/util/normalize.d.cts +7 -7
- package/dist/util/normalize.d.cts.map +1 -1
- package/dist/util/normalize.d.ts +7 -7
- package/dist/util/normalize.d.ts.map +1 -1
- package/dist/util/normalize.js +5 -5
- package/dist/util/normalize.js.map +1 -1
- package/dist/util/paginator.cjs +7 -1
- package/dist/util/paginator.cjs.map +1 -1
- package/dist/util/paginator.d.cts +3 -1
- package/dist/util/paginator.d.cts.map +1 -1
- package/dist/util/paginator.d.ts +3 -1
- package/dist/util/paginator.d.ts.map +1 -1
- package/dist/util/paginator.js +7 -1
- package/dist/util/paginator.js.map +1 -1
- package/package.json +38 -16
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-08-18
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Download response headers.** `DownloadHeaders` now exposes optional `contentDisposition`, `contentLanguage`, `contentEncoding`, `cacheControl`, `expires`, `contentRange`, `serverSideEncryption`, readable `fileRetention`/`legalHold`, typed `clientUnauthorizedToRead`, and `headerParseIssues`; new `DownloadHeaderName`, `DownloadClientUnauthorizedToReadMarker`, `DownloadServerSideEncryption`, and `SseCDownloadSetting` exports document the added surface.
|
|
15
|
+
- **Large-file part metadata.** Raw large-file response types now expose documented v4 metadata fields: `contentMd5` on `UploadPartResponse` and `PartInfo`, `serverSideEncryption` and `uploadTimestamp` on `CopyPartResponse`, `serverSideEncryption` on `PartInfo`, and `replicationStatus` on unfinished large-file metadata via the exported `ReplicationStatus` alias.
|
|
16
|
+
- **Custom upload timestamps.** Upload options now accept numeric `customUploadTimestamp` values to override B2's `uploadTimestamp`, distinct from `lastModifiedMillis` source metadata. Raw `b2_upload_file` headers serialize the value as `X-Bz-Custom-Upload-Timestamp`; raw `StartLargeFileRequest` accepts B2's documented decimal string/null body shape. Multipart uploads now also persist `lastModifiedMillis` as `src_last_modified_millis`, matching the small-file path; this metadata is included in multipart resume identity and older unfinished uploads that lack it can report `file-info-mismatch`. Custom timestamps require B2 account enablement, and resume diagnostics can report `upload-timestamp-mismatch` for incompatible unfinished large files.
|
|
17
|
+
- **Bucket lifecycle-rule capabilities.** `Capability` now exports `ReadBucketLifecycleRules` and `WriteBucketLifecycleRules`.
|
|
18
|
+
- **Partner authorize runtime surface.** New `@backblaze-labs/b2-sdk/partner` subpath exports `PartnerRawClient.authorizePartner()`, `PartnerAccountInfo`, and `InMemoryPartnerAccountInfo` for Master Application Key authorization against the Partner and Computer Backup suites.
|
|
19
|
+
- **Partner API runtime bindings.** `PartnerRawClient` now includes `createGroupMember()`, `ejectGroupMember()`, `listGroups()`, `listGroupMembers()`, and `reserveTrialCreateAccount()` bindings for Partner API group management and B2 Reserve trial-account creation.
|
|
20
|
+
- **High-level Partner facade.** The `@backblaze-labs/b2-sdk/partner` subpath now exports the experimental `PartnerClient` facade plus `PartnerClientOptions`, `PartnerAuthorizeOptions`, `ListGroupsOptions`, `PaginateGroupsOptions`, `ListGroupMembersOptions`, `PaginateGroupMembersOptions`, `CreateGroupMemberOptions`, `EjectGroupMemberOptions`, and `ReserveTrialAccountsOptions` for ergonomic Partner group/member pagination and B2 Reserve trial-account workflows.
|
|
21
|
+
- **Computer Backup runtime facade.** The new `@backblaze-labs/b2-sdk/backup` subpath exports experimental `BackupClient` and `BackupRawClient` bindings for Partner-authorized Computer Backup listing, pagination, and delete workflows.
|
|
22
|
+
- **Partner API and Computer Backup typed error subclasses.** New exported `TooManyMembersError`, `InvalidGroupIdError`, `InvalidEmailError`, `InvalidRegionError`, `InvalidSmsPhoneError`, `MethodFailureError`, `InvalidMemberAccountIdError`, `InvalidAccountIdError`, and `InvalidComputerIdError` classify Partner API and Computer Backup error codes; Partner 401 overload codes map to non-auth subclasses so they do not trigger reauth. Compatibility aliases preserve the issue-specified `TooManyGroupMembersError`, `MissingSmsPhoneError`, and `GroupMemberCreationFailedError` names.
|
|
23
|
+
- **Partner API and Computer Backup type layer.** Public exports now include Partner API and Computer Backup request/response types, branded `groupId()` / `computerId()` factories, `PartnerCapability`, and `Region`. The response types document each Partner and Backup endpoint's wire shape (array-shaped or single-object per the API docs) and preserve numeric-looking group IDs and B2 statistics counts as strings. Endpoint types still awaiting runtime methods are marked experimental until those methods are implemented and exercised.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **Computer Backup facade validates list pages.** `BackupRawClient.listComputers()` returns the documented single-object `bz_list_computers` wire response, and `BackupClient.listComputers()` validates that page object and returns its `computers` and `nextComputerId`, rejecting malformed wire shapes before pagination can silently truncate results.
|
|
28
|
+
- **Partner auth cache validation is non-destructive during construction.** `PartnerClient` and `BackupClient` ignore unsafe cached Partner authorization locally until `authorize()` replaces it, instead of clearing a shared `PartnerAccountInfo` store from a client constructor.
|
|
29
|
+
- **Partner authorize JSON serialization redacts tokens.** BREAKING: `JSON.stringify()` of `PartnerRawClient.authorizePartner()`, `PartnerClient.authorize()`, and `PartnerAccountInfo.getAuth()` responses now emits `[redacted Partner token]` instead of round-tripping `authorizationToken`. Trusted durable auth caches should stringify `partnerAuthorizeResponseForPersistence(auth)` only for encrypted or otherwise credential-grade storage, or store `authorizationToken` directly in secure storage before rehydrating with `setAuth()`. Rehydrating the redacted placeholder now fails fast instead of sending it as a Partner API bearer token.
|
|
30
|
+
- **Replication status metadata matches B2's wire shape.** BREAKING: `ReplicationStatus` values changed from lowercase (`'pending' | 'completed' | 'failed' | 'replica' | null`) to B2's documented uppercase values (`'PENDING' | 'COMPLETED' | 'FAILED' | 'REPLICA'`), and `FileVersion.replicationStatus`, `StartLargeFileResponse.replicationStatus`, and `UnfinishedLargeFile.replicationStatus` are now omitted instead of `null` when the file is not covered by a replication rule. Callers switching on lowercase literals or `null` must update those comparisons.
|
|
31
|
+
- **Event notification rule target type narrowed.** `EventNotificationRule.targetConfiguration.targetType` is now the string literal `'webhook'` instead of `string` (the only value B2 accepts, corrected from the earlier `'url'` documentation placeholder), and the rule configuration gained an optional `maxEventsPerBatch` field. Code that annotated `targetType` as an arbitrary `string` may need a cast (#123).
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **Unfinished large-file SHA-1 sentinel normalized.** `startLargeFile` and `listUnfinishedLargeFiles` now collapse B2's `contentSha1: 'none'` wire sentinel to `null`, matching finished and file-list endpoints; the optional `UnfinishedLargeFileMetadata.contentSha1` type is widened to `string | null`.
|
|
36
|
+
- **SHA-1 reader aborts classify as `AbortError`.** Pending stream reads aborted without an explicit reason now reject with an `AbortError` DOMException, and abort classification is centralized so abort-aware sync and upload-finish paths handle those cancellations consistently. Closes #208.
|
|
37
|
+
- **SSE-C keys serialized into JSON request bodies.** `copyFile`, `copyPart`, and `startLargeFile` now serialize customer-managed SSE-C key material into the `b2_copy_file` / `b2_copy_part` / `b2_start_large_file` JSON request bodies instead of the redacting `EncryptionKey` wrapper, which previously emitted the `[redacted SSE-C key]` placeholder and produced a `403` on server-side copy and large-file start with SSE-C. Header-based upload and download paths were unaffected. Closes #206.
|
|
38
|
+
- **B2 endpoint URL path hardening.** `b2Url()` now rejects literal and percent-encoded backslashes in prefix, version, and endpoint path components so WHATWG URL normalization cannot smuggle an endpoint into another path segment.
|
|
39
|
+
- **Real-B2 integration evidence is explicit and diagnosable.** Same-repo integration workflow runs now fail when required B2 secrets are missing instead of silently accepting an all-skipped suite, integration setup logs per-step timings and timeout failures for authorization, bucket listing, stale cleanup, and bucket creation, and contributor/release docs now spell out simulator-vs-live-B2 expectations.
|
|
40
|
+
- **B2Simulator upload write-path fidelity.** Simulator uploads now reject Content-Length/body mismatches, reject upload_part and copy_part part numbers outside B2's 1-10000 range, classify copy_part malformed ranges as 400 and unsatisfiable ranges as 416, verify large-file finish part SHA-1 entries, and report stored monotonic part timestamps from list_parts. Closes #21.
|
|
41
|
+
- **B2Simulator SSE-C fidelity.** Simulator uploads, large-file parts, downloads, copyFile, and copyPart now validate SSE-C customer-key headers more closely to B2, preserve destination encryption semantics, and keep customer keys out of public response metadata.
|
|
42
|
+
- **B2Simulator bucket-deletion fidelity.** `b2_delete_bucket` now rejects buckets that still contain file versions or unfinished large files with `400 cannot_delete_non_empty_bucket`, matching real B2.
|
|
43
|
+
- **B2Simulator bucket-configuration fidelity.** `b2_create_bucket` and `b2_update_bucket` now validate CORS, lifecycle, replication, and default retention rule shapes; `b2_list_buckets` honors the `bucketName`, `bucketId`, and `bucketTypes` filters; and `b2_update_bucket` enforces `ifRevisionIs` with a `409 conflict`. Closes #22.
|
|
44
|
+
- **B2Simulator create_key validation.** `b2_create_key` now rejects unknown or empty capabilities and out-of-range key names, and rejects capabilities that exceed the creating key's grant, matching real B2. Closes #18.
|
|
45
|
+
- **B2Simulator authorize grant fidelity.** `b2_authorize_account` now derives the response `allowed` capabilities and bucket/name-prefix scope from the authorizing key instead of always returning the full master grant, so a restricted key reports its real scope. Closes #19.
|
|
46
|
+
- **B2Simulator object-lock update fidelity.** `b2_update_file_retention` and `b2_update_file_legal_hold` now enforce retention and legal-hold update rules, governance-bypass requirements, and retention-clock validation, matching real B2. Closes #122.
|
|
47
|
+
- **B2Simulator download authorization fidelity.** `b2_get_download_authorization` tokens are now enforced on download: bucket/prefix scope validation, header normalization, and by-id download-auth source restriction. Closes #121.
|
|
48
|
+
- **B2Simulator notification-rule validation.** `b2_set_bucket_notification_rules` now validates event-notification rule shapes before accepting them. Closes #123.
|
|
49
|
+
|
|
10
50
|
## [0.2.0] - 2026-07-08
|
|
11
51
|
|
|
12
52
|
### Added
|
|
@@ -221,6 +261,7 @@ First public release of `@backblaze-labs/b2-sdk`. Everything below is new in thi
|
|
|
221
261
|
- TypeDoc for API documentation
|
|
222
262
|
- Vitest test suite with 486 tests across 20 files at ≥ 95% statement coverage. Tests run cleanly under both vitest (Node) and Bun's vitest-compat (no module-level mocking required)
|
|
223
263
|
|
|
224
|
-
[Unreleased]: https://github.com/backblaze-labs/b2-sdk-typescript/compare/v0.
|
|
264
|
+
[Unreleased]: git+https://github.com/backblaze-labs/b2-sdk-typescript/compare/v0.3.0...HEAD
|
|
265
|
+
[0.3.0]: git+https://github.com/backblaze-labs/b2-sdk-typescript/compare/v0.2.0...v0.3.0
|
|
225
266
|
[0.2.0]: https://github.com/backblaze-labs/b2-sdk-typescript/compare/v0.1.0...v0.2.0
|
|
226
267
|
[0.1.0]: https://github.com/backblaze-labs/b2-sdk-typescript/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://backblaze-labs.github.io/b2-sdk-typescript/)
|
|
5
5
|
[](https://www.npmjs.com/package/@backblaze-labs/b2-sdk)
|
|
6
6
|
[](LICENSE)
|
|
7
|
-
[](https://www.typescriptlang.org/)
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
9
|
[](package.json)
|
|
10
10
|
|
|
@@ -80,6 +80,17 @@ await bucket.delete()
|
|
|
80
80
|
|
|
81
81
|
> The `BucketType`, `RetentionMode`, `LegalHoldValue`, `Capability`, `EventType`, and `EncryptionMode` `as const` objects exported from the main entry give you type-safe alternatives to the raw string literals — pick whichever style you prefer; both are accepted at the type level.
|
|
82
82
|
|
|
83
|
+
### Partner and Computer Backup APIs
|
|
84
|
+
|
|
85
|
+
Partner API and Computer Backup API helpers are exported from dedicated subpaths:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { PartnerClient } from '@backblaze-labs/b2-sdk/partner'
|
|
89
|
+
import { BackupClient } from '@backblaze-labs/b2-sdk/backup'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Both clients authorize with a Master Application Key through the Partner authorization flow. Account-creating Partner operations, including group member creation and trial reservation, require a valid SMS phone number on the administrator account. See [examples/README.md](examples/README.md#partner-and-computer-backup-examples) for current prerequisites and runnable examples.
|
|
93
|
+
|
|
83
94
|
### Uploads
|
|
84
95
|
|
|
85
96
|
Small files (under the recommended part size, typically 100 MB) are uploaded in a single request. Larger files automatically use multipart upload with parallel part uploads.
|
|
@@ -475,6 +486,10 @@ import {
|
|
|
475
486
|
import { InMemoryAccountInfo } from '@backblaze-labs/b2-sdk/auth'
|
|
476
487
|
import { FileAccountInfo } from '@backblaze-labs/b2-sdk/auth/file'
|
|
477
488
|
|
|
489
|
+
// Partner and Computer Backup API surfaces
|
|
490
|
+
import { PartnerClient, PartnerRawClient } from '@backblaze-labs/b2-sdk/partner'
|
|
491
|
+
import { BackupClient, BackupRawClient } from '@backblaze-labs/b2-sdk/backup'
|
|
492
|
+
|
|
478
493
|
// Streaming utilities + SSE-C key wrapper
|
|
479
494
|
import {
|
|
480
495
|
IncrementalSha1,
|
|
@@ -493,6 +508,9 @@ import {
|
|
|
493
508
|
presignS3PutObjectUrl,
|
|
494
509
|
} from '@backblaze-labs/b2-sdk/s3'
|
|
495
510
|
|
|
511
|
+
// Webhook signature verification for B2 event notifications
|
|
512
|
+
import { verifyWebhookSignature, requireValidWebhook } from '@backblaze-labs/b2-sdk/notifications'
|
|
513
|
+
|
|
496
514
|
// In-memory B2 server for tests (no network required)
|
|
497
515
|
import { B2Simulator } from '@backblaze-labs/b2-sdk/simulator'
|
|
498
516
|
```
|
|
@@ -688,7 +706,7 @@ describe('my app', () => {
|
|
|
688
706
|
|
|
689
707
|
## Error handling
|
|
690
708
|
|
|
691
|
-
All B2 API errors are thrown as typed `B2Error` subclasses
|
|
709
|
+
All B2 API errors are thrown as typed `B2Error` subclasses. Client-side capability checks throw `B2InsufficientCapabilityError`.
|
|
692
710
|
|
|
693
711
|
```ts
|
|
694
712
|
import { BucketType } from '@backblaze-labs/b2-sdk'
|
package/dist/auth/file.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.cjs","names":[],"sources":["../../src/auth/file.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { chmod, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join } from 'node:path'\nimport type { AuthorizeAccountResponse } from '../types/auth.ts'\nimport type { BucketId } from '../types/ids.ts'\nimport type { AccountInfo, AuthContextAwareAccountInfo, UploadUrlEntry } from './account-info.ts'\nimport { InMemoryAccountInfo } from './in-memory.ts'\nimport { getRealmUrl } from './realms.ts'\n\nconst PRIVATE_FILE_MODE = 0o600\nconst PERSISTED_AUTH_VERSION = 1\nconst BUILT_IN_PRODUCTION_REALM_URL = getRealmUrl('production')\nconst BUILT_IN_STAGING_REALM_URL = getRealmUrl('staging')\nconst PRODUCTION_HOST_SUFFIX = 'backblazeb2.com'\nconst STAGING_HOST_SUFFIX = 'backblaze.net'\n\ninterface PersistedAuthState {\n readonly _b2sdk?: {\n readonly version?: number\n readonly realmUrl?: string\n readonly applicationKeyId?: string\n }\n}\n\n/** Reason a loaded auth cache entry was ignored for the current client. */\nexport type FileAccountInfoDiscardReason =\n | 'realm_mismatch'\n | 'application_key_mismatch'\n | 'endpoint_mismatch'\n\n/** Event emitted when a loaded cache entry is ignored without modifying disk. */\nexport interface FileAccountInfoDiscardEvent {\n /** Absolute path of the auth cache file. */\n readonly path: string\n /** Why the loaded auth entry could not be reused. */\n readonly reason: FileAccountInfoDiscardReason\n /** Realm URL stored in the cache metadata, or null for legacy entries. */\n readonly cachedRealmUrl: string | null | undefined\n /** Realm URL configured on the current client, if already bound. */\n readonly expectedRealmUrl: string | undefined\n /** Application key ID stored in cache metadata, or null for legacy entries. */\n readonly cachedApplicationKeyId: string | null | undefined\n /** Application key ID configured on the current client, if already bound. */\n readonly expectedApplicationKeyId: string | undefined\n}\n\n/** Event emitted when an asynchronous auth-cache write fails. */\nexport interface FileAccountInfoWriteErrorEvent {\n /** Absolute path of the auth cache file. */\n readonly path: string\n /** Underlying filesystem or serialization error. */\n readonly error: unknown\n}\n\n/** Optional observers for file-backed auth cache behavior. */\nexport interface FileAccountInfoOptions {\n /**\n * Called when loaded auth is ignored because its metadata or cached\n * endpoints are unsafe for this client. The on-disk file is left untouched\n * so another process using a different realm/key is not clobbered.\n */\n readonly onDiscard?: (event: FileAccountInfoDiscardEvent) => void\n /** Called when an asynchronous auth-cache write fails. */\n readonly onWriteError?: (event: FileAccountInfoWriteErrorEvent) => void\n}\n\nfunction readPersistedAuthState(value: unknown): {\n auth: AuthorizeAccountResponse\n realmUrl: string | null\n applicationKeyId: string | null\n} | null {\n if (value === null || typeof value !== 'object') return null\n\n const maybeState = value as PersistedAuthState\n if (\n maybeState._b2sdk?.version !== undefined &&\n maybeState._b2sdk.version !== PERSISTED_AUTH_VERSION\n ) {\n return null\n }\n const realmUrl =\n maybeState._b2sdk !== undefined && typeof maybeState._b2sdk.realmUrl === 'string'\n ? maybeState._b2sdk.realmUrl\n : null\n const applicationKeyId =\n maybeState._b2sdk !== undefined && typeof maybeState._b2sdk.applicationKeyId === 'string'\n ? maybeState._b2sdk.applicationKeyId\n : null\n return { auth: value as AuthorizeAccountResponse, realmUrl, applicationKeyId }\n}\n\nfunction isProductionBackblazeEndpoint(url: string): boolean {\n try {\n const parsed = new URL(url)\n return parsed.protocol === 'https:' && hasHostSuffix(parsed.hostname, PRODUCTION_HOST_SUFFIX)\n } catch {\n return false\n }\n}\n\nfunction isProductionAuthResponse(auth: AuthorizeAccountResponse): boolean {\n try {\n const storageApi = auth.apiInfo.storageApi\n return [storageApi.apiUrl, storageApi.downloadUrl, storageApi.s3ApiUrl].every(\n isProductionBackblazeEndpoint,\n )\n } catch {\n return false\n }\n}\n\nfunction verifiedRealmEndpointSuffix(realmUrl: string): string | null {\n try {\n const realm = new URL(realmUrl)\n if (realm.origin === new URL(BUILT_IN_PRODUCTION_REALM_URL).origin) {\n return PRODUCTION_HOST_SUFFIX\n }\n if (realm.origin === new URL(BUILT_IN_STAGING_REALM_URL).origin) {\n return STAGING_HOST_SUFFIX\n }\n } catch {\n // Fall through below.\n }\n return null\n}\n\nfunction hasHostSuffix(hostname: string, suffix: string): boolean {\n const host = hostname.toLowerCase()\n const lowered = suffix.toLowerCase()\n return host === lowered || host.endsWith(`.${lowered}`)\n}\n\nfunction isEndpointInRealm(url: string, realmUrl: string): boolean {\n try {\n const realm = new URL(realmUrl)\n const endpoint = new URL(url)\n const endpointHost = endpoint.hostname.toLowerCase()\n const realmHost = realm.hostname.toLowerCase()\n if (isLoopbackEndpointHost(endpointHost) || isLoopbackEndpointHost(realmHost)) {\n return endpoint.origin === realm.origin\n }\n\n const verifiedSuffix = verifiedRealmEndpointSuffix(realmUrl)\n if (verifiedSuffix !== null) {\n return endpoint.protocol === 'https:' && hasHostSuffix(endpointHost, verifiedSuffix)\n }\n\n return endpoint.protocol === realm.protocol && hasHostSuffix(endpointHost, realmHost)\n } catch {\n return false\n }\n}\n\nfunction isLoopbackEndpointHost(host: string): boolean {\n if (host === 'localhost' || host === '::1' || host === '[::1]') return true\n\n const parts = host.split('.')\n return (\n parts.length === 4 &&\n parts[0] === '127' &&\n parts.every((part) => /^\\d+$/.test(part) && Number(part) <= 255)\n )\n}\n\nfunction authEndpointsMatchRealm(auth: AuthorizeAccountResponse, realmUrl: string): boolean {\n try {\n const storageApi = auth.apiInfo.storageApi\n return [storageApi.apiUrl, storageApi.downloadUrl, storageApi.s3ApiUrl].every((url) =>\n isEndpointInRealm(url, realmUrl),\n )\n } catch {\n return false\n }\n}\n\n/**\n * Node-only {@link AccountInfo} backend that persists the authorization\n * response to a JSON file. Upload URL pools remain in memory because URLs are\n * short-lived and shouldn't be shared across processes.\n * The JSON file contains a live B2 authorization token, so treat it as\n * sensitive and keep it out of shared or world-readable locations.\n * `FileAccountInfo` serializes writes within one process but does not provide\n * inter-process locking. If multiple processes share one path, use\n * {@link FileAccountInfoOptions.onDiscard} and {@link FileAccountInfoOptions.onWriteError}\n * to observe cache churn, or prefer one cache path per resolved realm and key.\n *\n * On instantiation, call {@link FileAccountInfo.load} to populate state from\n * disk (or start fresh if the file is missing or corrupt). The authorization\n * response is written back to disk on every {@link setAuth} or {@link clear}\n * call so a process restart can resume without re-authorizing.\n * Cache files without an SDK metadata application-key binding are ignored once\n * bound to a `B2Client`, because the SDK cannot verify which key wrote them.\n *\n * This module imports `node:fs/promises`; do not load it in browser bundles.\n */\nexport class FileAccountInfo implements AccountInfo, AuthContextAwareAccountInfo {\n private readonly inner = new InMemoryAccountInfo()\n private writeQueue: Promise<void> = Promise.resolve()\n private realmUrl: string | undefined\n private loadedAuthRealmUrl: string | null | undefined\n private applicationKeyId: string | undefined\n private loadedAuthApplicationKeyId: string | null | undefined\n\n /**\n * Constructs a `FileAccountInfo` bound to a JSON file on disk. The file is\n * created on the first `setAuth` write; reading it back happens via\n * {@link load}.\n *\n * @param path - Absolute path to the JSON file that backs this account info.\n * @param options - Optional discard and write-error observers.\n */\n constructor(\n public readonly path: string,\n private readonly options: FileAccountInfoOptions = {},\n ) {}\n\n /**\n * Reads the JSON file at the configured path and populates in-memory state.\n * If the file is missing or contains invalid JSON, leaves state empty and\n * returns silently (a re-auth is expected next).\n *\n * @returns A promise that resolves when load is complete.\n */\n async load(): Promise<void> {\n try {\n const text = await readFile(this.path, 'utf8')\n const parsed = readPersistedAuthState(JSON.parse(text) as unknown)\n if (parsed === null) return\n this.inner.setAuth(parsed.auth)\n this.loadedAuthRealmUrl = parsed.realmUrl\n this.loadedAuthApplicationKeyId = parsed.applicationKeyId\n this.discardMismatchedAuth()\n } catch {\n // Missing file, invalid JSON, or permission denied: start empty.\n }\n }\n\n /**\n * Binds this cache to the configured realm URL. `B2Client` calls this when a\n * FileAccountInfo instance is supplied, so stale auth produced by another\n * realm is discarded before it can be used.\n *\n * @param realmUrl - Resolved authorize-account realm URL for the client.\n */\n setRealmUrl(realmUrl: string): void {\n this.realmUrl = realmUrl\n this.discardMismatchedAuth()\n }\n\n /**\n * Binds this cache to the configured application key ID. `B2Client` calls\n * this when a FileAccountInfo instance is supplied, so stale auth produced by\n * another key is discarded before it can be used.\n *\n * @param applicationKeyId - Application key ID configured on the client.\n */\n setApplicationKeyId(applicationKeyId: string): void {\n this.applicationKeyId = applicationKeyId\n this.discardMismatchedAuth()\n }\n\n private discardMismatchedAuth(): void {\n const auth = this.inner.getAuth()\n if (auth === null) return\n\n const reason = this.mismatchReason(auth)\n if (reason === null) return\n\n this.inner.clear()\n this.options.onDiscard?.({\n path: this.path,\n reason,\n cachedRealmUrl: this.loadedAuthRealmUrl,\n expectedRealmUrl: this.realmUrl,\n cachedApplicationKeyId: this.loadedAuthApplicationKeyId,\n expectedApplicationKeyId: this.applicationKeyId,\n })\n this.loadedAuthRealmUrl = undefined\n this.loadedAuthApplicationKeyId = undefined\n }\n\n private mismatchReason(auth: AuthorizeAccountResponse): FileAccountInfoDiscardReason | null {\n const realmMatches =\n this.realmUrl === undefined ||\n this.loadedAuthRealmUrl === this.realmUrl ||\n (this.loadedAuthRealmUrl === null &&\n this.realmUrl === BUILT_IN_PRODUCTION_REALM_URL &&\n isProductionAuthResponse(auth))\n const applicationKeyMatches =\n this.applicationKeyId === undefined ||\n this.loadedAuthApplicationKeyId === this.applicationKeyId\n if (!realmMatches) return 'realm_mismatch'\n if (!applicationKeyMatches) return 'application_key_mismatch'\n if (this.realmUrl !== undefined && !authEndpointsMatchRealm(auth, this.realmUrl)) {\n return 'endpoint_mismatch'\n }\n\n return null\n }\n\n /**\n * Persists the current auth response to the file. Atomically queues writes\n * so concurrent updates serialize.\n *\n * @returns A promise that resolves when the write completes.\n */\n private flush(): Promise<void> {\n const auth = this.inner.getAuth()\n const next = this.writeQueue.then(async () => {\n if (auth === null) {\n try {\n await this.writePrivateFileAtomically('')\n } catch {\n // Best-effort\n }\n return\n }\n const contents =\n this.realmUrl !== undefined\n ? JSON.stringify({\n ...auth,\n _b2sdk: {\n version: PERSISTED_AUTH_VERSION,\n realmUrl: this.realmUrl,\n ...(this.applicationKeyId !== undefined\n ? { applicationKeyId: this.applicationKeyId }\n : {}),\n },\n })\n : JSON.stringify(auth)\n await this.writePrivateFileAtomically(contents)\n })\n this.writeQueue = next.catch((error: unknown) => {\n this.options.onWriteError?.({ path: this.path, error })\n })\n return next\n }\n\n private async writePrivateFileAtomically(contents: string): Promise<void> {\n const dir = dirname(this.path)\n const tempPath = join(dir, `.${basename(this.path)}.${process.pid}.${randomUUID()}.tmp`)\n await mkdir(dir, { recursive: true })\n\n try {\n await writeFile(tempPath, contents, { encoding: 'utf8', mode: PRIVATE_FILE_MODE })\n await chmod(tempPath, PRIVATE_FILE_MODE)\n await rename(tempPath, this.path)\n await chmod(this.path, PRIVATE_FILE_MODE)\n } catch (error) {\n await rm(tempPath, { force: true }).catch(() => {})\n throw error\n }\n }\n\n /**\n * Stores the given authorization response and triggers an async write to disk.\n *\n * @param auth - The authorize-account response to persist.\n */\n setAuth(auth: AuthorizeAccountResponse): void {\n this.inner.setAuth(auth)\n this.loadedAuthRealmUrl = this.realmUrl ?? null\n this.loadedAuthApplicationKeyId = this.applicationKeyId ?? null\n void this.flush()\n }\n\n /**\n * Returns the currently cached authorization response, or `null` if none.\n *\n * @returns The cached authorization response, or `null` if not authorized.\n */\n getAuth(): AuthorizeAccountResponse | null {\n return this.inner.getAuth()\n }\n\n /** Discards in-memory state and clears the on-disk file. */\n clear(): void {\n this.inner.clear()\n this.loadedAuthRealmUrl = undefined\n this.loadedAuthApplicationKeyId = undefined\n void this.flush()\n }\n\n /**\n * Returns the cached B2 API URL.\n *\n * @returns The base URL for B2 API calls.\n */\n getApiUrl(): string {\n return this.inner.getApiUrl()\n }\n\n /**\n * Returns the cached file-download URL.\n *\n * @returns The base URL for file downloads.\n */\n getDownloadUrl(): string {\n return this.inner.getDownloadUrl()\n }\n\n /**\n * Returns the cached session authorization token.\n *\n * @returns The current authorization token.\n */\n getAuthToken(): string {\n return this.inner.getAuthToken()\n }\n\n /**\n * Returns the cached account identifier.\n *\n * @returns The authorized account ID.\n */\n getAccountId(): string {\n return this.inner.getAccountId()\n }\n\n /**\n * Returns the server-recommended part size for multipart uploads.\n *\n * @returns The recommended part size in bytes.\n */\n getRecommendedPartSize(): number {\n return this.inner.getRecommendedPartSize()\n }\n\n /**\n * Returns the server-enforced minimum part size for multipart uploads.\n *\n * @returns The absolute minimum part size in bytes.\n */\n getAbsoluteMinimumPartSize(): number {\n return this.inner.getAbsoluteMinimumPartSize()\n }\n\n /**\n * Returns the cached S3-compatible API URL.\n *\n * @returns The base URL for the S3-compatible API.\n */\n getS3ApiUrl(): string {\n return this.inner.getS3ApiUrl()\n }\n\n /**\n * Returns the bucket the key is restricted to, if any.\n *\n * @returns The restricted bucket ID, or `null` if the key is unrestricted.\n */\n getAllowedBucketId(): BucketId | null {\n return this.inner.getAllowedBucketId()\n }\n\n /**\n * Returns the buckets the key is restricted to, if any.\n *\n * @returns The restricted bucket IDs, or `null` if the key is unrestricted.\n */\n getAllowedBucketIds(): readonly BucketId[] | null {\n return this.inner.getAllowedBucketIds()\n }\n\n /**\n * Takes an upload URL from the in-memory pool for the given bucket.\n *\n * @param bucketId - Bucket to check out an upload URL for.\n *\n * @returns A reusable upload URL entry, or `null` if none are pooled.\n */\n checkoutUploadUrl(bucketId: BucketId): UploadUrlEntry | null {\n return this.inner.checkoutUploadUrl(bucketId)\n }\n\n /**\n * Returns a still-valid upload URL to the pool for reuse.\n *\n * @param bucketId - Bucket the upload URL belongs to.\n * @param entry - Upload URL entry to return.\n */\n returnUploadUrl(bucketId: BucketId, entry: UploadUrlEntry): void {\n this.inner.returnUploadUrl(bucketId, entry)\n }\n\n /**\n * Removes an upload URL from the pool after an upload error.\n *\n * @param bucketId - Bucket the failed upload URL belongs to.\n * @param entry - Upload URL entry to evict.\n */\n evictUploadUrl(bucketId: BucketId, entry: UploadUrlEntry): void {\n this.inner.evictUploadUrl(bucketId, entry)\n }\n\n /**\n * Takes a large-file part upload URL from the in-memory pool.\n *\n * @param fileId - Large file to check out a part upload URL for.\n *\n * @returns A reusable part upload URL entry, or `null` if none are pooled.\n */\n checkoutPartUploadUrl(fileId: string): UploadUrlEntry | null {\n return this.inner.checkoutPartUploadUrl(fileId)\n }\n\n /**\n * Returns a still-valid part upload URL to the pool for reuse.\n *\n * @param fileId - Large file the part upload URL belongs to.\n * @param entry - Part upload URL entry to return.\n */\n returnPartUploadUrl(fileId: string, entry: UploadUrlEntry): void {\n this.inner.returnPartUploadUrl(fileId, entry)\n }\n\n /**\n * Removes a part upload URL from the pool after an error.\n *\n * @param fileId - Large file the failed part upload URL belongs to.\n * @param entry - Part upload URL entry to evict.\n */\n evictPartUploadUrl(fileId: string, entry: UploadUrlEntry): void {\n this.inner.evictPartUploadUrl(fileId, entry)\n }\n\n /**\n * Awaits any pending disk writes. Call before process exit to ensure the\n * latest state is persisted.\n *\n * @returns A promise that resolves once all queued writes are flushed.\n */\n async flushed(): Promise<void> {\n await this.writeQueue\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"file.cjs","names":[],"sources":["../../src/auth/file.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { chmod, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join } from 'node:path'\nimport type { AuthorizeAccountResponse } from '../types/auth.ts'\nimport type { BucketId } from '../types/ids.ts'\nimport type { AccountInfo, AuthContextAwareAccountInfo, UploadUrlEntry } from './account-info.ts'\nimport { InMemoryAccountInfo } from './in-memory.ts'\nimport { getRealmUrl } from './realms.ts'\n\nconst PRIVATE_FILE_MODE = 0o600\nconst PERSISTED_AUTH_VERSION = 1\nconst BUILT_IN_PRODUCTION_REALM_URL = getRealmUrl('production')\nconst BUILT_IN_STAGING_REALM_URL = getRealmUrl('staging')\nconst PRODUCTION_HOST_SUFFIX = 'backblazeb2.com'\nconst STAGING_HOST_SUFFIX = 'backblaze.net'\n\ninterface PersistedAuthState {\n readonly _b2sdk?: {\n readonly version?: number\n readonly realmUrl?: string\n readonly applicationKeyId?: string\n }\n}\n\n/** Reason a loaded auth cache entry was ignored for the current client. */\nexport type FileAccountInfoDiscardReason =\n | 'realm_mismatch'\n | 'application_key_mismatch'\n | 'endpoint_mismatch'\n\n/** Event emitted when a loaded cache entry is ignored without modifying disk. */\nexport interface FileAccountInfoDiscardEvent {\n /** Absolute path of the auth cache file. */\n readonly path: string\n /** Why the loaded auth entry could not be reused. */\n readonly reason: FileAccountInfoDiscardReason\n /** Realm URL stored in the cache metadata, or null for legacy entries. */\n readonly cachedRealmUrl: string | null | undefined\n /** Realm URL configured on the current client, if already bound. */\n readonly expectedRealmUrl: string | undefined\n /** Application key ID stored in cache metadata, or null for legacy entries. */\n readonly cachedApplicationKeyId: string | null | undefined\n /** Application key ID configured on the current client, if already bound. */\n readonly expectedApplicationKeyId: string | undefined\n}\n\n/** Event emitted when an asynchronous auth-cache write fails. */\nexport interface FileAccountInfoWriteErrorEvent {\n /** Absolute path of the auth cache file. */\n readonly path: string\n /** Underlying filesystem or serialization error. */\n readonly error: unknown\n}\n\n/** Optional observers for file-backed auth cache behavior. */\nexport interface FileAccountInfoOptions {\n /**\n * Called when loaded auth is ignored because its metadata or cached\n * endpoints are unsafe for this client. The on-disk file is left untouched\n * so another process using a different realm/key is not clobbered.\n */\n readonly onDiscard?: (event: FileAccountInfoDiscardEvent) => void\n /** Called when an asynchronous auth-cache write fails. */\n readonly onWriteError?: (event: FileAccountInfoWriteErrorEvent) => void\n}\n\nfunction readPersistedAuthState(value: unknown): {\n auth: AuthorizeAccountResponse\n realmUrl: string | null\n applicationKeyId: string | null\n} | null {\n if (value === null || typeof value !== 'object') return null\n\n const maybeState = value as PersistedAuthState\n if (\n maybeState._b2sdk?.version !== undefined &&\n maybeState._b2sdk.version !== PERSISTED_AUTH_VERSION\n ) {\n return null\n }\n const realmUrl =\n maybeState._b2sdk !== undefined && typeof maybeState._b2sdk.realmUrl === 'string'\n ? maybeState._b2sdk.realmUrl\n : null\n const applicationKeyId =\n maybeState._b2sdk !== undefined && typeof maybeState._b2sdk.applicationKeyId === 'string'\n ? maybeState._b2sdk.applicationKeyId\n : null\n return { auth: value as AuthorizeAccountResponse, realmUrl, applicationKeyId }\n}\n\nfunction isProductionBackblazeEndpoint(url: string): boolean {\n try {\n const parsed = new URL(url)\n return parsed.protocol === 'https:' && hasHostSuffix(parsed.hostname, PRODUCTION_HOST_SUFFIX)\n } catch {\n return false\n }\n}\n\nfunction isProductionAuthResponse(auth: AuthorizeAccountResponse): boolean {\n try {\n const storageApi = auth.apiInfo.storageApi\n return [storageApi.apiUrl, storageApi.downloadUrl, storageApi.s3ApiUrl].every(\n isProductionBackblazeEndpoint,\n )\n } catch {\n return false\n }\n}\n\nfunction verifiedRealmEndpointSuffix(realmUrl: string): string | null {\n try {\n const realm = new URL(realmUrl)\n if (realm.origin === new URL(BUILT_IN_PRODUCTION_REALM_URL).origin) {\n return PRODUCTION_HOST_SUFFIX\n }\n if (realm.origin === new URL(BUILT_IN_STAGING_REALM_URL).origin) {\n return STAGING_HOST_SUFFIX\n }\n } catch {\n // Fall through below.\n }\n return null\n}\n\nfunction hasHostSuffix(hostname: string, suffix: string): boolean {\n const host = hostname.toLowerCase()\n const lowered = suffix.toLowerCase()\n return host === lowered || host.endsWith(`.${lowered}`)\n}\n\nfunction isEndpointInRealm(url: string, realmUrl: string): boolean {\n try {\n const realm = new URL(realmUrl)\n const endpoint = new URL(url)\n const endpointHost = endpoint.hostname.toLowerCase()\n const realmHost = realm.hostname.toLowerCase()\n if (isLoopbackEndpointHost(endpointHost) || isLoopbackEndpointHost(realmHost)) {\n return endpoint.origin === realm.origin\n }\n\n const verifiedSuffix = verifiedRealmEndpointSuffix(realmUrl)\n if (verifiedSuffix !== null) {\n return endpoint.protocol === 'https:' && hasHostSuffix(endpointHost, verifiedSuffix)\n }\n\n return endpoint.protocol === realm.protocol && hasHostSuffix(endpointHost, realmHost)\n } catch {\n return false\n }\n}\n\nfunction isLoopbackEndpointHost(host: string): boolean {\n if (host === 'localhost' || host === '::1' || host === '[::1]') return true\n\n const parts = host.split('.')\n return (\n parts.length === 4 &&\n parts[0] === '127' &&\n parts.every((part) => /^\\d+$/.test(part) && Number(part) <= 255)\n )\n}\n\nfunction authEndpointsMatchRealm(auth: AuthorizeAccountResponse, realmUrl: string): boolean {\n try {\n const storageApi = auth.apiInfo.storageApi\n return [storageApi.apiUrl, storageApi.downloadUrl, storageApi.s3ApiUrl].every((url) =>\n isEndpointInRealm(url, realmUrl),\n )\n } catch {\n return false\n }\n}\n\n/**\n * Node-only {@link AccountInfo} backend that persists the authorization\n * response to a JSON file. Upload URL pools remain in memory because URLs are\n * short-lived and shouldn't be shared across processes.\n * The JSON file contains a live B2 authorization token, so treat it as\n * sensitive and keep it out of shared or world-readable locations.\n * `FileAccountInfo` serializes writes within one process but does not provide\n * inter-process locking. If multiple processes share one path, use\n * {@link FileAccountInfoOptions.onDiscard} and {@link FileAccountInfoOptions.onWriteError}\n * to observe cache churn, or prefer one cache path per resolved realm and key.\n *\n * On instantiation, call {@link FileAccountInfo.load} to populate state from\n * disk (or start fresh if the file is missing or corrupt). The authorization\n * response is written back to disk on every {@link setAuth} or {@link clear}\n * call so a process restart can resume without re-authorizing.\n * Cache files without an SDK metadata application-key binding are ignored once\n * bound to a `B2Client`, because the SDK cannot verify which key wrote them.\n *\n * This module imports `node:fs/promises`; do not load it in browser bundles.\n */\nexport class FileAccountInfo implements AccountInfo, AuthContextAwareAccountInfo {\n private readonly inner = new InMemoryAccountInfo()\n private writeQueue: Promise<void> = Promise.resolve()\n private realmUrl: string | undefined\n private loadedAuthRealmUrl: string | null | undefined\n private applicationKeyId: string | undefined\n private loadedAuthApplicationKeyId: string | null | undefined\n\n /**\n * Constructs a `FileAccountInfo` bound to a JSON file on disk. The file is\n * created on the first `setAuth` write; reading it back happens via\n * {@link load}.\n *\n * @param path - Absolute path to the JSON file that backs this account info.\n * @param options - Optional discard and write-error observers.\n */\n constructor(\n public readonly path: string,\n private readonly options: FileAccountInfoOptions = {},\n ) {}\n\n /**\n * Reads the JSON file at the configured path and populates in-memory state.\n * If the file is missing or contains invalid JSON, leaves state empty and\n * returns silently (a re-auth is expected next).\n *\n * @returns A promise that resolves when load is complete.\n */\n async load(): Promise<void> {\n try {\n const text = await readFile(this.path, 'utf8')\n const parsed = readPersistedAuthState(JSON.parse(text) as unknown)\n if (parsed === null) return\n this.inner.setAuth(parsed.auth)\n this.loadedAuthRealmUrl = parsed.realmUrl\n this.loadedAuthApplicationKeyId = parsed.applicationKeyId\n this.discardMismatchedAuth()\n } catch {\n // Missing file, invalid JSON, or permission denied: start empty.\n }\n }\n\n /**\n * Binds this cache to the configured realm URL. `B2Client` calls this when a\n * FileAccountInfo instance is supplied, so stale auth produced by another\n * realm is discarded before it can be used.\n *\n * @param realmUrl - Resolved authorize-account realm URL for the client.\n */\n setRealmUrl(realmUrl: string): void {\n this.realmUrl = realmUrl\n this.discardMismatchedAuth()\n }\n\n /**\n * Binds this cache to the configured application key ID. `B2Client` calls\n * this when a FileAccountInfo instance is supplied, so stale auth produced by\n * another key is discarded before it can be used.\n *\n * @param applicationKeyId - Application key ID configured on the client.\n */\n setApplicationKeyId(applicationKeyId: string): void {\n this.applicationKeyId = applicationKeyId\n this.discardMismatchedAuth()\n }\n\n private discardMismatchedAuth(): void {\n const auth = this.inner.getAuth()\n if (auth === null) return\n\n const reason = this.mismatchReason(auth)\n if (reason === null) return\n\n this.inner.clear()\n this.options.onDiscard?.({\n path: this.path,\n reason,\n cachedRealmUrl: this.loadedAuthRealmUrl,\n expectedRealmUrl: this.realmUrl,\n cachedApplicationKeyId: this.loadedAuthApplicationKeyId,\n expectedApplicationKeyId: this.applicationKeyId,\n })\n this.loadedAuthRealmUrl = undefined\n this.loadedAuthApplicationKeyId = undefined\n }\n\n private mismatchReason(auth: AuthorizeAccountResponse): FileAccountInfoDiscardReason | null {\n const realmMatches =\n this.realmUrl === undefined ||\n this.loadedAuthRealmUrl === this.realmUrl ||\n (this.loadedAuthRealmUrl === null &&\n this.realmUrl === BUILT_IN_PRODUCTION_REALM_URL &&\n isProductionAuthResponse(auth))\n const applicationKeyMatches =\n this.applicationKeyId === undefined ||\n this.loadedAuthApplicationKeyId === this.applicationKeyId\n if (!realmMatches) return 'realm_mismatch'\n if (!applicationKeyMatches) return 'application_key_mismatch'\n if (this.realmUrl !== undefined && !authEndpointsMatchRealm(auth, this.realmUrl)) {\n return 'endpoint_mismatch'\n }\n\n return null\n }\n\n /**\n * Persists the current auth response to the file. Atomically queues writes\n * so concurrent updates serialize.\n *\n * @returns A promise that resolves when the write completes.\n */\n private flush(): Promise<void> {\n const auth = this.inner.getAuth()\n const next = this.writeQueue.then(async () => {\n if (auth === null) {\n try {\n await this.writePrivateFileAtomically('')\n } catch {\n // Best-effort\n }\n return\n }\n const contents =\n this.realmUrl !== undefined\n ? JSON.stringify({\n ...auth,\n _b2sdk: {\n version: PERSISTED_AUTH_VERSION,\n realmUrl: this.realmUrl,\n ...(this.applicationKeyId !== undefined\n ? { applicationKeyId: this.applicationKeyId }\n : {}),\n },\n })\n : JSON.stringify(auth)\n await this.writePrivateFileAtomically(contents)\n })\n this.writeQueue = next.catch((error: unknown) => {\n this.options.onWriteError?.({ path: this.path, error })\n })\n return next\n }\n\n private async writePrivateFileAtomically(contents: string): Promise<void> {\n const dir = dirname(this.path)\n const tempPath = join(dir, `.${basename(this.path)}.${process.pid}.${randomUUID()}.tmp`)\n await mkdir(dir, { recursive: true })\n\n try {\n await writeFile(tempPath, contents, { encoding: 'utf8', mode: PRIVATE_FILE_MODE })\n await chmod(tempPath, PRIVATE_FILE_MODE)\n await rename(tempPath, this.path)\n await chmod(this.path, PRIVATE_FILE_MODE)\n } catch (error) {\n await rm(tempPath, { force: true }).catch(() => {})\n throw error\n }\n }\n\n /**\n * Stores the given authorization response and triggers an async write to disk.\n *\n * @param auth - The authorize-account response to persist.\n */\n setAuth(auth: AuthorizeAccountResponse): void {\n this.inner.setAuth(auth)\n this.loadedAuthRealmUrl = this.realmUrl ?? null\n this.loadedAuthApplicationKeyId = this.applicationKeyId ?? null\n void this.flush()\n }\n\n /**\n * Returns the currently cached authorization response, or `null` if none.\n *\n * @returns The cached authorization response, or `null` if not authorized.\n */\n getAuth(): AuthorizeAccountResponse | null {\n return this.inner.getAuth()\n }\n\n /** Discards in-memory state and clears the on-disk file. */\n clear(): void {\n this.inner.clear()\n this.loadedAuthRealmUrl = undefined\n this.loadedAuthApplicationKeyId = undefined\n void this.flush()\n }\n\n /**\n * Returns the cached B2 API URL.\n *\n * @returns The base URL for B2 API calls.\n */\n getApiUrl(): string {\n return this.inner.getApiUrl()\n }\n\n /**\n * Returns the cached file-download URL.\n *\n * @returns The base URL for file downloads.\n */\n getDownloadUrl(): string {\n return this.inner.getDownloadUrl()\n }\n\n /**\n * Returns the cached session authorization token.\n *\n * @returns The current authorization token.\n */\n getAuthToken(): string {\n return this.inner.getAuthToken()\n }\n\n /**\n * Returns the cached account identifier.\n *\n * @returns The authorized account ID.\n */\n getAccountId(): string {\n return this.inner.getAccountId()\n }\n\n /**\n * Returns the server-recommended part size for multipart uploads.\n *\n * @returns The recommended part size in bytes.\n */\n getRecommendedPartSize(): number {\n return this.inner.getRecommendedPartSize()\n }\n\n /**\n * Returns the server-enforced minimum part size for multipart uploads.\n *\n * @returns The absolute minimum part size in bytes.\n */\n getAbsoluteMinimumPartSize(): number {\n return this.inner.getAbsoluteMinimumPartSize()\n }\n\n /**\n * Returns the cached S3-compatible API URL.\n *\n * @returns The base URL for the S3-compatible API.\n */\n getS3ApiUrl(): string {\n return this.inner.getS3ApiUrl()\n }\n\n /**\n * Returns the bucket the key is restricted to, if any.\n *\n * @returns The restricted bucket ID, or `null` if the key is unrestricted.\n */\n getAllowedBucketId(): BucketId | null {\n return this.inner.getAllowedBucketId()\n }\n\n /**\n * Returns the buckets the key is restricted to, if any.\n *\n * @returns The restricted bucket IDs, or `null` if the key is unrestricted.\n */\n getAllowedBucketIds(): readonly BucketId[] | null {\n return this.inner.getAllowedBucketIds()\n }\n\n /**\n * Takes an upload URL from the in-memory pool for the given bucket.\n *\n * @param bucketId - Bucket to check out an upload URL for.\n *\n * @returns A reusable upload URL entry, or `null` if none are pooled.\n */\n checkoutUploadUrl(bucketId: BucketId): UploadUrlEntry | null {\n return this.inner.checkoutUploadUrl(bucketId)\n }\n\n /**\n * Returns a still-valid upload URL to the pool for reuse.\n *\n * @param bucketId - Bucket the upload URL belongs to.\n * @param entry - Upload URL entry to return.\n */\n returnUploadUrl(bucketId: BucketId, entry: UploadUrlEntry): void {\n this.inner.returnUploadUrl(bucketId, entry)\n }\n\n /**\n * Removes an upload URL from the pool after an upload error.\n *\n * @param bucketId - Bucket the failed upload URL belongs to.\n * @param entry - Upload URL entry to evict.\n */\n evictUploadUrl(bucketId: BucketId, entry: UploadUrlEntry): void {\n this.inner.evictUploadUrl(bucketId, entry)\n }\n\n /**\n * Takes a large-file part upload URL from the in-memory pool.\n *\n * @param fileId - Large file to check out a part upload URL for.\n *\n * @returns A reusable part upload URL entry, or `null` if none are pooled.\n */\n checkoutPartUploadUrl(fileId: string): UploadUrlEntry | null {\n return this.inner.checkoutPartUploadUrl(fileId)\n }\n\n /**\n * Returns a still-valid part upload URL to the pool for reuse.\n *\n * @param fileId - Large file the part upload URL belongs to.\n * @param entry - Part upload URL entry to return.\n */\n returnPartUploadUrl(fileId: string, entry: UploadUrlEntry): void {\n this.inner.returnPartUploadUrl(fileId, entry)\n }\n\n /**\n * Removes a part upload URL from the pool after an error.\n *\n * @param fileId - Large file the failed part upload URL belongs to.\n * @param entry - Part upload URL entry to evict.\n */\n evictPartUploadUrl(fileId: string, entry: UploadUrlEntry): void {\n this.inner.evictPartUploadUrl(fileId, entry)\n }\n\n /**\n * Awaits any pending disk writes. Call before process exit to ensure the\n * latest state is persisted.\n *\n * @returns A promise that resolves once all queued writes are flushed.\n */\n async flushed(): Promise<void> {\n await this.writeQueue\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
const require_ids = require("../types/ids.cjs");
|
|
2
|
+
const require_errors_index = require("../errors/index.cjs");
|
|
3
|
+
const require_paginator = require("../util/paginator.cjs");
|
|
4
|
+
const require_redaction_placeholders = require("../partner/redaction-placeholders.cjs");
|
|
5
|
+
const require_auth_core = require("../partner/auth-core.cjs");
|
|
6
|
+
const require_raw = require("./raw.cjs");
|
|
7
|
+
//#region src/backup/client.ts
|
|
8
|
+
var MASTER_KEY_REDACTED = "[redacted Master Application Key]";
|
|
9
|
+
function badListComputersResponse(message) {
|
|
10
|
+
return new require_errors_index.BadJsonError({
|
|
11
|
+
status: 400,
|
|
12
|
+
code: "bad_json",
|
|
13
|
+
message: `bz_list_computers response did not match documented wire shape: ${message}`
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === "object" && value !== null;
|
|
18
|
+
}
|
|
19
|
+
function normalizeListComputersResponse(response) {
|
|
20
|
+
if (!isRecord(response) || Array.isArray(response)) throw badListComputersResponse("expected a JSON object");
|
|
21
|
+
const resultComputers = response["computers"];
|
|
22
|
+
if (!Array.isArray(resultComputers)) throw badListComputersResponse("response must include a computers array");
|
|
23
|
+
const cursor = response["nextComputerId"];
|
|
24
|
+
if (cursor !== null && typeof cursor !== "string") throw badListComputersResponse("response must include nextComputerId as string or null");
|
|
25
|
+
return {
|
|
26
|
+
nextComputerId: cursor === null ? null : require_ids.computerId(cursor),
|
|
27
|
+
computers: resultComputers
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* High-level Computer Backup API client for listing and deleting backup records.
|
|
32
|
+
*
|
|
33
|
+
* The client uses Partner authorization and the `backupApiUrl` returned by
|
|
34
|
+
* `authorizePartner`. The target account must belong to a group with
|
|
35
|
+
* Enterprise Controls enabled, and the caller must be a group admin. Deleting
|
|
36
|
+
* a backup also requires the Enterprise Controls setting that permits admins
|
|
37
|
+
* to delete member backups.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const backup = new BackupClient({
|
|
42
|
+
* masterKeyId: process.env.B2_MASTER_KEY_ID,
|
|
43
|
+
* masterKey: process.env.B2_MASTER_KEY,
|
|
44
|
+
* })
|
|
45
|
+
* await backup.authorize()
|
|
46
|
+
* for await (const computer of backup.paginateComputers()) {
|
|
47
|
+
* console.log(computer.computerName)
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.
|
|
52
|
+
*/
|
|
53
|
+
var BackupClient = class {
|
|
54
|
+
/** Low-level client for direct Computer Backup API calls. */
|
|
55
|
+
raw;
|
|
56
|
+
/** Partner authorization state storage (tokens, URLs, capabilities). */
|
|
57
|
+
partnerAccountInfo;
|
|
58
|
+
/**
|
|
59
|
+
* SSRF allow-list applied by the default transport. `null` when
|
|
60
|
+
* a custom transport was supplied; in that case the SDK does not own the
|
|
61
|
+
* guard.
|
|
62
|
+
*/
|
|
63
|
+
urlGuard;
|
|
64
|
+
authCore;
|
|
65
|
+
/**
|
|
66
|
+
* Creates a new BackupClient. Call {@link authorize} before making API requests
|
|
67
|
+
* unless `partnerAccountInfo` already contains valid Partner authorization.
|
|
68
|
+
*
|
|
69
|
+
* @param options - Configuration including credentials, realm, and transport settings.
|
|
70
|
+
*/
|
|
71
|
+
constructor(options) {
|
|
72
|
+
this.authCore = new require_auth_core.PartnerAuthCore(options);
|
|
73
|
+
this.partnerAccountInfo = this.authCore.partnerAccountInfo;
|
|
74
|
+
this.urlGuard = this.authCore.urlGuard;
|
|
75
|
+
this.raw = new require_raw.BackupRawClient({
|
|
76
|
+
transport: this.authCore.transport,
|
|
77
|
+
...this.authCore.cachedEndpointSuffixes !== void 0 ? { authorizedBackupEndpointSuffixes: this.authCore.cachedEndpointSuffixes } : {}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Authenticates with B2 Partner authorization and stores the authorization state.
|
|
82
|
+
*
|
|
83
|
+
* @param options - Optional abort signal.
|
|
84
|
+
*
|
|
85
|
+
* @returns The Partner authorization response containing tokens, URLs, and capabilities.
|
|
86
|
+
*
|
|
87
|
+
* @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.
|
|
88
|
+
*/
|
|
89
|
+
async authorize(options) {
|
|
90
|
+
const auth = await this.authCore.authorize(options);
|
|
91
|
+
this.raw.setAuthorizedEndpointSuffixes(this.authCore.activateEndpointSuffixes(auth));
|
|
92
|
+
return auth;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Lists active Computer Backup records for an account.
|
|
96
|
+
*
|
|
97
|
+
* @param options - Optional target account and pagination settings.
|
|
98
|
+
*
|
|
99
|
+
* @returns A normalized page of computer backup records with an optional continuation cursor.
|
|
100
|
+
*
|
|
101
|
+
* @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.
|
|
102
|
+
*/
|
|
103
|
+
async listComputers(options) {
|
|
104
|
+
const auth = this.ensureBackupAuthorization();
|
|
105
|
+
const { backupApiUrl, authToken, accountId } = this.backupCoordinates(auth, options?.accountId);
|
|
106
|
+
return normalizeListComputersResponse(await this.raw.listComputers(backupApiUrl, authToken, {
|
|
107
|
+
accountId,
|
|
108
|
+
...options?.startComputerId !== void 0 ? { startComputerId: options.startComputerId } : {},
|
|
109
|
+
...options?.pageSize !== void 0 ? { maxComputerCount: options.pageSize } : {}
|
|
110
|
+
}, options?.signal !== void 0 ? { signal: options.signal } : void 0));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Async iterator that yields every active Computer Backup record for an account.
|
|
114
|
+
*
|
|
115
|
+
* @param options - Optional target account, page size, and abort signal.
|
|
116
|
+
*
|
|
117
|
+
* @returns An async iterable of {@link ComputerBackup} entries.
|
|
118
|
+
*
|
|
119
|
+
* @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.
|
|
120
|
+
*/
|
|
121
|
+
paginateComputers(options) {
|
|
122
|
+
return require_paginator.paginateItems(async (cursor) => {
|
|
123
|
+
const resp = await this.listComputers({
|
|
124
|
+
...options?.accountId !== void 0 ? { accountId: options.accountId } : {},
|
|
125
|
+
...options?.pageSize !== void 0 ? { pageSize: options.pageSize } : {},
|
|
126
|
+
...cursor !== void 0 ? { startComputerId: cursor } : {},
|
|
127
|
+
...options?.signal !== void 0 ? { signal: options.signal } : {}
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
page: resp,
|
|
131
|
+
nextCursor: resp.nextComputerId ?? void 0
|
|
132
|
+
};
|
|
133
|
+
}, (page) => page.computers, options?.signal);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Deletes a Computer Backup record.
|
|
137
|
+
*
|
|
138
|
+
* This destructive mutation is not automatically retried or reauthorized in
|
|
139
|
+
* place; callers should reconcile backup state before issuing a new delete
|
|
140
|
+
* after an ambiguous failure.
|
|
141
|
+
*
|
|
142
|
+
* @param options - Target account, computer ID, and optional abort signal.
|
|
143
|
+
*
|
|
144
|
+
* @returns The deleted computer backup record array.
|
|
145
|
+
*
|
|
146
|
+
* @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.
|
|
147
|
+
*/
|
|
148
|
+
async deleteComputer(options) {
|
|
149
|
+
const auth = this.ensureBackupAuthorization();
|
|
150
|
+
const { backupApiUrl, authToken, accountId } = this.backupCoordinates(auth, options.accountId);
|
|
151
|
+
return this.raw.deleteComputer(backupApiUrl, authToken, {
|
|
152
|
+
accountId,
|
|
153
|
+
computerId: options.computerId
|
|
154
|
+
}, options.signal !== void 0 ? { signal: options.signal } : void 0);
|
|
155
|
+
}
|
|
156
|
+
ensureBackupAuthorization() {
|
|
157
|
+
const auth = this.partnerAccountInfo.getAuth();
|
|
158
|
+
if (auth === null) throw new require_errors_index.B2PartnerAuthorizationError("Not authorized. Call BackupClient.authorize() first.");
|
|
159
|
+
const suffixes = this.authCore.activateEndpointSuffixes(auth);
|
|
160
|
+
this.raw.setAuthorizedEndpointSuffixes(suffixes);
|
|
161
|
+
return auth;
|
|
162
|
+
}
|
|
163
|
+
backupCoordinates(auth, accountIdOverride) {
|
|
164
|
+
const backupApiUrl = auth.apiInfo.backupApi?.backupApiUrl;
|
|
165
|
+
if (backupApiUrl === void 0) throw new require_errors_index.B2PartnerAuthorizationError("Computer Backup API is not available; Partner authorization did not return apiInfo.backupApi.");
|
|
166
|
+
return {
|
|
167
|
+
backupApiUrl,
|
|
168
|
+
authToken: auth.authorizationToken,
|
|
169
|
+
accountId: accountIdOverride ?? auth.accountId
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Hides credentials and Partner tokens from `JSON.stringify(client)`.
|
|
174
|
+
*
|
|
175
|
+
* @returns A redacted diagnostic object.
|
|
176
|
+
*/
|
|
177
|
+
toJSON() {
|
|
178
|
+
return {
|
|
179
|
+
type: "BackupClient",
|
|
180
|
+
credentials: MASTER_KEY_REDACTED,
|
|
181
|
+
authorization: this.partnerAccountInfo.getAuth() === null ? "[unauthorized]" : require_redaction_placeholders.PARTNER_TOKEN_REDACTED,
|
|
182
|
+
authorized: this.partnerAccountInfo.getAuth() !== null,
|
|
183
|
+
urlGuardAllowedSuffixes: this.urlGuard?.getAllowedSuffixes() ?? null
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Hides credentials and Partner tokens from default stringification.
|
|
188
|
+
*
|
|
189
|
+
* @returns A short redacted label.
|
|
190
|
+
*/
|
|
191
|
+
toString() {
|
|
192
|
+
return `[BackupClient ${MASTER_KEY_REDACTED}]`;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Hides credentials and Partner tokens from Node's `util.inspect`.
|
|
196
|
+
*
|
|
197
|
+
* @returns A short redacted label.
|
|
198
|
+
*/
|
|
199
|
+
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
200
|
+
return this.toString();
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
//#endregion
|
|
204
|
+
exports.BackupClient = BackupClient;
|
|
205
|
+
|
|
206
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.cjs","names":[],"sources":["../../src/backup/client.ts"],"sourcesContent":["import { B2PartnerAuthorizationError, BadJsonError } from '../errors/index.ts'\nimport type { RetryOptions } from '../http/retry.ts'\nimport type { HttpTransport } from '../http/transport.ts'\nimport type { UrlGuard } from '../http/url-guard.ts'\nimport type { PartnerAccountInfo } from '../partner/account-info.ts'\nimport { PartnerAuthCore } from '../partner/auth-core.ts'\nimport { PARTNER_TOKEN_REDACTED } from '../partner/redaction.ts'\nimport type {\n ComputerBackup,\n DeleteComputerResponse,\n ListComputersResponse,\n ListComputersResult,\n} from '../types/backup.ts'\nimport { type AccountId, type ComputerId, computerId, type PartnerToken } from '../types/ids.ts'\nimport type { PartnerAuthorizeResponse } from '../types/partner.ts'\nimport { paginateItems } from '../util/paginator.ts'\nimport { BackupRawClient } from './raw.ts'\n\nconst MASTER_KEY_REDACTED = '[redacted Master Application Key]'\n\n/**\n * Configuration options for creating a {@link BackupClient}.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\nexport interface BackupClientOptions {\n /** The Master Application Key ID for the partner administrator account. */\n readonly masterKeyId: string\n /** The Master Application Key secret. */\n readonly masterKey: string\n /**\n * B2 realm to authenticate against. Accepts a known realm-map key\n * (`\"production\"` or `\"staging\"`) or a direct base URL. Custom HTTPS hosts\n * and loopback IP literal HTTP URLs for local testing are trusted with the\n * Master Application Key during Partner authorize, so never derive `realm`\n * from untrusted input. Defaults to `\"production\"`.\n */\n readonly realm?: string\n /**\n * Shared Partner authorization state. Defaults to\n * {@link InMemoryPartnerAccountInfo}. Pass the same store to\n * {@link BackupClient} and Partner clients when both surfaces should reuse\n * one Partner token. Cached authorization whose endpoints fail this client's\n * realm policy is ignored locally until `authorize()` replaces it; shared\n * stores are not cleared during construction.\n */\n readonly partnerAccountInfo?: PartnerAccountInfo\n /** Custom HTTP transport. Defaults to `FetchTransport`. Wrapped by `RetryTransport`. */\n readonly transport?: HttpTransport\n /** Override retry behavior (max retries, backoff, and per-attempt timeout). */\n readonly retry?: Partial<RetryOptions>\n /** Custom user-agent string prepended to the SDK default. */\n readonly userAgent?: string\n /**\n * Additional SSRF allow-list host suffixes for the default transport.\n *\n * Only consulted when {@link BackupClientOptions.transport} is unset; a\n * custom transport is the user's responsibility to harden.\n */\n readonly additionalAllowedHostSuffixes?: readonly string[]\n /**\n * Explicitly disable the default SSRF guard after authorize.\n *\n * Intended only for controlled simulator/private-proxy tests. Never enable\n * it for URLs derived from untrusted input or production credentials.\n */\n readonly disableSsrfGuard?: boolean\n /**\n * Follow same-origin GET/HEAD redirects in the default fetch transport after\n * checking each target with the SSRF guard. POST redirects remain blocked.\n * Defaults to true.\n */\n readonly followSameOriginRedirects?: boolean\n /**\n * Allow direct custom authorize realms for tests or private proxies.\n * Leave disabled unless the configured host is trusted with the Master\n * Application Key.\n */\n readonly allowCustomAuthorizeRealm?: boolean\n}\n\n/**\n * Options for Partner authorization performed by {@link BackupClient}.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\nexport interface BackupAuthorizeOptions {\n /** Abort signal for cancelling the authorize request. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Options for listing Computer Backup records.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\nexport interface ListComputersOptions {\n /**\n * Account whose active computer backups should be listed. Defaults to the\n * partner administrator account ID from authorization.\n */\n readonly accountId?: AccountId\n /** Computer ID to start listing from for pagination. */\n readonly startComputerId?: ComputerId\n /** Maximum computers to return in this page. Forwarded to `maxComputerCount`. */\n readonly pageSize?: number\n /** Abort signal for cancelling the request. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Options for paginating Computer Backup records.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\nexport interface PaginateComputersOptions extends Omit<ListComputersOptions, 'startComputerId'> {\n /**\n * Aborts the iteration between page fetches. The signal is also forwarded to\n * each underlying list request.\n */\n readonly signal?: AbortSignal\n}\n\n/**\n * Options for deleting a Computer Backup record.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\nexport interface DeleteComputerOptions {\n /**\n * Account that owns the computer backup. Defaults to the partner\n * administrator account ID from authorization.\n */\n readonly accountId?: AccountId\n /** Computer ID of the backup to delete. */\n readonly computerId: ComputerId\n /** Abort signal for cancelling the request. */\n readonly signal?: AbortSignal\n}\n\ninterface BackupCoordinates {\n readonly backupApiUrl: string\n readonly authToken: PartnerToken\n readonly accountId: AccountId\n}\n\nfunction badListComputersResponse(message: string): BadJsonError {\n return new BadJsonError({\n status: 400,\n code: 'bad_json',\n message: `bz_list_computers response did not match documented wire shape: ${message}`,\n })\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null\n}\n\nfunction normalizeListComputersResponse(response: ListComputersResponse): ListComputersResult {\n // Archived Backup API docs model bz_list_computers as a single result object\n // with a pagination cursor and a `computers` array (an empty account returns\n // an empty `computers` array, not a top-level empty array).\n if (!isRecord(response) || Array.isArray(response)) {\n throw badListComputersResponse('expected a JSON object')\n }\n const resultComputers = response['computers']\n if (!Array.isArray(resultComputers)) {\n throw badListComputersResponse('response must include a computers array')\n }\n const cursor = response['nextComputerId']\n if (cursor !== null && typeof cursor !== 'string') {\n throw badListComputersResponse('response must include nextComputerId as string or null')\n }\n\n return {\n nextComputerId: cursor === null ? null : computerId(cursor),\n computers: resultComputers as readonly ComputerBackup[],\n }\n}\n\n/** Redacted JSON diagnostic shape emitted by {@link BackupClient.toJSON}. */\nexport interface BackupClientJson {\n /** Object kind for log consumers. */\n readonly type: 'BackupClient'\n /** Redacted Master Application Key credential placeholder. */\n readonly credentials: string\n /** Redacted Partner token placeholder, or unauthorized state. */\n readonly authorization: string\n /** Whether this client currently has Partner authorization state. */\n readonly authorized: boolean\n /** Current default URL guard suffixes, or null when a custom transport owns guarding. */\n readonly urlGuardAllowedSuffixes: readonly string[] | null\n}\n\n/**\n * High-level Computer Backup API client for listing and deleting backup records.\n *\n * The client uses Partner authorization and the `backupApiUrl` returned by\n * `authorizePartner`. The target account must belong to a group with\n * Enterprise Controls enabled, and the caller must be a group admin. Deleting\n * a backup also requires the Enterprise Controls setting that permits admins\n * to delete member backups.\n *\n * @example\n * ```ts\n * const backup = new BackupClient({\n * masterKeyId: process.env.B2_MASTER_KEY_ID,\n * masterKey: process.env.B2_MASTER_KEY,\n * })\n * await backup.authorize()\n * for await (const computer of backup.paginateComputers()) {\n * console.log(computer.computerName)\n * }\n * ```\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\nexport class BackupClient {\n /** Low-level client for direct Computer Backup API calls. */\n readonly raw: BackupRawClient\n /** Partner authorization state storage (tokens, URLs, capabilities). */\n readonly partnerAccountInfo: PartnerAccountInfo\n /**\n * SSRF allow-list applied by the default transport. `null` when\n * a custom transport was supplied; in that case the SDK does not own the\n * guard.\n */\n readonly urlGuard: UrlGuard | null\n private readonly authCore: PartnerAuthCore\n\n /**\n * Creates a new BackupClient. Call {@link authorize} before making API requests\n * unless `partnerAccountInfo` already contains valid Partner authorization.\n *\n * @param options - Configuration including credentials, realm, and transport settings.\n */\n constructor(options: BackupClientOptions) {\n this.authCore = new PartnerAuthCore(options)\n this.partnerAccountInfo = this.authCore.partnerAccountInfo\n this.urlGuard = this.authCore.urlGuard\n this.raw = new BackupRawClient({\n transport: this.authCore.transport,\n ...(this.authCore.cachedEndpointSuffixes !== undefined\n ? { authorizedBackupEndpointSuffixes: this.authCore.cachedEndpointSuffixes }\n : {}),\n })\n }\n\n /**\n * Authenticates with B2 Partner authorization and stores the authorization state.\n *\n * @param options - Optional abort signal.\n *\n * @returns The Partner authorization response containing tokens, URLs, and capabilities.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\n async authorize(options?: BackupAuthorizeOptions): Promise<PartnerAuthorizeResponse> {\n const auth = await this.authCore.authorize(options)\n this.raw.setAuthorizedEndpointSuffixes(this.authCore.activateEndpointSuffixes(auth))\n return auth\n }\n\n /**\n * Lists active Computer Backup records for an account.\n *\n * @param options - Optional target account and pagination settings.\n *\n * @returns A normalized page of computer backup records with an optional continuation cursor.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\n async listComputers(options?: ListComputersOptions): Promise<ListComputersResult> {\n const auth = this.ensureBackupAuthorization()\n const { backupApiUrl, authToken, accountId } = this.backupCoordinates(auth, options?.accountId)\n const response = await this.raw.listComputers(\n backupApiUrl,\n authToken,\n {\n accountId,\n ...(options?.startComputerId !== undefined\n ? { startComputerId: options.startComputerId }\n : {}),\n ...(options?.pageSize !== undefined ? { maxComputerCount: options.pageSize } : {}),\n },\n options?.signal !== undefined ? { signal: options.signal } : undefined,\n )\n return normalizeListComputersResponse(response)\n }\n\n /**\n * Async iterator that yields every active Computer Backup record for an account.\n *\n * @param options - Optional target account, page size, and abort signal.\n *\n * @returns An async iterable of {@link ComputerBackup} entries.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\n paginateComputers(options?: PaginateComputersOptions): AsyncIterableIterator<ComputerBackup> {\n return paginateItems(\n async (cursor: ComputerId | undefined) => {\n const resp = await this.listComputers({\n ...(options?.accountId !== undefined ? { accountId: options.accountId } : {}),\n ...(options?.pageSize !== undefined ? { pageSize: options.pageSize } : {}),\n ...(cursor !== undefined ? { startComputerId: cursor } : {}),\n ...(options?.signal !== undefined ? { signal: options.signal } : {}),\n })\n return { page: resp, nextCursor: resp.nextComputerId ?? undefined }\n },\n (page) => page.computers,\n options?.signal,\n )\n }\n\n /**\n * Deletes a Computer Backup record.\n *\n * This destructive mutation is not automatically retried or reauthorized in\n * place; callers should reconcile backup state before issuing a new delete\n * after an ambiguous failure.\n *\n * @param options - Target account, computer ID, and optional abort signal.\n *\n * @returns The deleted computer backup record array.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\n async deleteComputer(options: DeleteComputerOptions): Promise<DeleteComputerResponse> {\n const auth = this.ensureBackupAuthorization()\n const { backupApiUrl, authToken, accountId } = this.backupCoordinates(auth, options.accountId)\n return this.raw.deleteComputer(\n backupApiUrl,\n authToken,\n {\n accountId,\n computerId: options.computerId,\n },\n options.signal !== undefined ? { signal: options.signal } : undefined,\n )\n }\n\n private ensureBackupAuthorization(): PartnerAuthorizeResponse {\n const auth = this.partnerAccountInfo.getAuth()\n if (auth === null) {\n throw new B2PartnerAuthorizationError('Not authorized. Call BackupClient.authorize() first.')\n }\n const suffixes = this.authCore.activateEndpointSuffixes(auth)\n this.raw.setAuthorizedEndpointSuffixes(suffixes)\n return auth\n }\n\n private backupCoordinates(\n auth: PartnerAuthorizeResponse,\n accountIdOverride: AccountId | undefined,\n ): BackupCoordinates {\n const backupApiUrl = auth.apiInfo.backupApi?.backupApiUrl\n if (backupApiUrl === undefined) {\n throw new B2PartnerAuthorizationError(\n 'Computer Backup API is not available; Partner authorization did not return apiInfo.backupApi.',\n )\n }\n return {\n backupApiUrl,\n authToken: auth.authorizationToken,\n accountId: accountIdOverride ?? auth.accountId,\n }\n }\n\n /**\n * Hides credentials and Partner tokens from `JSON.stringify(client)`.\n *\n * @returns A redacted diagnostic object.\n */\n toJSON(): BackupClientJson {\n return {\n type: 'BackupClient',\n credentials: MASTER_KEY_REDACTED,\n authorization:\n this.partnerAccountInfo.getAuth() === null ? '[unauthorized]' : PARTNER_TOKEN_REDACTED,\n authorized: this.partnerAccountInfo.getAuth() !== null,\n urlGuardAllowedSuffixes: this.urlGuard?.getAllowedSuffixes() ?? null,\n }\n }\n\n /**\n * Hides credentials and Partner tokens from default stringification.\n *\n * @returns A short redacted label.\n */\n toString(): string {\n return `[BackupClient ${MASTER_KEY_REDACTED}]`\n }\n\n /**\n * Hides credentials and Partner tokens from Node's `util.inspect`.\n *\n * @returns A short redacted label.\n */\n [Symbol.for('nodejs.util.inspect.custom')](): string {\n return this.toString()\n }\n}\n"],"mappings":"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"}
|