@backblaze-labs/b2-sdk 0.1.0 → 0.2.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 +110 -5
- package/README.md +144 -33
- package/dist/_virtual/_b2-sdk-version-json.cjs +4 -0
- package/dist/_virtual/_b2-sdk-version-json.js +4 -0
- package/dist/auth/account-info.d.cts +32 -0
- package/dist/auth/account-info.d.cts.map +1 -1
- package/dist/auth/account-info.d.ts +32 -0
- package/dist/auth/account-info.d.ts.map +1 -1
- package/dist/auth/file.cjs +408 -207
- package/dist/auth/file.cjs.map +1 -1
- package/dist/auth/file.d.cts +77 -3
- package/dist/auth/file.d.cts.map +1 -1
- package/dist/auth/file.d.ts +77 -3
- package/dist/auth/file.d.ts.map +1 -1
- package/dist/auth/file.js +407 -207
- package/dist/auth/file.js.map +1 -1
- package/dist/auth/in-memory.cjs +212 -183
- package/dist/auth/in-memory.cjs.map +1 -1
- package/dist/auth/in-memory.d.cts +8 -0
- package/dist/auth/in-memory.d.cts.map +1 -1
- package/dist/auth/in-memory.d.ts +8 -0
- package/dist/auth/in-memory.d.ts.map +1 -1
- package/dist/auth/in-memory.js +211 -182
- package/dist/auth/in-memory.js.map +1 -1
- package/dist/auth/index.cjs +7 -9
- package/dist/auth/index.d.cts +3 -2
- package/dist/auth/index.d.cts.map +1 -1
- package/dist/auth/index.d.ts +3 -2
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +2 -8
- package/dist/auth/realms.cjs +80 -7
- package/dist/auth/realms.cjs.map +1 -1
- package/dist/auth/realms.d.cts +27 -4
- package/dist/auth/realms.d.cts.map +1 -1
- package/dist/auth/realms.d.ts +27 -4
- package/dist/auth/realms.d.ts.map +1 -1
- package/dist/auth/realms.js +80 -9
- package/dist/auth/realms.js.map +1 -1
- package/dist/auth/upload-url-pool.cjs +55 -51
- package/dist/auth/upload-url-pool.cjs.map +1 -1
- package/dist/auth/upload-url-pool.js +55 -51
- package/dist/auth/upload-url-pool.js.map +1 -1
- package/dist/bucket.cjs +785 -787
- package/dist/bucket.cjs.map +1 -1
- package/dist/bucket.d.cts +35 -47
- package/dist/bucket.d.cts.map +1 -1
- package/dist/bucket.d.ts +35 -47
- package/dist/bucket.d.ts.map +1 -1
- package/dist/bucket.js +780 -782
- package/dist/bucket.js.map +1 -1
- package/dist/client.cjs +246 -228
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +25 -17
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.ts +25 -17
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +243 -225
- package/dist/client.js.map +1 -1
- package/dist/copy/large.cjs +114 -71
- package/dist/copy/large.cjs.map +1 -1
- package/dist/copy/large.d.cts +6 -0
- package/dist/copy/large.d.cts.map +1 -1
- package/dist/copy/large.d.ts +6 -0
- package/dist/copy/large.d.ts.map +1 -1
- package/dist/copy/large.js +112 -69
- package/dist/copy/large.js.map +1 -1
- package/dist/download/checksum.cjs +89 -0
- package/dist/download/checksum.cjs.map +1 -0
- package/dist/download/checksum.d.cts +40 -0
- package/dist/download/checksum.d.cts.map +1 -0
- package/dist/download/checksum.d.ts +40 -0
- package/dist/download/checksum.d.ts.map +1 -0
- package/dist/download/checksum.js +87 -0
- package/dist/download/checksum.js.map +1 -0
- package/dist/download/parallel.cjs +214 -118
- package/dist/download/parallel.cjs.map +1 -1
- package/dist/download/parallel.d.cts +5 -1
- package/dist/download/parallel.d.cts.map +1 -1
- package/dist/download/parallel.d.ts +5 -1
- package/dist/download/parallel.d.ts.map +1 -1
- package/dist/download/parallel.js +212 -116
- package/dist/download/parallel.js.map +1 -1
- package/dist/download/single.cjs +188 -97
- package/dist/download/single.cjs.map +1 -1
- package/dist/download/single.d.cts +6 -1
- package/dist/download/single.d.cts.map +1 -1
- package/dist/download/single.d.ts +6 -1
- package/dist/download/single.d.ts.map +1 -1
- package/dist/download/single.js +186 -98
- package/dist/download/single.js.map +1 -1
- package/dist/errors/index.cjs +699 -243
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +285 -82
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +285 -82
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +678 -261
- package/dist/errors/index.js.map +1 -1
- package/dist/http/retry.cjs +45 -28
- package/dist/http/retry.cjs.map +1 -1
- package/dist/http/retry.d.cts +15 -2
- package/dist/http/retry.d.cts.map +1 -1
- package/dist/http/retry.d.ts +15 -2
- package/dist/http/retry.d.ts.map +1 -1
- package/dist/http/retry.js +46 -31
- package/dist/http/retry.js.map +1 -1
- package/dist/http/transport.cjs +438 -140
- package/dist/http/transport.cjs.map +1 -1
- package/dist/http/transport.d.cts +21 -2
- package/dist/http/transport.d.cts.map +1 -1
- package/dist/http/transport.d.ts +21 -2
- package/dist/http/transport.d.ts.map +1 -1
- package/dist/http/transport.js +437 -140
- package/dist/http/transport.js.map +1 -1
- package/dist/http/url-guard.cjs +154 -89
- package/dist/http/url-guard.cjs.map +1 -1
- package/dist/http/url-guard.d.cts +25 -3
- package/dist/http/url-guard.d.cts.map +1 -1
- package/dist/http/url-guard.d.ts +25 -3
- package/dist/http/url-guard.d.ts.map +1 -1
- package/dist/http/url-guard.js +152 -90
- package/dist/http/url-guard.js.map +1 -1
- package/dist/http/user-agent.cjs +84 -44
- package/dist/http/user-agent.cjs.map +1 -1
- package/dist/http/user-agent.js +84 -46
- package/dist/http/user-agent.js.map +1 -1
- package/dist/index.cjs +101 -77
- package/dist/index.d.cts +27 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +27 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -71
- package/dist/internal/b2-naming.cjs +66 -0
- package/dist/internal/b2-naming.cjs.map +1 -0
- package/dist/internal/b2-naming.d.cts +43 -0
- package/dist/internal/b2-naming.d.cts.map +1 -0
- package/dist/internal/b2-naming.d.ts +43 -0
- package/dist/internal/b2-naming.d.ts.map +1 -0
- package/dist/internal/b2-naming.js +60 -0
- package/dist/internal/b2-naming.js.map +1 -0
- package/dist/internal/upload-retry-options.cjs +21 -0
- package/dist/internal/upload-retry-options.cjs.map +1 -0
- package/dist/internal/upload-retry-options.d.cts +12 -0
- package/dist/internal/upload-retry-options.d.cts.map +1 -0
- package/dist/internal/upload-retry-options.d.ts +12 -0
- package/dist/internal/upload-retry-options.d.ts.map +1 -0
- package/dist/internal/upload-retry-options.js +21 -0
- package/dist/internal/upload-retry-options.js.map +1 -0
- package/dist/internal/url-redaction.cjs +31 -0
- package/dist/internal/url-redaction.cjs.map +1 -0
- package/dist/internal/url-redaction.d.cts +17 -0
- package/dist/internal/url-redaction.d.cts.map +1 -0
- package/dist/internal/url-redaction.d.ts +17 -0
- package/dist/internal/url-redaction.d.ts.map +1 -0
- package/dist/internal/url-redaction.js +31 -0
- package/dist/internal/url-redaction.js.map +1 -0
- package/dist/notifications/index.cjs +4 -6
- package/dist/notifications/index.d.cts +1 -1
- package/dist/notifications/index.d.cts.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -6
- package/dist/notifications/webhook.cjs +195 -85
- package/dist/notifications/webhook.cjs.map +1 -1
- package/dist/notifications/webhook.d.cts.map +1 -1
- package/dist/notifications/webhook.d.ts.map +1 -1
- package/dist/notifications/webhook.js +195 -87
- package/dist/notifications/webhook.js.map +1 -1
- package/dist/object.cjs +253 -210
- package/dist/object.cjs.map +1 -1
- package/dist/object.d.cts +9 -55
- package/dist/object.d.cts.map +1 -1
- package/dist/object.d.ts +9 -55
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +249 -206
- package/dist/object.js.map +1 -1
- package/dist/raw/encoding.cjs +74 -35
- package/dist/raw/encoding.cjs.map +1 -1
- package/dist/raw/encoding.js +74 -38
- package/dist/raw/encoding.js.map +1 -1
- package/dist/raw/index.cjs +678 -587
- package/dist/raw/index.cjs.map +1 -1
- package/dist/raw/index.d.cts +121 -18
- package/dist/raw/index.d.cts.map +1 -1
- package/dist/raw/index.d.ts +121 -18
- package/dist/raw/index.d.ts.map +1 -1
- package/dist/raw/index.js +674 -589
- package/dist/raw/index.js.map +1 -1
- package/dist/s3/index.cjs +355 -18
- package/dist/s3/index.cjs.map +1 -1
- package/dist/s3/index.d.cts +233 -5
- package/dist/s3/index.d.cts.map +1 -1
- package/dist/s3/index.d.ts +233 -5
- package/dist/s3/index.d.ts.map +1 -1
- package/dist/s3/index.js +351 -22
- package/dist/s3/index.js.map +1 -1
- package/dist/s3/sigv4.cjs +149 -0
- package/dist/s3/sigv4.cjs.map +1 -0
- package/dist/s3/sigv4.d.cts +43 -0
- package/dist/s3/sigv4.d.cts.map +1 -0
- package/dist/s3/sigv4.d.ts +43 -0
- package/dist/s3/sigv4.d.ts.map +1 -0
- package/dist/s3/sigv4.js +148 -0
- package/dist/s3/sigv4.js.map +1 -0
- package/dist/s3/validation.cjs +69 -0
- package/dist/s3/validation.cjs.map +1 -0
- package/dist/s3/validation.d.cts +43 -0
- package/dist/s3/validation.d.cts.map +1 -0
- package/dist/s3/validation.d.ts +43 -0
- package/dist/s3/validation.d.ts.map +1 -0
- package/dist/s3/validation.js +66 -0
- package/dist/s3/validation.js.map +1 -0
- package/dist/simulator/capabilities.cjs +63 -48
- package/dist/simulator/capabilities.cjs.map +1 -1
- package/dist/simulator/capabilities.js +63 -49
- package/dist/simulator/capabilities.js.map +1 -1
- package/dist/simulator/index.cjs +2124 -1548
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.cts +79 -11
- package/dist/simulator/index.d.cts.map +1 -1
- package/dist/simulator/index.d.ts +79 -11
- package/dist/simulator/index.d.ts.map +1 -1
- package/dist/simulator/index.js +2112 -1546
- package/dist/simulator/index.js.map +1 -1
- package/dist/simulator/validation.cjs +191 -153
- package/dist/simulator/validation.cjs.map +1 -1
- package/dist/simulator/validation.d.cts +3 -21
- package/dist/simulator/validation.d.cts.map +1 -1
- package/dist/simulator/validation.d.ts +3 -21
- package/dist/simulator/validation.d.ts.map +1 -1
- package/dist/simulator/validation.js +188 -162
- package/dist/simulator/validation.js.map +1 -1
- package/dist/streams/collect.cjs +74 -24
- package/dist/streams/collect.cjs.map +1 -1
- package/dist/streams/collect.d.cts +14 -1
- package/dist/streams/collect.d.cts.map +1 -1
- package/dist/streams/collect.d.ts +14 -1
- package/dist/streams/collect.d.ts.map +1 -1
- package/dist/streams/collect.js +74 -25
- package/dist/streams/collect.js.map +1 -1
- package/dist/streams/file-source.cjs +287 -0
- package/dist/streams/file-source.cjs.map +1 -0
- package/dist/streams/file-source.d.cts +96 -0
- package/dist/streams/file-source.d.cts.map +1 -0
- package/dist/streams/file-source.d.ts +96 -0
- package/dist/streams/file-source.d.ts.map +1 -0
- package/dist/streams/file-source.js +286 -0
- package/dist/streams/file-source.js.map +1 -0
- package/dist/streams/hash.cjs +207 -115
- package/dist/streams/hash.cjs.map +1 -1
- package/dist/streams/hash.d.cts +5 -4
- package/dist/streams/hash.d.cts.map +1 -1
- package/dist/streams/hash.d.ts +5 -4
- package/dist/streams/hash.d.ts.map +1 -1
- package/dist/streams/hash.js +208 -95
- package/dist/streams/hash.js.map +1 -1
- package/dist/streams/index.cjs +12 -12
- package/dist/streams/index.d.cts +3 -3
- package/dist/streams/index.d.cts.map +1 -1
- package/dist/streams/index.d.ts +3 -3
- package/dist/streams/index.d.ts.map +1 -1
- package/dist/streams/index.js +2 -10
- package/dist/streams/progress.cjs +60 -46
- package/dist/streams/progress.cjs.map +1 -1
- package/dist/streams/progress.js +60 -46
- package/dist/streams/progress.js.map +1 -1
- package/dist/streams/source.cjs +316 -146
- package/dist/streams/source.cjs.map +1 -1
- package/dist/streams/source.d.cts +56 -18
- package/dist/streams/source.d.cts.map +1 -1
- package/dist/streams/source.d.ts +56 -18
- package/dist/streams/source.d.ts.map +1 -1
- package/dist/streams/source.js +312 -149
- package/dist/streams/source.js.map +1 -1
- package/dist/sync/actions/index.cjs +236 -184
- package/dist/sync/actions/index.cjs.map +1 -1
- package/dist/sync/actions/index.d.cts +29 -21
- package/dist/sync/actions/index.d.cts.map +1 -1
- package/dist/sync/actions/index.d.ts +29 -21
- package/dist/sync/actions/index.d.ts.map +1 -1
- package/dist/sync/actions/index.js +236 -190
- package/dist/sync/actions/index.js.map +1 -1
- package/dist/sync/b2-sha1-reader.cjs +148 -0
- package/dist/sync/b2-sha1-reader.cjs.map +1 -0
- package/dist/sync/b2-sha1-reader.d.cts +58 -0
- package/dist/sync/b2-sha1-reader.d.cts.map +1 -0
- package/dist/sync/b2-sha1-reader.d.ts +58 -0
- package/dist/sync/b2-sha1-reader.d.ts.map +1 -0
- package/dist/sync/b2-sha1-reader.js +145 -0
- package/dist/sync/b2-sha1-reader.js.map +1 -0
- package/dist/sync/download-staging.cjs +265 -0
- package/dist/sync/download-staging.cjs.map +1 -0
- package/dist/sync/download-staging.d.cts +54 -0
- package/dist/sync/download-staging.d.cts.map +1 -0
- package/dist/sync/download-staging.d.ts +54 -0
- package/dist/sync/download-staging.d.ts.map +1 -0
- package/dist/sync/download-staging.js +259 -0
- package/dist/sync/download-staging.js.map +1 -0
- package/dist/sync/filesystem-errors.cjs +32 -0
- package/dist/sync/filesystem-errors.cjs.map +1 -0
- package/dist/sync/filesystem-errors.d.cts +8 -0
- package/dist/sync/filesystem-errors.d.cts.map +1 -0
- package/dist/sync/filesystem-errors.d.ts +8 -0
- package/dist/sync/filesystem-errors.d.ts.map +1 -0
- package/dist/sync/filesystem-errors.js +32 -0
- package/dist/sync/filesystem-errors.js.map +1 -0
- package/dist/sync/filters.cjs +217 -0
- package/dist/sync/filters.cjs.map +1 -0
- package/dist/sync/filters.d.cts +51 -0
- package/dist/sync/filters.d.cts.map +1 -0
- package/dist/sync/filters.d.ts +51 -0
- package/dist/sync/filters.d.ts.map +1 -0
- package/dist/sync/filters.js +213 -0
- package/dist/sync/filters.js.map +1 -0
- package/dist/sync/index.cjs +39 -22
- package/dist/sync/index.d.cts +14 -9
- package/dist/sync/index.d.cts.map +1 -1
- package/dist/sync/index.d.ts +14 -9
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +9 -20
- package/dist/sync/local-file-identity.cjs +61 -0
- package/dist/sync/local-file-identity.cjs.map +1 -0
- package/dist/sync/local-file-identity.d.cts +37 -0
- package/dist/sync/local-file-identity.d.cts.map +1 -0
- package/dist/sync/local-file-identity.d.ts +37 -0
- package/dist/sync/local-file-identity.d.ts.map +1 -0
- package/dist/sync/local-file-identity.js +60 -0
- package/dist/sync/local-file-identity.js.map +1 -0
- package/dist/sync/local-file-io.cjs +364 -0
- package/dist/sync/local-file-io.cjs.map +1 -0
- package/dist/sync/local-file-io.d.cts +75 -0
- package/dist/sync/local-file-io.d.cts.map +1 -0
- package/dist/sync/local-file-io.d.ts +75 -0
- package/dist/sync/local-file-io.d.ts.map +1 -0
- package/dist/sync/local-file-io.js +360 -0
- package/dist/sync/local-file-io.js.map +1 -0
- package/dist/sync/local-filesystem-root.cjs +27 -0
- package/dist/sync/local-filesystem-root.cjs.map +1 -0
- package/dist/sync/local-filesystem-root.d.cts +18 -0
- package/dist/sync/local-filesystem-root.d.cts.map +1 -0
- package/dist/sync/local-filesystem-root.d.ts +18 -0
- package/dist/sync/local-filesystem-root.d.ts.map +1 -0
- package/dist/sync/local-filesystem-root.js +26 -0
- package/dist/sync/local-filesystem-root.js.map +1 -0
- package/dist/sync/local-sha1.cjs +119 -0
- package/dist/sync/local-sha1.cjs.map +1 -0
- package/dist/sync/local-sha1.d.cts +39 -0
- package/dist/sync/local-sha1.d.cts.map +1 -0
- package/dist/sync/local-sha1.d.ts +39 -0
- package/dist/sync/local-sha1.d.ts.map +1 -0
- package/dist/sync/local-sha1.js +117 -0
- package/dist/sync/local-sha1.js.map +1 -0
- package/dist/sync/pairing.cjs +118 -29
- package/dist/sync/pairing.cjs.map +1 -1
- package/dist/sync/pairing.d.cts +7 -2
- package/dist/sync/pairing.d.cts.map +1 -1
- package/dist/sync/pairing.d.ts +7 -2
- package/dist/sync/pairing.d.ts.map +1 -1
- package/dist/sync/pairing.js +120 -31
- package/dist/sync/pairing.js.map +1 -1
- package/dist/sync/path-order.cjs +30 -0
- package/dist/sync/path-order.cjs.map +1 -0
- package/dist/sync/path-order.d.cts +28 -0
- package/dist/sync/path-order.d.cts.map +1 -0
- package/dist/sync/path-order.d.ts +28 -0
- package/dist/sync/path-order.d.ts.map +1 -0
- package/dist/sync/path-order.js +29 -0
- package/dist/sync/path-order.js.map +1 -0
- package/dist/sync/path-safety.cjs +111 -0
- package/dist/sync/path-safety.cjs.map +1 -0
- package/dist/sync/path-safety.d.cts +78 -0
- package/dist/sync/path-safety.d.cts.map +1 -0
- package/dist/sync/path-safety.d.ts +78 -0
- package/dist/sync/path-safety.d.ts.map +1 -0
- package/dist/sync/path-safety.js +105 -0
- package/dist/sync/path-safety.js.map +1 -0
- package/dist/sync/paths.d.cts +19 -0
- package/dist/sync/paths.d.cts.map +1 -0
- package/dist/sync/paths.d.ts +19 -0
- package/dist/sync/paths.d.ts.map +1 -0
- package/dist/sync/policies/compare.cjs +389 -11
- package/dist/sync/policies/compare.cjs.map +1 -1
- package/dist/sync/policies/compare.d.cts +100 -2
- package/dist/sync/policies/compare.d.cts.map +1 -1
- package/dist/sync/policies/compare.d.ts +100 -2
- package/dist/sync/policies/compare.d.ts.map +1 -1
- package/dist/sync/policies/compare.js +387 -13
- package/dist/sync/policies/compare.js.map +1 -1
- package/dist/sync/policies/index.cjs +76 -65
- package/dist/sync/policies/index.cjs.map +1 -1
- package/dist/sync/policies/index.d.cts +35 -6
- package/dist/sync/policies/index.d.cts.map +1 -1
- package/dist/sync/policies/index.d.ts +35 -6
- package/dist/sync/policies/index.d.ts.map +1 -1
- package/dist/sync/policies/index.js +76 -65
- package/dist/sync/policies/index.js.map +1 -1
- package/dist/sync/prefix.cjs +96 -0
- package/dist/sync/prefix.cjs.map +1 -0
- package/dist/sync/prefix.d.cts +55 -0
- package/dist/sync/prefix.d.cts.map +1 -0
- package/dist/sync/prefix.d.ts +55 -0
- package/dist/sync/prefix.d.ts.map +1 -0
- package/dist/sync/prefix.js +92 -0
- package/dist/sync/prefix.js.map +1 -0
- package/dist/sync/regexp-safety.cjs +192 -0
- package/dist/sync/regexp-safety.cjs.map +1 -0
- package/dist/sync/regexp-safety.d.cts +29 -0
- package/dist/sync/regexp-safety.d.cts.map +1 -0
- package/dist/sync/regexp-safety.d.ts +29 -0
- package/dist/sync/regexp-safety.d.ts.map +1 -0
- package/dist/sync/regexp-safety.js +190 -0
- package/dist/sync/regexp-safety.js.map +1 -0
- package/dist/sync/scan-events.cjs +33 -0
- package/dist/sync/scan-events.cjs.map +1 -0
- package/dist/sync/scan-events.d.cts +17 -0
- package/dist/sync/scan-events.d.cts.map +1 -0
- package/dist/sync/scan-events.d.ts +17 -0
- package/dist/sync/scan-events.d.ts.map +1 -0
- package/dist/sync/scan-events.js +32 -0
- package/dist/sync/scan-events.js.map +1 -0
- package/dist/sync/scan-limit.cjs +33 -0
- package/dist/sync/scan-limit.cjs.map +1 -0
- package/dist/sync/scan-limit.d.cts +21 -0
- package/dist/sync/scan-limit.d.cts.map +1 -0
- package/dist/sync/scan-limit.d.ts +21 -0
- package/dist/sync/scan-limit.d.ts.map +1 -0
- package/dist/sync/scan-limit.js +31 -0
- package/dist/sync/scan-limit.js.map +1 -0
- package/dist/sync/scanners/b2.cjs +239 -55
- package/dist/sync/scanners/b2.cjs.map +1 -1
- package/dist/sync/scanners/b2.d.cts +26 -7
- package/dist/sync/scanners/b2.d.cts.map +1 -1
- package/dist/sync/scanners/b2.d.ts +26 -7
- package/dist/sync/scanners/b2.d.ts.map +1 -1
- package/dist/sync/scanners/b2.js +239 -55
- package/dist/sync/scanners/b2.js.map +1 -1
- package/dist/sync/scanners/local.cjs +215 -56
- package/dist/sync/scanners/local.cjs.map +1 -1
- package/dist/sync/scanners/local.d.cts +22 -7
- package/dist/sync/scanners/local.d.cts.map +1 -1
- package/dist/sync/scanners/local.d.ts +22 -7
- package/dist/sync/scanners/local.d.ts.map +1 -1
- package/dist/sync/scanners/local.js +216 -57
- package/dist/sync/scanners/local.js.map +1 -1
- package/dist/sync/sha1-metadata.cjs +94 -0
- package/dist/sync/sha1-metadata.cjs.map +1 -0
- package/dist/sync/sha1-metadata.d.cts +82 -0
- package/dist/sync/sha1-metadata.d.cts.map +1 -0
- package/dist/sync/sha1-metadata.d.ts +82 -0
- package/dist/sync/sha1-metadata.d.ts.map +1 -0
- package/dist/sync/sha1-metadata.js +89 -0
- package/dist/sync/sha1-metadata.js.map +1 -0
- package/dist/sync/sha1-options.cjs +23 -0
- package/dist/sync/sha1-options.cjs.map +1 -0
- package/dist/sync/sha1-options.d.cts +14 -0
- package/dist/sync/sha1-options.d.cts.map +1 -0
- package/dist/sync/sha1-options.d.ts +14 -0
- package/dist/sync/sha1-options.d.ts.map +1 -0
- package/dist/sync/sha1-options.js +21 -0
- package/dist/sync/sha1-options.js.map +1 -0
- package/dist/sync/synchronizer.cjs +782 -197
- package/dist/sync/synchronizer.cjs.map +1 -1
- package/dist/sync/synchronizer.d.cts +47 -1
- package/dist/sync/synchronizer.d.cts.map +1 -1
- package/dist/sync/synchronizer.d.ts +47 -1
- package/dist/sync/synchronizer.d.ts.map +1 -1
- package/dist/sync/synchronizer.js +779 -173
- package/dist/sync/synchronizer.js.map +1 -1
- package/dist/sync/types.d.cts +208 -16
- package/dist/sync/types.d.cts.map +1 -1
- package/dist/sync/types.d.ts +208 -16
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/types/auth.cjs +76 -56
- package/dist/types/auth.cjs.map +1 -1
- package/dist/types/auth.d.cts +67 -9
- package/dist/types/auth.d.cts.map +1 -1
- package/dist/types/auth.d.ts +67 -9
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/auth.js +77 -57
- package/dist/types/auth.js.map +1 -1
- package/dist/types/bucket.cjs +74 -38
- package/dist/types/bucket.cjs.map +1 -1
- package/dist/types/bucket.js +75 -41
- package/dist/types/bucket.js.map +1 -1
- package/dist/types/encryption.cjs +364 -327
- package/dist/types/encryption.cjs.map +1 -1
- package/dist/types/encryption.d.cts +16 -0
- package/dist/types/encryption.d.cts.map +1 -1
- package/dist/types/encryption.d.ts +16 -0
- package/dist/types/encryption.d.ts.map +1 -1
- package/dist/types/encryption.js +364 -310
- package/dist/types/encryption.js.map +1 -1
- package/dist/types/errors.cjs +47 -0
- package/dist/types/errors.cjs.map +1 -0
- package/dist/types/errors.d.cts +13 -4
- package/dist/types/errors.d.cts.map +1 -1
- package/dist/types/errors.d.ts +13 -4
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +47 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/file.cjs +36 -19
- package/dist/types/file.cjs.map +1 -1
- package/dist/types/file.d.cts +2 -2
- package/dist/types/file.d.cts.map +1 -1
- package/dist/types/file.d.ts +2 -2
- package/dist/types/file.d.ts.map +1 -1
- package/dist/types/file.js +37 -21
- package/dist/types/file.js.map +1 -1
- package/dist/types/ids.cjs +53 -9
- package/dist/types/ids.cjs.map +1 -1
- package/dist/types/ids.js +54 -15
- package/dist/types/ids.js.map +1 -1
- package/dist/types/index.d.cts +12 -11
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +12 -11
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/key.d.cts +44 -8
- package/dist/types/key.d.cts.map +1 -1
- package/dist/types/key.d.ts +44 -8
- package/dist/types/key.d.ts.map +1 -1
- package/dist/types/lock.cjs +33 -13
- package/dist/types/lock.cjs.map +1 -1
- package/dist/types/lock.js +34 -15
- package/dist/types/lock.js.map +1 -1
- package/dist/types/notifications.cjs +44 -22
- package/dist/types/notifications.cjs.map +1 -1
- package/dist/types/notifications.js +45 -23
- package/dist/types/notifications.js.map +1 -1
- package/dist/types/upload.d.cts +39 -5
- package/dist/types/upload.d.cts.map +1 -1
- package/dist/types/upload.d.ts +39 -5
- package/dist/types/upload.d.ts.map +1 -1
- package/dist/upload/abort-scope.cjs +91 -0
- package/dist/upload/abort-scope.cjs.map +1 -0
- package/dist/upload/abort-scope.d.cts +48 -0
- package/dist/upload/abort-scope.d.cts.map +1 -0
- package/dist/upload/abort-scope.d.ts +48 -0
- package/dist/upload/abort-scope.d.ts.map +1 -0
- package/dist/upload/abort-scope.js +88 -0
- package/dist/upload/abort-scope.js.map +1 -0
- package/dist/upload/cancel.cjs +183 -8
- package/dist/upload/cancel.cjs.map +1 -1
- package/dist/upload/cancel.d.cts +110 -2
- package/dist/upload/cancel.d.cts.map +1 -1
- package/dist/upload/cancel.d.ts +110 -2
- package/dist/upload/cancel.d.ts.map +1 -1
- package/dist/upload/cancel.js +178 -9
- package/dist/upload/cancel.js.map +1 -1
- package/dist/upload/concurrency.cjs +79 -52
- package/dist/upload/concurrency.cjs.map +1 -1
- package/dist/upload/concurrency.js +79 -53
- package/dist/upload/concurrency.js.map +1 -1
- package/dist/upload/finish.cjs +59 -0
- package/dist/upload/finish.cjs.map +1 -0
- package/dist/upload/finish.d.cts +26 -0
- package/dist/upload/finish.d.cts.map +1 -0
- package/dist/upload/finish.d.ts +26 -0
- package/dist/upload/finish.d.ts.map +1 -0
- package/dist/upload/finish.js +59 -0
- package/dist/upload/finish.js.map +1 -0
- package/dist/upload/large.cjs +301 -218
- package/dist/upload/large.cjs.map +1 -1
- package/dist/upload/large.d.cts +76 -9
- package/dist/upload/large.d.cts.map +1 -1
- package/dist/upload/large.d.ts +76 -9
- package/dist/upload/large.d.ts.map +1 -1
- package/dist/upload/large.js +301 -218
- package/dist/upload/large.js.map +1 -1
- package/dist/upload/options.cjs +28 -0
- package/dist/upload/options.cjs.map +1 -0
- package/dist/upload/options.d.cts +104 -0
- package/dist/upload/options.d.cts.map +1 -0
- package/dist/upload/options.d.ts +104 -0
- package/dist/upload/options.d.ts.map +1 -0
- package/dist/upload/options.js +27 -0
- package/dist/upload/options.js.map +1 -0
- package/dist/upload/resume.cjs +333 -33
- package/dist/upload/resume.cjs.map +1 -1
- package/dist/upload/resume.d.cts +82 -12
- package/dist/upload/resume.d.cts.map +1 -1
- package/dist/upload/resume.d.ts +82 -12
- package/dist/upload/resume.d.ts.map +1 -1
- package/dist/upload/resume.js +330 -33
- package/dist/upload/resume.js.map +1 -1
- package/dist/upload/retry.cjs +186 -0
- package/dist/upload/retry.cjs.map +1 -0
- package/dist/upload/retry.d.cts +127 -0
- package/dist/upload/retry.d.cts.map +1 -0
- package/dist/upload/retry.d.ts +127 -0
- package/dist/upload/retry.d.ts.map +1 -0
- package/dist/upload/retry.js +182 -0
- package/dist/upload/retry.js.map +1 -0
- package/dist/upload/single.cjs +68 -44
- package/dist/upload/single.cjs.map +1 -1
- package/dist/upload/single.d.cts +2 -1
- package/dist/upload/single.d.cts.map +1 -1
- package/dist/upload/single.d.ts +2 -1
- package/dist/upload/single.d.ts.map +1 -1
- package/dist/upload/single.js +68 -44
- package/dist/upload/single.js.map +1 -1
- package/dist/upload/stream.cjs +317 -193
- package/dist/upload/stream.cjs.map +1 -1
- package/dist/upload/stream.d.cts +3 -1
- package/dist/upload/stream.d.cts.map +1 -1
- package/dist/upload/stream.d.ts +3 -1
- package/dist/upload/stream.d.ts.map +1 -1
- package/dist/upload/stream.js +316 -192
- package/dist/upload/stream.js.map +1 -1
- package/dist/util/best-effort.cjs +42 -8
- package/dist/util/best-effort.cjs.map +1 -1
- package/dist/util/best-effort.d.cts +2 -1
- package/dist/util/best-effort.d.cts.map +1 -1
- package/dist/util/best-effort.d.ts +2 -1
- package/dist/util/best-effort.d.ts.map +1 -1
- package/dist/util/best-effort.js +43 -9
- package/dist/util/best-effort.js.map +1 -1
- package/dist/util/bytes.cjs +23 -0
- package/dist/util/bytes.cjs.map +1 -0
- package/dist/util/bytes.d.cts +15 -0
- package/dist/util/bytes.d.cts.map +1 -0
- package/dist/util/bytes.d.ts +15 -0
- package/dist/util/bytes.d.ts.map +1 -0
- package/dist/util/bytes.js +23 -0
- package/dist/util/bytes.js.map +1 -0
- package/dist/util/crypto.cjs +68 -0
- package/dist/util/crypto.cjs.map +1 -0
- package/dist/util/crypto.d.cts +26 -0
- package/dist/util/crypto.d.cts.map +1 -0
- package/dist/util/crypto.d.ts +26 -0
- package/dist/util/crypto.d.ts.map +1 -0
- package/dist/util/crypto.js +66 -0
- package/dist/util/crypto.js.map +1 -0
- package/dist/util/defaults.cjs +25 -9
- package/dist/util/defaults.cjs.map +1 -1
- package/dist/util/defaults.js +26 -11
- package/dist/util/defaults.js.map +1 -1
- package/dist/util/error-reason.cjs +41 -0
- package/dist/util/error-reason.cjs.map +1 -0
- package/dist/util/error-reason.d.cts +9 -0
- package/dist/util/error-reason.d.cts.map +1 -0
- package/dist/util/error-reason.d.ts +9 -0
- package/dist/util/error-reason.d.ts.map +1 -0
- package/dist/util/error-reason.js +41 -0
- package/dist/util/error-reason.js.map +1 -0
- package/dist/util/http.cjs +23 -0
- package/dist/util/http.cjs.map +1 -0
- package/dist/util/http.d.cts +13 -0
- package/dist/util/http.d.cts.map +1 -0
- package/dist/util/http.d.ts +13 -0
- package/dist/util/http.d.ts.map +1 -0
- package/dist/util/http.js +23 -0
- package/dist/util/http.js.map +1 -0
- package/dist/util/normalize.cjs +66 -7
- package/dist/util/normalize.cjs.map +1 -1
- package/dist/util/normalize.js +67 -10
- package/dist/util/normalize.js.map +1 -1
- package/dist/util/paginator.cjs +63 -14
- package/dist/util/paginator.cjs.map +1 -1
- package/dist/util/paginator.js +64 -16
- package/dist/util/paginator.js.map +1 -1
- package/dist/util/plan-ranges.cjs +47 -22
- package/dist/util/plan-ranges.cjs.map +1 -1
- package/dist/util/plan-ranges.js +48 -24
- package/dist/util/plan-ranges.js.map +1 -1
- package/dist/util/sha1.cjs +27 -0
- package/dist/util/sha1.cjs.map +1 -0
- package/dist/util/sha1.d.cts +17 -0
- package/dist/util/sha1.d.cts.map +1 -0
- package/dist/util/sha1.d.ts +17 -0
- package/dist/util/sha1.d.ts.map +1 -0
- package/dist/util/sha1.js +26 -0
- package/dist/util/sha1.js.map +1 -0
- package/dist/util/text-codec.cjs +29 -5
- package/dist/util/text-codec.cjs.map +1 -1
- package/dist/util/text-codec.js +30 -7
- package/dist/util/text-codec.js.map +1 -1
- package/dist/util/to-error.cjs +18 -4
- package/dist/util/to-error.cjs.map +1 -1
- package/dist/util/to-error.js +19 -5
- package/dist/util/to-error.js.map +1 -1
- package/dist/version.cjs +17 -5
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +3 -3
- package/dist/version.d.ts +3 -3
- package/dist/version.js +19 -6
- package/dist/version.js.map +1 -1
- package/package.json +19 -16
- package/dist/auth/index.cjs.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/notifications/index.cjs.map +0 -1
- package/dist/notifications/index.js.map +0 -1
- package/dist/package.json.cjs +0 -9
- package/dist/package.json.cjs.map +0 -1
- package/dist/package.json.js +0 -9
- package/dist/package.json.js.map +0 -1
- package/dist/streams/index.cjs.map +0 -1
- package/dist/streams/index.js.map +0 -1
- package/dist/sync/index.cjs.map +0 -1
- package/dist/sync/index.js.map +0 -1
package/dist/copy/large.cjs
CHANGED
|
@@ -1,74 +1,117 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
1
|
+
const require_file = require("../types/file.cjs");
|
|
2
|
+
const require_ids = require("../types/ids.cjs");
|
|
3
|
+
const require_abort_scope = require("../upload/abort-scope.cjs");
|
|
4
|
+
const require_cancel = require("../upload/cancel.cjs");
|
|
5
|
+
const require_concurrency = require("../upload/concurrency.cjs");
|
|
6
|
+
const require_finish = require("../upload/finish.cjs");
|
|
7
|
+
require("../util/defaults.cjs");
|
|
8
|
+
const require_plan_ranges = require("../util/plan-ranges.cjs");
|
|
9
|
+
//#region src/copy/large.ts
|
|
10
|
+
/**
|
|
11
|
+
* Performs a server-side copy of a file using the multipart `b2_copy_part` protocol.
|
|
12
|
+
* The source bytes never traverse the client; B2 copies each range internally.
|
|
13
|
+
*
|
|
14
|
+
* Falls back to a single `copyFile` call when the source fits in one part.
|
|
15
|
+
*
|
|
16
|
+
* @param raw - Low-level B2 API client.
|
|
17
|
+
* @param accountInfo - Authorized account state (used to resolve API URL, recommended part size).
|
|
18
|
+
* @param options - Copy parameters including source, destination, and concurrency.
|
|
19
|
+
*
|
|
20
|
+
* @returns The resulting destination {@link FileVersion}.
|
|
21
|
+
*/
|
|
8
22
|
async function copyLargeFile(raw, accountInfo, options) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
23
|
+
options.signal?.throwIfAborted();
|
|
24
|
+
const recommendedPartSize = accountInfo.getRecommendedPartSize();
|
|
25
|
+
const minPartSize = accountInfo.getAbsoluteMinimumPartSize();
|
|
26
|
+
const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize);
|
|
27
|
+
const concurrency = options.concurrency ?? 4;
|
|
28
|
+
const sourceInfo = await raw.getFileInfo(accountInfo.getApiUrl(), accountInfo.getAuthToken(), { fileId: options.sourceFileId });
|
|
29
|
+
const totalSize = sourceInfo.contentLength;
|
|
30
|
+
if (totalSize <= partSize) {
|
|
31
|
+
options.signal?.throwIfAborted();
|
|
32
|
+
const replaceMetadata = options.contentType !== void 0 || options.fileInfo !== void 0;
|
|
33
|
+
return raw.copyFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
|
|
34
|
+
sourceFileId: options.sourceFileId,
|
|
35
|
+
fileName: options.fileName,
|
|
36
|
+
...options.destinationBucketId !== void 0 ? { destinationBucketId: options.destinationBucketId } : {},
|
|
37
|
+
...replaceMetadata ? {
|
|
38
|
+
metadataDirective: require_file.MetadataDirective.Replace,
|
|
39
|
+
contentType: options.contentType ?? sourceInfo.contentType ?? "b2/x-auto",
|
|
40
|
+
fileInfo: options.fileInfo ?? {}
|
|
41
|
+
} : {},
|
|
42
|
+
...options.destinationServerSideEncryption !== void 0 ? { destinationServerSideEncryption: options.destinationServerSideEncryption } : {},
|
|
43
|
+
...options.sourceServerSideEncryption !== void 0 ? { sourceServerSideEncryption: options.sourceServerSideEncryption } : {}
|
|
44
|
+
}, options.signal !== void 0 ? { signal: options.signal } : void 0);
|
|
45
|
+
}
|
|
46
|
+
const destBucketId = options.destinationBucketId ?? sourceInfo.bucketId;
|
|
47
|
+
const ranges = require_plan_ranges.planRanges(totalSize, partSize);
|
|
48
|
+
const partSha1s = new Array(ranges.length);
|
|
49
|
+
const sem = new require_concurrency.Semaphore(concurrency);
|
|
50
|
+
const abortScope = require_abort_scope.createAbortScope(options.signal);
|
|
51
|
+
let largeFileId;
|
|
52
|
+
try {
|
|
53
|
+
abortScope.signal.throwIfAborted();
|
|
54
|
+
const startPromise = raw.startLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
|
|
55
|
+
bucketId: destBucketId,
|
|
56
|
+
fileName: options.fileName,
|
|
57
|
+
contentType: options.contentType ?? sourceInfo.contentType ?? "b2/x-auto",
|
|
58
|
+
fileInfo: options.fileInfo ?? {},
|
|
59
|
+
...options.destinationServerSideEncryption !== void 0 ? { serverSideEncryption: options.destinationServerSideEncryption } : {}
|
|
60
|
+
}, { signal: abortScope.signal });
|
|
61
|
+
try {
|
|
62
|
+
largeFileId = (await require_abort_scope.raceWithAbort(startPromise, abortScope.signal)).fileId;
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (abortScope.signal.aborted) cancelLargeFileAfterStart(startPromise, raw, accountInfo, options.onCleanupFailure);
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
const startedLargeFileId = largeFileId;
|
|
68
|
+
if (startedLargeFileId === void 0) throw new Error("copyLargeFile: start did not return a large file ID.");
|
|
69
|
+
const tasks = ranges.map(async (range) => {
|
|
70
|
+
await sem.acquire();
|
|
71
|
+
try {
|
|
72
|
+
abortScope.signal.throwIfAborted();
|
|
73
|
+
const resp = await require_abort_scope.raceWithAbort(raw.copyPart(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
|
|
74
|
+
sourceFileId: options.sourceFileId,
|
|
75
|
+
largeFileId: require_ids.fileId(startedLargeFileId),
|
|
76
|
+
partNumber: range.partNumber,
|
|
77
|
+
range: require_plan_ranges.byteRangeHeader(range.start, range.end),
|
|
78
|
+
...options.sourceServerSideEncryption !== void 0 ? { sourceServerSideEncryption: options.sourceServerSideEncryption } : {},
|
|
79
|
+
...options.destinationServerSideEncryption !== void 0 ? { destinationServerSideEncryption: options.destinationServerSideEncryption } : {}
|
|
80
|
+
}, { signal: abortScope.signal }), abortScope.signal);
|
|
81
|
+
partSha1s[range.partNumber - 1] = resp.contentSha1;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
abortScope.abort(err);
|
|
84
|
+
throw err;
|
|
85
|
+
} finally {
|
|
86
|
+
sem.release();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
require_abort_scope.throwRejectedOrAbortReason(await Promise.allSettled(tasks), abortScope);
|
|
90
|
+
return await require_finish.finishLargeFileWithAbortReconciliation(raw, accountInfo, {
|
|
91
|
+
fileId: startedLargeFileId,
|
|
92
|
+
bucketId: destBucketId,
|
|
93
|
+
fileName: options.fileName,
|
|
94
|
+
partSha1s,
|
|
95
|
+
signal: abortScope.signal
|
|
96
|
+
});
|
|
97
|
+
} catch (err) {
|
|
98
|
+
abortScope.abort(err);
|
|
99
|
+
if (largeFileId === void 0) throw err;
|
|
100
|
+
return await require_cancel.cleanupAfterLargeFileError(err, raw, accountInfo, {
|
|
101
|
+
fileId: largeFileId,
|
|
102
|
+
bucketId: destBucketId,
|
|
103
|
+
fileName: options.fileName,
|
|
104
|
+
signal: options.signal,
|
|
105
|
+
onCleanupFailure: options.onCleanupFailure
|
|
106
|
+
});
|
|
107
|
+
} finally {
|
|
108
|
+
abortScope.dispose();
|
|
109
|
+
}
|
|
72
110
|
}
|
|
111
|
+
function cancelLargeFileAfterStart(started, raw, accountInfo, onCleanupFailure) {
|
|
112
|
+
started.then((resp) => require_cancel.cancelLargeFileBestEffort(raw, accountInfo, resp.fileId, onCleanupFailure === void 0 ? void 0 : { onCleanupFailure })).catch(() => {});
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
73
115
|
exports.copyLargeFile = copyLargeFile;
|
|
74
|
-
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=large.cjs.map
|
package/dist/copy/large.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"large.cjs","sources":["../../src/copy/large.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport { type BucketId, type FileId, fileId as fileIdOf } from '../types/ids.ts'\nimport { cancelLargeFileBestEffort } from '../upload/cancel.ts'\nimport { Semaphore } from '../upload/concurrency.ts'\nimport { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { byteRangeHeader, planRanges } from '../util/plan-ranges.ts'\n\n/** Options for a server-side multipart copy. */\nexport interface CopyLargeFileOptions {\n /** ID of the source file version to copy. */\n readonly sourceFileId: FileId\n /** Destination file name in the target bucket. */\n readonly fileName: string\n /** Target bucket. Defaults to the source's bucket. */\n readonly destinationBucketId?: BucketId\n /** Size of each part in bytes. Defaults to the account's recommended part size. */\n readonly partSize?: number\n /**\n * Maximum number of parts copied in parallel. Defaults to\n * {@link DEFAULT_TRANSFER_CONCURRENCY}.\n */\n readonly concurrency?: number\n /** MIME type for the destination file. Defaults to `b2/x-auto`. */\n readonly contentType?: string\n /** Custom file info merged into the destination. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption applied to the destination. */\n readonly destinationServerSideEncryption?: EncryptionSetting\n /** SSE-C settings used to read the source if it was uploaded with SSE-C. */\n readonly sourceServerSideEncryption?: EncryptionSetting\n /**\n * Optional abort signal. Checked before dispatching each part and\n * between parts; an aborted signal cancels remaining parts and rolls\n * back the unfinished large file via best-effort `cancelLargeFile`.\n *\n * Aborting does NOT roll back parts already accepted by B2; those\n * exist on the unfinished large file until the cancel call completes\n * or B2's lifecycle expires the in-progress upload (24 hours).\n */\n readonly signal?: AbortSignal\n}\n\n/**\n * Performs a server-side copy of a file using the multipart `b2_copy_part` protocol.\n * The source bytes never traverse the client; B2 copies each range internally.\n *\n * Falls back to a single `copyFile` call when the source fits in one part.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (used to resolve API URL, recommended part size).\n * @param options - Copy parameters including source, destination, and concurrency.\n *\n * @returns The resulting destination {@link FileVersion}.\n */\nexport async function copyLargeFile(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: CopyLargeFileOptions,\n): Promise<FileVersion> {\n const recommendedPartSize = accountInfo.getRecommendedPartSize()\n const minPartSize = accountInfo.getAbsoluteMinimumPartSize()\n const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize)\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n\n // Discover the source size via getFileInfo.\n const sourceInfo = await raw.getFileInfo(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n fileId: options.sourceFileId,\n })\n const totalSize = sourceInfo.contentLength\n\n // Below the part threshold, take the single-call fast path.\n if (totalSize <= partSize) {\n return raw.copyFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n sourceFileId: options.sourceFileId,\n fileName: options.fileName,\n ...(options.destinationBucketId !== undefined\n ? { destinationBucketId: options.destinationBucketId }\n : {}),\n ...(options.contentType !== undefined ? { contentType: options.contentType } : {}),\n ...(options.fileInfo !== undefined ? { fileInfo: options.fileInfo } : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n })\n }\n\n // Resolve destination bucket (defaults to source's bucket). Both operands\n // are already typed `BucketId`, so no cast is needed.\n const destBucketId = options.destinationBucketId ?? sourceInfo.bucketId\n\n // Start the multipart file.\n const startResp = await raw.startLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n bucketId: destBucketId,\n fileName: options.fileName,\n contentType: options.contentType ?? sourceInfo.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n ...(options.destinationServerSideEncryption !== undefined\n ? { serverSideEncryption: options.destinationServerSideEncryption }\n : {}),\n })\n const largeFileId = startResp.fileId\n\n const ranges = planRanges(totalSize, partSize)\n const partSha1s: string[] = new Array(ranges.length)\n const sem = new Semaphore(concurrency)\n\n try {\n options.signal?.throwIfAborted()\n await Promise.all(\n ranges.map(async (range) => {\n await sem.acquire()\n try {\n // Re-check the abort flag after every queue handoff so the\n // first cancelled task short-circuits the remaining parts.\n options.signal?.throwIfAborted()\n const resp = await raw.copyPart(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n sourceFileId: options.sourceFileId,\n // `startLargeFile` returns `LargeFileId`; `copyPart` takes the\n // same value typed as `FileId`. Re-brand via the factory.\n largeFileId: fileIdOf(largeFileId),\n partNumber: range.partNumber,\n range: byteRangeHeader(range.start, range.end),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n })\n partSha1s[range.partNumber - 1] = resp.contentSha1\n } finally {\n sem.release()\n }\n }),\n )\n\n options.signal?.throwIfAborted()\n return await raw.finishLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n fileId: largeFileId,\n partSha1Array: partSha1s,\n })\n } catch (err) {\n await cancelLargeFileBestEffort(raw, accountInfo, largeFileId)\n throw err\n }\n}\n"],"names":["concurrency","DEFAULT_TRANSFER_CONCURRENCY","DEFAULT_CONTENT_TYPE","planRanges","Semaphore","fileIdOf","byteRangeHeader","cancelLargeFileBestEffort"],"mappings":";;;;;;;AAyDA,eAAsB,cACpB,KACA,aACA,SACsB;AACtB,QAAM,sBAAsB,YAAY,uBAAA;AACxC,QAAM,cAAc,YAAY,2BAAA;AAChC,QAAM,WAAW,KAAK,IAAI,QAAQ,YAAY,qBAAqB,WAAW;AAC9E,QAAMA,gBAAc,QAAQ,eAAeC,SAAAA;AAG3C,QAAM,aAAa,MAAM,IAAI,YAAY,YAAY,UAAA,GAAa,YAAY,gBAAgB;AAAA,IAC5F,QAAQ,QAAQ;AAAA,EAAA,CACjB;AACD,QAAM,YAAY,WAAW;AAG7B,MAAI,aAAa,UAAU;AACzB,WAAO,IAAI,SAAS,YAAY,aAAa,YAAY,gBAAgB;AAAA,MACvE,cAAc,QAAQ;AAAA,MACtB,UAAU,QAAQ;AAAA,MAClB,GAAI,QAAQ,wBAAwB,SAChC,EAAE,qBAAqB,QAAQ,oBAAA,IAC/B,CAAA;AAAA,MACJ,GAAI,QAAQ,gBAAgB,SAAY,EAAE,aAAa,QAAQ,YAAA,IAAgB,CAAA;AAAA,MAC/E,GAAI,QAAQ,aAAa,SAAY,EAAE,UAAU,QAAQ,SAAA,IAAa,CAAA;AAAA,MACtE,GAAI,QAAQ,oCAAoC,SAC5C,EAAE,iCAAiC,QAAQ,gCAAA,IAC3C,CAAA;AAAA,MACJ,GAAI,QAAQ,+BAA+B,SACvC,EAAE,4BAA4B,QAAQ,+BACtC,CAAA;AAAA,IAAC,CACN;AAAA,EACH;AAIA,QAAM,eAAe,QAAQ,uBAAuB,WAAW;AAG/D,QAAM,YAAY,MAAM,IAAI,eAAe,YAAY,UAAA,GAAa,YAAY,gBAAgB;AAAA,IAC9F,UAAU;AAAA,IACV,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ,eAAe,WAAW,eAAeC,SAAAA;AAAAA,IAC9D,UAAU,QAAQ,YAAY,CAAA;AAAA,IAC9B,GAAI,QAAQ,oCAAoC,SAC5C,EAAE,sBAAsB,QAAQ,oCAChC,CAAA;AAAA,EAAC,CACN;AACD,QAAM,cAAc,UAAU;AAE9B,QAAM,SAASC,WAAAA,WAAW,WAAW,QAAQ;AAC7C,QAAM,YAAsB,IAAI,MAAM,OAAO,MAAM;AACnD,QAAM,MAAM,IAAIC,YAAAA,UAAUJ,aAAW;AAErC,MAAI;AACF,YAAQ,QAAQ,eAAA;AAChB,UAAM,QAAQ;AAAA,MACZ,OAAO,IAAI,OAAO,UAAU;AAC1B,cAAM,IAAI,QAAA;AACV,YAAI;AAGF,kBAAQ,QAAQ,eAAA;AAChB,gBAAM,OAAO,MAAM,IAAI,SAAS,YAAY,UAAA,GAAa,YAAY,gBAAgB;AAAA,YACnF,cAAc,QAAQ;AAAA;AAAA;AAAA,YAGtB,aAAaK,IAAAA,OAAS,WAAW;AAAA,YACjC,YAAY,MAAM;AAAA,YAClB,OAAOC,WAAAA,gBAAgB,MAAM,OAAO,MAAM,GAAG;AAAA,YAC7C,GAAI,QAAQ,+BAA+B,SACvC,EAAE,4BAA4B,QAAQ,2BAAA,IACtC,CAAA;AAAA,YACJ,GAAI,QAAQ,oCAAoC,SAC5C,EAAE,iCAAiC,QAAQ,oCAC3C,CAAA;AAAA,UAAC,CACN;AACD,oBAAU,MAAM,aAAa,CAAC,IAAI,KAAK;AAAA,QACzC,UAAA;AACE,cAAI,QAAA;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IAAA;AAGH,YAAQ,QAAQ,eAAA;AAChB,WAAO,MAAM,IAAI,gBAAgB,YAAY,aAAa,YAAY,gBAAgB;AAAA,MACpF,QAAQ;AAAA,MACR,eAAe;AAAA,IAAA,CAChB;AAAA,EACH,SAAS,KAAK;AACZ,UAAMC,iCAA0B,KAAK,aAAa,WAAW;AAC7D,UAAM;AAAA,EACR;AACF;;"}
|
|
1
|
+
{"version":3,"file":"large.cjs","names":[],"sources":["../../src/copy/large.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport { type FileVersion, MetadataDirective } from '../types/file.ts'\nimport { type BucketId, type FileId, fileId as fileIdOf, type LargeFileId } from '../types/ids.ts'\nimport {\n createAbortScope,\n raceWithAbort,\n throwRejectedOrAbortReason,\n} from '../upload/abort-scope.ts'\nimport {\n type CleanupFailureListener,\n cancelLargeFileBestEffort,\n cleanupAfterLargeFileError,\n} from '../upload/cancel.ts'\nimport { Semaphore } from '../upload/concurrency.ts'\nimport { finishLargeFileWithAbortReconciliation } from '../upload/finish.ts'\nimport { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { byteRangeHeader, planRanges } from '../util/plan-ranges.ts'\n\n/** Options for a server-side multipart copy. */\nexport interface CopyLargeFileOptions {\n /** ID of the source file version to copy. */\n readonly sourceFileId: FileId\n /** Destination file name in the target bucket. */\n readonly fileName: string\n /** Target bucket. Defaults to the source's bucket. */\n readonly destinationBucketId?: BucketId\n /** Size of each part in bytes. Defaults to the account's recommended part size. */\n readonly partSize?: number\n /**\n * Maximum number of parts copied in parallel. Defaults to\n * {@link DEFAULT_TRANSFER_CONCURRENCY}.\n */\n readonly concurrency?: number\n /** MIME type for the destination file. Defaults to `b2/x-auto`. */\n readonly contentType?: string\n /** Custom file info merged into the destination. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption applied to the destination. */\n readonly destinationServerSideEncryption?: EncryptionSetting\n /** SSE-C settings used to read the source if it was uploaded with SSE-C. */\n readonly sourceServerSideEncryption?: EncryptionSetting\n /**\n * Callback invoked if best-effort cancellation fails, or if cancellation is\n * skipped because `b2_finish_large_file` may already have committed.\n */\n readonly onCleanupFailure?: CleanupFailureListener\n /**\n * Optional abort signal. Checked before dispatching each part and\n * between parts; an aborted signal cancels remaining parts and rolls\n * back the unfinished large file via best-effort `cancelLargeFile`.\n *\n * Aborting does NOT roll back parts already accepted by B2; those\n * exist on the unfinished large file until the cancel call completes\n * or B2's lifecycle expires the in-progress upload (24 hours).\n */\n readonly signal?: AbortSignal\n}\n\n/**\n * Performs a server-side copy of a file using the multipart `b2_copy_part` protocol.\n * The source bytes never traverse the client; B2 copies each range internally.\n *\n * Falls back to a single `copyFile` call when the source fits in one part.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (used to resolve API URL, recommended part size).\n * @param options - Copy parameters including source, destination, and concurrency.\n *\n * @returns The resulting destination {@link FileVersion}.\n */\nexport async function copyLargeFile(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: CopyLargeFileOptions,\n): Promise<FileVersion> {\n options.signal?.throwIfAborted()\n const recommendedPartSize = accountInfo.getRecommendedPartSize()\n const minPartSize = accountInfo.getAbsoluteMinimumPartSize()\n const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize)\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n\n // Discover the source size via getFileInfo.\n const sourceInfo = await raw.getFileInfo(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n fileId: options.sourceFileId,\n })\n const totalSize = sourceInfo.contentLength\n\n // Below the part threshold, take the single-call fast path.\n if (totalSize <= partSize) {\n options.signal?.throwIfAborted()\n // `b2_copy_file` only accepts replacement contentType/fileInfo under\n // `metadataDirective: REPLACE`; supplying them in the default COPY mode is\n // rejected by B2. When the caller sets either, switch to REPLACE with a\n // required contentType (the override, else the source's, else b2/x-auto),\n // matching the multipart path's metadata semantics below.\n const replaceMetadata = options.contentType !== undefined || options.fileInfo !== undefined\n return raw.copyFile(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n sourceFileId: options.sourceFileId,\n fileName: options.fileName,\n ...(options.destinationBucketId !== undefined\n ? { destinationBucketId: options.destinationBucketId }\n : {}),\n ...(replaceMetadata\n ? {\n metadataDirective: MetadataDirective.Replace,\n contentType: options.contentType ?? sourceInfo.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n }\n : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n },\n options.signal !== undefined ? { signal: options.signal } : undefined,\n )\n }\n\n // Resolve destination bucket (defaults to source's bucket). Both operands\n // are already typed `BucketId`, so no cast is needed.\n const destBucketId = options.destinationBucketId ?? sourceInfo.bucketId\n\n const ranges = planRanges(totalSize, partSize)\n const partSha1s: string[] = new Array(ranges.length)\n const sem = new Semaphore(concurrency)\n const abortScope = createAbortScope(options.signal)\n let largeFileId: LargeFileId | undefined\n\n try {\n abortScope.signal.throwIfAborted()\n const startPromise = raw.startLargeFile(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n bucketId: destBucketId,\n fileName: options.fileName,\n contentType: options.contentType ?? sourceInfo.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n ...(options.destinationServerSideEncryption !== undefined\n ? { serverSideEncryption: options.destinationServerSideEncryption }\n : {}),\n },\n { signal: abortScope.signal },\n )\n try {\n const startResp = await raceWithAbort(startPromise, abortScope.signal)\n largeFileId = startResp.fileId\n } catch (err) {\n if (abortScope.signal.aborted) {\n cancelLargeFileAfterStart(startPromise, raw, accountInfo, options.onCleanupFailure)\n }\n throw err\n }\n const startedLargeFileId = largeFileId\n if (startedLargeFileId === undefined) {\n throw new Error('copyLargeFile: start did not return a large file ID.')\n }\n\n const tasks = ranges.map(async (range) => {\n await sem.acquire()\n try {\n abortScope.signal.throwIfAborted()\n const copyPromise = raw.copyPart(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n sourceFileId: options.sourceFileId,\n // `startLargeFile` returns `LargeFileId`; `copyPart` takes the\n // same value typed as `FileId`. Re-brand via the factory.\n largeFileId: fileIdOf(startedLargeFileId),\n partNumber: range.partNumber,\n range: byteRangeHeader(range.start, range.end),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n },\n { signal: abortScope.signal },\n )\n const resp = await raceWithAbort(copyPromise, abortScope.signal)\n partSha1s[range.partNumber - 1] = resp.contentSha1\n } catch (err) {\n abortScope.abort(err)\n throw err\n } finally {\n sem.release()\n }\n })\n\n throwRejectedOrAbortReason(await Promise.allSettled(tasks), abortScope)\n\n return await finishLargeFileWithAbortReconciliation(raw, accountInfo, {\n fileId: startedLargeFileId,\n bucketId: destBucketId,\n fileName: options.fileName,\n partSha1s,\n signal: abortScope.signal,\n })\n } catch (err) {\n abortScope.abort(err)\n if (largeFileId === undefined) throw err\n return await cleanupAfterLargeFileError(err, raw, accountInfo, {\n fileId: largeFileId,\n bucketId: destBucketId,\n fileName: options.fileName,\n signal: options.signal,\n onCleanupFailure: options.onCleanupFailure,\n })\n } finally {\n abortScope.dispose()\n }\n}\n\nfunction cancelLargeFileAfterStart(\n started: Promise<{ readonly fileId: LargeFileId }>,\n raw: RawClient,\n accountInfo: AccountInfo,\n onCleanupFailure: CleanupFailureListener | undefined,\n): void {\n void started\n .then((resp) =>\n cancelLargeFileBestEffort(\n raw,\n accountInfo,\n resp.fileId,\n onCleanupFailure === undefined ? undefined : { onCleanupFailure },\n ),\n )\n .catch(() => {\n // If start failed, no file ID is available to cancel.\n })\n}\n"],"mappings":"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"}
|
package/dist/copy/large.d.cts
CHANGED
|
@@ -3,6 +3,7 @@ import { RawClient } from '../raw/index.js';
|
|
|
3
3
|
import { EncryptionSetting } from '../types/encryption.js';
|
|
4
4
|
import { FileVersion } from '../types/file.js';
|
|
5
5
|
import { BucketId, FileId } from '../types/ids.js';
|
|
6
|
+
import { CleanupFailureListener } from '../upload/cancel.js';
|
|
6
7
|
/** Options for a server-side multipart copy. */
|
|
7
8
|
export interface CopyLargeFileOptions {
|
|
8
9
|
/** ID of the source file version to copy. */
|
|
@@ -26,6 +27,11 @@ export interface CopyLargeFileOptions {
|
|
|
26
27
|
readonly destinationServerSideEncryption?: EncryptionSetting;
|
|
27
28
|
/** SSE-C settings used to read the source if it was uploaded with SSE-C. */
|
|
28
29
|
readonly sourceServerSideEncryption?: EncryptionSetting;
|
|
30
|
+
/**
|
|
31
|
+
* Callback invoked if best-effort cancellation fails, or if cancellation is
|
|
32
|
+
* skipped because `b2_finish_large_file` may already have committed.
|
|
33
|
+
*/
|
|
34
|
+
readonly onCleanupFailure?: CleanupFailureListener;
|
|
29
35
|
/**
|
|
30
36
|
* Optional abort signal. Checked before dispatching each part and
|
|
31
37
|
* between parts; an aborted signal cancels remaining parts and rolls
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"large.d.ts","sourceRoot":"","sources":["../../src/copy/large.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"large.d.ts","sourceRoot":"","sources":["../../src/copy/large.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,kBAAkB,CAAA;AACtE,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAwC,MAAM,iBAAiB,CAAA;AAMlG,OAAO,EACL,KAAK,sBAAsB,EAG5B,MAAM,qBAAqB,CAAA;AAM5B,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,QAAQ,CAAA;IACvC,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,yDAAyD;IACzD,QAAQ,CAAC,+BAA+B,CAAC,EAAE,iBAAiB,CAAA;IAC5D,4EAA4E;IAC5E,QAAQ,CAAC,0BAA0B,CAAC,EAAE,iBAAiB,CAAA;IACvD;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,sBAAsB,CAAA;IAClD;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,WAAW,CAAC,CAgJtB"}
|
package/dist/copy/large.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { RawClient } from '../raw/index.js';
|
|
|
3
3
|
import { EncryptionSetting } from '../types/encryption.js';
|
|
4
4
|
import { FileVersion } from '../types/file.js';
|
|
5
5
|
import { BucketId, FileId } from '../types/ids.js';
|
|
6
|
+
import { CleanupFailureListener } from '../upload/cancel.js';
|
|
6
7
|
/** Options for a server-side multipart copy. */
|
|
7
8
|
export interface CopyLargeFileOptions {
|
|
8
9
|
/** ID of the source file version to copy. */
|
|
@@ -26,6 +27,11 @@ export interface CopyLargeFileOptions {
|
|
|
26
27
|
readonly destinationServerSideEncryption?: EncryptionSetting;
|
|
27
28
|
/** SSE-C settings used to read the source if it was uploaded with SSE-C. */
|
|
28
29
|
readonly sourceServerSideEncryption?: EncryptionSetting;
|
|
30
|
+
/**
|
|
31
|
+
* Callback invoked if best-effort cancellation fails, or if cancellation is
|
|
32
|
+
* skipped because `b2_finish_large_file` may already have committed.
|
|
33
|
+
*/
|
|
34
|
+
readonly onCleanupFailure?: CleanupFailureListener;
|
|
29
35
|
/**
|
|
30
36
|
* Optional abort signal. Checked before dispatching each part and
|
|
31
37
|
* between parts; an aborted signal cancels remaining parts and rolls
|
package/dist/copy/large.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"large.d.ts","sourceRoot":"","sources":["../../src/copy/large.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"large.d.ts","sourceRoot":"","sources":["../../src/copy/large.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,kBAAkB,CAAA;AACtE,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAwC,MAAM,iBAAiB,CAAA;AAMlG,OAAO,EACL,KAAK,sBAAsB,EAG5B,MAAM,qBAAqB,CAAA;AAM5B,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,QAAQ,CAAA;IACvC,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,yDAAyD;IACzD,QAAQ,CAAC,+BAA+B,CAAC,EAAE,iBAAiB,CAAA;IAC5D,4EAA4E;IAC5E,QAAQ,CAAC,0BAA0B,CAAC,EAAE,iBAAiB,CAAA;IACvD;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,sBAAsB,CAAA;IAClD;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,WAAW,CAAC,CAgJtB"}
|
package/dist/copy/large.js
CHANGED
|
@@ -1,74 +1,117 @@
|
|
|
1
|
+
import { MetadataDirective } from "../types/file.js";
|
|
1
2
|
import { fileId } from "../types/ids.js";
|
|
2
|
-
import {
|
|
3
|
+
import { createAbortScope, raceWithAbort, throwRejectedOrAbortReason } from "../upload/abort-scope.js";
|
|
4
|
+
import { cancelLargeFileBestEffort, cleanupAfterLargeFileError } from "../upload/cancel.js";
|
|
3
5
|
import { Semaphore } from "../upload/concurrency.js";
|
|
4
|
-
import {
|
|
6
|
+
import { finishLargeFileWithAbortReconciliation } from "../upload/finish.js";
|
|
7
|
+
import "../util/defaults.js";
|
|
5
8
|
import { byteRangeHeader, planRanges } from "../util/plan-ranges.js";
|
|
9
|
+
//#region src/copy/large.ts
|
|
10
|
+
/**
|
|
11
|
+
* Performs a server-side copy of a file using the multipart `b2_copy_part` protocol.
|
|
12
|
+
* The source bytes never traverse the client; B2 copies each range internally.
|
|
13
|
+
*
|
|
14
|
+
* Falls back to a single `copyFile` call when the source fits in one part.
|
|
15
|
+
*
|
|
16
|
+
* @param raw - Low-level B2 API client.
|
|
17
|
+
* @param accountInfo - Authorized account state (used to resolve API URL, recommended part size).
|
|
18
|
+
* @param options - Copy parameters including source, destination, and concurrency.
|
|
19
|
+
*
|
|
20
|
+
* @returns The resulting destination {@link FileVersion}.
|
|
21
|
+
*/
|
|
6
22
|
async function copyLargeFile(raw, accountInfo, options) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
23
|
+
options.signal?.throwIfAborted();
|
|
24
|
+
const recommendedPartSize = accountInfo.getRecommendedPartSize();
|
|
25
|
+
const minPartSize = accountInfo.getAbsoluteMinimumPartSize();
|
|
26
|
+
const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize);
|
|
27
|
+
const concurrency = options.concurrency ?? 4;
|
|
28
|
+
const sourceInfo = await raw.getFileInfo(accountInfo.getApiUrl(), accountInfo.getAuthToken(), { fileId: options.sourceFileId });
|
|
29
|
+
const totalSize = sourceInfo.contentLength;
|
|
30
|
+
if (totalSize <= partSize) {
|
|
31
|
+
options.signal?.throwIfAborted();
|
|
32
|
+
const replaceMetadata = options.contentType !== void 0 || options.fileInfo !== void 0;
|
|
33
|
+
return raw.copyFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
|
|
34
|
+
sourceFileId: options.sourceFileId,
|
|
35
|
+
fileName: options.fileName,
|
|
36
|
+
...options.destinationBucketId !== void 0 ? { destinationBucketId: options.destinationBucketId } : {},
|
|
37
|
+
...replaceMetadata ? {
|
|
38
|
+
metadataDirective: MetadataDirective.Replace,
|
|
39
|
+
contentType: options.contentType ?? sourceInfo.contentType ?? "b2/x-auto",
|
|
40
|
+
fileInfo: options.fileInfo ?? {}
|
|
41
|
+
} : {},
|
|
42
|
+
...options.destinationServerSideEncryption !== void 0 ? { destinationServerSideEncryption: options.destinationServerSideEncryption } : {},
|
|
43
|
+
...options.sourceServerSideEncryption !== void 0 ? { sourceServerSideEncryption: options.sourceServerSideEncryption } : {}
|
|
44
|
+
}, options.signal !== void 0 ? { signal: options.signal } : void 0);
|
|
45
|
+
}
|
|
46
|
+
const destBucketId = options.destinationBucketId ?? sourceInfo.bucketId;
|
|
47
|
+
const ranges = planRanges(totalSize, partSize);
|
|
48
|
+
const partSha1s = new Array(ranges.length);
|
|
49
|
+
const sem = new Semaphore(concurrency);
|
|
50
|
+
const abortScope = createAbortScope(options.signal);
|
|
51
|
+
let largeFileId;
|
|
52
|
+
try {
|
|
53
|
+
abortScope.signal.throwIfAborted();
|
|
54
|
+
const startPromise = raw.startLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
|
|
55
|
+
bucketId: destBucketId,
|
|
56
|
+
fileName: options.fileName,
|
|
57
|
+
contentType: options.contentType ?? sourceInfo.contentType ?? "b2/x-auto",
|
|
58
|
+
fileInfo: options.fileInfo ?? {},
|
|
59
|
+
...options.destinationServerSideEncryption !== void 0 ? { serverSideEncryption: options.destinationServerSideEncryption } : {}
|
|
60
|
+
}, { signal: abortScope.signal });
|
|
61
|
+
try {
|
|
62
|
+
largeFileId = (await raceWithAbort(startPromise, abortScope.signal)).fileId;
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (abortScope.signal.aborted) cancelLargeFileAfterStart(startPromise, raw, accountInfo, options.onCleanupFailure);
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
const startedLargeFileId = largeFileId;
|
|
68
|
+
if (startedLargeFileId === void 0) throw new Error("copyLargeFile: start did not return a large file ID.");
|
|
69
|
+
const tasks = ranges.map(async (range) => {
|
|
70
|
+
await sem.acquire();
|
|
71
|
+
try {
|
|
72
|
+
abortScope.signal.throwIfAborted();
|
|
73
|
+
const resp = await raceWithAbort(raw.copyPart(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
|
|
74
|
+
sourceFileId: options.sourceFileId,
|
|
75
|
+
largeFileId: fileId(startedLargeFileId),
|
|
76
|
+
partNumber: range.partNumber,
|
|
77
|
+
range: byteRangeHeader(range.start, range.end),
|
|
78
|
+
...options.sourceServerSideEncryption !== void 0 ? { sourceServerSideEncryption: options.sourceServerSideEncryption } : {},
|
|
79
|
+
...options.destinationServerSideEncryption !== void 0 ? { destinationServerSideEncryption: options.destinationServerSideEncryption } : {}
|
|
80
|
+
}, { signal: abortScope.signal }), abortScope.signal);
|
|
81
|
+
partSha1s[range.partNumber - 1] = resp.contentSha1;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
abortScope.abort(err);
|
|
84
|
+
throw err;
|
|
85
|
+
} finally {
|
|
86
|
+
sem.release();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
throwRejectedOrAbortReason(await Promise.allSettled(tasks), abortScope);
|
|
90
|
+
return await finishLargeFileWithAbortReconciliation(raw, accountInfo, {
|
|
91
|
+
fileId: startedLargeFileId,
|
|
92
|
+
bucketId: destBucketId,
|
|
93
|
+
fileName: options.fileName,
|
|
94
|
+
partSha1s,
|
|
95
|
+
signal: abortScope.signal
|
|
96
|
+
});
|
|
97
|
+
} catch (err) {
|
|
98
|
+
abortScope.abort(err);
|
|
99
|
+
if (largeFileId === void 0) throw err;
|
|
100
|
+
return await cleanupAfterLargeFileError(err, raw, accountInfo, {
|
|
101
|
+
fileId: largeFileId,
|
|
102
|
+
bucketId: destBucketId,
|
|
103
|
+
fileName: options.fileName,
|
|
104
|
+
signal: options.signal,
|
|
105
|
+
onCleanupFailure: options.onCleanupFailure
|
|
106
|
+
});
|
|
107
|
+
} finally {
|
|
108
|
+
abortScope.dispose();
|
|
109
|
+
}
|
|
70
110
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
//#
|
|
111
|
+
function cancelLargeFileAfterStart(started, raw, accountInfo, onCleanupFailure) {
|
|
112
|
+
started.then((resp) => cancelLargeFileBestEffort(raw, accountInfo, resp.fileId, onCleanupFailure === void 0 ? void 0 : { onCleanupFailure })).catch(() => {});
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { copyLargeFile };
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=large.js.map
|
package/dist/copy/large.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"large.js","sources":["../../src/copy/large.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport { type BucketId, type FileId, fileId as fileIdOf } from '../types/ids.ts'\nimport { cancelLargeFileBestEffort } from '../upload/cancel.ts'\nimport { Semaphore } from '../upload/concurrency.ts'\nimport { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { byteRangeHeader, planRanges } from '../util/plan-ranges.ts'\n\n/** Options for a server-side multipart copy. */\nexport interface CopyLargeFileOptions {\n /** ID of the source file version to copy. */\n readonly sourceFileId: FileId\n /** Destination file name in the target bucket. */\n readonly fileName: string\n /** Target bucket. Defaults to the source's bucket. */\n readonly destinationBucketId?: BucketId\n /** Size of each part in bytes. Defaults to the account's recommended part size. */\n readonly partSize?: number\n /**\n * Maximum number of parts copied in parallel. Defaults to\n * {@link DEFAULT_TRANSFER_CONCURRENCY}.\n */\n readonly concurrency?: number\n /** MIME type for the destination file. Defaults to `b2/x-auto`. */\n readonly contentType?: string\n /** Custom file info merged into the destination. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption applied to the destination. */\n readonly destinationServerSideEncryption?: EncryptionSetting\n /** SSE-C settings used to read the source if it was uploaded with SSE-C. */\n readonly sourceServerSideEncryption?: EncryptionSetting\n /**\n * Optional abort signal. Checked before dispatching each part and\n * between parts; an aborted signal cancels remaining parts and rolls\n * back the unfinished large file via best-effort `cancelLargeFile`.\n *\n * Aborting does NOT roll back parts already accepted by B2; those\n * exist on the unfinished large file until the cancel call completes\n * or B2's lifecycle expires the in-progress upload (24 hours).\n */\n readonly signal?: AbortSignal\n}\n\n/**\n * Performs a server-side copy of a file using the multipart `b2_copy_part` protocol.\n * The source bytes never traverse the client; B2 copies each range internally.\n *\n * Falls back to a single `copyFile` call when the source fits in one part.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (used to resolve API URL, recommended part size).\n * @param options - Copy parameters including source, destination, and concurrency.\n *\n * @returns The resulting destination {@link FileVersion}.\n */\nexport async function copyLargeFile(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: CopyLargeFileOptions,\n): Promise<FileVersion> {\n const recommendedPartSize = accountInfo.getRecommendedPartSize()\n const minPartSize = accountInfo.getAbsoluteMinimumPartSize()\n const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize)\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n\n // Discover the source size via getFileInfo.\n const sourceInfo = await raw.getFileInfo(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n fileId: options.sourceFileId,\n })\n const totalSize = sourceInfo.contentLength\n\n // Below the part threshold, take the single-call fast path.\n if (totalSize <= partSize) {\n return raw.copyFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n sourceFileId: options.sourceFileId,\n fileName: options.fileName,\n ...(options.destinationBucketId !== undefined\n ? { destinationBucketId: options.destinationBucketId }\n : {}),\n ...(options.contentType !== undefined ? { contentType: options.contentType } : {}),\n ...(options.fileInfo !== undefined ? { fileInfo: options.fileInfo } : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n })\n }\n\n // Resolve destination bucket (defaults to source's bucket). Both operands\n // are already typed `BucketId`, so no cast is needed.\n const destBucketId = options.destinationBucketId ?? sourceInfo.bucketId\n\n // Start the multipart file.\n const startResp = await raw.startLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n bucketId: destBucketId,\n fileName: options.fileName,\n contentType: options.contentType ?? sourceInfo.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n ...(options.destinationServerSideEncryption !== undefined\n ? { serverSideEncryption: options.destinationServerSideEncryption }\n : {}),\n })\n const largeFileId = startResp.fileId\n\n const ranges = planRanges(totalSize, partSize)\n const partSha1s: string[] = new Array(ranges.length)\n const sem = new Semaphore(concurrency)\n\n try {\n options.signal?.throwIfAborted()\n await Promise.all(\n ranges.map(async (range) => {\n await sem.acquire()\n try {\n // Re-check the abort flag after every queue handoff so the\n // first cancelled task short-circuits the remaining parts.\n options.signal?.throwIfAborted()\n const resp = await raw.copyPart(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n sourceFileId: options.sourceFileId,\n // `startLargeFile` returns `LargeFileId`; `copyPart` takes the\n // same value typed as `FileId`. Re-brand via the factory.\n largeFileId: fileIdOf(largeFileId),\n partNumber: range.partNumber,\n range: byteRangeHeader(range.start, range.end),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n })\n partSha1s[range.partNumber - 1] = resp.contentSha1\n } finally {\n sem.release()\n }\n }),\n )\n\n options.signal?.throwIfAborted()\n return await raw.finishLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n fileId: largeFileId,\n partSha1Array: partSha1s,\n })\n } catch (err) {\n await cancelLargeFileBestEffort(raw, accountInfo, largeFileId)\n throw err\n }\n}\n"],"names":["fileIdOf"],"mappings":";;;;;AAyDA,eAAsB,cACpB,KACA,aACA,SACsB;AACtB,QAAM,sBAAsB,YAAY,uBAAA;AACxC,QAAM,cAAc,YAAY,2BAAA;AAChC,QAAM,WAAW,KAAK,IAAI,QAAQ,YAAY,qBAAqB,WAAW;AAC9E,QAAM,cAAc,QAAQ,eAAe;AAG3C,QAAM,aAAa,MAAM,IAAI,YAAY,YAAY,UAAA,GAAa,YAAY,gBAAgB;AAAA,IAC5F,QAAQ,QAAQ;AAAA,EAAA,CACjB;AACD,QAAM,YAAY,WAAW;AAG7B,MAAI,aAAa,UAAU;AACzB,WAAO,IAAI,SAAS,YAAY,aAAa,YAAY,gBAAgB;AAAA,MACvE,cAAc,QAAQ;AAAA,MACtB,UAAU,QAAQ;AAAA,MAClB,GAAI,QAAQ,wBAAwB,SAChC,EAAE,qBAAqB,QAAQ,oBAAA,IAC/B,CAAA;AAAA,MACJ,GAAI,QAAQ,gBAAgB,SAAY,EAAE,aAAa,QAAQ,YAAA,IAAgB,CAAA;AAAA,MAC/E,GAAI,QAAQ,aAAa,SAAY,EAAE,UAAU,QAAQ,SAAA,IAAa,CAAA;AAAA,MACtE,GAAI,QAAQ,oCAAoC,SAC5C,EAAE,iCAAiC,QAAQ,gCAAA,IAC3C,CAAA;AAAA,MACJ,GAAI,QAAQ,+BAA+B,SACvC,EAAE,4BAA4B,QAAQ,+BACtC,CAAA;AAAA,IAAC,CACN;AAAA,EACH;AAIA,QAAM,eAAe,QAAQ,uBAAuB,WAAW;AAG/D,QAAM,YAAY,MAAM,IAAI,eAAe,YAAY,UAAA,GAAa,YAAY,gBAAgB;AAAA,IAC9F,UAAU;AAAA,IACV,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ,eAAe,WAAW,eAAe;AAAA,IAC9D,UAAU,QAAQ,YAAY,CAAA;AAAA,IAC9B,GAAI,QAAQ,oCAAoC,SAC5C,EAAE,sBAAsB,QAAQ,oCAChC,CAAA;AAAA,EAAC,CACN;AACD,QAAM,cAAc,UAAU;AAE9B,QAAM,SAAS,WAAW,WAAW,QAAQ;AAC7C,QAAM,YAAsB,IAAI,MAAM,OAAO,MAAM;AACnD,QAAM,MAAM,IAAI,UAAU,WAAW;AAErC,MAAI;AACF,YAAQ,QAAQ,eAAA;AAChB,UAAM,QAAQ;AAAA,MACZ,OAAO,IAAI,OAAO,UAAU;AAC1B,cAAM,IAAI,QAAA;AACV,YAAI;AAGF,kBAAQ,QAAQ,eAAA;AAChB,gBAAM,OAAO,MAAM,IAAI,SAAS,YAAY,UAAA,GAAa,YAAY,gBAAgB;AAAA,YACnF,cAAc,QAAQ;AAAA;AAAA;AAAA,YAGtB,aAAaA,OAAS,WAAW;AAAA,YACjC,YAAY,MAAM;AAAA,YAClB,OAAO,gBAAgB,MAAM,OAAO,MAAM,GAAG;AAAA,YAC7C,GAAI,QAAQ,+BAA+B,SACvC,EAAE,4BAA4B,QAAQ,2BAAA,IACtC,CAAA;AAAA,YACJ,GAAI,QAAQ,oCAAoC,SAC5C,EAAE,iCAAiC,QAAQ,oCAC3C,CAAA;AAAA,UAAC,CACN;AACD,oBAAU,MAAM,aAAa,CAAC,IAAI,KAAK;AAAA,QACzC,UAAA;AACE,cAAI,QAAA;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IAAA;AAGH,YAAQ,QAAQ,eAAA;AAChB,WAAO,MAAM,IAAI,gBAAgB,YAAY,aAAa,YAAY,gBAAgB;AAAA,MACpF,QAAQ;AAAA,MACR,eAAe;AAAA,IAAA,CAChB;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,0BAA0B,KAAK,aAAa,WAAW;AAC7D,UAAM;AAAA,EACR;AACF;"}
|
|
1
|
+
{"version":3,"file":"large.js","names":[],"sources":["../../src/copy/large.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport { type FileVersion, MetadataDirective } from '../types/file.ts'\nimport { type BucketId, type FileId, fileId as fileIdOf, type LargeFileId } from '../types/ids.ts'\nimport {\n createAbortScope,\n raceWithAbort,\n throwRejectedOrAbortReason,\n} from '../upload/abort-scope.ts'\nimport {\n type CleanupFailureListener,\n cancelLargeFileBestEffort,\n cleanupAfterLargeFileError,\n} from '../upload/cancel.ts'\nimport { Semaphore } from '../upload/concurrency.ts'\nimport { finishLargeFileWithAbortReconciliation } from '../upload/finish.ts'\nimport { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { byteRangeHeader, planRanges } from '../util/plan-ranges.ts'\n\n/** Options for a server-side multipart copy. */\nexport interface CopyLargeFileOptions {\n /** ID of the source file version to copy. */\n readonly sourceFileId: FileId\n /** Destination file name in the target bucket. */\n readonly fileName: string\n /** Target bucket. Defaults to the source's bucket. */\n readonly destinationBucketId?: BucketId\n /** Size of each part in bytes. Defaults to the account's recommended part size. */\n readonly partSize?: number\n /**\n * Maximum number of parts copied in parallel. Defaults to\n * {@link DEFAULT_TRANSFER_CONCURRENCY}.\n */\n readonly concurrency?: number\n /** MIME type for the destination file. Defaults to `b2/x-auto`. */\n readonly contentType?: string\n /** Custom file info merged into the destination. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption applied to the destination. */\n readonly destinationServerSideEncryption?: EncryptionSetting\n /** SSE-C settings used to read the source if it was uploaded with SSE-C. */\n readonly sourceServerSideEncryption?: EncryptionSetting\n /**\n * Callback invoked if best-effort cancellation fails, or if cancellation is\n * skipped because `b2_finish_large_file` may already have committed.\n */\n readonly onCleanupFailure?: CleanupFailureListener\n /**\n * Optional abort signal. Checked before dispatching each part and\n * between parts; an aborted signal cancels remaining parts and rolls\n * back the unfinished large file via best-effort `cancelLargeFile`.\n *\n * Aborting does NOT roll back parts already accepted by B2; those\n * exist on the unfinished large file until the cancel call completes\n * or B2's lifecycle expires the in-progress upload (24 hours).\n */\n readonly signal?: AbortSignal\n}\n\n/**\n * Performs a server-side copy of a file using the multipart `b2_copy_part` protocol.\n * The source bytes never traverse the client; B2 copies each range internally.\n *\n * Falls back to a single `copyFile` call when the source fits in one part.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (used to resolve API URL, recommended part size).\n * @param options - Copy parameters including source, destination, and concurrency.\n *\n * @returns The resulting destination {@link FileVersion}.\n */\nexport async function copyLargeFile(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: CopyLargeFileOptions,\n): Promise<FileVersion> {\n options.signal?.throwIfAborted()\n const recommendedPartSize = accountInfo.getRecommendedPartSize()\n const minPartSize = accountInfo.getAbsoluteMinimumPartSize()\n const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize)\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n\n // Discover the source size via getFileInfo.\n const sourceInfo = await raw.getFileInfo(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n fileId: options.sourceFileId,\n })\n const totalSize = sourceInfo.contentLength\n\n // Below the part threshold, take the single-call fast path.\n if (totalSize <= partSize) {\n options.signal?.throwIfAborted()\n // `b2_copy_file` only accepts replacement contentType/fileInfo under\n // `metadataDirective: REPLACE`; supplying them in the default COPY mode is\n // rejected by B2. When the caller sets either, switch to REPLACE with a\n // required contentType (the override, else the source's, else b2/x-auto),\n // matching the multipart path's metadata semantics below.\n const replaceMetadata = options.contentType !== undefined || options.fileInfo !== undefined\n return raw.copyFile(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n sourceFileId: options.sourceFileId,\n fileName: options.fileName,\n ...(options.destinationBucketId !== undefined\n ? { destinationBucketId: options.destinationBucketId }\n : {}),\n ...(replaceMetadata\n ? {\n metadataDirective: MetadataDirective.Replace,\n contentType: options.contentType ?? sourceInfo.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n }\n : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n },\n options.signal !== undefined ? { signal: options.signal } : undefined,\n )\n }\n\n // Resolve destination bucket (defaults to source's bucket). Both operands\n // are already typed `BucketId`, so no cast is needed.\n const destBucketId = options.destinationBucketId ?? sourceInfo.bucketId\n\n const ranges = planRanges(totalSize, partSize)\n const partSha1s: string[] = new Array(ranges.length)\n const sem = new Semaphore(concurrency)\n const abortScope = createAbortScope(options.signal)\n let largeFileId: LargeFileId | undefined\n\n try {\n abortScope.signal.throwIfAborted()\n const startPromise = raw.startLargeFile(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n bucketId: destBucketId,\n fileName: options.fileName,\n contentType: options.contentType ?? sourceInfo.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n ...(options.destinationServerSideEncryption !== undefined\n ? { serverSideEncryption: options.destinationServerSideEncryption }\n : {}),\n },\n { signal: abortScope.signal },\n )\n try {\n const startResp = await raceWithAbort(startPromise, abortScope.signal)\n largeFileId = startResp.fileId\n } catch (err) {\n if (abortScope.signal.aborted) {\n cancelLargeFileAfterStart(startPromise, raw, accountInfo, options.onCleanupFailure)\n }\n throw err\n }\n const startedLargeFileId = largeFileId\n if (startedLargeFileId === undefined) {\n throw new Error('copyLargeFile: start did not return a large file ID.')\n }\n\n const tasks = ranges.map(async (range) => {\n await sem.acquire()\n try {\n abortScope.signal.throwIfAborted()\n const copyPromise = raw.copyPart(\n accountInfo.getApiUrl(),\n accountInfo.getAuthToken(),\n {\n sourceFileId: options.sourceFileId,\n // `startLargeFile` returns `LargeFileId`; `copyPart` takes the\n // same value typed as `FileId`. Re-brand via the factory.\n largeFileId: fileIdOf(startedLargeFileId),\n partNumber: range.partNumber,\n range: byteRangeHeader(range.start, range.end),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n },\n { signal: abortScope.signal },\n )\n const resp = await raceWithAbort(copyPromise, abortScope.signal)\n partSha1s[range.partNumber - 1] = resp.contentSha1\n } catch (err) {\n abortScope.abort(err)\n throw err\n } finally {\n sem.release()\n }\n })\n\n throwRejectedOrAbortReason(await Promise.allSettled(tasks), abortScope)\n\n return await finishLargeFileWithAbortReconciliation(raw, accountInfo, {\n fileId: startedLargeFileId,\n bucketId: destBucketId,\n fileName: options.fileName,\n partSha1s,\n signal: abortScope.signal,\n })\n } catch (err) {\n abortScope.abort(err)\n if (largeFileId === undefined) throw err\n return await cleanupAfterLargeFileError(err, raw, accountInfo, {\n fileId: largeFileId,\n bucketId: destBucketId,\n fileName: options.fileName,\n signal: options.signal,\n onCleanupFailure: options.onCleanupFailure,\n })\n } finally {\n abortScope.dispose()\n }\n}\n\nfunction cancelLargeFileAfterStart(\n started: Promise<{ readonly fileId: LargeFileId }>,\n raw: RawClient,\n accountInfo: AccountInfo,\n onCleanupFailure: CleanupFailureListener | undefined,\n): void {\n void started\n .then((resp) =>\n cancelLargeFileBestEffort(\n raw,\n accountInfo,\n resp.fileId,\n onCleanupFailure === undefined ? undefined : { onCleanupFailure },\n ),\n )\n .catch(() => {\n // If start failed, no file ID is available to cancel.\n })\n}\n"],"mappings":"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"}
|