@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-file-io.js","names":[],"sources":["../../src/sync/local-file-io.ts"],"sourcesContent":["import { sanitizeErrorReason } from '../util/error-reason.ts'\nimport { readStreamChunkWithTimeout } from './b2-sha1-reader.ts'\nimport {\n assertDownloadPathSameDevice,\n createDownloadStagingDirectory,\n DOWNLOAD_STAGING_DIRECTORY_NAME,\n isDownloadStagingDirectorySegment,\n} from './download-staging.ts'\nimport { assertSameScannedRegularFile } from './local-file-identity.ts'\nimport {\n assertPathInsideRoot,\n hasErrorCode,\n makeReservedSyncTempFileName,\n noFollowFlag,\n safeRelativePathSegments,\n} from './path-safety.ts'\nimport { DEFAULT_SHA1_IDLE_TIMEOUT_MILLIS } from './sha1-options.ts'\nimport type { LocalSyncPath } from './types.ts'\n\ntype DeviceStat = { readonly dev: number | bigint }\ntype DeviceStatFn = (path: string) => Promise<DeviceStat>\n\nexport { DOWNLOAD_STAGING_DIRECTORY_NAME }\n\n/** @internal */\nexport const localFileIoTestHooks: {\n afterParentDirectoryValidated?: (path: string) => Promise<void> | void\n afterDownloadBackupRename?: (path: string) => Promise<void> | void\n afterTempFileCreated?: (path: string, stagingDirectory: string) => Promise<void> | void\n beforeDownloadPublish?: (path: string) => Promise<void> | void\n beforeFinalRename?: (path: string) => Promise<void> | void\n beforeLocalDeleteOpenParent?: (path: string) => Promise<void> | void\n beforeLocalDeleteUnlink?: (path: string) => Promise<void> | void\n beforeStagingMarkerWrite?: (path: string) => Promise<void> | void\n disableProcFdAnchoring?: boolean\n platform?: string\n statForDeviceCheck?: DeviceStatFn\n} = {}\n\ninterface ScannedLocalFileHandle {\n stat(): Promise<{\n isFile(): boolean\n readonly dev: number\n readonly ino: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n readonly size: number\n }>\n readFile(): Promise<Uint8Array>\n close(): Promise<void>\n}\n\n/**\n * Verifies that a previously scanned local file still points at the same regular file.\n *\n * @param path - Scanned local path and file identity.\n *\n * @internal\n */\nexport async function validateScannedLocalFile(path: LocalSyncPath): Promise<void> {\n const handle = await openValidatedScannedLocalFile(path)\n await handle.close()\n}\n\n/**\n * Reads a previously scanned local file while rejecting symlink swaps and metadata drift.\n *\n * @param path - Scanned local path and file identity.\n *\n * @returns The file bytes.\n *\n * @internal\n */\nexport async function readScannedLocalFile(path: LocalSyncPath): Promise<Uint8Array> {\n const handle = await openValidatedScannedLocalFile(path)\n try {\n const data = await handle.readFile()\n if (data.byteLength !== path.size) {\n throw new Error('local file changed before upload: size changed while reading')\n }\n return data\n } finally {\n await handle.close()\n }\n}\n\nasync function openValidatedScannedLocalFile(path: LocalSyncPath): Promise<ScannedLocalFileHandle> {\n const { constants } = await import('node:fs')\n const { open } = await import('node:fs/promises')\n const flags =\n constants.O_RDONLY |\n noFollowFlag(constants) |\n ((constants as { O_NONBLOCK?: number }).O_NONBLOCK ?? 0)\n const handle = await open(path.absolutePath, flags).catch((err: unknown) => {\n if (hasErrorCode(err, 'ELOOP')) {\n throw new Error('local file changed before upload: not a regular file')\n }\n throw new Error(\n `local file changed before upload: could not open scanned file: ${sanitizeErrorReason(err)}`,\n )\n })\n try {\n const stats = await handle.stat()\n assertSameScannedRegularFile(stats, path, 'upload', {\n platform: localFileIoTestHooks.platform,\n })\n return handle\n } catch (err) {\n await handle.close().catch(() => {})\n throw err\n }\n}\n\n/**\n * Writes bytes under a local sync root after path containment checks.\n *\n * @param root - Local sync root.\n * @param relPath - Destination path relative to the root.\n * @param data - Bytes to write.\n *\n * @internal\n */\nexport async function writeLocalFileInsideRoot(\n root: string,\n relPath: string,\n data: Uint8Array,\n): Promise<void> {\n await writeLocalStreamInsideRoot(\n root,\n relPath,\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(data)\n controller.close()\n },\n }),\n {\n expectedBytes: data.byteLength,\n idleTimeoutMillis: DEFAULT_SHA1_IDLE_TIMEOUT_MILLIS,\n },\n )\n}\n\n/**\n * Streams a B2 download under a local sync root with path, timeout, and size checks.\n *\n * @param root - Local sync root.\n * @param relPath - Destination path relative to the root.\n * @param body - Download body stream.\n * @param options - Expected byte count, idle timeout, and optional abort signal.\n *\n * @internal\n */\nexport async function writeLocalStreamInsideRoot(\n root: string,\n relPath: string,\n body: ReadableStream<Uint8Array>,\n options: {\n readonly expectedBytes: number\n readonly expectedDestination?: LocalSyncPath | null\n readonly idleTimeoutMillis: number\n readonly signal?: AbortSignal\n },\n): Promise<void> {\n const { constants } = await import('node:fs')\n const { link, lstat, mkdir, open, realpath, rename, rm, stat } = await import('node:fs/promises')\n const path = await import('node:path')\n const { randomUUID } = await import('node:crypto')\n assertValidExpectedBytes(options.expectedBytes)\n const segments = safeRelativePathSegments(relPath)\n if (isDownloadStagingDirectorySegment(segments[0])) {\n throw new Error(\n `unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is reserved for SDK download staging`,\n )\n }\n const rootRealPath = await realpath(root)\n\n let current = rootRealPath\n for (const segment of segments.slice(0, -1)) {\n current = path.join(current, segment)\n try {\n await mkdir(current)\n } catch (err) {\n if (!hasErrorCode(err, 'EEXIST')) throw err\n }\n const stats = await lstat(current)\n if (!stats.isDirectory()) {\n throw new Error('unsafe local destination path: parent is not a directory')\n }\n await localFileIoTestHooks.afterParentDirectoryValidated?.(current)\n }\n\n const destPath = path.join(rootRealPath, ...segments)\n assertPathInsideRoot(rootRealPath, destPath, path)\n\n const parentRealPath = await realpath(path.dirname(destPath))\n const finalPath = path.join(parentRealPath, path.basename(destPath))\n assertPathInsideRoot(rootRealPath, finalPath, path)\n try {\n const targetStats = await lstat(finalPath)\n if (targetStats.isSymbolicLink()) {\n throw new Error('unsafe local destination path: target is a symbolic link')\n }\n if (targetStats.isFile() && targetStats.nlink > 1) {\n throw new Error('unsafe local destination path: target has multiple hard links')\n }\n } catch (err) {\n if (!hasErrorCode(err, 'ENOENT')) throw err\n }\n const statForDeviceCheck = localFileIoTestHooks.statForDeviceCheck ?? stat\n await assertDownloadPathSameDevice(\n rootRealPath,\n parentRealPath,\n statForDeviceCheck,\n 'unsafe local destination path: cannot publish download across filesystems',\n )\n\n let parentHandle: Awaited<ReturnType<typeof open>> | undefined\n let anchoredParentPath: string | undefined\n const platform = (globalThis as { process?: { platform?: string } }).process?.platform\n /* v8 ignore start -- Linux-only fd-relative path support is covered by Linux CI */\n if (\n platform === 'linux' &&\n constants.O_DIRECTORY !== undefined &&\n localFileIoTestHooks.disableProcFdAnchoring !== true\n ) {\n try {\n parentHandle = await open(\n parentRealPath,\n constants.O_RDONLY | constants.O_DIRECTORY | noFollowFlag(constants),\n )\n anchoredParentPath = `/proc/self/fd/${parentHandle.fd}`\n } catch (err) {\n if (hasErrorCode(err, 'ELOOP') || hasErrorCode(err, 'ENOTDIR')) {\n throw new Error('unsafe local destination path: parent is not a directory')\n }\n throw err\n }\n }\n /* v8 ignore stop */\n const finalName = path.basename(destPath)\n const finalWritePath = path.join(anchoredParentPath ?? parentRealPath, finalName)\n let publishMode: number\n try {\n publishMode = await replacementFileMode(finalPath)\n } catch (err) {\n /* v8 ignore next -- best-effort close during setup failure */\n await parentHandle?.close().catch(() => {})\n throw err\n }\n let stagingDirectory: string\n try {\n stagingDirectory = await createDownloadStagingDirectory(\n rootRealPath,\n path,\n randomUUID,\n statForDeviceCheck,\n localFileIoTestHooks.beforeStagingMarkerWrite,\n )\n } catch (err) {\n /* v8 ignore next -- best-effort close during setup failure */\n await parentHandle?.close().catch(() => {})\n throw err\n }\n const tmpPath = path.join(stagingDirectory, `.b2sdk-${randomUUID()}.partial`)\n let handle: Awaited<ReturnType<typeof open>> | undefined\n try {\n handle = await open(\n tmpPath,\n constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollowFlag(constants),\n PRIVATE_DOWNLOAD_FILE_MODE,\n )\n /* v8 ignore next -- best-effort chmod */\n await handle.chmod(PRIVATE_DOWNLOAD_FILE_MODE).catch(() => {})\n await localFileIoTestHooks.afterTempFileCreated?.(tmpPath, stagingDirectory)\n /* v8 ignore start -- defensive cleanup before the main write try/finally exists */\n } catch (err) {\n await parentHandle?.close().catch(() => {})\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n throw err\n }\n /* v8 ignore stop */\n try {\n const tmpRealPath = await realpath(tmpPath)\n assertPathInsideRoot(stagingDirectory, tmpRealPath, path)\n } catch (err) {\n /* v8 ignore next -- best-effort cleanup */\n await handle?.close().catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await rm(tmpPath, { force: true }).catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await parentHandle?.close().catch(() => {})\n throw err\n }\n const writeHandle = handle\n const reader = body.getReader()\n let completed = false\n try {\n let bytesWritten = 0\n while (true) {\n const { done, value } = await readStreamChunkWithTimeout(\n reader,\n options.idleTimeoutMillis,\n `download read stalled for ${options.idleTimeoutMillis} ms`,\n options.signal,\n )\n if (done) break\n if (bytesWritten + value.byteLength > options.expectedBytes) {\n throw new Error(`download read exceeded ${options.expectedBytes} byte limit`)\n }\n await writeAll(writeHandle, value, bytesWritten)\n bytesWritten += value.byteLength\n }\n if (bytesWritten !== options.expectedBytes) {\n throw new Error(\n `download read ended after ${bytesWritten} bytes, expected ${options.expectedBytes}`,\n )\n }\n if (publishMode !== PRIVATE_DOWNLOAD_FILE_MODE) {\n /* v8 ignore next -- best-effort mode preservation */\n await writeHandle.chmod(publishMode).catch(() => {})\n }\n await writeHandle.close()\n handle = undefined\n\n const [parentRealPathBeforeRename, parentStatsBeforeRename] = await Promise.all([\n realpath(path.dirname(destPath)),\n stat(path.dirname(destPath)),\n ])\n const finalPathBeforeRename = path.join(parentRealPathBeforeRename, path.basename(destPath))\n assertPathInsideRoot(rootRealPath, finalPathBeforeRename, path)\n await localFileIoTestHooks.beforeFinalRename?.(parentRealPathBeforeRename)\n let publishPath = finalWritePath\n if (anchoredParentPath === undefined) {\n const [parentRealPathAfterHook, parentStatsAfterHook] = await Promise.all([\n realpath(path.dirname(destPath)),\n stat(path.dirname(destPath)),\n ])\n if (\n parentRealPathAfterHook !== parentRealPathBeforeRename ||\n !sameParentIdentity(parentStatsAfterHook, parentStatsBeforeRename)\n ) {\n throw new Error('unsafe local destination path: parent changed before final publish')\n }\n publishPath = path.join(parentRealPathAfterHook, path.basename(destPath))\n assertPathInsideRoot(rootRealPath, publishPath, path)\n }\n await publishDownload(\n lstat,\n link,\n path,\n randomUUID,\n rename,\n rm,\n tmpPath,\n publishPath,\n options.expectedDestination,\n )\n completed = true\n } catch (err) {\n /* v8 ignore next -- best-effort cleanup */\n void reader.cancel(err).catch(() => {})\n throw err\n } finally {\n reader.releaseLock()\n if (!completed) {\n /* v8 ignore next -- best-effort cleanup */\n await handle?.close().catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await rm(tmpPath, { force: true }).catch(() => {})\n }\n /* v8 ignore next -- best-effort cleanup */\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await parentHandle?.close().catch(() => {})\n }\n}\n\nfunction assertValidExpectedBytes(expectedBytes: number): void {\n if (!Number.isSafeInteger(expectedBytes) || expectedBytes < 0) {\n throw new Error('download expectedBytes must be a non-negative safe integer')\n }\n}\n\nasync function assertExpectedDownloadDestination(\n lstat: typeof import('node:fs/promises')['lstat'],\n finalPath: string,\n expectedDestination: LocalSyncPath | null | undefined,\n): Promise<void> {\n if (expectedDestination === undefined) return\n\n try {\n const stats = await lstat(finalPath)\n if (expectedDestination === null) {\n throw new Error('local destination changed before download: file was created')\n }\n assertSameScannedRegularFile(\n stats,\n { ...expectedDestination, absolutePath: finalPath },\n 'download',\n { platform: localFileIoTestHooks.platform },\n )\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) {\n if (expectedDestination === null) return\n throw new Error('local file changed before download: file missing')\n }\n throw err\n }\n}\n\nasync function publishDownload(\n lstat: typeof import('node:fs/promises')['lstat'],\n link: typeof import('node:fs/promises')['link'],\n path: typeof import('node:path'),\n randomUUID: () => string,\n rename: typeof import('node:fs/promises')['rename'],\n rm: typeof import('node:fs/promises')['rm'],\n tmpPath: string,\n publishPath: string,\n expectedDestination: LocalSyncPath | null | undefined,\n): Promise<void> {\n await assertExpectedDownloadDestination(lstat, publishPath, expectedDestination)\n await localFileIoTestHooks.beforeDownloadPublish?.(publishPath)\n\n if (expectedDestination === undefined) {\n await rename(tmpPath, publishPath)\n return\n }\n\n if (expectedDestination === null) {\n await linkDownloadNoOverwrite(\n link,\n tmpPath,\n publishPath,\n 'local destination changed before download: file was created',\n )\n /* v8 ignore next -- staging cleanup is best-effort after a guarded publish succeeds. */\n await rm(tmpPath, { force: true }).catch(() => {})\n return\n }\n\n const backupPath = path.join(\n path.dirname(publishPath),\n makeReservedSyncTempFileName(path.basename(publishPath), randomUUID()),\n )\n let backupExists = false\n let removeBackup = false\n\n try {\n try {\n await rename(publishPath, backupPath)\n backupExists = true\n await localFileIoTestHooks.afterDownloadBackupRename?.(backupPath)\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) {\n throw new Error('local file changed before download: file missing')\n }\n throw err\n }\n\n const backupStats = await lstat(backupPath)\n assertSameScannedRegularFile(\n backupStats,\n { ...expectedDestination, absolutePath: backupPath },\n 'download',\n { compareChangeTime: false, platform: localFileIoTestHooks.platform },\n )\n await linkDownloadNoOverwrite(\n link,\n tmpPath,\n publishPath,\n 'local destination changed before download: file was created',\n )\n removeBackup = true\n /* v8 ignore next -- staging cleanup is best-effort after a guarded publish succeeds. */\n await rm(tmpPath, { force: true }).catch(() => {})\n } catch (err) {\n if (backupExists && !removeBackup) {\n await restoreBackupWithoutOverwrite(link, rm, backupPath, publishPath).catch(() => {})\n }\n throw err\n } finally {\n if (removeBackup) {\n /* v8 ignore next -- old destination cleanup is best-effort after publish succeeds. */\n await rm(backupPath, { force: true }).catch(() => {})\n }\n }\n}\n\nasync function linkDownloadNoOverwrite(\n link: typeof import('node:fs/promises')['link'],\n sourcePath: string,\n destPath: string,\n message: string,\n): Promise<void> {\n try {\n await link(sourcePath, destPath)\n } catch (err) {\n if (hasErrorCode(err, 'EEXIST')) throw new Error(message)\n throw err\n }\n}\n\nasync function restoreBackupWithoutOverwrite(\n link: typeof import('node:fs/promises')['link'],\n rm: typeof import('node:fs/promises')['rm'],\n backupPath: string,\n publishPath: string,\n): Promise<void> {\n try {\n await link(backupPath, publishPath)\n await rm(backupPath, { force: true })\n } catch (err) {\n if (hasErrorCode(err, 'EEXIST')) return\n throw err\n }\n}\n\nconst PRIVATE_DOWNLOAD_FILE_MODE = 0o600\nasync function replacementFileMode(filePath: string): Promise<number> {\n const { lstat } = await import('node:fs/promises')\n try {\n const stats = await lstat(filePath)\n return stats.isFile() ? stats.mode & 0o777 : PRIVATE_DOWNLOAD_FILE_MODE\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) return PRIVATE_DOWNLOAD_FILE_MODE\n throw err\n }\n}\n\n/**\n * Deletes a scanned local file under a sync root without re-resolving attacker-controlled parents.\n *\n * @param root - Local sync root.\n * @param scannedPath - Previously scanned local file metadata.\n *\n * @internal\n */\nexport async function deleteLocalFileInsideRoot(\n root: string,\n scannedPath: LocalSyncPath,\n): Promise<void> {\n if (root === '') {\n throw new Error('Local sync root required for filesystem mutation')\n }\n\n const { constants } = await import('node:fs')\n const { lstat, open, realpath, stat, unlink } = await import('node:fs/promises')\n const path = await import('node:path')\n const segments = safeRelativePathSegments(scannedPath.relativePath)\n const safeRoot = path.resolve(root)\n const rootStats = await lstat(safeRoot)\n if (rootStats.isSymbolicLink()) {\n throw new Error(`Refusing to access sync root through symlink: ${scannedPath.relativePath}`)\n }\n if (!rootStats.isDirectory()) {\n throw new Error(`Local sync root is not a directory: ${scannedPath.relativePath}`)\n }\n const rootRealPath = await realpath(safeRoot)\n const expectedPath = path.join(rootRealPath, ...segments)\n assertPathInsideRoot(rootRealPath, expectedPath, path)\n\n const scannerPath = path.resolve(scannedPath.absolutePath)\n if (scannerPath !== expectedPath) {\n throw new Error(`Refusing to delete outside sync root: ${scannedPath.relativePath}`)\n }\n\n const [parentRealPath, parentStats] = await Promise.all([\n realpath(path.dirname(expectedPath)),\n stat(path.dirname(expectedPath)),\n ])\n const finalPath = path.join(parentRealPath, path.basename(expectedPath))\n assertPathInsideRoot(rootRealPath, finalPath, path)\n\n const platform = (globalThis as { process?: { platform?: string } }).process?.platform\n let parentHandle: Awaited<ReturnType<typeof open>> | undefined\n let anchoredParentPath: string | undefined\n /* v8 ignore start -- Linux-only fd-relative path support is covered by Linux CI */\n if (\n platform === 'linux' &&\n constants.O_DIRECTORY !== undefined &&\n localFileIoTestHooks.disableProcFdAnchoring !== true\n ) {\n try {\n await localFileIoTestHooks.beforeLocalDeleteOpenParent?.(parentRealPath)\n parentHandle = await open(\n parentRealPath,\n constants.O_RDONLY | constants.O_DIRECTORY | noFollowFlag(constants),\n )\n anchoredParentPath = `/proc/self/fd/${parentHandle.fd}`\n } catch (err) {\n if (hasErrorCode(err, 'ELOOP') || hasErrorCode(err, 'ENOTDIR')) {\n throw new Error('unsafe local delete path: parent is not a directory')\n }\n throw err\n }\n }\n /* v8 ignore stop */\n\n try {\n const unlinkPath =\n anchoredParentPath === undefined\n ? finalPath\n : path.join(anchoredParentPath, path.basename(expectedPath))\n const stats = await lstat(unlinkPath)\n assertSameScannedRegularFile(stats, { ...scannedPath, absolutePath: unlinkPath }, 'delete', {\n platform: localFileIoTestHooks.platform,\n })\n await localFileIoTestHooks.beforeLocalDeleteUnlink?.(parentRealPath)\n if (\n anchoredParentPath === undefined &&\n localFileIoTestHooks.disableProcFdAnchoring === true &&\n parentRealPath !== rootRealPath\n ) {\n throw new Error('unsafe local delete path: stable parent handle unavailable for unlink')\n }\n\n if (anchoredParentPath === undefined) {\n const [parentRealPathBeforeUnlink, parentStatsBeforeUnlink] = await Promise.all([\n realpath(path.dirname(expectedPath)),\n stat(path.dirname(expectedPath)),\n ])\n if (\n parentRealPathBeforeUnlink !== parentRealPath ||\n !sameParentIdentity(parentStatsBeforeUnlink, parentStats)\n ) {\n throw new Error('unsafe local delete path: parent changed before unlink')\n }\n }\n const finalStats = await lstat(unlinkPath)\n assertSameScannedRegularFile(\n finalStats,\n { ...scannedPath, absolutePath: unlinkPath },\n 'delete',\n { platform: localFileIoTestHooks.platform },\n )\n await unlink(unlinkPath)\n } finally {\n /* v8 ignore next -- best-effort cleanup */\n await parentHandle?.close().catch(() => {})\n }\n}\n\nfunction sameParentIdentity(\n current: { readonly dev: number | bigint; readonly ino: number | bigint },\n expected: { readonly dev: number | bigint; readonly ino: number | bigint },\n): boolean {\n return current.dev === expected.dev && current.ino === expected.ino\n}\n\nasync function writeAll(\n handle: {\n write(\n buffer: Uint8Array,\n offset: number,\n length: number,\n position: number,\n ): Promise<{ readonly bytesWritten: number }>\n },\n data: Uint8Array,\n position: number,\n): Promise<void> {\n let offset = 0\n while (offset < data.byteLength) {\n const { bytesWritten } = await handle.write(\n data,\n offset,\n data.byteLength - offset,\n position + offset,\n )\n /* v8 ignore next -- defensive: FileHandle.write should progress for non-empty chunks. */\n if (bytesWritten <= 0) throw new Error('download write made no progress')\n offset += bytesWritten\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"local-file-io.js","names":[],"sources":["../../src/sync/local-file-io.ts"],"sourcesContent":["import { sanitizeErrorReason } from '../util/error-reason.ts'\nimport { readStreamChunkWithTimeout } from './b2-sha1-reader.ts'\nimport {\n assertDownloadPathSameDevice,\n createDownloadStagingDirectory,\n DOWNLOAD_STAGING_DIRECTORY_NAME,\n isDownloadStagingDirectorySegment,\n} from './download-staging.ts'\nimport { assertSameScannedRegularFile } from './local-file-identity.ts'\nimport {\n assertPathInsideRoot,\n hasErrorCode,\n makeReservedSyncTempFileName,\n noFollowFlag,\n safeRelativePathSegments,\n} from './path-safety.ts'\nimport { DEFAULT_SHA1_IDLE_TIMEOUT_MILLIS } from './sha1-options.ts'\nimport type { LocalSyncPath } from './types.ts'\n\ntype DeviceStat = { readonly dev: number | bigint }\ntype DeviceStatFn = (path: string) => Promise<DeviceStat>\n\nexport { DOWNLOAD_STAGING_DIRECTORY_NAME }\n\n/** @internal */\nexport const localFileIoTestHooks: {\n afterParentDirectoryValidated?: (path: string) => Promise<void> | void\n afterDownloadBackupRename?: (path: string) => Promise<void> | void\n afterTempFileCreated?: (path: string, stagingDirectory: string) => Promise<void> | void\n beforeDownloadPublish?: (path: string) => Promise<void> | void\n beforeFinalRename?: (path: string) => Promise<void> | void\n beforeLocalDeleteOpenParent?: (path: string) => Promise<void> | void\n beforeLocalDeleteUnlink?: (path: string) => Promise<void> | void\n beforeStagingMarkerWrite?: (path: string) => Promise<void> | void\n disableProcFdAnchoring?: boolean\n downloadStagingActivityEntryLimit?: number\n platform?: string\n statForDeviceCheck?: DeviceStatFn\n} = {}\n\ninterface ScannedLocalFileHandle {\n stat(): Promise<{\n isFile(): boolean\n readonly dev: number\n readonly ino: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n readonly size: number\n }>\n readFile(): Promise<Uint8Array>\n close(): Promise<void>\n}\n\n/**\n * Verifies that a previously scanned local file still points at the same regular file.\n *\n * @param path - Scanned local path and file identity.\n *\n * @internal\n */\nexport async function validateScannedLocalFile(path: LocalSyncPath): Promise<void> {\n const handle = await openValidatedScannedLocalFile(path)\n await handle.close()\n}\n\n/**\n * Reads a previously scanned local file while rejecting symlink swaps and metadata drift.\n *\n * @param path - Scanned local path and file identity.\n *\n * @returns The file bytes.\n *\n * @internal\n */\nexport async function readScannedLocalFile(path: LocalSyncPath): Promise<Uint8Array> {\n const handle = await openValidatedScannedLocalFile(path)\n try {\n const data = await handle.readFile()\n if (data.byteLength !== path.size) {\n throw new Error('local file changed before upload: size changed while reading')\n }\n return data\n } finally {\n await handle.close()\n }\n}\n\nasync function openValidatedScannedLocalFile(path: LocalSyncPath): Promise<ScannedLocalFileHandle> {\n const { constants } = await import('node:fs')\n const { open } = await import('node:fs/promises')\n const flags =\n constants.O_RDONLY |\n noFollowFlag(constants) |\n ((constants as { O_NONBLOCK?: number }).O_NONBLOCK ?? 0)\n const handle = await open(path.absolutePath, flags).catch((err: unknown) => {\n if (hasErrorCode(err, 'ELOOP')) {\n throw new Error('local file changed before upload: not a regular file')\n }\n throw new Error(\n `local file changed before upload: could not open scanned file: ${sanitizeErrorReason(err)}`,\n )\n })\n try {\n const stats = await handle.stat()\n assertSameScannedRegularFile(stats, path, 'upload', {\n platform: localFileIoTestHooks.platform,\n })\n return handle\n } catch (err) {\n await handle.close().catch(() => {})\n throw err\n }\n}\n\n/**\n * Writes bytes under a local sync root after path containment checks.\n *\n * @param root - Local sync root.\n * @param relPath - Destination path relative to the root.\n * @param data - Bytes to write.\n *\n * @internal\n */\nexport async function writeLocalFileInsideRoot(\n root: string,\n relPath: string,\n data: Uint8Array,\n): Promise<void> {\n await writeLocalStreamInsideRoot(\n root,\n relPath,\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(data)\n controller.close()\n },\n }),\n {\n expectedBytes: data.byteLength,\n idleTimeoutMillis: DEFAULT_SHA1_IDLE_TIMEOUT_MILLIS,\n },\n )\n}\n\n/**\n * Streams a B2 download under a local sync root with path, timeout, and size checks.\n *\n * @param root - Local sync root.\n * @param relPath - Destination path relative to the root.\n * @param body - Download body stream.\n * @param options - Expected byte count, idle timeout, and optional abort signal.\n *\n * @internal\n */\nexport async function writeLocalStreamInsideRoot(\n root: string,\n relPath: string,\n body: ReadableStream<Uint8Array>,\n options: {\n readonly expectedBytes: number\n readonly expectedDestination?: LocalSyncPath | null\n readonly idleTimeoutMillis: number\n readonly signal?: AbortSignal\n },\n): Promise<void> {\n const { constants } = await import('node:fs')\n const { link, lstat, mkdir, open, realpath, rename, rm, stat } = await import('node:fs/promises')\n const path = await import('node:path')\n const { randomUUID } = await import('node:crypto')\n assertValidExpectedBytes(options.expectedBytes)\n const segments = safeRelativePathSegments(relPath)\n if (isDownloadStagingDirectorySegment(segments[0])) {\n throw new Error(\n `unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is reserved for SDK download staging`,\n )\n }\n const rootRealPath = await realpath(root)\n\n let current = rootRealPath\n for (const segment of segments.slice(0, -1)) {\n current = path.join(current, segment)\n try {\n await mkdir(current)\n } catch (err) {\n if (!hasErrorCode(err, 'EEXIST')) throw err\n }\n const stats = await lstat(current)\n if (!stats.isDirectory()) {\n throw new Error('unsafe local destination path: parent is not a directory')\n }\n await localFileIoTestHooks.afterParentDirectoryValidated?.(current)\n }\n\n const destPath = path.join(rootRealPath, ...segments)\n assertPathInsideRoot(rootRealPath, destPath, path)\n\n const parentRealPath = await realpath(path.dirname(destPath))\n const finalPath = path.join(parentRealPath, path.basename(destPath))\n assertPathInsideRoot(rootRealPath, finalPath, path)\n try {\n const targetStats = await lstat(finalPath)\n if (targetStats.isSymbolicLink()) {\n throw new Error('unsafe local destination path: target is a symbolic link')\n }\n if (targetStats.isFile() && targetStats.nlink > 1) {\n throw new Error('unsafe local destination path: target has multiple hard links')\n }\n } catch (err) {\n if (!hasErrorCode(err, 'ENOENT')) throw err\n }\n const statForDeviceCheck = localFileIoTestHooks.statForDeviceCheck ?? stat\n await assertDownloadPathSameDevice(\n rootRealPath,\n parentRealPath,\n statForDeviceCheck,\n 'unsafe local destination path: cannot publish download across filesystems',\n )\n\n let parentHandle: Awaited<ReturnType<typeof open>> | undefined\n let anchoredParentPath: string | undefined\n const platform = (globalThis as { process?: { platform?: string } }).process?.platform\n /* v8 ignore start -- Linux-only fd-relative path support is covered by Linux CI */\n if (\n platform === 'linux' &&\n constants.O_DIRECTORY !== undefined &&\n localFileIoTestHooks.disableProcFdAnchoring !== true\n ) {\n try {\n parentHandle = await open(\n parentRealPath,\n constants.O_RDONLY | constants.O_DIRECTORY | noFollowFlag(constants),\n )\n anchoredParentPath = `/proc/self/fd/${parentHandle.fd}`\n } catch (err) {\n if (hasErrorCode(err, 'ELOOP') || hasErrorCode(err, 'ENOTDIR')) {\n throw new Error('unsafe local destination path: parent is not a directory')\n }\n throw err\n }\n }\n /* v8 ignore stop */\n const finalName = path.basename(destPath)\n const finalWritePath = path.join(anchoredParentPath ?? parentRealPath, finalName)\n let publishMode: number\n try {\n publishMode = await replacementFileMode(finalPath)\n } catch (err) {\n /* v8 ignore next -- best-effort close during setup failure */\n await parentHandle?.close().catch(() => {})\n throw err\n }\n let stagingDirectory: string\n try {\n stagingDirectory = await createDownloadStagingDirectory(\n rootRealPath,\n path,\n randomUUID,\n statForDeviceCheck,\n localFileIoTestHooks.beforeStagingMarkerWrite,\n localFileIoTestHooks.downloadStagingActivityEntryLimit,\n )\n } catch (err) {\n /* v8 ignore next -- best-effort close during setup failure */\n await parentHandle?.close().catch(() => {})\n throw err\n }\n const tmpPath = path.join(stagingDirectory, `.b2sdk-${randomUUID()}.partial`)\n let handle: Awaited<ReturnType<typeof open>> | undefined\n try {\n handle = await open(\n tmpPath,\n constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollowFlag(constants),\n PRIVATE_DOWNLOAD_FILE_MODE,\n )\n /* v8 ignore next -- best-effort chmod */\n await handle.chmod(PRIVATE_DOWNLOAD_FILE_MODE).catch(() => {})\n await localFileIoTestHooks.afterTempFileCreated?.(tmpPath, stagingDirectory)\n /* v8 ignore start -- defensive cleanup before the main write try/finally exists */\n } catch (err) {\n await parentHandle?.close().catch(() => {})\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n throw err\n }\n /* v8 ignore stop */\n try {\n const tmpRealPath = await realpath(tmpPath)\n assertPathInsideRoot(stagingDirectory, tmpRealPath, path)\n } catch (err) {\n /* v8 ignore next -- best-effort cleanup */\n await handle?.close().catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await rm(tmpPath, { force: true }).catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await parentHandle?.close().catch(() => {})\n throw err\n }\n const writeHandle = handle\n const reader = body.getReader()\n let completed = false\n try {\n let bytesWritten = 0\n while (true) {\n const { done, value } = await readStreamChunkWithTimeout(\n reader,\n options.idleTimeoutMillis,\n `download read stalled for ${options.idleTimeoutMillis} ms`,\n options.signal,\n )\n if (done) break\n if (bytesWritten + value.byteLength > options.expectedBytes) {\n throw new Error(`download read exceeded ${options.expectedBytes} byte limit`)\n }\n await writeAll(writeHandle, value, bytesWritten)\n bytesWritten += value.byteLength\n }\n if (bytesWritten !== options.expectedBytes) {\n throw new Error(\n `download read ended after ${bytesWritten} bytes, expected ${options.expectedBytes}`,\n )\n }\n if (publishMode !== PRIVATE_DOWNLOAD_FILE_MODE) {\n /* v8 ignore next -- best-effort mode preservation */\n await writeHandle.chmod(publishMode).catch(() => {})\n }\n await writeHandle.close()\n handle = undefined\n\n const [parentRealPathBeforeRename, parentStatsBeforeRename] = await Promise.all([\n realpath(path.dirname(destPath)),\n stat(path.dirname(destPath)),\n ])\n const finalPathBeforeRename = path.join(parentRealPathBeforeRename, path.basename(destPath))\n assertPathInsideRoot(rootRealPath, finalPathBeforeRename, path)\n await localFileIoTestHooks.beforeFinalRename?.(parentRealPathBeforeRename)\n let publishPath = finalWritePath\n if (anchoredParentPath === undefined) {\n const [parentRealPathAfterHook, parentStatsAfterHook] = await Promise.all([\n realpath(path.dirname(destPath)),\n stat(path.dirname(destPath)),\n ])\n if (\n parentRealPathAfterHook !== parentRealPathBeforeRename ||\n !sameParentIdentity(parentStatsAfterHook, parentStatsBeforeRename)\n ) {\n throw new Error('unsafe local destination path: parent changed before final publish')\n }\n publishPath = path.join(parentRealPathAfterHook, path.basename(destPath))\n assertPathInsideRoot(rootRealPath, publishPath, path)\n }\n await publishDownload(\n lstat,\n link,\n path,\n randomUUID,\n rename,\n rm,\n tmpPath,\n publishPath,\n options.expectedDestination,\n )\n completed = true\n } catch (err) {\n /* v8 ignore next -- best-effort cleanup */\n void reader.cancel(err).catch(() => {})\n throw err\n } finally {\n reader.releaseLock()\n if (!completed) {\n /* v8 ignore next -- best-effort cleanup */\n await handle?.close().catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await rm(tmpPath, { force: true }).catch(() => {})\n }\n /* v8 ignore next -- best-effort cleanup */\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await parentHandle?.close().catch(() => {})\n }\n}\n\nfunction assertValidExpectedBytes(expectedBytes: number): void {\n if (!Number.isSafeInteger(expectedBytes) || expectedBytes < 0) {\n throw new Error('download expectedBytes must be a non-negative safe integer')\n }\n}\n\nasync function assertExpectedDownloadDestination(\n lstat: typeof import('node:fs/promises')['lstat'],\n finalPath: string,\n expectedDestination: LocalSyncPath | null | undefined,\n): Promise<void> {\n if (expectedDestination === undefined) return\n\n try {\n const stats = await lstat(finalPath)\n if (expectedDestination === null) {\n throw new Error('local destination changed before download: file was created')\n }\n assertSameScannedRegularFile(\n stats,\n { ...expectedDestination, absolutePath: finalPath },\n 'download',\n { platform: localFileIoTestHooks.platform },\n )\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) {\n if (expectedDestination === null) return\n throw new Error('local file changed before download: file missing')\n }\n throw err\n }\n}\n\nasync function publishDownload(\n lstat: typeof import('node:fs/promises')['lstat'],\n link: typeof import('node:fs/promises')['link'],\n path: typeof import('node:path'),\n randomUUID: () => string,\n rename: typeof import('node:fs/promises')['rename'],\n rm: typeof import('node:fs/promises')['rm'],\n tmpPath: string,\n publishPath: string,\n expectedDestination: LocalSyncPath | null | undefined,\n): Promise<void> {\n await assertExpectedDownloadDestination(lstat, publishPath, expectedDestination)\n await localFileIoTestHooks.beforeDownloadPublish?.(publishPath)\n\n if (expectedDestination === undefined) {\n await rename(tmpPath, publishPath)\n return\n }\n\n if (expectedDestination === null) {\n await linkDownloadNoOverwrite(\n link,\n tmpPath,\n publishPath,\n 'local destination changed before download: file was created',\n )\n /* v8 ignore next -- staging cleanup is best-effort after a guarded publish succeeds. */\n await rm(tmpPath, { force: true }).catch(() => {})\n return\n }\n\n const backupPath = path.join(\n path.dirname(publishPath),\n makeReservedSyncTempFileName(path.basename(publishPath), randomUUID()),\n )\n let backupExists = false\n let removeBackup = false\n\n try {\n try {\n await rename(publishPath, backupPath)\n backupExists = true\n await localFileIoTestHooks.afterDownloadBackupRename?.(backupPath)\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) {\n throw new Error('local file changed before download: file missing')\n }\n throw err\n }\n\n const backupStats = await lstat(backupPath)\n assertSameScannedRegularFile(\n backupStats,\n { ...expectedDestination, absolutePath: backupPath },\n 'download',\n { compareChangeTime: false, platform: localFileIoTestHooks.platform },\n )\n await linkDownloadNoOverwrite(\n link,\n tmpPath,\n publishPath,\n 'local destination changed before download: file was created',\n )\n removeBackup = true\n /* v8 ignore next -- staging cleanup is best-effort after a guarded publish succeeds. */\n await rm(tmpPath, { force: true }).catch(() => {})\n } catch (err) {\n if (backupExists && !removeBackup) {\n await restoreBackupWithoutOverwrite(link, rm, backupPath, publishPath).catch(() => {})\n }\n throw err\n } finally {\n if (removeBackup) {\n /* v8 ignore next -- old destination cleanup is best-effort after publish succeeds. */\n await rm(backupPath, { force: true }).catch(() => {})\n }\n }\n}\n\nasync function linkDownloadNoOverwrite(\n link: typeof import('node:fs/promises')['link'],\n sourcePath: string,\n destPath: string,\n message: string,\n): Promise<void> {\n try {\n await link(sourcePath, destPath)\n } catch (err) {\n if (hasErrorCode(err, 'EEXIST')) throw new Error(message)\n throw err\n }\n}\n\nasync function restoreBackupWithoutOverwrite(\n link: typeof import('node:fs/promises')['link'],\n rm: typeof import('node:fs/promises')['rm'],\n backupPath: string,\n publishPath: string,\n): Promise<void> {\n try {\n await link(backupPath, publishPath)\n await rm(backupPath, { force: true })\n } catch (err) {\n if (hasErrorCode(err, 'EEXIST')) return\n throw err\n }\n}\n\nconst PRIVATE_DOWNLOAD_FILE_MODE = 0o600\nasync function replacementFileMode(filePath: string): Promise<number> {\n const { lstat } = await import('node:fs/promises')\n try {\n const stats = await lstat(filePath)\n return stats.isFile() ? stats.mode & 0o777 : PRIVATE_DOWNLOAD_FILE_MODE\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) return PRIVATE_DOWNLOAD_FILE_MODE\n throw err\n }\n}\n\n/**\n * Deletes a scanned local file under a sync root without re-resolving attacker-controlled parents.\n *\n * @param root - Local sync root.\n * @param scannedPath - Previously scanned local file metadata.\n *\n * @internal\n */\nexport async function deleteLocalFileInsideRoot(\n root: string,\n scannedPath: LocalSyncPath,\n): Promise<void> {\n if (root === '') {\n throw new Error('Local sync root required for filesystem mutation')\n }\n\n const { constants } = await import('node:fs')\n const { lstat, open, realpath, stat, unlink } = await import('node:fs/promises')\n const path = await import('node:path')\n const segments = safeRelativePathSegments(scannedPath.relativePath)\n const safeRoot = path.resolve(root)\n const rootStats = await lstat(safeRoot)\n if (rootStats.isSymbolicLink()) {\n throw new Error(`Refusing to access sync root through symlink: ${scannedPath.relativePath}`)\n }\n if (!rootStats.isDirectory()) {\n throw new Error(`Local sync root is not a directory: ${scannedPath.relativePath}`)\n }\n const rootRealPath = await realpath(safeRoot)\n const expectedPath = path.join(rootRealPath, ...segments)\n assertPathInsideRoot(rootRealPath, expectedPath, path)\n\n const scannerPath = path.resolve(scannedPath.absolutePath)\n if (scannerPath !== expectedPath) {\n throw new Error(`Refusing to delete outside sync root: ${scannedPath.relativePath}`)\n }\n\n const [parentRealPath, parentStats] = await Promise.all([\n realpath(path.dirname(expectedPath)),\n stat(path.dirname(expectedPath)),\n ])\n const finalPath = path.join(parentRealPath, path.basename(expectedPath))\n assertPathInsideRoot(rootRealPath, finalPath, path)\n\n const platform = (globalThis as { process?: { platform?: string } }).process?.platform\n let parentHandle: Awaited<ReturnType<typeof open>> | undefined\n let anchoredParentPath: string | undefined\n /* v8 ignore start -- Linux-only fd-relative path support is covered by Linux CI */\n if (\n platform === 'linux' &&\n constants.O_DIRECTORY !== undefined &&\n localFileIoTestHooks.disableProcFdAnchoring !== true\n ) {\n try {\n await localFileIoTestHooks.beforeLocalDeleteOpenParent?.(parentRealPath)\n parentHandle = await open(\n parentRealPath,\n constants.O_RDONLY | constants.O_DIRECTORY | noFollowFlag(constants),\n )\n anchoredParentPath = `/proc/self/fd/${parentHandle.fd}`\n } catch (err) {\n if (hasErrorCode(err, 'ELOOP') || hasErrorCode(err, 'ENOTDIR')) {\n throw new Error('unsafe local delete path: parent is not a directory')\n }\n throw err\n }\n }\n /* v8 ignore stop */\n\n try {\n const unlinkPath =\n anchoredParentPath === undefined\n ? finalPath\n : path.join(anchoredParentPath, path.basename(expectedPath))\n const stats = await lstat(unlinkPath)\n assertSameScannedRegularFile(stats, { ...scannedPath, absolutePath: unlinkPath }, 'delete', {\n platform: localFileIoTestHooks.platform,\n })\n await localFileIoTestHooks.beforeLocalDeleteUnlink?.(parentRealPath)\n if (\n anchoredParentPath === undefined &&\n localFileIoTestHooks.disableProcFdAnchoring === true &&\n parentRealPath !== rootRealPath\n ) {\n throw new Error('unsafe local delete path: stable parent handle unavailable for unlink')\n }\n\n if (anchoredParentPath === undefined) {\n const [parentRealPathBeforeUnlink, parentStatsBeforeUnlink] = await Promise.all([\n realpath(path.dirname(expectedPath)),\n stat(path.dirname(expectedPath)),\n ])\n if (\n parentRealPathBeforeUnlink !== parentRealPath ||\n !sameParentIdentity(parentStatsBeforeUnlink, parentStats)\n ) {\n throw new Error('unsafe local delete path: parent changed before unlink')\n }\n }\n const finalStats = await lstat(unlinkPath)\n assertSameScannedRegularFile(\n finalStats,\n { ...scannedPath, absolutePath: unlinkPath },\n 'delete',\n { platform: localFileIoTestHooks.platform },\n )\n await unlink(unlinkPath)\n } finally {\n /* v8 ignore next -- best-effort cleanup */\n await parentHandle?.close().catch(() => {})\n }\n}\n\nfunction sameParentIdentity(\n current: { readonly dev: number | bigint; readonly ino: number | bigint },\n expected: { readonly dev: number | bigint; readonly ino: number | bigint },\n): boolean {\n return current.dev === expected.dev && current.ino === expected.ino\n}\n\nasync function writeAll(\n handle: {\n write(\n buffer: Uint8Array,\n offset: number,\n length: number,\n position: number,\n ): Promise<{ readonly bytesWritten: number }>\n },\n data: Uint8Array,\n position: number,\n): Promise<void> {\n let offset = 0\n while (offset < data.byteLength) {\n const { bytesWritten } = await handle.write(\n data,\n offset,\n data.byteLength - offset,\n position + offset,\n )\n /* v8 ignore next -- defensive: FileHandle.write should progress for non-empty chunks. */\n if (bytesWritten <= 0) throw new Error('download write made no progress')\n offset += bytesWritten\n }\n}\n"],"mappings":"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"}
|
package/dist/sync/local-sha1.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const require_hash = require("../streams/hash.cjs");
|
|
2
|
-
const require_to_error = require("../util/to-error.cjs");
|
|
3
2
|
const require_error_reason = require("../util/error-reason.cjs");
|
|
4
3
|
const require_local_file_identity = require("./local-file-identity.cjs");
|
|
5
4
|
const require_sha1_options = require("./sha1-options.cjs");
|
|
@@ -15,16 +14,6 @@ function formatHashError(error) {
|
|
|
15
14
|
return require_error_reason.sanitizeErrorReason(error);
|
|
16
15
|
}
|
|
17
16
|
/**
|
|
18
|
-
* Returns whether an error represents an abort.
|
|
19
|
-
*
|
|
20
|
-
* @param err - Unknown thrown value.
|
|
21
|
-
*
|
|
22
|
-
* @returns True for AbortError values.
|
|
23
|
-
*/
|
|
24
|
-
function isAbortError(err) {
|
|
25
|
-
return require_to_error.toError(err).name === "AbortError";
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
17
|
* Reads a local file and computes its SHA-1 digest with non-regular-file rejection,
|
|
29
18
|
* scanned-size bounds, abort support, and an idle/no-progress timeout.
|
|
30
19
|
*
|
|
@@ -49,10 +38,13 @@ async function readLocalSha1File(path, signal, options = {}) {
|
|
|
49
38
|
}
|
|
50
39
|
try {
|
|
51
40
|
signal?.throwIfAborted();
|
|
52
|
-
|
|
41
|
+
const preOpenStat = await withTimeout(lstat(path.absolutePath), timeoutMillis, "sha1 file status");
|
|
42
|
+
require_local_file_identity.assertSameScannedRegularFile(preOpenStat, path, "sha1 comparison");
|
|
53
43
|
file = await openWithTimeout(open(path.absolutePath, flags), timeoutMillis);
|
|
54
|
-
|
|
55
|
-
require_local_file_identity.assertSameScannedRegularFile(
|
|
44
|
+
const postOpenStat = await withTimeout(lstat(path.absolutePath), timeoutMillis, "sha1 file status");
|
|
45
|
+
require_local_file_identity.assertSameScannedRegularFile(postOpenStat, path, "sha1 comparison");
|
|
46
|
+
const stat = await withTimeout(file.stat(), timeoutMillis, "sha1 file status");
|
|
47
|
+
require_local_file_identity.assertSameScannedRegularFile(stat, path, "sha1 comparison");
|
|
56
48
|
stream = file.createReadStream({
|
|
57
49
|
...path.size > 0 ? {
|
|
58
50
|
start: 0,
|
|
@@ -113,7 +105,6 @@ async function openWithTimeout(promise, timeoutMillis) {
|
|
|
113
105
|
/* v8 ignore stop */
|
|
114
106
|
//#endregion
|
|
115
107
|
exports.formatHashError = formatHashError;
|
|
116
|
-
exports.isAbortError = isAbortError;
|
|
117
108
|
exports.readLocalSha1File = readLocalSha1File;
|
|
118
109
|
|
|
119
110
|
//# sourceMappingURL=local-sha1.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-sha1.cjs","names":[],"sources":["../../src/sync/local-sha1.ts"],"sourcesContent":["import { IncrementalSha1 } from '../streams/hash.ts'\nimport { sanitizeErrorReason } from '../util/error-reason.ts'\nimport {
|
|
1
|
+
{"version":3,"file":"local-sha1.cjs","names":[],"sources":["../../src/sync/local-sha1.ts"],"sourcesContent":["import { IncrementalSha1 } from '../streams/hash.ts'\nimport { sanitizeErrorReason } from '../util/error-reason.ts'\nimport { assertSameScannedRegularFile } from './local-file-identity.ts'\nimport { normalizeSha1TimeoutMillis } from './sha1-options.ts'\nimport type { LocalSyncPath } from './types.ts'\n\n/** Options for reading a local file SHA-1 digest. */\nexport interface LocalSha1ReadOptions {\n /**\n * Maximum milliseconds to allow without filesystem progress while hashing.\n * Defaults to 30 seconds.\n */\n readonly timeoutMillis?: number\n}\n\n/** Reads a local file and returns its SHA-1 digest, or null when unavailable. */\nexport type LocalSha1Reader = (\n path: LocalSyncPath,\n signal?: AbortSignal,\n options?: LocalSha1ReadOptions,\n) => Promise<string | null>\n\n/**\n * Formats a hash error for public sync events without leaking filesystem paths.\n *\n * @param error - Error thrown while hashing.\n *\n * @returns A sanitized reason suitable for event messages.\n */\nexport function formatHashError(error: Error): string {\n return sanitizeErrorReason(error)\n}\n\n/**\n * Reads a local file and computes its SHA-1 digest with non-regular-file rejection,\n * scanned-size bounds, abort support, and an idle/no-progress timeout.\n *\n * @param path - Local sync path to hash.\n * @param signal - Optional abort signal.\n * @param options - Optional idle timeout override.\n *\n * @returns The lowercase SHA-1 digest of the file bytes.\n */\nexport async function readLocalSha1File(\n path: LocalSyncPath,\n signal?: AbortSignal,\n options: LocalSha1ReadOptions = {},\n): Promise<string> {\n const { constants } = await import('node:fs')\n const { lstat, open } = await import('node:fs/promises')\n const timeoutMillis = normalizeSha1TimeoutMillis(options.timeoutMillis)\n const flags = constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0)\n const hash = new IncrementalSha1()\n let stream: (AsyncIterable<Uint8Array> & { destroy(error?: Error): void }) | undefined\n let file: Awaited<ReturnType<typeof open>> | undefined\n let timeout: ReturnType<typeof setTimeout> | undefined\n\n function armTimeout(onTimeout: () => void): void {\n if (timeout !== undefined) clearTimeout(timeout)\n timeout = setTimeout(onTimeout, timeoutMillis)\n }\n\n try {\n signal?.throwIfAborted()\n\n const preOpenStat = await withTimeout(\n lstat(path.absolutePath),\n timeoutMillis,\n 'sha1 file status',\n )\n assertSameScannedRegularFile(preOpenStat, path, 'sha1 comparison')\n\n file = await openWithTimeout(open(path.absolutePath, flags), timeoutMillis)\n\n const postOpenStat = await withTimeout(\n lstat(path.absolutePath),\n timeoutMillis,\n 'sha1 file status',\n )\n assertSameScannedRegularFile(postOpenStat, path, 'sha1 comparison')\n\n const stat = await withTimeout(file.stat(), timeoutMillis, 'sha1 file status')\n assertSameScannedRegularFile(stat, path, 'sha1 comparison')\n\n stream = file.createReadStream({\n ...(path.size > 0 ? { start: 0, end: path.size - 1 } : {}),\n ...(signal !== undefined ? { signal } : {}),\n })\n\n let bytesRead = 0\n /* v8 ignore next 3 -- idle-timeout firing is timing-dependent in filesystem tests */\n armTimeout(() => {\n stream?.destroy(new Error(`sha1 read stalled for ${timeoutMillis} ms`))\n })\n for await (const chunk of stream) {\n bytesRead += chunk.byteLength\n await hash.update(chunk)\n /* v8 ignore next 3 -- idle-timeout firing is timing-dependent in filesystem tests */\n armTimeout(() => {\n stream?.destroy(new Error(`sha1 read stalled for ${timeoutMillis} ms`))\n })\n }\n\n /* v8 ignore next -- defensive TOCTOU guard after the bounded stream completes */\n if (bytesRead !== path.size) throw new Error('file changed during sha1 comparison')\n return hash.digest()\n } finally {\n if (timeout !== undefined) clearTimeout(timeout)\n stream?.destroy()\n await file?.close().catch(() => {})\n }\n}\n\n/* v8 ignore start -- defensive stale-filesystem stall handling is not portable to trigger */\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMillis: number,\n operation: string,\n): Promise<T> {\n let timeout: ReturnType<typeof setTimeout> | undefined\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => {\n reject(new Error(`${operation} stalled for ${timeoutMillis} ms`))\n }, timeoutMillis)\n }),\n ])\n } finally {\n if (timeout !== undefined) clearTimeout(timeout)\n }\n}\n\nasync function openWithTimeout<T extends { close(): Promise<void> }>(\n promise: Promise<T>,\n timeoutMillis: number,\n): Promise<T> {\n let timedOut = false\n const tracked = promise.then(\n (file) => {\n if (timedOut) void file.close().catch(() => {})\n return file\n },\n (err: unknown) => {\n throw err\n },\n )\n\n try {\n return await withTimeout(tracked, timeoutMillis, 'sha1 file open')\n } catch (err) {\n timedOut = true\n throw err\n }\n}\n/* v8 ignore stop */\n"],"mappings":";;;;;;;;;;;;AA6BA,SAAgB,gBAAgB,OAAsB;CACpD,OAAO,qBAAA,oBAAoB,KAAK;AAClC;;;;;;;;;;;AAYA,eAAsB,kBACpB,MACA,QACA,UAAgC,CAAC,GAChB;CACjB,MAAM,EAAE,cAAc,MAAM,OAAO;CACnC,MAAM,EAAE,OAAO,SAAS,MAAM,OAAO;CACrC,MAAM,gBAAgB,qBAAA,2BAA2B,QAAQ,aAAa;CACtE,MAAM,QAAQ,UAAU,YAAY,UAAU,cAAc,MAAM,UAAU,cAAc;CAC1F,MAAM,OAAO,IAAI,aAAA,gBAAgB;CACjC,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,SAAS,WAAW,WAA6B;EAC/C,IAAI,YAAY,KAAA,GAAW,aAAa,OAAO;EAC/C,UAAU,WAAW,WAAW,aAAa;CAC/C;CAEA,IAAI;EACF,QAAQ,eAAe;EAEvB,MAAM,cAAc,MAAM,YACxB,MAAM,KAAK,YAAY,GACvB,eACA,kBACF;EACA,4BAAA,6BAA6B,aAAa,MAAM,iBAAiB;EAEjE,OAAO,MAAM,gBAAgB,KAAK,KAAK,cAAc,KAAK,GAAG,aAAa;EAE1E,MAAM,eAAe,MAAM,YACzB,MAAM,KAAK,YAAY,GACvB,eACA,kBACF;EACA,4BAAA,6BAA6B,cAAc,MAAM,iBAAiB;EAElE,MAAM,OAAO,MAAM,YAAY,KAAK,KAAK,GAAG,eAAe,kBAAkB;EAC7E,4BAAA,6BAA6B,MAAM,MAAM,iBAAiB;EAE1D,SAAS,KAAK,iBAAiB;GAC7B,GAAI,KAAK,OAAO,IAAI;IAAE,OAAO;IAAG,KAAK,KAAK,OAAO;GAAE,IAAI,CAAC;GACxD,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;EAC3C,CAAC;EAED,IAAI,YAAY;;EAEhB,iBAAiB;GACf,QAAQ,wBAAQ,IAAI,MAAM,yBAAyB,cAAc,IAAI,CAAC;EACxE,CAAC;EACD,WAAW,MAAM,SAAS,QAAQ;GAChC,aAAa,MAAM;GACnB,MAAM,KAAK,OAAO,KAAK;;GAEvB,iBAAiB;IACf,QAAQ,wBAAQ,IAAI,MAAM,yBAAyB,cAAc,IAAI,CAAC;GACxE,CAAC;EACH;;EAGA,IAAI,cAAc,KAAK,MAAM,MAAM,IAAI,MAAM,qCAAqC;EAClF,OAAO,KAAK,OAAO;CACrB,UAAU;EACR,IAAI,YAAY,KAAA,GAAW,aAAa,OAAO;EAC/C,QAAQ,QAAQ;EAChB,MAAM,MAAM,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;CACpC;AACF;;AAGA,eAAe,YACb,SACA,eACA,WACY;CACZ,IAAI;CACJ,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,CACxB,SACA,IAAI,SAAgB,GAAG,WAAW;GAChC,UAAU,iBAAiB;IACzB,uBAAO,IAAI,MAAM,GAAG,UAAU,eAAe,cAAc,IAAI,CAAC;GAClE,GAAG,aAAa;EAClB,CAAC,CACH,CAAC;CACH,UAAU;EACR,IAAI,YAAY,KAAA,GAAW,aAAa,OAAO;CACjD;AACF;AAEA,eAAe,gBACb,SACA,eACY;CACZ,IAAI,WAAW;CACf,MAAM,UAAU,QAAQ,MACrB,SAAS;EACR,IAAI,UAAU,KAAU,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EAC9C,OAAO;CACT,IACC,QAAiB;EAChB,MAAM;CACR,CACF;CAEA,IAAI;EACF,OAAO,MAAM,YAAY,SAAS,eAAe,gBAAgB;CACnE,SAAS,KAAK;EACZ,WAAW;EACX,MAAM;CACR;AACF"}
|
|
@@ -17,14 +17,6 @@ export type LocalSha1Reader = (path: LocalSyncPath, signal?: AbortSignal, option
|
|
|
17
17
|
* @returns A sanitized reason suitable for event messages.
|
|
18
18
|
*/
|
|
19
19
|
export declare function formatHashError(error: Error): string;
|
|
20
|
-
/**
|
|
21
|
-
* Returns whether an error represents an abort.
|
|
22
|
-
*
|
|
23
|
-
* @param err - Unknown thrown value.
|
|
24
|
-
*
|
|
25
|
-
* @returns True for AbortError values.
|
|
26
|
-
*/
|
|
27
|
-
export declare function isAbortError(err: unknown): boolean;
|
|
28
20
|
/**
|
|
29
21
|
* Reads a local file and computes its SHA-1 digest with non-regular-file rejection,
|
|
30
22
|
* scanned-size bounds, abort support, and an idle/no-progress timeout.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-sha1.d.ts","sourceRoot":"","sources":["../../src/sync/local-sha1.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"local-sha1.d.ts","sourceRoot":"","sources":["../../src/sync/local-sha1.js"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,qDAAqD;AACrD,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,iFAAiF;AACjF,MAAM,MAAM,eAAe,GAAG,CAC5B,IAAI,EAAE,aAAa,EACnB,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;AAE3B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAEpD;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,aAAa,EACnB,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,MAAM,CAAC,CAgEjB"}
|
|
@@ -17,14 +17,6 @@ export type LocalSha1Reader = (path: LocalSyncPath, signal?: AbortSignal, option
|
|
|
17
17
|
* @returns A sanitized reason suitable for event messages.
|
|
18
18
|
*/
|
|
19
19
|
export declare function formatHashError(error: Error): string;
|
|
20
|
-
/**
|
|
21
|
-
* Returns whether an error represents an abort.
|
|
22
|
-
*
|
|
23
|
-
* @param err - Unknown thrown value.
|
|
24
|
-
*
|
|
25
|
-
* @returns True for AbortError values.
|
|
26
|
-
*/
|
|
27
|
-
export declare function isAbortError(err: unknown): boolean;
|
|
28
20
|
/**
|
|
29
21
|
* Reads a local file and computes its SHA-1 digest with non-regular-file rejection,
|
|
30
22
|
* scanned-size bounds, abort support, and an idle/no-progress timeout.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-sha1.d.ts","sourceRoot":"","sources":["../../src/sync/local-sha1.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"local-sha1.d.ts","sourceRoot":"","sources":["../../src/sync/local-sha1.js"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,qDAAqD;AACrD,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,iFAAiF;AACjF,MAAM,MAAM,eAAe,GAAG,CAC5B,IAAI,EAAE,aAAa,EACnB,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;AAE3B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAEpD;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,aAAa,EACnB,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,MAAM,CAAC,CAgEjB"}
|
package/dist/sync/local-sha1.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { IncrementalSha1 } from "../streams/hash.js";
|
|
2
|
-
import { toError } from "../util/to-error.js";
|
|
3
2
|
import { sanitizeErrorReason } from "../util/error-reason.js";
|
|
4
3
|
import { assertSameScannedRegularFile } from "./local-file-identity.js";
|
|
5
4
|
import { normalizeSha1TimeoutMillis } from "./sha1-options.js";
|
|
@@ -15,16 +14,6 @@ function formatHashError(error) {
|
|
|
15
14
|
return sanitizeErrorReason(error);
|
|
16
15
|
}
|
|
17
16
|
/**
|
|
18
|
-
* Returns whether an error represents an abort.
|
|
19
|
-
*
|
|
20
|
-
* @param err - Unknown thrown value.
|
|
21
|
-
*
|
|
22
|
-
* @returns True for AbortError values.
|
|
23
|
-
*/
|
|
24
|
-
function isAbortError(err) {
|
|
25
|
-
return toError(err).name === "AbortError";
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
17
|
* Reads a local file and computes its SHA-1 digest with non-regular-file rejection,
|
|
29
18
|
* scanned-size bounds, abort support, and an idle/no-progress timeout.
|
|
30
19
|
*
|
|
@@ -49,10 +38,13 @@ async function readLocalSha1File(path, signal, options = {}) {
|
|
|
49
38
|
}
|
|
50
39
|
try {
|
|
51
40
|
signal?.throwIfAborted();
|
|
52
|
-
|
|
41
|
+
const preOpenStat = await withTimeout(lstat(path.absolutePath), timeoutMillis, "sha1 file status");
|
|
42
|
+
assertSameScannedRegularFile(preOpenStat, path, "sha1 comparison");
|
|
53
43
|
file = await openWithTimeout(open(path.absolutePath, flags), timeoutMillis);
|
|
54
|
-
|
|
55
|
-
assertSameScannedRegularFile(
|
|
44
|
+
const postOpenStat = await withTimeout(lstat(path.absolutePath), timeoutMillis, "sha1 file status");
|
|
45
|
+
assertSameScannedRegularFile(postOpenStat, path, "sha1 comparison");
|
|
46
|
+
const stat = await withTimeout(file.stat(), timeoutMillis, "sha1 file status");
|
|
47
|
+
assertSameScannedRegularFile(stat, path, "sha1 comparison");
|
|
56
48
|
stream = file.createReadStream({
|
|
57
49
|
...path.size > 0 ? {
|
|
58
50
|
start: 0,
|
|
@@ -112,6 +104,6 @@ async function openWithTimeout(promise, timeoutMillis) {
|
|
|
112
104
|
}
|
|
113
105
|
/* v8 ignore stop */
|
|
114
106
|
//#endregion
|
|
115
|
-
export { formatHashError,
|
|
107
|
+
export { formatHashError, readLocalSha1File };
|
|
116
108
|
|
|
117
109
|
//# sourceMappingURL=local-sha1.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-sha1.js","names":[],"sources":["../../src/sync/local-sha1.ts"],"sourcesContent":["import { IncrementalSha1 } from '../streams/hash.ts'\nimport { sanitizeErrorReason } from '../util/error-reason.ts'\nimport {
|
|
1
|
+
{"version":3,"file":"local-sha1.js","names":[],"sources":["../../src/sync/local-sha1.ts"],"sourcesContent":["import { IncrementalSha1 } from '../streams/hash.ts'\nimport { sanitizeErrorReason } from '../util/error-reason.ts'\nimport { assertSameScannedRegularFile } from './local-file-identity.ts'\nimport { normalizeSha1TimeoutMillis } from './sha1-options.ts'\nimport type { LocalSyncPath } from './types.ts'\n\n/** Options for reading a local file SHA-1 digest. */\nexport interface LocalSha1ReadOptions {\n /**\n * Maximum milliseconds to allow without filesystem progress while hashing.\n * Defaults to 30 seconds.\n */\n readonly timeoutMillis?: number\n}\n\n/** Reads a local file and returns its SHA-1 digest, or null when unavailable. */\nexport type LocalSha1Reader = (\n path: LocalSyncPath,\n signal?: AbortSignal,\n options?: LocalSha1ReadOptions,\n) => Promise<string | null>\n\n/**\n * Formats a hash error for public sync events without leaking filesystem paths.\n *\n * @param error - Error thrown while hashing.\n *\n * @returns A sanitized reason suitable for event messages.\n */\nexport function formatHashError(error: Error): string {\n return sanitizeErrorReason(error)\n}\n\n/**\n * Reads a local file and computes its SHA-1 digest with non-regular-file rejection,\n * scanned-size bounds, abort support, and an idle/no-progress timeout.\n *\n * @param path - Local sync path to hash.\n * @param signal - Optional abort signal.\n * @param options - Optional idle timeout override.\n *\n * @returns The lowercase SHA-1 digest of the file bytes.\n */\nexport async function readLocalSha1File(\n path: LocalSyncPath,\n signal?: AbortSignal,\n options: LocalSha1ReadOptions = {},\n): Promise<string> {\n const { constants } = await import('node:fs')\n const { lstat, open } = await import('node:fs/promises')\n const timeoutMillis = normalizeSha1TimeoutMillis(options.timeoutMillis)\n const flags = constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0)\n const hash = new IncrementalSha1()\n let stream: (AsyncIterable<Uint8Array> & { destroy(error?: Error): void }) | undefined\n let file: Awaited<ReturnType<typeof open>> | undefined\n let timeout: ReturnType<typeof setTimeout> | undefined\n\n function armTimeout(onTimeout: () => void): void {\n if (timeout !== undefined) clearTimeout(timeout)\n timeout = setTimeout(onTimeout, timeoutMillis)\n }\n\n try {\n signal?.throwIfAborted()\n\n const preOpenStat = await withTimeout(\n lstat(path.absolutePath),\n timeoutMillis,\n 'sha1 file status',\n )\n assertSameScannedRegularFile(preOpenStat, path, 'sha1 comparison')\n\n file = await openWithTimeout(open(path.absolutePath, flags), timeoutMillis)\n\n const postOpenStat = await withTimeout(\n lstat(path.absolutePath),\n timeoutMillis,\n 'sha1 file status',\n )\n assertSameScannedRegularFile(postOpenStat, path, 'sha1 comparison')\n\n const stat = await withTimeout(file.stat(), timeoutMillis, 'sha1 file status')\n assertSameScannedRegularFile(stat, path, 'sha1 comparison')\n\n stream = file.createReadStream({\n ...(path.size > 0 ? { start: 0, end: path.size - 1 } : {}),\n ...(signal !== undefined ? { signal } : {}),\n })\n\n let bytesRead = 0\n /* v8 ignore next 3 -- idle-timeout firing is timing-dependent in filesystem tests */\n armTimeout(() => {\n stream?.destroy(new Error(`sha1 read stalled for ${timeoutMillis} ms`))\n })\n for await (const chunk of stream) {\n bytesRead += chunk.byteLength\n await hash.update(chunk)\n /* v8 ignore next 3 -- idle-timeout firing is timing-dependent in filesystem tests */\n armTimeout(() => {\n stream?.destroy(new Error(`sha1 read stalled for ${timeoutMillis} ms`))\n })\n }\n\n /* v8 ignore next -- defensive TOCTOU guard after the bounded stream completes */\n if (bytesRead !== path.size) throw new Error('file changed during sha1 comparison')\n return hash.digest()\n } finally {\n if (timeout !== undefined) clearTimeout(timeout)\n stream?.destroy()\n await file?.close().catch(() => {})\n }\n}\n\n/* v8 ignore start -- defensive stale-filesystem stall handling is not portable to trigger */\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMillis: number,\n operation: string,\n): Promise<T> {\n let timeout: ReturnType<typeof setTimeout> | undefined\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => {\n reject(new Error(`${operation} stalled for ${timeoutMillis} ms`))\n }, timeoutMillis)\n }),\n ])\n } finally {\n if (timeout !== undefined) clearTimeout(timeout)\n }\n}\n\nasync function openWithTimeout<T extends { close(): Promise<void> }>(\n promise: Promise<T>,\n timeoutMillis: number,\n): Promise<T> {\n let timedOut = false\n const tracked = promise.then(\n (file) => {\n if (timedOut) void file.close().catch(() => {})\n return file\n },\n (err: unknown) => {\n throw err\n },\n )\n\n try {\n return await withTimeout(tracked, timeoutMillis, 'sha1 file open')\n } catch (err) {\n timedOut = true\n throw err\n }\n}\n/* v8 ignore stop */\n"],"mappings":";;;;;;;;;;;;AA6BA,SAAgB,gBAAgB,OAAsB;CACpD,OAAO,oBAAoB,KAAK;AAClC;;;;;;;;;;;AAYA,eAAsB,kBACpB,MACA,QACA,UAAgC,CAAC,GAChB;CACjB,MAAM,EAAE,cAAc,MAAM,OAAO;CACnC,MAAM,EAAE,OAAO,SAAS,MAAM,OAAO;CACrC,MAAM,gBAAgB,2BAA2B,QAAQ,aAAa;CACtE,MAAM,QAAQ,UAAU,YAAY,UAAU,cAAc,MAAM,UAAU,cAAc;CAC1F,MAAM,OAAO,IAAI,gBAAgB;CACjC,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,SAAS,WAAW,WAA6B;EAC/C,IAAI,YAAY,KAAA,GAAW,aAAa,OAAO;EAC/C,UAAU,WAAW,WAAW,aAAa;CAC/C;CAEA,IAAI;EACF,QAAQ,eAAe;EAEvB,MAAM,cAAc,MAAM,YACxB,MAAM,KAAK,YAAY,GACvB,eACA,kBACF;EACA,6BAA6B,aAAa,MAAM,iBAAiB;EAEjE,OAAO,MAAM,gBAAgB,KAAK,KAAK,cAAc,KAAK,GAAG,aAAa;EAE1E,MAAM,eAAe,MAAM,YACzB,MAAM,KAAK,YAAY,GACvB,eACA,kBACF;EACA,6BAA6B,cAAc,MAAM,iBAAiB;EAElE,MAAM,OAAO,MAAM,YAAY,KAAK,KAAK,GAAG,eAAe,kBAAkB;EAC7E,6BAA6B,MAAM,MAAM,iBAAiB;EAE1D,SAAS,KAAK,iBAAiB;GAC7B,GAAI,KAAK,OAAO,IAAI;IAAE,OAAO;IAAG,KAAK,KAAK,OAAO;GAAE,IAAI,CAAC;GACxD,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;EAC3C,CAAC;EAED,IAAI,YAAY;;EAEhB,iBAAiB;GACf,QAAQ,wBAAQ,IAAI,MAAM,yBAAyB,cAAc,IAAI,CAAC;EACxE,CAAC;EACD,WAAW,MAAM,SAAS,QAAQ;GAChC,aAAa,MAAM;GACnB,MAAM,KAAK,OAAO,KAAK;;GAEvB,iBAAiB;IACf,QAAQ,wBAAQ,IAAI,MAAM,yBAAyB,cAAc,IAAI,CAAC;GACxE,CAAC;EACH;;EAGA,IAAI,cAAc,KAAK,MAAM,MAAM,IAAI,MAAM,qCAAqC;EAClF,OAAO,KAAK,OAAO;CACrB,UAAU;EACR,IAAI,YAAY,KAAA,GAAW,aAAa,OAAO;EAC/C,QAAQ,QAAQ;EAChB,MAAM,MAAM,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;CACpC;AACF;;AAGA,eAAe,YACb,SACA,eACA,WACY;CACZ,IAAI;CACJ,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,CACxB,SACA,IAAI,SAAgB,GAAG,WAAW;GAChC,UAAU,iBAAiB;IACzB,uBAAO,IAAI,MAAM,GAAG,UAAU,eAAe,cAAc,IAAI,CAAC;GAClE,GAAG,aAAa;EAClB,CAAC,CACH,CAAC;CACH,UAAU;EACR,IAAI,YAAY,KAAA,GAAW,aAAa,OAAO;CACjD;AACF;AAEA,eAAe,gBACb,SACA,eACY;CACZ,IAAI,WAAW;CACf,MAAM,UAAU,QAAQ,MACrB,SAAS;EACR,IAAI,UAAU,KAAU,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EAC9C,OAAO;CACT,IACC,QAAiB;EAChB,MAAM;CACR,CACF;CAEA,IAAI;EACF,OAAO,MAAM,YAAY,SAAS,eAAe,gBAAgB;CACnE,SAAS,KAAK;EACZ,WAAW;EACX,MAAM;CACR;AACF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_concurrency = require("../../upload/concurrency.cjs");
|
|
2
|
+
const require_abort = require("../../util/abort.cjs");
|
|
2
3
|
const require_sha1 = require("../../util/sha1.cjs");
|
|
3
4
|
const require_to_error = require("../../util/to-error.cjs");
|
|
4
5
|
const require_local_sha1 = require("../local-sha1.cjs");
|
|
@@ -109,7 +110,8 @@ async function preparePairsForCompare(pairs, compareMode, options = {}) {
|
|
|
109
110
|
originalPair: pair,
|
|
110
111
|
prepared: readyComparePair(pair)
|
|
111
112
|
}));
|
|
112
|
-
|
|
113
|
+
const concurrency = normalizeConcurrency(options.concurrency);
|
|
114
|
+
return require_concurrency.mapConcurrent(pairs, concurrency, async (pair) => {
|
|
113
115
|
if (options.signal?.aborted) return {
|
|
114
116
|
originalPair: pair,
|
|
115
117
|
prepared: aborted(pair)
|
|
@@ -174,7 +176,7 @@ async function prepareLocalPathSha1(path, options) {
|
|
|
174
176
|
aborted: false
|
|
175
177
|
};
|
|
176
178
|
} catch (err) {
|
|
177
|
-
if (options.signal
|
|
179
|
+
if (require_abort.isSignalAbortError(options.signal, err)) return {
|
|
178
180
|
path,
|
|
179
181
|
bytesHashed: 0,
|
|
180
182
|
bytesVerified: 0,
|
|
@@ -251,7 +253,7 @@ async function prepareB2PathSha1(path, options) {
|
|
|
251
253
|
aborted: false
|
|
252
254
|
};
|
|
253
255
|
} catch (err) {
|
|
254
|
-
if (options.signal
|
|
256
|
+
if (require_abort.isSignalAbortError(options.signal, err)) return {
|
|
255
257
|
path,
|
|
256
258
|
bytesHashed: 0,
|
|
257
259
|
bytesVerified: 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compare.cjs","names":[],"sources":["../../../src/sync/policies/compare.ts"],"sourcesContent":["import { mapConcurrent } from '../../upload/concurrency.ts'\nimport { normalizeVerifiableSha1 } from '../../util/sha1.ts'\nimport { toError } from '../../util/to-error.ts'\nimport { formatHashError, isAbortError, type LocalSha1Reader } from '../local-sha1.ts'\nimport type { SyncPair } from '../pairing.ts'\nimport { selectB2ComparableSha1, syncSha1StateOf } from '../sha1-metadata.ts'\nimport type {\n B2SyncPath,\n CompareMode,\n LocalSyncPath,\n SyncEvent,\n SyncPath,\n SyncSkipEvent,\n} from '../types.ts'\n\nexport { selectB2ComparableSha1 } from '../sha1-metadata.ts'\n\n/** Result from a B2 SHA-1 verification read. */\nexport interface B2Sha1ReadResult {\n /** SHA-1 digest of the selected B2 bytes, or null when unavailable. */\n readonly contentSha1: string | null\n /** Number of B2 bytes downloaded and hashed while verifying. */\n readonly bytesRead: number\n}\n\n/** Reads a B2 file and returns the SHA-1 digest of its actual bytes, or null when unavailable. */\nexport type B2Sha1Reader = (\n path: B2SyncPath,\n signal?: AbortSignal,\n) => Promise<string | null | B2Sha1ReadResult>\n\n/** Options for preparing a file pair before comparison. */\nexport interface PreparePairForCompareOptions {\n /** Signal used to abort local file hashing. */\n readonly signal?: AbortSignal\n /** Optional idle/no-progress timeout for local SHA-1 reads. */\n readonly sha1ReadTimeoutMillis?: number\n /** Optional local hashing override for tests or custom runtimes. */\n readonly readLocalSha1?: LocalSha1Reader\n /** Optional B2 hashing override used to verify metadata matches before skipping. */\n readonly readB2Sha1?: B2Sha1Reader\n}\n\n/** Options for preparing multiple file pairs before comparison. */\nexport interface PreparePairsForCompareOptions extends PreparePairForCompareOptions {\n /** Maximum number of file pairs to prepare concurrently. */\n readonly concurrency?: number\n}\n\n/** Result of preparing a file pair before policy comparison. */\nexport interface ComparePreparationResult {\n /** The pair with compare-mode metadata populated where possible. */\n readonly pair: SyncPair\n /** Events surfaced while preparing the pair, such as per-file hash errors or skips. */\n readonly events: readonly SyncEvent[]\n /** Errors surfaced while preparing the pair. These contribute to the final summary. */\n readonly errors: readonly Error[]\n /** Local file bytes read for hashing while preparing this pair. */\n readonly bytesHashed: number\n /** B2 bytes read for untrusted metadata verification while preparing this pair. */\n readonly bytesVerified: number\n /** Whether action generation should be skipped for this pair. */\n readonly skipActionGeneration: boolean\n /** Whether preparation stopped because the abort signal fired. */\n readonly aborted: boolean\n}\n\n/** A prepared pair result with the original pair retained for event context. */\nexport interface PreparedComparePair {\n /** Original pair from {@link zipFolders}. */\n readonly originalPair: SyncPair\n /** Preparation result for the original pair. */\n readonly prepared: ComparePreparationResult\n}\n\n/**\n * Determines whether two files should be considered different based on the compare mode.\n * For `sha1`, callers that use the low-level policy helpers should first prepare the pair so\n * local hashes and comparable B2 hashes are populated.\n *\n * @param source - The source file metadata.\n * @param dest - The destination file metadata.\n * @param compareMode - The comparison strategy: 'modtime', 'size', 'sha1', or 'none'.\n * @param threshold - Tolerance for the comparison (bytes for size, milliseconds for modtime).\n *\n * @returns `true` if the files are considered different.\n *\n * @throws When `compareMode` is not one of the supported compare modes.\n */\nexport function filesAreDifferent(\n source: SyncPath,\n dest: SyncPath,\n compareMode: CompareMode,\n threshold = 0,\n): boolean {\n switch (compareMode) {\n case 'none':\n return false\n case 'size':\n return Math.abs(source.size - dest.size) > threshold\n case 'sha1':\n return sha1ValuesAreDifferent(source, dest)\n case 'modtime':\n return Math.abs(source.modTimeMillis - dest.modTimeMillis) > threshold\n default:\n throw new Error(`Unsupported compare mode: ${String(compareMode)}`)\n }\n}\n\n/**\n * Throws when a runtime compare mode value is unsupported.\n *\n * @param compareMode - User-supplied compare mode.\n *\n * @throws When `compareMode` is not one of the supported values.\n */\nexport function assertSupportedCompareMode(\n compareMode: unknown,\n): asserts compareMode is CompareMode {\n switch (compareMode) {\n case 'none':\n case 'size':\n case 'sha1':\n case 'modtime':\n return\n default:\n throw new Error(`Unsupported compare mode: ${String(compareMode)}`)\n }\n}\n\nfunction sha1ValuesAreDifferent(source: SyncPath, dest: SyncPath): boolean {\n if (source.size !== dest.size) return true\n\n const sourceSha1 = comparableSha1(source)\n const destSha1 = comparableSha1(dest)\n if (sourceSha1.kind === 'untrusted' || destSha1.kind === 'untrusted') return true\n if (sourceSha1.kind === 'unavailable' || destSha1.kind === 'unavailable') return true\n return sourceSha1.value !== destSha1.value\n}\n\n/**\n * Prepares a pair for the selected compare mode.\n *\n * For `sha1`, this fills missing B2 hashes from comparable metadata and, when an explicit local\n * reader is supplied, hashes the local side only when size cannot already prove a difference.\n * Reader failures are converted into per-file sync events instead of aborting the whole run.\n *\n * @param pair - Source/destination pair from {@link zipFolders}.\n * @param compareMode - The comparison strategy.\n * @param options - Optional hashing dependencies and cancellation signal.\n *\n * @returns The prepared pair plus any preparation events.\n */\nexport async function preparePairForCompare(\n pair: SyncPair,\n compareMode: CompareMode,\n options: PreparePairForCompareOptions = {},\n): Promise<ComparePreparationResult> {\n assertSupportedCompareMode(compareMode)\n if (compareMode !== 'sha1') return readyComparePair(pair)\n\n const [source, dest] = pair\n if (source === null || dest === null) return readyComparePair(pair)\n if (source.size !== dest.size) return readyComparePair(pair)\n\n const metadataPair: SyncPair = [withB2ContentSha1(source), withB2ContentSha1(dest)]\n if (hasUnavailableB2Sha1(metadataPair)) {\n return skipped(metadataPair, unavailableSha1Event(metadataPair))\n }\n if (\n options.readB2Sha1 === undefined &&\n hasUntrustedSha1(metadataPair) &&\n !hasVerifiableUntrustedSha1(metadataPair)\n ) {\n return readyComparePair(metadataPair)\n }\n\n const [metadataSource, metadataDest] = metadataPair\n if (metadataSource === null || metadataDest === null) return readyComparePair(metadataPair)\n\n const sourceResult = await prepareLocalPathSha1(metadataSource, options)\n if (sourceResult.aborted) return aborted(metadataPair)\n if (sourceResult.event) {\n return skipped(metadataPair, sourceResult.event, sourceResult.error, sourceResult.bytesHashed)\n }\n\n const destResult = await prepareLocalPathSha1(metadataDest, options)\n if (destResult.aborted) return aborted([sourceResult.path, destResult.path])\n if (destResult.event) {\n return skipped(\n [sourceResult.path, destResult.path],\n destResult.event,\n destResult.error,\n sourceResult.bytesHashed,\n )\n }\n\n const preparedPair: SyncPair = [sourceResult.path, destResult.path]\n const sourceState = comparableSha1(sourceResult.path)\n const destState = comparableSha1(destResult.path)\n const bytesHashed = sourceResult.bytesHashed + destResult.bytesHashed\n\n if (sourceState.kind === 'unavailable' || destState.kind === 'unavailable') {\n return skipped(preparedPair, unavailableSha1Event(preparedPair), undefined, bytesHashed)\n }\n\n const shouldVerifyB2Bytes = untrustedSha1CouldSuppressTransfer(sourceState, destState)\n const readB2Sha1 = options.readB2Sha1\n if (shouldVerifyB2Bytes && hasB2Path(preparedPair) && readB2Sha1 !== undefined) {\n return verifyB2Sha1Bytes(preparedPair, { ...options, readB2Sha1 }, bytesHashed)\n }\n\n return readyComparePair(preparedPair, bytesHashed)\n}\n\n/**\n * Prepares a list of pairs for the selected compare mode with bounded concurrency.\n *\n * @param pairs - Source/destination pairs from {@link zipFolders}.\n * @param compareMode - The comparison strategy.\n * @param options - Optional hashing dependencies, cancellation signal, and concurrency.\n *\n * @returns Prepared results in the same order as the input pairs.\n */\nexport async function preparePairsForCompare(\n pairs: readonly SyncPair[],\n compareMode: CompareMode,\n options: PreparePairsForCompareOptions = {},\n): Promise<PreparedComparePair[]> {\n assertSupportedCompareMode(compareMode)\n if (compareMode !== 'sha1') {\n return pairs.map((pair) => ({ originalPair: pair, prepared: readyComparePair(pair) }))\n }\n\n const concurrency = normalizeConcurrency(options.concurrency)\n return mapConcurrent(pairs, concurrency, async (pair): Promise<PreparedComparePair> => {\n if (options.signal?.aborted) return { originalPair: pair, prepared: aborted(pair) }\n return {\n originalPair: pair,\n prepared: await preparePairForCompare(pair, compareMode, options),\n }\n })\n}\n\ntype Sha1State =\n | { readonly kind: 'verified'; readonly value: string }\n | { readonly kind: 'untrusted'; readonly value: string | null }\n | { readonly kind: 'unavailable' }\n\ninterface PreparedPath {\n readonly path: SyncPath\n readonly bytesHashed: number\n readonly bytesVerified: number\n readonly event?: SyncEvent\n readonly error?: Error\n readonly aborted: boolean\n}\n\nasync function prepareLocalPathSha1(\n path: SyncPath,\n options: PreparePairForCompareOptions,\n): Promise<PreparedPath> {\n if (!isLocalSyncPath(path)) {\n return { path, bytesHashed: 0, bytesVerified: 0, aborted: false }\n }\n\n if (options.signal?.aborted) return { path, bytesHashed: 0, bytesVerified: 0, aborted: true }\n\n try {\n const state = syncSha1StateOf(path)\n if (state.kind === 'verified') {\n return {\n path: { ...path, contentSha1: state.value },\n bytesHashed: 0,\n bytesVerified: 0,\n aborted: false,\n }\n }\n if (state.kind === 'unavailable') {\n return {\n path: { ...path, contentSha1: null },\n bytesHashed: 0,\n bytesVerified: 0,\n aborted: false,\n }\n }\n\n const readLocalSha1 = options.readLocalSha1\n if (readLocalSha1 === undefined) {\n return {\n path: { ...path, contentSha1: path.contentSha1 ?? null },\n bytesHashed: 0,\n bytesVerified: 0,\n aborted: false,\n }\n }\n const contentSha1 = await readLocalSha1(path, options.signal, {\n ...(options.sha1ReadTimeoutMillis !== undefined\n ? { timeoutMillis: options.sha1ReadTimeoutMillis }\n : {}),\n })\n return {\n path: {\n ...path,\n contentSha1,\n },\n bytesHashed: normalizeVerifiableSha1(contentSha1) === null ? 0 : path.size,\n bytesVerified: 0,\n aborted: false,\n }\n } catch (err) {\n if (options.signal?.aborted || isAbortError(err)) {\n return { path, bytesHashed: 0, bytesVerified: 0, aborted: true }\n }\n const error = toError(err)\n return {\n path,\n bytesHashed: 0,\n event: {\n type: 'error',\n path: path.relativePath,\n size: 0,\n message: `failed to hash local file for sha1 comparison: ${formatHashError(error)}`,\n },\n error,\n bytesVerified: 0,\n aborted: false,\n }\n }\n}\n\nasync function verifyB2Sha1Bytes(\n pair: SyncPair,\n options: PreparePairForCompareOptions & { readonly readB2Sha1: B2Sha1Reader },\n bytesHashed: number,\n): Promise<ComparePreparationResult> {\n const [source, dest] = pair\n /* v8 ignore next -- callers only verify B2 bytes for paired compare results */\n if (source === null || dest === null) return readyComparePair(pair, bytesHashed)\n\n const sourceResult = await prepareUntrustedB2PathSha1(source, options)\n const sourceBytesVerified = sourceResult.bytesVerified\n if (sourceResult.aborted) return aborted(pair)\n if (sourceResult.event) {\n return skipped(\n [sourceResult.path, dest],\n sourceResult.event,\n sourceResult.error,\n bytesHashed,\n sourceBytesVerified,\n )\n }\n\n const destResult = await prepareUntrustedB2PathSha1(dest, options)\n const bytesVerified = sourceBytesVerified + destResult.bytesVerified\n /* v8 ignore next -- destination abort mirrors the covered source abort path */\n if (destResult.aborted) return aborted([sourceResult.path, destResult.path])\n if (destResult.event) {\n return skipped(\n [sourceResult.path, destResult.path],\n destResult.event,\n destResult.error,\n bytesHashed,\n bytesVerified,\n )\n }\n\n return readyComparePair([sourceResult.path, destResult.path], bytesHashed, bytesVerified)\n}\n\nasync function prepareUntrustedB2PathSha1(\n path: SyncPath,\n options: PreparePairForCompareOptions & { readonly readB2Sha1: B2Sha1Reader },\n): Promise<PreparedPath> {\n if (!isB2SyncPath(path) || comparableSha1(path).kind !== 'untrusted') {\n return { path, bytesHashed: 0, bytesVerified: 0, aborted: false }\n }\n return prepareB2PathSha1(path, options)\n}\n\nasync function prepareB2PathSha1(\n path: B2SyncPath,\n options: PreparePairForCompareOptions & { readonly readB2Sha1: B2Sha1Reader },\n): Promise<PreparedPath> {\n /* v8 ignore next -- pre-aborted B2 reads are covered at pair level */\n if (options.signal?.aborted) return { path, bytesHashed: 0, bytesVerified: 0, aborted: true }\n\n try {\n const result = await options.readB2Sha1(path, options.signal)\n const contentSha1 = typeof result === 'object' && result !== null ? result.contentSha1 : result\n const bytesVerified =\n typeof result === 'object' && result !== null ? Math.max(0, result.bytesRead) : 0\n const preparedPath = {\n ...path,\n contentSha1,\n contentSha1State: syncSha1StateOf({ contentSha1 }),\n }\n if (contentSha1 === null) {\n return {\n path: preparedPath,\n bytesHashed: 0,\n bytesVerified,\n event: unavailableSha1PathEvent(path),\n aborted: false,\n }\n }\n return {\n path: preparedPath,\n bytesHashed: 0,\n bytesVerified,\n aborted: false,\n }\n } catch (err) {\n if (options.signal?.aborted || isAbortError(err)) {\n return { path, bytesHashed: 0, bytesVerified: 0, aborted: true }\n }\n const error = toError(err)\n return {\n path,\n bytesHashed: 0,\n bytesVerified: 0,\n event: {\n type: 'skip',\n path: path.relativePath,\n size: 0,\n message: `sha1 comparison skipped because B2 verification failed: ${formatHashError(error)}`,\n },\n aborted: false,\n }\n }\n}\n\nfunction comparableSha1(path: SyncPath): Sha1State {\n const state = syncSha1StateOf(path)\n if (state.kind === 'verified') return { kind: 'verified', value: state.value }\n if (state.kind === 'untrusted') return { kind: 'untrusted', value: state.value }\n return { kind: 'unavailable' }\n}\n\nfunction untrustedSha1CouldSuppressTransfer(source: Sha1State, dest: Sha1State): boolean {\n if (source.kind === 'untrusted' && dest.kind === 'verified') return source.value === dest.value\n if (dest.kind === 'untrusted' && source.kind === 'verified') return dest.value === source.value\n if (source.kind === 'untrusted' && dest.kind === 'untrusted') {\n return source.value !== null && source.value === dest.value\n }\n return false\n}\n\nfunction withB2ContentSha1(path: SyncPath): SyncPath {\n if (\n !isB2SyncPath(path) ||\n path.contentSha1 !== undefined ||\n path.contentSha1State !== undefined\n ) {\n return path\n }\n const contentSha1 = selectB2ComparableSha1(path.selectedVersion)\n return { ...path, contentSha1, contentSha1State: syncSha1StateOf({ contentSha1 }) }\n}\n\nfunction hasUnavailableB2Sha1(pair: SyncPair): boolean {\n const [source, dest] = pair\n return (\n (source !== null && isB2SyncPath(source) && comparableSha1(source).kind === 'unavailable') ||\n (dest !== null && isB2SyncPath(dest) && comparableSha1(dest).kind === 'unavailable')\n )\n}\n\nfunction hasUntrustedSha1(pair: SyncPair): boolean {\n const [source, dest] = pair\n return (\n (source !== null && comparableSha1(source).kind === 'untrusted') ||\n (dest !== null && comparableSha1(dest).kind === 'untrusted')\n )\n}\n\nfunction hasVerifiableUntrustedSha1(pair: SyncPair): boolean {\n const [source, dest] = pair\n return (\n (source !== null && verifiableUntrustedSha1(source)) ||\n (dest !== null && verifiableUntrustedSha1(dest))\n )\n}\n\nfunction verifiableUntrustedSha1(path: SyncPath): boolean {\n const state = comparableSha1(path)\n return state.kind === 'untrusted' && state.value !== null\n}\n\nfunction hasB2Path(pair: SyncPair): boolean {\n const [source, dest] = pair\n return (source !== null && isB2SyncPath(source)) || (dest !== null && isB2SyncPath(dest))\n}\n\nfunction isB2SyncPath(path: SyncPath): path is B2SyncPath {\n return 'selectedVersion' in path\n}\n\nfunction isLocalSyncPath(path: SyncPath): path is LocalSyncPath {\n return 'absolutePath' in path\n}\n\n/**\n * Creates a successful no-op compare preparation result for a pair.\n *\n * @param pair - Source/destination pair from {@link zipFolders}.\n * @param bytesHashed - Local file bytes read while preparing the pair.\n * @param bytesVerified - B2 bytes read while verifying untrusted SHA-1 metadata.\n *\n * @returns A ready preparation result that allows action generation.\n */\nexport function readyComparePair(\n pair: SyncPair,\n bytesHashed = 0,\n bytesVerified = 0,\n): ComparePreparationResult {\n return {\n pair,\n events: [],\n errors: [],\n bytesHashed,\n bytesVerified,\n skipActionGeneration: false,\n aborted: false,\n }\n}\n\nfunction skipped(\n pair: SyncPair,\n event: SyncEvent,\n error?: Error,\n bytesHashed = 0,\n bytesVerified = 0,\n): ComparePreparationResult {\n return {\n pair,\n events: [event],\n errors: error !== undefined ? [error] : [],\n bytesHashed,\n bytesVerified,\n skipActionGeneration: true,\n aborted: false,\n }\n}\n\nfunction aborted(pair: SyncPair): ComparePreparationResult {\n return {\n pair,\n events: [],\n errors: [],\n bytesHashed: 0,\n bytesVerified: 0,\n skipActionGeneration: true,\n aborted: true,\n }\n}\n\nfunction unavailableSha1Event(pair: SyncPair): SyncSkipEvent {\n /* v8 ignore next -- unavailable SHA-1 events always have at least one side */\n const path = (pair[0] ?? pair[1])?.relativePath ?? ''\n return unavailableSha1PathEvent({ relativePath: path })\n}\n\nfunction unavailableSha1PathEvent(path: Pick<SyncPath, 'relativePath'>): SyncSkipEvent {\n return {\n type: 'skip',\n path: path.relativePath,\n size: 0,\n message: 'sha1 comparison skipped because a verifiable SHA-1 is unavailable',\n }\n}\n\nfunction normalizeConcurrency(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value < 1) return 1\n return Math.max(1, Math.floor(value))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compare.cjs","names":[],"sources":["../../../src/sync/policies/compare.ts"],"sourcesContent":["import { mapConcurrent } from '../../upload/concurrency.ts'\nimport { isSignalAbortError } from '../../util/abort.ts'\nimport { normalizeVerifiableSha1 } from '../../util/sha1.ts'\nimport { toError } from '../../util/to-error.ts'\nimport { formatHashError, type LocalSha1Reader } from '../local-sha1.ts'\nimport type { SyncPair } from '../pairing.ts'\nimport { selectB2ComparableSha1, syncSha1StateOf } from '../sha1-metadata.ts'\nimport type {\n B2SyncPath,\n CompareMode,\n LocalSyncPath,\n SyncEvent,\n SyncPath,\n SyncSkipEvent,\n} from '../types.ts'\n\nexport { selectB2ComparableSha1 } from '../sha1-metadata.ts'\n\n/** Result from a B2 SHA-1 verification read. */\nexport interface B2Sha1ReadResult {\n /** SHA-1 digest of the selected B2 bytes, or null when unavailable. */\n readonly contentSha1: string | null\n /** Number of B2 bytes downloaded and hashed while verifying. */\n readonly bytesRead: number\n}\n\n/** Reads a B2 file and returns the SHA-1 digest of its actual bytes, or null when unavailable. */\nexport type B2Sha1Reader = (\n path: B2SyncPath,\n signal?: AbortSignal,\n) => Promise<string | null | B2Sha1ReadResult>\n\n/** Options for preparing a file pair before comparison. */\nexport interface PreparePairForCompareOptions {\n /** Signal used to abort local file hashing. */\n readonly signal?: AbortSignal\n /** Optional idle/no-progress timeout for local SHA-1 reads. */\n readonly sha1ReadTimeoutMillis?: number\n /** Optional local hashing override for tests or custom runtimes. */\n readonly readLocalSha1?: LocalSha1Reader\n /** Optional B2 hashing override used to verify metadata matches before skipping. */\n readonly readB2Sha1?: B2Sha1Reader\n}\n\n/** Options for preparing multiple file pairs before comparison. */\nexport interface PreparePairsForCompareOptions extends PreparePairForCompareOptions {\n /** Maximum number of file pairs to prepare concurrently. */\n readonly concurrency?: number\n}\n\n/** Result of preparing a file pair before policy comparison. */\nexport interface ComparePreparationResult {\n /** The pair with compare-mode metadata populated where possible. */\n readonly pair: SyncPair\n /** Events surfaced while preparing the pair, such as per-file hash errors or skips. */\n readonly events: readonly SyncEvent[]\n /** Errors surfaced while preparing the pair. These contribute to the final summary. */\n readonly errors: readonly Error[]\n /** Local file bytes read for hashing while preparing this pair. */\n readonly bytesHashed: number\n /** B2 bytes read for untrusted metadata verification while preparing this pair. */\n readonly bytesVerified: number\n /** Whether action generation should be skipped for this pair. */\n readonly skipActionGeneration: boolean\n /** Whether preparation stopped because the abort signal fired. */\n readonly aborted: boolean\n}\n\n/** A prepared pair result with the original pair retained for event context. */\nexport interface PreparedComparePair {\n /** Original pair from {@link zipFolders}. */\n readonly originalPair: SyncPair\n /** Preparation result for the original pair. */\n readonly prepared: ComparePreparationResult\n}\n\n/**\n * Determines whether two files should be considered different based on the compare mode.\n * For `sha1`, callers that use the low-level policy helpers should first prepare the pair so\n * local hashes and comparable B2 hashes are populated.\n *\n * @param source - The source file metadata.\n * @param dest - The destination file metadata.\n * @param compareMode - The comparison strategy: 'modtime', 'size', 'sha1', or 'none'.\n * @param threshold - Tolerance for the comparison (bytes for size, milliseconds for modtime).\n *\n * @returns `true` if the files are considered different.\n *\n * @throws When `compareMode` is not one of the supported compare modes.\n */\nexport function filesAreDifferent(\n source: SyncPath,\n dest: SyncPath,\n compareMode: CompareMode,\n threshold = 0,\n): boolean {\n switch (compareMode) {\n case 'none':\n return false\n case 'size':\n return Math.abs(source.size - dest.size) > threshold\n case 'sha1':\n return sha1ValuesAreDifferent(source, dest)\n case 'modtime':\n return Math.abs(source.modTimeMillis - dest.modTimeMillis) > threshold\n default:\n throw new Error(`Unsupported compare mode: ${String(compareMode)}`)\n }\n}\n\n/**\n * Throws when a runtime compare mode value is unsupported.\n *\n * @param compareMode - User-supplied compare mode.\n *\n * @throws When `compareMode` is not one of the supported values.\n */\nexport function assertSupportedCompareMode(\n compareMode: unknown,\n): asserts compareMode is CompareMode {\n switch (compareMode) {\n case 'none':\n case 'size':\n case 'sha1':\n case 'modtime':\n return\n default:\n throw new Error(`Unsupported compare mode: ${String(compareMode)}`)\n }\n}\n\nfunction sha1ValuesAreDifferent(source: SyncPath, dest: SyncPath): boolean {\n if (source.size !== dest.size) return true\n\n const sourceSha1 = comparableSha1(source)\n const destSha1 = comparableSha1(dest)\n if (sourceSha1.kind === 'untrusted' || destSha1.kind === 'untrusted') return true\n if (sourceSha1.kind === 'unavailable' || destSha1.kind === 'unavailable') return true\n return sourceSha1.value !== destSha1.value\n}\n\n/**\n * Prepares a pair for the selected compare mode.\n *\n * For `sha1`, this fills missing B2 hashes from comparable metadata and, when an explicit local\n * reader is supplied, hashes the local side only when size cannot already prove a difference.\n * Reader failures are converted into per-file sync events instead of aborting the whole run.\n *\n * @param pair - Source/destination pair from {@link zipFolders}.\n * @param compareMode - The comparison strategy.\n * @param options - Optional hashing dependencies and cancellation signal.\n *\n * @returns The prepared pair plus any preparation events.\n */\nexport async function preparePairForCompare(\n pair: SyncPair,\n compareMode: CompareMode,\n options: PreparePairForCompareOptions = {},\n): Promise<ComparePreparationResult> {\n assertSupportedCompareMode(compareMode)\n if (compareMode !== 'sha1') return readyComparePair(pair)\n\n const [source, dest] = pair\n if (source === null || dest === null) return readyComparePair(pair)\n if (source.size !== dest.size) return readyComparePair(pair)\n\n const metadataPair: SyncPair = [withB2ContentSha1(source), withB2ContentSha1(dest)]\n if (hasUnavailableB2Sha1(metadataPair)) {\n return skipped(metadataPair, unavailableSha1Event(metadataPair))\n }\n if (\n options.readB2Sha1 === undefined &&\n hasUntrustedSha1(metadataPair) &&\n !hasVerifiableUntrustedSha1(metadataPair)\n ) {\n return readyComparePair(metadataPair)\n }\n\n const [metadataSource, metadataDest] = metadataPair\n if (metadataSource === null || metadataDest === null) return readyComparePair(metadataPair)\n\n const sourceResult = await prepareLocalPathSha1(metadataSource, options)\n if (sourceResult.aborted) return aborted(metadataPair)\n if (sourceResult.event) {\n return skipped(metadataPair, sourceResult.event, sourceResult.error, sourceResult.bytesHashed)\n }\n\n const destResult = await prepareLocalPathSha1(metadataDest, options)\n if (destResult.aborted) return aborted([sourceResult.path, destResult.path])\n if (destResult.event) {\n return skipped(\n [sourceResult.path, destResult.path],\n destResult.event,\n destResult.error,\n sourceResult.bytesHashed,\n )\n }\n\n const preparedPair: SyncPair = [sourceResult.path, destResult.path]\n const sourceState = comparableSha1(sourceResult.path)\n const destState = comparableSha1(destResult.path)\n const bytesHashed = sourceResult.bytesHashed + destResult.bytesHashed\n\n if (sourceState.kind === 'unavailable' || destState.kind === 'unavailable') {\n return skipped(preparedPair, unavailableSha1Event(preparedPair), undefined, bytesHashed)\n }\n\n const shouldVerifyB2Bytes = untrustedSha1CouldSuppressTransfer(sourceState, destState)\n const readB2Sha1 = options.readB2Sha1\n if (shouldVerifyB2Bytes && hasB2Path(preparedPair) && readB2Sha1 !== undefined) {\n return verifyB2Sha1Bytes(preparedPair, { ...options, readB2Sha1 }, bytesHashed)\n }\n\n return readyComparePair(preparedPair, bytesHashed)\n}\n\n/**\n * Prepares a list of pairs for the selected compare mode with bounded concurrency.\n *\n * @param pairs - Source/destination pairs from {@link zipFolders}.\n * @param compareMode - The comparison strategy.\n * @param options - Optional hashing dependencies, cancellation signal, and concurrency.\n *\n * @returns Prepared results in the same order as the input pairs.\n */\nexport async function preparePairsForCompare(\n pairs: readonly SyncPair[],\n compareMode: CompareMode,\n options: PreparePairsForCompareOptions = {},\n): Promise<PreparedComparePair[]> {\n assertSupportedCompareMode(compareMode)\n if (compareMode !== 'sha1') {\n return pairs.map((pair) => ({ originalPair: pair, prepared: readyComparePair(pair) }))\n }\n\n const concurrency = normalizeConcurrency(options.concurrency)\n return mapConcurrent(pairs, concurrency, async (pair): Promise<PreparedComparePair> => {\n if (options.signal?.aborted) return { originalPair: pair, prepared: aborted(pair) }\n return {\n originalPair: pair,\n prepared: await preparePairForCompare(pair, compareMode, options),\n }\n })\n}\n\ntype Sha1State =\n | { readonly kind: 'verified'; readonly value: string }\n | { readonly kind: 'untrusted'; readonly value: string | null }\n | { readonly kind: 'unavailable' }\n\ninterface PreparedPath {\n readonly path: SyncPath\n readonly bytesHashed: number\n readonly bytesVerified: number\n readonly event?: SyncEvent\n readonly error?: Error\n readonly aborted: boolean\n}\n\nasync function prepareLocalPathSha1(\n path: SyncPath,\n options: PreparePairForCompareOptions,\n): Promise<PreparedPath> {\n if (!isLocalSyncPath(path)) {\n return { path, bytesHashed: 0, bytesVerified: 0, aborted: false }\n }\n\n if (options.signal?.aborted) return { path, bytesHashed: 0, bytesVerified: 0, aborted: true }\n\n try {\n const state = syncSha1StateOf(path)\n if (state.kind === 'verified') {\n return {\n path: { ...path, contentSha1: state.value },\n bytesHashed: 0,\n bytesVerified: 0,\n aborted: false,\n }\n }\n if (state.kind === 'unavailable') {\n return {\n path: { ...path, contentSha1: null },\n bytesHashed: 0,\n bytesVerified: 0,\n aborted: false,\n }\n }\n\n const readLocalSha1 = options.readLocalSha1\n if (readLocalSha1 === undefined) {\n return {\n path: { ...path, contentSha1: path.contentSha1 ?? null },\n bytesHashed: 0,\n bytesVerified: 0,\n aborted: false,\n }\n }\n const contentSha1 = await readLocalSha1(path, options.signal, {\n ...(options.sha1ReadTimeoutMillis !== undefined\n ? { timeoutMillis: options.sha1ReadTimeoutMillis }\n : {}),\n })\n return {\n path: {\n ...path,\n contentSha1,\n },\n bytesHashed: normalizeVerifiableSha1(contentSha1) === null ? 0 : path.size,\n bytesVerified: 0,\n aborted: false,\n }\n } catch (err) {\n if (isSignalAbortError(options.signal, err)) {\n return { path, bytesHashed: 0, bytesVerified: 0, aborted: true }\n }\n const error = toError(err)\n return {\n path,\n bytesHashed: 0,\n event: {\n type: 'error',\n path: path.relativePath,\n size: 0,\n message: `failed to hash local file for sha1 comparison: ${formatHashError(error)}`,\n },\n error,\n bytesVerified: 0,\n aborted: false,\n }\n }\n}\n\nasync function verifyB2Sha1Bytes(\n pair: SyncPair,\n options: PreparePairForCompareOptions & { readonly readB2Sha1: B2Sha1Reader },\n bytesHashed: number,\n): Promise<ComparePreparationResult> {\n const [source, dest] = pair\n /* v8 ignore next -- callers only verify B2 bytes for paired compare results */\n if (source === null || dest === null) return readyComparePair(pair, bytesHashed)\n\n const sourceResult = await prepareUntrustedB2PathSha1(source, options)\n const sourceBytesVerified = sourceResult.bytesVerified\n if (sourceResult.aborted) return aborted(pair)\n if (sourceResult.event) {\n return skipped(\n [sourceResult.path, dest],\n sourceResult.event,\n sourceResult.error,\n bytesHashed,\n sourceBytesVerified,\n )\n }\n\n const destResult = await prepareUntrustedB2PathSha1(dest, options)\n const bytesVerified = sourceBytesVerified + destResult.bytesVerified\n /* v8 ignore next -- destination abort mirrors the covered source abort path */\n if (destResult.aborted) return aborted([sourceResult.path, destResult.path])\n if (destResult.event) {\n return skipped(\n [sourceResult.path, destResult.path],\n destResult.event,\n destResult.error,\n bytesHashed,\n bytesVerified,\n )\n }\n\n return readyComparePair([sourceResult.path, destResult.path], bytesHashed, bytesVerified)\n}\n\nasync function prepareUntrustedB2PathSha1(\n path: SyncPath,\n options: PreparePairForCompareOptions & { readonly readB2Sha1: B2Sha1Reader },\n): Promise<PreparedPath> {\n if (!isB2SyncPath(path) || comparableSha1(path).kind !== 'untrusted') {\n return { path, bytesHashed: 0, bytesVerified: 0, aborted: false }\n }\n return prepareB2PathSha1(path, options)\n}\n\nasync function prepareB2PathSha1(\n path: B2SyncPath,\n options: PreparePairForCompareOptions & { readonly readB2Sha1: B2Sha1Reader },\n): Promise<PreparedPath> {\n /* v8 ignore next -- pre-aborted B2 reads are covered at pair level */\n if (options.signal?.aborted) return { path, bytesHashed: 0, bytesVerified: 0, aborted: true }\n\n try {\n const result = await options.readB2Sha1(path, options.signal)\n const contentSha1 = typeof result === 'object' && result !== null ? result.contentSha1 : result\n const bytesVerified =\n typeof result === 'object' && result !== null ? Math.max(0, result.bytesRead) : 0\n const preparedPath = {\n ...path,\n contentSha1,\n contentSha1State: syncSha1StateOf({ contentSha1 }),\n }\n if (contentSha1 === null) {\n return {\n path: preparedPath,\n bytesHashed: 0,\n bytesVerified,\n event: unavailableSha1PathEvent(path),\n aborted: false,\n }\n }\n return {\n path: preparedPath,\n bytesHashed: 0,\n bytesVerified,\n aborted: false,\n }\n } catch (err) {\n if (isSignalAbortError(options.signal, err)) {\n return { path, bytesHashed: 0, bytesVerified: 0, aborted: true }\n }\n const error = toError(err)\n return {\n path,\n bytesHashed: 0,\n bytesVerified: 0,\n event: {\n type: 'skip',\n path: path.relativePath,\n size: 0,\n message: `sha1 comparison skipped because B2 verification failed: ${formatHashError(error)}`,\n },\n aborted: false,\n }\n }\n}\n\nfunction comparableSha1(path: SyncPath): Sha1State {\n const state = syncSha1StateOf(path)\n if (state.kind === 'verified') return { kind: 'verified', value: state.value }\n if (state.kind === 'untrusted') return { kind: 'untrusted', value: state.value }\n return { kind: 'unavailable' }\n}\n\nfunction untrustedSha1CouldSuppressTransfer(source: Sha1State, dest: Sha1State): boolean {\n if (source.kind === 'untrusted' && dest.kind === 'verified') return source.value === dest.value\n if (dest.kind === 'untrusted' && source.kind === 'verified') return dest.value === source.value\n if (source.kind === 'untrusted' && dest.kind === 'untrusted') {\n return source.value !== null && source.value === dest.value\n }\n return false\n}\n\nfunction withB2ContentSha1(path: SyncPath): SyncPath {\n if (\n !isB2SyncPath(path) ||\n path.contentSha1 !== undefined ||\n path.contentSha1State !== undefined\n ) {\n return path\n }\n const contentSha1 = selectB2ComparableSha1(path.selectedVersion)\n return { ...path, contentSha1, contentSha1State: syncSha1StateOf({ contentSha1 }) }\n}\n\nfunction hasUnavailableB2Sha1(pair: SyncPair): boolean {\n const [source, dest] = pair\n return (\n (source !== null && isB2SyncPath(source) && comparableSha1(source).kind === 'unavailable') ||\n (dest !== null && isB2SyncPath(dest) && comparableSha1(dest).kind === 'unavailable')\n )\n}\n\nfunction hasUntrustedSha1(pair: SyncPair): boolean {\n const [source, dest] = pair\n return (\n (source !== null && comparableSha1(source).kind === 'untrusted') ||\n (dest !== null && comparableSha1(dest).kind === 'untrusted')\n )\n}\n\nfunction hasVerifiableUntrustedSha1(pair: SyncPair): boolean {\n const [source, dest] = pair\n return (\n (source !== null && verifiableUntrustedSha1(source)) ||\n (dest !== null && verifiableUntrustedSha1(dest))\n )\n}\n\nfunction verifiableUntrustedSha1(path: SyncPath): boolean {\n const state = comparableSha1(path)\n return state.kind === 'untrusted' && state.value !== null\n}\n\nfunction hasB2Path(pair: SyncPair): boolean {\n const [source, dest] = pair\n return (source !== null && isB2SyncPath(source)) || (dest !== null && isB2SyncPath(dest))\n}\n\nfunction isB2SyncPath(path: SyncPath): path is B2SyncPath {\n return 'selectedVersion' in path\n}\n\nfunction isLocalSyncPath(path: SyncPath): path is LocalSyncPath {\n return 'absolutePath' in path\n}\n\n/**\n * Creates a successful no-op compare preparation result for a pair.\n *\n * @param pair - Source/destination pair from {@link zipFolders}.\n * @param bytesHashed - Local file bytes read while preparing the pair.\n * @param bytesVerified - B2 bytes read while verifying untrusted SHA-1 metadata.\n *\n * @returns A ready preparation result that allows action generation.\n */\nexport function readyComparePair(\n pair: SyncPair,\n bytesHashed = 0,\n bytesVerified = 0,\n): ComparePreparationResult {\n return {\n pair,\n events: [],\n errors: [],\n bytesHashed,\n bytesVerified,\n skipActionGeneration: false,\n aborted: false,\n }\n}\n\nfunction skipped(\n pair: SyncPair,\n event: SyncEvent,\n error?: Error,\n bytesHashed = 0,\n bytesVerified = 0,\n): ComparePreparationResult {\n return {\n pair,\n events: [event],\n errors: error !== undefined ? [error] : [],\n bytesHashed,\n bytesVerified,\n skipActionGeneration: true,\n aborted: false,\n }\n}\n\nfunction aborted(pair: SyncPair): ComparePreparationResult {\n return {\n pair,\n events: [],\n errors: [],\n bytesHashed: 0,\n bytesVerified: 0,\n skipActionGeneration: true,\n aborted: true,\n }\n}\n\nfunction unavailableSha1Event(pair: SyncPair): SyncSkipEvent {\n /* v8 ignore next -- unavailable SHA-1 events always have at least one side */\n const path = (pair[0] ?? pair[1])?.relativePath ?? ''\n return unavailableSha1PathEvent({ relativePath: path })\n}\n\nfunction unavailableSha1PathEvent(path: Pick<SyncPath, 'relativePath'>): SyncSkipEvent {\n return {\n type: 'skip',\n path: path.relativePath,\n size: 0,\n message: 'sha1 comparison skipped because a verifiable SHA-1 is unavailable',\n }\n}\n\nfunction normalizeConcurrency(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value < 1) return 1\n return Math.max(1, Math.floor(value))\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../../src/sync/policies/compare.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../../src/sync/policies/compare.js"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../../src/sync/policies/compare.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../../src/sync/policies/compare.js"],"names":[],"mappings":"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"}
|