@backblaze-labs/b2-sdk 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +86 -1
- package/README.md +38 -7
- 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/auth/reauth-coalescer.cjs +65 -0
- package/dist/auth/reauth-coalescer.cjs.map +1 -0
- package/dist/auth/reauth-coalescer.d.cts +34 -0
- package/dist/auth/reauth-coalescer.d.cts.map +1 -0
- package/dist/auth/reauth-coalescer.d.ts +34 -0
- package/dist/auth/reauth-coalescer.d.ts.map +1 -0
- package/dist/auth/reauth-coalescer.js +65 -0
- package/dist/auth/reauth-coalescer.js.map +1 -0
- package/dist/backup/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 +168 -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 +168 -0
- package/dist/backup/raw.js.map +1 -0
- package/dist/bucket-validation.cjs +111 -0
- package/dist/bucket-validation.cjs.map +1 -0
- package/dist/bucket-validation.d.cts +27 -0
- package/dist/bucket-validation.d.cts.map +1 -0
- package/dist/bucket-validation.d.ts +27 -0
- package/dist/bucket-validation.d.ts.map +1 -0
- package/dist/bucket-validation.js +109 -0
- package/dist/bucket-validation.js.map +1 -0
- package/dist/bucket.cjs +125 -74
- package/dist/bucket.cjs.map +1 -1
- package/dist/bucket.d.cts +156 -61
- package/dist/bucket.d.cts.map +1 -1
- package/dist/bucket.d.ts +156 -61
- package/dist/bucket.d.ts.map +1 -1
- package/dist/bucket.js +125 -74
- package/dist/bucket.js.map +1 -1
- package/dist/client.cjs +29 -8
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +30 -9
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.ts +30 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +29 -8
- package/dist/client.js.map +1 -1
- package/dist/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 +179 -19
- package/dist/download/single.cjs.map +1 -1
- package/dist/download/single.d.cts +10 -4
- package/dist/download/single.d.cts.map +1 -1
- package/dist/download/single.d.ts +10 -4
- package/dist/download/single.d.ts.map +1 -1
- package/dist/download/single.js +179 -19
- package/dist/download/single.js.map +1 -1
- package/dist/errors/index.cjs +196 -3
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +134 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +134 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +183 -4
- package/dist/errors/index.js.map +1 -1
- package/dist/http/non-idempotent-mutations.cjs +38 -0
- package/dist/http/non-idempotent-mutations.cjs.map +1 -0
- package/dist/http/non-idempotent-mutations.d.cts +25 -0
- package/dist/http/non-idempotent-mutations.d.cts.map +1 -0
- package/dist/http/non-idempotent-mutations.d.ts +25 -0
- package/dist/http/non-idempotent-mutations.d.ts.map +1 -0
- package/dist/http/non-idempotent-mutations.js +35 -0
- package/dist/http/non-idempotent-mutations.js.map +1 -0
- package/dist/http/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 +62 -23
- package/dist/http/transport.cjs.map +1 -1
- package/dist/http/transport.d.cts +30 -3
- package/dist/http/transport.d.cts.map +1 -1
- package/dist/http/transport.d.ts +30 -3
- package/dist/http/transport.d.ts.map +1 -1
- package/dist/http/transport.js +60 -22
- package/dist/http/transport.js.map +1 -1
- package/dist/http/user-agent.cjs +12 -1
- package/dist/http/user-agent.cjs.map +1 -1
- package/dist/http/user-agent.d.cts +8 -0
- package/dist/http/user-agent.d.cts.map +1 -1
- package/dist/http/user-agent.d.ts +8 -0
- package/dist/http/user-agent.d.ts.map +1 -1
- package/dist/http/user-agent.js +13 -3
- package/dist/http/user-agent.js.map +1 -1
- package/dist/index.cjs +50 -2
- package/dist/index.d.cts +15 -12
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +15 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -10
- package/dist/notifications/index.cjs +3 -0
- package/dist/notifications/index.d.cts +2 -0
- package/dist/notifications/index.d.cts.map +1 -1
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +2 -1
- package/dist/object.cjs +13 -2
- package/dist/object.cjs.map +1 -1
- package/dist/object.d.cts +11 -3
- package/dist/object.d.cts.map +1 -1
- package/dist/object.d.ts +11 -3
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +13 -2
- 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 +156 -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 +156 -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 +270 -0
- package/dist/partner/client.cjs.map +1 -0
- package/dist/partner/client.d.cts +357 -0
- package/dist/partner/client.d.cts.map +1 -0
- package/dist/partner/client.d.ts +357 -0
- package/dist/partner/client.d.ts.map +1 -0
- package/dist/partner/client.js +270 -0
- package/dist/partner/client.js.map +1 -0
- package/dist/partner/endpoint-url.cjs +109 -0
- package/dist/partner/endpoint-url.cjs.map +1 -0
- package/dist/partner/endpoint-url.d.cts +74 -0
- package/dist/partner/endpoint-url.d.cts.map +1 -0
- package/dist/partner/endpoint-url.d.ts +74 -0
- package/dist/partner/endpoint-url.d.ts.map +1 -0
- package/dist/partner/endpoint-url.js +106 -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 +27 -0
- package/dist/partner/index.d.cts +32 -0
- package/dist/partner/index.d.cts.map +1 -0
- package/dist/partner/index.d.ts +32 -0
- package/dist/partner/index.d.ts.map +1 -0
- package/dist/partner/index.js +9 -0
- package/dist/partner/raw.cjs +400 -0
- package/dist/partner/raw.cjs.map +1 -0
- package/dist/partner/raw.d.cts +220 -0
- package/dist/partner/raw.d.cts.map +1 -0
- package/dist/partner/raw.d.ts +220 -0
- package/dist/partner/raw.d.ts.map +1 -0
- package/dist/partner/raw.js +398 -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 +292 -0
- package/dist/partner/redaction.cjs.map +1 -0
- package/dist/partner/redaction.d.cts +158 -0
- package/dist/partner/redaction.d.cts.map +1 -0
- package/dist/partner/redaction.d.ts +158 -0
- package/dist/partner/redaction.d.ts.map +1 -0
- package/dist/partner/redaction.js +281 -0
- package/dist/partner/redaction.js.map +1 -0
- package/dist/raw/encoding.cjs +29 -10
- package/dist/raw/encoding.cjs.map +1 -1
- package/dist/raw/encoding.d.cts +3 -2
- package/dist/raw/encoding.d.cts.map +1 -1
- package/dist/raw/encoding.d.ts +3 -2
- package/dist/raw/encoding.d.ts.map +1 -1
- package/dist/raw/encoding.js +29 -10
- package/dist/raw/encoding.js.map +1 -1
- package/dist/raw/index.cjs +131 -44
- package/dist/raw/index.cjs.map +1 -1
- package/dist/raw/index.d.cts +21 -15
- package/dist/raw/index.d.cts.map +1 -1
- package/dist/raw/index.d.ts +21 -15
- package/dist/raw/index.d.ts.map +1 -1
- package/dist/raw/index.js +114 -45
- 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 +1579 -257
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.cts +124 -11
- package/dist/simulator/index.d.cts.map +1 -1
- package/dist/simulator/index.d.ts +124 -11
- package/dist/simulator/index.d.ts.map +1 -1
- package/dist/simulator/index.js +1568 -262
- package/dist/simulator/index.js.map +1 -1
- package/dist/simulator/partner-capabilities.cjs +77 -0
- package/dist/simulator/partner-capabilities.cjs.map +1 -0
- package/dist/simulator/partner-capabilities.d.cts +53 -0
- package/dist/simulator/partner-capabilities.d.cts.map +1 -0
- package/dist/simulator/partner-capabilities.d.ts +53 -0
- package/dist/simulator/partner-capabilities.d.ts.map +1 -0
- package/dist/simulator/partner-capabilities.js +73 -0
- package/dist/simulator/partner-capabilities.js.map +1 -0
- package/dist/simulator/partner.cjs +713 -0
- package/dist/simulator/partner.cjs.map +1 -0
- package/dist/simulator/partner.d.cts +186 -0
- package/dist/simulator/partner.d.cts.map +1 -0
- package/dist/simulator/partner.d.ts +186 -0
- package/dist/simulator/partner.d.ts.map +1 -0
- package/dist/simulator/partner.js +713 -0
- package/dist/simulator/partner.js.map +1 -0
- package/dist/simulator/validation.cjs +732 -25
- package/dist/simulator/validation.cjs.map +1 -1
- package/dist/simulator/validation.d.cts +145 -6
- package/dist/simulator/validation.d.cts.map +1 -1
- package/dist/simulator/validation.d.ts +145 -6
- package/dist/simulator/validation.d.ts.map +1 -1
- package/dist/simulator/validation.js +705 -26
- package/dist/simulator/validation.js.map +1 -1
- package/dist/streams/file-source.cjs +2 -2
- package/dist/streams/file-source.cjs.map +1 -1
- package/dist/streams/file-source.js +2 -2
- package/dist/streams/file-source.js.map +1 -1
- package/dist/streams/hash.cjs +246 -13
- package/dist/streams/hash.cjs.map +1 -1
- package/dist/streams/hash.d.cts +37 -12
- package/dist/streams/hash.d.cts.map +1 -1
- package/dist/streams/hash.d.ts +37 -12
- package/dist/streams/hash.d.ts.map +1 -1
- package/dist/streams/hash.js +246 -15
- package/dist/streams/hash.js.map +1 -1
- package/dist/streams/index.cjs +3 -1
- package/dist/streams/index.d.cts +1 -1
- package/dist/streams/index.d.cts.map +1 -1
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.d.ts.map +1 -1
- package/dist/streams/index.js +3 -3
- package/dist/streams/progress.cjs +63 -4
- package/dist/streams/progress.cjs.map +1 -1
- package/dist/streams/progress.d.cts +28 -2
- package/dist/streams/progress.d.cts.map +1 -1
- package/dist/streams/progress.d.ts +28 -2
- package/dist/streams/progress.d.ts.map +1 -1
- package/dist/streams/progress.js +63 -4
- package/dist/streams/progress.js.map +1 -1
- package/dist/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 +156 -43
- package/dist/sync/download-staging.cjs.map +1 -1
- package/dist/sync/download-staging.d.cts +26 -2
- package/dist/sync/download-staging.d.cts.map +1 -1
- package/dist/sync/download-staging.d.ts +26 -2
- package/dist/sync/download-staging.d.ts.map +1 -1
- package/dist/sync/download-staging.js +155 -44
- 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/index.d.cts +1 -1
- package/dist/sync/index.d.cts.map +1 -1
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/local-file-io.cjs +15 -6
- package/dist/sync/local-file-io.cjs.map +1 -1
- package/dist/sync/local-file-io.d.cts +3 -0
- package/dist/sync/local-file-io.d.cts.map +1 -1
- package/dist/sync/local-file-io.d.ts +3 -0
- package/dist/sync/local-file-io.d.ts.map +1 -1
- package/dist/sync/local-file-io.js +15 -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/pairing.cjs +1 -0
- package/dist/sync/pairing.cjs.map +1 -1
- package/dist/sync/pairing.js +1 -0
- package/dist/sync/pairing.js.map +1 -1
- package/dist/sync/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 +4 -2
- package/dist/sync/scanners/b2.cjs.map +1 -1
- package/dist/sync/scanners/b2.d.cts.map +1 -1
- package/dist/sync/scanners/b2.d.ts.map +1 -1
- package/dist/sync/scanners/b2.js +4 -2
- package/dist/sync/scanners/b2.js.map +1 -1
- package/dist/sync/scanners/local.cjs +150 -33
- package/dist/sync/scanners/local.cjs.map +1 -1
- package/dist/sync/scanners/local.d.cts +6 -0
- package/dist/sync/scanners/local.d.cts.map +1 -1
- package/dist/sync/scanners/local.d.ts +6 -0
- package/dist/sync/scanners/local.d.ts.map +1 -1
- package/dist/sync/scanners/local.js +150 -33
- package/dist/sync/scanners/local.js.map +1 -1
- package/dist/sync/synchronizer.cjs +79 -31
- package/dist/sync/synchronizer.cjs.map +1 -1
- package/dist/sync/synchronizer.d.cts +1 -0
- package/dist/sync/synchronizer.d.cts.map +1 -1
- package/dist/sync/synchronizer.d.ts +1 -0
- package/dist/sync/synchronizer.d.ts.map +1 -1
- package/dist/sync/synchronizer.js +79 -31
- package/dist/sync/synchronizer.js.map +1 -1
- package/dist/sync/types.d.cts +19 -3
- package/dist/sync/types.d.cts.map +1 -1
- package/dist/sync/types.d.ts +19 -3
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/types/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 +85 -0
- package/dist/types/backup.d.cts.map +1 -0
- package/dist/types/backup.d.ts +85 -0
- package/dist/types/backup.d.ts.map +1 -0
- package/dist/types/bucket.cjs +81 -11
- package/dist/types/bucket.cjs.map +1 -1
- package/dist/types/bucket.d.cts +194 -41
- package/dist/types/bucket.d.cts.map +1 -1
- package/dist/types/bucket.d.ts +194 -41
- package/dist/types/bucket.d.ts.map +1 -1
- package/dist/types/bucket.js +67 -12
- package/dist/types/bucket.js.map +1 -1
- package/dist/types/download.cjs +62 -0
- package/dist/types/download.cjs.map +1 -0
- package/dist/types/download.d.cts +100 -16
- package/dist/types/download.d.cts.map +1 -1
- package/dist/types/download.d.ts +100 -16
- 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 +76 -229
- package/dist/types/encryption.cjs.map +1 -1
- package/dist/types/encryption.d.cts +48 -6
- package/dist/types/encryption.d.cts.map +1 -1
- package/dist/types/encryption.d.ts +48 -6
- package/dist/types/encryption.d.ts.map +1 -1
- package/dist/types/encryption.js +74 -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 +3 -0
- package/dist/types/file.cjs.map +1 -1
- package/dist/types/file.d.cts +140 -18
- package/dist/types/file.d.cts.map +1 -1
- package/dist/types/file.d.ts +140 -18
- package/dist/types/file.d.ts.map +1 -1
- package/dist/types/file.js +3 -1
- package/dist/types/file.js.map +1 -1
- package/dist/types/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 +16 -12
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +16 -12
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/key.d.cts +4 -3
- package/dist/types/key.d.cts.map +1 -1
- package/dist/types/key.d.ts +4 -3
- package/dist/types/key.d.ts.map +1 -1
- package/dist/types/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 +31 -1
- package/dist/types/notifications.cjs.map +1 -1
- package/dist/types/notifications.d.cts +31 -4
- package/dist/types/notifications.d.cts.map +1 -1
- package/dist/types/notifications.d.ts +31 -4
- package/dist/types/notifications.d.ts.map +1 -1
- package/dist/types/notifications.js +30 -2
- 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 +386 -0
- package/dist/types/partner.d.cts.map +1 -0
- package/dist/types/partner.d.ts +386 -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 +35 -18
- package/dist/types/upload.d.cts.map +1 -1
- package/dist/types/upload.d.ts +35 -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 +15 -8
- package/dist/upload/large.cjs.map +1 -1
- package/dist/upload/large.d.cts +31 -8
- package/dist/upload/large.d.cts.map +1 -1
- package/dist/upload/large.d.ts +31 -8
- package/dist/upload/large.d.ts.map +1 -1
- package/dist/upload/large.js +15 -8
- package/dist/upload/large.js.map +1 -1
- package/dist/upload/options.cjs.map +1 -1
- package/dist/upload/options.d.cts +24 -0
- package/dist/upload/options.d.cts.map +1 -1
- package/dist/upload/options.d.ts +24 -0
- package/dist/upload/options.d.ts.map +1 -1
- package/dist/upload/options.js.map +1 -1
- package/dist/upload/resume.cjs +6 -4
- package/dist/upload/resume.cjs.map +1 -1
- package/dist/upload/resume.d.cts +9 -5
- package/dist/upload/resume.d.cts.map +1 -1
- package/dist/upload/resume.d.ts +9 -5
- package/dist/upload/resume.d.ts.map +1 -1
- package/dist/upload/resume.js +6 -4
- package/dist/upload/resume.js.map +1 -1
- package/dist/upload/retry.cjs +25 -13
- package/dist/upload/retry.cjs.map +1 -1
- package/dist/upload/retry.d.cts.map +1 -1
- package/dist/upload/retry.d.ts.map +1 -1
- package/dist/upload/retry.js +25 -13
- package/dist/upload/retry.js.map +1 -1
- package/dist/upload/single.cjs +35 -30
- package/dist/upload/single.cjs.map +1 -1
- package/dist/upload/single.d.cts +12 -0
- package/dist/upload/single.d.cts.map +1 -1
- package/dist/upload/single.d.ts +12 -0
- package/dist/upload/single.d.ts.map +1 -1
- package/dist/upload/single.js +35 -30
- package/dist/upload/single.js.map +1 -1
- package/dist/upload/stream.cjs +18 -5
- package/dist/upload/stream.cjs.map +1 -1
- package/dist/upload/stream.d.cts +12 -0
- package/dist/upload/stream.d.cts.map +1 -1
- package/dist/upload/stream.d.ts +12 -0
- package/dist/upload/stream.d.ts.map +1 -1
- package/dist/upload/stream.js +15 -2
- 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/dist/version.cjs +56 -4
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +53 -4
- package/dist/version.d.cts.map +1 -1
- package/dist/version.d.ts +53 -4
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +52 -5
- package/dist/version.js.map +1 -1
- package/package.json +47 -18
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { localFilesystemErrorReason } from "./filesystem-errors.js";
|
|
1
2
|
import { assertPathInsideRoot, hasErrorCode, noFollowFlag } from "./path-safety.js";
|
|
2
3
|
//#region src/sync/download-staging.ts
|
|
3
4
|
/** @internal */
|
|
@@ -6,12 +7,15 @@ var DOWNLOAD_STAGING_DIRECTORY_NAME = ".b2sdk-download-staging";
|
|
|
6
7
|
var DOWNLOAD_STAGING_MARKER_NAME = ".b2sdk-staging-marker.partial";
|
|
7
8
|
var CANONICAL_DOWNLOAD_STAGING_DIRECTORY_NAME = canonicalLocalFilesystemSegment(DOWNLOAD_STAGING_DIRECTORY_NAME);
|
|
8
9
|
var DOWNLOAD_STAGING_ENTRY_SUFFIX = ".download";
|
|
9
|
-
var STALE_DOWNLOAD_STAGING_AGE_MS =
|
|
10
|
+
var STALE_DOWNLOAD_STAGING_AGE_MS = 864e5;
|
|
10
11
|
var MAX_STAGING_CLEANUP_CONCURRENCY = 8;
|
|
11
12
|
var MAX_CLEANUP_WARNING_ENTRIES = 3;
|
|
12
13
|
/** @internal */
|
|
13
14
|
var DOWNLOAD_STAGING_ACTIVITY_ENTRY_LIMIT = 1024;
|
|
14
15
|
var reapedManagedDirectories = /* @__PURE__ */ new Map();
|
|
16
|
+
var activeManagedDirectoryCreations = /* @__PURE__ */ new Map();
|
|
17
|
+
/** @internal */
|
|
18
|
+
var downloadStagingTestHooks = {};
|
|
15
19
|
/**
|
|
16
20
|
* Checks whether a single path segment matches the SDK-managed staging directory name.
|
|
17
21
|
* @param segment - Candidate path segment.
|
|
@@ -31,48 +35,123 @@ function isDownloadStagingDirectorySegment(segment) {
|
|
|
31
35
|
* @param randomUUID - UUID provider used to create unique staging entries.
|
|
32
36
|
* @param statForDeviceCheck - Stat function used to verify filesystem devices.
|
|
33
37
|
* @param beforeStagingMarkerWrite - Test hook called before marker creation.
|
|
38
|
+
* @param activityEntryLimit - Maximum entry count inspected for stale staging activity.
|
|
39
|
+
* @param afterManagedStagingMarkerWrite - Test hook called after managed-root marker creation.
|
|
34
40
|
*
|
|
35
|
-
* @returns The resolved staging directory
|
|
41
|
+
* @returns The resolved managed and per-run staging directory paths.
|
|
36
42
|
*
|
|
37
43
|
* @internal
|
|
38
44
|
*/
|
|
39
|
-
async function createDownloadStagingDirectory(rootRealPath, path, randomUUID, statForDeviceCheck, beforeStagingMarkerWrite) {
|
|
45
|
+
async function createDownloadStagingDirectory(rootRealPath, path, randomUUID, statForDeviceCheck, beforeStagingMarkerWrite, activityEntryLimit = DOWNLOAD_STAGING_ACTIVITY_ENTRY_LIMIT, afterManagedStagingMarkerWrite) {
|
|
40
46
|
const { chmod, lstat, mkdir, readdir, realpath, rm } = await import("node:fs/promises");
|
|
41
47
|
const managedDirectory = path.join(rootRealPath, DOWNLOAD_STAGING_DIRECTORY_NAME);
|
|
48
|
+
const releaseManagedDirectoryCreation = retainDownloadStagingRootCreation(managedDirectory);
|
|
42
49
|
try {
|
|
43
|
-
|
|
50
|
+
try {
|
|
51
|
+
await mkdir(managedDirectory, { mode: PRIVATE_DOWNLOAD_DIRECTORY_MODE });
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (!hasErrorCode(err, "EEXIST")) throw err;
|
|
54
|
+
}
|
|
55
|
+
if (!(await lstat(managedDirectory)).isDirectory()) throw new Error(`unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is not a directory`);
|
|
56
|
+
const realManagedDirectory = await realpath(managedDirectory);
|
|
57
|
+
assertPathInsideRoot(rootRealPath, realManagedDirectory, path);
|
|
58
|
+
await assertDownloadPathSameDevice(rootRealPath, realManagedDirectory, statForDeviceCheck, "unsafe local destination path: cannot stage download across filesystems");
|
|
59
|
+
if (!await isManagedDownloadStagingRoot(realManagedDirectory)) {
|
|
60
|
+
if ((await readdir(realManagedDirectory)).length > 0 && !await isManagedDownloadStagingRoot(realManagedDirectory)) throw new Error(`unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is reserved for SDK download staging`);
|
|
61
|
+
}
|
|
62
|
+
await beforeStagingMarkerWrite?.(realManagedDirectory);
|
|
63
|
+
await writeStagingMarker(realManagedDirectory, path);
|
|
64
|
+
await afterManagedStagingMarkerWrite?.(realManagedDirectory);
|
|
65
|
+
/* v8 ignore next -- best-effort chmod */
|
|
66
|
+
await chmod(realManagedDirectory, PRIVATE_DOWNLOAD_DIRECTORY_MODE).catch(() => {});
|
|
67
|
+
await reapStaleDownloadStagingDirectoriesOnce(realManagedDirectory, path, Date.now(), activityEntryLimit);
|
|
68
|
+
const stagingDirectory = path.join(realManagedDirectory, `${Date.now()}-${randomUUID()}${DOWNLOAD_STAGING_ENTRY_SUFFIX}`);
|
|
69
|
+
await mkdir(stagingDirectory, { mode: PRIVATE_DOWNLOAD_DIRECTORY_MODE });
|
|
70
|
+
/* v8 ignore next -- best-effort chmod */
|
|
71
|
+
await chmod(stagingDirectory, PRIVATE_DOWNLOAD_DIRECTORY_MODE).catch(() => {});
|
|
72
|
+
try {
|
|
73
|
+
const realStagingDirectory = await realpath(stagingDirectory);
|
|
74
|
+
assertPathInsideRoot(realManagedDirectory, realStagingDirectory, path);
|
|
75
|
+
await assertDownloadPathSameDevice(rootRealPath, realStagingDirectory, statForDeviceCheck, "unsafe local destination path: cannot stage download across filesystems");
|
|
76
|
+
await beforeStagingMarkerWrite?.(realStagingDirectory);
|
|
77
|
+
await writeStagingMarker(realStagingDirectory, path);
|
|
78
|
+
return {
|
|
79
|
+
managedDirectory: realManagedDirectory,
|
|
80
|
+
stagingDirectory: realStagingDirectory
|
|
81
|
+
};
|
|
82
|
+
} catch (err) {
|
|
83
|
+
/* v8 ignore next -- best-effort cleanup */
|
|
84
|
+
await rm(stagingDirectory, {
|
|
85
|
+
recursive: true,
|
|
86
|
+
force: true
|
|
87
|
+
}).catch(() => {});
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
} finally {
|
|
91
|
+
releaseManagedDirectoryCreation();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Removes the SDK root marker and the now-empty SDK-managed staging root
|
|
96
|
+
* once all per-run `.download` entries are gone.
|
|
97
|
+
* @param rootRealPath - Resolved local sync root path.
|
|
98
|
+
* @param managedDirectory - Resolved SDK-managed staging root path.
|
|
99
|
+
* @param path - Node path module used for platform-specific path operations.
|
|
100
|
+
*
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
async function removeDownloadStagingRootMarkerAndDirectory(rootRealPath, managedDirectory, path) {
|
|
104
|
+
try {
|
|
105
|
+
await removeDownloadStagingRootMarkerAndDirectoryCore(rootRealPath, managedDirectory, path);
|
|
44
106
|
} catch (err) {
|
|
45
|
-
|
|
107
|
+
emitCleanupWarning(`failed to remove B2 SDK download staging root: ${localFilesystemErrorReason(err)}`);
|
|
46
108
|
}
|
|
47
|
-
|
|
48
|
-
|
|
109
|
+
}
|
|
110
|
+
async function removeDownloadStagingRootMarkerAndDirectoryCore(rootRealPath, managedDirectory, path) {
|
|
111
|
+
const { lstat, readdir, realpath, rmdir, unlink } = await import("node:fs/promises");
|
|
112
|
+
if (downloadStagingRootCreationIsActive(managedDirectory)) return;
|
|
113
|
+
const managedStats = await lstat(managedDirectory).catch((err) => {
|
|
114
|
+
if (hasErrorCode(err, "ENOENT")) return void 0;
|
|
115
|
+
throw err;
|
|
116
|
+
});
|
|
117
|
+
if (managedStats === void 0 || !managedStats.isDirectory()) return;
|
|
118
|
+
const realManagedDirectory = await realpath(managedDirectory).catch((err) => {
|
|
119
|
+
if (hasErrorCode(err, "ENOENT")) return void 0;
|
|
120
|
+
throw err;
|
|
121
|
+
});
|
|
122
|
+
if (realManagedDirectory === void 0) return;
|
|
49
123
|
assertPathInsideRoot(rootRealPath, realManagedDirectory, path);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
await
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
124
|
+
if (downloadStagingRootCreationIsActive(realManagedDirectory)) return;
|
|
125
|
+
const markerPath = path.join(realManagedDirectory, DOWNLOAD_STAGING_MARKER_NAME);
|
|
126
|
+
if ((await lstat(markerPath).catch((err) => {
|
|
127
|
+
if (hasErrorCode(err, "ENOENT")) return void 0;
|
|
128
|
+
throw err;
|
|
129
|
+
}))?.isFile() !== true) return;
|
|
130
|
+
await downloadStagingTestHooks.afterCleanupMarkerStat?.(realManagedDirectory);
|
|
131
|
+
const entries = await readdir(realManagedDirectory, { withFileTypes: true }).catch((err) => {
|
|
132
|
+
if (hasErrorCode(err, "ENOENT")) return void 0;
|
|
133
|
+
throw err;
|
|
134
|
+
});
|
|
135
|
+
if (entries === void 0) return;
|
|
136
|
+
if (entries.length !== 1 || entries[0]?.name !== ".b2sdk-staging-marker.partial") return;
|
|
137
|
+
if (await realpath(managedDirectory).catch(() => void 0) !== realManagedDirectory) return;
|
|
138
|
+
if (downloadStagingRootCreationIsActive(realManagedDirectory)) return;
|
|
139
|
+
await downloadStagingTestHooks.beforeCleanupMarkerUnlink?.(markerPath);
|
|
140
|
+
if (!await unlink(markerPath).then(() => true).catch((err) => {
|
|
141
|
+
if (hasErrorCode(err, "ENOENT")) return false;
|
|
142
|
+
throw err;
|
|
143
|
+
})) return;
|
|
63
144
|
try {
|
|
64
|
-
|
|
65
|
-
assertPathInsideRoot(realManagedDirectory, realStagingDirectory, path);
|
|
66
|
-
await assertDownloadPathSameDevice(rootRealPath, realStagingDirectory, statForDeviceCheck, "unsafe local destination path: cannot stage download across filesystems");
|
|
67
|
-
await beforeStagingMarkerWrite?.(realStagingDirectory);
|
|
68
|
-
await writeStagingMarker(realStagingDirectory, path);
|
|
69
|
-
return realStagingDirectory;
|
|
145
|
+
await rmdir(realManagedDirectory);
|
|
70
146
|
} catch (err) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
147
|
+
if (hasErrorCode(err, "ENOENT")) return;
|
|
148
|
+
if (hasErrorCode(err, "ENOTEMPTY") || hasErrorCode(err, "EEXIST")) {
|
|
149
|
+
await restoreDownloadStagingRootMarker(realManagedDirectory, path);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
await restoreDownloadStagingRootMarker(realManagedDirectory, path).catch((restoreErr) => {
|
|
153
|
+
emitCleanupWarning(`failed to restore B2 SDK download staging marker: ${localFilesystemErrorReason(restoreErr)}`);
|
|
154
|
+
});
|
|
76
155
|
throw err;
|
|
77
156
|
}
|
|
78
157
|
}
|
|
@@ -118,7 +197,9 @@ async function writeStagingMarker(directory, path) {
|
|
|
118
197
|
/* v8 ignore next -- best-effort chmod */
|
|
119
198
|
await handle.chmod(PRIVATE_DOWNLOAD_FILE_MODE).catch(() => {});
|
|
120
199
|
} catch (err) {
|
|
121
|
-
if (hasErrorCode(err, "EEXIST") ||
|
|
200
|
+
if (hasErrorCode(err, "EEXIST") ||
|
|
201
|
+
/* v8 ignore next -- ELOOP depends on platform-specific O_NOFOLLOW behavior */
|
|
202
|
+
hasErrorCode(err, "ELOOP")) {
|
|
122
203
|
if ((await lstat(markerPath).catch(() => void 0))?.isFile() === true) return;
|
|
123
204
|
throw new Error("unsafe local destination path: staging marker is not a regular file");
|
|
124
205
|
}
|
|
@@ -128,6 +209,24 @@ async function writeStagingMarker(directory, path) {
|
|
|
128
209
|
await handle?.close().catch(() => {});
|
|
129
210
|
}
|
|
130
211
|
}
|
|
212
|
+
function retainDownloadStagingRootCreation(managedDirectory) {
|
|
213
|
+
activeManagedDirectoryCreations.set(managedDirectory, (activeManagedDirectoryCreations.get(managedDirectory) ?? 0) + 1);
|
|
214
|
+
return () => {
|
|
215
|
+
const count = activeManagedDirectoryCreations.get(managedDirectory);
|
|
216
|
+
/* v8 ignore next -- releases are paired with retains in a single closure */
|
|
217
|
+
if (count === void 0 || count <= 1) activeManagedDirectoryCreations.delete(managedDirectory);
|
|
218
|
+
else activeManagedDirectoryCreations.set(managedDirectory, count - 1);
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function downloadStagingRootCreationIsActive(managedDirectory) {
|
|
222
|
+
return activeManagedDirectoryCreations.has(managedDirectory);
|
|
223
|
+
}
|
|
224
|
+
async function restoreDownloadStagingRootMarker(managedDirectory, path) {
|
|
225
|
+
const { lstat, realpath } = await import("node:fs/promises");
|
|
226
|
+
if ((await lstat(managedDirectory).catch(() => void 0))?.isDirectory() !== true) return;
|
|
227
|
+
if (await realpath(managedDirectory).catch(() => void 0) !== managedDirectory) return;
|
|
228
|
+
await writeStagingMarker(managedDirectory, path);
|
|
229
|
+
}
|
|
131
230
|
/**
|
|
132
231
|
* Verifies that a candidate path is on the same filesystem device as the root.
|
|
133
232
|
* @param rootRealPath - Resolved local sync root path.
|
|
@@ -141,33 +240,36 @@ async function assertDownloadPathSameDevice(rootRealPath, candidateRealPath, sta
|
|
|
141
240
|
const [rootStats, candidateStats] = await Promise.all([statForDeviceCheck(rootRealPath), statForDeviceCheck(candidateRealPath)]);
|
|
142
241
|
if (rootStats.dev !== candidateStats.dev) throw new Error(message);
|
|
143
242
|
}
|
|
144
|
-
async function reapStaleDownloadStagingDirectoriesOnce(managedDirectory, path, nowMillis) {
|
|
243
|
+
async function reapStaleDownloadStagingDirectoriesOnce(managedDirectory, path, nowMillis, activityEntryLimit) {
|
|
145
244
|
const previous = reapedManagedDirectories.get(managedDirectory);
|
|
146
245
|
if (previous !== void 0) {
|
|
147
246
|
await previous;
|
|
148
247
|
return;
|
|
149
248
|
}
|
|
150
|
-
const next = reapStaleDownloadStagingDirectories(managedDirectory, path, nowMillis).finally(() => {
|
|
249
|
+
const next = reapStaleDownloadStagingDirectories(managedDirectory, path, nowMillis, activityEntryLimit).finally(() => {
|
|
250
|
+
/* v8 ignore next -- this map entry cannot be replaced until the current cleanup settles */
|
|
151
251
|
if (reapedManagedDirectories.get(managedDirectory) === next) reapedManagedDirectories.delete(managedDirectory);
|
|
152
252
|
});
|
|
153
253
|
reapedManagedDirectories.set(managedDirectory, next);
|
|
154
254
|
await next;
|
|
155
255
|
}
|
|
156
|
-
async function reapStaleDownloadStagingDirectories(managedDirectory, path, nowMillis) {
|
|
256
|
+
async function reapStaleDownloadStagingDirectories(managedDirectory, path, nowMillis, activityEntryLimit) {
|
|
157
257
|
const { readdir, realpath, rm } = await import("node:fs/promises");
|
|
158
258
|
let entries;
|
|
159
259
|
try {
|
|
160
260
|
entries = await readdir(managedDirectory, { withFileTypes: true });
|
|
161
261
|
} catch (err) {
|
|
262
|
+
/* v8 ignore next -- concurrent managed-root removal is a filesystem race without a stable hook */
|
|
162
263
|
if (hasErrorCode(err, "ENOENT")) return;
|
|
163
|
-
|
|
264
|
+
/* v8 ignore next -- platform-specific root permission failures cannot be simulated portably */
|
|
265
|
+
emitCleanupWarning(`failed to inspect B2 SDK download staging entries: ${localFilesystemErrorReason(err)}`);
|
|
164
266
|
return;
|
|
165
267
|
}
|
|
166
268
|
const cleanupErrors = [];
|
|
167
269
|
await forEachWithConcurrency(entries, MAX_STAGING_CLEANUP_CONCURRENCY, async (entry) => {
|
|
168
270
|
if (!entry.isDirectory() || !entry.name.endsWith(DOWNLOAD_STAGING_ENTRY_SUFFIX)) return;
|
|
169
271
|
const candidate = path.join(managedDirectory, entry.name);
|
|
170
|
-
const activity = await readManagedStagingEntryActivity(candidate, path);
|
|
272
|
+
const activity = await readManagedStagingEntryActivity(candidate, path, activityEntryLimit);
|
|
171
273
|
if (activity === void 0 || !stagingActivityIsStale(activity, nowMillis)) return;
|
|
172
274
|
const realCandidate = await realpath(candidate).catch(() => {
|
|
173
275
|
cleanupErrors.push({
|
|
@@ -175,11 +277,14 @@ async function reapStaleDownloadStagingDirectories(managedDirectory, path, nowMi
|
|
|
175
277
|
operation: "inspect"
|
|
176
278
|
});
|
|
177
279
|
});
|
|
280
|
+
/* v8 ignore next -- requires a concurrent delete between activity read and realpath */
|
|
178
281
|
if (realCandidate === void 0) return;
|
|
179
282
|
try {
|
|
180
283
|
assertPathInsideRoot(managedDirectory, realCandidate, path);
|
|
181
|
-
const latestActivity = await readManagedStagingEntryActivity(candidate, path);
|
|
182
|
-
if (latestActivity === void 0 || latestActivity.signature !== activity.signature || !stagingActivityIsStale(latestActivity, nowMillis))
|
|
284
|
+
const latestActivity = await readManagedStagingEntryActivity(candidate, path, activityEntryLimit);
|
|
285
|
+
if (latestActivity === void 0 || latestActivity.signature !== activity.signature || !stagingActivityIsStale(latestActivity, nowMillis))
|
|
286
|
+
/* v8 ignore next -- requires concurrent writes between the first and second activity reads */
|
|
287
|
+
return;
|
|
183
288
|
await rm(realCandidate, {
|
|
184
289
|
recursive: true,
|
|
185
290
|
force: true
|
|
@@ -196,16 +301,16 @@ async function reapStaleDownloadStagingDirectories(managedDirectory, path, nowMi
|
|
|
196
301
|
emitCleanupWarning(`failed to reap ${cleanupErrors.length} stale B2 SDK download staging ${noun}: ${cleanupErrors.slice(0, MAX_CLEANUP_WARNING_ENTRIES).map(formatCleanupWarning).join("; ")}`);
|
|
197
302
|
}
|
|
198
303
|
}
|
|
199
|
-
async function readManagedStagingEntryActivity(candidate, path) {
|
|
304
|
+
async function readManagedStagingEntryActivity(candidate, path, activityEntryLimit) {
|
|
200
305
|
const { lstat, readdir } = await import("node:fs/promises");
|
|
201
306
|
try {
|
|
202
307
|
const [directoryStats, markerStats] = await Promise.all([lstat(candidate), lstat(path.join(candidate, DOWNLOAD_STAGING_MARKER_NAME))]);
|
|
203
308
|
if (!directoryStats.isDirectory() || !markerStats.isFile()) return void 0;
|
|
204
309
|
const entries = await readdir(candidate, { withFileTypes: true });
|
|
205
|
-
if (entries.length >
|
|
310
|
+
if (entries.length > activityEntryLimit) return void 0;
|
|
206
311
|
const statsParts = [`.:${stagingStatsSignature(directoryStats)}`, `${DOWNLOAD_STAGING_MARKER_NAME}:${stagingStatsSignature(markerStats)}`];
|
|
207
312
|
let newestActivityMs = Math.max(stagingStatsActivityMs(directoryStats), stagingStatsActivityMs(markerStats));
|
|
208
|
-
for (const entry of [...entries].sort(
|
|
313
|
+
for (const entry of [...entries].sort(compareDirectoryEntriesByName)) {
|
|
209
314
|
const stats = await lstat(path.join(candidate, entry.name));
|
|
210
315
|
statsParts.push(`${entry.name}:${stagingStatsSignature(stats)}`);
|
|
211
316
|
newestActivityMs = Math.max(newestActivityMs, stagingStatsActivityMs(stats));
|
|
@@ -218,6 +323,12 @@ async function readManagedStagingEntryActivity(candidate, path) {
|
|
|
218
323
|
return;
|
|
219
324
|
}
|
|
220
325
|
}
|
|
326
|
+
function compareDirectoryEntriesByName(a, b) {
|
|
327
|
+
if (a.name < b.name) return -1;
|
|
328
|
+
if (a.name > b.name) return 1;
|
|
329
|
+
/* v8 ignore next -- duplicate directory names cannot occur in a single readdir result */
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
221
332
|
function stagingStatsActivityMs(stats) {
|
|
222
333
|
return stats.mtimeMs;
|
|
223
334
|
}
|
|
@@ -245,7 +356,7 @@ async function forEachWithConcurrency(items, concurrency, fn) {
|
|
|
245
356
|
while (index < items.length) {
|
|
246
357
|
const item = items[index];
|
|
247
358
|
index += 1;
|
|
248
|
-
|
|
359
|
+
await fn(item);
|
|
249
360
|
}
|
|
250
361
|
});
|
|
251
362
|
await Promise.all(workers);
|
|
@@ -254,6 +365,6 @@ function emitCleanupWarning(message) {
|
|
|
254
365
|
globalThis.process?.emitWarning?.(message, { code: "B2SDK_DOWNLOAD_STAGING_CLEANUP_FAILED" });
|
|
255
366
|
}
|
|
256
367
|
//#endregion
|
|
257
|
-
export { DOWNLOAD_STAGING_ACTIVITY_ENTRY_LIMIT, DOWNLOAD_STAGING_DIRECTORY_NAME, DOWNLOAD_STAGING_MARKER_NAME, assertDownloadPathSameDevice, createDownloadStagingDirectory, isDownloadStagingDirectorySegment, isManagedDownloadStagingRoot };
|
|
368
|
+
export { DOWNLOAD_STAGING_ACTIVITY_ENTRY_LIMIT, DOWNLOAD_STAGING_DIRECTORY_NAME, DOWNLOAD_STAGING_MARKER_NAME, assertDownloadPathSameDevice, createDownloadStagingDirectory, downloadStagingTestHooks, isDownloadStagingDirectorySegment, isManagedDownloadStagingRoot, removeDownloadStagingRootMarkerAndDirectory };
|
|
258
369
|
|
|
259
370
|
//# sourceMappingURL=download-staging.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download-staging.js","names":[],"sources":["../../src/sync/download-staging.ts"],"sourcesContent":["import { assertPathInsideRoot, hasErrorCode, noFollowFlag } from './path-safety.ts'\n\ntype DeviceStat = { readonly dev: number | bigint }\ntype DeviceStatFn = (path: string) => Promise<DeviceStat>\n\n/** @internal */\nexport const DOWNLOAD_STAGING_DIRECTORY_NAME = '.b2sdk-download-staging'\n/** @internal */\nexport const DOWNLOAD_STAGING_MARKER_NAME = '.b2sdk-staging-marker.partial'\n\nconst CANONICAL_DOWNLOAD_STAGING_DIRECTORY_NAME = canonicalLocalFilesystemSegment(\n DOWNLOAD_STAGING_DIRECTORY_NAME,\n)\nconst DOWNLOAD_STAGING_ENTRY_SUFFIX = '.download'\nconst STALE_DOWNLOAD_STAGING_AGE_MS = 24 * 60 * 60 * 1000\nconst MAX_STAGING_CLEANUP_CONCURRENCY = 8\nconst MAX_CLEANUP_WARNING_ENTRIES = 3\n/** @internal */\nexport const DOWNLOAD_STAGING_ACTIVITY_ENTRY_LIMIT = 1024\nconst reapedManagedDirectories = new Map<string, Promise<void>>()\n\ntype StagingActivitySnapshot = {\n readonly newestActivityMs: number\n readonly signature: string\n}\n\ntype CleanupWarning = {\n readonly entryName: string\n readonly operation: 'inspect' | 'remove'\n}\n\n/**\n * Checks whether a single path segment matches the SDK-managed staging directory name.\n * @param segment - Candidate path segment.\n *\n * @returns True when the segment is the reserved staging directory name under\n * local filesystem canonicalization.\n *\n * @internal\n */\nexport function isDownloadStagingDirectorySegment(segment: string | undefined): boolean {\n return (\n segment !== undefined &&\n canonicalLocalFilesystemSegment(segment) === CANONICAL_DOWNLOAD_STAGING_DIRECTORY_NAME\n )\n}\n\n/**\n * Creates a private SDK-managed staging directory under a local sync root.\n * @param rootRealPath - Resolved local sync root path.\n * @param path - Node path module used for platform-specific path operations.\n * @param randomUUID - UUID provider used to create unique staging entries.\n * @param statForDeviceCheck - Stat function used to verify filesystem devices.\n * @param beforeStagingMarkerWrite - Test hook called before marker creation.\n *\n * @returns The resolved staging directory path.\n *\n * @internal\n */\nexport async function createDownloadStagingDirectory(\n rootRealPath: string,\n path: typeof import('node:path'),\n randomUUID: () => string,\n statForDeviceCheck: DeviceStatFn,\n beforeStagingMarkerWrite?: (directory: string) => Promise<void> | void,\n): Promise<string> {\n const { chmod, lstat, mkdir, readdir, realpath, rm } = await import('node:fs/promises')\n const managedDirectory = path.join(rootRealPath, DOWNLOAD_STAGING_DIRECTORY_NAME)\n try {\n await mkdir(managedDirectory, { mode: PRIVATE_DOWNLOAD_DIRECTORY_MODE })\n } catch (err) {\n if (!hasErrorCode(err, 'EEXIST')) throw err\n }\n const managedStats = await lstat(managedDirectory)\n if (!managedStats.isDirectory()) {\n throw new Error(\n `unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is not a directory`,\n )\n }\n const realManagedDirectory = await realpath(managedDirectory)\n assertPathInsideRoot(rootRealPath, realManagedDirectory, path)\n await assertDownloadPathSameDevice(\n rootRealPath,\n realManagedDirectory,\n statForDeviceCheck,\n 'unsafe local destination path: cannot stage download across filesystems',\n )\n\n if (!(await isManagedDownloadStagingRoot(realManagedDirectory))) {\n const entries = await readdir(realManagedDirectory)\n if (entries.length > 0 && !(await isManagedDownloadStagingRoot(realManagedDirectory))) {\n throw new Error(\n `unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is reserved for SDK download staging`,\n )\n }\n }\n await beforeStagingMarkerWrite?.(realManagedDirectory)\n await writeStagingMarker(realManagedDirectory, path)\n /* v8 ignore next -- best-effort chmod */\n await chmod(realManagedDirectory, PRIVATE_DOWNLOAD_DIRECTORY_MODE).catch(() => {})\n await reapStaleDownloadStagingDirectoriesOnce(realManagedDirectory, path, Date.now())\n\n const stagingDirectory = path.join(\n realManagedDirectory,\n `${Date.now()}-${randomUUID()}${DOWNLOAD_STAGING_ENTRY_SUFFIX}`,\n )\n await mkdir(stagingDirectory, { mode: PRIVATE_DOWNLOAD_DIRECTORY_MODE })\n /* v8 ignore next -- best-effort chmod */\n await chmod(stagingDirectory, PRIVATE_DOWNLOAD_DIRECTORY_MODE).catch(() => {})\n try {\n const realStagingDirectory = await realpath(stagingDirectory)\n assertPathInsideRoot(realManagedDirectory, realStagingDirectory, path)\n await assertDownloadPathSameDevice(\n rootRealPath,\n realStagingDirectory,\n statForDeviceCheck,\n 'unsafe local destination path: cannot stage download across filesystems',\n )\n await beforeStagingMarkerWrite?.(realStagingDirectory)\n await writeStagingMarker(realStagingDirectory, path)\n return realStagingDirectory\n } catch (err) {\n /* v8 ignore next -- best-effort cleanup */\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n throw err\n }\n}\n\n/**\n * Returns true when a scan-root entry is an SDK-managed download staging root.\n * @param directory - Candidate directory to inspect.\n *\n * @returns True when the directory contains SDK staging markers.\n *\n * @internal\n */\nexport async function isManagedDownloadStagingRoot(directory: string): Promise<boolean> {\n const { lstat, readdir } = await import('node:fs/promises')\n const path = await import('node:path')\n\n try {\n const markerStats = await lstat(path.join(directory, DOWNLOAD_STAGING_MARKER_NAME))\n if (markerStats.isFile()) return true\n } catch (err) {\n if (!hasErrorCode(err, 'ENOENT')) return false\n }\n\n let entries: import('node:fs').Dirent[]\n try {\n entries = await readdir(directory, { withFileTypes: true })\n } catch {\n return false\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory() || !entry.name.endsWith(DOWNLOAD_STAGING_ENTRY_SUFFIX)) continue\n try {\n const markerStats = await lstat(\n path.join(directory, entry.name, DOWNLOAD_STAGING_MARKER_NAME),\n )\n if (markerStats.isFile()) return true\n } catch {\n // Keep checking siblings; one corrupt entry should not hide user content.\n }\n }\n return false\n}\n\nconst PRIVATE_DOWNLOAD_FILE_MODE = 0o600\nconst PRIVATE_DOWNLOAD_DIRECTORY_MODE = 0o700\n\nasync function writeStagingMarker(\n directory: string,\n path: typeof import('node:path'),\n): Promise<void> {\n const { constants } = await import('node:fs')\n const { lstat, open } = await import('node:fs/promises')\n const markerPath = path.join(directory, DOWNLOAD_STAGING_MARKER_NAME)\n let handle: Awaited<ReturnType<typeof open>> | undefined\n try {\n handle = await open(\n markerPath,\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 } catch (err) {\n if (hasErrorCode(err, 'EEXIST') || hasErrorCode(err, 'ELOOP')) {\n const markerStats = await lstat(markerPath).catch(() => undefined)\n if (markerStats?.isFile() === true) return\n throw new Error('unsafe local destination path: staging marker is not a regular file')\n }\n throw err\n } finally {\n /* v8 ignore next -- best-effort close */\n await handle?.close().catch(() => {})\n }\n}\n\n/**\n * Verifies that a candidate path is on the same filesystem device as the root.\n * @param rootRealPath - Resolved local sync root path.\n * @param candidateRealPath - Resolved candidate path to compare.\n * @param statForDeviceCheck - Stat function used to read device IDs.\n * @param message - Error message used when devices differ.\n *\n * @internal\n */\nexport async function assertDownloadPathSameDevice(\n rootRealPath: string,\n candidateRealPath: string,\n statForDeviceCheck: DeviceStatFn,\n message: string,\n): Promise<void> {\n const [rootStats, candidateStats] = await Promise.all([\n statForDeviceCheck(rootRealPath),\n statForDeviceCheck(candidateRealPath),\n ])\n if (rootStats.dev !== candidateStats.dev) throw new Error(message)\n}\n\nasync function reapStaleDownloadStagingDirectoriesOnce(\n managedDirectory: string,\n path: typeof import('node:path'),\n nowMillis: number,\n): Promise<void> {\n const previous = reapedManagedDirectories.get(managedDirectory)\n if (previous !== undefined) {\n await previous\n return\n }\n\n const next = reapStaleDownloadStagingDirectories(managedDirectory, path, nowMillis).finally(\n () => {\n if (reapedManagedDirectories.get(managedDirectory) === next) {\n reapedManagedDirectories.delete(managedDirectory)\n }\n },\n )\n reapedManagedDirectories.set(managedDirectory, next)\n await next\n}\n\nasync function reapStaleDownloadStagingDirectories(\n managedDirectory: string,\n path: typeof import('node:path'),\n nowMillis: number,\n): Promise<void> {\n const { readdir, realpath, rm } = await import('node:fs/promises')\n let entries: import('node:fs').Dirent[]\n try {\n entries = await readdir(managedDirectory, { withFileTypes: true })\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) return\n emitCleanupWarning('failed to inspect B2 SDK download staging entries')\n return\n }\n\n const cleanupErrors: CleanupWarning[] = []\n await forEachWithConcurrency(entries, MAX_STAGING_CLEANUP_CONCURRENCY, async (entry) => {\n if (!entry.isDirectory() || !entry.name.endsWith(DOWNLOAD_STAGING_ENTRY_SUFFIX)) return\n const candidate = path.join(managedDirectory, entry.name)\n const activity = await readManagedStagingEntryActivity(candidate, path)\n if (activity === undefined || !stagingActivityIsStale(activity, nowMillis)) return\n const realCandidate = await realpath(candidate).catch(() => {\n cleanupErrors.push({ entryName: entry.name, operation: 'inspect' })\n return undefined\n })\n if (realCandidate === undefined) return\n try {\n assertPathInsideRoot(managedDirectory, realCandidate, path)\n const latestActivity = await readManagedStagingEntryActivity(candidate, path)\n if (\n latestActivity === undefined ||\n latestActivity.signature !== activity.signature ||\n !stagingActivityIsStale(latestActivity, nowMillis)\n ) {\n return\n }\n await rm(realCandidate, { recursive: true, force: true })\n } catch {\n cleanupErrors.push({ entryName: entry.name, operation: 'remove' })\n }\n })\n\n if (cleanupErrors.length > 0) {\n const noun = cleanupErrors.length === 1 ? 'entry' : 'entries'\n emitCleanupWarning(\n `failed to reap ${cleanupErrors.length} stale B2 SDK download staging ${noun}: ${cleanupErrors\n .slice(0, MAX_CLEANUP_WARNING_ENTRIES)\n .map(formatCleanupWarning)\n .join('; ')}`,\n )\n }\n}\n\nasync function readManagedStagingEntryActivity(\n candidate: string,\n path: typeof import('node:path'),\n): Promise<StagingActivitySnapshot | undefined> {\n const { lstat, readdir } = await import('node:fs/promises')\n\n try {\n const [directoryStats, markerStats] = await Promise.all([\n lstat(candidate),\n lstat(path.join(candidate, DOWNLOAD_STAGING_MARKER_NAME)),\n ])\n if (!directoryStats.isDirectory() || !markerStats.isFile()) return undefined\n\n const entries = await readdir(candidate, { withFileTypes: true })\n if (entries.length > DOWNLOAD_STAGING_ACTIVITY_ENTRY_LIMIT) return undefined\n\n const statsParts = [\n `.:${stagingStatsSignature(directoryStats)}`,\n `${DOWNLOAD_STAGING_MARKER_NAME}:${stagingStatsSignature(markerStats)}`,\n ]\n let newestActivityMs = Math.max(\n stagingStatsActivityMs(directoryStats),\n stagingStatsActivityMs(markerStats),\n )\n\n for (const entry of [...entries].sort((a, b) =>\n a.name < b.name ? -1 : a.name > b.name ? 1 : 0,\n )) {\n const stats = await lstat(path.join(candidate, entry.name))\n statsParts.push(`${entry.name}:${stagingStatsSignature(stats)}`)\n newestActivityMs = Math.max(newestActivityMs, stagingStatsActivityMs(stats))\n }\n\n return { newestActivityMs, signature: statsParts.join('|') }\n } catch {\n return undefined\n }\n}\n\nfunction stagingStatsActivityMs(stats: { readonly mtimeMs: number }) {\n return stats.mtimeMs\n}\n\nfunction stagingStatsSignature(stats: {\n readonly ctimeMs: number\n readonly dev: number | bigint\n readonly ino: number | bigint\n readonly mtimeMs: number\n readonly size: number\n}): string {\n return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`\n}\n\nfunction stagingActivityIsStale(activity: StagingActivitySnapshot, nowMillis: number): boolean {\n return nowMillis - activity.newestActivityMs >= STALE_DOWNLOAD_STAGING_AGE_MS\n}\n\nfunction formatCleanupWarning(error: CleanupWarning): string {\n return `${error.operation} ${safeWarningName(error.entryName)}`\n}\n\nfunction safeWarningName(name: string): string {\n const chars = Array.from(name)\n const bounded = chars.slice(0, 80).join('')\n const suffix = chars.length > 80 ? '...' : ''\n return JSON.stringify(`${bounded.replace(/[^A-Za-z0-9._-]/g, '?')}${suffix}`)\n}\n\nfunction canonicalLocalFilesystemSegment(segment: string): string {\n return segment.normalize('NFC').toLocaleLowerCase('en-US')\n}\n\nasync function forEachWithConcurrency<T>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T) => Promise<void>,\n): Promise<void> {\n let index = 0\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (index < items.length) {\n const item = items[index]\n index += 1\n if (item !== undefined) await fn(item)\n }\n })\n await Promise.all(workers)\n}\n\nfunction emitCleanupWarning(message: string): void {\n const processLike = (\n globalThis as {\n process?: {\n emitWarning?: (warning: string, options?: { readonly code?: string }) => void\n }\n }\n ).process\n processLike?.emitWarning?.(message, { code: 'B2SDK_DOWNLOAD_STAGING_CLEANUP_FAILED' })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"download-staging.js","names":[],"sources":["../../src/sync/download-staging.ts"],"sourcesContent":["import { localFilesystemErrorReason } from './filesystem-errors.ts'\nimport { assertPathInsideRoot, hasErrorCode, noFollowFlag } from './path-safety.ts'\n\ntype DeviceStat = { readonly dev: number | bigint }\ntype DeviceStatFn = (path: string) => Promise<DeviceStat>\n\n/** @internal */\nexport const DOWNLOAD_STAGING_DIRECTORY_NAME = '.b2sdk-download-staging'\n/** @internal */\nexport const DOWNLOAD_STAGING_MARKER_NAME = '.b2sdk-staging-marker.partial'\n\nconst CANONICAL_DOWNLOAD_STAGING_DIRECTORY_NAME = canonicalLocalFilesystemSegment(\n DOWNLOAD_STAGING_DIRECTORY_NAME,\n)\nconst DOWNLOAD_STAGING_ENTRY_SUFFIX = '.download'\nconst STALE_DOWNLOAD_STAGING_AGE_MS = 24 * 60 * 60 * 1000\nconst MAX_STAGING_CLEANUP_CONCURRENCY = 8\nconst MAX_CLEANUP_WARNING_ENTRIES = 3\n/** @internal */\nexport const DOWNLOAD_STAGING_ACTIVITY_ENTRY_LIMIT = 1024\nconst reapedManagedDirectories = new Map<string, Promise<void>>()\nconst activeManagedDirectoryCreations = new Map<string, number>()\n\n/** @internal */\nexport const downloadStagingTestHooks: {\n afterCleanupMarkerStat?: (managedDirectory: string) => Promise<void> | void\n beforeCleanupMarkerUnlink?: (markerPath: string) => Promise<void> | void\n} = {}\n\ntype StagingActivitySnapshot = {\n readonly newestActivityMs: number\n readonly signature: string\n}\n\ntype CleanupWarning = {\n readonly entryName: string\n readonly operation: 'inspect' | 'remove'\n}\n\n/** @internal */\nexport interface DownloadStagingDirectory {\n /** Resolved SDK-managed staging root shared by download runs under one sync root. */\n readonly managedDirectory: string\n /** Resolved per-run staging directory for one download. */\n readonly stagingDirectory: string\n}\n\n/**\n * Checks whether a single path segment matches the SDK-managed staging directory name.\n * @param segment - Candidate path segment.\n *\n * @returns True when the segment is the reserved staging directory name under\n * local filesystem canonicalization.\n *\n * @internal\n */\nexport function isDownloadStagingDirectorySegment(segment: string | undefined): boolean {\n return (\n segment !== undefined &&\n canonicalLocalFilesystemSegment(segment) === CANONICAL_DOWNLOAD_STAGING_DIRECTORY_NAME\n )\n}\n\n/**\n * Creates a private SDK-managed staging directory under a local sync root.\n * @param rootRealPath - Resolved local sync root path.\n * @param path - Node path module used for platform-specific path operations.\n * @param randomUUID - UUID provider used to create unique staging entries.\n * @param statForDeviceCheck - Stat function used to verify filesystem devices.\n * @param beforeStagingMarkerWrite - Test hook called before marker creation.\n * @param activityEntryLimit - Maximum entry count inspected for stale staging activity.\n * @param afterManagedStagingMarkerWrite - Test hook called after managed-root marker creation.\n *\n * @returns The resolved managed and per-run staging directory paths.\n *\n * @internal\n */\nexport async function createDownloadStagingDirectory(\n rootRealPath: string,\n path: typeof import('node:path'),\n randomUUID: () => string,\n statForDeviceCheck: DeviceStatFn,\n beforeStagingMarkerWrite?: (directory: string) => Promise<void> | void,\n activityEntryLimit = DOWNLOAD_STAGING_ACTIVITY_ENTRY_LIMIT,\n afterManagedStagingMarkerWrite?: (directory: string) => Promise<void> | void,\n): Promise<DownloadStagingDirectory> {\n const { chmod, lstat, mkdir, readdir, realpath, rm } = await import('node:fs/promises')\n const managedDirectory = path.join(rootRealPath, DOWNLOAD_STAGING_DIRECTORY_NAME)\n const releaseManagedDirectoryCreation = retainDownloadStagingRootCreation(managedDirectory)\n try {\n try {\n await mkdir(managedDirectory, { mode: PRIVATE_DOWNLOAD_DIRECTORY_MODE })\n } catch (err) {\n if (!hasErrorCode(err, 'EEXIST')) throw err\n }\n const managedStats = await lstat(managedDirectory)\n if (!managedStats.isDirectory()) {\n throw new Error(\n `unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is not a directory`,\n )\n }\n const realManagedDirectory = await realpath(managedDirectory)\n assertPathInsideRoot(rootRealPath, realManagedDirectory, path)\n await assertDownloadPathSameDevice(\n rootRealPath,\n realManagedDirectory,\n statForDeviceCheck,\n 'unsafe local destination path: cannot stage download across filesystems',\n )\n\n if (!(await isManagedDownloadStagingRoot(realManagedDirectory))) {\n const entries = await readdir(realManagedDirectory)\n if (entries.length > 0 && !(await isManagedDownloadStagingRoot(realManagedDirectory))) {\n throw new Error(\n `unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is reserved for SDK download staging`,\n )\n }\n }\n await beforeStagingMarkerWrite?.(realManagedDirectory)\n await writeStagingMarker(realManagedDirectory, path)\n await afterManagedStagingMarkerWrite?.(realManagedDirectory)\n /* v8 ignore next -- best-effort chmod */\n await chmod(realManagedDirectory, PRIVATE_DOWNLOAD_DIRECTORY_MODE).catch(() => {})\n await reapStaleDownloadStagingDirectoriesOnce(\n realManagedDirectory,\n path,\n Date.now(),\n activityEntryLimit,\n )\n\n const stagingDirectory = path.join(\n realManagedDirectory,\n `${Date.now()}-${randomUUID()}${DOWNLOAD_STAGING_ENTRY_SUFFIX}`,\n )\n await mkdir(stagingDirectory, { mode: PRIVATE_DOWNLOAD_DIRECTORY_MODE })\n /* v8 ignore next -- best-effort chmod */\n await chmod(stagingDirectory, PRIVATE_DOWNLOAD_DIRECTORY_MODE).catch(() => {})\n try {\n const realStagingDirectory = await realpath(stagingDirectory)\n assertPathInsideRoot(realManagedDirectory, realStagingDirectory, path)\n await assertDownloadPathSameDevice(\n rootRealPath,\n realStagingDirectory,\n statForDeviceCheck,\n 'unsafe local destination path: cannot stage download across filesystems',\n )\n await beforeStagingMarkerWrite?.(realStagingDirectory)\n await writeStagingMarker(realStagingDirectory, path)\n return {\n managedDirectory: realManagedDirectory,\n stagingDirectory: realStagingDirectory,\n }\n } catch (err) {\n /* v8 ignore next -- best-effort cleanup */\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n throw err\n }\n } finally {\n releaseManagedDirectoryCreation()\n }\n}\n\n/**\n * Removes the SDK root marker and the now-empty SDK-managed staging root\n * once all per-run `.download` entries are gone.\n * @param rootRealPath - Resolved local sync root path.\n * @param managedDirectory - Resolved SDK-managed staging root path.\n * @param path - Node path module used for platform-specific path operations.\n *\n * @internal\n */\nexport async function removeDownloadStagingRootMarkerAndDirectory(\n rootRealPath: string,\n managedDirectory: string,\n path: typeof import('node:path'),\n): Promise<void> {\n try {\n await removeDownloadStagingRootMarkerAndDirectoryCore(rootRealPath, managedDirectory, path)\n } catch (err) {\n emitCleanupWarning(\n `failed to remove B2 SDK download staging root: ${localFilesystemErrorReason(err)}`,\n )\n }\n}\n\nasync function removeDownloadStagingRootMarkerAndDirectoryCore(\n rootRealPath: string,\n managedDirectory: string,\n path: typeof import('node:path'),\n): Promise<void> {\n const { lstat, readdir, realpath, rmdir, unlink } = await import('node:fs/promises')\n if (downloadStagingRootCreationIsActive(managedDirectory)) return\n\n const managedStats = await lstat(managedDirectory).catch((err: unknown) => {\n if (hasErrorCode(err, 'ENOENT')) return undefined\n throw err\n })\n if (managedStats === undefined || !managedStats.isDirectory()) return\n\n const realManagedDirectory = await realpath(managedDirectory).catch((err: unknown) => {\n if (hasErrorCode(err, 'ENOENT')) return undefined\n throw err\n })\n if (realManagedDirectory === undefined) return\n assertPathInsideRoot(rootRealPath, realManagedDirectory, path)\n if (downloadStagingRootCreationIsActive(realManagedDirectory)) return\n\n const markerPath = path.join(realManagedDirectory, DOWNLOAD_STAGING_MARKER_NAME)\n const markerStats = await lstat(markerPath).catch((err: unknown) => {\n if (hasErrorCode(err, 'ENOENT')) return undefined\n throw err\n })\n if (markerStats?.isFile() !== true) return\n\n await downloadStagingTestHooks.afterCleanupMarkerStat?.(realManagedDirectory)\n const entries = await readdir(realManagedDirectory, { withFileTypes: true }).catch(\n (err: unknown) => {\n if (hasErrorCode(err, 'ENOENT')) return undefined\n throw err\n },\n )\n if (entries === undefined) return\n if (entries.length !== 1 || entries[0]?.name !== DOWNLOAD_STAGING_MARKER_NAME) return\n const currentRealManagedDirectory = await realpath(managedDirectory).catch(() => undefined)\n if (currentRealManagedDirectory !== realManagedDirectory) return\n if (downloadStagingRootCreationIsActive(realManagedDirectory)) return\n\n await downloadStagingTestHooks.beforeCleanupMarkerUnlink?.(markerPath)\n const markerRemoved = await unlink(markerPath)\n .then(() => true)\n .catch((err: unknown) => {\n if (hasErrorCode(err, 'ENOENT')) return false\n throw err\n })\n if (!markerRemoved) return\n try {\n await rmdir(realManagedDirectory)\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) return\n if (hasErrorCode(err, 'ENOTEMPTY') || hasErrorCode(err, 'EEXIST')) {\n await restoreDownloadStagingRootMarker(realManagedDirectory, path)\n return\n }\n await restoreDownloadStagingRootMarker(realManagedDirectory, path).catch((restoreErr) => {\n emitCleanupWarning(\n `failed to restore B2 SDK download staging marker: ${localFilesystemErrorReason(\n restoreErr,\n )}`,\n )\n })\n throw err\n }\n}\n\n/**\n * Returns true when a scan-root entry is an SDK-managed download staging root.\n * @param directory - Candidate directory to inspect.\n *\n * @returns True when the directory contains SDK staging markers.\n *\n * @internal\n */\nexport async function isManagedDownloadStagingRoot(directory: string): Promise<boolean> {\n const { lstat, readdir } = await import('node:fs/promises')\n const path = await import('node:path')\n\n try {\n const markerStats = await lstat(path.join(directory, DOWNLOAD_STAGING_MARKER_NAME))\n if (markerStats.isFile()) return true\n } catch (err) {\n if (!hasErrorCode(err, 'ENOENT')) return false\n }\n\n let entries: import('node:fs').Dirent[]\n try {\n entries = await readdir(directory, { withFileTypes: true })\n } catch {\n return false\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory() || !entry.name.endsWith(DOWNLOAD_STAGING_ENTRY_SUFFIX)) continue\n try {\n const markerStats = await lstat(\n path.join(directory, entry.name, DOWNLOAD_STAGING_MARKER_NAME),\n )\n if (markerStats.isFile()) return true\n } catch {\n // Keep checking siblings; one corrupt entry should not hide user content.\n }\n }\n return false\n}\n\nconst PRIVATE_DOWNLOAD_FILE_MODE = 0o600\nconst PRIVATE_DOWNLOAD_DIRECTORY_MODE = 0o700\n\nasync function writeStagingMarker(\n directory: string,\n path: typeof import('node:path'),\n): Promise<void> {\n const { constants } = await import('node:fs')\n const { lstat, open } = await import('node:fs/promises')\n const markerPath = path.join(directory, DOWNLOAD_STAGING_MARKER_NAME)\n let handle: Awaited<ReturnType<typeof open>> | undefined\n try {\n handle = await open(\n markerPath,\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 } catch (err) {\n const markerAlreadyExists = hasErrorCode(err, 'EEXIST')\n if (\n markerAlreadyExists ||\n /* v8 ignore next -- ELOOP depends on platform-specific O_NOFOLLOW behavior */\n hasErrorCode(err, 'ELOOP')\n ) {\n const markerStats = await lstat(markerPath).catch(() => undefined)\n if (markerStats?.isFile() === true) return\n throw new Error('unsafe local destination path: staging marker is not a regular file')\n }\n throw err\n } finally {\n /* v8 ignore next -- best-effort close */\n await handle?.close().catch(() => {})\n }\n}\n\nfunction retainDownloadStagingRootCreation(managedDirectory: string): () => void {\n activeManagedDirectoryCreations.set(\n managedDirectory,\n (activeManagedDirectoryCreations.get(managedDirectory) ?? 0) + 1,\n )\n return () => {\n const count = activeManagedDirectoryCreations.get(managedDirectory)\n /* v8 ignore next -- releases are paired with retains in a single closure */\n if (count === undefined || count <= 1) {\n activeManagedDirectoryCreations.delete(managedDirectory)\n } else {\n activeManagedDirectoryCreations.set(managedDirectory, count - 1)\n }\n }\n}\n\nfunction downloadStagingRootCreationIsActive(managedDirectory: string): boolean {\n return activeManagedDirectoryCreations.has(managedDirectory)\n}\n\nasync function restoreDownloadStagingRootMarker(\n managedDirectory: string,\n path: typeof import('node:path'),\n): Promise<void> {\n const { lstat, realpath } = await import('node:fs/promises')\n const managedStats = await lstat(managedDirectory).catch(() => undefined)\n if (managedStats?.isDirectory() !== true) return\n const currentRealManagedDirectory = await realpath(managedDirectory).catch(() => undefined)\n if (currentRealManagedDirectory !== managedDirectory) return\n await writeStagingMarker(managedDirectory, path)\n}\n\n/**\n * Verifies that a candidate path is on the same filesystem device as the root.\n * @param rootRealPath - Resolved local sync root path.\n * @param candidateRealPath - Resolved candidate path to compare.\n * @param statForDeviceCheck - Stat function used to read device IDs.\n * @param message - Error message used when devices differ.\n *\n * @internal\n */\nexport async function assertDownloadPathSameDevice(\n rootRealPath: string,\n candidateRealPath: string,\n statForDeviceCheck: DeviceStatFn,\n message: string,\n): Promise<void> {\n const [rootStats, candidateStats] = await Promise.all([\n statForDeviceCheck(rootRealPath),\n statForDeviceCheck(candidateRealPath),\n ])\n if (rootStats.dev !== candidateStats.dev) throw new Error(message)\n}\n\nasync function reapStaleDownloadStagingDirectoriesOnce(\n managedDirectory: string,\n path: typeof import('node:path'),\n nowMillis: number,\n activityEntryLimit: number,\n): Promise<void> {\n const previous = reapedManagedDirectories.get(managedDirectory)\n if (previous !== undefined) {\n await previous\n return\n }\n\n const next = reapStaleDownloadStagingDirectories(\n managedDirectory,\n path,\n nowMillis,\n activityEntryLimit,\n ).finally(() => {\n /* v8 ignore next -- this map entry cannot be replaced until the current cleanup settles */\n if (reapedManagedDirectories.get(managedDirectory) === next) {\n reapedManagedDirectories.delete(managedDirectory)\n }\n })\n reapedManagedDirectories.set(managedDirectory, next)\n await next\n}\n\nasync function reapStaleDownloadStagingDirectories(\n managedDirectory: string,\n path: typeof import('node:path'),\n nowMillis: number,\n activityEntryLimit: number,\n): Promise<void> {\n const { readdir, realpath, rm } = await import('node:fs/promises')\n let entries: import('node:fs').Dirent[]\n try {\n entries = await readdir(managedDirectory, { withFileTypes: true })\n } catch (err) {\n /* v8 ignore next -- concurrent managed-root removal is a filesystem race without a stable hook */\n if (hasErrorCode(err, 'ENOENT')) return\n /* v8 ignore next -- platform-specific root permission failures cannot be simulated portably */\n emitCleanupWarning(\n `failed to inspect B2 SDK download staging entries: ${localFilesystemErrorReason(err)}`,\n )\n return\n }\n\n const cleanupErrors: CleanupWarning[] = []\n await forEachWithConcurrency(entries, MAX_STAGING_CLEANUP_CONCURRENCY, async (entry) => {\n if (!entry.isDirectory() || !entry.name.endsWith(DOWNLOAD_STAGING_ENTRY_SUFFIX)) return\n const candidate = path.join(managedDirectory, entry.name)\n const activity = await readManagedStagingEntryActivity(candidate, path, activityEntryLimit)\n if (activity === undefined || !stagingActivityIsStale(activity, nowMillis)) return\n const realCandidate = await realpath(candidate).catch(() => {\n cleanupErrors.push({ entryName: entry.name, operation: 'inspect' })\n return undefined\n })\n /* v8 ignore next -- requires a concurrent delete between activity read and realpath */\n if (realCandidate === undefined) return\n try {\n assertPathInsideRoot(managedDirectory, realCandidate, path)\n const latestActivity = await readManagedStagingEntryActivity(\n candidate,\n path,\n activityEntryLimit,\n )\n if (\n latestActivity === undefined ||\n latestActivity.signature !== activity.signature ||\n !stagingActivityIsStale(latestActivity, nowMillis)\n ) {\n /* v8 ignore next -- requires concurrent writes between the first and second activity reads */\n return\n }\n await rm(realCandidate, { recursive: true, force: true })\n } catch {\n cleanupErrors.push({ entryName: entry.name, operation: 'remove' })\n }\n })\n\n if (cleanupErrors.length > 0) {\n const noun = cleanupErrors.length === 1 ? 'entry' : 'entries'\n emitCleanupWarning(\n `failed to reap ${cleanupErrors.length} stale B2 SDK download staging ${noun}: ${cleanupErrors\n .slice(0, MAX_CLEANUP_WARNING_ENTRIES)\n .map(formatCleanupWarning)\n .join('; ')}`,\n )\n }\n}\n\nasync function readManagedStagingEntryActivity(\n candidate: string,\n path: typeof import('node:path'),\n activityEntryLimit: number,\n): Promise<StagingActivitySnapshot | undefined> {\n const { lstat, readdir } = await import('node:fs/promises')\n\n try {\n const [directoryStats, markerStats] = await Promise.all([\n lstat(candidate),\n lstat(path.join(candidate, DOWNLOAD_STAGING_MARKER_NAME)),\n ])\n if (!directoryStats.isDirectory() || !markerStats.isFile()) return undefined\n\n const entries = await readdir(candidate, { withFileTypes: true })\n if (entries.length > activityEntryLimit) return undefined\n\n const statsParts = [\n `.:${stagingStatsSignature(directoryStats)}`,\n `${DOWNLOAD_STAGING_MARKER_NAME}:${stagingStatsSignature(markerStats)}`,\n ]\n let newestActivityMs = Math.max(\n stagingStatsActivityMs(directoryStats),\n stagingStatsActivityMs(markerStats),\n )\n\n for (const entry of [...entries].sort(compareDirectoryEntriesByName)) {\n const stats = await lstat(path.join(candidate, entry.name))\n statsParts.push(`${entry.name}:${stagingStatsSignature(stats)}`)\n newestActivityMs = Math.max(newestActivityMs, stagingStatsActivityMs(stats))\n }\n\n return { newestActivityMs, signature: statsParts.join('|') }\n } catch {\n return undefined\n }\n}\n\nfunction compareDirectoryEntriesByName(\n a: { readonly name: string },\n b: { readonly name: string },\n): number {\n if (a.name < b.name) return -1\n if (a.name > b.name) return 1\n /* v8 ignore next -- duplicate directory names cannot occur in a single readdir result */\n return 0\n}\n\nfunction stagingStatsActivityMs(stats: { readonly mtimeMs: number }) {\n return stats.mtimeMs\n}\n\nfunction stagingStatsSignature(stats: {\n readonly ctimeMs: number\n readonly dev: number | bigint\n readonly ino: number | bigint\n readonly mtimeMs: number\n readonly size: number\n}): string {\n return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`\n}\n\nfunction stagingActivityIsStale(activity: StagingActivitySnapshot, nowMillis: number): boolean {\n return nowMillis - activity.newestActivityMs >= STALE_DOWNLOAD_STAGING_AGE_MS\n}\n\nfunction formatCleanupWarning(error: CleanupWarning): string {\n return `${error.operation} ${safeWarningName(error.entryName)}`\n}\n\nfunction safeWarningName(name: string): string {\n const chars = Array.from(name)\n const bounded = chars.slice(0, 80).join('')\n const suffix = chars.length > 80 ? '...' : ''\n return JSON.stringify(`${bounded.replace(/[^A-Za-z0-9._-]/g, '?')}${suffix}`)\n}\n\nfunction canonicalLocalFilesystemSegment(segment: string): string {\n return segment.normalize('NFC').toLocaleLowerCase('en-US')\n}\n\nasync function forEachWithConcurrency<T>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T) => Promise<void>,\n): Promise<void> {\n let index = 0\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (index < items.length) {\n const item = items[index] as T\n index += 1\n await fn(item)\n }\n })\n await Promise.all(workers)\n}\n\nfunction emitCleanupWarning(message: string): void {\n const processLike = (\n globalThis as {\n process?: {\n emitWarning?: (warning: string, options?: { readonly code?: string }) => void\n }\n }\n ).process\n processLike?.emitWarning?.(message, { code: 'B2SDK_DOWNLOAD_STAGING_CLEANUP_FAILED' })\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filters.cjs","names":[],"sources":["../../src/sync/filters.ts"],"sourcesContent":["import {\n pathExceedsSafeRegExpInput,\n regexpMatchesSyncPath,\n validateSyncFilters,\n} from './regexp-safety.ts'\nimport { emitScannerSkip, regexpInputTooLongSkip } from './scan-events.ts'\nimport type { SyncFilterOptions, SyncFilterPattern, SyncPath, SyncScanOptions } from './types.ts'\n\n/**\n * Tests whether a relative sync path is included by the configured include/exclude filters.\n * Exclude filters win over include filters when both match the same path.\n *\n * @param relativePath - Folder-relative path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when the path should remain in the sync scan.\n */\nexport function pathPassesSyncFilters(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n const path = normalizePath(relativePath)\n\n if (normalizedPathSkippedByRegExpInputLimit(path, filters)) return false\n\n const include = filters?.include ?? []\n const exclude = filters?.exclude ?? []\n\n if (include.length > 0) {\n if (!include.some((pattern) => matchesPattern(path, pattern))) return false\n }\n\n return !exclude.some((pattern) => matchesPattern(path, pattern))\n}\n\n/**\n * Tests whether a directory may contain paths admitted by the configured filters.\n *\n * @param relativePath - Folder-relative directory path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when the scanner should descend into the directory.\n */\nexport function directoryMayContainSyncPaths(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n const path = normalizePath(relativePath)\n if (path === '') return true\n\n const exclude = filters?.exclude ?? []\n if (exclude.some((pattern) => stringPatternExcludesAllDescendants(path, pattern))) {\n return false\n }\n\n const include = filters?.include ?? []\n return include.length === 0 || include.some((pattern) => patternMayMatchDescendant(path, pattern))\n}\n\n/**\n * Returns the safe literal prefix that B2 listing can use for include filters.\n * Exclude filters are not considered because they cannot narrow a B2 prefix.\n *\n * @param filters - Optional include and exclude filters.\n *\n * @returns A folder-relative literal prefix, or an empty string when no safe narrowing exists.\n */\nexport function literalPrefixForSyncFilters(filters: SyncFilterOptions | undefined): string {\n validateSyncFilters(filters)\n const include = filters?.include ?? []\n let commonPrefix: string | undefined\n\n for (const pattern of include) {\n if (patternIsRegExp(pattern)) return ''\n\n const glob = normalizePath(pattern)\n if (!glob.includes('/')) return ''\n\n const prefix = literalPrefixForGlob(glob)\n if (prefix === '') return ''\n commonPrefix = commonPrefix === undefined ? prefix : commonLiteralPrefix(commonPrefix, prefix)\n }\n\n return commonPrefix ?? ''\n}\n\n/**\n * Filters an async iterable of sync paths while preserving the original item type.\n *\n * @typeParam T - Concrete sync path shape yielded by the source folder.\n *\n * @param paths - Async iterable of folder scan results.\n * @param filters - Optional include and exclude filters.\n *\n * @returns A filtered async generator of sync paths.\n */\nexport async function* filterSyncPaths<T extends SyncPath>(\n paths: AsyncIterable<T>,\n filters: SyncScanOptions | undefined,\n): AsyncGenerator<T> {\n for await (const path of paths) {\n if (pathPassesSyncFilters(path.relativePath, filters)) {\n yield path\n } else if (pathSkippedByRegExpInputLimit(path.relativePath, filters)) {\n emitScannerSkip(filters, regexpInputTooLongSkip(normalizePath(path.relativePath)))\n }\n }\n}\n\n/**\n * Tests whether a path is skipped because RegExp filters are configured and the normalized path\n * exceeds the SDK RegExp input guard.\n *\n * @param relativePath - Folder-relative path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when any RegExp filter is present and the path is too long to evaluate.\n */\nexport function pathSkippedByRegExpInputLimit(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n return normalizedPathSkippedByRegExpInputLimit(normalizePath(relativePath), filters)\n}\n\nfunction normalizedPathSkippedByRegExpInputLimit(\n normalizedPath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n if (!pathExceedsSafeRegExpInput(normalizedPath) || !filtersContainRegExp(filters)) {\n return false\n }\n return true\n}\n\nfunction matchesPattern(relativePath: string, pattern: SyncFilterPattern): boolean {\n if (patternIsRegExp(pattern)) {\n return regexpMatchesSyncPath(relativePath, pattern)\n }\n\n const glob = normalizePath(pattern)\n if (glob === '') return relativePath === ''\n\n const segments = splitPath(relativePath)\n if (!glob.includes('/')) {\n return segments.some((segment) => matchSegmentGlob(segment, glob))\n }\n\n return matchPathGlob(segments, splitPath(glob))\n}\n\nfunction stringPatternExcludesAllDescendants(\n relativePath: string,\n pattern: SyncFilterPattern,\n): boolean {\n if (patternIsRegExp(pattern)) return false\n\n const glob = normalizePath(pattern)\n if (glob === '') return false\n if (!glob.includes('/')) return matchesPattern(relativePath, pattern)\n\n const globSegments = splitPath(glob)\n return globSegments.at(-1) === '**' && matchPathGlob(splitPath(relativePath), globSegments)\n}\n\nfunction filtersContainRegExp(filters: SyncFilterOptions | undefined): boolean {\n return (\n filters?.include?.some(patternIsRegExp) === true ||\n filters?.exclude?.some(patternIsRegExp) === true\n )\n}\n\nfunction patternMayMatchDescendant(relativePath: string, pattern: SyncFilterPattern): boolean {\n if (patternIsRegExp(pattern)) return true\n\n const glob = normalizePath(pattern)\n if (glob === '' || !glob.includes('/')) return true\n\n const pathSegments = splitPath(relativePath)\n const globSegments = splitPath(glob)\n const length = Math.min(pathSegments.length, globSegments.length)\n\n for (let i = 0; i < length; i++) {\n const globSegment = globSegments[i]\n if (globSegment === '**' || globSegment === undefined || hasGlobWildcard(globSegment)) {\n return true\n }\n if (globSegment !== pathSegments[i]) {\n return false\n }\n }\n\n return true\n}\n\nfunction matchPathGlob(pathSegments: readonly string[], globSegments: readonly string[]): boolean {\n let reachable = new Array<boolean>(pathSegments.length + 1).fill(false)\n reachable[0] = true\n\n for (const globSegment of globSegments) {\n const next = new Array<boolean>(pathSegments.length + 1).fill(false)\n\n if (globSegment === '**') {\n // A whole-segment `**` consumes zero or more complete path segments.\n let canReach = false\n for (let i = 0; i <= pathSegments.length; i++) {\n canReach = canReach || reachable[i] === true\n next[i] = canReach\n }\n } else {\n for (let i = 0; i < pathSegments.length; i++) {\n if (reachable[i] === true && matchSegmentGlob(pathSegments[i] ?? '', globSegment)) {\n next[i + 1] = true\n }\n }\n }\n\n reachable = next\n }\n\n return reachable[pathSegments.length] === true\n}\n\nfunction matchSegmentGlob(segment: string, glob: string): boolean {\n let segmentIndex = 0\n let globIndex = 0\n let starIndex = -1\n let starMatchIndex = 0\n\n while (segmentIndex < segment.length) {\n const globChar = glob[globIndex]\n if (globChar === '?' || globChar === segment[segmentIndex]) {\n globIndex++\n segmentIndex++\n } else if (globChar === '*') {\n // Repeated `*` in one segment is still one segment-local wildcard.\n while (glob[globIndex + 1] === '*') globIndex++\n starIndex = globIndex\n starMatchIndex = segmentIndex\n globIndex++\n } else if (starIndex !== -1) {\n globIndex = starIndex + 1\n starMatchIndex++\n segmentIndex = starMatchIndex\n } else {\n return false\n }\n }\n\n while (glob[globIndex] === '*') globIndex++\n return globIndex === glob.length\n}\n\nfunction literalPrefixForGlob(glob: string): string {\n const segments = splitPath(glob)\n const literalSegments: string[] = []\n let firstWildcardIndex = segments.length\n\n for (const [index, segment] of segments.entries()) {\n if (segment === '**' || hasGlobWildcard(segment)) {\n firstWildcardIndex = index\n break\n }\n literalSegments.push(segment)\n }\n\n if (literalSegments.length === 0) return ''\n const prefix = literalSegments.join('/')\n const wildcardTail = segments.slice(firstWildcardIndex)\n const tailMayMatchBarePrefix =\n wildcardTail.length > 0 && wildcardTail.every((segment) => segment === '**')\n if (tailMayMatchBarePrefix) return prefix\n return literalSegments.length < segments.length ? `${prefix}/` : prefix\n}\n\nfunction commonLiteralPrefix(a: string, b: string): string {\n let end = 0\n const max = Math.min(a.length, b.length)\n while (end < max && a[end] === b[end]) end++\n return trimTrailingHighSurrogate(a.slice(0, end))\n}\n\nfunction trimTrailingHighSurrogate(value: string): string {\n const lastCodeUnit = value.charCodeAt(value.length - 1)\n return lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff ? value.slice(0, -1) : value\n}\n\nfunction hasGlobWildcard(glob: string): boolean {\n return glob.includes('*') || glob.includes('?')\n}\n\nfunction patternIsRegExp(pattern: SyncFilterPattern): pattern is RegExp {\n return typeof pattern !== 'string'\n}\n\nfunction splitPath(path: string): string[] {\n if (path === '') return []\n return path.split('/').filter((segment) => segment !== '')\n}\n\nfunction normalizePath(path: string): string {\n let normalized = path.split('\\\\').join('/')\n while (normalized.startsWith('./')) normalized = normalized.slice(2)\n while (normalized.startsWith('/')) normalized = normalized.slice(1)\n while (normalized.endsWith('/') && normalized.length > 1) {\n normalized = normalized.slice(0, -1)\n }\n return normalized\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filters.cjs","names":[],"sources":["../../src/sync/filters.ts"],"sourcesContent":["import {\n pathExceedsSafeRegExpInput,\n regexpMatchesSyncPath,\n validateSyncFilters,\n} from './regexp-safety.ts'\nimport { emitScannerSkip, regexpInputTooLongSkip } from './scan-events.ts'\nimport type { SyncFilterOptions, SyncFilterPattern, SyncPath, SyncScanOptions } from './types.ts'\n\n/**\n * Tests whether a relative sync path is included by the configured include/exclude filters.\n * Exclude filters win over include filters when both match the same path.\n *\n * @param relativePath - Folder-relative path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when the path should remain in the sync scan.\n */\nexport function pathPassesSyncFilters(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n const path = normalizePath(relativePath)\n\n if (normalizedPathSkippedByRegExpInputLimit(path, filters)) return false\n\n const include = filters?.include ?? []\n const exclude = filters?.exclude ?? []\n\n if (include.length > 0) {\n if (!include.some((pattern) => matchesPattern(path, pattern))) return false\n }\n\n return !exclude.some((pattern) => matchesPattern(path, pattern))\n}\n\n/**\n * Tests whether a directory may contain paths admitted by the configured filters.\n *\n * @param relativePath - Folder-relative directory path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when the scanner should descend into the directory.\n */\nexport function directoryMayContainSyncPaths(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n const path = normalizePath(relativePath)\n if (path === '') return true\n\n const exclude = filters?.exclude ?? []\n if (exclude.some((pattern) => stringPatternExcludesAllDescendants(path, pattern))) {\n return false\n }\n\n const include = filters?.include ?? []\n return include.length === 0 || include.some((pattern) => patternMayMatchDescendant(path, pattern))\n}\n\n/**\n * Returns the safe literal prefix that B2 listing can use for include filters.\n * Exclude filters are not considered because they cannot narrow a B2 prefix.\n *\n * @param filters - Optional include and exclude filters.\n *\n * @returns A folder-relative literal prefix, or an empty string when no safe narrowing exists.\n */\nexport function literalPrefixForSyncFilters(filters: SyncFilterOptions | undefined): string {\n validateSyncFilters(filters)\n const include = filters?.include ?? []\n let commonPrefix: string | undefined\n\n for (const pattern of include) {\n if (patternIsRegExp(pattern)) return ''\n\n const glob = normalizePath(pattern)\n if (!glob.includes('/')) return ''\n\n const prefix = literalPrefixForGlob(glob)\n if (prefix === '') return ''\n commonPrefix = commonPrefix === undefined ? prefix : commonLiteralPrefix(commonPrefix, prefix)\n }\n\n return commonPrefix ?? ''\n}\n\n/**\n * Filters an async iterable of sync paths while preserving the original item type.\n *\n * @typeParam T - Concrete sync path shape yielded by the source folder.\n *\n * @param paths - Async iterable of folder scan results.\n * @param filters - Optional include and exclude filters.\n *\n * @returns A filtered async generator of sync paths.\n */\nexport async function* filterSyncPaths<T extends SyncPath>(\n paths: AsyncIterable<T>,\n filters: SyncScanOptions | undefined,\n): AsyncGenerator<T> {\n for await (const path of paths) {\n if (pathPassesSyncFilters(path.relativePath, filters)) {\n yield path\n } else if (pathSkippedByRegExpInputLimit(path.relativePath, filters)) {\n emitScannerSkip(filters, regexpInputTooLongSkip(normalizePath(path.relativePath)))\n }\n }\n}\n\n/**\n * Tests whether a path is skipped because RegExp filters are configured and the normalized path\n * exceeds the SDK RegExp input guard.\n *\n * @param relativePath - Folder-relative path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when any RegExp filter is present and the path is too long to evaluate.\n */\nexport function pathSkippedByRegExpInputLimit(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n return normalizedPathSkippedByRegExpInputLimit(normalizePath(relativePath), filters)\n}\n\nfunction normalizedPathSkippedByRegExpInputLimit(\n normalizedPath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n if (!pathExceedsSafeRegExpInput(normalizedPath) || !filtersContainRegExp(filters)) {\n return false\n }\n return true\n}\n\nfunction matchesPattern(relativePath: string, pattern: SyncFilterPattern): boolean {\n if (patternIsRegExp(pattern)) {\n return regexpMatchesSyncPath(relativePath, pattern)\n }\n\n const glob = normalizePath(pattern)\n if (glob === '') return relativePath === ''\n\n const segments = splitPath(relativePath)\n if (!glob.includes('/')) {\n return segments.some((segment) => matchSegmentGlob(segment, glob))\n }\n\n return matchPathGlob(segments, splitPath(glob))\n}\n\nfunction stringPatternExcludesAllDescendants(\n relativePath: string,\n pattern: SyncFilterPattern,\n): boolean {\n if (patternIsRegExp(pattern)) return false\n\n const glob = normalizePath(pattern)\n if (glob === '') return false\n if (!glob.includes('/')) return matchesPattern(relativePath, pattern)\n\n const globSegments = splitPath(glob)\n return globSegments.at(-1) === '**' && matchPathGlob(splitPath(relativePath), globSegments)\n}\n\nfunction filtersContainRegExp(filters: SyncFilterOptions | undefined): boolean {\n return (\n filters?.include?.some(patternIsRegExp) === true ||\n filters?.exclude?.some(patternIsRegExp) === true\n )\n}\n\nfunction patternMayMatchDescendant(relativePath: string, pattern: SyncFilterPattern): boolean {\n if (patternIsRegExp(pattern)) return true\n\n const glob = normalizePath(pattern)\n if (glob === '' || !glob.includes('/')) return true\n\n const pathSegments = splitPath(relativePath)\n const globSegments = splitPath(glob)\n const length = Math.min(pathSegments.length, globSegments.length)\n\n for (let i = 0; i < length; i++) {\n const globSegment = globSegments[i]\n if (globSegment === '**' || globSegment === undefined || hasGlobWildcard(globSegment)) {\n return true\n }\n if (globSegment !== pathSegments[i]) {\n return false\n }\n }\n\n return true\n}\n\nfunction matchPathGlob(pathSegments: readonly string[], globSegments: readonly string[]): boolean {\n let reachable = new Array<boolean>(pathSegments.length + 1).fill(false)\n reachable[0] = true\n\n for (const globSegment of globSegments) {\n const next = new Array<boolean>(pathSegments.length + 1).fill(false)\n\n if (globSegment === '**') {\n // A whole-segment `**` consumes zero or more complete path segments.\n let canReach = false\n for (let i = 0; i <= pathSegments.length; i++) {\n canReach = canReach || reachable[i] === true\n next[i] = canReach\n }\n } else {\n for (let i = 0; i < pathSegments.length; i++) {\n if (reachable[i] === true && matchSegmentGlob(pathSegments[i] ?? '', globSegment)) {\n next[i + 1] = true\n }\n }\n }\n\n reachable = next\n }\n\n return reachable[pathSegments.length] === true\n}\n\nfunction matchSegmentGlob(segment: string, glob: string): boolean {\n let segmentIndex = 0\n let globIndex = 0\n let starIndex = -1\n let starMatchIndex = 0\n\n while (segmentIndex < segment.length) {\n const globChar = glob[globIndex]\n if (globChar === '?' || globChar === segment[segmentIndex]) {\n globIndex++\n segmentIndex++\n } else if (globChar === '*') {\n // Repeated `*` in one segment is still one segment-local wildcard.\n while (glob[globIndex + 1] === '*') globIndex++\n starIndex = globIndex\n starMatchIndex = segmentIndex\n globIndex++\n } else if (starIndex !== -1) {\n globIndex = starIndex + 1\n starMatchIndex++\n segmentIndex = starMatchIndex\n } else {\n return false\n }\n }\n\n while (glob[globIndex] === '*') globIndex++\n return globIndex === glob.length\n}\n\nfunction literalPrefixForGlob(glob: string): string {\n const segments = splitPath(glob)\n const literalSegments: string[] = []\n let firstWildcardIndex = segments.length\n\n for (const [index, segment] of segments.entries()) {\n if (segment === '**' || hasGlobWildcard(segment)) {\n firstWildcardIndex = index\n break\n }\n literalSegments.push(segment)\n }\n\n if (literalSegments.length === 0) return ''\n const prefix = literalSegments.join('/')\n const wildcardTail = segments.slice(firstWildcardIndex)\n const tailMayMatchBarePrefix =\n wildcardTail.length > 0 && wildcardTail.every((segment) => segment === '**')\n if (tailMayMatchBarePrefix) return prefix\n return literalSegments.length < segments.length ? `${prefix}/` : prefix\n}\n\nfunction commonLiteralPrefix(a: string, b: string): string {\n let end = 0\n const max = Math.min(a.length, b.length)\n while (end < max && a[end] === b[end]) end++\n return trimTrailingHighSurrogate(a.slice(0, end))\n}\n\nfunction trimTrailingHighSurrogate(value: string): string {\n const lastCodeUnit = value.charCodeAt(value.length - 1)\n return lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff ? value.slice(0, -1) : value\n}\n\nfunction hasGlobWildcard(glob: string): boolean {\n return glob.includes('*') || glob.includes('?')\n}\n\nfunction patternIsRegExp(pattern: SyncFilterPattern): pattern is RegExp {\n return typeof pattern !== 'string'\n}\n\nfunction splitPath(path: string): string[] {\n if (path === '') return []\n return path.split('/').filter((segment) => segment !== '')\n}\n\nfunction normalizePath(path: string): string {\n let normalized = path.split('\\\\').join('/')\n while (normalized.startsWith('./')) normalized = normalized.slice(2)\n while (normalized.startsWith('/')) normalized = normalized.slice(1)\n while (normalized.endsWith('/') && normalized.length > 1) {\n normalized = normalized.slice(0, -1)\n }\n return normalized\n}\n"],"mappings":"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"}
|
package/dist/sync/filters.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filters.js","names":[],"sources":["../../src/sync/filters.ts"],"sourcesContent":["import {\n pathExceedsSafeRegExpInput,\n regexpMatchesSyncPath,\n validateSyncFilters,\n} from './regexp-safety.ts'\nimport { emitScannerSkip, regexpInputTooLongSkip } from './scan-events.ts'\nimport type { SyncFilterOptions, SyncFilterPattern, SyncPath, SyncScanOptions } from './types.ts'\n\n/**\n * Tests whether a relative sync path is included by the configured include/exclude filters.\n * Exclude filters win over include filters when both match the same path.\n *\n * @param relativePath - Folder-relative path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when the path should remain in the sync scan.\n */\nexport function pathPassesSyncFilters(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n const path = normalizePath(relativePath)\n\n if (normalizedPathSkippedByRegExpInputLimit(path, filters)) return false\n\n const include = filters?.include ?? []\n const exclude = filters?.exclude ?? []\n\n if (include.length > 0) {\n if (!include.some((pattern) => matchesPattern(path, pattern))) return false\n }\n\n return !exclude.some((pattern) => matchesPattern(path, pattern))\n}\n\n/**\n * Tests whether a directory may contain paths admitted by the configured filters.\n *\n * @param relativePath - Folder-relative directory path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when the scanner should descend into the directory.\n */\nexport function directoryMayContainSyncPaths(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n const path = normalizePath(relativePath)\n if (path === '') return true\n\n const exclude = filters?.exclude ?? []\n if (exclude.some((pattern) => stringPatternExcludesAllDescendants(path, pattern))) {\n return false\n }\n\n const include = filters?.include ?? []\n return include.length === 0 || include.some((pattern) => patternMayMatchDescendant(path, pattern))\n}\n\n/**\n * Returns the safe literal prefix that B2 listing can use for include filters.\n * Exclude filters are not considered because they cannot narrow a B2 prefix.\n *\n * @param filters - Optional include and exclude filters.\n *\n * @returns A folder-relative literal prefix, or an empty string when no safe narrowing exists.\n */\nexport function literalPrefixForSyncFilters(filters: SyncFilterOptions | undefined): string {\n validateSyncFilters(filters)\n const include = filters?.include ?? []\n let commonPrefix: string | undefined\n\n for (const pattern of include) {\n if (patternIsRegExp(pattern)) return ''\n\n const glob = normalizePath(pattern)\n if (!glob.includes('/')) return ''\n\n const prefix = literalPrefixForGlob(glob)\n if (prefix === '') return ''\n commonPrefix = commonPrefix === undefined ? prefix : commonLiteralPrefix(commonPrefix, prefix)\n }\n\n return commonPrefix ?? ''\n}\n\n/**\n * Filters an async iterable of sync paths while preserving the original item type.\n *\n * @typeParam T - Concrete sync path shape yielded by the source folder.\n *\n * @param paths - Async iterable of folder scan results.\n * @param filters - Optional include and exclude filters.\n *\n * @returns A filtered async generator of sync paths.\n */\nexport async function* filterSyncPaths<T extends SyncPath>(\n paths: AsyncIterable<T>,\n filters: SyncScanOptions | undefined,\n): AsyncGenerator<T> {\n for await (const path of paths) {\n if (pathPassesSyncFilters(path.relativePath, filters)) {\n yield path\n } else if (pathSkippedByRegExpInputLimit(path.relativePath, filters)) {\n emitScannerSkip(filters, regexpInputTooLongSkip(normalizePath(path.relativePath)))\n }\n }\n}\n\n/**\n * Tests whether a path is skipped because RegExp filters are configured and the normalized path\n * exceeds the SDK RegExp input guard.\n *\n * @param relativePath - Folder-relative path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when any RegExp filter is present and the path is too long to evaluate.\n */\nexport function pathSkippedByRegExpInputLimit(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n return normalizedPathSkippedByRegExpInputLimit(normalizePath(relativePath), filters)\n}\n\nfunction normalizedPathSkippedByRegExpInputLimit(\n normalizedPath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n if (!pathExceedsSafeRegExpInput(normalizedPath) || !filtersContainRegExp(filters)) {\n return false\n }\n return true\n}\n\nfunction matchesPattern(relativePath: string, pattern: SyncFilterPattern): boolean {\n if (patternIsRegExp(pattern)) {\n return regexpMatchesSyncPath(relativePath, pattern)\n }\n\n const glob = normalizePath(pattern)\n if (glob === '') return relativePath === ''\n\n const segments = splitPath(relativePath)\n if (!glob.includes('/')) {\n return segments.some((segment) => matchSegmentGlob(segment, glob))\n }\n\n return matchPathGlob(segments, splitPath(glob))\n}\n\nfunction stringPatternExcludesAllDescendants(\n relativePath: string,\n pattern: SyncFilterPattern,\n): boolean {\n if (patternIsRegExp(pattern)) return false\n\n const glob = normalizePath(pattern)\n if (glob === '') return false\n if (!glob.includes('/')) return matchesPattern(relativePath, pattern)\n\n const globSegments = splitPath(glob)\n return globSegments.at(-1) === '**' && matchPathGlob(splitPath(relativePath), globSegments)\n}\n\nfunction filtersContainRegExp(filters: SyncFilterOptions | undefined): boolean {\n return (\n filters?.include?.some(patternIsRegExp) === true ||\n filters?.exclude?.some(patternIsRegExp) === true\n )\n}\n\nfunction patternMayMatchDescendant(relativePath: string, pattern: SyncFilterPattern): boolean {\n if (patternIsRegExp(pattern)) return true\n\n const glob = normalizePath(pattern)\n if (glob === '' || !glob.includes('/')) return true\n\n const pathSegments = splitPath(relativePath)\n const globSegments = splitPath(glob)\n const length = Math.min(pathSegments.length, globSegments.length)\n\n for (let i = 0; i < length; i++) {\n const globSegment = globSegments[i]\n if (globSegment === '**' || globSegment === undefined || hasGlobWildcard(globSegment)) {\n return true\n }\n if (globSegment !== pathSegments[i]) {\n return false\n }\n }\n\n return true\n}\n\nfunction matchPathGlob(pathSegments: readonly string[], globSegments: readonly string[]): boolean {\n let reachable = new Array<boolean>(pathSegments.length + 1).fill(false)\n reachable[0] = true\n\n for (const globSegment of globSegments) {\n const next = new Array<boolean>(pathSegments.length + 1).fill(false)\n\n if (globSegment === '**') {\n // A whole-segment `**` consumes zero or more complete path segments.\n let canReach = false\n for (let i = 0; i <= pathSegments.length; i++) {\n canReach = canReach || reachable[i] === true\n next[i] = canReach\n }\n } else {\n for (let i = 0; i < pathSegments.length; i++) {\n if (reachable[i] === true && matchSegmentGlob(pathSegments[i] ?? '', globSegment)) {\n next[i + 1] = true\n }\n }\n }\n\n reachable = next\n }\n\n return reachable[pathSegments.length] === true\n}\n\nfunction matchSegmentGlob(segment: string, glob: string): boolean {\n let segmentIndex = 0\n let globIndex = 0\n let starIndex = -1\n let starMatchIndex = 0\n\n while (segmentIndex < segment.length) {\n const globChar = glob[globIndex]\n if (globChar === '?' || globChar === segment[segmentIndex]) {\n globIndex++\n segmentIndex++\n } else if (globChar === '*') {\n // Repeated `*` in one segment is still one segment-local wildcard.\n while (glob[globIndex + 1] === '*') globIndex++\n starIndex = globIndex\n starMatchIndex = segmentIndex\n globIndex++\n } else if (starIndex !== -1) {\n globIndex = starIndex + 1\n starMatchIndex++\n segmentIndex = starMatchIndex\n } else {\n return false\n }\n }\n\n while (glob[globIndex] === '*') globIndex++\n return globIndex === glob.length\n}\n\nfunction literalPrefixForGlob(glob: string): string {\n const segments = splitPath(glob)\n const literalSegments: string[] = []\n let firstWildcardIndex = segments.length\n\n for (const [index, segment] of segments.entries()) {\n if (segment === '**' || hasGlobWildcard(segment)) {\n firstWildcardIndex = index\n break\n }\n literalSegments.push(segment)\n }\n\n if (literalSegments.length === 0) return ''\n const prefix = literalSegments.join('/')\n const wildcardTail = segments.slice(firstWildcardIndex)\n const tailMayMatchBarePrefix =\n wildcardTail.length > 0 && wildcardTail.every((segment) => segment === '**')\n if (tailMayMatchBarePrefix) return prefix\n return literalSegments.length < segments.length ? `${prefix}/` : prefix\n}\n\nfunction commonLiteralPrefix(a: string, b: string): string {\n let end = 0\n const max = Math.min(a.length, b.length)\n while (end < max && a[end] === b[end]) end++\n return trimTrailingHighSurrogate(a.slice(0, end))\n}\n\nfunction trimTrailingHighSurrogate(value: string): string {\n const lastCodeUnit = value.charCodeAt(value.length - 1)\n return lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff ? value.slice(0, -1) : value\n}\n\nfunction hasGlobWildcard(glob: string): boolean {\n return glob.includes('*') || glob.includes('?')\n}\n\nfunction patternIsRegExp(pattern: SyncFilterPattern): pattern is RegExp {\n return typeof pattern !== 'string'\n}\n\nfunction splitPath(path: string): string[] {\n if (path === '') return []\n return path.split('/').filter((segment) => segment !== '')\n}\n\nfunction normalizePath(path: string): string {\n let normalized = path.split('\\\\').join('/')\n while (normalized.startsWith('./')) normalized = normalized.slice(2)\n while (normalized.startsWith('/')) normalized = normalized.slice(1)\n while (normalized.endsWith('/') && normalized.length > 1) {\n normalized = normalized.slice(0, -1)\n }\n return normalized\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filters.js","names":[],"sources":["../../src/sync/filters.ts"],"sourcesContent":["import {\n pathExceedsSafeRegExpInput,\n regexpMatchesSyncPath,\n validateSyncFilters,\n} from './regexp-safety.ts'\nimport { emitScannerSkip, regexpInputTooLongSkip } from './scan-events.ts'\nimport type { SyncFilterOptions, SyncFilterPattern, SyncPath, SyncScanOptions } from './types.ts'\n\n/**\n * Tests whether a relative sync path is included by the configured include/exclude filters.\n * Exclude filters win over include filters when both match the same path.\n *\n * @param relativePath - Folder-relative path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when the path should remain in the sync scan.\n */\nexport function pathPassesSyncFilters(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n const path = normalizePath(relativePath)\n\n if (normalizedPathSkippedByRegExpInputLimit(path, filters)) return false\n\n const include = filters?.include ?? []\n const exclude = filters?.exclude ?? []\n\n if (include.length > 0) {\n if (!include.some((pattern) => matchesPattern(path, pattern))) return false\n }\n\n return !exclude.some((pattern) => matchesPattern(path, pattern))\n}\n\n/**\n * Tests whether a directory may contain paths admitted by the configured filters.\n *\n * @param relativePath - Folder-relative directory path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when the scanner should descend into the directory.\n */\nexport function directoryMayContainSyncPaths(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n const path = normalizePath(relativePath)\n if (path === '') return true\n\n const exclude = filters?.exclude ?? []\n if (exclude.some((pattern) => stringPatternExcludesAllDescendants(path, pattern))) {\n return false\n }\n\n const include = filters?.include ?? []\n return include.length === 0 || include.some((pattern) => patternMayMatchDescendant(path, pattern))\n}\n\n/**\n * Returns the safe literal prefix that B2 listing can use for include filters.\n * Exclude filters are not considered because they cannot narrow a B2 prefix.\n *\n * @param filters - Optional include and exclude filters.\n *\n * @returns A folder-relative literal prefix, or an empty string when no safe narrowing exists.\n */\nexport function literalPrefixForSyncFilters(filters: SyncFilterOptions | undefined): string {\n validateSyncFilters(filters)\n const include = filters?.include ?? []\n let commonPrefix: string | undefined\n\n for (const pattern of include) {\n if (patternIsRegExp(pattern)) return ''\n\n const glob = normalizePath(pattern)\n if (!glob.includes('/')) return ''\n\n const prefix = literalPrefixForGlob(glob)\n if (prefix === '') return ''\n commonPrefix = commonPrefix === undefined ? prefix : commonLiteralPrefix(commonPrefix, prefix)\n }\n\n return commonPrefix ?? ''\n}\n\n/**\n * Filters an async iterable of sync paths while preserving the original item type.\n *\n * @typeParam T - Concrete sync path shape yielded by the source folder.\n *\n * @param paths - Async iterable of folder scan results.\n * @param filters - Optional include and exclude filters.\n *\n * @returns A filtered async generator of sync paths.\n */\nexport async function* filterSyncPaths<T extends SyncPath>(\n paths: AsyncIterable<T>,\n filters: SyncScanOptions | undefined,\n): AsyncGenerator<T> {\n for await (const path of paths) {\n if (pathPassesSyncFilters(path.relativePath, filters)) {\n yield path\n } else if (pathSkippedByRegExpInputLimit(path.relativePath, filters)) {\n emitScannerSkip(filters, regexpInputTooLongSkip(normalizePath(path.relativePath)))\n }\n }\n}\n\n/**\n * Tests whether a path is skipped because RegExp filters are configured and the normalized path\n * exceeds the SDK RegExp input guard.\n *\n * @param relativePath - Folder-relative path using forward slashes.\n * @param filters - Optional include and exclude filters.\n *\n * @returns True when any RegExp filter is present and the path is too long to evaluate.\n */\nexport function pathSkippedByRegExpInputLimit(\n relativePath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n validateSyncFilters(filters)\n return normalizedPathSkippedByRegExpInputLimit(normalizePath(relativePath), filters)\n}\n\nfunction normalizedPathSkippedByRegExpInputLimit(\n normalizedPath: string,\n filters: SyncFilterOptions | undefined,\n): boolean {\n if (!pathExceedsSafeRegExpInput(normalizedPath) || !filtersContainRegExp(filters)) {\n return false\n }\n return true\n}\n\nfunction matchesPattern(relativePath: string, pattern: SyncFilterPattern): boolean {\n if (patternIsRegExp(pattern)) {\n return regexpMatchesSyncPath(relativePath, pattern)\n }\n\n const glob = normalizePath(pattern)\n if (glob === '') return relativePath === ''\n\n const segments = splitPath(relativePath)\n if (!glob.includes('/')) {\n return segments.some((segment) => matchSegmentGlob(segment, glob))\n }\n\n return matchPathGlob(segments, splitPath(glob))\n}\n\nfunction stringPatternExcludesAllDescendants(\n relativePath: string,\n pattern: SyncFilterPattern,\n): boolean {\n if (patternIsRegExp(pattern)) return false\n\n const glob = normalizePath(pattern)\n if (glob === '') return false\n if (!glob.includes('/')) return matchesPattern(relativePath, pattern)\n\n const globSegments = splitPath(glob)\n return globSegments.at(-1) === '**' && matchPathGlob(splitPath(relativePath), globSegments)\n}\n\nfunction filtersContainRegExp(filters: SyncFilterOptions | undefined): boolean {\n return (\n filters?.include?.some(patternIsRegExp) === true ||\n filters?.exclude?.some(patternIsRegExp) === true\n )\n}\n\nfunction patternMayMatchDescendant(relativePath: string, pattern: SyncFilterPattern): boolean {\n if (patternIsRegExp(pattern)) return true\n\n const glob = normalizePath(pattern)\n if (glob === '' || !glob.includes('/')) return true\n\n const pathSegments = splitPath(relativePath)\n const globSegments = splitPath(glob)\n const length = Math.min(pathSegments.length, globSegments.length)\n\n for (let i = 0; i < length; i++) {\n const globSegment = globSegments[i]\n if (globSegment === '**' || globSegment === undefined || hasGlobWildcard(globSegment)) {\n return true\n }\n if (globSegment !== pathSegments[i]) {\n return false\n }\n }\n\n return true\n}\n\nfunction matchPathGlob(pathSegments: readonly string[], globSegments: readonly string[]): boolean {\n let reachable = new Array<boolean>(pathSegments.length + 1).fill(false)\n reachable[0] = true\n\n for (const globSegment of globSegments) {\n const next = new Array<boolean>(pathSegments.length + 1).fill(false)\n\n if (globSegment === '**') {\n // A whole-segment `**` consumes zero or more complete path segments.\n let canReach = false\n for (let i = 0; i <= pathSegments.length; i++) {\n canReach = canReach || reachable[i] === true\n next[i] = canReach\n }\n } else {\n for (let i = 0; i < pathSegments.length; i++) {\n if (reachable[i] === true && matchSegmentGlob(pathSegments[i] ?? '', globSegment)) {\n next[i + 1] = true\n }\n }\n }\n\n reachable = next\n }\n\n return reachable[pathSegments.length] === true\n}\n\nfunction matchSegmentGlob(segment: string, glob: string): boolean {\n let segmentIndex = 0\n let globIndex = 0\n let starIndex = -1\n let starMatchIndex = 0\n\n while (segmentIndex < segment.length) {\n const globChar = glob[globIndex]\n if (globChar === '?' || globChar === segment[segmentIndex]) {\n globIndex++\n segmentIndex++\n } else if (globChar === '*') {\n // Repeated `*` in one segment is still one segment-local wildcard.\n while (glob[globIndex + 1] === '*') globIndex++\n starIndex = globIndex\n starMatchIndex = segmentIndex\n globIndex++\n } else if (starIndex !== -1) {\n globIndex = starIndex + 1\n starMatchIndex++\n segmentIndex = starMatchIndex\n } else {\n return false\n }\n }\n\n while (glob[globIndex] === '*') globIndex++\n return globIndex === glob.length\n}\n\nfunction literalPrefixForGlob(glob: string): string {\n const segments = splitPath(glob)\n const literalSegments: string[] = []\n let firstWildcardIndex = segments.length\n\n for (const [index, segment] of segments.entries()) {\n if (segment === '**' || hasGlobWildcard(segment)) {\n firstWildcardIndex = index\n break\n }\n literalSegments.push(segment)\n }\n\n if (literalSegments.length === 0) return ''\n const prefix = literalSegments.join('/')\n const wildcardTail = segments.slice(firstWildcardIndex)\n const tailMayMatchBarePrefix =\n wildcardTail.length > 0 && wildcardTail.every((segment) => segment === '**')\n if (tailMayMatchBarePrefix) return prefix\n return literalSegments.length < segments.length ? `${prefix}/` : prefix\n}\n\nfunction commonLiteralPrefix(a: string, b: string): string {\n let end = 0\n const max = Math.min(a.length, b.length)\n while (end < max && a[end] === b[end]) end++\n return trimTrailingHighSurrogate(a.slice(0, end))\n}\n\nfunction trimTrailingHighSurrogate(value: string): string {\n const lastCodeUnit = value.charCodeAt(value.length - 1)\n return lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff ? value.slice(0, -1) : value\n}\n\nfunction hasGlobWildcard(glob: string): boolean {\n return glob.includes('*') || glob.includes('?')\n}\n\nfunction patternIsRegExp(pattern: SyncFilterPattern): pattern is RegExp {\n return typeof pattern !== 'string'\n}\n\nfunction splitPath(path: string): string[] {\n if (path === '') return []\n return path.split('/').filter((segment) => segment !== '')\n}\n\nfunction normalizePath(path: string): string {\n let normalized = path.split('\\\\').join('/')\n while (normalized.startsWith('./')) normalized = normalized.slice(2)\n while (normalized.startsWith('/')) normalized = normalized.slice(1)\n while (normalized.endsWith('/') && normalized.length > 1) {\n normalized = normalized.slice(0, -1)\n }\n return normalized\n}\n"],"mappings":"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"}
|
package/dist/sync/index.d.cts
CHANGED
|
@@ -22,5 +22,5 @@ export type { SyncSha1PendingState, SyncSha1State, SyncSha1UnavailableState, Syn
|
|
|
22
22
|
export { isUntrustedSha1, parseSyncContentSha1, selectB2ComparableSha1, syncSha1StateOf, untrustedSha1, untrustedSha1Prefix, } from './sha1-metadata.js';
|
|
23
23
|
export type { B2SyncFolder, LocalSyncFolder, SupportedSynchronizerConfig, SynchronizerB2Config, SynchronizerConfig, SynchronizerDownConfig, SynchronizerUpConfig, } from './synchronizer.js';
|
|
24
24
|
export { synchronize } from './synchronizer.js';
|
|
25
|
-
export type { B2SyncPath, CompareMode, KeepMode, LocalFileIdentity, LocalSyncPath, SyncActionEvent, SyncActionEventType, SyncCompareEvent, SyncDirection, SyncEncryptionProvider, SyncErrorEvent, SyncEvent, SyncEventType, SyncFilterOptions, SyncFilterPattern, SyncFolder, SyncOptions, SyncPath, SyncScanOptions, SyncSkipEvent, SyncSkipReason, } from './types.js';
|
|
25
|
+
export type { B2SyncPath, CompareMode, KeepMode, LocalFileIdentity, LocalSymlinkPolicy, LocalSyncPath, SyncActionEvent, SyncActionEventType, SyncCompareEvent, SyncDirection, SyncEncryptionProvider, SyncErrorEvent, SyncEvent, SyncEventType, SyncFilterOptions, SyncFilterPattern, SyncFolder, SyncOptions, SyncPath, SyncScanOptions, SyncSkipEvent, SyncSkipReason, } from './types.js';
|
|
26
26
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.js"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,EACV,UAAU,EACV,cAAc,GACf,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,UAAU,EACV,YAAY,GACb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,4BAA4B,EAC5B,eAAe,EACf,2BAA2B,EAC3B,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EACL,gBAAgB,EAChB,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,YAAY,EACV,oBAAoB,EACpB,aAAa,EACb,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,aAAa,EACb,mBAAmB,GACpB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACV,YAAY,EACZ,eAAe,EACf,2BAA2B,EAC3B,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,YAAY,EACV,UAAU,EACV,WAAW,EACX,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,sBAAsB,EACtB,cAAc,EACd,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,eAAe,EACf,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.js"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,EACV,UAAU,EACV,cAAc,GACf,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,UAAU,EACV,YAAY,GACb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,4BAA4B,EAC5B,eAAe,EACf,2BAA2B,EAC3B,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EACL,gBAAgB,EAChB,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,YAAY,EACV,oBAAoB,EACpB,aAAa,EACb,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,aAAa,EACb,mBAAmB,GACpB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACV,YAAY,EACZ,eAAe,EACf,2BAA2B,EAC3B,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,YAAY,EACV,UAAU,EACV,WAAW,EACX,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,sBAAsB,EACtB,cAAc,EACd,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,eAAe,EACf,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAA"}
|