@backblaze-labs/b2-sdk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -1
- package/README.md +20 -2
- package/dist/_virtual/_b2-sdk-version-json.cjs +1 -1
- package/dist/_virtual/_b2-sdk-version-json.js +1 -1
- package/dist/auth/file.cjs.map +1 -1
- package/dist/backup/client.cjs +206 -0
- package/dist/backup/client.cjs.map +1 -0
- package/dist/backup/client.d.cts +238 -0
- package/dist/backup/client.d.cts.map +1 -0
- package/dist/backup/client.d.ts +238 -0
- package/dist/backup/client.d.ts.map +1 -0
- package/dist/backup/client.js +206 -0
- package/dist/backup/client.js.map +1 -0
- package/dist/backup/index.cjs +15 -0
- package/dist/backup/index.d.cts +22 -0
- package/dist/backup/index.d.cts.map +1 -0
- package/dist/backup/index.d.ts +22 -0
- package/dist/backup/index.d.ts.map +1 -0
- package/dist/backup/index.js +7 -0
- package/dist/backup/raw.cjs +146 -0
- package/dist/backup/raw.cjs.map +1 -0
- package/dist/backup/raw.d.cts +122 -0
- package/dist/backup/raw.d.cts.map +1 -0
- package/dist/backup/raw.d.ts +122 -0
- package/dist/backup/raw.d.ts.map +1 -0
- package/dist/backup/raw.js +146 -0
- package/dist/backup/raw.js.map +1 -0
- package/dist/bucket.cjs +2 -1
- package/dist/bucket.cjs.map +1 -1
- package/dist/bucket.js +2 -1
- package/dist/bucket.js.map +1 -1
- package/dist/copy/large.cjs +5 -3
- package/dist/copy/large.cjs.map +1 -1
- package/dist/copy/large.d.cts.map +1 -1
- package/dist/copy/large.d.ts.map +1 -1
- package/dist/copy/large.js +5 -3
- package/dist/copy/large.js.map +1 -1
- package/dist/download/checksum.cjs +6 -2
- package/dist/download/checksum.cjs.map +1 -1
- package/dist/download/checksum.js +6 -2
- package/dist/download/checksum.js.map +1 -1
- package/dist/download/parallel.cjs +52 -15
- package/dist/download/parallel.cjs.map +1 -1
- package/dist/download/parallel.d.cts +8 -1
- package/dist/download/parallel.d.cts.map +1 -1
- package/dist/download/parallel.d.ts +8 -1
- package/dist/download/parallel.d.ts.map +1 -1
- package/dist/download/parallel.js +52 -15
- package/dist/download/parallel.js.map +1 -1
- package/dist/download/single.cjs +141 -6
- package/dist/download/single.cjs.map +1 -1
- package/dist/download/single.d.cts.map +1 -1
- package/dist/download/single.d.ts.map +1 -1
- package/dist/download/single.js +141 -6
- package/dist/download/single.js.map +1 -1
- package/dist/errors/index.cjs +173 -3
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +119 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +119 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +161 -4
- package/dist/errors/index.js.map +1 -1
- package/dist/http/retry.cjs +1 -1
- package/dist/http/retry.cjs.map +1 -1
- package/dist/http/retry.js +1 -1
- package/dist/http/retry.js.map +1 -1
- package/dist/http/transport.cjs +61 -23
- package/dist/http/transport.cjs.map +1 -1
- package/dist/http/transport.d.cts +25 -3
- package/dist/http/transport.d.cts.map +1 -1
- package/dist/http/transport.d.ts +25 -3
- package/dist/http/transport.d.ts.map +1 -1
- package/dist/http/transport.js +59 -22
- package/dist/http/transport.js.map +1 -1
- package/dist/index.cjs +25 -2
- package/dist/index.d.cts +6 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/object.cjs +1 -1
- package/dist/object.cjs.map +1 -1
- package/dist/object.d.cts +4 -2
- package/dist/object.d.cts.map +1 -1
- package/dist/object.d.ts +4 -2
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +1 -1
- package/dist/object.js.map +1 -1
- package/dist/partner/account-info.d.cts +43 -0
- package/dist/partner/account-info.d.cts.map +1 -0
- package/dist/partner/account-info.d.ts +43 -0
- package/dist/partner/account-info.d.ts.map +1 -0
- package/dist/partner/auth-clone.cjs +64 -0
- package/dist/partner/auth-clone.cjs.map +1 -0
- package/dist/partner/auth-clone.d.cts +27 -0
- package/dist/partner/auth-clone.d.cts.map +1 -0
- package/dist/partner/auth-clone.d.ts +27 -0
- package/dist/partner/auth-clone.d.ts.map +1 -0
- package/dist/partner/auth-clone.js +63 -0
- package/dist/partner/auth-clone.js.map +1 -0
- package/dist/partner/auth-core.cjs +175 -0
- package/dist/partner/auth-core.cjs.map +1 -0
- package/dist/partner/auth-core.d.cts +111 -0
- package/dist/partner/auth-core.d.cts.map +1 -0
- package/dist/partner/auth-core.d.ts +111 -0
- package/dist/partner/auth-core.d.ts.map +1 -0
- package/dist/partner/auth-core.js +175 -0
- package/dist/partner/auth-core.js.map +1 -0
- package/dist/partner/auth-shape.cjs +36 -0
- package/dist/partner/auth-shape.cjs.map +1 -0
- package/dist/partner/auth-shape.d.cts +12 -0
- package/dist/partner/auth-shape.d.cts.map +1 -0
- package/dist/partner/auth-shape.d.ts +12 -0
- package/dist/partner/auth-shape.d.ts.map +1 -0
- package/dist/partner/auth-shape.js +36 -0
- package/dist/partner/auth-shape.js.map +1 -0
- package/dist/partner/client.cjs +255 -0
- package/dist/partner/client.cjs.map +1 -0
- package/dist/partner/client.d.cts +335 -0
- package/dist/partner/client.d.cts.map +1 -0
- package/dist/partner/client.d.ts +335 -0
- package/dist/partner/client.d.ts.map +1 -0
- package/dist/partner/client.js +255 -0
- package/dist/partner/client.js.map +1 -0
- package/dist/partner/endpoint-url.cjs +108 -0
- package/dist/partner/endpoint-url.cjs.map +1 -0
- package/dist/partner/endpoint-url.d.cts +72 -0
- package/dist/partner/endpoint-url.d.cts.map +1 -0
- package/dist/partner/endpoint-url.d.ts +72 -0
- package/dist/partner/endpoint-url.d.ts.map +1 -0
- package/dist/partner/endpoint-url.js +105 -0
- package/dist/partner/endpoint-url.js.map +1 -0
- package/dist/partner/in-memory.cjs +135 -0
- package/dist/partner/in-memory.cjs.map +1 -0
- package/dist/partner/in-memory.d.cts +99 -0
- package/dist/partner/in-memory.d.cts.map +1 -0
- package/dist/partner/in-memory.d.ts +99 -0
- package/dist/partner/in-memory.d.ts.map +1 -0
- package/dist/partner/in-memory.js +135 -0
- package/dist/partner/in-memory.js.map +1 -0
- package/dist/partner/index.cjs +18 -0
- package/dist/partner/index.d.cts +23 -0
- package/dist/partner/index.d.cts.map +1 -0
- package/dist/partner/index.d.ts +23 -0
- package/dist/partner/index.d.ts.map +1 -0
- package/dist/partner/index.js +7 -0
- package/dist/partner/raw.cjs +392 -0
- package/dist/partner/raw.cjs.map +1 -0
- package/dist/partner/raw.d.cts +223 -0
- package/dist/partner/raw.d.cts.map +1 -0
- package/dist/partner/raw.d.ts +223 -0
- package/dist/partner/raw.d.ts.map +1 -0
- package/dist/partner/raw.js +390 -0
- package/dist/partner/raw.js.map +1 -0
- package/dist/partner/redaction-placeholders.cjs +10 -0
- package/dist/partner/redaction-placeholders.cjs.map +1 -0
- package/dist/partner/redaction-placeholders.d.cts +5 -0
- package/dist/partner/redaction-placeholders.d.cts.map +1 -0
- package/dist/partner/redaction-placeholders.d.ts +5 -0
- package/dist/partner/redaction-placeholders.d.ts.map +1 -0
- package/dist/partner/redaction-placeholders.js +9 -0
- package/dist/partner/redaction-placeholders.js.map +1 -0
- package/dist/partner/redaction.cjs +267 -0
- package/dist/partner/redaction.cjs.map +1 -0
- package/dist/partner/redaction.d.cts +124 -0
- package/dist/partner/redaction.d.cts.map +1 -0
- package/dist/partner/redaction.d.ts +124 -0
- package/dist/partner/redaction.d.ts.map +1 -0
- package/dist/partner/redaction.js +257 -0
- package/dist/partner/redaction.js.map +1 -0
- package/dist/raw/index.cjs +81 -19
- package/dist/raw/index.cjs.map +1 -1
- package/dist/raw/index.d.cts +6 -2
- package/dist/raw/index.d.cts.map +1 -1
- package/dist/raw/index.d.ts +6 -2
- package/dist/raw/index.d.ts.map +1 -1
- package/dist/raw/index.js +81 -19
- package/dist/raw/index.js.map +1 -1
- package/dist/raw/url.cjs +111 -0
- package/dist/raw/url.cjs.map +1 -0
- package/dist/raw/url.d.cts +41 -0
- package/dist/raw/url.d.cts.map +1 -0
- package/dist/raw/url.d.ts +41 -0
- package/dist/raw/url.d.ts.map +1 -0
- package/dist/raw/url.js +110 -0
- package/dist/raw/url.js.map +1 -0
- package/dist/s3/sigv4.cjs +15 -10
- package/dist/s3/sigv4.cjs.map +1 -1
- package/dist/s3/sigv4.js +15 -10
- package/dist/s3/sigv4.js.map +1 -1
- package/dist/simulator/index.cjs +1197 -206
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.cts +109 -11
- package/dist/simulator/index.d.cts.map +1 -1
- package/dist/simulator/index.d.ts +109 -11
- package/dist/simulator/index.d.ts.map +1 -1
- package/dist/simulator/index.js +1196 -209
- package/dist/simulator/index.js.map +1 -1
- package/dist/simulator/partner.cjs +719 -0
- package/dist/simulator/partner.cjs.map +1 -0
- package/dist/simulator/partner.d.cts +183 -0
- package/dist/simulator/partner.d.cts.map +1 -0
- package/dist/simulator/partner.d.ts +183 -0
- package/dist/simulator/partner.d.ts.map +1 -0
- package/dist/simulator/partner.js +718 -0
- package/dist/simulator/partner.js.map +1 -0
- package/dist/simulator/validation.cjs +577 -0
- package/dist/simulator/validation.cjs.map +1 -1
- package/dist/simulator/validation.d.cts +117 -0
- package/dist/simulator/validation.d.cts.map +1 -1
- package/dist/simulator/validation.d.ts +117 -0
- package/dist/simulator/validation.d.ts.map +1 -1
- package/dist/simulator/validation.js +563 -1
- package/dist/simulator/validation.js.map +1 -1
- package/dist/streams/file-source.cjs +1 -1
- package/dist/streams/file-source.cjs.map +1 -1
- package/dist/streams/file-source.js +1 -1
- package/dist/streams/file-source.js.map +1 -1
- package/dist/streams/index.cjs +1 -1
- package/dist/streams/index.js +1 -1
- package/dist/streams/source.cjs +2 -1
- package/dist/streams/source.cjs.map +1 -1
- package/dist/streams/source.js +2 -1
- package/dist/streams/source.js.map +1 -1
- package/dist/sync/b2-sha1-reader.cjs +6 -2
- package/dist/sync/b2-sha1-reader.cjs.map +1 -1
- package/dist/sync/b2-sha1-reader.d.cts.map +1 -1
- package/dist/sync/b2-sha1-reader.d.ts.map +1 -1
- package/dist/sync/b2-sha1-reader.js +6 -2
- package/dist/sync/b2-sha1-reader.js.map +1 -1
- package/dist/sync/download-staging.cjs +11 -10
- package/dist/sync/download-staging.cjs.map +1 -1
- package/dist/sync/download-staging.d.cts +2 -1
- package/dist/sync/download-staging.d.cts.map +1 -1
- package/dist/sync/download-staging.d.ts +2 -1
- package/dist/sync/download-staging.d.ts.map +1 -1
- package/dist/sync/download-staging.js +11 -10
- package/dist/sync/download-staging.js.map +1 -1
- package/dist/sync/filters.cjs.map +1 -1
- package/dist/sync/filters.js.map +1 -1
- package/dist/sync/local-file-io.cjs +11 -6
- package/dist/sync/local-file-io.cjs.map +1 -1
- package/dist/sync/local-file-io.d.cts +1 -0
- package/dist/sync/local-file-io.d.cts.map +1 -1
- package/dist/sync/local-file-io.d.ts +1 -0
- package/dist/sync/local-file-io.d.ts.map +1 -1
- package/dist/sync/local-file-io.js +11 -6
- package/dist/sync/local-file-io.js.map +1 -1
- package/dist/sync/local-sha1.cjs +6 -15
- package/dist/sync/local-sha1.cjs.map +1 -1
- package/dist/sync/local-sha1.d.cts +0 -8
- package/dist/sync/local-sha1.d.cts.map +1 -1
- package/dist/sync/local-sha1.d.ts +0 -8
- package/dist/sync/local-sha1.d.ts.map +1 -1
- package/dist/sync/local-sha1.js +7 -15
- package/dist/sync/local-sha1.js.map +1 -1
- package/dist/sync/policies/compare.cjs +5 -3
- package/dist/sync/policies/compare.cjs.map +1 -1
- package/dist/sync/policies/compare.d.cts.map +1 -1
- package/dist/sync/policies/compare.d.ts.map +1 -1
- package/dist/sync/policies/compare.js +6 -4
- package/dist/sync/policies/compare.js.map +1 -1
- package/dist/sync/policies/index.cjs +4 -10
- package/dist/sync/policies/index.cjs.map +1 -1
- package/dist/sync/policies/index.js +4 -10
- package/dist/sync/policies/index.js.map +1 -1
- package/dist/sync/scanners/b2.cjs +2 -2
- package/dist/sync/scanners/b2.cjs.map +1 -1
- package/dist/sync/scanners/b2.js +2 -2
- package/dist/sync/scanners/b2.js.map +1 -1
- package/dist/sync/scanners/local.cjs.map +1 -1
- package/dist/sync/scanners/local.js.map +1 -1
- package/dist/sync/synchronizer.cjs +3 -2
- package/dist/sync/synchronizer.cjs.map +1 -1
- package/dist/sync/synchronizer.js +3 -2
- package/dist/sync/synchronizer.js.map +1 -1
- package/dist/types/auth.cjs +4 -0
- package/dist/types/auth.cjs.map +1 -1
- package/dist/types/auth.d.cts +4 -0
- package/dist/types/auth.d.cts.map +1 -1
- package/dist/types/auth.d.ts +4 -0
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/auth.js +4 -0
- package/dist/types/auth.js.map +1 -1
- package/dist/types/backup.d.cts +80 -0
- package/dist/types/backup.d.cts.map +1 -0
- package/dist/types/backup.d.ts +80 -0
- package/dist/types/backup.d.ts.map +1 -0
- package/dist/types/bucket.cjs.map +1 -1
- package/dist/types/bucket.d.cts +4 -2
- package/dist/types/bucket.d.cts.map +1 -1
- package/dist/types/bucket.d.ts +4 -2
- package/dist/types/bucket.d.ts.map +1 -1
- package/dist/types/bucket.js.map +1 -1
- package/dist/types/download.cjs +62 -0
- package/dist/types/download.cjs.map +1 -0
- package/dist/types/download.d.cts +98 -0
- package/dist/types/download.d.cts.map +1 -1
- package/dist/types/download.d.ts +98 -0
- package/dist/types/download.d.ts.map +1 -1
- package/dist/types/download.js +61 -0
- package/dist/types/download.js.map +1 -0
- package/dist/types/encryption.cjs +58 -229
- package/dist/types/encryption.cjs.map +1 -1
- package/dist/types/encryption.d.cts +32 -0
- package/dist/types/encryption.d.cts.map +1 -1
- package/dist/types/encryption.d.ts +32 -0
- package/dist/types/encryption.d.ts.map +1 -1
- package/dist/types/encryption.js +56 -229
- package/dist/types/encryption.js.map +1 -1
- package/dist/types/errors.cjs +9 -0
- package/dist/types/errors.cjs.map +1 -1
- package/dist/types/errors.d.cts +1 -1
- package/dist/types/errors.d.cts.map +1 -1
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +9 -0
- package/dist/types/errors.js.map +1 -1
- package/dist/types/file.cjs.map +1 -1
- package/dist/types/file.d.cts +15 -2
- package/dist/types/file.d.cts.map +1 -1
- package/dist/types/file.d.ts +15 -2
- package/dist/types/file.d.ts.map +1 -1
- package/dist/types/file.js.map +1 -1
- package/dist/types/ids.cjs +30 -0
- package/dist/types/ids.cjs.map +1 -1
- package/dist/types/ids.d.cts +32 -0
- package/dist/types/ids.d.cts.map +1 -1
- package/dist/types/ids.d.ts +32 -0
- package/dist/types/ids.d.ts.map +1 -1
- package/dist/types/ids.js +28 -1
- package/dist/types/ids.js.map +1 -1
- package/dist/types/index.d.cts +10 -6
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +10 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lock.cjs.map +1 -1
- package/dist/types/lock.d.cts +14 -0
- package/dist/types/lock.d.cts.map +1 -1
- package/dist/types/lock.d.ts +14 -0
- package/dist/types/lock.d.ts.map +1 -1
- package/dist/types/lock.js.map +1 -1
- package/dist/types/notifications.cjs +1 -1
- package/dist/types/notifications.cjs.map +1 -1
- package/dist/types/notifications.d.cts +5 -3
- package/dist/types/notifications.d.cts.map +1 -1
- package/dist/types/notifications.d.ts +5 -3
- package/dist/types/notifications.d.ts.map +1 -1
- package/dist/types/notifications.js +1 -1
- package/dist/types/notifications.js.map +1 -1
- package/dist/types/partner.cjs +32 -0
- package/dist/types/partner.cjs.map +1 -0
- package/dist/types/partner.d.cts +394 -0
- package/dist/types/partner.d.cts.map +1 -0
- package/dist/types/partner.d.ts +394 -0
- package/dist/types/partner.d.ts.map +1 -0
- package/dist/types/partner.js +29 -0
- package/dist/types/partner.js.map +1 -0
- package/dist/types/upload.d.cts +31 -18
- package/dist/types/upload.d.cts.map +1 -1
- package/dist/types/upload.d.ts +31 -18
- package/dist/types/upload.d.ts.map +1 -1
- package/dist/upload/abort-scope.cjs +0 -45
- package/dist/upload/abort-scope.cjs.map +1 -1
- package/dist/upload/abort-scope.d.cts +0 -22
- package/dist/upload/abort-scope.d.cts.map +1 -1
- package/dist/upload/abort-scope.d.ts +0 -22
- package/dist/upload/abort-scope.d.ts.map +1 -1
- package/dist/upload/abort-scope.js +1 -44
- package/dist/upload/abort-scope.js.map +1 -1
- package/dist/upload/finish.cjs +3 -8
- package/dist/upload/finish.cjs.map +1 -1
- package/dist/upload/finish.d.cts.map +1 -1
- package/dist/upload/finish.d.ts.map +1 -1
- package/dist/upload/finish.js +1 -6
- package/dist/upload/finish.js.map +1 -1
- package/dist/upload/large.cjs +5 -1
- package/dist/upload/large.cjs.map +1 -1
- package/dist/upload/large.d.cts +16 -2
- package/dist/upload/large.d.cts.map +1 -1
- package/dist/upload/large.d.ts +16 -2
- package/dist/upload/large.d.ts.map +1 -1
- package/dist/upload/large.js +5 -1
- package/dist/upload/large.js.map +1 -1
- package/dist/upload/options.cjs.map +1 -1
- package/dist/upload/options.d.cts +12 -0
- package/dist/upload/options.d.cts.map +1 -1
- package/dist/upload/options.d.ts +12 -0
- package/dist/upload/options.d.ts.map +1 -1
- package/dist/upload/options.js.map +1 -1
- package/dist/upload/resume.cjs +2 -1
- package/dist/upload/resume.cjs.map +1 -1
- package/dist/upload/resume.d.cts +3 -1
- package/dist/upload/resume.d.cts.map +1 -1
- package/dist/upload/resume.d.ts +3 -1
- package/dist/upload/resume.d.ts.map +1 -1
- package/dist/upload/resume.js +2 -1
- package/dist/upload/resume.js.map +1 -1
- package/dist/upload/retry.cjs +8 -3
- package/dist/upload/retry.cjs.map +1 -1
- package/dist/upload/retry.js +8 -3
- package/dist/upload/retry.js.map +1 -1
- package/dist/upload/single.cjs +2 -1
- package/dist/upload/single.cjs.map +1 -1
- package/dist/upload/single.d.cts +6 -0
- package/dist/upload/single.d.cts.map +1 -1
- package/dist/upload/single.d.ts +6 -0
- package/dist/upload/single.d.ts.map +1 -1
- package/dist/upload/single.js +2 -1
- package/dist/upload/single.js.map +1 -1
- package/dist/upload/stream.cjs +6 -4
- package/dist/upload/stream.cjs.map +1 -1
- package/dist/upload/stream.d.cts +6 -0
- package/dist/upload/stream.d.cts.map +1 -1
- package/dist/upload/stream.d.ts +6 -0
- package/dist/upload/stream.d.ts.map +1 -1
- package/dist/upload/stream.js +3 -1
- package/dist/upload/stream.js.map +1 -1
- package/dist/util/abort.cjs +114 -0
- package/dist/util/abort.cjs.map +1 -0
- package/dist/util/abort.d.cts +65 -0
- package/dist/util/abort.d.cts.map +1 -0
- package/dist/util/abort.d.ts +65 -0
- package/dist/util/abort.d.ts.map +1 -0
- package/dist/util/abort.js +109 -0
- package/dist/util/abort.js.map +1 -0
- package/dist/util/defaults.cjs +3 -0
- package/dist/util/defaults.cjs.map +1 -1
- package/dist/util/defaults.d.cts +2 -0
- package/dist/util/defaults.d.cts.map +1 -1
- package/dist/util/defaults.d.ts +2 -0
- package/dist/util/defaults.d.ts.map +1 -1
- package/dist/util/defaults.js +3 -1
- package/dist/util/defaults.js.map +1 -1
- package/dist/util/md5.cjs +211 -0
- package/dist/util/md5.cjs.map +1 -0
- package/dist/util/md5.d.cts +43 -0
- package/dist/util/md5.d.cts.map +1 -0
- package/dist/util/md5.d.ts +43 -0
- package/dist/util/md5.d.ts.map +1 -0
- package/dist/util/md5.js +209 -0
- package/dist/util/md5.js.map +1 -0
- package/dist/util/normalize.cjs +5 -5
- package/dist/util/normalize.cjs.map +1 -1
- package/dist/util/normalize.d.cts +7 -7
- package/dist/util/normalize.d.cts.map +1 -1
- package/dist/util/normalize.d.ts +7 -7
- package/dist/util/normalize.d.ts.map +1 -1
- package/dist/util/normalize.js +5 -5
- package/dist/util/normalize.js.map +1 -1
- package/dist/util/paginator.cjs +7 -1
- package/dist/util/paginator.cjs.map +1 -1
- package/dist/util/paginator.d.cts +3 -1
- package/dist/util/paginator.d.cts.map +1 -1
- package/dist/util/paginator.d.ts +3 -1
- package/dist/util/paginator.d.ts.map +1 -1
- package/dist/util/paginator.js +7 -1
- package/dist/util/paginator.js.map +1 -1
- package/package.json +38 -16
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.cjs","names":[],"sources":["../../src/raw/url.ts"],"sourcesContent":["/**\n * B2-style API version segment, such as `v1`, `v3`, or `v4`.\n *\n * The template-literal type documents the compile-time shape; use\n * {@link isB2ApiVersion} when accepting untyped runtime input.\n */\nexport type B2ApiVersion = `v${number}`\n\nconst SLASH = '/'\nconst BACKSLASH = '\\\\'\nconst VERSION_PREFIX = 'v'\nconst VERSION_DIGITS = '0123456789'\nconst URL_DELIMITERS = ['?', '#'] as const\n\n/** URL path options for a B2 API endpoint. */\nexport interface B2UrlOptions {\n /** Base path prefix before the optional version segment. Defaults to `b2api`. */\n readonly prefix?: string\n /**\n * Optional API version segment. SDK storage endpoints use `v3` and `v4`;\n * Computer Backup endpoints use the same `vN` segment shape under a\n * different prefix, such as `api/backup/v1`.\n */\n readonly version?: B2ApiVersion\n /** Endpoint name appended after the prefix and optional version. */\n readonly endpoint: string\n}\n\n/** URL path options for a B2 API surface before the endpoint is selected. */\nexport type B2EndpointUrlOptions = Omit<B2UrlOptions, 'endpoint'>\n\n/**\n * Returns true when `value` is a B2 API version segment of the form `vN`.\n *\n * @param value - Candidate API version segment.\n *\n * @returns Whether the value is a valid B2 API version segment.\n */\nexport function isB2ApiVersion(value: string): value is B2ApiVersion {\n if (!value.startsWith(VERSION_PREFIX) || value.length === VERSION_PREFIX.length) return false\n\n for (let index = VERSION_PREFIX.length; index < value.length; index += 1) {\n const digit = value[index]\n if (digit === undefined || !VERSION_DIGITS.includes(digit)) return false\n }\n\n return true\n}\n\nfunction assertB2ApiVersion(value: string): asserts value is B2ApiVersion {\n if (!isB2ApiVersion(value)) {\n throw new TypeError('Invalid version: expected a B2 API version segment like \"v3\"')\n }\n}\n\nfunction trimSlashes(\n value: string,\n { leading, trailing }: { readonly leading: boolean; readonly trailing: boolean },\n): string {\n let start = 0\n let end = value.length\n\n while (leading && start < end && value[start] === SLASH) {\n start += 1\n }\n while (trailing && end > start && value[end - 1] === SLASH) {\n end -= 1\n }\n\n return start === 0 && end === value.length ? value : value.slice(start, end)\n}\n\nfunction hasUrlDelimiter(value: string): boolean {\n return URL_DELIMITERS.some((delimiter) => value.includes(delimiter))\n}\n\nfunction hasEncodedPathDelimiter(value: string): boolean {\n const lowerValue = value.toLowerCase()\n return (\n lowerValue.includes('%2f') ||\n lowerValue.includes('%3f') ||\n lowerValue.includes('%23') ||\n lowerValue.includes('%5c')\n )\n}\n\nfunction withDotEscapesDecoded(value: string): string {\n let decoded = ''\n let index = 0\n\n while (index < value.length) {\n if (value.slice(index, index + 3).toLowerCase() === '%2e') {\n decoded += '.'\n index += 3\n } else {\n decoded += value[index]\n index += 1\n }\n }\n\n return decoded\n}\n\nfunction isTraversalComponent(value: string): boolean {\n const decoded = withDotEscapesDecoded(value)\n return decoded === '.' || decoded === '..'\n}\n\nfunction validatePathComponent(component: string, optionName: string): void {\n if (component.length === 0) {\n throw new TypeError(`Invalid ${optionName}: path components must not be empty`)\n }\n if (hasUrlDelimiter(component)) {\n throw new TypeError(`Invalid ${optionName}: path components must not contain \"?\" or \"#\"`)\n }\n if (component.includes(BACKSLASH)) {\n throw new TypeError(`Invalid ${optionName}: path components must not contain \"\\\\\"`)\n }\n if (hasEncodedPathDelimiter(component)) {\n throw new TypeError(\n `Invalid ${optionName}: path components must not contain encoded \"/\", \"\\\\\", \"?\", or \"#\"`,\n )\n }\n if (isTraversalComponent(component)) {\n throw new TypeError(`Invalid ${optionName}: path components must not be \".\" or \"..\"`)\n }\n}\n\nfunction preparePathPrefix(prefix: string): string {\n const trimmed = trimSlashes(prefix, { leading: true, trailing: true })\n if (trimmed.length === 0) return ''\n\n for (const component of trimmed.split(SLASH)) {\n validatePathComponent(component, 'prefix')\n }\n\n return trimmed\n}\n\nfunction preparePathSegment(segment: string, optionName: string): string {\n const trimmed = trimSlashes(segment, { leading: true, trailing: true })\n if (trimmed.length === 0) return ''\n if (trimmed.includes(SLASH)) {\n throw new TypeError(`Invalid ${optionName}: expected a single path segment`)\n }\n\n validatePathComponent(trimmed, optionName)\n return trimmed\n}\n\nfunction prepareVersionSegment(version: B2ApiVersion): B2ApiVersion {\n const trimmed = preparePathSegment(version, 'version')\n assertB2ApiVersion(trimmed)\n return trimmed\n}\n\n/**\n * Builds a B2 API endpoint URL from a base URL, path prefix, optional version,\n * and endpoint name.\n *\n * @param baseUrl - API or download base URL.\n * @param options - Path prefix, optional version, and endpoint name.\n *\n * @returns The absolute endpoint URL.\n */\nexport function b2Url(\n baseUrl: string,\n { prefix = 'b2api', version, endpoint }: B2UrlOptions,\n): string {\n const base = trimSlashes(baseUrl, { leading: false, trailing: true })\n const path = [\n preparePathPrefix(prefix),\n version === undefined ? undefined : prepareVersionSegment(version),\n preparePathSegment(endpoint, 'endpoint'),\n ]\n .filter((segment): segment is string => segment !== undefined)\n .filter((segment) => segment.length > 0)\n .join(SLASH)\n\n return path.length === 0 ? base : `${base}/${path}`\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* B2-style API version segment, such as `v1`, `v3`, or `v4`.
|
|
3
|
+
*
|
|
4
|
+
* The template-literal type documents the compile-time shape; use
|
|
5
|
+
* {@link isB2ApiVersion} when accepting untyped runtime input.
|
|
6
|
+
*/
|
|
7
|
+
export type B2ApiVersion = `v${number}`;
|
|
8
|
+
/** URL path options for a B2 API endpoint. */
|
|
9
|
+
export interface B2UrlOptions {
|
|
10
|
+
/** Base path prefix before the optional version segment. Defaults to `b2api`. */
|
|
11
|
+
readonly prefix?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Optional API version segment. SDK storage endpoints use `v3` and `v4`;
|
|
14
|
+
* Computer Backup endpoints use the same `vN` segment shape under a
|
|
15
|
+
* different prefix, such as `api/backup/v1`.
|
|
16
|
+
*/
|
|
17
|
+
readonly version?: B2ApiVersion;
|
|
18
|
+
/** Endpoint name appended after the prefix and optional version. */
|
|
19
|
+
readonly endpoint: string;
|
|
20
|
+
}
|
|
21
|
+
/** URL path options for a B2 API surface before the endpoint is selected. */
|
|
22
|
+
export type B2EndpointUrlOptions = Omit<B2UrlOptions, 'endpoint'>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns true when `value` is a B2 API version segment of the form `vN`.
|
|
25
|
+
*
|
|
26
|
+
* @param value - Candidate API version segment.
|
|
27
|
+
*
|
|
28
|
+
* @returns Whether the value is a valid B2 API version segment.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isB2ApiVersion(value: string): value is B2ApiVersion;
|
|
31
|
+
/**
|
|
32
|
+
* Builds a B2 API endpoint URL from a base URL, path prefix, optional version,
|
|
33
|
+
* and endpoint name.
|
|
34
|
+
*
|
|
35
|
+
* @param baseUrl - API or download base URL.
|
|
36
|
+
* @param options - Path prefix, optional version, and endpoint name.
|
|
37
|
+
*
|
|
38
|
+
* @returns The absolute endpoint URL.
|
|
39
|
+
*/
|
|
40
|
+
export declare function b2Url(baseUrl: string, { prefix, version, endpoint }: B2UrlOptions): string;
|
|
41
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/raw/url.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,MAAM,EAAE,CAAA;AAQvC,8CAA8C;AAC9C,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAA;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,6EAA6E;AAC7E,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,YAAY,CASnE;AA6GD;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,MAAM,EACf,EAAE,MAAgB,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,YAAY,GACpD,MAAM,CAYR"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* B2-style API version segment, such as `v1`, `v3`, or `v4`.
|
|
3
|
+
*
|
|
4
|
+
* The template-literal type documents the compile-time shape; use
|
|
5
|
+
* {@link isB2ApiVersion} when accepting untyped runtime input.
|
|
6
|
+
*/
|
|
7
|
+
export type B2ApiVersion = `v${number}`;
|
|
8
|
+
/** URL path options for a B2 API endpoint. */
|
|
9
|
+
export interface B2UrlOptions {
|
|
10
|
+
/** Base path prefix before the optional version segment. Defaults to `b2api`. */
|
|
11
|
+
readonly prefix?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Optional API version segment. SDK storage endpoints use `v3` and `v4`;
|
|
14
|
+
* Computer Backup endpoints use the same `vN` segment shape under a
|
|
15
|
+
* different prefix, such as `api/backup/v1`.
|
|
16
|
+
*/
|
|
17
|
+
readonly version?: B2ApiVersion;
|
|
18
|
+
/** Endpoint name appended after the prefix and optional version. */
|
|
19
|
+
readonly endpoint: string;
|
|
20
|
+
}
|
|
21
|
+
/** URL path options for a B2 API surface before the endpoint is selected. */
|
|
22
|
+
export type B2EndpointUrlOptions = Omit<B2UrlOptions, 'endpoint'>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns true when `value` is a B2 API version segment of the form `vN`.
|
|
25
|
+
*
|
|
26
|
+
* @param value - Candidate API version segment.
|
|
27
|
+
*
|
|
28
|
+
* @returns Whether the value is a valid B2 API version segment.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isB2ApiVersion(value: string): value is B2ApiVersion;
|
|
31
|
+
/**
|
|
32
|
+
* Builds a B2 API endpoint URL from a base URL, path prefix, optional version,
|
|
33
|
+
* and endpoint name.
|
|
34
|
+
*
|
|
35
|
+
* @param baseUrl - API or download base URL.
|
|
36
|
+
* @param options - Path prefix, optional version, and endpoint name.
|
|
37
|
+
*
|
|
38
|
+
* @returns The absolute endpoint URL.
|
|
39
|
+
*/
|
|
40
|
+
export declare function b2Url(baseUrl: string, { prefix, version, endpoint }: B2UrlOptions): string;
|
|
41
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/raw/url.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,MAAM,EAAE,CAAA;AAQvC,8CAA8C;AAC9C,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAA;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,6EAA6E;AAC7E,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,YAAY,CASnE;AA6GD;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,MAAM,EACf,EAAE,MAAgB,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,YAAY,GACpD,MAAM,CAYR"}
|
package/dist/raw/url.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
//#region src/raw/url.ts
|
|
2
|
+
var SLASH = "/";
|
|
3
|
+
var BACKSLASH = "\\";
|
|
4
|
+
var VERSION_PREFIX = "v";
|
|
5
|
+
var VERSION_DIGITS = "0123456789";
|
|
6
|
+
var URL_DELIMITERS = ["?", "#"];
|
|
7
|
+
/**
|
|
8
|
+
* Returns true when `value` is a B2 API version segment of the form `vN`.
|
|
9
|
+
*
|
|
10
|
+
* @param value - Candidate API version segment.
|
|
11
|
+
*
|
|
12
|
+
* @returns Whether the value is a valid B2 API version segment.
|
|
13
|
+
*/
|
|
14
|
+
function isB2ApiVersion(value) {
|
|
15
|
+
if (!value.startsWith(VERSION_PREFIX) || value.length === 1) return false;
|
|
16
|
+
for (let index = 1; index < value.length; index += 1) {
|
|
17
|
+
const digit = value[index];
|
|
18
|
+
if (digit === void 0 || !VERSION_DIGITS.includes(digit)) return false;
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
function assertB2ApiVersion(value) {
|
|
23
|
+
if (!isB2ApiVersion(value)) throw new TypeError("Invalid version: expected a B2 API version segment like \"v3\"");
|
|
24
|
+
}
|
|
25
|
+
function trimSlashes(value, { leading, trailing }) {
|
|
26
|
+
let start = 0;
|
|
27
|
+
let end = value.length;
|
|
28
|
+
while (leading && start < end && value[start] === SLASH) start += 1;
|
|
29
|
+
while (trailing && end > start && value[end - 1] === SLASH) end -= 1;
|
|
30
|
+
return start === 0 && end === value.length ? value : value.slice(start, end);
|
|
31
|
+
}
|
|
32
|
+
function hasUrlDelimiter(value) {
|
|
33
|
+
return URL_DELIMITERS.some((delimiter) => value.includes(delimiter));
|
|
34
|
+
}
|
|
35
|
+
function hasEncodedPathDelimiter(value) {
|
|
36
|
+
const lowerValue = value.toLowerCase();
|
|
37
|
+
return lowerValue.includes("%2f") || lowerValue.includes("%3f") || lowerValue.includes("%23") || lowerValue.includes("%5c");
|
|
38
|
+
}
|
|
39
|
+
function withDotEscapesDecoded(value) {
|
|
40
|
+
let decoded = "";
|
|
41
|
+
let index = 0;
|
|
42
|
+
while (index < value.length) if (value.slice(index, index + 3).toLowerCase() === "%2e") {
|
|
43
|
+
decoded += ".";
|
|
44
|
+
index += 3;
|
|
45
|
+
} else {
|
|
46
|
+
decoded += value[index];
|
|
47
|
+
index += 1;
|
|
48
|
+
}
|
|
49
|
+
return decoded;
|
|
50
|
+
}
|
|
51
|
+
function isTraversalComponent(value) {
|
|
52
|
+
const decoded = withDotEscapesDecoded(value);
|
|
53
|
+
return decoded === "." || decoded === "..";
|
|
54
|
+
}
|
|
55
|
+
function validatePathComponent(component, optionName) {
|
|
56
|
+
if (component.length === 0) throw new TypeError(`Invalid ${optionName}: path components must not be empty`);
|
|
57
|
+
if (hasUrlDelimiter(component)) throw new TypeError(`Invalid ${optionName}: path components must not contain "?" or "#"`);
|
|
58
|
+
if (component.includes(BACKSLASH)) throw new TypeError(`Invalid ${optionName}: path components must not contain "\\"`);
|
|
59
|
+
if (hasEncodedPathDelimiter(component)) throw new TypeError(`Invalid ${optionName}: path components must not contain encoded "/", "\\", "?", or "#"`);
|
|
60
|
+
if (isTraversalComponent(component)) throw new TypeError(`Invalid ${optionName}: path components must not be "." or ".."`);
|
|
61
|
+
}
|
|
62
|
+
function preparePathPrefix(prefix) {
|
|
63
|
+
const trimmed = trimSlashes(prefix, {
|
|
64
|
+
leading: true,
|
|
65
|
+
trailing: true
|
|
66
|
+
});
|
|
67
|
+
if (trimmed.length === 0) return "";
|
|
68
|
+
for (const component of trimmed.split(SLASH)) validatePathComponent(component, "prefix");
|
|
69
|
+
return trimmed;
|
|
70
|
+
}
|
|
71
|
+
function preparePathSegment(segment, optionName) {
|
|
72
|
+
const trimmed = trimSlashes(segment, {
|
|
73
|
+
leading: true,
|
|
74
|
+
trailing: true
|
|
75
|
+
});
|
|
76
|
+
if (trimmed.length === 0) return "";
|
|
77
|
+
if (trimmed.includes(SLASH)) throw new TypeError(`Invalid ${optionName}: expected a single path segment`);
|
|
78
|
+
validatePathComponent(trimmed, optionName);
|
|
79
|
+
return trimmed;
|
|
80
|
+
}
|
|
81
|
+
function prepareVersionSegment(version) {
|
|
82
|
+
const trimmed = preparePathSegment(version, "version");
|
|
83
|
+
assertB2ApiVersion(trimmed);
|
|
84
|
+
return trimmed;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Builds a B2 API endpoint URL from a base URL, path prefix, optional version,
|
|
88
|
+
* and endpoint name.
|
|
89
|
+
*
|
|
90
|
+
* @param baseUrl - API or download base URL.
|
|
91
|
+
* @param options - Path prefix, optional version, and endpoint name.
|
|
92
|
+
*
|
|
93
|
+
* @returns The absolute endpoint URL.
|
|
94
|
+
*/
|
|
95
|
+
function b2Url(baseUrl, { prefix = "b2api", version, endpoint }) {
|
|
96
|
+
const base = trimSlashes(baseUrl, {
|
|
97
|
+
leading: false,
|
|
98
|
+
trailing: true
|
|
99
|
+
});
|
|
100
|
+
const path = [
|
|
101
|
+
preparePathPrefix(prefix),
|
|
102
|
+
version === void 0 ? void 0 : prepareVersionSegment(version),
|
|
103
|
+
preparePathSegment(endpoint, "endpoint")
|
|
104
|
+
].filter((segment) => segment !== void 0).filter((segment) => segment.length > 0).join(SLASH);
|
|
105
|
+
return path.length === 0 ? base : `${base}/${path}`;
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
export { b2Url, isB2ApiVersion };
|
|
109
|
+
|
|
110
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","names":[],"sources":["../../src/raw/url.ts"],"sourcesContent":["/**\n * B2-style API version segment, such as `v1`, `v3`, or `v4`.\n *\n * The template-literal type documents the compile-time shape; use\n * {@link isB2ApiVersion} when accepting untyped runtime input.\n */\nexport type B2ApiVersion = `v${number}`\n\nconst SLASH = '/'\nconst BACKSLASH = '\\\\'\nconst VERSION_PREFIX = 'v'\nconst VERSION_DIGITS = '0123456789'\nconst URL_DELIMITERS = ['?', '#'] as const\n\n/** URL path options for a B2 API endpoint. */\nexport interface B2UrlOptions {\n /** Base path prefix before the optional version segment. Defaults to `b2api`. */\n readonly prefix?: string\n /**\n * Optional API version segment. SDK storage endpoints use `v3` and `v4`;\n * Computer Backup endpoints use the same `vN` segment shape under a\n * different prefix, such as `api/backup/v1`.\n */\n readonly version?: B2ApiVersion\n /** Endpoint name appended after the prefix and optional version. */\n readonly endpoint: string\n}\n\n/** URL path options for a B2 API surface before the endpoint is selected. */\nexport type B2EndpointUrlOptions = Omit<B2UrlOptions, 'endpoint'>\n\n/**\n * Returns true when `value` is a B2 API version segment of the form `vN`.\n *\n * @param value - Candidate API version segment.\n *\n * @returns Whether the value is a valid B2 API version segment.\n */\nexport function isB2ApiVersion(value: string): value is B2ApiVersion {\n if (!value.startsWith(VERSION_PREFIX) || value.length === VERSION_PREFIX.length) return false\n\n for (let index = VERSION_PREFIX.length; index < value.length; index += 1) {\n const digit = value[index]\n if (digit === undefined || !VERSION_DIGITS.includes(digit)) return false\n }\n\n return true\n}\n\nfunction assertB2ApiVersion(value: string): asserts value is B2ApiVersion {\n if (!isB2ApiVersion(value)) {\n throw new TypeError('Invalid version: expected a B2 API version segment like \"v3\"')\n }\n}\n\nfunction trimSlashes(\n value: string,\n { leading, trailing }: { readonly leading: boolean; readonly trailing: boolean },\n): string {\n let start = 0\n let end = value.length\n\n while (leading && start < end && value[start] === SLASH) {\n start += 1\n }\n while (trailing && end > start && value[end - 1] === SLASH) {\n end -= 1\n }\n\n return start === 0 && end === value.length ? value : value.slice(start, end)\n}\n\nfunction hasUrlDelimiter(value: string): boolean {\n return URL_DELIMITERS.some((delimiter) => value.includes(delimiter))\n}\n\nfunction hasEncodedPathDelimiter(value: string): boolean {\n const lowerValue = value.toLowerCase()\n return (\n lowerValue.includes('%2f') ||\n lowerValue.includes('%3f') ||\n lowerValue.includes('%23') ||\n lowerValue.includes('%5c')\n )\n}\n\nfunction withDotEscapesDecoded(value: string): string {\n let decoded = ''\n let index = 0\n\n while (index < value.length) {\n if (value.slice(index, index + 3).toLowerCase() === '%2e') {\n decoded += '.'\n index += 3\n } else {\n decoded += value[index]\n index += 1\n }\n }\n\n return decoded\n}\n\nfunction isTraversalComponent(value: string): boolean {\n const decoded = withDotEscapesDecoded(value)\n return decoded === '.' || decoded === '..'\n}\n\nfunction validatePathComponent(component: string, optionName: string): void {\n if (component.length === 0) {\n throw new TypeError(`Invalid ${optionName}: path components must not be empty`)\n }\n if (hasUrlDelimiter(component)) {\n throw new TypeError(`Invalid ${optionName}: path components must not contain \"?\" or \"#\"`)\n }\n if (component.includes(BACKSLASH)) {\n throw new TypeError(`Invalid ${optionName}: path components must not contain \"\\\\\"`)\n }\n if (hasEncodedPathDelimiter(component)) {\n throw new TypeError(\n `Invalid ${optionName}: path components must not contain encoded \"/\", \"\\\\\", \"?\", or \"#\"`,\n )\n }\n if (isTraversalComponent(component)) {\n throw new TypeError(`Invalid ${optionName}: path components must not be \".\" or \"..\"`)\n }\n}\n\nfunction preparePathPrefix(prefix: string): string {\n const trimmed = trimSlashes(prefix, { leading: true, trailing: true })\n if (trimmed.length === 0) return ''\n\n for (const component of trimmed.split(SLASH)) {\n validatePathComponent(component, 'prefix')\n }\n\n return trimmed\n}\n\nfunction preparePathSegment(segment: string, optionName: string): string {\n const trimmed = trimSlashes(segment, { leading: true, trailing: true })\n if (trimmed.length === 0) return ''\n if (trimmed.includes(SLASH)) {\n throw new TypeError(`Invalid ${optionName}: expected a single path segment`)\n }\n\n validatePathComponent(trimmed, optionName)\n return trimmed\n}\n\nfunction prepareVersionSegment(version: B2ApiVersion): B2ApiVersion {\n const trimmed = preparePathSegment(version, 'version')\n assertB2ApiVersion(trimmed)\n return trimmed\n}\n\n/**\n * Builds a B2 API endpoint URL from a base URL, path prefix, optional version,\n * and endpoint name.\n *\n * @param baseUrl - API or download base URL.\n * @param options - Path prefix, optional version, and endpoint name.\n *\n * @returns The absolute endpoint URL.\n */\nexport function b2Url(\n baseUrl: string,\n { prefix = 'b2api', version, endpoint }: B2UrlOptions,\n): string {\n const base = trimSlashes(baseUrl, { leading: false, trailing: true })\n const path = [\n preparePathPrefix(prefix),\n version === undefined ? undefined : prepareVersionSegment(version),\n preparePathSegment(endpoint, 'endpoint'),\n ]\n .filter((segment): segment is string => segment !== undefined)\n .filter((segment) => segment.length > 0)\n .join(SLASH)\n\n return path.length === 0 ? base : `${base}/${path}`\n}\n"],"mappings":";AAQA,IAAM,QAAQ;AACd,IAAM,YAAY;AAClB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB,CAAC,KAAK,GAAG;;;;;;;;AA0BhC,SAAgB,eAAe,OAAsC;CACnE,IAAI,CAAC,MAAM,WAAW,cAAc,KAAK,MAAM,WAAW,GAAuB,OAAO;CAExF,KAAK,IAAI,QAAQ,GAAuB,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACxE,MAAM,QAAQ,MAAM;EACpB,IAAI,UAAU,KAAA,KAAa,CAAC,eAAe,SAAS,KAAK,GAAG,OAAO;CACrE;CAEA,OAAO;AACT;AAEA,SAAS,mBAAmB,OAA8C;CACxE,IAAI,CAAC,eAAe,KAAK,GACvB,MAAM,IAAI,UAAU,gEAA8D;AAEtF;AAEA,SAAS,YACP,OACA,EAAE,SAAS,YACH;CACR,IAAI,QAAQ;CACZ,IAAI,MAAM,MAAM;CAEhB,OAAO,WAAW,QAAQ,OAAO,MAAM,WAAW,OAChD,SAAS;CAEX,OAAO,YAAY,MAAM,SAAS,MAAM,MAAM,OAAO,OACnD,OAAO;CAGT,OAAO,UAAU,KAAK,QAAQ,MAAM,SAAS,QAAQ,MAAM,MAAM,OAAO,GAAG;AAC7E;AAEA,SAAS,gBAAgB,OAAwB;CAC/C,OAAO,eAAe,MAAM,cAAc,MAAM,SAAS,SAAS,CAAC;AACrE;AAEA,SAAS,wBAAwB,OAAwB;CACvD,MAAM,aAAa,MAAM,YAAY;CACrC,OACE,WAAW,SAAS,KAAK,KACzB,WAAW,SAAS,KAAK,KACzB,WAAW,SAAS,KAAK,KACzB,WAAW,SAAS,KAAK;AAE7B;AAEA,SAAS,sBAAsB,OAAuB;CACpD,IAAI,UAAU;CACd,IAAI,QAAQ;CAEZ,OAAO,QAAQ,MAAM,QACnB,IAAI,MAAM,MAAM,OAAO,QAAQ,CAAC,CAAC,CAAC,YAAY,MAAM,OAAO;EACzD,WAAW;EACX,SAAS;CACX,OAAO;EACL,WAAW,MAAM;EACjB,SAAS;CACX;CAGF,OAAO;AACT;AAEA,SAAS,qBAAqB,OAAwB;CACpD,MAAM,UAAU,sBAAsB,KAAK;CAC3C,OAAO,YAAY,OAAO,YAAY;AACxC;AAEA,SAAS,sBAAsB,WAAmB,YAA0B;CAC1E,IAAI,UAAU,WAAW,GACvB,MAAM,IAAI,UAAU,WAAW,WAAW,oCAAoC;CAEhF,IAAI,gBAAgB,SAAS,GAC3B,MAAM,IAAI,UAAU,WAAW,WAAW,8CAA8C;CAE1F,IAAI,UAAU,SAAS,SAAS,GAC9B,MAAM,IAAI,UAAU,WAAW,WAAW,wCAAwC;CAEpF,IAAI,wBAAwB,SAAS,GACnC,MAAM,IAAI,UACR,WAAW,WAAW,kEACxB;CAEF,IAAI,qBAAqB,SAAS,GAChC,MAAM,IAAI,UAAU,WAAW,WAAW,0CAA0C;AAExF;AAEA,SAAS,kBAAkB,QAAwB;CACjD,MAAM,UAAU,YAAY,QAAQ;EAAE,SAAS;EAAM,UAAU;CAAK,CAAC;CACrE,IAAI,QAAQ,WAAW,GAAG,OAAO;CAEjC,KAAK,MAAM,aAAa,QAAQ,MAAM,KAAK,GACzC,sBAAsB,WAAW,QAAQ;CAG3C,OAAO;AACT;AAEA,SAAS,mBAAmB,SAAiB,YAA4B;CACvE,MAAM,UAAU,YAAY,SAAS;EAAE,SAAS;EAAM,UAAU;CAAK,CAAC;CACtE,IAAI,QAAQ,WAAW,GAAG,OAAO;CACjC,IAAI,QAAQ,SAAS,KAAK,GACxB,MAAM,IAAI,UAAU,WAAW,WAAW,iCAAiC;CAG7E,sBAAsB,SAAS,UAAU;CACzC,OAAO;AACT;AAEA,SAAS,sBAAsB,SAAqC;CAClE,MAAM,UAAU,mBAAmB,SAAS,SAAS;CACrD,mBAAmB,OAAO;CAC1B,OAAO;AACT;;;;;;;;;;AAWA,SAAgB,MACd,SACA,EAAE,SAAS,SAAS,SAAS,YACrB;CACR,MAAM,OAAO,YAAY,SAAS;EAAE,SAAS;EAAO,UAAU;CAAK,CAAC;CACpE,MAAM,OAAO;EACX,kBAAkB,MAAM;EACxB,YAAY,KAAA,IAAY,KAAA,IAAY,sBAAsB,OAAO;EACjE,mBAAmB,UAAU,UAAU;CACzC,CAAC,CACE,QAAQ,YAA+B,YAAY,KAAA,CAAS,CAAC,CAC7D,QAAQ,YAAY,QAAQ,SAAS,CAAC,CAAC,CACvC,KAAK,KAAK;CAEb,OAAO,KAAK,WAAW,IAAI,OAAO,GAAG,KAAK,GAAG;AAC/C"}
|
package/dist/s3/sigv4.cjs
CHANGED
|
@@ -44,20 +44,22 @@ async function presignS3Request(method, options, extraQuery, extraHeaders) {
|
|
|
44
44
|
["X-Amz-SignedHeaders", signedHeaders],
|
|
45
45
|
...extraQuery
|
|
46
46
|
];
|
|
47
|
+
const canonicalRequest = [
|
|
48
|
+
method,
|
|
49
|
+
canonicalUri,
|
|
50
|
+
canonicalQueryString(query),
|
|
51
|
+
headers.map(([name, value]) => `${name}:${value}\n`).join(""),
|
|
52
|
+
signedHeaders,
|
|
53
|
+
UNSIGNED_PAYLOAD
|
|
54
|
+
].join("\n");
|
|
47
55
|
const stringToSign = [
|
|
48
56
|
"AWS4-HMAC-SHA256",
|
|
49
57
|
longDate,
|
|
50
58
|
credentialScope,
|
|
51
|
-
await require_crypto.sha256Hex(
|
|
52
|
-
method,
|
|
53
|
-
canonicalUri,
|
|
54
|
-
canonicalQueryString(query),
|
|
55
|
-
headers.map(([name, value]) => `${name}:${value}\n`).join(""),
|
|
56
|
-
signedHeaders,
|
|
57
|
-
UNSIGNED_PAYLOAD
|
|
58
|
-
].join("\n"))
|
|
59
|
+
await require_crypto.sha256Hex(canonicalRequest)
|
|
59
60
|
].join("\n");
|
|
60
|
-
const
|
|
61
|
+
const signingKey = await deriveSigningKey(options.secretAccessKey, shortDate, options.region);
|
|
62
|
+
const signature = require_crypto.hexEncode(await require_crypto.hmacSha256(signingKey, stringToSign));
|
|
61
63
|
const finalQuery = canonicalQueryString([...query, ["X-Amz-Signature", signature]]);
|
|
62
64
|
return `${endpoint.origin}${canonicalUri}?${finalQuery}`;
|
|
63
65
|
}
|
|
@@ -140,7 +142,10 @@ function normalizeHeaderValue(value) {
|
|
|
140
142
|
return value.trim().replace(/ +/g, " ");
|
|
141
143
|
}
|
|
142
144
|
async function deriveSigningKey(secretAccessKey, shortDate, region) {
|
|
143
|
-
|
|
145
|
+
const dateKey = await require_crypto.hmacSha256(`AWS4${secretAccessKey}`, shortDate);
|
|
146
|
+
const dateRegionKey = await require_crypto.hmacSha256(dateKey, region);
|
|
147
|
+
const dateRegionServiceKey = await require_crypto.hmacSha256(dateRegionKey, SERVICE);
|
|
148
|
+
return await require_crypto.hmacSha256(dateRegionServiceKey, TERMINATOR);
|
|
144
149
|
}
|
|
145
150
|
//#endregion
|
|
146
151
|
exports.DEFAULT_PRESIGN_EXPIRES_IN = DEFAULT_PRESIGN_EXPIRES_IN;
|
package/dist/s3/sigv4.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sigv4.cjs","names":[],"sources":["../../src/s3/sigv4.ts"],"sourcesContent":["import { redactUrlForError } from '../internal/url-redaction.ts'\nimport { hexEncode, hmacSha256, sha256Hex } from '../util/crypto.ts'\nimport { hasHttpHeaderControlCharacter } from '../util/http.ts'\nimport { assertSafeBucketName, assertValidB2FileName } from './validation.ts'\n\nconst MAX_PRESIGN_EXPIRES_IN = 604_800\nconst UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'\nconst SERVICE = 's3'\nconst TERMINATOR = 'aws4_request'\n/** Default S3 presigned URL validity duration in seconds. */\nexport const DEFAULT_PRESIGN_EXPIRES_IN = 3600\n\n/** Date input accepted by SigV4 presign internals and deterministic tests. */\nexport type S3PresignDate = Date | number\n\n/** Query parameter included in the SigV4 canonical query string. */\nexport type QueryParam = readonly [name: string, value: string]\n\n/** HTTP header included in the SigV4 signed headers list. */\nexport type SignedHeader = readonly [name: string, value: string]\n\n/** Internal inputs required to presign one S3-compatible object request. */\nexport interface SigV4PresignRequestOptions {\n /** S3-compatible endpoint URL. */\n readonly endpoint: string\n /** S3 signing region. */\n readonly region: string\n /** S3 access key ID. */\n readonly accessKeyId: string\n /** S3 secret access key. */\n readonly secretAccessKey: string\n /** Bucket containing the object. */\n readonly bucketName: string\n /** Object key / B2 file name. */\n readonly fileName: string\n /** Optional URL validity duration in seconds. */\n readonly expiresIn?: number\n /** Optional signing clock override. */\n readonly signingDate?: S3PresignDate\n}\n\n/**\n * Generates an AWS Signature Version 4 presigned S3-compatible request URL.\n *\n * This module owns only canonical SigV4 mechanics: canonical URI/query/header\n * construction, signing-key derivation, and signature encoding. Public B2 S3\n * option validation and naming stay in `index.ts`.\n *\n * @param method - HTTP method to presign.\n * @param options - S3 endpoint, credentials, object key, and signing options.\n * @param extraQuery - Operation-specific query parameters to sign.\n * @param extraHeaders - Operation-specific request headers to sign.\n *\n * @returns The presigned request URL.\n */\nexport async function presignS3Request(\n method: 'GET' | 'PUT',\n options: SigV4PresignRequestOptions,\n extraQuery: readonly QueryParam[],\n extraHeaders: readonly SignedHeader[],\n): Promise<string> {\n const endpoint = parseEndpoint(options.endpoint)\n assertHttpsEndpoint(endpoint)\n const expiresIn = normalizeExpiresIn(options.expiresIn)\n const { shortDate, longDate } = formatSigningDate(options.signingDate)\n const credentialScope = `${shortDate}/${options.region}/${SERVICE}/${TERMINATOR}`\n const credential = `${options.accessKeyId}/${credentialScope}`\n assertSafeBucketName(options.bucketName)\n assertValidB2FileName(options.fileName)\n const canonicalUri = buildCanonicalUri(endpoint.pathname, options.bucketName, options.fileName)\n const headers = normalizeSignedHeaders([['host', canonicalHostHeader(endpoint)], ...extraHeaders])\n const signedHeaders = headers.map(([name]) => name).join(';')\n\n const query: QueryParam[] = [\n ['X-Amz-Algorithm', 'AWS4-HMAC-SHA256'],\n ['X-Amz-Credential', credential],\n ['X-Amz-Content-Sha256', UNSIGNED_PAYLOAD],\n ['X-Amz-Date', longDate],\n ['X-Amz-Expires', String(expiresIn)],\n ['X-Amz-SignedHeaders', signedHeaders],\n ...extraQuery,\n ]\n const canonicalQuery = canonicalQueryString(query)\n const canonicalHeaders = headers.map(([name, value]) => `${name}:${value}\\n`).join('')\n const canonicalRequest = [\n method,\n canonicalUri,\n canonicalQuery,\n canonicalHeaders,\n signedHeaders,\n UNSIGNED_PAYLOAD,\n ].join('\\n')\n const stringToSign = [\n 'AWS4-HMAC-SHA256',\n longDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join('\\n')\n const signingKey = await deriveSigningKey(options.secretAccessKey, shortDate, options.region)\n const signature = hexEncode(await hmacSha256(signingKey, stringToSign))\n const finalQuery = canonicalQueryString([...query, ['X-Amz-Signature', signature]])\n\n return `${endpoint.origin}${canonicalUri}?${finalQuery}`\n}\n\nfunction normalizeExpiresIn(expiresIn: number | undefined): number {\n const value = expiresIn ?? DEFAULT_PRESIGN_EXPIRES_IN\n if (!Number.isInteger(value) || value < 1 || value > MAX_PRESIGN_EXPIRES_IN) {\n throw new RangeError(\n `expiresIn must be an integer from 1 to ${MAX_PRESIGN_EXPIRES_IN} seconds; received ${String(value)}.`,\n )\n }\n return value\n}\n\nfunction formatSigningDate(input: S3PresignDate | undefined): {\n readonly shortDate: string\n readonly longDate: string\n} {\n const date = input === undefined ? new Date() : new Date(input)\n if (!Number.isFinite(date.getTime())) {\n throw new RangeError('signingDate must be a valid Date or timestamp.')\n }\n\n const year = String(date.getUTCFullYear()).padStart(4, '0')\n const month = String(date.getUTCMonth() + 1).padStart(2, '0')\n const day = String(date.getUTCDate()).padStart(2, '0')\n const hour = String(date.getUTCHours()).padStart(2, '0')\n const minute = String(date.getUTCMinutes()).padStart(2, '0')\n const second = String(date.getUTCSeconds()).padStart(2, '0')\n const shortDate = `${year}${month}${day}`\n return {\n shortDate,\n longDate: `${shortDate}T${hour}${minute}${second}Z`,\n }\n}\n\nfunction buildCanonicalUri(endpointPath: string, bucketName: string, fileName: string): string {\n const basePath =\n endpointPath === '' || endpointPath === '/' ? '' : endpointPath.replace(/\\/+$/, '')\n return `${basePath}/${awsPercentEncode(bucketName)}/${encodePath(fileName)}`\n}\n\nfunction canonicalHostHeader(endpoint: URL): string {\n const host = endpoint.host\n if (endpoint.protocol === 'https:' && host.endsWith(':443')) {\n return host.slice(0, -4)\n }\n return host\n}\n\nfunction parseEndpoint(endpoint: string): URL {\n try {\n return new URL(endpoint)\n } catch (cause) {\n throw new TypeError(\n `S3 presigned URL endpoint must be a valid URL; received \"${redactUrlForError(endpoint, {\n invalidUrlLabel: '<invalid S3 endpoint URL>',\n })}\".`,\n { cause },\n )\n }\n}\n\nfunction assertHttpsEndpoint(endpoint: URL): void {\n if (endpoint.protocol !== 'https:') {\n throw new TypeError(\n `S3 presigned URLs require an https: endpoint; received \"${redactUrlForError(endpoint)}\".`,\n )\n }\n}\n\nfunction encodePath(path: string): string {\n return path.split('/').map(awsPercentEncode).join('/')\n}\n\nfunction canonicalQueryString(query: readonly QueryParam[]): string {\n return query\n .map(([name, value]) => [awsPercentEncode(name), awsPercentEncode(value)] as const)\n .sort(([aName, aValue], [bName, bValue]) => {\n if (aName < bName) return -1\n if (aName > bName) return 1\n if (aValue < bValue) return -1\n if (aValue > bValue) return 1\n return 0\n })\n .map(([name, value]) => `${name}=${value}`)\n .join('&')\n}\n\n/**\n * Percent-encode according to AWS SigV4 canonical request rules.\n *\n * @param value - Raw value to encode.\n *\n * @returns AWS-compatible percent-encoded value.\n */\nfunction awsPercentEncode(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n )\n}\n\nfunction normalizeSignedHeaders(headers: readonly SignedHeader[]): SignedHeader[] {\n const combinedHeaders = new Map<string, string[]>()\n for (const [name, value] of headers) {\n const normalizedName = name.toLowerCase()\n const values = combinedHeaders.get(normalizedName)\n if (values) {\n values.push(normalizeHeaderValue(value))\n } else {\n combinedHeaders.set(normalizedName, [normalizeHeaderValue(value)])\n }\n }\n\n return [...combinedHeaders.entries()]\n .map(([name, values]) => [name, values.join(',')] as const)\n .sort(([a], [b]) => {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n })\n}\n\nfunction normalizeHeaderValue(value: string): string {\n if (hasHttpHeaderControlCharacter(value)) {\n throw new TypeError('signed header values must not contain control characters.')\n }\n\n return value.trim().replace(/ +/g, ' ')\n}\n\nasync function deriveSigningKey(\n secretAccessKey: string,\n shortDate: string,\n region: string,\n): Promise<Uint8Array> {\n const dateKey = await hmacSha256(`AWS4${secretAccessKey}`, shortDate)\n const dateRegionKey = await hmacSha256(dateKey, region)\n const dateRegionServiceKey = await hmacSha256(dateRegionKey, SERVICE)\n return await hmacSha256(dateRegionServiceKey, TERMINATOR)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sigv4.cjs","names":[],"sources":["../../src/s3/sigv4.ts"],"sourcesContent":["import { redactUrlForError } from '../internal/url-redaction.ts'\nimport { hexEncode, hmacSha256, sha256Hex } from '../util/crypto.ts'\nimport { hasHttpHeaderControlCharacter } from '../util/http.ts'\nimport { assertSafeBucketName, assertValidB2FileName } from './validation.ts'\n\nconst MAX_PRESIGN_EXPIRES_IN = 604_800\nconst UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'\nconst SERVICE = 's3'\nconst TERMINATOR = 'aws4_request'\n/** Default S3 presigned URL validity duration in seconds. */\nexport const DEFAULT_PRESIGN_EXPIRES_IN = 3600\n\n/** Date input accepted by SigV4 presign internals and deterministic tests. */\nexport type S3PresignDate = Date | number\n\n/** Query parameter included in the SigV4 canonical query string. */\nexport type QueryParam = readonly [name: string, value: string]\n\n/** HTTP header included in the SigV4 signed headers list. */\nexport type SignedHeader = readonly [name: string, value: string]\n\n/** Internal inputs required to presign one S3-compatible object request. */\nexport interface SigV4PresignRequestOptions {\n /** S3-compatible endpoint URL. */\n readonly endpoint: string\n /** S3 signing region. */\n readonly region: string\n /** S3 access key ID. */\n readonly accessKeyId: string\n /** S3 secret access key. */\n readonly secretAccessKey: string\n /** Bucket containing the object. */\n readonly bucketName: string\n /** Object key / B2 file name. */\n readonly fileName: string\n /** Optional URL validity duration in seconds. */\n readonly expiresIn?: number\n /** Optional signing clock override. */\n readonly signingDate?: S3PresignDate\n}\n\n/**\n * Generates an AWS Signature Version 4 presigned S3-compatible request URL.\n *\n * This module owns only canonical SigV4 mechanics: canonical URI/query/header\n * construction, signing-key derivation, and signature encoding. Public B2 S3\n * option validation and naming stay in `index.ts`.\n *\n * @param method - HTTP method to presign.\n * @param options - S3 endpoint, credentials, object key, and signing options.\n * @param extraQuery - Operation-specific query parameters to sign.\n * @param extraHeaders - Operation-specific request headers to sign.\n *\n * @returns The presigned request URL.\n */\nexport async function presignS3Request(\n method: 'GET' | 'PUT',\n options: SigV4PresignRequestOptions,\n extraQuery: readonly QueryParam[],\n extraHeaders: readonly SignedHeader[],\n): Promise<string> {\n const endpoint = parseEndpoint(options.endpoint)\n assertHttpsEndpoint(endpoint)\n const expiresIn = normalizeExpiresIn(options.expiresIn)\n const { shortDate, longDate } = formatSigningDate(options.signingDate)\n const credentialScope = `${shortDate}/${options.region}/${SERVICE}/${TERMINATOR}`\n const credential = `${options.accessKeyId}/${credentialScope}`\n assertSafeBucketName(options.bucketName)\n assertValidB2FileName(options.fileName)\n const canonicalUri = buildCanonicalUri(endpoint.pathname, options.bucketName, options.fileName)\n const headers = normalizeSignedHeaders([['host', canonicalHostHeader(endpoint)], ...extraHeaders])\n const signedHeaders = headers.map(([name]) => name).join(';')\n\n const query: QueryParam[] = [\n ['X-Amz-Algorithm', 'AWS4-HMAC-SHA256'],\n ['X-Amz-Credential', credential],\n ['X-Amz-Content-Sha256', UNSIGNED_PAYLOAD],\n ['X-Amz-Date', longDate],\n ['X-Amz-Expires', String(expiresIn)],\n ['X-Amz-SignedHeaders', signedHeaders],\n ...extraQuery,\n ]\n const canonicalQuery = canonicalQueryString(query)\n const canonicalHeaders = headers.map(([name, value]) => `${name}:${value}\\n`).join('')\n const canonicalRequest = [\n method,\n canonicalUri,\n canonicalQuery,\n canonicalHeaders,\n signedHeaders,\n UNSIGNED_PAYLOAD,\n ].join('\\n')\n const stringToSign = [\n 'AWS4-HMAC-SHA256',\n longDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join('\\n')\n const signingKey = await deriveSigningKey(options.secretAccessKey, shortDate, options.region)\n const signature = hexEncode(await hmacSha256(signingKey, stringToSign))\n const finalQuery = canonicalQueryString([...query, ['X-Amz-Signature', signature]])\n\n return `${endpoint.origin}${canonicalUri}?${finalQuery}`\n}\n\nfunction normalizeExpiresIn(expiresIn: number | undefined): number {\n const value = expiresIn ?? DEFAULT_PRESIGN_EXPIRES_IN\n if (!Number.isInteger(value) || value < 1 || value > MAX_PRESIGN_EXPIRES_IN) {\n throw new RangeError(\n `expiresIn must be an integer from 1 to ${MAX_PRESIGN_EXPIRES_IN} seconds; received ${String(value)}.`,\n )\n }\n return value\n}\n\nfunction formatSigningDate(input: S3PresignDate | undefined): {\n readonly shortDate: string\n readonly longDate: string\n} {\n const date = input === undefined ? new Date() : new Date(input)\n if (!Number.isFinite(date.getTime())) {\n throw new RangeError('signingDate must be a valid Date or timestamp.')\n }\n\n const year = String(date.getUTCFullYear()).padStart(4, '0')\n const month = String(date.getUTCMonth() + 1).padStart(2, '0')\n const day = String(date.getUTCDate()).padStart(2, '0')\n const hour = String(date.getUTCHours()).padStart(2, '0')\n const minute = String(date.getUTCMinutes()).padStart(2, '0')\n const second = String(date.getUTCSeconds()).padStart(2, '0')\n const shortDate = `${year}${month}${day}`\n return {\n shortDate,\n longDate: `${shortDate}T${hour}${minute}${second}Z`,\n }\n}\n\nfunction buildCanonicalUri(endpointPath: string, bucketName: string, fileName: string): string {\n const basePath =\n endpointPath === '' || endpointPath === '/' ? '' : endpointPath.replace(/\\/+$/, '')\n return `${basePath}/${awsPercentEncode(bucketName)}/${encodePath(fileName)}`\n}\n\nfunction canonicalHostHeader(endpoint: URL): string {\n const host = endpoint.host\n if (endpoint.protocol === 'https:' && host.endsWith(':443')) {\n return host.slice(0, -4)\n }\n return host\n}\n\nfunction parseEndpoint(endpoint: string): URL {\n try {\n return new URL(endpoint)\n } catch (cause) {\n throw new TypeError(\n `S3 presigned URL endpoint must be a valid URL; received \"${redactUrlForError(endpoint, {\n invalidUrlLabel: '<invalid S3 endpoint URL>',\n })}\".`,\n { cause },\n )\n }\n}\n\nfunction assertHttpsEndpoint(endpoint: URL): void {\n if (endpoint.protocol !== 'https:') {\n throw new TypeError(\n `S3 presigned URLs require an https: endpoint; received \"${redactUrlForError(endpoint)}\".`,\n )\n }\n}\n\nfunction encodePath(path: string): string {\n return path.split('/').map(awsPercentEncode).join('/')\n}\n\nfunction canonicalQueryString(query: readonly QueryParam[]): string {\n return query\n .map(([name, value]) => [awsPercentEncode(name), awsPercentEncode(value)] as const)\n .sort(([aName, aValue], [bName, bValue]) => {\n if (aName < bName) return -1\n if (aName > bName) return 1\n if (aValue < bValue) return -1\n if (aValue > bValue) return 1\n return 0\n })\n .map(([name, value]) => `${name}=${value}`)\n .join('&')\n}\n\n/**\n * Percent-encode according to AWS SigV4 canonical request rules.\n *\n * @param value - Raw value to encode.\n *\n * @returns AWS-compatible percent-encoded value.\n */\nfunction awsPercentEncode(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n )\n}\n\nfunction normalizeSignedHeaders(headers: readonly SignedHeader[]): SignedHeader[] {\n const combinedHeaders = new Map<string, string[]>()\n for (const [name, value] of headers) {\n const normalizedName = name.toLowerCase()\n const values = combinedHeaders.get(normalizedName)\n if (values) {\n values.push(normalizeHeaderValue(value))\n } else {\n combinedHeaders.set(normalizedName, [normalizeHeaderValue(value)])\n }\n }\n\n return [...combinedHeaders.entries()]\n .map(([name, values]) => [name, values.join(',')] as const)\n .sort(([a], [b]) => {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n })\n}\n\nfunction normalizeHeaderValue(value: string): string {\n if (hasHttpHeaderControlCharacter(value)) {\n throw new TypeError('signed header values must not contain control characters.')\n }\n\n return value.trim().replace(/ +/g, ' ')\n}\n\nasync function deriveSigningKey(\n secretAccessKey: string,\n shortDate: string,\n region: string,\n): Promise<Uint8Array> {\n const dateKey = await hmacSha256(`AWS4${secretAccessKey}`, shortDate)\n const dateRegionKey = await hmacSha256(dateKey, region)\n const dateRegionServiceKey = await hmacSha256(dateRegionKey, SERVICE)\n return await hmacSha256(dateRegionServiceKey, TERMINATOR)\n}\n"],"mappings":";;;;;AAKA,IAAM,yBAAyB;AAC/B,IAAM,mBAAmB;AACzB,IAAM,UAAU;AAChB,IAAM,aAAa;;AAEnB,IAAa,6BAA6B;;;;;;;;;;;;;;;AA6C1C,eAAsB,iBACpB,QACA,SACA,YACA,cACiB;CACjB,MAAM,WAAW,cAAc,QAAQ,QAAQ;CAC/C,oBAAoB,QAAQ;CAC5B,MAAM,YAAY,mBAAmB,QAAQ,SAAS;CACtD,MAAM,EAAE,WAAW,aAAa,kBAAkB,QAAQ,WAAW;CACrE,MAAM,kBAAkB,GAAG,UAAU,GAAG,QAAQ,OAAO,GAAG,QAAQ,GAAG;CACrE,MAAM,aAAa,GAAG,QAAQ,YAAY,GAAG;CAC7C,mBAAA,qBAAqB,QAAQ,UAAU;CACvC,mBAAA,sBAAsB,QAAQ,QAAQ;CACtC,MAAM,eAAe,kBAAkB,SAAS,UAAU,QAAQ,YAAY,QAAQ,QAAQ;CAC9F,MAAM,UAAU,uBAAuB,CAAC,CAAC,QAAQ,oBAAoB,QAAQ,CAAC,GAAG,GAAG,YAAY,CAAC;CACjG,MAAM,gBAAgB,QAAQ,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC,KAAK,GAAG;CAE5D,MAAM,QAAsB;EAC1B,CAAC,mBAAmB,kBAAkB;EACtC,CAAC,oBAAoB,UAAU;EAC/B,CAAC,wBAAwB,gBAAgB;EACzC,CAAC,cAAc,QAAQ;EACvB,CAAC,iBAAiB,OAAO,SAAS,CAAC;EACnC,CAAC,uBAAuB,aAAa;EACrC,GAAG;CACL;CAGA,MAAM,mBAAmB;EACvB;EACA;EAJqB,qBAAqB,KAK1C;EAJuB,QAAQ,KAAK,CAAC,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,KAAK,EAKjF;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI;CACX,MAAM,eAAe;EACnB;EACA;EACA;EACA,MAAM,eAAA,UAAU,gBAAgB;CAClC,CAAC,CAAC,KAAK,IAAI;CACX,MAAM,aAAa,MAAM,iBAAiB,QAAQ,iBAAiB,WAAW,QAAQ,MAAM;CAC5F,MAAM,YAAY,eAAA,UAAU,MAAM,eAAA,WAAW,YAAY,YAAY,CAAC;CACtE,MAAM,aAAa,qBAAqB,CAAC,GAAG,OAAO,CAAC,mBAAmB,SAAS,CAAC,CAAC;CAElF,OAAO,GAAG,SAAS,SAAS,aAAa,GAAG;AAC9C;AAEA,SAAS,mBAAmB,WAAuC;CACjE,MAAM,QAAQ,aAAA;CACd,IAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,QAAQ,wBACnD,MAAM,IAAI,WACR,0CAA0C,uBAAuB,qBAAqB,OAAO,KAAK,EAAE,EACtG;CAEF,OAAO;AACT;AAEA,SAAS,kBAAkB,OAGzB;CACA,MAAM,OAAO,UAAU,KAAA,oBAAY,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK;CAC9D,IAAI,CAAC,OAAO,SAAS,KAAK,QAAQ,CAAC,GACjC,MAAM,IAAI,WAAW,gDAAgD;CAGvE,MAAM,OAAO,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CAC1D,MAAM,QAAQ,OAAO,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CAC5D,MAAM,MAAM,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CACrD,MAAM,OAAO,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CACvD,MAAM,SAAS,OAAO,KAAK,cAAc,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CAC3D,MAAM,SAAS,OAAO,KAAK,cAAc,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CAC3D,MAAM,YAAY,GAAG,OAAO,QAAQ;CACpC,OAAO;EACL;EACA,UAAU,GAAG,UAAU,GAAG,OAAO,SAAS,OAAO;CACnD;AACF;AAEA,SAAS,kBAAkB,cAAsB,YAAoB,UAA0B;CAG7F,OAAO,GADL,iBAAiB,MAAM,iBAAiB,MAAM,KAAK,aAAa,QAAQ,QAAQ,EAAE,EACjE,GAAG,iBAAiB,UAAU,EAAE,GAAG,WAAW,QAAQ;AAC3E;AAEA,SAAS,oBAAoB,UAAuB;CAClD,MAAM,OAAO,SAAS;CACtB,IAAI,SAAS,aAAa,YAAY,KAAK,SAAS,MAAM,GACxD,OAAO,KAAK,MAAM,GAAG,EAAE;CAEzB,OAAO;AACT;AAEA,SAAS,cAAc,UAAuB;CAC5C,IAAI;EACF,OAAO,IAAI,IAAI,QAAQ;CACzB,SAAS,OAAO;EACd,MAAM,IAAI,UACR,4DAA4D,sBAAA,kBAAkB,UAAU,EACtF,iBAAiB,4BACnB,CAAC,EAAE,KACH,EAAE,MAAM,CACV;CACF;AACF;AAEA,SAAS,oBAAoB,UAAqB;CAChD,IAAI,SAAS,aAAa,UACxB,MAAM,IAAI,UACR,2DAA2D,sBAAA,kBAAkB,QAAQ,EAAE,GACzF;AAEJ;AAEA,SAAS,WAAW,MAAsB;CACxC,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,gBAAgB,CAAC,CAAC,KAAK,GAAG;AACvD;AAEA,SAAS,qBAAqB,OAAsC;CAClE,OAAO,MACJ,KAAK,CAAC,MAAM,WAAW,CAAC,iBAAiB,IAAI,GAAG,iBAAiB,KAAK,CAAC,CAAU,CAAC,CAClF,MAAM,CAAC,OAAO,SAAS,CAAC,OAAO,YAAY;EAC1C,IAAI,QAAQ,OAAO,OAAO;EAC1B,IAAI,QAAQ,OAAO,OAAO;EAC1B,IAAI,SAAS,QAAQ,OAAO;EAC5B,IAAI,SAAS,QAAQ,OAAO;EAC5B,OAAO;CACT,CAAC,CAAC,CACD,KAAK,CAAC,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC,CAC1C,KAAK,GAAG;AACb;;;;;;;;AASA,SAAS,iBAAiB,OAAuB;CAC/C,OAAO,mBAAmB,KAAK,CAAC,CAAC,QAC/B,aACC,SAAS,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,YAAY,GAC5D;AACF;AAEA,SAAS,uBAAuB,SAAkD;CAChF,MAAM,kCAAkB,IAAI,IAAsB;CAClD,KAAK,MAAM,CAAC,MAAM,UAAU,SAAS;EACnC,MAAM,iBAAiB,KAAK,YAAY;EACxC,MAAM,SAAS,gBAAgB,IAAI,cAAc;EACjD,IAAI,QACF,OAAO,KAAK,qBAAqB,KAAK,CAAC;OAEvC,gBAAgB,IAAI,gBAAgB,CAAC,qBAAqB,KAAK,CAAC,CAAC;CAErE;CAEA,OAAO,CAAC,GAAG,gBAAgB,QAAQ,CAAC,CAAC,CAClC,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,OAAO,KAAK,GAAG,CAAC,CAAU,CAAC,CAC1D,MAAM,CAAC,IAAI,CAAC,OAAO;EAClB,IAAI,IAAI,GAAG,OAAO;EAClB,IAAI,IAAI,GAAG,OAAO;EAClB,OAAO;CACT,CAAC;AACL;AAEA,SAAS,qBAAqB,OAAuB;CACnD,IAAI,aAAA,8BAA8B,KAAK,GACrC,MAAM,IAAI,UAAU,2DAA2D;CAGjF,OAAO,MAAM,KAAK,CAAC,CAAC,QAAQ,OAAO,GAAG;AACxC;AAEA,eAAe,iBACb,iBACA,WACA,QACqB;CACrB,MAAM,UAAU,MAAM,eAAA,WAAW,OAAO,mBAAmB,SAAS;CACpE,MAAM,gBAAgB,MAAM,eAAA,WAAW,SAAS,MAAM;CACtD,MAAM,uBAAuB,MAAM,eAAA,WAAW,eAAe,OAAO;CACpE,OAAO,MAAM,eAAA,WAAW,sBAAsB,UAAU;AAC1D"}
|
package/dist/s3/sigv4.js
CHANGED
|
@@ -44,20 +44,22 @@ async function presignS3Request(method, options, extraQuery, extraHeaders) {
|
|
|
44
44
|
["X-Amz-SignedHeaders", signedHeaders],
|
|
45
45
|
...extraQuery
|
|
46
46
|
];
|
|
47
|
+
const canonicalRequest = [
|
|
48
|
+
method,
|
|
49
|
+
canonicalUri,
|
|
50
|
+
canonicalQueryString(query),
|
|
51
|
+
headers.map(([name, value]) => `${name}:${value}\n`).join(""),
|
|
52
|
+
signedHeaders,
|
|
53
|
+
UNSIGNED_PAYLOAD
|
|
54
|
+
].join("\n");
|
|
47
55
|
const stringToSign = [
|
|
48
56
|
"AWS4-HMAC-SHA256",
|
|
49
57
|
longDate,
|
|
50
58
|
credentialScope,
|
|
51
|
-
await sha256Hex(
|
|
52
|
-
method,
|
|
53
|
-
canonicalUri,
|
|
54
|
-
canonicalQueryString(query),
|
|
55
|
-
headers.map(([name, value]) => `${name}:${value}\n`).join(""),
|
|
56
|
-
signedHeaders,
|
|
57
|
-
UNSIGNED_PAYLOAD
|
|
58
|
-
].join("\n"))
|
|
59
|
+
await sha256Hex(canonicalRequest)
|
|
59
60
|
].join("\n");
|
|
60
|
-
const
|
|
61
|
+
const signingKey = await deriveSigningKey(options.secretAccessKey, shortDate, options.region);
|
|
62
|
+
const signature = hexEncode(await hmacSha256(signingKey, stringToSign));
|
|
61
63
|
const finalQuery = canonicalQueryString([...query, ["X-Amz-Signature", signature]]);
|
|
62
64
|
return `${endpoint.origin}${canonicalUri}?${finalQuery}`;
|
|
63
65
|
}
|
|
@@ -140,7 +142,10 @@ function normalizeHeaderValue(value) {
|
|
|
140
142
|
return value.trim().replace(/ +/g, " ");
|
|
141
143
|
}
|
|
142
144
|
async function deriveSigningKey(secretAccessKey, shortDate, region) {
|
|
143
|
-
|
|
145
|
+
const dateKey = await hmacSha256(`AWS4${secretAccessKey}`, shortDate);
|
|
146
|
+
const dateRegionKey = await hmacSha256(dateKey, region);
|
|
147
|
+
const dateRegionServiceKey = await hmacSha256(dateRegionKey, SERVICE);
|
|
148
|
+
return await hmacSha256(dateRegionServiceKey, TERMINATOR);
|
|
144
149
|
}
|
|
145
150
|
//#endregion
|
|
146
151
|
export { DEFAULT_PRESIGN_EXPIRES_IN, presignS3Request };
|
package/dist/s3/sigv4.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sigv4.js","names":[],"sources":["../../src/s3/sigv4.ts"],"sourcesContent":["import { redactUrlForError } from '../internal/url-redaction.ts'\nimport { hexEncode, hmacSha256, sha256Hex } from '../util/crypto.ts'\nimport { hasHttpHeaderControlCharacter } from '../util/http.ts'\nimport { assertSafeBucketName, assertValidB2FileName } from './validation.ts'\n\nconst MAX_PRESIGN_EXPIRES_IN = 604_800\nconst UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'\nconst SERVICE = 's3'\nconst TERMINATOR = 'aws4_request'\n/** Default S3 presigned URL validity duration in seconds. */\nexport const DEFAULT_PRESIGN_EXPIRES_IN = 3600\n\n/** Date input accepted by SigV4 presign internals and deterministic tests. */\nexport type S3PresignDate = Date | number\n\n/** Query parameter included in the SigV4 canonical query string. */\nexport type QueryParam = readonly [name: string, value: string]\n\n/** HTTP header included in the SigV4 signed headers list. */\nexport type SignedHeader = readonly [name: string, value: string]\n\n/** Internal inputs required to presign one S3-compatible object request. */\nexport interface SigV4PresignRequestOptions {\n /** S3-compatible endpoint URL. */\n readonly endpoint: string\n /** S3 signing region. */\n readonly region: string\n /** S3 access key ID. */\n readonly accessKeyId: string\n /** S3 secret access key. */\n readonly secretAccessKey: string\n /** Bucket containing the object. */\n readonly bucketName: string\n /** Object key / B2 file name. */\n readonly fileName: string\n /** Optional URL validity duration in seconds. */\n readonly expiresIn?: number\n /** Optional signing clock override. */\n readonly signingDate?: S3PresignDate\n}\n\n/**\n * Generates an AWS Signature Version 4 presigned S3-compatible request URL.\n *\n * This module owns only canonical SigV4 mechanics: canonical URI/query/header\n * construction, signing-key derivation, and signature encoding. Public B2 S3\n * option validation and naming stay in `index.ts`.\n *\n * @param method - HTTP method to presign.\n * @param options - S3 endpoint, credentials, object key, and signing options.\n * @param extraQuery - Operation-specific query parameters to sign.\n * @param extraHeaders - Operation-specific request headers to sign.\n *\n * @returns The presigned request URL.\n */\nexport async function presignS3Request(\n method: 'GET' | 'PUT',\n options: SigV4PresignRequestOptions,\n extraQuery: readonly QueryParam[],\n extraHeaders: readonly SignedHeader[],\n): Promise<string> {\n const endpoint = parseEndpoint(options.endpoint)\n assertHttpsEndpoint(endpoint)\n const expiresIn = normalizeExpiresIn(options.expiresIn)\n const { shortDate, longDate } = formatSigningDate(options.signingDate)\n const credentialScope = `${shortDate}/${options.region}/${SERVICE}/${TERMINATOR}`\n const credential = `${options.accessKeyId}/${credentialScope}`\n assertSafeBucketName(options.bucketName)\n assertValidB2FileName(options.fileName)\n const canonicalUri = buildCanonicalUri(endpoint.pathname, options.bucketName, options.fileName)\n const headers = normalizeSignedHeaders([['host', canonicalHostHeader(endpoint)], ...extraHeaders])\n const signedHeaders = headers.map(([name]) => name).join(';')\n\n const query: QueryParam[] = [\n ['X-Amz-Algorithm', 'AWS4-HMAC-SHA256'],\n ['X-Amz-Credential', credential],\n ['X-Amz-Content-Sha256', UNSIGNED_PAYLOAD],\n ['X-Amz-Date', longDate],\n ['X-Amz-Expires', String(expiresIn)],\n ['X-Amz-SignedHeaders', signedHeaders],\n ...extraQuery,\n ]\n const canonicalQuery = canonicalQueryString(query)\n const canonicalHeaders = headers.map(([name, value]) => `${name}:${value}\\n`).join('')\n const canonicalRequest = [\n method,\n canonicalUri,\n canonicalQuery,\n canonicalHeaders,\n signedHeaders,\n UNSIGNED_PAYLOAD,\n ].join('\\n')\n const stringToSign = [\n 'AWS4-HMAC-SHA256',\n longDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join('\\n')\n const signingKey = await deriveSigningKey(options.secretAccessKey, shortDate, options.region)\n const signature = hexEncode(await hmacSha256(signingKey, stringToSign))\n const finalQuery = canonicalQueryString([...query, ['X-Amz-Signature', signature]])\n\n return `${endpoint.origin}${canonicalUri}?${finalQuery}`\n}\n\nfunction normalizeExpiresIn(expiresIn: number | undefined): number {\n const value = expiresIn ?? DEFAULT_PRESIGN_EXPIRES_IN\n if (!Number.isInteger(value) || value < 1 || value > MAX_PRESIGN_EXPIRES_IN) {\n throw new RangeError(\n `expiresIn must be an integer from 1 to ${MAX_PRESIGN_EXPIRES_IN} seconds; received ${String(value)}.`,\n )\n }\n return value\n}\n\nfunction formatSigningDate(input: S3PresignDate | undefined): {\n readonly shortDate: string\n readonly longDate: string\n} {\n const date = input === undefined ? new Date() : new Date(input)\n if (!Number.isFinite(date.getTime())) {\n throw new RangeError('signingDate must be a valid Date or timestamp.')\n }\n\n const year = String(date.getUTCFullYear()).padStart(4, '0')\n const month = String(date.getUTCMonth() + 1).padStart(2, '0')\n const day = String(date.getUTCDate()).padStart(2, '0')\n const hour = String(date.getUTCHours()).padStart(2, '0')\n const minute = String(date.getUTCMinutes()).padStart(2, '0')\n const second = String(date.getUTCSeconds()).padStart(2, '0')\n const shortDate = `${year}${month}${day}`\n return {\n shortDate,\n longDate: `${shortDate}T${hour}${minute}${second}Z`,\n }\n}\n\nfunction buildCanonicalUri(endpointPath: string, bucketName: string, fileName: string): string {\n const basePath =\n endpointPath === '' || endpointPath === '/' ? '' : endpointPath.replace(/\\/+$/, '')\n return `${basePath}/${awsPercentEncode(bucketName)}/${encodePath(fileName)}`\n}\n\nfunction canonicalHostHeader(endpoint: URL): string {\n const host = endpoint.host\n if (endpoint.protocol === 'https:' && host.endsWith(':443')) {\n return host.slice(0, -4)\n }\n return host\n}\n\nfunction parseEndpoint(endpoint: string): URL {\n try {\n return new URL(endpoint)\n } catch (cause) {\n throw new TypeError(\n `S3 presigned URL endpoint must be a valid URL; received \"${redactUrlForError(endpoint, {\n invalidUrlLabel: '<invalid S3 endpoint URL>',\n })}\".`,\n { cause },\n )\n }\n}\n\nfunction assertHttpsEndpoint(endpoint: URL): void {\n if (endpoint.protocol !== 'https:') {\n throw new TypeError(\n `S3 presigned URLs require an https: endpoint; received \"${redactUrlForError(endpoint)}\".`,\n )\n }\n}\n\nfunction encodePath(path: string): string {\n return path.split('/').map(awsPercentEncode).join('/')\n}\n\nfunction canonicalQueryString(query: readonly QueryParam[]): string {\n return query\n .map(([name, value]) => [awsPercentEncode(name), awsPercentEncode(value)] as const)\n .sort(([aName, aValue], [bName, bValue]) => {\n if (aName < bName) return -1\n if (aName > bName) return 1\n if (aValue < bValue) return -1\n if (aValue > bValue) return 1\n return 0\n })\n .map(([name, value]) => `${name}=${value}`)\n .join('&')\n}\n\n/**\n * Percent-encode according to AWS SigV4 canonical request rules.\n *\n * @param value - Raw value to encode.\n *\n * @returns AWS-compatible percent-encoded value.\n */\nfunction awsPercentEncode(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n )\n}\n\nfunction normalizeSignedHeaders(headers: readonly SignedHeader[]): SignedHeader[] {\n const combinedHeaders = new Map<string, string[]>()\n for (const [name, value] of headers) {\n const normalizedName = name.toLowerCase()\n const values = combinedHeaders.get(normalizedName)\n if (values) {\n values.push(normalizeHeaderValue(value))\n } else {\n combinedHeaders.set(normalizedName, [normalizeHeaderValue(value)])\n }\n }\n\n return [...combinedHeaders.entries()]\n .map(([name, values]) => [name, values.join(',')] as const)\n .sort(([a], [b]) => {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n })\n}\n\nfunction normalizeHeaderValue(value: string): string {\n if (hasHttpHeaderControlCharacter(value)) {\n throw new TypeError('signed header values must not contain control characters.')\n }\n\n return value.trim().replace(/ +/g, ' ')\n}\n\nasync function deriveSigningKey(\n secretAccessKey: string,\n shortDate: string,\n region: string,\n): Promise<Uint8Array> {\n const dateKey = await hmacSha256(`AWS4${secretAccessKey}`, shortDate)\n const dateRegionKey = await hmacSha256(dateKey, region)\n const dateRegionServiceKey = await hmacSha256(dateRegionKey, SERVICE)\n return await hmacSha256(dateRegionServiceKey, TERMINATOR)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sigv4.js","names":[],"sources":["../../src/s3/sigv4.ts"],"sourcesContent":["import { redactUrlForError } from '../internal/url-redaction.ts'\nimport { hexEncode, hmacSha256, sha256Hex } from '../util/crypto.ts'\nimport { hasHttpHeaderControlCharacter } from '../util/http.ts'\nimport { assertSafeBucketName, assertValidB2FileName } from './validation.ts'\n\nconst MAX_PRESIGN_EXPIRES_IN = 604_800\nconst UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'\nconst SERVICE = 's3'\nconst TERMINATOR = 'aws4_request'\n/** Default S3 presigned URL validity duration in seconds. */\nexport const DEFAULT_PRESIGN_EXPIRES_IN = 3600\n\n/** Date input accepted by SigV4 presign internals and deterministic tests. */\nexport type S3PresignDate = Date | number\n\n/** Query parameter included in the SigV4 canonical query string. */\nexport type QueryParam = readonly [name: string, value: string]\n\n/** HTTP header included in the SigV4 signed headers list. */\nexport type SignedHeader = readonly [name: string, value: string]\n\n/** Internal inputs required to presign one S3-compatible object request. */\nexport interface SigV4PresignRequestOptions {\n /** S3-compatible endpoint URL. */\n readonly endpoint: string\n /** S3 signing region. */\n readonly region: string\n /** S3 access key ID. */\n readonly accessKeyId: string\n /** S3 secret access key. */\n readonly secretAccessKey: string\n /** Bucket containing the object. */\n readonly bucketName: string\n /** Object key / B2 file name. */\n readonly fileName: string\n /** Optional URL validity duration in seconds. */\n readonly expiresIn?: number\n /** Optional signing clock override. */\n readonly signingDate?: S3PresignDate\n}\n\n/**\n * Generates an AWS Signature Version 4 presigned S3-compatible request URL.\n *\n * This module owns only canonical SigV4 mechanics: canonical URI/query/header\n * construction, signing-key derivation, and signature encoding. Public B2 S3\n * option validation and naming stay in `index.ts`.\n *\n * @param method - HTTP method to presign.\n * @param options - S3 endpoint, credentials, object key, and signing options.\n * @param extraQuery - Operation-specific query parameters to sign.\n * @param extraHeaders - Operation-specific request headers to sign.\n *\n * @returns The presigned request URL.\n */\nexport async function presignS3Request(\n method: 'GET' | 'PUT',\n options: SigV4PresignRequestOptions,\n extraQuery: readonly QueryParam[],\n extraHeaders: readonly SignedHeader[],\n): Promise<string> {\n const endpoint = parseEndpoint(options.endpoint)\n assertHttpsEndpoint(endpoint)\n const expiresIn = normalizeExpiresIn(options.expiresIn)\n const { shortDate, longDate } = formatSigningDate(options.signingDate)\n const credentialScope = `${shortDate}/${options.region}/${SERVICE}/${TERMINATOR}`\n const credential = `${options.accessKeyId}/${credentialScope}`\n assertSafeBucketName(options.bucketName)\n assertValidB2FileName(options.fileName)\n const canonicalUri = buildCanonicalUri(endpoint.pathname, options.bucketName, options.fileName)\n const headers = normalizeSignedHeaders([['host', canonicalHostHeader(endpoint)], ...extraHeaders])\n const signedHeaders = headers.map(([name]) => name).join(';')\n\n const query: QueryParam[] = [\n ['X-Amz-Algorithm', 'AWS4-HMAC-SHA256'],\n ['X-Amz-Credential', credential],\n ['X-Amz-Content-Sha256', UNSIGNED_PAYLOAD],\n ['X-Amz-Date', longDate],\n ['X-Amz-Expires', String(expiresIn)],\n ['X-Amz-SignedHeaders', signedHeaders],\n ...extraQuery,\n ]\n const canonicalQuery = canonicalQueryString(query)\n const canonicalHeaders = headers.map(([name, value]) => `${name}:${value}\\n`).join('')\n const canonicalRequest = [\n method,\n canonicalUri,\n canonicalQuery,\n canonicalHeaders,\n signedHeaders,\n UNSIGNED_PAYLOAD,\n ].join('\\n')\n const stringToSign = [\n 'AWS4-HMAC-SHA256',\n longDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join('\\n')\n const signingKey = await deriveSigningKey(options.secretAccessKey, shortDate, options.region)\n const signature = hexEncode(await hmacSha256(signingKey, stringToSign))\n const finalQuery = canonicalQueryString([...query, ['X-Amz-Signature', signature]])\n\n return `${endpoint.origin}${canonicalUri}?${finalQuery}`\n}\n\nfunction normalizeExpiresIn(expiresIn: number | undefined): number {\n const value = expiresIn ?? DEFAULT_PRESIGN_EXPIRES_IN\n if (!Number.isInteger(value) || value < 1 || value > MAX_PRESIGN_EXPIRES_IN) {\n throw new RangeError(\n `expiresIn must be an integer from 1 to ${MAX_PRESIGN_EXPIRES_IN} seconds; received ${String(value)}.`,\n )\n }\n return value\n}\n\nfunction formatSigningDate(input: S3PresignDate | undefined): {\n readonly shortDate: string\n readonly longDate: string\n} {\n const date = input === undefined ? new Date() : new Date(input)\n if (!Number.isFinite(date.getTime())) {\n throw new RangeError('signingDate must be a valid Date or timestamp.')\n }\n\n const year = String(date.getUTCFullYear()).padStart(4, '0')\n const month = String(date.getUTCMonth() + 1).padStart(2, '0')\n const day = String(date.getUTCDate()).padStart(2, '0')\n const hour = String(date.getUTCHours()).padStart(2, '0')\n const minute = String(date.getUTCMinutes()).padStart(2, '0')\n const second = String(date.getUTCSeconds()).padStart(2, '0')\n const shortDate = `${year}${month}${day}`\n return {\n shortDate,\n longDate: `${shortDate}T${hour}${minute}${second}Z`,\n }\n}\n\nfunction buildCanonicalUri(endpointPath: string, bucketName: string, fileName: string): string {\n const basePath =\n endpointPath === '' || endpointPath === '/' ? '' : endpointPath.replace(/\\/+$/, '')\n return `${basePath}/${awsPercentEncode(bucketName)}/${encodePath(fileName)}`\n}\n\nfunction canonicalHostHeader(endpoint: URL): string {\n const host = endpoint.host\n if (endpoint.protocol === 'https:' && host.endsWith(':443')) {\n return host.slice(0, -4)\n }\n return host\n}\n\nfunction parseEndpoint(endpoint: string): URL {\n try {\n return new URL(endpoint)\n } catch (cause) {\n throw new TypeError(\n `S3 presigned URL endpoint must be a valid URL; received \"${redactUrlForError(endpoint, {\n invalidUrlLabel: '<invalid S3 endpoint URL>',\n })}\".`,\n { cause },\n )\n }\n}\n\nfunction assertHttpsEndpoint(endpoint: URL): void {\n if (endpoint.protocol !== 'https:') {\n throw new TypeError(\n `S3 presigned URLs require an https: endpoint; received \"${redactUrlForError(endpoint)}\".`,\n )\n }\n}\n\nfunction encodePath(path: string): string {\n return path.split('/').map(awsPercentEncode).join('/')\n}\n\nfunction canonicalQueryString(query: readonly QueryParam[]): string {\n return query\n .map(([name, value]) => [awsPercentEncode(name), awsPercentEncode(value)] as const)\n .sort(([aName, aValue], [bName, bValue]) => {\n if (aName < bName) return -1\n if (aName > bName) return 1\n if (aValue < bValue) return -1\n if (aValue > bValue) return 1\n return 0\n })\n .map(([name, value]) => `${name}=${value}`)\n .join('&')\n}\n\n/**\n * Percent-encode according to AWS SigV4 canonical request rules.\n *\n * @param value - Raw value to encode.\n *\n * @returns AWS-compatible percent-encoded value.\n */\nfunction awsPercentEncode(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n )\n}\n\nfunction normalizeSignedHeaders(headers: readonly SignedHeader[]): SignedHeader[] {\n const combinedHeaders = new Map<string, string[]>()\n for (const [name, value] of headers) {\n const normalizedName = name.toLowerCase()\n const values = combinedHeaders.get(normalizedName)\n if (values) {\n values.push(normalizeHeaderValue(value))\n } else {\n combinedHeaders.set(normalizedName, [normalizeHeaderValue(value)])\n }\n }\n\n return [...combinedHeaders.entries()]\n .map(([name, values]) => [name, values.join(',')] as const)\n .sort(([a], [b]) => {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n })\n}\n\nfunction normalizeHeaderValue(value: string): string {\n if (hasHttpHeaderControlCharacter(value)) {\n throw new TypeError('signed header values must not contain control characters.')\n }\n\n return value.trim().replace(/ +/g, ' ')\n}\n\nasync function deriveSigningKey(\n secretAccessKey: string,\n shortDate: string,\n region: string,\n): Promise<Uint8Array> {\n const dateKey = await hmacSha256(`AWS4${secretAccessKey}`, shortDate)\n const dateRegionKey = await hmacSha256(dateKey, region)\n const dateRegionServiceKey = await hmacSha256(dateRegionKey, SERVICE)\n return await hmacSha256(dateRegionServiceKey, TERMINATOR)\n}\n"],"mappings":"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"}
|