@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/dist/upload/retry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.js","names":[],"sources":["../../src/upload/retry.ts"],"sourcesContent":["import type { AccountInfo, UploadUrlEntry } from '../auth/account-info.ts'\nimport {\n B2Error,\n B2SsrfError,\n BadAuthTokenError,\n BadRequestError,\n BadUploadUrlError,\n NetworkError,\n UploadResponseBodyError,\n} from '../errors/index.ts'\nimport { computeBackoff, DEFAULT_RETRY_OPTIONS, type RetryOptions, sleep } from '../http/retry.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { BucketId, LargeFileId } from '../types/ids.ts'\nimport type { UploadPartResponse } from '../types/upload.ts'\n\n/** Event emitted before an upload is retried against a fresh upload URL. */\nexport interface UploadRetryEvent {\n /** File name being uploaded. */\n readonly fileName: string\n /** Multipart part number, or `null` for a single-request file upload. */\n readonly partNumber: number | null\n /** One-based retry attempt that is about to run. */\n readonly attempt: number\n /** Maximum number of retry attempts allowed for this upload operation. */\n readonly maxRetries: number\n /** Backoff delay in milliseconds before the fresh URL is fetched. */\n readonly delayMs: number\n /** Classified error that triggered the fresh-URL retry. */\n readonly error: B2Error | NetworkError\n}\n\n/** Callback invoked before an upload retry fetches a fresh upload URL. */\nexport type UploadRetryListener = (event: UploadRetryEvent) => void\n\n/** Shared public options for upload-layer fresh-URL retries. */\nexport interface UploadRetryOptions {\n /** Retry settings shared with the main transport retry configuration. */\n readonly retry?: Partial<RetryOptions> | undefined\n /** Callback invoked before a fresh-URL upload retry. */\n readonly onUploadRetry?: UploadRetryListener | undefined\n /**\n * Whether ambiguous failures after an upload POST should be retried,\n * including response-body read failures and transport errors where B2 may\n * have stored the payload before the success response was lost.\n * Defaults to false. Set true only when duplicate versions or parts are\n * acceptable.\n */\n readonly retryResponseBodyFailures?: boolean | undefined\n}\n\ninterface UploadLayerRetryOptions extends Omit<UploadRetryOptions, 'retryResponseBodyFailures'> {\n readonly retryResponseBodyFailures: boolean\n /** Abort signal for cancelling upload attempts and retry backoff sleeps. */\n readonly signal?: AbortSignal | undefined\n}\n\ninterface FreshUrlRetryOptions<T> extends UploadLayerRetryOptions {\n readonly fileName: string\n readonly partNumber: number | null\n readonly checkout: () => UploadUrlEntry | null\n readonly fetchFresh: () => Promise<UploadUrlEntry>\n readonly returnEntry: (entry: UploadUrlEntry) => void\n readonly evictEntry: (entry: UploadUrlEntry) => void\n readonly upload: (entry: UploadUrlEntry) => Promise<T>\n}\n\nconst freshUrlRetryOverride: Partial<RetryOptions> = { maxRetries: 0 }\n\n/**\n * Resolves the public default for `retryResponseBodyFailures`. Callers must\n * opt into replaying ambiguous upload POST failures for every upload mode.\n *\n * @param value - Caller-provided override, if any.\n *\n * @returns The boolean value passed to the upload retry helper.\n */\nexport function resolveRetryResponseBodyFailures(value: boolean | undefined): boolean {\n return value ?? false\n}\n\n/**\n * Fetches a small-file upload URL, bypassing the pool.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param bucketId - Bucket to upload into.\n * @param signal - Optional abort signal for the fresh URL request.\n *\n * @returns A fresh upload URL entry.\n */\nexport async function fetchFreshUploadUrl(\n raw: RawClient,\n accountInfo: AccountInfo,\n bucketId: BucketId,\n signal?: AbortSignal,\n): Promise<UploadUrlEntry> {\n const resp = await raw.getUploadUrl(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n bucketId,\n },\n {\n ...(signal !== undefined ? { signal } : {}),\n retry: freshUrlRetryOverride,\n },\n )\n return { uploadUrl: resp.uploadUrl, authorizationToken: resp.authorizationToken }\n}\n\n/**\n * Fetches a large-file part upload URL, bypassing the pool.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param fileId - Large file to upload a part into.\n * @param signal - Optional abort signal for the fresh URL request.\n *\n * @returns A fresh part upload URL entry.\n */\nexport async function fetchFreshPartUploadUrl(\n raw: RawClient,\n accountInfo: AccountInfo,\n fileId: LargeFileId,\n signal?: AbortSignal,\n): Promise<UploadUrlEntry> {\n const resp = await raw.getUploadPartUrl(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n fileId,\n },\n {\n ...(signal !== undefined ? { signal } : {}),\n retry: freshUrlRetryOverride,\n },\n )\n return { uploadUrl: resp.uploadUrl, authorizationToken: resp.authorizationToken }\n}\n\ninterface UploadPartWithFreshUrlOptions extends UploadLayerRetryOptions {\n /** File name associated with the large-file upload. */\n readonly fileName: string\n /** One-based part number. */\n readonly partNumber: number\n /** Part bytes to upload. */\n readonly data: BodyInit\n /** Number of bytes in the part. */\n readonly contentLength: number\n /** SHA-1 hex digest of the part bytes. */\n readonly contentSha1: string\n /** Server-side encryption settings for this part. */\n readonly serverSideEncryption?: EncryptionSetting | undefined\n}\n\n/**\n * Uploads one multipart part with fresh-URL retry.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param fileId - Large file to upload a part into.\n * @param options - Part upload parameters and retry settings.\n *\n * @returns The uploaded part response.\n */\nexport function uploadPartWithFreshUrl(\n raw: RawClient,\n accountInfo: AccountInfo,\n fileId: LargeFileId,\n options: UploadPartWithFreshUrlOptions,\n): Promise<UploadPartResponse> {\n return withFreshUploadUrlRetry({\n fileName: options.fileName,\n partNumber: options.partNumber,\n retry: options.retry,\n signal: options.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: options.retryResponseBodyFailures,\n checkout: () => accountInfo.checkoutPartUploadUrl(fileId),\n fetchFresh: () => fetchFreshPartUploadUrl(raw, accountInfo, fileId, options.signal),\n returnEntry: (entry) => accountInfo.returnPartUploadUrl(fileId, entry),\n evictEntry: (entry) => accountInfo.evictPartUploadUrl(fileId, entry),\n upload: (entry) =>\n raw.uploadPart(\n entry.uploadUrl,\n {\n authorization: entry.authorizationToken,\n partNumber: options.partNumber,\n contentLength: options.contentLength,\n contentSha1: options.contentSha1,\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n },\n options.data,\n {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n ),\n })\n}\n\n/**\n * Runs an upload operation with B2's documented retry flow: evict the failed\n * upload URL, back off, fetch a fresh upload URL, and retry there.\n *\n * For single-request file uploads, sending the POST again after a lost success\n * response can create a duplicate file version. Multipart retries re-send the\n * same part number instead. Response-body retry defaults are caller-specific:\n * single-request uploads keep them off by default, while multipart callers can\n * opt in with `retryResponseBodyFailures: true` when replaying the same part\n * number is acceptable.\n *\n * @param options - URL checkout, upload, eviction, and retry callbacks.\n *\n * @returns The successful upload result.\n */\nexport async function withFreshUploadUrlRetry<T>(options: FreshUrlRetryOptions<T>): Promise<T> {\n const retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...options.retry }\n\n for (let attempt = 0; attempt <= retryOptions.maxRetries; attempt++) {\n let uploadEntry: UploadUrlEntry | undefined\n let uploadStarted = false\n\n try {\n options.signal?.throwIfAborted()\n uploadEntry =\n attempt === 0\n ? (options.checkout() ?? (await options.fetchFresh()))\n : await options.fetchFresh()\n\n uploadStarted = true\n const result = await options.upload(uploadEntry)\n options.returnEntry(uploadEntry)\n return result\n } catch (err) {\n const retryError = normalizeUploadRetryError(err, options)\n if (uploadEntry !== undefined) {\n if (isUploadRateLimitError(retryError)) {\n options.returnEntry(uploadEntry)\n } else {\n options.evictEntry(uploadEntry)\n }\n }\n if (options.signal?.aborted) {\n throw err\n }\n if (isUploadRateLimitError(retryError) && uploadEntry !== undefined) {\n throw retryError\n }\n if (\n !isUploadRetryable(retryError, { ...options, uploadStarted }) ||\n attempt === retryOptions.maxRetries\n ) {\n throw retryError\n }\n\n const retryAttempt = attempt + 1\n const retryAfter = retryError instanceof B2Error ? retryError.retryAfter : undefined\n const delayMs = computeBackoff(attempt, retryOptions, retryAfter)\n notifyUploadRetry(options, {\n fileName: options.fileName,\n partNumber: options.partNumber,\n attempt: retryAttempt,\n maxRetries: retryOptions.maxRetries,\n delayMs,\n error: retryError,\n })\n await sleep(delayMs, options.signal)\n }\n }\n\n // Unreachable at runtime: the loop returns on success or throws from the\n // final failed attempt. This satisfies TypeScript's return-path analysis.\n /* v8 ignore next -- defensive return-path guard. */\n throw new NetworkError('Upload retry budget exhausted')\n}\n\nfunction notifyUploadRetry(options: UploadLayerRetryOptions, event: UploadRetryEvent): void {\n try {\n options.onUploadRetry?.(event)\n } catch {\n // Telemetry observers must not replace the underlying upload failure or\n // prevent a retry that can recover with a fresh upload URL.\n }\n}\n\nfunction isUploadRetryable(\n err: unknown,\n options: UploadLayerRetryOptions & {\n readonly partNumber: number | null\n readonly uploadStarted: boolean\n },\n): err is B2Error | NetworkError {\n if (err instanceof NetworkError) {\n if (err.cause instanceof B2SsrfError) return false\n if (!options.uploadStarted) return true\n return options.retryResponseBodyFailures === true\n }\n if (err instanceof BadAuthTokenError) return true\n if (isUploadUrlInvalidationError(err)) return true\n return err instanceof B2Error && err.retryable\n}\n\nfunction isUploadRateLimitError(err: unknown): err is B2Error {\n return err instanceof B2Error && err.status === 429\n}\n\nfunction normalizeUploadRetryError(err: unknown, options: UploadLayerRetryOptions): unknown {\n if (err instanceof B2Error || err instanceof NetworkError) return err\n if (err instanceof UploadResponseBodyError) {\n if (!options.retryResponseBodyFailures) return err\n return new NetworkError(err.message, err)\n }\n if (err instanceof DOMException && err.name === 'AbortError') return err\n if (err instanceof TypeError || err instanceof SyntaxError || err instanceof DOMException) {\n const message = err instanceof Error ? err.message : 'Upload response read failed'\n return new NetworkError(message, err)\n }\n return err\n}\n\nfunction isUploadUrlInvalidationError(err: unknown): boolean {\n if (err instanceof BadUploadUrlError) return true\n return err instanceof BadRequestError && /upload url/i.test(err.message)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"retry.js","names":[],"sources":["../../src/upload/retry.ts"],"sourcesContent":["import type { AccountInfo, UploadUrlEntry } from '../auth/account-info.ts'\nimport {\n B2Error,\n B2SsrfError,\n BadAuthTokenError,\n BadRequestError,\n BadUploadUrlError,\n NetworkError,\n UploadResponseBodyError,\n} from '../errors/index.ts'\nimport { computeBackoff, DEFAULT_RETRY_OPTIONS, type RetryOptions, sleep } from '../http/retry.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { BucketId, LargeFileId } from '../types/ids.ts'\nimport type { UploadPartResponse } from '../types/upload.ts'\n\n/** Event emitted before an upload is retried against a fresh upload URL. */\nexport interface UploadRetryEvent {\n /** File name being uploaded. */\n readonly fileName: string\n /** Multipart part number, or `null` for a single-request file upload. */\n readonly partNumber: number | null\n /** One-based retry attempt that is about to run. */\n readonly attempt: number\n /** Maximum number of retry attempts allowed for this upload operation. */\n readonly maxRetries: number\n /** Backoff delay in milliseconds before the fresh URL is fetched. */\n readonly delayMs: number\n /** Classified error that triggered the fresh-URL retry. */\n readonly error: B2Error | NetworkError\n}\n\n/** Callback invoked before an upload retry fetches a fresh upload URL. */\nexport type UploadRetryListener = (event: UploadRetryEvent) => void\n\n/** Shared public options for upload-layer fresh-URL retries. */\nexport interface UploadRetryOptions {\n /** Retry settings shared with the main transport retry configuration. */\n readonly retry?: Partial<RetryOptions> | undefined\n /** Callback invoked before a fresh-URL upload retry. */\n readonly onUploadRetry?: UploadRetryListener | undefined\n /**\n * Whether ambiguous failures after an upload POST should be retried,\n * including response-body read failures and transport errors where B2 may\n * have stored the payload before the success response was lost.\n * Defaults to false. Set true only when duplicate versions or parts are\n * acceptable.\n */\n readonly retryResponseBodyFailures?: boolean | undefined\n}\n\ninterface UploadLayerRetryOptions extends Omit<UploadRetryOptions, 'retryResponseBodyFailures'> {\n readonly retryResponseBodyFailures: boolean\n /** Abort signal for cancelling upload attempts and retry backoff sleeps. */\n readonly signal?: AbortSignal | undefined\n}\n\ninterface FreshUrlRetryOptions<T> extends UploadLayerRetryOptions {\n readonly fileName: string\n readonly partNumber: number | null\n readonly checkout: () => UploadUrlEntry | null\n readonly fetchFresh: () => Promise<UploadUrlEntry>\n readonly returnEntry: (entry: UploadUrlEntry) => void\n readonly evictEntry: (entry: UploadUrlEntry) => void\n readonly upload: (entry: UploadUrlEntry) => Promise<T>\n}\n\nconst freshUrlRetryOverride: Partial<RetryOptions> = { maxRetries: 0 }\n\n/**\n * Resolves the public default for `retryResponseBodyFailures`. Callers must\n * opt into replaying ambiguous upload POST failures for every upload mode.\n *\n * @param value - Caller-provided override, if any.\n *\n * @returns The boolean value passed to the upload retry helper.\n */\nexport function resolveRetryResponseBodyFailures(value: boolean | undefined): boolean {\n return value ?? false\n}\n\n/**\n * Fetches a small-file upload URL, bypassing the pool.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param bucketId - Bucket to upload into.\n * @param signal - Optional abort signal for the fresh URL request.\n *\n * @returns A fresh upload URL entry.\n */\nexport async function fetchFreshUploadUrl(\n raw: RawClient,\n accountInfo: AccountInfo,\n bucketId: BucketId,\n signal?: AbortSignal,\n): Promise<UploadUrlEntry> {\n const resp = await raw.getUploadUrl(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n bucketId,\n },\n {\n ...(signal !== undefined ? { signal } : {}),\n retry: freshUrlRetryOverride,\n },\n )\n return { uploadUrl: resp.uploadUrl, authorizationToken: resp.authorizationToken }\n}\n\n/**\n * Fetches a large-file part upload URL, bypassing the pool.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param fileId - Large file to upload a part into.\n * @param signal - Optional abort signal for the fresh URL request.\n *\n * @returns A fresh part upload URL entry.\n */\nexport async function fetchFreshPartUploadUrl(\n raw: RawClient,\n accountInfo: AccountInfo,\n fileId: LargeFileId,\n signal?: AbortSignal,\n): Promise<UploadUrlEntry> {\n const resp = await raw.getUploadPartUrl(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n fileId,\n },\n {\n ...(signal !== undefined ? { signal } : {}),\n retry: freshUrlRetryOverride,\n },\n )\n return { uploadUrl: resp.uploadUrl, authorizationToken: resp.authorizationToken }\n}\n\ninterface UploadPartWithFreshUrlOptions extends UploadLayerRetryOptions {\n /** File name associated with the large-file upload. */\n readonly fileName: string\n /** One-based part number. */\n readonly partNumber: number\n /** Part bytes to upload. */\n readonly data: BodyInit\n /** Number of bytes in the part. */\n readonly contentLength: number\n /** SHA-1 hex digest of the part bytes. */\n readonly contentSha1: string\n /** Server-side encryption settings for this part. */\n readonly serverSideEncryption?: EncryptionSetting | undefined\n}\n\n/**\n * Uploads one multipart part with fresh-URL retry.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param fileId - Large file to upload a part into.\n * @param options - Part upload parameters and retry settings.\n *\n * @returns The uploaded part response.\n */\nexport function uploadPartWithFreshUrl(\n raw: RawClient,\n accountInfo: AccountInfo,\n fileId: LargeFileId,\n options: UploadPartWithFreshUrlOptions,\n): Promise<UploadPartResponse> {\n return withFreshUploadUrlRetry({\n fileName: options.fileName,\n partNumber: options.partNumber,\n retry: options.retry,\n signal: options.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: options.retryResponseBodyFailures,\n checkout: () => accountInfo.checkoutPartUploadUrl(fileId),\n fetchFresh: () => fetchFreshPartUploadUrl(raw, accountInfo, fileId, options.signal),\n returnEntry: (entry) => accountInfo.returnPartUploadUrl(fileId, entry),\n evictEntry: (entry) => accountInfo.evictPartUploadUrl(fileId, entry),\n upload: (entry) =>\n raw.uploadPart(\n entry.uploadUrl,\n {\n authorization: entry.authorizationToken,\n partNumber: options.partNumber,\n contentLength: options.contentLength,\n contentSha1: options.contentSha1,\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n },\n options.data,\n {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n ),\n })\n}\n\n/**\n * Runs an upload operation with B2's documented retry flow: evict the failed\n * upload URL, back off, fetch a fresh upload URL, and retry there.\n *\n * For single-request file uploads, sending the POST again after a lost success\n * response can create a duplicate file version. Multipart retries re-send the\n * same part number instead. Response-body retry defaults are caller-specific:\n * single-request uploads keep them off by default, while multipart callers can\n * opt in with `retryResponseBodyFailures: true` when replaying the same part\n * number is acceptable.\n *\n * @param options - URL checkout, upload, eviction, and retry callbacks.\n *\n * @returns The successful upload result.\n */\nexport async function withFreshUploadUrlRetry<T>(options: FreshUrlRetryOptions<T>): Promise<T> {\n const retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...options.retry }\n\n for (let attempt = 0; attempt <= retryOptions.maxRetries; attempt++) {\n let uploadEntry: UploadUrlEntry | undefined\n let uploadStarted = false\n\n try {\n options.signal?.throwIfAborted()\n uploadEntry =\n attempt === 0\n ? (options.checkout() ?? (await options.fetchFresh()))\n : await options.fetchFresh()\n\n uploadStarted = true\n const result = await options.upload(uploadEntry)\n options.returnEntry(uploadEntry)\n return result\n } catch (err) {\n const retryError = normalizeUploadRetryError(err, options)\n if (uploadEntry !== undefined) {\n if (isUploadRateLimitError(retryError)) {\n options.returnEntry(uploadEntry)\n } else {\n options.evictEntry(uploadEntry)\n }\n }\n if (options.signal?.aborted) {\n throw err\n }\n if (isUploadRateLimitError(retryError) && uploadEntry !== undefined) {\n throw retryError\n }\n if (\n !isUploadRetryable(retryError, { ...options, uploadStarted }) ||\n attempt === retryOptions.maxRetries\n ) {\n throw retryError\n }\n\n const retryAttempt = attempt + 1\n const retryAfter = retryError instanceof B2Error ? retryError.retryAfter : undefined\n const delayMs = computeBackoff(attempt, retryOptions, retryAfter)\n notifyUploadRetry(options, {\n fileName: options.fileName,\n partNumber: options.partNumber,\n attempt: retryAttempt,\n maxRetries: retryOptions.maxRetries,\n delayMs,\n error: retryError,\n })\n await sleep(delayMs, options.signal)\n }\n }\n\n // Unreachable at runtime: the loop returns on success or throws from the\n // final failed attempt. This satisfies TypeScript's return-path analysis.\n /* v8 ignore next -- defensive return-path guard. */\n throw new NetworkError('Upload retry budget exhausted')\n}\n\nfunction notifyUploadRetry(options: UploadLayerRetryOptions, event: UploadRetryEvent): void {\n try {\n options.onUploadRetry?.(event)\n } catch {\n // Telemetry observers must not replace the underlying upload failure or\n // prevent a retry that can recover with a fresh upload URL.\n }\n}\n\nfunction isUploadRetryable(\n err: unknown,\n options: UploadLayerRetryOptions & {\n readonly partNumber: number | null\n readonly uploadStarted: boolean\n },\n): err is B2Error | NetworkError {\n if (err instanceof NetworkError) {\n if (err.cause instanceof B2SsrfError) return false\n if (!options.uploadStarted) return true\n return options.retryResponseBodyFailures === true\n }\n if (err instanceof BadAuthTokenError) return true\n if (isUploadUrlInvalidationError(err)) return true\n return err instanceof B2Error && err.retryable\n}\n\nfunction isUploadRateLimitError(err: unknown): err is B2Error {\n return err instanceof B2Error && err.status === 429\n}\n\nfunction normalizeUploadRetryError(err: unknown, options: UploadLayerRetryOptions): unknown {\n if (err instanceof B2Error || err instanceof NetworkError) return err\n if (err instanceof UploadResponseBodyError) {\n if (!options.retryResponseBodyFailures) return err\n return new NetworkError(err.message, err)\n }\n if (err instanceof DOMException && err.name === 'AbortError') return err\n if (err instanceof TypeError || err instanceof SyntaxError || err instanceof DOMException) {\n const message = err instanceof Error ? err.message : 'Upload response read failed'\n return new NetworkError(message, err)\n }\n return err\n}\n\nfunction isUploadUrlInvalidationError(err: unknown): boolean {\n if (err instanceof BadUploadUrlError) return true\n return err instanceof BadRequestError && /upload url/i.test(err.message)\n}\n"],"mappings":"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"}
|
package/dist/upload/single.cjs
CHANGED
|
@@ -48,7 +48,8 @@ async function uploadSmallFile(raw, accountInfo, options) {
|
|
|
48
48
|
...options.serverSideEncryption !== void 0 ? { serverSideEncryption: options.serverSideEncryption } : {},
|
|
49
49
|
...options.fileRetention !== void 0 ? { fileRetention: options.fileRetention } : {},
|
|
50
50
|
...options.legalHold !== void 0 ? { legalHold: options.legalHold } : {},
|
|
51
|
-
...options.lastModifiedMillis !== void 0 ? { lastModifiedMillis: options.lastModifiedMillis } : {}
|
|
51
|
+
...options.lastModifiedMillis !== void 0 ? { lastModifiedMillis: options.lastModifiedMillis } : {},
|
|
52
|
+
...options.customUploadTimestamp !== void 0 ? { customUploadTimestamp: options.customUploadTimestamp } : {}
|
|
52
53
|
}, data, {
|
|
53
54
|
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
54
55
|
...options.retry !== void 0 ? { retry: options.retry } : {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"single.cjs","names":[],"sources":["../../src/upload/single.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { IncrementalSha1 } from '../streams/hash.ts'\nimport { type ProgressListener, ProgressTracker } from '../streams/progress.ts'\nimport { type ContentSource, collectStreamExactly } from '../streams/source.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport type { BucketId } from '../types/ids.ts'\nimport type { FileRetentionValue, LegalHoldValue } from '../types/lock.ts'\nimport { DEFAULT_CONTENT_TYPE } from '../util/defaults.ts'\nimport {\n fetchFreshUploadUrl,\n resolveRetryResponseBodyFailures,\n type UploadRetryOptions,\n withFreshUploadUrlRetry,\n} from './retry.ts'\n\n/** Options for uploading a small file in a single HTTP request. */\nexport interface UploadFileOptions extends UploadRetryOptions {\n /** Target bucket for the upload. */\n readonly bucketId: BucketId\n /** Full B2 file name including any path prefix. */\n readonly fileName: string\n /** Content to upload. */\n readonly source: ContentSource\n /** MIME type. Defaults to `b2/x-auto` for server-side detection. */\n readonly contentType?: string\n /** Custom file info key/value pairs stored with the file. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption settings. */\n readonly serverSideEncryption?: EncryptionSetting\n /** File retention policy applied at upload time. */\n readonly fileRetention?: FileRetentionValue\n /** Legal hold status applied at upload time. */\n readonly legalHold?: LegalHoldValue\n /** Override the last-modified timestamp (epoch millis). */\n readonly lastModifiedMillis?: number\n /** Callback invoked with upload progress updates. */\n readonly onProgress?: ProgressListener\n /** Signal to abort the upload. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Uploads a file in a single HTTP request (suitable for files up to ~100 MB).\n *\n * The entire file content is read into memory, SHA-1 hashed, and sent in one\n * `b2_upload_file` call. For files larger than the recommended part size, use\n * {@link uploadLargeFile} which splits the file into parts uploaded in parallel.\n *\n * Upload URLs are pooled via {@link AccountInfo} and recycled on success or\n * evicted on failure.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (tokens, URLs, upload URL pool).\n * @param options - Upload parameters.\n *\n * @returns The resulting {@link FileVersion} metadata.\n */\nexport async function uploadSmallFile(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: UploadFileOptions,\n): Promise<FileVersion> {\n const data = await readSmallFileSource(options.source, options.signal)\n if (data.byteLength !== options.source.size) {\n throw new Error(\n `uploadSmallFile: source byte count does not match advertised size ` +\n `(expected ${options.source.size} bytes, got ${data.byteLength} bytes).`,\n )\n }\n const sha1 = new IncrementalSha1()\n await sha1.update(data)\n const sha1Hex = await sha1.digest()\n\n // Single-request uploads send the whole payload in one shot, so the\n // tracker reports a single \"part\" worth of progress: one byte burst at\n // completion and a parts-completed transition from 0 → 1. The tracker\n // is created (rather than calling the listener directly) so consumers\n // see the same {bytesTransferred, totalBytes, partsCompleted,\n // totalParts, elapsedMs} shape they get from `uploadLargeFile`.\n const tracker = new ProgressTracker(options.onProgress, data.byteLength, 1)\n\n const result = await withFreshUploadUrlRetry({\n fileName: options.fileName,\n partNumber: null,\n retry: options.retry,\n signal: options.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: resolveRetryResponseBodyFailures(options.retryResponseBodyFailures),\n checkout: () => accountInfo.checkoutUploadUrl(options.bucketId),\n fetchFresh: () => fetchFreshUploadUrl(raw, accountInfo, options.bucketId, options.signal),\n returnEntry: (entry) => accountInfo.returnUploadUrl(options.bucketId, entry),\n evictEntry: (entry) => accountInfo.evictUploadUrl(options.bucketId, entry),\n upload: (entry) =>\n raw.uploadFile(\n entry.uploadUrl,\n {\n authorization: entry.authorizationToken,\n fileName: options.fileName,\n contentType: options.contentType ?? DEFAULT_CONTENT_TYPE,\n contentLength: data.byteLength,\n contentSha1: sha1Hex,\n ...(options.fileInfo !== undefined ? { fileInfo: options.fileInfo } : {}),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n ...(options.fileRetention !== undefined ? { fileRetention: options.fileRetention } : {}),\n ...(options.legalHold !== undefined ? { legalHold: options.legalHold } : {}),\n ...(options.lastModifiedMillis !== undefined\n ? { lastModifiedMillis: options.lastModifiedMillis }\n : {}),\n },\n data,\n {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n ),\n })\n\n tracker.addBytes(data.byteLength)\n tracker.completePart()\n return result\n}\n\nasync function readSmallFileSource(\n source: ContentSource,\n signal: AbortSignal | undefined,\n): Promise<Uint8Array<ArrayBuffer>> {\n signal?.throwIfAborted()\n if (!source.canSlice) {\n return collectStreamExactly(source.stream(), source.size, signal)\n }\n const data = new Uint8Array(\n await (signal === undefined ? source.toArrayBuffer() : source.toArrayBuffer({ signal })),\n )\n signal?.throwIfAborted()\n return data\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"single.cjs","names":[],"sources":["../../src/upload/single.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { IncrementalSha1 } from '../streams/hash.ts'\nimport { type ProgressListener, ProgressTracker } from '../streams/progress.ts'\nimport { type ContentSource, collectStreamExactly } from '../streams/source.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport type { BucketId } from '../types/ids.ts'\nimport type { FileRetentionValue, LegalHoldValue } from '../types/lock.ts'\nimport { DEFAULT_CONTENT_TYPE } from '../util/defaults.ts'\nimport {\n fetchFreshUploadUrl,\n resolveRetryResponseBodyFailures,\n type UploadRetryOptions,\n withFreshUploadUrlRetry,\n} from './retry.ts'\n\n/** Options for uploading a small file in a single HTTP request. */\nexport interface UploadFileOptions extends UploadRetryOptions {\n /** Target bucket for the upload. */\n readonly bucketId: BucketId\n /** Full B2 file name including any path prefix. */\n readonly fileName: string\n /** Content to upload. */\n readonly source: ContentSource\n /** MIME type. Defaults to `b2/x-auto` for server-side detection. */\n readonly contentType?: string\n /** Custom file info key/value pairs stored with the file. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption settings. */\n readonly serverSideEncryption?: EncryptionSetting\n /** File retention policy applied at upload time. */\n readonly fileRetention?: FileRetentionValue\n /** Legal hold status applied at upload time. */\n readonly legalHold?: LegalHoldValue\n /** Override the last-modified timestamp (epoch millis). */\n readonly lastModifiedMillis?: number\n /**\n * B2 upload timestamp override in milliseconds since epoch.\n * Sets the file version `uploadTimestamp`; distinct from `lastModifiedMillis` metadata.\n * Requires B2 account enablement for Custom Upload Timestamp.\n */\n readonly customUploadTimestamp?: number\n /** Callback invoked with upload progress updates. */\n readonly onProgress?: ProgressListener\n /** Signal to abort the upload. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Uploads a file in a single HTTP request (suitable for files up to ~100 MB).\n *\n * The entire file content is read into memory, SHA-1 hashed, and sent in one\n * `b2_upload_file` call. For files larger than the recommended part size, use\n * {@link uploadLargeFile} which splits the file into parts uploaded in parallel.\n *\n * Upload URLs are pooled via {@link AccountInfo} and recycled on success or\n * evicted on failure.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (tokens, URLs, upload URL pool).\n * @param options - Upload parameters.\n *\n * @returns The resulting {@link FileVersion} metadata.\n */\nexport async function uploadSmallFile(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: UploadFileOptions,\n): Promise<FileVersion> {\n const data = await readSmallFileSource(options.source, options.signal)\n if (data.byteLength !== options.source.size) {\n throw new Error(\n `uploadSmallFile: source byte count does not match advertised size ` +\n `(expected ${options.source.size} bytes, got ${data.byteLength} bytes).`,\n )\n }\n const sha1 = new IncrementalSha1()\n await sha1.update(data)\n const sha1Hex = await sha1.digest()\n\n // Single-request uploads send the whole payload in one shot, so the\n // tracker reports a single \"part\" worth of progress: one byte burst at\n // completion and a parts-completed transition from 0 → 1. The tracker\n // is created (rather than calling the listener directly) so consumers\n // see the same {bytesTransferred, totalBytes, partsCompleted,\n // totalParts, elapsedMs} shape they get from `uploadLargeFile`.\n const tracker = new ProgressTracker(options.onProgress, data.byteLength, 1)\n\n const result = await withFreshUploadUrlRetry({\n fileName: options.fileName,\n partNumber: null,\n retry: options.retry,\n signal: options.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: resolveRetryResponseBodyFailures(options.retryResponseBodyFailures),\n checkout: () => accountInfo.checkoutUploadUrl(options.bucketId),\n fetchFresh: () => fetchFreshUploadUrl(raw, accountInfo, options.bucketId, options.signal),\n returnEntry: (entry) => accountInfo.returnUploadUrl(options.bucketId, entry),\n evictEntry: (entry) => accountInfo.evictUploadUrl(options.bucketId, entry),\n upload: (entry) =>\n raw.uploadFile(\n entry.uploadUrl,\n {\n authorization: entry.authorizationToken,\n fileName: options.fileName,\n contentType: options.contentType ?? DEFAULT_CONTENT_TYPE,\n contentLength: data.byteLength,\n contentSha1: sha1Hex,\n ...(options.fileInfo !== undefined ? { fileInfo: options.fileInfo } : {}),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n ...(options.fileRetention !== undefined ? { fileRetention: options.fileRetention } : {}),\n ...(options.legalHold !== undefined ? { legalHold: options.legalHold } : {}),\n ...(options.lastModifiedMillis !== undefined\n ? { lastModifiedMillis: options.lastModifiedMillis }\n : {}),\n ...(options.customUploadTimestamp !== undefined\n ? { customUploadTimestamp: options.customUploadTimestamp }\n : {}),\n },\n data,\n {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n ),\n })\n\n tracker.addBytes(data.byteLength)\n tracker.completePart()\n return result\n}\n\nasync function readSmallFileSource(\n source: ContentSource,\n signal: AbortSignal | undefined,\n): Promise<Uint8Array<ArrayBuffer>> {\n signal?.throwIfAborted()\n if (!source.canSlice) {\n return collectStreamExactly(source.stream(), source.size, signal)\n }\n const data = new Uint8Array(\n await (signal === undefined ? source.toArrayBuffer() : source.toArrayBuffer({ signal })),\n )\n signal?.throwIfAborted()\n return data\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAiEA,eAAsB,gBACpB,KACA,aACA,SACsB;CACtB,MAAM,OAAO,MAAM,oBAAoB,QAAQ,QAAQ,QAAQ,MAAM;CACrE,IAAI,KAAK,eAAe,QAAQ,OAAO,MACrC,MAAM,IAAI,MACR,+EACe,QAAQ,OAAO,KAAK,cAAc,KAAK,WAAW,SACnE;CAEF,MAAM,OAAO,IAAI,aAAA,gBAAgB;CACjC,MAAM,KAAK,OAAO,IAAI;CACtB,MAAM,UAAU,MAAM,KAAK,OAAO;CAQlC,MAAM,UAAU,IAAI,iBAAA,gBAAgB,QAAQ,YAAY,KAAK,YAAY,CAAC;CAE1E,MAAM,SAAS,MAAM,cAAA,wBAAwB;EAC3C,UAAU,QAAQ;EAClB,YAAY;EACZ,OAAO,QAAQ;EACf,QAAQ,QAAQ;EAChB,eAAe,QAAQ;EACvB,2BAA2B,cAAA,iCAAiC,QAAQ,yBAAyB;EAC7F,gBAAgB,YAAY,kBAAkB,QAAQ,QAAQ;EAC9D,kBAAkB,cAAA,oBAAoB,KAAK,aAAa,QAAQ,UAAU,QAAQ,MAAM;EACxF,cAAc,UAAU,YAAY,gBAAgB,QAAQ,UAAU,KAAK;EAC3E,aAAa,UAAU,YAAY,eAAe,QAAQ,UAAU,KAAK;EACzE,SAAS,UACP,IAAI,WACF,MAAM,WACN;GACE,eAAe,MAAM;GACrB,UAAU,QAAQ;GAClB,aAAa,QAAQ,eAAA;GACrB,eAAe,KAAK;GACpB,aAAa;GACb,GAAI,QAAQ,aAAa,KAAA,IAAY,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;GACvE,GAAI,QAAQ,yBAAyB,KAAA,IACjC,EAAE,sBAAsB,QAAQ,qBAAqB,IACrD,CAAC;GACL,GAAI,QAAQ,kBAAkB,KAAA,IAAY,EAAE,eAAe,QAAQ,cAAc,IAAI,CAAC;GACtF,GAAI,QAAQ,cAAc,KAAA,IAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;GAC1E,GAAI,QAAQ,uBAAuB,KAAA,IAC/B,EAAE,oBAAoB,QAAQ,mBAAmB,IACjD,CAAC;GACL,GAAI,QAAQ,0BAA0B,KAAA,IAClC,EAAE,uBAAuB,QAAQ,sBAAsB,IACvD,CAAC;EACP,GACA,MACA;GACE,GAAI,QAAQ,WAAW,KAAA,IAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;GACjE,GAAI,QAAQ,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;EAChE,CACF;CACJ,CAAC;CAED,QAAQ,SAAS,KAAK,UAAU;CAChC,QAAQ,aAAa;CACrB,OAAO;AACT;AAEA,eAAe,oBACb,QACA,QACkC;CAClC,QAAQ,eAAe;CACvB,IAAI,CAAC,OAAO,UACV,OAAO,eAAA,qBAAqB,OAAO,OAAO,GAAG,OAAO,MAAM,MAAM;CAElE,MAAM,OAAO,IAAI,WACf,OAAO,WAAW,KAAA,IAAY,OAAO,cAAc,IAAI,OAAO,cAAc,EAAE,OAAO,CAAC,EACxF;CACA,QAAQ,eAAe;CACvB,OAAO;AACT"}
|
package/dist/upload/single.d.cts
CHANGED
|
@@ -27,6 +27,12 @@ export interface UploadFileOptions extends UploadRetryOptions {
|
|
|
27
27
|
readonly legalHold?: LegalHoldValue;
|
|
28
28
|
/** Override the last-modified timestamp (epoch millis). */
|
|
29
29
|
readonly lastModifiedMillis?: number;
|
|
30
|
+
/**
|
|
31
|
+
* B2 upload timestamp override in milliseconds since epoch.
|
|
32
|
+
* Sets the file version `uploadTimestamp`; distinct from `lastModifiedMillis` metadata.
|
|
33
|
+
* Requires B2 account enablement for Custom Upload Timestamp.
|
|
34
|
+
*/
|
|
35
|
+
readonly customUploadTimestamp?: number;
|
|
30
36
|
/** Callback invoked with upload progress updates. */
|
|
31
37
|
readonly onProgress?: ProgressListener;
|
|
32
38
|
/** Signal to abort the upload. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"single.d.ts","sourceRoot":"","sources":["../../src/upload/single.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,KAAK,gBAAgB,EAAmB,MAAM,wBAAwB,CAAA;AAC/E,OAAO,EAAE,KAAK,aAAa,EAAwB,MAAM,sBAAsB,CAAA;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE1E,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAA;AAEnB,mEAAmE;AACnE,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,yBAAyB;IACzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,uCAAuC;IACvC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,iBAAiB,CAAA;IACjD,oDAAoD;IACpD,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAC3C,gDAAgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAA;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IACpC,qDAAqD;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IACtC,kCAAkC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"single.d.ts","sourceRoot":"","sources":["../../src/upload/single.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,KAAK,gBAAgB,EAAmB,MAAM,wBAAwB,CAAA;AAC/E,OAAO,EAAE,KAAK,aAAa,EAAwB,MAAM,sBAAsB,CAAA;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE1E,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAA;AAEnB,mEAAmE;AACnE,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,yBAAyB;IACzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,uCAAuC;IACvC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,iBAAiB,CAAA;IACjD,oDAAoD;IACpD,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAC3C,gDAAgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAA;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IACpC;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAA;IACvC,qDAAqD;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IACtC,kCAAkC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,CAgEtB"}
|
package/dist/upload/single.d.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface UploadFileOptions extends UploadRetryOptions {
|
|
|
27
27
|
readonly legalHold?: LegalHoldValue;
|
|
28
28
|
/** Override the last-modified timestamp (epoch millis). */
|
|
29
29
|
readonly lastModifiedMillis?: number;
|
|
30
|
+
/**
|
|
31
|
+
* B2 upload timestamp override in milliseconds since epoch.
|
|
32
|
+
* Sets the file version `uploadTimestamp`; distinct from `lastModifiedMillis` metadata.
|
|
33
|
+
* Requires B2 account enablement for Custom Upload Timestamp.
|
|
34
|
+
*/
|
|
35
|
+
readonly customUploadTimestamp?: number;
|
|
30
36
|
/** Callback invoked with upload progress updates. */
|
|
31
37
|
readonly onProgress?: ProgressListener;
|
|
32
38
|
/** Signal to abort the upload. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"single.d.ts","sourceRoot":"","sources":["../../src/upload/single.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,KAAK,gBAAgB,EAAmB,MAAM,wBAAwB,CAAA;AAC/E,OAAO,EAAE,KAAK,aAAa,EAAwB,MAAM,sBAAsB,CAAA;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE1E,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAA;AAEnB,mEAAmE;AACnE,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,yBAAyB;IACzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,uCAAuC;IACvC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,iBAAiB,CAAA;IACjD,oDAAoD;IACpD,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAC3C,gDAAgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAA;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IACpC,qDAAqD;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IACtC,kCAAkC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"single.d.ts","sourceRoot":"","sources":["../../src/upload/single.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,KAAK,gBAAgB,EAAmB,MAAM,wBAAwB,CAAA;AAC/E,OAAO,EAAE,KAAK,aAAa,EAAwB,MAAM,sBAAsB,CAAA;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE1E,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAA;AAEnB,mEAAmE;AACnE,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,yBAAyB;IACzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,uCAAuC;IACvC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,iBAAiB,CAAA;IACjD,oDAAoD;IACpD,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAC3C,gDAAgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAA;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IACpC;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAA;IACvC,qDAAqD;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IACtC,kCAAkC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,CAgEtB"}
|
package/dist/upload/single.js
CHANGED
|
@@ -48,7 +48,8 @@ async function uploadSmallFile(raw, accountInfo, options) {
|
|
|
48
48
|
...options.serverSideEncryption !== void 0 ? { serverSideEncryption: options.serverSideEncryption } : {},
|
|
49
49
|
...options.fileRetention !== void 0 ? { fileRetention: options.fileRetention } : {},
|
|
50
50
|
...options.legalHold !== void 0 ? { legalHold: options.legalHold } : {},
|
|
51
|
-
...options.lastModifiedMillis !== void 0 ? { lastModifiedMillis: options.lastModifiedMillis } : {}
|
|
51
|
+
...options.lastModifiedMillis !== void 0 ? { lastModifiedMillis: options.lastModifiedMillis } : {},
|
|
52
|
+
...options.customUploadTimestamp !== void 0 ? { customUploadTimestamp: options.customUploadTimestamp } : {}
|
|
52
53
|
}, data, {
|
|
53
54
|
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
54
55
|
...options.retry !== void 0 ? { retry: options.retry } : {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"single.js","names":[],"sources":["../../src/upload/single.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { IncrementalSha1 } from '../streams/hash.ts'\nimport { type ProgressListener, ProgressTracker } from '../streams/progress.ts'\nimport { type ContentSource, collectStreamExactly } from '../streams/source.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport type { BucketId } from '../types/ids.ts'\nimport type { FileRetentionValue, LegalHoldValue } from '../types/lock.ts'\nimport { DEFAULT_CONTENT_TYPE } from '../util/defaults.ts'\nimport {\n fetchFreshUploadUrl,\n resolveRetryResponseBodyFailures,\n type UploadRetryOptions,\n withFreshUploadUrlRetry,\n} from './retry.ts'\n\n/** Options for uploading a small file in a single HTTP request. */\nexport interface UploadFileOptions extends UploadRetryOptions {\n /** Target bucket for the upload. */\n readonly bucketId: BucketId\n /** Full B2 file name including any path prefix. */\n readonly fileName: string\n /** Content to upload. */\n readonly source: ContentSource\n /** MIME type. Defaults to `b2/x-auto` for server-side detection. */\n readonly contentType?: string\n /** Custom file info key/value pairs stored with the file. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption settings. */\n readonly serverSideEncryption?: EncryptionSetting\n /** File retention policy applied at upload time. */\n readonly fileRetention?: FileRetentionValue\n /** Legal hold status applied at upload time. */\n readonly legalHold?: LegalHoldValue\n /** Override the last-modified timestamp (epoch millis). */\n readonly lastModifiedMillis?: number\n /** Callback invoked with upload progress updates. */\n readonly onProgress?: ProgressListener\n /** Signal to abort the upload. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Uploads a file in a single HTTP request (suitable for files up to ~100 MB).\n *\n * The entire file content is read into memory, SHA-1 hashed, and sent in one\n * `b2_upload_file` call. For files larger than the recommended part size, use\n * {@link uploadLargeFile} which splits the file into parts uploaded in parallel.\n *\n * Upload URLs are pooled via {@link AccountInfo} and recycled on success or\n * evicted on failure.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (tokens, URLs, upload URL pool).\n * @param options - Upload parameters.\n *\n * @returns The resulting {@link FileVersion} metadata.\n */\nexport async function uploadSmallFile(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: UploadFileOptions,\n): Promise<FileVersion> {\n const data = await readSmallFileSource(options.source, options.signal)\n if (data.byteLength !== options.source.size) {\n throw new Error(\n `uploadSmallFile: source byte count does not match advertised size ` +\n `(expected ${options.source.size} bytes, got ${data.byteLength} bytes).`,\n )\n }\n const sha1 = new IncrementalSha1()\n await sha1.update(data)\n const sha1Hex = await sha1.digest()\n\n // Single-request uploads send the whole payload in one shot, so the\n // tracker reports a single \"part\" worth of progress: one byte burst at\n // completion and a parts-completed transition from 0 → 1. The tracker\n // is created (rather than calling the listener directly) so consumers\n // see the same {bytesTransferred, totalBytes, partsCompleted,\n // totalParts, elapsedMs} shape they get from `uploadLargeFile`.\n const tracker = new ProgressTracker(options.onProgress, data.byteLength, 1)\n\n const result = await withFreshUploadUrlRetry({\n fileName: options.fileName,\n partNumber: null,\n retry: options.retry,\n signal: options.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: resolveRetryResponseBodyFailures(options.retryResponseBodyFailures),\n checkout: () => accountInfo.checkoutUploadUrl(options.bucketId),\n fetchFresh: () => fetchFreshUploadUrl(raw, accountInfo, options.bucketId, options.signal),\n returnEntry: (entry) => accountInfo.returnUploadUrl(options.bucketId, entry),\n evictEntry: (entry) => accountInfo.evictUploadUrl(options.bucketId, entry),\n upload: (entry) =>\n raw.uploadFile(\n entry.uploadUrl,\n {\n authorization: entry.authorizationToken,\n fileName: options.fileName,\n contentType: options.contentType ?? DEFAULT_CONTENT_TYPE,\n contentLength: data.byteLength,\n contentSha1: sha1Hex,\n ...(options.fileInfo !== undefined ? { fileInfo: options.fileInfo } : {}),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n ...(options.fileRetention !== undefined ? { fileRetention: options.fileRetention } : {}),\n ...(options.legalHold !== undefined ? { legalHold: options.legalHold } : {}),\n ...(options.lastModifiedMillis !== undefined\n ? { lastModifiedMillis: options.lastModifiedMillis }\n : {}),\n },\n data,\n {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n ),\n })\n\n tracker.addBytes(data.byteLength)\n tracker.completePart()\n return result\n}\n\nasync function readSmallFileSource(\n source: ContentSource,\n signal: AbortSignal | undefined,\n): Promise<Uint8Array<ArrayBuffer>> {\n signal?.throwIfAborted()\n if (!source.canSlice) {\n return collectStreamExactly(source.stream(), source.size, signal)\n }\n const data = new Uint8Array(\n await (signal === undefined ? source.toArrayBuffer() : source.toArrayBuffer({ signal })),\n )\n signal?.throwIfAborted()\n return data\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"single.js","names":[],"sources":["../../src/upload/single.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { IncrementalSha1 } from '../streams/hash.ts'\nimport { type ProgressListener, ProgressTracker } from '../streams/progress.ts'\nimport { type ContentSource, collectStreamExactly } from '../streams/source.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport type { BucketId } from '../types/ids.ts'\nimport type { FileRetentionValue, LegalHoldValue } from '../types/lock.ts'\nimport { DEFAULT_CONTENT_TYPE } from '../util/defaults.ts'\nimport {\n fetchFreshUploadUrl,\n resolveRetryResponseBodyFailures,\n type UploadRetryOptions,\n withFreshUploadUrlRetry,\n} from './retry.ts'\n\n/** Options for uploading a small file in a single HTTP request. */\nexport interface UploadFileOptions extends UploadRetryOptions {\n /** Target bucket for the upload. */\n readonly bucketId: BucketId\n /** Full B2 file name including any path prefix. */\n readonly fileName: string\n /** Content to upload. */\n readonly source: ContentSource\n /** MIME type. Defaults to `b2/x-auto` for server-side detection. */\n readonly contentType?: string\n /** Custom file info key/value pairs stored with the file. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption settings. */\n readonly serverSideEncryption?: EncryptionSetting\n /** File retention policy applied at upload time. */\n readonly fileRetention?: FileRetentionValue\n /** Legal hold status applied at upload time. */\n readonly legalHold?: LegalHoldValue\n /** Override the last-modified timestamp (epoch millis). */\n readonly lastModifiedMillis?: number\n /**\n * B2 upload timestamp override in milliseconds since epoch.\n * Sets the file version `uploadTimestamp`; distinct from `lastModifiedMillis` metadata.\n * Requires B2 account enablement for Custom Upload Timestamp.\n */\n readonly customUploadTimestamp?: number\n /** Callback invoked with upload progress updates. */\n readonly onProgress?: ProgressListener\n /** Signal to abort the upload. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Uploads a file in a single HTTP request (suitable for files up to ~100 MB).\n *\n * The entire file content is read into memory, SHA-1 hashed, and sent in one\n * `b2_upload_file` call. For files larger than the recommended part size, use\n * {@link uploadLargeFile} which splits the file into parts uploaded in parallel.\n *\n * Upload URLs are pooled via {@link AccountInfo} and recycled on success or\n * evicted on failure.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (tokens, URLs, upload URL pool).\n * @param options - Upload parameters.\n *\n * @returns The resulting {@link FileVersion} metadata.\n */\nexport async function uploadSmallFile(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: UploadFileOptions,\n): Promise<FileVersion> {\n const data = await readSmallFileSource(options.source, options.signal)\n if (data.byteLength !== options.source.size) {\n throw new Error(\n `uploadSmallFile: source byte count does not match advertised size ` +\n `(expected ${options.source.size} bytes, got ${data.byteLength} bytes).`,\n )\n }\n const sha1 = new IncrementalSha1()\n await sha1.update(data)\n const sha1Hex = await sha1.digest()\n\n // Single-request uploads send the whole payload in one shot, so the\n // tracker reports a single \"part\" worth of progress: one byte burst at\n // completion and a parts-completed transition from 0 → 1. The tracker\n // is created (rather than calling the listener directly) so consumers\n // see the same {bytesTransferred, totalBytes, partsCompleted,\n // totalParts, elapsedMs} shape they get from `uploadLargeFile`.\n const tracker = new ProgressTracker(options.onProgress, data.byteLength, 1)\n\n const result = await withFreshUploadUrlRetry({\n fileName: options.fileName,\n partNumber: null,\n retry: options.retry,\n signal: options.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: resolveRetryResponseBodyFailures(options.retryResponseBodyFailures),\n checkout: () => accountInfo.checkoutUploadUrl(options.bucketId),\n fetchFresh: () => fetchFreshUploadUrl(raw, accountInfo, options.bucketId, options.signal),\n returnEntry: (entry) => accountInfo.returnUploadUrl(options.bucketId, entry),\n evictEntry: (entry) => accountInfo.evictUploadUrl(options.bucketId, entry),\n upload: (entry) =>\n raw.uploadFile(\n entry.uploadUrl,\n {\n authorization: entry.authorizationToken,\n fileName: options.fileName,\n contentType: options.contentType ?? DEFAULT_CONTENT_TYPE,\n contentLength: data.byteLength,\n contentSha1: sha1Hex,\n ...(options.fileInfo !== undefined ? { fileInfo: options.fileInfo } : {}),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n ...(options.fileRetention !== undefined ? { fileRetention: options.fileRetention } : {}),\n ...(options.legalHold !== undefined ? { legalHold: options.legalHold } : {}),\n ...(options.lastModifiedMillis !== undefined\n ? { lastModifiedMillis: options.lastModifiedMillis }\n : {}),\n ...(options.customUploadTimestamp !== undefined\n ? { customUploadTimestamp: options.customUploadTimestamp }\n : {}),\n },\n data,\n {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n ),\n })\n\n tracker.addBytes(data.byteLength)\n tracker.completePart()\n return result\n}\n\nasync function readSmallFileSource(\n source: ContentSource,\n signal: AbortSignal | undefined,\n): Promise<Uint8Array<ArrayBuffer>> {\n signal?.throwIfAborted()\n if (!source.canSlice) {\n return collectStreamExactly(source.stream(), source.size, signal)\n }\n const data = new Uint8Array(\n await (signal === undefined ? source.toArrayBuffer() : source.toArrayBuffer({ signal })),\n )\n signal?.throwIfAborted()\n return data\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAiEA,eAAsB,gBACpB,KACA,aACA,SACsB;CACtB,MAAM,OAAO,MAAM,oBAAoB,QAAQ,QAAQ,QAAQ,MAAM;CACrE,IAAI,KAAK,eAAe,QAAQ,OAAO,MACrC,MAAM,IAAI,MACR,+EACe,QAAQ,OAAO,KAAK,cAAc,KAAK,WAAW,SACnE;CAEF,MAAM,OAAO,IAAI,gBAAgB;CACjC,MAAM,KAAK,OAAO,IAAI;CACtB,MAAM,UAAU,MAAM,KAAK,OAAO;CAQlC,MAAM,UAAU,IAAI,gBAAgB,QAAQ,YAAY,KAAK,YAAY,CAAC;CAE1E,MAAM,SAAS,MAAM,wBAAwB;EAC3C,UAAU,QAAQ;EAClB,YAAY;EACZ,OAAO,QAAQ;EACf,QAAQ,QAAQ;EAChB,eAAe,QAAQ;EACvB,2BAA2B,iCAAiC,QAAQ,yBAAyB;EAC7F,gBAAgB,YAAY,kBAAkB,QAAQ,QAAQ;EAC9D,kBAAkB,oBAAoB,KAAK,aAAa,QAAQ,UAAU,QAAQ,MAAM;EACxF,cAAc,UAAU,YAAY,gBAAgB,QAAQ,UAAU,KAAK;EAC3E,aAAa,UAAU,YAAY,eAAe,QAAQ,UAAU,KAAK;EACzE,SAAS,UACP,IAAI,WACF,MAAM,WACN;GACE,eAAe,MAAM;GACrB,UAAU,QAAQ;GAClB,aAAa,QAAQ,eAAA;GACrB,eAAe,KAAK;GACpB,aAAa;GACb,GAAI,QAAQ,aAAa,KAAA,IAAY,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;GACvE,GAAI,QAAQ,yBAAyB,KAAA,IACjC,EAAE,sBAAsB,QAAQ,qBAAqB,IACrD,CAAC;GACL,GAAI,QAAQ,kBAAkB,KAAA,IAAY,EAAE,eAAe,QAAQ,cAAc,IAAI,CAAC;GACtF,GAAI,QAAQ,cAAc,KAAA,IAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;GAC1E,GAAI,QAAQ,uBAAuB,KAAA,IAC/B,EAAE,oBAAoB,QAAQ,mBAAmB,IACjD,CAAC;GACL,GAAI,QAAQ,0BAA0B,KAAA,IAClC,EAAE,uBAAuB,QAAQ,sBAAsB,IACvD,CAAC;EACP,GACA,MACA;GACE,GAAI,QAAQ,WAAW,KAAA,IAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;GACjE,GAAI,QAAQ,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;EAChE,CACF;CACJ,CAAC;CAED,QAAQ,SAAS,KAAK,UAAU;CAChC,QAAQ,aAAa;CACrB,OAAO;AACT;AAEA,eAAe,oBACb,QACA,QACkC;CAClC,QAAQ,eAAe;CACvB,IAAI,CAAC,OAAO,UACV,OAAO,qBAAqB,OAAO,OAAO,GAAG,OAAO,MAAM,MAAM;CAElE,MAAM,OAAO,IAAI,WACf,OAAO,WAAW,KAAA,IAAY,OAAO,cAAc,IAAI,OAAO,cAAc,EAAE,OAAO,CAAC,EACxF;CACA,QAAQ,eAAe;CACvB,OAAO;AACT"}
|
package/dist/upload/stream.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const require_abort_scope = require("./abort-scope.cjs");
|
|
2
2
|
const require_cancel = require("./cancel.cjs");
|
|
3
3
|
const require_concurrency = require("./concurrency.cjs");
|
|
4
|
+
const require_abort = require("../util/abort.cjs");
|
|
4
5
|
const require_finish = require("./finish.cjs");
|
|
5
6
|
require("../util/defaults.cjs");
|
|
6
7
|
const require_progress = require("../streams/progress.cjs");
|
|
@@ -50,6 +51,7 @@ function createWriteStream(raw, accountInfo, options) {
|
|
|
50
51
|
fileName: options.fileName,
|
|
51
52
|
contentType: options.contentType ?? "b2/x-auto",
|
|
52
53
|
fileInfo: options.fileInfo ?? {},
|
|
54
|
+
...options.customUploadTimestamp !== void 0 ? { customUploadTimestamp: String(options.customUploadTimestamp) } : {},
|
|
53
55
|
...options.serverSideEncryption !== void 0 ? { serverSideEncryption: options.serverSideEncryption } : {}
|
|
54
56
|
}, {
|
|
55
57
|
signal: abortScope.signal,
|
|
@@ -60,7 +62,7 @@ function createWriteStream(raw, accountInfo, options) {
|
|
|
60
62
|
return largeFileId;
|
|
61
63
|
});
|
|
62
64
|
const started = startPromise;
|
|
63
|
-
return
|
|
65
|
+
return require_abort.raceWithAbort(started, abortScope.signal).catch((err) => {
|
|
64
66
|
if (abortScope.signal.aborted) scheduleCancelLargeFileAfterStart(started);
|
|
65
67
|
throw err;
|
|
66
68
|
});
|
|
@@ -114,7 +116,7 @@ function createWriteStream(raw, accountInfo, options) {
|
|
|
114
116
|
settled.then(() => "settled"),
|
|
115
117
|
startPromise.then(() => "start-settled", () => "start-settled"),
|
|
116
118
|
abortWaiter.promise.then(() => "aborted")
|
|
117
|
-
]) === "aborted" && largeFileId === null) throw
|
|
119
|
+
]) === "aborted" && largeFileId === null) throw require_abort.abortReason(abortScope.signal);
|
|
118
120
|
return await settled;
|
|
119
121
|
} finally {
|
|
120
122
|
abortWaiter.dispose();
|
|
@@ -302,13 +304,13 @@ function carveExact(chunks, size) {
|
|
|
302
304
|
}
|
|
303
305
|
function waitForAbort(signal) {
|
|
304
306
|
if (signal.aborted) return {
|
|
305
|
-
promise: Promise.resolve(
|
|
307
|
+
promise: Promise.resolve(require_abort.abortReason(signal)),
|
|
306
308
|
dispose() {}
|
|
307
309
|
};
|
|
308
310
|
let onAbort;
|
|
309
311
|
return {
|
|
310
312
|
promise: new Promise((resolve) => {
|
|
311
|
-
onAbort = () => resolve(
|
|
313
|
+
onAbort = () => resolve(require_abort.abortReason(signal));
|
|
312
314
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
313
315
|
}),
|
|
314
316
|
dispose() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.cjs","names":[],"sources":["../../src/upload/stream.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { IncrementalSha1 } from '../streams/hash.ts'\nimport type { ProgressListener } from '../streams/progress.ts'\nimport { ProgressTracker } from '../streams/progress.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport type { BucketId, LargeFileId } from '../types/ids.ts'\nimport { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { toError } from '../util/to-error.ts'\nimport { abortReason, createAbortScope, raceWithAbort } from './abort-scope.ts'\nimport {\n type CleanupFailureOptions,\n cancelLargeFileBestEffort,\n DEFAULT_CLEANUP_TIMEOUT_MS,\n resolveLargeFileErrorAfterCleanup,\n} from './cancel.ts'\nimport { Semaphore } from './concurrency.ts'\nimport { finishLargeFileWithAbortReconciliation } from './finish.ts'\nimport {\n resolveRetryResponseBodyFailures,\n type UploadRetryOptions,\n uploadPartWithFreshUrl,\n} from './retry.ts'\n\n/** Options for creating a streaming multipart upload sink. */\nexport interface CreateWriteStreamOptions extends UploadRetryOptions, CleanupFailureOptions {\n /** Target bucket for the upload. */\n readonly bucketId: BucketId\n /** Destination file name in the bucket. */\n readonly fileName: string\n /** MIME type. Defaults to `b2/x-auto`. */\n readonly contentType?: string\n /** Custom file info stored with the file. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption applied to each part. */\n readonly serverSideEncryption?: EncryptionSetting\n /**\n * Target part size in bytes. The stream buffers writes until this many bytes\n * are accumulated, then ships a part. Must be at least the account's\n * absolute minimum part size; the implementation will raise it if too small.\n */\n readonly partSize?: number\n /** Maximum number of parts uploaded in parallel. Defaults to 4. */\n readonly concurrency?: number\n /** Callback invoked with upload progress events. `totalBytes` is `null` (size unknown). */\n readonly onProgress?: ProgressListener\n /** Aborts the upload and cancels the unfinished large file. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Handle returned by `B2Object.createWriteStream`: the Web `WritableStream` to\n * pipe data into, plus a promise that resolves with the finished\n * {@link FileVersion} once the stream is closed and all parts have been\n * uploaded.\n */\nexport interface UploadWriteHandle {\n /** Web `WritableStream` sink to pipe data into. */\n readonly writable: WritableStream<Uint8Array>\n /** Resolves with the finalized file version when the stream closes successfully. */\n readonly done: Promise<FileVersion>\n}\n\n/**\n * Creates a {@link WritableStream} that streams data into a B2 multipart upload.\n *\n * Buffers incoming chunks until `partSize` bytes are accumulated, ships each\n * complete part through the standard multipart engine, and finalizes the file\n * once the stream is closed. Honours backpressure via the queue's bounded\n * concurrency. Streaming uploads do not support resume because the total size\n * and per-part hashes aren't known in advance; use {@link uploadLargeFile} with\n * a buffered source when resume is required.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (tokens, URLs, part URL pool).\n * @param options - Streaming upload parameters.\n *\n * @returns A {@link UploadWriteHandle} with the writable sink and a completion promise.\n */\nexport function createWriteStream(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: CreateWriteStreamOptions,\n): UploadWriteHandle {\n const minPartSize = accountInfo.getAbsoluteMinimumPartSize()\n const recommendedPartSize = accountInfo.getRecommendedPartSize()\n const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize)\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n const tracker = new ProgressTracker(options.onProgress, null, null)\n const sem = new Semaphore(concurrency)\n const abortScope = createAbortScope(options.signal)\n\n let largeFileId: LargeFileId | null = null\n let startPromise: Promise<LargeFileId> | null = null\n let cancelAfterStartScheduled = false\n let nextPartNumber = 1\n let pendingBytes = 0\n const pending: Uint8Array[] = []\n const partSha1s: string[] = []\n const inflight: Promise<void>[] = []\n let errored: Error | null = null\n\n // ES2024 `Promise.withResolvers()` exposes the resolve/reject pair as\n // properties of the returned object, avoiding the `let resolve!:` /\n // `let reject!:` non-null-assertion pattern needed before this API\n // existed. Available in Node 22 (our minimum), Bun, Deno, modern\n // browsers, and Cloudflare Workers.\n const {\n promise: done,\n resolve: resolveDone,\n reject: rejectDone,\n } = Promise.withResolvers<FileVersion>()\n // Attach a no-op rejection handler so a `done` that rejects before the\n // caller observes it never surfaces as a process-level unhandled rejection.\n // The error is not swallowed: `close()`/`abort()` also reject the writable\n // (so `pipeTo`/`getWriter().close()` throw), and any later `await done` /\n // `done.then(onRejected)` the caller attaches still sees the rejection —\n // extra handlers on the same promise all fire. Mirrors the\n // `task.catch(() => {})` pattern used for inflight part uploads below, and\n // keeps the engine well-behaved under Bun and Node's strict\n // unhandled-rejection mode.\n done.catch(() => {})\n\n function ensureStarted(): Promise<LargeFileId> {\n if (largeFileId !== null) return Promise.resolve(largeFileId)\n if (startPromise === null) {\n const rawStart = raw.startLargeFile(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n bucketId: options.bucketId,\n fileName: options.fileName,\n contentType: options.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n },\n {\n signal: abortScope.signal,\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n )\n startPromise = rawStart.then((resp) => {\n largeFileId = resp.fileId\n if (abortScope.signal.aborted || errored !== null) {\n cancelStartedLargeFile(largeFileId)\n }\n return largeFileId\n })\n }\n const started = startPromise\n return raceWithAbort(started, abortScope.signal).catch((err) => {\n if (abortScope.signal.aborted) scheduleCancelLargeFileAfterStart(started)\n throw err\n })\n }\n\n async function shipPart(data: Uint8Array, partNumber: number): Promise<void> {\n if (errored !== null) throw errored\n abortScope.signal.throwIfAborted()\n const fileId = await ensureStarted()\n if (errored !== null) throw errored\n abortScope.signal.throwIfAborted()\n\n const sha1 = new IncrementalSha1()\n await sha1.update(data)\n const sha1Hex = await sha1.digest()\n abortScope.signal.throwIfAborted()\n\n const result = await uploadPartWithFreshUrl(raw, accountInfo, fileId, {\n fileName: options.fileName,\n partNumber,\n data: data as BodyInit,\n contentLength: data.byteLength,\n contentSha1: sha1Hex,\n retry: options.retry,\n signal: abortScope.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: resolveRetryResponseBodyFailures(\n options.retryResponseBodyFailures,\n ),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n })\n partSha1s[partNumber - 1] = result.contentSha1\n tracker.addBytes(data.byteLength)\n tracker.completePart()\n }\n\n function markErrored(err: unknown): Error {\n const error = toError(err)\n errored = error\n abortScope.abort(error)\n return error\n }\n\n function cancelStartedLargeFile(fileId: LargeFileId): void {\n if (cancelAfterStartScheduled) return\n cancelAfterStartScheduled = true\n void cancelLargeFileBestEffort(raw, accountInfo, fileId, cleanupWriteStreamOptions(options))\n }\n\n function scheduleCancelLargeFileAfterStart(started: Promise<LargeFileId>): void {\n void started\n .then((fileId) => cancelStartedLargeFile(fileId))\n .catch(() => {\n // If start failed, no file ID is available to cancel.\n })\n }\n\n async function settleInflightForClose(): Promise<PromiseSettledResult<void>[]> {\n const settled = Promise.allSettled(inflight)\n if (startPromise === null || largeFileId !== null) return await settled\n\n const abortWaiter = waitForAbort(abortScope.signal)\n try {\n const first = await Promise.race([\n settled.then(() => 'settled' as const),\n startPromise.then(\n () => 'start-settled' as const,\n () => 'start-settled' as const,\n ),\n abortWaiter.promise.then(() => 'aborted' as const),\n ])\n if (first === 'aborted' && largeFileId === null) {\n throw abortReason(abortScope.signal)\n }\n return await settled\n } finally {\n abortWaiter.dispose()\n }\n }\n\n function startPartWithAcquiredSlot(data: Uint8Array, partNumber: number): void {\n const task = (async () => {\n try {\n await shipPart(data, partNumber)\n } catch (err) {\n markErrored(err)\n throw err\n } finally {\n sem.release()\n }\n })()\n inflight.push(task)\n // Swallow rejection here; we check `errored` later.\n task.catch(() => {})\n }\n\n async function acquirePartSlot(): Promise<void> {\n await sem.acquire()\n if (errored !== null) {\n sem.release()\n throw errored\n }\n try {\n abortScope.signal.throwIfAborted()\n } catch (err) {\n sem.release()\n throw err\n }\n }\n\n async function waitForInflightPartsToSettle(\n timeoutMs = DEFAULT_CLEANUP_TIMEOUT_MS,\n ): Promise<void> {\n if (inflight.length === 0) return\n let timeout: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n Promise.allSettled(inflight).then(() => undefined),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs)\n }),\n ])\n } finally {\n if (timeout !== undefined) clearTimeout(timeout)\n }\n }\n\n async function dispatchPart(): Promise<void> {\n if (pending.length === 0) return\n await acquirePartSlot()\n let data: Uint8Array\n if (pending.length === 1) {\n const head = pending[0]\n if (!head) {\n sem.release()\n return\n }\n data = head\n } else {\n const total = pending.reduce((sum, chunk) => sum + chunk.byteLength, 0)\n data = new Uint8Array(total)\n let offset = 0\n for (const chunk of pending) {\n data.set(chunk, offset)\n offset += chunk.byteLength\n }\n }\n pending.length = 0\n pendingBytes = 0\n const partNumber = nextPartNumber++\n startPartWithAcquiredSlot(data, partNumber)\n }\n\n const writable = new WritableStream<Uint8Array>({\n async write(chunk: Uint8Array): Promise<void> {\n if (errored) throw errored\n abortScope.signal.throwIfAborted()\n if (chunk.byteLength === 0) return\n\n pending.push(chunk)\n pendingBytes += chunk.byteLength\n while (pendingBytes >= partSize) {\n await acquirePartSlot()\n if (errored) {\n sem.release()\n throw errored\n }\n // Pull exactly partSize bytes off the front\n const carved = carveExact(pending, partSize)\n const partNumber = nextPartNumber++\n pendingBytes -= partSize\n startPartWithAcquiredSlot(carved, partNumber)\n }\n },\n\n async close(): Promise<void> {\n try {\n if (errored) throw errored\n abortScope.signal.throwIfAborted()\n\n // Ship any remaining bytes as the last part. If the total fit in one\n // single buffered batch (no parts shipped yet), we have to use the\n // multipart path anyway since we already called startLargeFile lazily.\n if (pendingBytes > 0) {\n await dispatchPart()\n }\n\n const settled = await settleInflightForClose()\n const rejected = settled.find(\n (result): result is PromiseRejectedResult => result.status === 'rejected',\n )\n if (rejected !== undefined && errored === null) {\n markErrored(rejected.reason)\n }\n if (errored) throw errored\n\n if (largeFileId === null) {\n // Stream closed before any data was written. Start an empty large\n // file? B2 requires at least 2 parts. Reject — callers shouldn't\n // close an empty stream.\n throw new Error('createWriteStream closed without any data written.')\n }\n\n const result = await finishLargeFileWithAbortReconciliation(raw, accountInfo, {\n fileId: largeFileId,\n bucketId: options.bucketId,\n fileName: options.fileName,\n partSha1s,\n signal: abortScope.signal,\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n })\n abortScope.dispose()\n resolveDone(result)\n } catch (err) {\n const closeError = toError(err)\n if (errored === null) errored = closeError\n abortScope.abort(errored)\n const observedError = errored\n const fileIdToCancel = largeFileId\n if (fileIdToCancel === null && startPromise !== null) {\n // Mirror writer.abort(): close() must not wait for non-abortable\n // b2_start_large_file. If a file ID arrives later, cancel it in the\n // background.\n scheduleCancelLargeFileAfterStart(startPromise)\n abortScope.dispose()\n rejectDone(observedError)\n throw observedError\n }\n await Promise.allSettled(inflight)\n // Capture into a const so the cancel closure sees a non-null\n // `fileId`; closures don't observe the outer `!== null` narrowing\n // because the variable is mutable across the lambda boundary.\n let finalError: unknown = observedError\n if (fileIdToCancel !== null) {\n finalError = await resolveLargeFileErrorAfterCleanup(observedError, raw, accountInfo, {\n fileId: fileIdToCancel,\n bucketId: options.bucketId,\n fileName: options.fileName,\n signal: options.signal,\n onCleanupFailure: options.onCleanupFailure,\n })\n }\n abortScope.dispose()\n rejectDone(finalError)\n throw finalError\n }\n },\n\n async abort(reason: unknown): Promise<void> {\n const abortError = markErrored(reason)\n pending.length = 0\n pendingBytes = 0\n const fileIdToCancel = largeFileId\n if (fileIdToCancel === null && startPromise !== null) {\n // Do not await an in-flight start request here. A stalled\n // b2_start_large_file must not make writer.abort() hang; if it later\n // returns a file ID, cancel it best-effort in the background.\n scheduleCancelLargeFileAfterStart(startPromise)\n }\n if (fileIdToCancel !== null) {\n await waitForInflightPartsToSettle()\n await cancelLargeFileBestEffort(\n raw,\n accountInfo,\n fileIdToCancel,\n cleanupWriteStreamOptions(options),\n )\n }\n abortScope.dispose()\n rejectDone(abortError)\n },\n })\n\n return { writable, done }\n}\n\nfunction cleanupWriteStreamOptions(options: CreateWriteStreamOptions): {\n readonly signal?: AbortSignal\n} & CleanupFailureOptions {\n return {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.onCleanupFailure !== undefined\n ? { onCleanupFailure: options.onCleanupFailure }\n : {}),\n }\n}\n\n/**\n * Removes exactly `size` bytes from the front of `chunks` (mutates) and returns\n * them as a contiguous Uint8Array. Any trailing remainder of the last chunk\n * stays at the front of `chunks` for the next part.\n *\n * @param chunks - Queue of pending chunks. Modified in place.\n * @param size - Number of bytes to carve off the front.\n *\n * @returns A new Uint8Array containing exactly `size` bytes.\n */\nfunction carveExact(chunks: Uint8Array[], size: number): Uint8Array {\n const out = new Uint8Array(size)\n let written = 0\n while (written < size && chunks.length > 0) {\n const head = chunks[0]\n if (!head) break\n const need = size - written\n if (head.byteLength <= need) {\n out.set(head, written)\n written += head.byteLength\n chunks.shift()\n } else {\n out.set(head.subarray(0, need), written)\n chunks[0] = head.subarray(need)\n written += need\n }\n }\n return out\n}\n\nfunction waitForAbort(signal: AbortSignal): {\n readonly promise: Promise<unknown>\n dispose(): void\n} {\n if (signal.aborted) {\n return {\n promise: Promise.resolve(abortReason(signal)),\n dispose() {},\n }\n }\n\n let onAbort: (() => void) | undefined\n const promise = new Promise<unknown>((resolve) => {\n onAbort = () => resolve(abortReason(signal))\n signal.addEventListener('abort', onAbort, { once: true })\n })\n\n return {\n promise,\n dispose() {\n if (onAbort !== undefined) signal.removeEventListener('abort', onAbort)\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stream.cjs","names":[],"sources":["../../src/upload/stream.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { IncrementalSha1 } from '../streams/hash.ts'\nimport type { ProgressListener } from '../streams/progress.ts'\nimport { ProgressTracker } from '../streams/progress.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport type { BucketId, LargeFileId } from '../types/ids.ts'\nimport { abortReason, raceWithAbort } from '../util/abort.ts'\nimport { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { toError } from '../util/to-error.ts'\nimport { createAbortScope } from './abort-scope.ts'\nimport {\n type CleanupFailureOptions,\n cancelLargeFileBestEffort,\n DEFAULT_CLEANUP_TIMEOUT_MS,\n resolveLargeFileErrorAfterCleanup,\n} from './cancel.ts'\nimport { Semaphore } from './concurrency.ts'\nimport { finishLargeFileWithAbortReconciliation } from './finish.ts'\nimport {\n resolveRetryResponseBodyFailures,\n type UploadRetryOptions,\n uploadPartWithFreshUrl,\n} from './retry.ts'\n\n/** Options for creating a streaming multipart upload sink. */\nexport interface CreateWriteStreamOptions extends UploadRetryOptions, CleanupFailureOptions {\n /** Target bucket for the upload. */\n readonly bucketId: BucketId\n /** Destination file name in the bucket. */\n readonly fileName: string\n /** MIME type. Defaults to `b2/x-auto`. */\n readonly contentType?: string\n /** Custom file info stored with the file. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption applied to each part. */\n readonly serverSideEncryption?: EncryptionSetting\n /**\n * B2 upload timestamp override in milliseconds since epoch.\n * Sets the file version `uploadTimestamp`; distinct from `src_last_modified_millis` metadata.\n * Requires B2 account enablement for Custom Upload Timestamp.\n */\n readonly customUploadTimestamp?: number\n /**\n * Target part size in bytes. The stream buffers writes until this many bytes\n * are accumulated, then ships a part. Must be at least the account's\n * absolute minimum part size; the implementation will raise it if too small.\n */\n readonly partSize?: number\n /** Maximum number of parts uploaded in parallel. Defaults to 4. */\n readonly concurrency?: number\n /** Callback invoked with upload progress events. `totalBytes` is `null` (size unknown). */\n readonly onProgress?: ProgressListener\n /** Aborts the upload and cancels the unfinished large file. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Handle returned by `B2Object.createWriteStream`: the Web `WritableStream` to\n * pipe data into, plus a promise that resolves with the finished\n * {@link FileVersion} once the stream is closed and all parts have been\n * uploaded.\n */\nexport interface UploadWriteHandle {\n /** Web `WritableStream` sink to pipe data into. */\n readonly writable: WritableStream<Uint8Array>\n /** Resolves with the finalized file version when the stream closes successfully. */\n readonly done: Promise<FileVersion>\n}\n\n/**\n * Creates a {@link WritableStream} that streams data into a B2 multipart upload.\n *\n * Buffers incoming chunks until `partSize` bytes are accumulated, ships each\n * complete part through the standard multipart engine, and finalizes the file\n * once the stream is closed. Honours backpressure via the queue's bounded\n * concurrency. Streaming uploads do not support resume because the total size\n * and per-part hashes aren't known in advance; use {@link uploadLargeFile} with\n * a buffered source when resume is required.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (tokens, URLs, part URL pool).\n * @param options - Streaming upload parameters.\n *\n * @returns A {@link UploadWriteHandle} with the writable sink and a completion promise.\n */\nexport function createWriteStream(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: CreateWriteStreamOptions,\n): UploadWriteHandle {\n const minPartSize = accountInfo.getAbsoluteMinimumPartSize()\n const recommendedPartSize = accountInfo.getRecommendedPartSize()\n const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize)\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n const tracker = new ProgressTracker(options.onProgress, null, null)\n const sem = new Semaphore(concurrency)\n const abortScope = createAbortScope(options.signal)\n\n let largeFileId: LargeFileId | null = null\n let startPromise: Promise<LargeFileId> | null = null\n let cancelAfterStartScheduled = false\n let nextPartNumber = 1\n let pendingBytes = 0\n const pending: Uint8Array[] = []\n const partSha1s: string[] = []\n const inflight: Promise<void>[] = []\n let errored: Error | null = null\n\n // ES2024 `Promise.withResolvers()` exposes the resolve/reject pair as\n // properties of the returned object, avoiding the `let resolve!:` /\n // `let reject!:` non-null-assertion pattern needed before this API\n // existed. Available in Node 22 (our minimum), Bun, Deno, modern\n // browsers, and Cloudflare Workers.\n const {\n promise: done,\n resolve: resolveDone,\n reject: rejectDone,\n } = Promise.withResolvers<FileVersion>()\n // Attach a no-op rejection handler so a `done` that rejects before the\n // caller observes it never surfaces as a process-level unhandled rejection.\n // The error is not swallowed: `close()`/`abort()` also reject the writable\n // (so `pipeTo`/`getWriter().close()` throw), and any later `await done` /\n // `done.then(onRejected)` the caller attaches still sees the rejection —\n // extra handlers on the same promise all fire. Mirrors the\n // `task.catch(() => {})` pattern used for inflight part uploads below, and\n // keeps the engine well-behaved under Bun and Node's strict\n // unhandled-rejection mode.\n done.catch(() => {})\n\n function ensureStarted(): Promise<LargeFileId> {\n if (largeFileId !== null) return Promise.resolve(largeFileId)\n if (startPromise === null) {\n const rawStart = raw.startLargeFile(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n bucketId: options.bucketId,\n fileName: options.fileName,\n contentType: options.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n ...(options.customUploadTimestamp !== undefined\n ? { customUploadTimestamp: String(options.customUploadTimestamp) }\n : {}),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n },\n {\n signal: abortScope.signal,\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n )\n startPromise = rawStart.then((resp) => {\n largeFileId = resp.fileId\n if (abortScope.signal.aborted || errored !== null) {\n cancelStartedLargeFile(largeFileId)\n }\n return largeFileId\n })\n }\n const started = startPromise\n return raceWithAbort(started, abortScope.signal).catch((err) => {\n if (abortScope.signal.aborted) scheduleCancelLargeFileAfterStart(started)\n throw err\n })\n }\n\n async function shipPart(data: Uint8Array, partNumber: number): Promise<void> {\n if (errored !== null) throw errored\n abortScope.signal.throwIfAborted()\n const fileId = await ensureStarted()\n if (errored !== null) throw errored\n abortScope.signal.throwIfAborted()\n\n const sha1 = new IncrementalSha1()\n await sha1.update(data)\n const sha1Hex = await sha1.digest()\n abortScope.signal.throwIfAborted()\n\n const result = await uploadPartWithFreshUrl(raw, accountInfo, fileId, {\n fileName: options.fileName,\n partNumber,\n data: data as BodyInit,\n contentLength: data.byteLength,\n contentSha1: sha1Hex,\n retry: options.retry,\n signal: abortScope.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: resolveRetryResponseBodyFailures(\n options.retryResponseBodyFailures,\n ),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n })\n partSha1s[partNumber - 1] = result.contentSha1\n tracker.addBytes(data.byteLength)\n tracker.completePart()\n }\n\n function markErrored(err: unknown): Error {\n const error = toError(err)\n errored = error\n abortScope.abort(error)\n return error\n }\n\n function cancelStartedLargeFile(fileId: LargeFileId): void {\n if (cancelAfterStartScheduled) return\n cancelAfterStartScheduled = true\n void cancelLargeFileBestEffort(raw, accountInfo, fileId, cleanupWriteStreamOptions(options))\n }\n\n function scheduleCancelLargeFileAfterStart(started: Promise<LargeFileId>): void {\n void started\n .then((fileId) => cancelStartedLargeFile(fileId))\n .catch(() => {\n // If start failed, no file ID is available to cancel.\n })\n }\n\n async function settleInflightForClose(): Promise<PromiseSettledResult<void>[]> {\n const settled = Promise.allSettled(inflight)\n if (startPromise === null || largeFileId !== null) return await settled\n\n const abortWaiter = waitForAbort(abortScope.signal)\n try {\n const first = await Promise.race([\n settled.then(() => 'settled' as const),\n startPromise.then(\n () => 'start-settled' as const,\n () => 'start-settled' as const,\n ),\n abortWaiter.promise.then(() => 'aborted' as const),\n ])\n if (first === 'aborted' && largeFileId === null) {\n throw abortReason(abortScope.signal)\n }\n return await settled\n } finally {\n abortWaiter.dispose()\n }\n }\n\n function startPartWithAcquiredSlot(data: Uint8Array, partNumber: number): void {\n const task = (async () => {\n try {\n await shipPart(data, partNumber)\n } catch (err) {\n markErrored(err)\n throw err\n } finally {\n sem.release()\n }\n })()\n inflight.push(task)\n // Swallow rejection here; we check `errored` later.\n task.catch(() => {})\n }\n\n async function acquirePartSlot(): Promise<void> {\n await sem.acquire()\n if (errored !== null) {\n sem.release()\n throw errored\n }\n try {\n abortScope.signal.throwIfAborted()\n } catch (err) {\n sem.release()\n throw err\n }\n }\n\n async function waitForInflightPartsToSettle(\n timeoutMs = DEFAULT_CLEANUP_TIMEOUT_MS,\n ): Promise<void> {\n if (inflight.length === 0) return\n let timeout: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n Promise.allSettled(inflight).then(() => undefined),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs)\n }),\n ])\n } finally {\n if (timeout !== undefined) clearTimeout(timeout)\n }\n }\n\n async function dispatchPart(): Promise<void> {\n if (pending.length === 0) return\n await acquirePartSlot()\n let data: Uint8Array\n if (pending.length === 1) {\n const head = pending[0]\n if (!head) {\n sem.release()\n return\n }\n data = head\n } else {\n const total = pending.reduce((sum, chunk) => sum + chunk.byteLength, 0)\n data = new Uint8Array(total)\n let offset = 0\n for (const chunk of pending) {\n data.set(chunk, offset)\n offset += chunk.byteLength\n }\n }\n pending.length = 0\n pendingBytes = 0\n const partNumber = nextPartNumber++\n startPartWithAcquiredSlot(data, partNumber)\n }\n\n const writable = new WritableStream<Uint8Array>({\n async write(chunk: Uint8Array): Promise<void> {\n if (errored) throw errored\n abortScope.signal.throwIfAborted()\n if (chunk.byteLength === 0) return\n\n pending.push(chunk)\n pendingBytes += chunk.byteLength\n while (pendingBytes >= partSize) {\n await acquirePartSlot()\n if (errored) {\n sem.release()\n throw errored\n }\n // Pull exactly partSize bytes off the front\n const carved = carveExact(pending, partSize)\n const partNumber = nextPartNumber++\n pendingBytes -= partSize\n startPartWithAcquiredSlot(carved, partNumber)\n }\n },\n\n async close(): Promise<void> {\n try {\n if (errored) throw errored\n abortScope.signal.throwIfAborted()\n\n // Ship any remaining bytes as the last part. If the total fit in one\n // single buffered batch (no parts shipped yet), we have to use the\n // multipart path anyway since we already called startLargeFile lazily.\n if (pendingBytes > 0) {\n await dispatchPart()\n }\n\n const settled = await settleInflightForClose()\n const rejected = settled.find(\n (result): result is PromiseRejectedResult => result.status === 'rejected',\n )\n if (rejected !== undefined && errored === null) {\n markErrored(rejected.reason)\n }\n if (errored) throw errored\n\n if (largeFileId === null) {\n // Stream closed before any data was written. Start an empty large\n // file? B2 requires at least 2 parts. Reject — callers shouldn't\n // close an empty stream.\n throw new Error('createWriteStream closed without any data written.')\n }\n\n const result = await finishLargeFileWithAbortReconciliation(raw, accountInfo, {\n fileId: largeFileId,\n bucketId: options.bucketId,\n fileName: options.fileName,\n partSha1s,\n signal: abortScope.signal,\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n })\n abortScope.dispose()\n resolveDone(result)\n } catch (err) {\n const closeError = toError(err)\n if (errored === null) errored = closeError\n abortScope.abort(errored)\n const observedError = errored\n const fileIdToCancel = largeFileId\n if (fileIdToCancel === null && startPromise !== null) {\n // Mirror writer.abort(): close() must not wait for non-abortable\n // b2_start_large_file. If a file ID arrives later, cancel it in the\n // background.\n scheduleCancelLargeFileAfterStart(startPromise)\n abortScope.dispose()\n rejectDone(observedError)\n throw observedError\n }\n await Promise.allSettled(inflight)\n // Capture into a const so the cancel closure sees a non-null\n // `fileId`; closures don't observe the outer `!== null` narrowing\n // because the variable is mutable across the lambda boundary.\n let finalError: unknown = observedError\n if (fileIdToCancel !== null) {\n finalError = await resolveLargeFileErrorAfterCleanup(observedError, raw, accountInfo, {\n fileId: fileIdToCancel,\n bucketId: options.bucketId,\n fileName: options.fileName,\n signal: options.signal,\n onCleanupFailure: options.onCleanupFailure,\n })\n }\n abortScope.dispose()\n rejectDone(finalError)\n throw finalError\n }\n },\n\n async abort(reason: unknown): Promise<void> {\n const abortError = markErrored(reason)\n pending.length = 0\n pendingBytes = 0\n const fileIdToCancel = largeFileId\n if (fileIdToCancel === null && startPromise !== null) {\n // Do not await an in-flight start request here. A stalled\n // b2_start_large_file must not make writer.abort() hang; if it later\n // returns a file ID, cancel it best-effort in the background.\n scheduleCancelLargeFileAfterStart(startPromise)\n }\n if (fileIdToCancel !== null) {\n await waitForInflightPartsToSettle()\n await cancelLargeFileBestEffort(\n raw,\n accountInfo,\n fileIdToCancel,\n cleanupWriteStreamOptions(options),\n )\n }\n abortScope.dispose()\n rejectDone(abortError)\n },\n })\n\n return { writable, done }\n}\n\nfunction cleanupWriteStreamOptions(options: CreateWriteStreamOptions): {\n readonly signal?: AbortSignal\n} & CleanupFailureOptions {\n return {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.onCleanupFailure !== undefined\n ? { onCleanupFailure: options.onCleanupFailure }\n : {}),\n }\n}\n\n/**\n * Removes exactly `size` bytes from the front of `chunks` (mutates) and returns\n * them as a contiguous Uint8Array. Any trailing remainder of the last chunk\n * stays at the front of `chunks` for the next part.\n *\n * @param chunks - Queue of pending chunks. Modified in place.\n * @param size - Number of bytes to carve off the front.\n *\n * @returns A new Uint8Array containing exactly `size` bytes.\n */\nfunction carveExact(chunks: Uint8Array[], size: number): Uint8Array {\n const out = new Uint8Array(size)\n let written = 0\n while (written < size && chunks.length > 0) {\n const head = chunks[0]\n if (!head) break\n const need = size - written\n if (head.byteLength <= need) {\n out.set(head, written)\n written += head.byteLength\n chunks.shift()\n } else {\n out.set(head.subarray(0, need), written)\n chunks[0] = head.subarray(need)\n written += need\n }\n }\n return out\n}\n\nfunction waitForAbort(signal: AbortSignal): {\n readonly promise: Promise<unknown>\n dispose(): void\n} {\n if (signal.aborted) {\n return {\n promise: Promise.resolve(abortReason(signal)),\n dispose() {},\n }\n }\n\n let onAbort: (() => void) | undefined\n const promise = new Promise<unknown>((resolve) => {\n onAbort = () => resolve(abortReason(signal))\n signal.addEventListener('abort', onAbort, { once: true })\n })\n\n return {\n promise,\n dispose() {\n if (onAbort !== undefined) signal.removeEventListener('abort', onAbort)\n },\n }\n}\n"],"mappings":"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"}
|
package/dist/upload/stream.d.cts
CHANGED
|
@@ -18,6 +18,12 @@ export interface CreateWriteStreamOptions extends UploadRetryOptions, CleanupFai
|
|
|
18
18
|
readonly fileInfo?: Record<string, string>;
|
|
19
19
|
/** Server-side encryption applied to each part. */
|
|
20
20
|
readonly serverSideEncryption?: EncryptionSetting;
|
|
21
|
+
/**
|
|
22
|
+
* B2 upload timestamp override in milliseconds since epoch.
|
|
23
|
+
* Sets the file version `uploadTimestamp`; distinct from `src_last_modified_millis` metadata.
|
|
24
|
+
* Requires B2 account enablement for Custom Upload Timestamp.
|
|
25
|
+
*/
|
|
26
|
+
readonly customUploadTimestamp?: number;
|
|
21
27
|
/**
|
|
22
28
|
* Target part size in bytes. The stream buffers writes until this many bytes
|
|
23
29
|
* are accumulated, then ships a part. Must be at least the account's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/upload/stream.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAE9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/upload/stream.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAE9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,iBAAiB,CAAA;AAK5D,OAAO,EACL,KAAK,qBAAqB,EAI3B,MAAM,aAAa,CAAA;AAGpB,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAA;AAEnB,8DAA8D;AAC9D,MAAM,WAAW,wBAAyB,SAAQ,kBAAkB,EAAE,qBAAqB;IACzF,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,mDAAmD;IACnD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,iBAAiB,CAAA;IACjD;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAA;IACvC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,2FAA2F;IAC3F,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;IAC7C,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;CACpC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,wBAAwB,GAChC,iBAAiB,CA6VnB"}
|
package/dist/upload/stream.d.ts
CHANGED
|
@@ -18,6 +18,12 @@ export interface CreateWriteStreamOptions extends UploadRetryOptions, CleanupFai
|
|
|
18
18
|
readonly fileInfo?: Record<string, string>;
|
|
19
19
|
/** Server-side encryption applied to each part. */
|
|
20
20
|
readonly serverSideEncryption?: EncryptionSetting;
|
|
21
|
+
/**
|
|
22
|
+
* B2 upload timestamp override in milliseconds since epoch.
|
|
23
|
+
* Sets the file version `uploadTimestamp`; distinct from `src_last_modified_millis` metadata.
|
|
24
|
+
* Requires B2 account enablement for Custom Upload Timestamp.
|
|
25
|
+
*/
|
|
26
|
+
readonly customUploadTimestamp?: number;
|
|
21
27
|
/**
|
|
22
28
|
* Target part size in bytes. The stream buffers writes until this many bytes
|
|
23
29
|
* are accumulated, then ships a part. Must be at least the account's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/upload/stream.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAE9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/upload/stream.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAE9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,iBAAiB,CAAA;AAK5D,OAAO,EACL,KAAK,qBAAqB,EAI3B,MAAM,aAAa,CAAA;AAGpB,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAA;AAEnB,8DAA8D;AAC9D,MAAM,WAAW,wBAAyB,SAAQ,kBAAkB,EAAE,qBAAqB;IACzF,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,mDAAmD;IACnD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,iBAAiB,CAAA;IACjD;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAA;IACvC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,2FAA2F;IAC3F,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;IAC7C,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;CACpC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,wBAAwB,GAChC,iBAAiB,CA6VnB"}
|
package/dist/upload/stream.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createAbortScope } from "./abort-scope.js";
|
|
2
2
|
import { DEFAULT_CLEANUP_TIMEOUT_MS, cancelLargeFileBestEffort, resolveLargeFileErrorAfterCleanup } from "./cancel.js";
|
|
3
3
|
import { Semaphore } from "./concurrency.js";
|
|
4
|
+
import { abortReason, raceWithAbort } from "../util/abort.js";
|
|
4
5
|
import { finishLargeFileWithAbortReconciliation } from "./finish.js";
|
|
5
6
|
import "../util/defaults.js";
|
|
6
7
|
import { ProgressTracker } from "../streams/progress.js";
|
|
@@ -50,6 +51,7 @@ function createWriteStream(raw, accountInfo, options) {
|
|
|
50
51
|
fileName: options.fileName,
|
|
51
52
|
contentType: options.contentType ?? "b2/x-auto",
|
|
52
53
|
fileInfo: options.fileInfo ?? {},
|
|
54
|
+
...options.customUploadTimestamp !== void 0 ? { customUploadTimestamp: String(options.customUploadTimestamp) } : {},
|
|
53
55
|
...options.serverSideEncryption !== void 0 ? { serverSideEncryption: options.serverSideEncryption } : {}
|
|
54
56
|
}, {
|
|
55
57
|
signal: abortScope.signal,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.js","names":[],"sources":["../../src/upload/stream.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { IncrementalSha1 } from '../streams/hash.ts'\nimport type { ProgressListener } from '../streams/progress.ts'\nimport { ProgressTracker } from '../streams/progress.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport type { BucketId, LargeFileId } from '../types/ids.ts'\nimport { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { toError } from '../util/to-error.ts'\nimport { abortReason, createAbortScope, raceWithAbort } from './abort-scope.ts'\nimport {\n type CleanupFailureOptions,\n cancelLargeFileBestEffort,\n DEFAULT_CLEANUP_TIMEOUT_MS,\n resolveLargeFileErrorAfterCleanup,\n} from './cancel.ts'\nimport { Semaphore } from './concurrency.ts'\nimport { finishLargeFileWithAbortReconciliation } from './finish.ts'\nimport {\n resolveRetryResponseBodyFailures,\n type UploadRetryOptions,\n uploadPartWithFreshUrl,\n} from './retry.ts'\n\n/** Options for creating a streaming multipart upload sink. */\nexport interface CreateWriteStreamOptions extends UploadRetryOptions, CleanupFailureOptions {\n /** Target bucket for the upload. */\n readonly bucketId: BucketId\n /** Destination file name in the bucket. */\n readonly fileName: string\n /** MIME type. Defaults to `b2/x-auto`. */\n readonly contentType?: string\n /** Custom file info stored with the file. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption applied to each part. */\n readonly serverSideEncryption?: EncryptionSetting\n /**\n * Target part size in bytes. The stream buffers writes until this many bytes\n * are accumulated, then ships a part. Must be at least the account's\n * absolute minimum part size; the implementation will raise it if too small.\n */\n readonly partSize?: number\n /** Maximum number of parts uploaded in parallel. Defaults to 4. */\n readonly concurrency?: number\n /** Callback invoked with upload progress events. `totalBytes` is `null` (size unknown). */\n readonly onProgress?: ProgressListener\n /** Aborts the upload and cancels the unfinished large file. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Handle returned by `B2Object.createWriteStream`: the Web `WritableStream` to\n * pipe data into, plus a promise that resolves with the finished\n * {@link FileVersion} once the stream is closed and all parts have been\n * uploaded.\n */\nexport interface UploadWriteHandle {\n /** Web `WritableStream` sink to pipe data into. */\n readonly writable: WritableStream<Uint8Array>\n /** Resolves with the finalized file version when the stream closes successfully. */\n readonly done: Promise<FileVersion>\n}\n\n/**\n * Creates a {@link WritableStream} that streams data into a B2 multipart upload.\n *\n * Buffers incoming chunks until `partSize` bytes are accumulated, ships each\n * complete part through the standard multipart engine, and finalizes the file\n * once the stream is closed. Honours backpressure via the queue's bounded\n * concurrency. Streaming uploads do not support resume because the total size\n * and per-part hashes aren't known in advance; use {@link uploadLargeFile} with\n * a buffered source when resume is required.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (tokens, URLs, part URL pool).\n * @param options - Streaming upload parameters.\n *\n * @returns A {@link UploadWriteHandle} with the writable sink and a completion promise.\n */\nexport function createWriteStream(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: CreateWriteStreamOptions,\n): UploadWriteHandle {\n const minPartSize = accountInfo.getAbsoluteMinimumPartSize()\n const recommendedPartSize = accountInfo.getRecommendedPartSize()\n const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize)\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n const tracker = new ProgressTracker(options.onProgress, null, null)\n const sem = new Semaphore(concurrency)\n const abortScope = createAbortScope(options.signal)\n\n let largeFileId: LargeFileId | null = null\n let startPromise: Promise<LargeFileId> | null = null\n let cancelAfterStartScheduled = false\n let nextPartNumber = 1\n let pendingBytes = 0\n const pending: Uint8Array[] = []\n const partSha1s: string[] = []\n const inflight: Promise<void>[] = []\n let errored: Error | null = null\n\n // ES2024 `Promise.withResolvers()` exposes the resolve/reject pair as\n // properties of the returned object, avoiding the `let resolve!:` /\n // `let reject!:` non-null-assertion pattern needed before this API\n // existed. Available in Node 22 (our minimum), Bun, Deno, modern\n // browsers, and Cloudflare Workers.\n const {\n promise: done,\n resolve: resolveDone,\n reject: rejectDone,\n } = Promise.withResolvers<FileVersion>()\n // Attach a no-op rejection handler so a `done` that rejects before the\n // caller observes it never surfaces as a process-level unhandled rejection.\n // The error is not swallowed: `close()`/`abort()` also reject the writable\n // (so `pipeTo`/`getWriter().close()` throw), and any later `await done` /\n // `done.then(onRejected)` the caller attaches still sees the rejection —\n // extra handlers on the same promise all fire. Mirrors the\n // `task.catch(() => {})` pattern used for inflight part uploads below, and\n // keeps the engine well-behaved under Bun and Node's strict\n // unhandled-rejection mode.\n done.catch(() => {})\n\n function ensureStarted(): Promise<LargeFileId> {\n if (largeFileId !== null) return Promise.resolve(largeFileId)\n if (startPromise === null) {\n const rawStart = raw.startLargeFile(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n bucketId: options.bucketId,\n fileName: options.fileName,\n contentType: options.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n },\n {\n signal: abortScope.signal,\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n )\n startPromise = rawStart.then((resp) => {\n largeFileId = resp.fileId\n if (abortScope.signal.aborted || errored !== null) {\n cancelStartedLargeFile(largeFileId)\n }\n return largeFileId\n })\n }\n const started = startPromise\n return raceWithAbort(started, abortScope.signal).catch((err) => {\n if (abortScope.signal.aborted) scheduleCancelLargeFileAfterStart(started)\n throw err\n })\n }\n\n async function shipPart(data: Uint8Array, partNumber: number): Promise<void> {\n if (errored !== null) throw errored\n abortScope.signal.throwIfAborted()\n const fileId = await ensureStarted()\n if (errored !== null) throw errored\n abortScope.signal.throwIfAborted()\n\n const sha1 = new IncrementalSha1()\n await sha1.update(data)\n const sha1Hex = await sha1.digest()\n abortScope.signal.throwIfAborted()\n\n const result = await uploadPartWithFreshUrl(raw, accountInfo, fileId, {\n fileName: options.fileName,\n partNumber,\n data: data as BodyInit,\n contentLength: data.byteLength,\n contentSha1: sha1Hex,\n retry: options.retry,\n signal: abortScope.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: resolveRetryResponseBodyFailures(\n options.retryResponseBodyFailures,\n ),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n })\n partSha1s[partNumber - 1] = result.contentSha1\n tracker.addBytes(data.byteLength)\n tracker.completePart()\n }\n\n function markErrored(err: unknown): Error {\n const error = toError(err)\n errored = error\n abortScope.abort(error)\n return error\n }\n\n function cancelStartedLargeFile(fileId: LargeFileId): void {\n if (cancelAfterStartScheduled) return\n cancelAfterStartScheduled = true\n void cancelLargeFileBestEffort(raw, accountInfo, fileId, cleanupWriteStreamOptions(options))\n }\n\n function scheduleCancelLargeFileAfterStart(started: Promise<LargeFileId>): void {\n void started\n .then((fileId) => cancelStartedLargeFile(fileId))\n .catch(() => {\n // If start failed, no file ID is available to cancel.\n })\n }\n\n async function settleInflightForClose(): Promise<PromiseSettledResult<void>[]> {\n const settled = Promise.allSettled(inflight)\n if (startPromise === null || largeFileId !== null) return await settled\n\n const abortWaiter = waitForAbort(abortScope.signal)\n try {\n const first = await Promise.race([\n settled.then(() => 'settled' as const),\n startPromise.then(\n () => 'start-settled' as const,\n () => 'start-settled' as const,\n ),\n abortWaiter.promise.then(() => 'aborted' as const),\n ])\n if (first === 'aborted' && largeFileId === null) {\n throw abortReason(abortScope.signal)\n }\n return await settled\n } finally {\n abortWaiter.dispose()\n }\n }\n\n function startPartWithAcquiredSlot(data: Uint8Array, partNumber: number): void {\n const task = (async () => {\n try {\n await shipPart(data, partNumber)\n } catch (err) {\n markErrored(err)\n throw err\n } finally {\n sem.release()\n }\n })()\n inflight.push(task)\n // Swallow rejection here; we check `errored` later.\n task.catch(() => {})\n }\n\n async function acquirePartSlot(): Promise<void> {\n await sem.acquire()\n if (errored !== null) {\n sem.release()\n throw errored\n }\n try {\n abortScope.signal.throwIfAborted()\n } catch (err) {\n sem.release()\n throw err\n }\n }\n\n async function waitForInflightPartsToSettle(\n timeoutMs = DEFAULT_CLEANUP_TIMEOUT_MS,\n ): Promise<void> {\n if (inflight.length === 0) return\n let timeout: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n Promise.allSettled(inflight).then(() => undefined),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs)\n }),\n ])\n } finally {\n if (timeout !== undefined) clearTimeout(timeout)\n }\n }\n\n async function dispatchPart(): Promise<void> {\n if (pending.length === 0) return\n await acquirePartSlot()\n let data: Uint8Array\n if (pending.length === 1) {\n const head = pending[0]\n if (!head) {\n sem.release()\n return\n }\n data = head\n } else {\n const total = pending.reduce((sum, chunk) => sum + chunk.byteLength, 0)\n data = new Uint8Array(total)\n let offset = 0\n for (const chunk of pending) {\n data.set(chunk, offset)\n offset += chunk.byteLength\n }\n }\n pending.length = 0\n pendingBytes = 0\n const partNumber = nextPartNumber++\n startPartWithAcquiredSlot(data, partNumber)\n }\n\n const writable = new WritableStream<Uint8Array>({\n async write(chunk: Uint8Array): Promise<void> {\n if (errored) throw errored\n abortScope.signal.throwIfAborted()\n if (chunk.byteLength === 0) return\n\n pending.push(chunk)\n pendingBytes += chunk.byteLength\n while (pendingBytes >= partSize) {\n await acquirePartSlot()\n if (errored) {\n sem.release()\n throw errored\n }\n // Pull exactly partSize bytes off the front\n const carved = carveExact(pending, partSize)\n const partNumber = nextPartNumber++\n pendingBytes -= partSize\n startPartWithAcquiredSlot(carved, partNumber)\n }\n },\n\n async close(): Promise<void> {\n try {\n if (errored) throw errored\n abortScope.signal.throwIfAborted()\n\n // Ship any remaining bytes as the last part. If the total fit in one\n // single buffered batch (no parts shipped yet), we have to use the\n // multipart path anyway since we already called startLargeFile lazily.\n if (pendingBytes > 0) {\n await dispatchPart()\n }\n\n const settled = await settleInflightForClose()\n const rejected = settled.find(\n (result): result is PromiseRejectedResult => result.status === 'rejected',\n )\n if (rejected !== undefined && errored === null) {\n markErrored(rejected.reason)\n }\n if (errored) throw errored\n\n if (largeFileId === null) {\n // Stream closed before any data was written. Start an empty large\n // file? B2 requires at least 2 parts. Reject — callers shouldn't\n // close an empty stream.\n throw new Error('createWriteStream closed without any data written.')\n }\n\n const result = await finishLargeFileWithAbortReconciliation(raw, accountInfo, {\n fileId: largeFileId,\n bucketId: options.bucketId,\n fileName: options.fileName,\n partSha1s,\n signal: abortScope.signal,\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n })\n abortScope.dispose()\n resolveDone(result)\n } catch (err) {\n const closeError = toError(err)\n if (errored === null) errored = closeError\n abortScope.abort(errored)\n const observedError = errored\n const fileIdToCancel = largeFileId\n if (fileIdToCancel === null && startPromise !== null) {\n // Mirror writer.abort(): close() must not wait for non-abortable\n // b2_start_large_file. If a file ID arrives later, cancel it in the\n // background.\n scheduleCancelLargeFileAfterStart(startPromise)\n abortScope.dispose()\n rejectDone(observedError)\n throw observedError\n }\n await Promise.allSettled(inflight)\n // Capture into a const so the cancel closure sees a non-null\n // `fileId`; closures don't observe the outer `!== null` narrowing\n // because the variable is mutable across the lambda boundary.\n let finalError: unknown = observedError\n if (fileIdToCancel !== null) {\n finalError = await resolveLargeFileErrorAfterCleanup(observedError, raw, accountInfo, {\n fileId: fileIdToCancel,\n bucketId: options.bucketId,\n fileName: options.fileName,\n signal: options.signal,\n onCleanupFailure: options.onCleanupFailure,\n })\n }\n abortScope.dispose()\n rejectDone(finalError)\n throw finalError\n }\n },\n\n async abort(reason: unknown): Promise<void> {\n const abortError = markErrored(reason)\n pending.length = 0\n pendingBytes = 0\n const fileIdToCancel = largeFileId\n if (fileIdToCancel === null && startPromise !== null) {\n // Do not await an in-flight start request here. A stalled\n // b2_start_large_file must not make writer.abort() hang; if it later\n // returns a file ID, cancel it best-effort in the background.\n scheduleCancelLargeFileAfterStart(startPromise)\n }\n if (fileIdToCancel !== null) {\n await waitForInflightPartsToSettle()\n await cancelLargeFileBestEffort(\n raw,\n accountInfo,\n fileIdToCancel,\n cleanupWriteStreamOptions(options),\n )\n }\n abortScope.dispose()\n rejectDone(abortError)\n },\n })\n\n return { writable, done }\n}\n\nfunction cleanupWriteStreamOptions(options: CreateWriteStreamOptions): {\n readonly signal?: AbortSignal\n} & CleanupFailureOptions {\n return {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.onCleanupFailure !== undefined\n ? { onCleanupFailure: options.onCleanupFailure }\n : {}),\n }\n}\n\n/**\n * Removes exactly `size` bytes from the front of `chunks` (mutates) and returns\n * them as a contiguous Uint8Array. Any trailing remainder of the last chunk\n * stays at the front of `chunks` for the next part.\n *\n * @param chunks - Queue of pending chunks. Modified in place.\n * @param size - Number of bytes to carve off the front.\n *\n * @returns A new Uint8Array containing exactly `size` bytes.\n */\nfunction carveExact(chunks: Uint8Array[], size: number): Uint8Array {\n const out = new Uint8Array(size)\n let written = 0\n while (written < size && chunks.length > 0) {\n const head = chunks[0]\n if (!head) break\n const need = size - written\n if (head.byteLength <= need) {\n out.set(head, written)\n written += head.byteLength\n chunks.shift()\n } else {\n out.set(head.subarray(0, need), written)\n chunks[0] = head.subarray(need)\n written += need\n }\n }\n return out\n}\n\nfunction waitForAbort(signal: AbortSignal): {\n readonly promise: Promise<unknown>\n dispose(): void\n} {\n if (signal.aborted) {\n return {\n promise: Promise.resolve(abortReason(signal)),\n dispose() {},\n }\n }\n\n let onAbort: (() => void) | undefined\n const promise = new Promise<unknown>((resolve) => {\n onAbort = () => resolve(abortReason(signal))\n signal.addEventListener('abort', onAbort, { once: true })\n })\n\n return {\n promise,\n dispose() {\n if (onAbort !== undefined) signal.removeEventListener('abort', onAbort)\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stream.js","names":[],"sources":["../../src/upload/stream.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { IncrementalSha1 } from '../streams/hash.ts'\nimport type { ProgressListener } from '../streams/progress.ts'\nimport { ProgressTracker } from '../streams/progress.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport type { BucketId, LargeFileId } from '../types/ids.ts'\nimport { abortReason, raceWithAbort } from '../util/abort.ts'\nimport { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { toError } from '../util/to-error.ts'\nimport { createAbortScope } from './abort-scope.ts'\nimport {\n type CleanupFailureOptions,\n cancelLargeFileBestEffort,\n DEFAULT_CLEANUP_TIMEOUT_MS,\n resolveLargeFileErrorAfterCleanup,\n} from './cancel.ts'\nimport { Semaphore } from './concurrency.ts'\nimport { finishLargeFileWithAbortReconciliation } from './finish.ts'\nimport {\n resolveRetryResponseBodyFailures,\n type UploadRetryOptions,\n uploadPartWithFreshUrl,\n} from './retry.ts'\n\n/** Options for creating a streaming multipart upload sink. */\nexport interface CreateWriteStreamOptions extends UploadRetryOptions, CleanupFailureOptions {\n /** Target bucket for the upload. */\n readonly bucketId: BucketId\n /** Destination file name in the bucket. */\n readonly fileName: string\n /** MIME type. Defaults to `b2/x-auto`. */\n readonly contentType?: string\n /** Custom file info stored with the file. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption applied to each part. */\n readonly serverSideEncryption?: EncryptionSetting\n /**\n * B2 upload timestamp override in milliseconds since epoch.\n * Sets the file version `uploadTimestamp`; distinct from `src_last_modified_millis` metadata.\n * Requires B2 account enablement for Custom Upload Timestamp.\n */\n readonly customUploadTimestamp?: number\n /**\n * Target part size in bytes. The stream buffers writes until this many bytes\n * are accumulated, then ships a part. Must be at least the account's\n * absolute minimum part size; the implementation will raise it if too small.\n */\n readonly partSize?: number\n /** Maximum number of parts uploaded in parallel. Defaults to 4. */\n readonly concurrency?: number\n /** Callback invoked with upload progress events. `totalBytes` is `null` (size unknown). */\n readonly onProgress?: ProgressListener\n /** Aborts the upload and cancels the unfinished large file. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Handle returned by `B2Object.createWriteStream`: the Web `WritableStream` to\n * pipe data into, plus a promise that resolves with the finished\n * {@link FileVersion} once the stream is closed and all parts have been\n * uploaded.\n */\nexport interface UploadWriteHandle {\n /** Web `WritableStream` sink to pipe data into. */\n readonly writable: WritableStream<Uint8Array>\n /** Resolves with the finalized file version when the stream closes successfully. */\n readonly done: Promise<FileVersion>\n}\n\n/**\n * Creates a {@link WritableStream} that streams data into a B2 multipart upload.\n *\n * Buffers incoming chunks until `partSize` bytes are accumulated, ships each\n * complete part through the standard multipart engine, and finalizes the file\n * once the stream is closed. Honours backpressure via the queue's bounded\n * concurrency. Streaming uploads do not support resume because the total size\n * and per-part hashes aren't known in advance; use {@link uploadLargeFile} with\n * a buffered source when resume is required.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (tokens, URLs, part URL pool).\n * @param options - Streaming upload parameters.\n *\n * @returns A {@link UploadWriteHandle} with the writable sink and a completion promise.\n */\nexport function createWriteStream(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: CreateWriteStreamOptions,\n): UploadWriteHandle {\n const minPartSize = accountInfo.getAbsoluteMinimumPartSize()\n const recommendedPartSize = accountInfo.getRecommendedPartSize()\n const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize)\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n const tracker = new ProgressTracker(options.onProgress, null, null)\n const sem = new Semaphore(concurrency)\n const abortScope = createAbortScope(options.signal)\n\n let largeFileId: LargeFileId | null = null\n let startPromise: Promise<LargeFileId> | null = null\n let cancelAfterStartScheduled = false\n let nextPartNumber = 1\n let pendingBytes = 0\n const pending: Uint8Array[] = []\n const partSha1s: string[] = []\n const inflight: Promise<void>[] = []\n let errored: Error | null = null\n\n // ES2024 `Promise.withResolvers()` exposes the resolve/reject pair as\n // properties of the returned object, avoiding the `let resolve!:` /\n // `let reject!:` non-null-assertion pattern needed before this API\n // existed. Available in Node 22 (our minimum), Bun, Deno, modern\n // browsers, and Cloudflare Workers.\n const {\n promise: done,\n resolve: resolveDone,\n reject: rejectDone,\n } = Promise.withResolvers<FileVersion>()\n // Attach a no-op rejection handler so a `done` that rejects before the\n // caller observes it never surfaces as a process-level unhandled rejection.\n // The error is not swallowed: `close()`/`abort()` also reject the writable\n // (so `pipeTo`/`getWriter().close()` throw), and any later `await done` /\n // `done.then(onRejected)` the caller attaches still sees the rejection —\n // extra handlers on the same promise all fire. Mirrors the\n // `task.catch(() => {})` pattern used for inflight part uploads below, and\n // keeps the engine well-behaved under Bun and Node's strict\n // unhandled-rejection mode.\n done.catch(() => {})\n\n function ensureStarted(): Promise<LargeFileId> {\n if (largeFileId !== null) return Promise.resolve(largeFileId)\n if (startPromise === null) {\n const rawStart = raw.startLargeFile(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n bucketId: options.bucketId,\n fileName: options.fileName,\n contentType: options.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n ...(options.customUploadTimestamp !== undefined\n ? { customUploadTimestamp: String(options.customUploadTimestamp) }\n : {}),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n },\n {\n signal: abortScope.signal,\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n },\n )\n startPromise = rawStart.then((resp) => {\n largeFileId = resp.fileId\n if (abortScope.signal.aborted || errored !== null) {\n cancelStartedLargeFile(largeFileId)\n }\n return largeFileId\n })\n }\n const started = startPromise\n return raceWithAbort(started, abortScope.signal).catch((err) => {\n if (abortScope.signal.aborted) scheduleCancelLargeFileAfterStart(started)\n throw err\n })\n }\n\n async function shipPart(data: Uint8Array, partNumber: number): Promise<void> {\n if (errored !== null) throw errored\n abortScope.signal.throwIfAborted()\n const fileId = await ensureStarted()\n if (errored !== null) throw errored\n abortScope.signal.throwIfAborted()\n\n const sha1 = new IncrementalSha1()\n await sha1.update(data)\n const sha1Hex = await sha1.digest()\n abortScope.signal.throwIfAborted()\n\n const result = await uploadPartWithFreshUrl(raw, accountInfo, fileId, {\n fileName: options.fileName,\n partNumber,\n data: data as BodyInit,\n contentLength: data.byteLength,\n contentSha1: sha1Hex,\n retry: options.retry,\n signal: abortScope.signal,\n onUploadRetry: options.onUploadRetry,\n retryResponseBodyFailures: resolveRetryResponseBodyFailures(\n options.retryResponseBodyFailures,\n ),\n ...(options.serverSideEncryption !== undefined\n ? { serverSideEncryption: options.serverSideEncryption }\n : {}),\n })\n partSha1s[partNumber - 1] = result.contentSha1\n tracker.addBytes(data.byteLength)\n tracker.completePart()\n }\n\n function markErrored(err: unknown): Error {\n const error = toError(err)\n errored = error\n abortScope.abort(error)\n return error\n }\n\n function cancelStartedLargeFile(fileId: LargeFileId): void {\n if (cancelAfterStartScheduled) return\n cancelAfterStartScheduled = true\n void cancelLargeFileBestEffort(raw, accountInfo, fileId, cleanupWriteStreamOptions(options))\n }\n\n function scheduleCancelLargeFileAfterStart(started: Promise<LargeFileId>): void {\n void started\n .then((fileId) => cancelStartedLargeFile(fileId))\n .catch(() => {\n // If start failed, no file ID is available to cancel.\n })\n }\n\n async function settleInflightForClose(): Promise<PromiseSettledResult<void>[]> {\n const settled = Promise.allSettled(inflight)\n if (startPromise === null || largeFileId !== null) return await settled\n\n const abortWaiter = waitForAbort(abortScope.signal)\n try {\n const first = await Promise.race([\n settled.then(() => 'settled' as const),\n startPromise.then(\n () => 'start-settled' as const,\n () => 'start-settled' as const,\n ),\n abortWaiter.promise.then(() => 'aborted' as const),\n ])\n if (first === 'aborted' && largeFileId === null) {\n throw abortReason(abortScope.signal)\n }\n return await settled\n } finally {\n abortWaiter.dispose()\n }\n }\n\n function startPartWithAcquiredSlot(data: Uint8Array, partNumber: number): void {\n const task = (async () => {\n try {\n await shipPart(data, partNumber)\n } catch (err) {\n markErrored(err)\n throw err\n } finally {\n sem.release()\n }\n })()\n inflight.push(task)\n // Swallow rejection here; we check `errored` later.\n task.catch(() => {})\n }\n\n async function acquirePartSlot(): Promise<void> {\n await sem.acquire()\n if (errored !== null) {\n sem.release()\n throw errored\n }\n try {\n abortScope.signal.throwIfAborted()\n } catch (err) {\n sem.release()\n throw err\n }\n }\n\n async function waitForInflightPartsToSettle(\n timeoutMs = DEFAULT_CLEANUP_TIMEOUT_MS,\n ): Promise<void> {\n if (inflight.length === 0) return\n let timeout: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n Promise.allSettled(inflight).then(() => undefined),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs)\n }),\n ])\n } finally {\n if (timeout !== undefined) clearTimeout(timeout)\n }\n }\n\n async function dispatchPart(): Promise<void> {\n if (pending.length === 0) return\n await acquirePartSlot()\n let data: Uint8Array\n if (pending.length === 1) {\n const head = pending[0]\n if (!head) {\n sem.release()\n return\n }\n data = head\n } else {\n const total = pending.reduce((sum, chunk) => sum + chunk.byteLength, 0)\n data = new Uint8Array(total)\n let offset = 0\n for (const chunk of pending) {\n data.set(chunk, offset)\n offset += chunk.byteLength\n }\n }\n pending.length = 0\n pendingBytes = 0\n const partNumber = nextPartNumber++\n startPartWithAcquiredSlot(data, partNumber)\n }\n\n const writable = new WritableStream<Uint8Array>({\n async write(chunk: Uint8Array): Promise<void> {\n if (errored) throw errored\n abortScope.signal.throwIfAborted()\n if (chunk.byteLength === 0) return\n\n pending.push(chunk)\n pendingBytes += chunk.byteLength\n while (pendingBytes >= partSize) {\n await acquirePartSlot()\n if (errored) {\n sem.release()\n throw errored\n }\n // Pull exactly partSize bytes off the front\n const carved = carveExact(pending, partSize)\n const partNumber = nextPartNumber++\n pendingBytes -= partSize\n startPartWithAcquiredSlot(carved, partNumber)\n }\n },\n\n async close(): Promise<void> {\n try {\n if (errored) throw errored\n abortScope.signal.throwIfAborted()\n\n // Ship any remaining bytes as the last part. If the total fit in one\n // single buffered batch (no parts shipped yet), we have to use the\n // multipart path anyway since we already called startLargeFile lazily.\n if (pendingBytes > 0) {\n await dispatchPart()\n }\n\n const settled = await settleInflightForClose()\n const rejected = settled.find(\n (result): result is PromiseRejectedResult => result.status === 'rejected',\n )\n if (rejected !== undefined && errored === null) {\n markErrored(rejected.reason)\n }\n if (errored) throw errored\n\n if (largeFileId === null) {\n // Stream closed before any data was written. Start an empty large\n // file? B2 requires at least 2 parts. Reject — callers shouldn't\n // close an empty stream.\n throw new Error('createWriteStream closed without any data written.')\n }\n\n const result = await finishLargeFileWithAbortReconciliation(raw, accountInfo, {\n fileId: largeFileId,\n bucketId: options.bucketId,\n fileName: options.fileName,\n partSha1s,\n signal: abortScope.signal,\n ...(options.retry !== undefined ? { retry: options.retry } : {}),\n })\n abortScope.dispose()\n resolveDone(result)\n } catch (err) {\n const closeError = toError(err)\n if (errored === null) errored = closeError\n abortScope.abort(errored)\n const observedError = errored\n const fileIdToCancel = largeFileId\n if (fileIdToCancel === null && startPromise !== null) {\n // Mirror writer.abort(): close() must not wait for non-abortable\n // b2_start_large_file. If a file ID arrives later, cancel it in the\n // background.\n scheduleCancelLargeFileAfterStart(startPromise)\n abortScope.dispose()\n rejectDone(observedError)\n throw observedError\n }\n await Promise.allSettled(inflight)\n // Capture into a const so the cancel closure sees a non-null\n // `fileId`; closures don't observe the outer `!== null` narrowing\n // because the variable is mutable across the lambda boundary.\n let finalError: unknown = observedError\n if (fileIdToCancel !== null) {\n finalError = await resolveLargeFileErrorAfterCleanup(observedError, raw, accountInfo, {\n fileId: fileIdToCancel,\n bucketId: options.bucketId,\n fileName: options.fileName,\n signal: options.signal,\n onCleanupFailure: options.onCleanupFailure,\n })\n }\n abortScope.dispose()\n rejectDone(finalError)\n throw finalError\n }\n },\n\n async abort(reason: unknown): Promise<void> {\n const abortError = markErrored(reason)\n pending.length = 0\n pendingBytes = 0\n const fileIdToCancel = largeFileId\n if (fileIdToCancel === null && startPromise !== null) {\n // Do not await an in-flight start request here. A stalled\n // b2_start_large_file must not make writer.abort() hang; if it later\n // returns a file ID, cancel it best-effort in the background.\n scheduleCancelLargeFileAfterStart(startPromise)\n }\n if (fileIdToCancel !== null) {\n await waitForInflightPartsToSettle()\n await cancelLargeFileBestEffort(\n raw,\n accountInfo,\n fileIdToCancel,\n cleanupWriteStreamOptions(options),\n )\n }\n abortScope.dispose()\n rejectDone(abortError)\n },\n })\n\n return { writable, done }\n}\n\nfunction cleanupWriteStreamOptions(options: CreateWriteStreamOptions): {\n readonly signal?: AbortSignal\n} & CleanupFailureOptions {\n return {\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.onCleanupFailure !== undefined\n ? { onCleanupFailure: options.onCleanupFailure }\n : {}),\n }\n}\n\n/**\n * Removes exactly `size` bytes from the front of `chunks` (mutates) and returns\n * them as a contiguous Uint8Array. Any trailing remainder of the last chunk\n * stays at the front of `chunks` for the next part.\n *\n * @param chunks - Queue of pending chunks. Modified in place.\n * @param size - Number of bytes to carve off the front.\n *\n * @returns A new Uint8Array containing exactly `size` bytes.\n */\nfunction carveExact(chunks: Uint8Array[], size: number): Uint8Array {\n const out = new Uint8Array(size)\n let written = 0\n while (written < size && chunks.length > 0) {\n const head = chunks[0]\n if (!head) break\n const need = size - written\n if (head.byteLength <= need) {\n out.set(head, written)\n written += head.byteLength\n chunks.shift()\n } else {\n out.set(head.subarray(0, need), written)\n chunks[0] = head.subarray(need)\n written += need\n }\n }\n return out\n}\n\nfunction waitForAbort(signal: AbortSignal): {\n readonly promise: Promise<unknown>\n dispose(): void\n} {\n if (signal.aborted) {\n return {\n promise: Promise.resolve(abortReason(signal)),\n dispose() {},\n }\n }\n\n let onAbort: (() => void) | undefined\n const promise = new Promise<unknown>((resolve) => {\n onAbort = () => resolve(abortReason(signal))\n signal.addEventListener('abort', onAbort, { once: true })\n })\n\n return {\n promise,\n dispose() {\n if (onAbort !== undefined) signal.removeEventListener('abort', onAbort)\n },\n }\n}\n"],"mappings":"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"}
|