@backblaze-labs/b2-sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -5
- package/README.md +144 -33
- package/dist/_virtual/_b2-sdk-version-json.cjs +4 -0
- package/dist/_virtual/_b2-sdk-version-json.js +4 -0
- package/dist/auth/account-info.d.cts +32 -0
- package/dist/auth/account-info.d.cts.map +1 -1
- package/dist/auth/account-info.d.ts +32 -0
- package/dist/auth/account-info.d.ts.map +1 -1
- package/dist/auth/file.cjs +408 -207
- package/dist/auth/file.cjs.map +1 -1
- package/dist/auth/file.d.cts +77 -3
- package/dist/auth/file.d.cts.map +1 -1
- package/dist/auth/file.d.ts +77 -3
- package/dist/auth/file.d.ts.map +1 -1
- package/dist/auth/file.js +407 -207
- package/dist/auth/file.js.map +1 -1
- package/dist/auth/in-memory.cjs +212 -183
- package/dist/auth/in-memory.cjs.map +1 -1
- package/dist/auth/in-memory.d.cts +8 -0
- package/dist/auth/in-memory.d.cts.map +1 -1
- package/dist/auth/in-memory.d.ts +8 -0
- package/dist/auth/in-memory.d.ts.map +1 -1
- package/dist/auth/in-memory.js +211 -182
- package/dist/auth/in-memory.js.map +1 -1
- package/dist/auth/index.cjs +7 -9
- package/dist/auth/index.d.cts +3 -2
- package/dist/auth/index.d.cts.map +1 -1
- package/dist/auth/index.d.ts +3 -2
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +2 -8
- package/dist/auth/realms.cjs +80 -7
- package/dist/auth/realms.cjs.map +1 -1
- package/dist/auth/realms.d.cts +27 -4
- package/dist/auth/realms.d.cts.map +1 -1
- package/dist/auth/realms.d.ts +27 -4
- package/dist/auth/realms.d.ts.map +1 -1
- package/dist/auth/realms.js +80 -9
- package/dist/auth/realms.js.map +1 -1
- package/dist/auth/upload-url-pool.cjs +55 -51
- package/dist/auth/upload-url-pool.cjs.map +1 -1
- package/dist/auth/upload-url-pool.js +55 -51
- package/dist/auth/upload-url-pool.js.map +1 -1
- package/dist/bucket.cjs +785 -787
- package/dist/bucket.cjs.map +1 -1
- package/dist/bucket.d.cts +35 -47
- package/dist/bucket.d.cts.map +1 -1
- package/dist/bucket.d.ts +35 -47
- package/dist/bucket.d.ts.map +1 -1
- package/dist/bucket.js +780 -782
- package/dist/bucket.js.map +1 -1
- package/dist/client.cjs +246 -228
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +25 -17
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.ts +25 -17
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +243 -225
- package/dist/client.js.map +1 -1
- package/dist/copy/large.cjs +114 -71
- package/dist/copy/large.cjs.map +1 -1
- package/dist/copy/large.d.cts +6 -0
- package/dist/copy/large.d.cts.map +1 -1
- package/dist/copy/large.d.ts +6 -0
- package/dist/copy/large.d.ts.map +1 -1
- package/dist/copy/large.js +112 -69
- package/dist/copy/large.js.map +1 -1
- package/dist/download/checksum.cjs +89 -0
- package/dist/download/checksum.cjs.map +1 -0
- package/dist/download/checksum.d.cts +40 -0
- package/dist/download/checksum.d.cts.map +1 -0
- package/dist/download/checksum.d.ts +40 -0
- package/dist/download/checksum.d.ts.map +1 -0
- package/dist/download/checksum.js +87 -0
- package/dist/download/checksum.js.map +1 -0
- package/dist/download/parallel.cjs +214 -118
- package/dist/download/parallel.cjs.map +1 -1
- package/dist/download/parallel.d.cts +5 -1
- package/dist/download/parallel.d.cts.map +1 -1
- package/dist/download/parallel.d.ts +5 -1
- package/dist/download/parallel.d.ts.map +1 -1
- package/dist/download/parallel.js +212 -116
- package/dist/download/parallel.js.map +1 -1
- package/dist/download/single.cjs +188 -97
- package/dist/download/single.cjs.map +1 -1
- package/dist/download/single.d.cts +6 -1
- package/dist/download/single.d.cts.map +1 -1
- package/dist/download/single.d.ts +6 -1
- package/dist/download/single.d.ts.map +1 -1
- package/dist/download/single.js +186 -98
- package/dist/download/single.js.map +1 -1
- package/dist/errors/index.cjs +699 -243
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +285 -82
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +285 -82
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +678 -261
- package/dist/errors/index.js.map +1 -1
- package/dist/http/retry.cjs +45 -28
- package/dist/http/retry.cjs.map +1 -1
- package/dist/http/retry.d.cts +15 -2
- package/dist/http/retry.d.cts.map +1 -1
- package/dist/http/retry.d.ts +15 -2
- package/dist/http/retry.d.ts.map +1 -1
- package/dist/http/retry.js +46 -31
- package/dist/http/retry.js.map +1 -1
- package/dist/http/transport.cjs +438 -140
- package/dist/http/transport.cjs.map +1 -1
- package/dist/http/transport.d.cts +21 -2
- package/dist/http/transport.d.cts.map +1 -1
- package/dist/http/transport.d.ts +21 -2
- package/dist/http/transport.d.ts.map +1 -1
- package/dist/http/transport.js +437 -140
- package/dist/http/transport.js.map +1 -1
- package/dist/http/url-guard.cjs +154 -89
- package/dist/http/url-guard.cjs.map +1 -1
- package/dist/http/url-guard.d.cts +25 -3
- package/dist/http/url-guard.d.cts.map +1 -1
- package/dist/http/url-guard.d.ts +25 -3
- package/dist/http/url-guard.d.ts.map +1 -1
- package/dist/http/url-guard.js +152 -90
- package/dist/http/url-guard.js.map +1 -1
- package/dist/http/user-agent.cjs +84 -44
- package/dist/http/user-agent.cjs.map +1 -1
- package/dist/http/user-agent.js +84 -46
- package/dist/http/user-agent.js.map +1 -1
- package/dist/index.cjs +101 -77
- package/dist/index.d.cts +27 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +27 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -71
- package/dist/internal/b2-naming.cjs +66 -0
- package/dist/internal/b2-naming.cjs.map +1 -0
- package/dist/internal/b2-naming.d.cts +43 -0
- package/dist/internal/b2-naming.d.cts.map +1 -0
- package/dist/internal/b2-naming.d.ts +43 -0
- package/dist/internal/b2-naming.d.ts.map +1 -0
- package/dist/internal/b2-naming.js +60 -0
- package/dist/internal/b2-naming.js.map +1 -0
- package/dist/internal/upload-retry-options.cjs +21 -0
- package/dist/internal/upload-retry-options.cjs.map +1 -0
- package/dist/internal/upload-retry-options.d.cts +12 -0
- package/dist/internal/upload-retry-options.d.cts.map +1 -0
- package/dist/internal/upload-retry-options.d.ts +12 -0
- package/dist/internal/upload-retry-options.d.ts.map +1 -0
- package/dist/internal/upload-retry-options.js +21 -0
- package/dist/internal/upload-retry-options.js.map +1 -0
- package/dist/internal/url-redaction.cjs +31 -0
- package/dist/internal/url-redaction.cjs.map +1 -0
- package/dist/internal/url-redaction.d.cts +17 -0
- package/dist/internal/url-redaction.d.cts.map +1 -0
- package/dist/internal/url-redaction.d.ts +17 -0
- package/dist/internal/url-redaction.d.ts.map +1 -0
- package/dist/internal/url-redaction.js +31 -0
- package/dist/internal/url-redaction.js.map +1 -0
- package/dist/notifications/index.cjs +4 -6
- package/dist/notifications/index.d.cts +1 -1
- package/dist/notifications/index.d.cts.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -6
- package/dist/notifications/webhook.cjs +195 -85
- package/dist/notifications/webhook.cjs.map +1 -1
- package/dist/notifications/webhook.d.cts.map +1 -1
- package/dist/notifications/webhook.d.ts.map +1 -1
- package/dist/notifications/webhook.js +195 -87
- package/dist/notifications/webhook.js.map +1 -1
- package/dist/object.cjs +253 -210
- package/dist/object.cjs.map +1 -1
- package/dist/object.d.cts +9 -55
- package/dist/object.d.cts.map +1 -1
- package/dist/object.d.ts +9 -55
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +249 -206
- package/dist/object.js.map +1 -1
- package/dist/raw/encoding.cjs +74 -35
- package/dist/raw/encoding.cjs.map +1 -1
- package/dist/raw/encoding.js +74 -38
- package/dist/raw/encoding.js.map +1 -1
- package/dist/raw/index.cjs +678 -587
- package/dist/raw/index.cjs.map +1 -1
- package/dist/raw/index.d.cts +121 -18
- package/dist/raw/index.d.cts.map +1 -1
- package/dist/raw/index.d.ts +121 -18
- package/dist/raw/index.d.ts.map +1 -1
- package/dist/raw/index.js +674 -589
- package/dist/raw/index.js.map +1 -1
- package/dist/s3/index.cjs +355 -18
- package/dist/s3/index.cjs.map +1 -1
- package/dist/s3/index.d.cts +233 -5
- package/dist/s3/index.d.cts.map +1 -1
- package/dist/s3/index.d.ts +233 -5
- package/dist/s3/index.d.ts.map +1 -1
- package/dist/s3/index.js +351 -22
- package/dist/s3/index.js.map +1 -1
- package/dist/s3/sigv4.cjs +149 -0
- package/dist/s3/sigv4.cjs.map +1 -0
- package/dist/s3/sigv4.d.cts +43 -0
- package/dist/s3/sigv4.d.cts.map +1 -0
- package/dist/s3/sigv4.d.ts +43 -0
- package/dist/s3/sigv4.d.ts.map +1 -0
- package/dist/s3/sigv4.js +148 -0
- package/dist/s3/sigv4.js.map +1 -0
- package/dist/s3/validation.cjs +69 -0
- package/dist/s3/validation.cjs.map +1 -0
- package/dist/s3/validation.d.cts +43 -0
- package/dist/s3/validation.d.cts.map +1 -0
- package/dist/s3/validation.d.ts +43 -0
- package/dist/s3/validation.d.ts.map +1 -0
- package/dist/s3/validation.js +66 -0
- package/dist/s3/validation.js.map +1 -0
- package/dist/simulator/capabilities.cjs +63 -48
- package/dist/simulator/capabilities.cjs.map +1 -1
- package/dist/simulator/capabilities.js +63 -49
- package/dist/simulator/capabilities.js.map +1 -1
- package/dist/simulator/index.cjs +2124 -1548
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.cts +79 -11
- package/dist/simulator/index.d.cts.map +1 -1
- package/dist/simulator/index.d.ts +79 -11
- package/dist/simulator/index.d.ts.map +1 -1
- package/dist/simulator/index.js +2112 -1546
- package/dist/simulator/index.js.map +1 -1
- package/dist/simulator/validation.cjs +191 -153
- package/dist/simulator/validation.cjs.map +1 -1
- package/dist/simulator/validation.d.cts +3 -21
- package/dist/simulator/validation.d.cts.map +1 -1
- package/dist/simulator/validation.d.ts +3 -21
- package/dist/simulator/validation.d.ts.map +1 -1
- package/dist/simulator/validation.js +188 -162
- package/dist/simulator/validation.js.map +1 -1
- package/dist/streams/collect.cjs +74 -24
- package/dist/streams/collect.cjs.map +1 -1
- package/dist/streams/collect.d.cts +14 -1
- package/dist/streams/collect.d.cts.map +1 -1
- package/dist/streams/collect.d.ts +14 -1
- package/dist/streams/collect.d.ts.map +1 -1
- package/dist/streams/collect.js +74 -25
- package/dist/streams/collect.js.map +1 -1
- package/dist/streams/file-source.cjs +287 -0
- package/dist/streams/file-source.cjs.map +1 -0
- package/dist/streams/file-source.d.cts +96 -0
- package/dist/streams/file-source.d.cts.map +1 -0
- package/dist/streams/file-source.d.ts +96 -0
- package/dist/streams/file-source.d.ts.map +1 -0
- package/dist/streams/file-source.js +286 -0
- package/dist/streams/file-source.js.map +1 -0
- package/dist/streams/hash.cjs +207 -115
- package/dist/streams/hash.cjs.map +1 -1
- package/dist/streams/hash.d.cts +5 -4
- package/dist/streams/hash.d.cts.map +1 -1
- package/dist/streams/hash.d.ts +5 -4
- package/dist/streams/hash.d.ts.map +1 -1
- package/dist/streams/hash.js +208 -95
- package/dist/streams/hash.js.map +1 -1
- package/dist/streams/index.cjs +12 -12
- package/dist/streams/index.d.cts +3 -3
- package/dist/streams/index.d.cts.map +1 -1
- package/dist/streams/index.d.ts +3 -3
- package/dist/streams/index.d.ts.map +1 -1
- package/dist/streams/index.js +2 -10
- package/dist/streams/progress.cjs +60 -46
- package/dist/streams/progress.cjs.map +1 -1
- package/dist/streams/progress.js +60 -46
- package/dist/streams/progress.js.map +1 -1
- package/dist/streams/source.cjs +316 -146
- package/dist/streams/source.cjs.map +1 -1
- package/dist/streams/source.d.cts +56 -18
- package/dist/streams/source.d.cts.map +1 -1
- package/dist/streams/source.d.ts +56 -18
- package/dist/streams/source.d.ts.map +1 -1
- package/dist/streams/source.js +312 -149
- package/dist/streams/source.js.map +1 -1
- package/dist/sync/actions/index.cjs +236 -184
- package/dist/sync/actions/index.cjs.map +1 -1
- package/dist/sync/actions/index.d.cts +29 -21
- package/dist/sync/actions/index.d.cts.map +1 -1
- package/dist/sync/actions/index.d.ts +29 -21
- package/dist/sync/actions/index.d.ts.map +1 -1
- package/dist/sync/actions/index.js +236 -190
- package/dist/sync/actions/index.js.map +1 -1
- package/dist/sync/b2-sha1-reader.cjs +148 -0
- package/dist/sync/b2-sha1-reader.cjs.map +1 -0
- package/dist/sync/b2-sha1-reader.d.cts +58 -0
- package/dist/sync/b2-sha1-reader.d.cts.map +1 -0
- package/dist/sync/b2-sha1-reader.d.ts +58 -0
- package/dist/sync/b2-sha1-reader.d.ts.map +1 -0
- package/dist/sync/b2-sha1-reader.js +145 -0
- package/dist/sync/b2-sha1-reader.js.map +1 -0
- package/dist/sync/download-staging.cjs +265 -0
- package/dist/sync/download-staging.cjs.map +1 -0
- package/dist/sync/download-staging.d.cts +54 -0
- package/dist/sync/download-staging.d.cts.map +1 -0
- package/dist/sync/download-staging.d.ts +54 -0
- package/dist/sync/download-staging.d.ts.map +1 -0
- package/dist/sync/download-staging.js +259 -0
- package/dist/sync/download-staging.js.map +1 -0
- package/dist/sync/filesystem-errors.cjs +32 -0
- package/dist/sync/filesystem-errors.cjs.map +1 -0
- package/dist/sync/filesystem-errors.d.cts +8 -0
- package/dist/sync/filesystem-errors.d.cts.map +1 -0
- package/dist/sync/filesystem-errors.d.ts +8 -0
- package/dist/sync/filesystem-errors.d.ts.map +1 -0
- package/dist/sync/filesystem-errors.js +32 -0
- package/dist/sync/filesystem-errors.js.map +1 -0
- package/dist/sync/filters.cjs +217 -0
- package/dist/sync/filters.cjs.map +1 -0
- package/dist/sync/filters.d.cts +51 -0
- package/dist/sync/filters.d.cts.map +1 -0
- package/dist/sync/filters.d.ts +51 -0
- package/dist/sync/filters.d.ts.map +1 -0
- package/dist/sync/filters.js +213 -0
- package/dist/sync/filters.js.map +1 -0
- package/dist/sync/index.cjs +39 -22
- package/dist/sync/index.d.cts +14 -9
- package/dist/sync/index.d.cts.map +1 -1
- package/dist/sync/index.d.ts +14 -9
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +9 -20
- package/dist/sync/local-file-identity.cjs +61 -0
- package/dist/sync/local-file-identity.cjs.map +1 -0
- package/dist/sync/local-file-identity.d.cts +37 -0
- package/dist/sync/local-file-identity.d.cts.map +1 -0
- package/dist/sync/local-file-identity.d.ts +37 -0
- package/dist/sync/local-file-identity.d.ts.map +1 -0
- package/dist/sync/local-file-identity.js +60 -0
- package/dist/sync/local-file-identity.js.map +1 -0
- package/dist/sync/local-file-io.cjs +364 -0
- package/dist/sync/local-file-io.cjs.map +1 -0
- package/dist/sync/local-file-io.d.cts +75 -0
- package/dist/sync/local-file-io.d.cts.map +1 -0
- package/dist/sync/local-file-io.d.ts +75 -0
- package/dist/sync/local-file-io.d.ts.map +1 -0
- package/dist/sync/local-file-io.js +360 -0
- package/dist/sync/local-file-io.js.map +1 -0
- package/dist/sync/local-filesystem-root.cjs +27 -0
- package/dist/sync/local-filesystem-root.cjs.map +1 -0
- package/dist/sync/local-filesystem-root.d.cts +18 -0
- package/dist/sync/local-filesystem-root.d.cts.map +1 -0
- package/dist/sync/local-filesystem-root.d.ts +18 -0
- package/dist/sync/local-filesystem-root.d.ts.map +1 -0
- package/dist/sync/local-filesystem-root.js +26 -0
- package/dist/sync/local-filesystem-root.js.map +1 -0
- package/dist/sync/local-sha1.cjs +119 -0
- package/dist/sync/local-sha1.cjs.map +1 -0
- package/dist/sync/local-sha1.d.cts +39 -0
- package/dist/sync/local-sha1.d.cts.map +1 -0
- package/dist/sync/local-sha1.d.ts +39 -0
- package/dist/sync/local-sha1.d.ts.map +1 -0
- package/dist/sync/local-sha1.js +117 -0
- package/dist/sync/local-sha1.js.map +1 -0
- package/dist/sync/pairing.cjs +118 -29
- package/dist/sync/pairing.cjs.map +1 -1
- package/dist/sync/pairing.d.cts +7 -2
- package/dist/sync/pairing.d.cts.map +1 -1
- package/dist/sync/pairing.d.ts +7 -2
- package/dist/sync/pairing.d.ts.map +1 -1
- package/dist/sync/pairing.js +120 -31
- package/dist/sync/pairing.js.map +1 -1
- package/dist/sync/path-order.cjs +30 -0
- package/dist/sync/path-order.cjs.map +1 -0
- package/dist/sync/path-order.d.cts +28 -0
- package/dist/sync/path-order.d.cts.map +1 -0
- package/dist/sync/path-order.d.ts +28 -0
- package/dist/sync/path-order.d.ts.map +1 -0
- package/dist/sync/path-order.js +29 -0
- package/dist/sync/path-order.js.map +1 -0
- package/dist/sync/path-safety.cjs +111 -0
- package/dist/sync/path-safety.cjs.map +1 -0
- package/dist/sync/path-safety.d.cts +78 -0
- package/dist/sync/path-safety.d.cts.map +1 -0
- package/dist/sync/path-safety.d.ts +78 -0
- package/dist/sync/path-safety.d.ts.map +1 -0
- package/dist/sync/path-safety.js +105 -0
- package/dist/sync/path-safety.js.map +1 -0
- package/dist/sync/paths.d.cts +19 -0
- package/dist/sync/paths.d.cts.map +1 -0
- package/dist/sync/paths.d.ts +19 -0
- package/dist/sync/paths.d.ts.map +1 -0
- package/dist/sync/policies/compare.cjs +389 -11
- package/dist/sync/policies/compare.cjs.map +1 -1
- package/dist/sync/policies/compare.d.cts +100 -2
- package/dist/sync/policies/compare.d.cts.map +1 -1
- package/dist/sync/policies/compare.d.ts +100 -2
- package/dist/sync/policies/compare.d.ts.map +1 -1
- package/dist/sync/policies/compare.js +387 -13
- package/dist/sync/policies/compare.js.map +1 -1
- package/dist/sync/policies/index.cjs +76 -65
- package/dist/sync/policies/index.cjs.map +1 -1
- package/dist/sync/policies/index.d.cts +35 -6
- package/dist/sync/policies/index.d.cts.map +1 -1
- package/dist/sync/policies/index.d.ts +35 -6
- package/dist/sync/policies/index.d.ts.map +1 -1
- package/dist/sync/policies/index.js +76 -65
- package/dist/sync/policies/index.js.map +1 -1
- package/dist/sync/prefix.cjs +96 -0
- package/dist/sync/prefix.cjs.map +1 -0
- package/dist/sync/prefix.d.cts +55 -0
- package/dist/sync/prefix.d.cts.map +1 -0
- package/dist/sync/prefix.d.ts +55 -0
- package/dist/sync/prefix.d.ts.map +1 -0
- package/dist/sync/prefix.js +92 -0
- package/dist/sync/prefix.js.map +1 -0
- package/dist/sync/regexp-safety.cjs +192 -0
- package/dist/sync/regexp-safety.cjs.map +1 -0
- package/dist/sync/regexp-safety.d.cts +29 -0
- package/dist/sync/regexp-safety.d.cts.map +1 -0
- package/dist/sync/regexp-safety.d.ts +29 -0
- package/dist/sync/regexp-safety.d.ts.map +1 -0
- package/dist/sync/regexp-safety.js +190 -0
- package/dist/sync/regexp-safety.js.map +1 -0
- package/dist/sync/scan-events.cjs +33 -0
- package/dist/sync/scan-events.cjs.map +1 -0
- package/dist/sync/scan-events.d.cts +17 -0
- package/dist/sync/scan-events.d.cts.map +1 -0
- package/dist/sync/scan-events.d.ts +17 -0
- package/dist/sync/scan-events.d.ts.map +1 -0
- package/dist/sync/scan-events.js +32 -0
- package/dist/sync/scan-events.js.map +1 -0
- package/dist/sync/scan-limit.cjs +33 -0
- package/dist/sync/scan-limit.cjs.map +1 -0
- package/dist/sync/scan-limit.d.cts +21 -0
- package/dist/sync/scan-limit.d.cts.map +1 -0
- package/dist/sync/scan-limit.d.ts +21 -0
- package/dist/sync/scan-limit.d.ts.map +1 -0
- package/dist/sync/scan-limit.js +31 -0
- package/dist/sync/scan-limit.js.map +1 -0
- package/dist/sync/scanners/b2.cjs +239 -55
- package/dist/sync/scanners/b2.cjs.map +1 -1
- package/dist/sync/scanners/b2.d.cts +26 -7
- package/dist/sync/scanners/b2.d.cts.map +1 -1
- package/dist/sync/scanners/b2.d.ts +26 -7
- package/dist/sync/scanners/b2.d.ts.map +1 -1
- package/dist/sync/scanners/b2.js +239 -55
- package/dist/sync/scanners/b2.js.map +1 -1
- package/dist/sync/scanners/local.cjs +215 -56
- package/dist/sync/scanners/local.cjs.map +1 -1
- package/dist/sync/scanners/local.d.cts +22 -7
- package/dist/sync/scanners/local.d.cts.map +1 -1
- package/dist/sync/scanners/local.d.ts +22 -7
- package/dist/sync/scanners/local.d.ts.map +1 -1
- package/dist/sync/scanners/local.js +216 -57
- package/dist/sync/scanners/local.js.map +1 -1
- package/dist/sync/sha1-metadata.cjs +94 -0
- package/dist/sync/sha1-metadata.cjs.map +1 -0
- package/dist/sync/sha1-metadata.d.cts +82 -0
- package/dist/sync/sha1-metadata.d.cts.map +1 -0
- package/dist/sync/sha1-metadata.d.ts +82 -0
- package/dist/sync/sha1-metadata.d.ts.map +1 -0
- package/dist/sync/sha1-metadata.js +89 -0
- package/dist/sync/sha1-metadata.js.map +1 -0
- package/dist/sync/sha1-options.cjs +23 -0
- package/dist/sync/sha1-options.cjs.map +1 -0
- package/dist/sync/sha1-options.d.cts +14 -0
- package/dist/sync/sha1-options.d.cts.map +1 -0
- package/dist/sync/sha1-options.d.ts +14 -0
- package/dist/sync/sha1-options.d.ts.map +1 -0
- package/dist/sync/sha1-options.js +21 -0
- package/dist/sync/sha1-options.js.map +1 -0
- package/dist/sync/synchronizer.cjs +782 -197
- package/dist/sync/synchronizer.cjs.map +1 -1
- package/dist/sync/synchronizer.d.cts +47 -1
- package/dist/sync/synchronizer.d.cts.map +1 -1
- package/dist/sync/synchronizer.d.ts +47 -1
- package/dist/sync/synchronizer.d.ts.map +1 -1
- package/dist/sync/synchronizer.js +779 -173
- package/dist/sync/synchronizer.js.map +1 -1
- package/dist/sync/types.d.cts +208 -16
- package/dist/sync/types.d.cts.map +1 -1
- package/dist/sync/types.d.ts +208 -16
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/types/auth.cjs +76 -56
- package/dist/types/auth.cjs.map +1 -1
- package/dist/types/auth.d.cts +67 -9
- package/dist/types/auth.d.cts.map +1 -1
- package/dist/types/auth.d.ts +67 -9
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/auth.js +77 -57
- package/dist/types/auth.js.map +1 -1
- package/dist/types/bucket.cjs +74 -38
- package/dist/types/bucket.cjs.map +1 -1
- package/dist/types/bucket.js +75 -41
- package/dist/types/bucket.js.map +1 -1
- package/dist/types/encryption.cjs +364 -327
- package/dist/types/encryption.cjs.map +1 -1
- package/dist/types/encryption.d.cts +16 -0
- package/dist/types/encryption.d.cts.map +1 -1
- package/dist/types/encryption.d.ts +16 -0
- package/dist/types/encryption.d.ts.map +1 -1
- package/dist/types/encryption.js +364 -310
- package/dist/types/encryption.js.map +1 -1
- package/dist/types/errors.cjs +47 -0
- package/dist/types/errors.cjs.map +1 -0
- package/dist/types/errors.d.cts +13 -4
- package/dist/types/errors.d.cts.map +1 -1
- package/dist/types/errors.d.ts +13 -4
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +47 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/file.cjs +36 -19
- package/dist/types/file.cjs.map +1 -1
- package/dist/types/file.d.cts +2 -2
- package/dist/types/file.d.cts.map +1 -1
- package/dist/types/file.d.ts +2 -2
- package/dist/types/file.d.ts.map +1 -1
- package/dist/types/file.js +37 -21
- package/dist/types/file.js.map +1 -1
- package/dist/types/ids.cjs +53 -9
- package/dist/types/ids.cjs.map +1 -1
- package/dist/types/ids.js +54 -15
- package/dist/types/ids.js.map +1 -1
- package/dist/types/index.d.cts +12 -11
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +12 -11
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/key.d.cts +44 -8
- package/dist/types/key.d.cts.map +1 -1
- package/dist/types/key.d.ts +44 -8
- package/dist/types/key.d.ts.map +1 -1
- package/dist/types/lock.cjs +33 -13
- package/dist/types/lock.cjs.map +1 -1
- package/dist/types/lock.js +34 -15
- package/dist/types/lock.js.map +1 -1
- package/dist/types/notifications.cjs +44 -22
- package/dist/types/notifications.cjs.map +1 -1
- package/dist/types/notifications.js +45 -23
- package/dist/types/notifications.js.map +1 -1
- package/dist/types/upload.d.cts +39 -5
- package/dist/types/upload.d.cts.map +1 -1
- package/dist/types/upload.d.ts +39 -5
- package/dist/types/upload.d.ts.map +1 -1
- package/dist/upload/abort-scope.cjs +91 -0
- package/dist/upload/abort-scope.cjs.map +1 -0
- package/dist/upload/abort-scope.d.cts +48 -0
- package/dist/upload/abort-scope.d.cts.map +1 -0
- package/dist/upload/abort-scope.d.ts +48 -0
- package/dist/upload/abort-scope.d.ts.map +1 -0
- package/dist/upload/abort-scope.js +88 -0
- package/dist/upload/abort-scope.js.map +1 -0
- package/dist/upload/cancel.cjs +183 -8
- package/dist/upload/cancel.cjs.map +1 -1
- package/dist/upload/cancel.d.cts +110 -2
- package/dist/upload/cancel.d.cts.map +1 -1
- package/dist/upload/cancel.d.ts +110 -2
- package/dist/upload/cancel.d.ts.map +1 -1
- package/dist/upload/cancel.js +178 -9
- package/dist/upload/cancel.js.map +1 -1
- package/dist/upload/concurrency.cjs +79 -52
- package/dist/upload/concurrency.cjs.map +1 -1
- package/dist/upload/concurrency.js +79 -53
- package/dist/upload/concurrency.js.map +1 -1
- package/dist/upload/finish.cjs +59 -0
- package/dist/upload/finish.cjs.map +1 -0
- package/dist/upload/finish.d.cts +26 -0
- package/dist/upload/finish.d.cts.map +1 -0
- package/dist/upload/finish.d.ts +26 -0
- package/dist/upload/finish.d.ts.map +1 -0
- package/dist/upload/finish.js +59 -0
- package/dist/upload/finish.js.map +1 -0
- package/dist/upload/large.cjs +301 -218
- package/dist/upload/large.cjs.map +1 -1
- package/dist/upload/large.d.cts +76 -9
- package/dist/upload/large.d.cts.map +1 -1
- package/dist/upload/large.d.ts +76 -9
- package/dist/upload/large.d.ts.map +1 -1
- package/dist/upload/large.js +301 -218
- package/dist/upload/large.js.map +1 -1
- package/dist/upload/options.cjs +28 -0
- package/dist/upload/options.cjs.map +1 -0
- package/dist/upload/options.d.cts +104 -0
- package/dist/upload/options.d.cts.map +1 -0
- package/dist/upload/options.d.ts +104 -0
- package/dist/upload/options.d.ts.map +1 -0
- package/dist/upload/options.js +27 -0
- package/dist/upload/options.js.map +1 -0
- package/dist/upload/resume.cjs +333 -33
- package/dist/upload/resume.cjs.map +1 -1
- package/dist/upload/resume.d.cts +82 -12
- package/dist/upload/resume.d.cts.map +1 -1
- package/dist/upload/resume.d.ts +82 -12
- package/dist/upload/resume.d.ts.map +1 -1
- package/dist/upload/resume.js +330 -33
- package/dist/upload/resume.js.map +1 -1
- package/dist/upload/retry.cjs +186 -0
- package/dist/upload/retry.cjs.map +1 -0
- package/dist/upload/retry.d.cts +127 -0
- package/dist/upload/retry.d.cts.map +1 -0
- package/dist/upload/retry.d.ts +127 -0
- package/dist/upload/retry.d.ts.map +1 -0
- package/dist/upload/retry.js +182 -0
- package/dist/upload/retry.js.map +1 -0
- package/dist/upload/single.cjs +68 -44
- package/dist/upload/single.cjs.map +1 -1
- package/dist/upload/single.d.cts +2 -1
- package/dist/upload/single.d.cts.map +1 -1
- package/dist/upload/single.d.ts +2 -1
- package/dist/upload/single.d.ts.map +1 -1
- package/dist/upload/single.js +68 -44
- package/dist/upload/single.js.map +1 -1
- package/dist/upload/stream.cjs +317 -193
- package/dist/upload/stream.cjs.map +1 -1
- package/dist/upload/stream.d.cts +3 -1
- package/dist/upload/stream.d.cts.map +1 -1
- package/dist/upload/stream.d.ts +3 -1
- package/dist/upload/stream.d.ts.map +1 -1
- package/dist/upload/stream.js +316 -192
- package/dist/upload/stream.js.map +1 -1
- package/dist/util/best-effort.cjs +42 -8
- package/dist/util/best-effort.cjs.map +1 -1
- package/dist/util/best-effort.d.cts +2 -1
- package/dist/util/best-effort.d.cts.map +1 -1
- package/dist/util/best-effort.d.ts +2 -1
- package/dist/util/best-effort.d.ts.map +1 -1
- package/dist/util/best-effort.js +43 -9
- package/dist/util/best-effort.js.map +1 -1
- package/dist/util/bytes.cjs +23 -0
- package/dist/util/bytes.cjs.map +1 -0
- package/dist/util/bytes.d.cts +15 -0
- package/dist/util/bytes.d.cts.map +1 -0
- package/dist/util/bytes.d.ts +15 -0
- package/dist/util/bytes.d.ts.map +1 -0
- package/dist/util/bytes.js +23 -0
- package/dist/util/bytes.js.map +1 -0
- package/dist/util/crypto.cjs +68 -0
- package/dist/util/crypto.cjs.map +1 -0
- package/dist/util/crypto.d.cts +26 -0
- package/dist/util/crypto.d.cts.map +1 -0
- package/dist/util/crypto.d.ts +26 -0
- package/dist/util/crypto.d.ts.map +1 -0
- package/dist/util/crypto.js +66 -0
- package/dist/util/crypto.js.map +1 -0
- package/dist/util/defaults.cjs +25 -9
- package/dist/util/defaults.cjs.map +1 -1
- package/dist/util/defaults.js +26 -11
- package/dist/util/defaults.js.map +1 -1
- package/dist/util/error-reason.cjs +41 -0
- package/dist/util/error-reason.cjs.map +1 -0
- package/dist/util/error-reason.d.cts +9 -0
- package/dist/util/error-reason.d.cts.map +1 -0
- package/dist/util/error-reason.d.ts +9 -0
- package/dist/util/error-reason.d.ts.map +1 -0
- package/dist/util/error-reason.js +41 -0
- package/dist/util/error-reason.js.map +1 -0
- package/dist/util/http.cjs +23 -0
- package/dist/util/http.cjs.map +1 -0
- package/dist/util/http.d.cts +13 -0
- package/dist/util/http.d.cts.map +1 -0
- package/dist/util/http.d.ts +13 -0
- package/dist/util/http.d.ts.map +1 -0
- package/dist/util/http.js +23 -0
- package/dist/util/http.js.map +1 -0
- package/dist/util/normalize.cjs +66 -7
- package/dist/util/normalize.cjs.map +1 -1
- package/dist/util/normalize.js +67 -10
- package/dist/util/normalize.js.map +1 -1
- package/dist/util/paginator.cjs +63 -14
- package/dist/util/paginator.cjs.map +1 -1
- package/dist/util/paginator.js +64 -16
- package/dist/util/paginator.js.map +1 -1
- package/dist/util/plan-ranges.cjs +47 -22
- package/dist/util/plan-ranges.cjs.map +1 -1
- package/dist/util/plan-ranges.js +48 -24
- package/dist/util/plan-ranges.js.map +1 -1
- package/dist/util/sha1.cjs +27 -0
- package/dist/util/sha1.cjs.map +1 -0
- package/dist/util/sha1.d.cts +17 -0
- package/dist/util/sha1.d.cts.map +1 -0
- package/dist/util/sha1.d.ts +17 -0
- package/dist/util/sha1.d.ts.map +1 -0
- package/dist/util/sha1.js +26 -0
- package/dist/util/sha1.js.map +1 -0
- package/dist/util/text-codec.cjs +29 -5
- package/dist/util/text-codec.cjs.map +1 -1
- package/dist/util/text-codec.js +30 -7
- package/dist/util/text-codec.js.map +1 -1
- package/dist/util/to-error.cjs +18 -4
- package/dist/util/to-error.cjs.map +1 -1
- package/dist/util/to-error.js +19 -5
- package/dist/util/to-error.js.map +1 -1
- package/dist/version.cjs +17 -5
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +3 -3
- package/dist/version.d.ts +3 -3
- package/dist/version.js +19 -6
- package/dist/version.js.map +1 -1
- package/package.json +19 -16
- package/dist/auth/index.cjs.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/notifications/index.cjs.map +0 -1
- package/dist/notifications/index.js.map +0 -1
- package/dist/package.json.cjs +0 -9
- package/dist/package.json.cjs.map +0 -1
- package/dist/package.json.js +0 -9
- package/dist/package.json.js.map +0 -1
- package/dist/streams/index.cjs.map +0 -1
- package/dist/streams/index.js.map +0 -1
- package/dist/sync/index.cjs.map +0 -1
- package/dist/sync/index.js.map +0 -1
|
@@ -1,106 +1,216 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
const require_text_codec = require("../util/text-codec.cjs");
|
|
2
|
+
const require_crypto = require("../util/crypto.cjs");
|
|
3
|
+
//#region src/notifications/webhook.ts
|
|
4
|
+
/**
|
|
5
|
+
* The HTTP header B2 attaches to every event-notification webhook delivery.
|
|
6
|
+
* The value is a versioned HMAC-SHA256 signature of the raw request body:
|
|
7
|
+
* `v1=<lowercase-hex>`.
|
|
8
|
+
*
|
|
9
|
+
* The lowercase form matches Node's `req.headers` normalisation and most
|
|
10
|
+
* server-framework conventions. Producers must compare case-insensitively.
|
|
11
|
+
*/
|
|
12
|
+
var B2_WEBHOOK_SIGNATURE_HEADER = "x-bz-event-notification-signature";
|
|
13
|
+
/** Current B2 event-notification signature scheme prefix. */
|
|
14
|
+
var SIGNATURE_VERSION_PREFIX = "v1=";
|
|
15
|
+
/**
|
|
16
|
+
* Constant-time comparison of two equal-length byte sequences.
|
|
17
|
+
*
|
|
18
|
+
* Web Crypto exposes no native timing-safe compare, so we do the standard
|
|
19
|
+
* XOR-and-OR pattern ourselves. The early-out on length mismatch is fine
|
|
20
|
+
* here because the signature length is publicly fixed (32 bytes for
|
|
21
|
+
* HMAC-SHA256), so a length-mismatch branch leaks nothing about the secret.
|
|
22
|
+
*
|
|
23
|
+
* @param a - First byte sequence.
|
|
24
|
+
* @param b - Second byte sequence.
|
|
25
|
+
*
|
|
26
|
+
* @returns `true` if the byte sequences are byte-for-byte equal.
|
|
27
|
+
*/
|
|
6
28
|
function timingSafeEqual(a, b) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
return diff === 0;
|
|
29
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
30
|
+
let diff = 0;
|
|
31
|
+
for (let i = 0; i < a.byteLength; i++) diff |= a[i] ^ b[i];
|
|
32
|
+
return diff === 0;
|
|
13
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Decode a lowercase hex string into bytes. Returns `null` on any malformed
|
|
36
|
+
* input (odd length, non-hex characters). We don't surface a thrown error
|
|
37
|
+
* because all malformed signatures should land in the same
|
|
38
|
+
* `reason: 'bad signature format'` bucket on the verifier's caller,
|
|
39
|
+
* regardless of which specific malformation triggered it.
|
|
40
|
+
*
|
|
41
|
+
* @param hex - The lowercase hex string to decode.
|
|
42
|
+
*
|
|
43
|
+
* @returns The decoded bytes, or `null` if the input was not valid hex.
|
|
44
|
+
*/
|
|
14
45
|
function hexToBytes(hex) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
46
|
+
if (hex.length % 2 !== 0) return null;
|
|
47
|
+
const out = new Uint8Array(hex.length / 2);
|
|
48
|
+
for (let i = 0; i < out.byteLength; i++) {
|
|
49
|
+
const byte = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
50
|
+
if (Number.isNaN(byte)) return null;
|
|
51
|
+
out[i] = byte;
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
23
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Pick the single signature string from {@link VerifyWebhookOptions.signature}.
|
|
57
|
+
* Returns `null` if absent, empty, or supplied as an empty array.
|
|
58
|
+
*
|
|
59
|
+
* @param raw - The signature value as supplied by the caller.
|
|
60
|
+
*
|
|
61
|
+
* @returns The chosen signature string, or `null` if no candidate was usable.
|
|
62
|
+
*/
|
|
24
63
|
function pickSignature(raw) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
return typeof raw === "string" && raw.length > 0 ? raw : null;
|
|
64
|
+
if (raw === void 0) return null;
|
|
65
|
+
if (Array.isArray(raw)) {
|
|
66
|
+
for (const v of raw) if (typeof v === "string" && v.length > 0) return v;
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return typeof raw === "string" && raw.length > 0 ? raw : null;
|
|
33
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Encode a UTF-8 string into bytes using a shared encoder.
|
|
73
|
+
*
|
|
74
|
+
* @param s - The string to encode.
|
|
75
|
+
*
|
|
76
|
+
* @returns The UTF-8 encoded bytes.
|
|
77
|
+
*/
|
|
34
78
|
function bytes(s) {
|
|
35
|
-
|
|
79
|
+
return require_text_codec.utf8Encoder.encode(s);
|
|
36
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Normalise the body parameter to a `Uint8Array` view of its raw bytes.
|
|
83
|
+
*
|
|
84
|
+
* Strings are UTF-8 encoded. `Uint8Array` inputs pass through untouched.
|
|
85
|
+
*
|
|
86
|
+
* @param body - The raw request body as supplied by the caller.
|
|
87
|
+
*
|
|
88
|
+
* @returns The body as a byte sequence ready for HMAC computation.
|
|
89
|
+
*/
|
|
37
90
|
function bodyBytes(body) {
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
async function hmacSha256(secret, body) {
|
|
41
|
-
const secretBytes = bytes(secret);
|
|
42
|
-
const key = await globalThis.crypto.subtle.importKey(
|
|
43
|
-
"raw",
|
|
44
|
-
secretBytes.buffer.slice(
|
|
45
|
-
secretBytes.byteOffset,
|
|
46
|
-
secretBytes.byteOffset + secretBytes.byteLength
|
|
47
|
-
),
|
|
48
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
49
|
-
false,
|
|
50
|
-
["sign"]
|
|
51
|
-
);
|
|
52
|
-
const sig = await globalThis.crypto.subtle.sign(
|
|
53
|
-
"HMAC",
|
|
54
|
-
key,
|
|
55
|
-
body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength)
|
|
56
|
-
);
|
|
57
|
-
return new Uint8Array(sig);
|
|
91
|
+
return typeof body === "string" ? bytes(body) : body;
|
|
58
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Best-effort JSON parse that returns `null` on failure. We accept partial
|
|
95
|
+
* structural mismatches (no `events` array etc.) at the caller's discretion.
|
|
96
|
+
*
|
|
97
|
+
* @param raw - The decoded request body string.
|
|
98
|
+
*
|
|
99
|
+
* @returns The parsed payload, or `null` if `raw` is not valid JSON.
|
|
100
|
+
*/
|
|
59
101
|
function tryParse(raw) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(raw);
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
65
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Narrow `value` to {@link WebhookPayload}. Currently only checks that
|
|
110
|
+
* `events` is an array; per-event validation is the caller's responsibility
|
|
111
|
+
* since B2 may add new optional fields and we don't want to reject deliveries
|
|
112
|
+
* that include them.
|
|
113
|
+
*
|
|
114
|
+
* @param value - The parsed JSON value to validate.
|
|
115
|
+
*
|
|
116
|
+
* @returns `true` if the value matches the {@link WebhookPayload} shape.
|
|
117
|
+
*/
|
|
66
118
|
function looksLikePayload(value) {
|
|
67
|
-
|
|
119
|
+
return typeof value === "object" && value !== null && Array.isArray(value.events);
|
|
68
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Verify the HMAC-SHA256 signature B2 attaches to event-notification webhooks
|
|
123
|
+
* and parse the body on success.
|
|
124
|
+
*
|
|
125
|
+
* Never throws: returns a discriminated {@link VerifyWebhookResult}. Use this
|
|
126
|
+
* when you want a single, explicit place to branch on `valid` and log the
|
|
127
|
+
* `reason` on rejection.
|
|
128
|
+
*
|
|
129
|
+
* @param opts - The body, signature header, and signing secret. See {@link VerifyWebhookOptions}.
|
|
130
|
+
*
|
|
131
|
+
* @returns Verification result with optional parsed payload and failure reason.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* import { verifyWebhookSignature, B2_WEBHOOK_SIGNATURE_HEADER } from '@backblaze-labs/b2-sdk/notifications'
|
|
136
|
+
*
|
|
137
|
+
* app.post('/webhook', async (req, res) => {
|
|
138
|
+
* const raw = await readRawBody(req) // raw bytes, NOT json
|
|
139
|
+
* const result = await verifyWebhookSignature({
|
|
140
|
+
* body: raw,
|
|
141
|
+
* signature: req.headers[B2_WEBHOOK_SIGNATURE_HEADER],
|
|
142
|
+
* secret: process.env.B2_WEBHOOK_SECRET,
|
|
143
|
+
* })
|
|
144
|
+
* if (!result.valid) return res.status(401).send(result.reason)
|
|
145
|
+
* for (const event of result.payload.events) handleEvent(event)
|
|
146
|
+
* res.status(204).end()
|
|
147
|
+
* })
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
*/
|
|
69
151
|
async function verifyWebhookSignature(opts) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
152
|
+
if (typeof opts.secret !== "string" || opts.secret.length === 0) return {
|
|
153
|
+
valid: false,
|
|
154
|
+
payload: null,
|
|
155
|
+
reason: "missing signing secret"
|
|
156
|
+
};
|
|
157
|
+
const signature = pickSignature(opts.signature);
|
|
158
|
+
if (signature === null) return {
|
|
159
|
+
valid: false,
|
|
160
|
+
payload: null,
|
|
161
|
+
reason: "missing signature header"
|
|
162
|
+
};
|
|
163
|
+
if (!signature.startsWith(SIGNATURE_VERSION_PREFIX)) return {
|
|
164
|
+
valid: false,
|
|
165
|
+
payload: null,
|
|
166
|
+
reason: "unsupported signature version"
|
|
167
|
+
};
|
|
168
|
+
const claimed = hexToBytes(signature.slice(3).toLowerCase());
|
|
169
|
+
if (claimed === null) return {
|
|
170
|
+
valid: false,
|
|
171
|
+
payload: null,
|
|
172
|
+
reason: "bad signature format"
|
|
173
|
+
};
|
|
174
|
+
const raw = bodyBytes(opts.body);
|
|
175
|
+
if (!timingSafeEqual(claimed, await require_crypto.hmacSha256(opts.secret, raw))) return {
|
|
176
|
+
valid: false,
|
|
177
|
+
payload: null,
|
|
178
|
+
reason: "signature mismatch"
|
|
179
|
+
};
|
|
180
|
+
const parsed = tryParse(typeof opts.body === "string" ? opts.body : require_text_codec.utf8Decoder.decode(raw));
|
|
181
|
+
if (!looksLikePayload(parsed)) return {
|
|
182
|
+
valid: false,
|
|
183
|
+
payload: null,
|
|
184
|
+
reason: "invalid payload shape"
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
valid: true,
|
|
188
|
+
payload: parsed,
|
|
189
|
+
reason: null
|
|
190
|
+
};
|
|
95
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Throwing wrapper around {@link verifyWebhookSignature}. Returns the parsed
|
|
194
|
+
* payload on success; throws an `Error` whose message is the verifier's
|
|
195
|
+
* `reason` string on any failure.
|
|
196
|
+
*
|
|
197
|
+
* Useful when you want the verification to integrate with your framework's
|
|
198
|
+
* existing error-handling middleware rather than handling the result inline.
|
|
199
|
+
*
|
|
200
|
+
* @param opts - The body, signature header, and signing secret. See {@link VerifyWebhookOptions}.
|
|
201
|
+
*
|
|
202
|
+
* @returns The parsed webhook payload.
|
|
203
|
+
*
|
|
204
|
+
* @throws When verification fails for any reason.
|
|
205
|
+
*/
|
|
96
206
|
async function requireValidWebhook(opts) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
return result.payload;
|
|
207
|
+
const result = await verifyWebhookSignature(opts);
|
|
208
|
+
if (!result.valid || result.payload === null) throw new Error(`B2 webhook verification failed: ${result.reason ?? "unknown"}`);
|
|
209
|
+
return result.payload;
|
|
102
210
|
}
|
|
211
|
+
//#endregion
|
|
103
212
|
exports.B2_WEBHOOK_SIGNATURE_HEADER = B2_WEBHOOK_SIGNATURE_HEADER;
|
|
104
213
|
exports.requireValidWebhook = requireValidWebhook;
|
|
105
214
|
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
106
|
-
|
|
215
|
+
|
|
216
|
+
//# sourceMappingURL=webhook.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.cjs","sources":["../../src/notifications/webhook.ts"],"sourcesContent":["import type { EventType } from '../types/notifications.ts'\nimport { utf8Decoder, utf8Encoder } from '../util/text-codec.ts'\n\n/**\n * The HTTP header B2 attaches to every event-notification webhook delivery.\n * The value is a versioned HMAC-SHA256 signature of the raw request body:\n * `v1=<lowercase-hex>`.\n *\n * The lowercase form matches Node's `req.headers` normalisation and most\n * server-framework conventions. Producers must compare case-insensitively.\n */\nexport const B2_WEBHOOK_SIGNATURE_HEADER = 'x-bz-event-notification-signature'\n\n/** Current B2 event-notification signature scheme prefix. */\nconst SIGNATURE_VERSION_PREFIX = 'v1='\n\n/**\n * A single event entry inside a B2 webhook payload.\n *\n * The `events` array on the parsed body contains zero or more of these. Most\n * deliveries hold a single event, but B2 may coalesce multiple events from the\n * same bucket into one delivery for efficiency, so callers should iterate.\n *\n * @see https://www.backblaze.com/apidocs/event-notifications-overview\n */\nexport interface WebhookEvent {\n /** Account that owns the bucket the event fired on. */\n readonly accountId: string\n /** Bucket ID the event fired on. */\n readonly bucketId: string\n /** Bucket name at the moment the event fired. */\n readonly bucketName: string\n /** Globally unique event identifier (useful for idempotency on the receiver). */\n readonly eventId: string\n /** Event time in UNIX milliseconds (UTC). */\n readonly eventTimestamp: number\n /** Specific event type, e.g. `'b2:ObjectCreated:Upload'`. */\n readonly eventType: EventType\n /** Schema version of the event payload. Currently `1`. */\n readonly eventVersion: number\n /** Name of the `EventNotificationRule` on the bucket that matched. */\n readonly matchedRuleName: string\n /** B2 file name of the affected object. */\n readonly objectName: string\n /** Affected object's size in bytes, when present (absent for some delete events). */\n readonly objectSize?: number\n /** Specific file version ID, when applicable. */\n readonly objectVersionId?: string\n}\n\n/**\n * The complete JSON body B2 POSTs to a webhook endpoint after signature\n * verification succeeds. See {@link verifyWebhookSignature}.\n */\nexport interface WebhookPayload {\n /** One or more event entries. Always non-empty on real deliveries. */\n readonly events: readonly WebhookEvent[]\n}\n\n/**\n * Options for {@link verifyWebhookSignature} and {@link requireValidWebhook}.\n */\nexport interface VerifyWebhookOptions {\n /**\n * The raw request body B2 sent. MUST be the exact bytes received: any\n * JSON re-serialisation or whitespace normalisation will break the HMAC.\n *\n * Pass a `Uint8Array` whenever the surrounding framework gives you raw\n * bytes (e.g. `req.rawBody` in fastify, `req.arrayBuffer()` in Hono /\n * Workers). The string overload is provided for convenience when a\n * framework already decoded the body as UTF-8.\n */\n readonly body: string | Uint8Array\n /**\n * Value of the {@link B2_WEBHOOK_SIGNATURE_HEADER} header.\n *\n * Accepts a single string or an array (Node's `req.headers` returns\n * `string | string[] | undefined`). When an array is supplied, the first\n * non-empty element is used: B2 only ever sends one value, and rejecting\n * the multi-value case prevents callers from accidentally trusting a\n * spoofed second header injected by a buggy reverse proxy.\n */\n readonly signature: string | readonly string[] | undefined\n /**\n * The signing secret from `EventNotificationRule.targetConfiguration.hmacSha256SigningSecret`.\n *\n * This is the exact string B2 returned in the `b2_set_bucket_notification_rules`\n * response. Do NOT base64-decode or otherwise transform it.\n */\n readonly secret: string\n}\n\n/**\n * Outcome of {@link verifyWebhookSignature}.\n *\n * The verifier never throws; instead it returns a discriminated result so\n * callers can branch on `valid` and log `reason` without try/catch noise.\n * Use {@link requireValidWebhook} when an exception-based control flow is\n * more convenient.\n */\nexport interface VerifyWebhookResult {\n /** `true` only when the HMAC matched and the body parsed as a valid payload. */\n readonly valid: boolean\n /** Parsed payload when {@link valid} is `true`; otherwise `null`. */\n readonly payload: WebhookPayload | null\n /** Short reason describing why verification failed, or `null` on success. */\n readonly reason: string | null\n}\n\n/**\n * Constant-time comparison of two equal-length byte sequences.\n *\n * Web Crypto exposes no native timing-safe compare, so we do the standard\n * XOR-and-OR pattern ourselves. The early-out on length mismatch is fine\n * here because the signature length is publicly fixed (32 bytes for\n * HMAC-SHA256), so a length-mismatch branch leaks nothing about the secret.\n *\n * @param a - First byte sequence.\n * @param b - Second byte sequence.\n *\n * @returns `true` if the byte sequences are byte-for-byte equal.\n */\nfunction timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.byteLength !== b.byteLength) return false\n let diff = 0\n for (let i = 0; i < a.byteLength; i++) {\n // Non-null asserted: `a` and `b` have the same length and `i` is in range.\n diff |= (a[i] as number) ^ (b[i] as number)\n }\n return diff === 0\n}\n\n/**\n * Decode a lowercase hex string into bytes. Returns `null` on any malformed\n * input (odd length, non-hex characters). We don't surface a thrown error\n * because all malformed signatures should land in the same\n * `reason: 'bad signature format'` bucket on the verifier's caller,\n * regardless of which specific malformation triggered it.\n *\n * @param hex - The lowercase hex string to decode.\n *\n * @returns The decoded bytes, or `null` if the input was not valid hex.\n */\nfunction hexToBytes(hex: string): Uint8Array | null {\n if (hex.length % 2 !== 0) return null\n const out = new Uint8Array(hex.length / 2)\n for (let i = 0; i < out.byteLength; i++) {\n const byte = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16)\n if (Number.isNaN(byte)) return null\n out[i] = byte\n }\n return out\n}\n\n/**\n * Pick the single signature string from {@link VerifyWebhookOptions.signature}.\n * Returns `null` if absent, empty, or supplied as an empty array.\n *\n * @param raw - The signature value as supplied by the caller.\n *\n * @returns The chosen signature string, or `null` if no candidate was usable.\n */\nfunction pickSignature(raw: VerifyWebhookOptions['signature']): string | null {\n if (raw === undefined) return null\n if (Array.isArray(raw)) {\n for (const v of raw) {\n if (typeof v === 'string' && v.length > 0) return v\n }\n return null\n }\n return typeof raw === 'string' && raw.length > 0 ? raw : null\n}\n\n/**\n * Encode a UTF-8 string into bytes using a shared encoder.\n *\n * @param s - The string to encode.\n *\n * @returns The UTF-8 encoded bytes.\n */\nfunction bytes(s: string): Uint8Array {\n return utf8Encoder.encode(s)\n}\n\n/**\n * Normalise the body parameter to a `Uint8Array` view of its raw bytes.\n *\n * Strings are UTF-8 encoded. `Uint8Array` inputs pass through untouched.\n *\n * @param body - The raw request body as supplied by the caller.\n *\n * @returns The body as a byte sequence ready for HMAC computation.\n */\nfunction bodyBytes(body: string | Uint8Array): Uint8Array {\n return typeof body === 'string' ? bytes(body) : body\n}\n\n/**\n * Compute the HMAC-SHA256 of `body` under `secret` using the isomorphic Web\n * Crypto API. Works in Node 22+, browsers, Bun, Deno, and Cloudflare Workers\n * without any platform-specific code.\n *\n * @param secret - The HMAC signing secret.\n * @param body - The bytes to sign.\n *\n * @returns The 32-byte HMAC-SHA256 digest.\n */\nasync function hmacSha256(secret: string, body: Uint8Array): Promise<Uint8Array> {\n // The explicit `slice(byteOffset, byteOffset + byteLength)` defends against\n // a Uint8Array view that points at a subset of a larger buffer. Casting to\n // `ArrayBuffer` is needed because TS 5.7+ types Uint8Array's buffer as\n // `ArrayBufferLike`, which includes `SharedArrayBuffer`. Web Crypto's\n // signatures only accept the plain `ArrayBuffer` variant.\n const secretBytes = bytes(secret)\n const key = await globalThis.crypto.subtle.importKey(\n 'raw',\n secretBytes.buffer.slice(\n secretBytes.byteOffset,\n secretBytes.byteOffset + secretBytes.byteLength,\n ) as ArrayBuffer,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await globalThis.crypto.subtle.sign(\n 'HMAC',\n key,\n body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer,\n )\n return new Uint8Array(sig)\n}\n\n/**\n * Best-effort JSON parse that returns `null` on failure. We accept partial\n * structural mismatches (no `events` array etc.) at the caller's discretion.\n *\n * @param raw - The decoded request body string.\n *\n * @returns The parsed payload, or `null` if `raw` is not valid JSON.\n */\nfunction tryParse(raw: string): unknown {\n try {\n return JSON.parse(raw) as unknown\n } catch {\n return null\n }\n}\n\n/**\n * Narrow `value` to {@link WebhookPayload}. Currently only checks that\n * `events` is an array; per-event validation is the caller's responsibility\n * since B2 may add new optional fields and we don't want to reject deliveries\n * that include them.\n *\n * @param value - The parsed JSON value to validate.\n *\n * @returns `true` if the value matches the {@link WebhookPayload} shape.\n */\nfunction looksLikePayload(value: unknown): value is WebhookPayload {\n return (\n typeof value === 'object' &&\n value !== null &&\n Array.isArray((value as { events?: unknown }).events)\n )\n}\n\n/**\n * Verify the HMAC-SHA256 signature B2 attaches to event-notification webhooks\n * and parse the body on success.\n *\n * Never throws: returns a discriminated {@link VerifyWebhookResult}. Use this\n * when you want a single, explicit place to branch on `valid` and log the\n * `reason` on rejection.\n *\n * @param opts - The body, signature header, and signing secret. See {@link VerifyWebhookOptions}.\n *\n * @returns Verification result with optional parsed payload and failure reason.\n *\n * @example\n * ```ts\n * import { verifyWebhookSignature, B2_WEBHOOK_SIGNATURE_HEADER } from '@backblaze-labs/b2-sdk/notifications'\n *\n * app.post('/webhook', async (req, res) => {\n * const raw = await readRawBody(req) // raw bytes, NOT json\n * const result = await verifyWebhookSignature({\n * body: raw,\n * signature: req.headers[B2_WEBHOOK_SIGNATURE_HEADER],\n * secret: process.env.B2_WEBHOOK_SECRET,\n * })\n * if (!result.valid) return res.status(401).send(result.reason)\n * for (const event of result.payload.events) handleEvent(event)\n * res.status(204).end()\n * })\n * ```\n *\n */\nexport async function verifyWebhookSignature(\n opts: VerifyWebhookOptions,\n): Promise<VerifyWebhookResult> {\n if (typeof opts.secret !== 'string' || opts.secret.length === 0) {\n return { valid: false, payload: null, reason: 'missing signing secret' }\n }\n\n const signature = pickSignature(opts.signature)\n if (signature === null) {\n return { valid: false, payload: null, reason: 'missing signature header' }\n }\n\n if (!signature.startsWith(SIGNATURE_VERSION_PREFIX)) {\n return { valid: false, payload: null, reason: 'unsupported signature version' }\n }\n\n const claimed = hexToBytes(signature.slice(SIGNATURE_VERSION_PREFIX.length).toLowerCase())\n if (claimed === null) {\n return { valid: false, payload: null, reason: 'bad signature format' }\n }\n\n const raw = bodyBytes(opts.body)\n const expected = await hmacSha256(opts.secret, raw)\n\n if (!timingSafeEqual(claimed, expected)) {\n return { valid: false, payload: null, reason: 'signature mismatch' }\n }\n\n const decoded = typeof opts.body === 'string' ? opts.body : utf8Decoder.decode(raw)\n const parsed = tryParse(decoded)\n if (!looksLikePayload(parsed)) {\n return { valid: false, payload: null, reason: 'invalid payload shape' }\n }\n\n return { valid: true, payload: parsed, reason: null }\n}\n\n/**\n * Throwing wrapper around {@link verifyWebhookSignature}. Returns the parsed\n * payload on success; throws an `Error` whose message is the verifier's\n * `reason` string on any failure.\n *\n * Useful when you want the verification to integrate with your framework's\n * existing error-handling middleware rather than handling the result inline.\n *\n * @param opts - The body, signature header, and signing secret. See {@link VerifyWebhookOptions}.\n *\n * @returns The parsed webhook payload.\n *\n * @throws When verification fails for any reason.\n */\nexport async function requireValidWebhook(opts: VerifyWebhookOptions): Promise<WebhookPayload> {\n const result = await verifyWebhookSignature(opts)\n if (!result.valid || result.payload === null) {\n throw new Error(`B2 webhook verification failed: ${result.reason ?? 'unknown'}`)\n }\n return result.payload\n}\n"],"names":["utf8Encoder","utf8Decoder"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webhook.cjs","names":[],"sources":["../../src/notifications/webhook.ts"],"sourcesContent":["import type { EventType } from '../types/notifications.ts'\nimport { hmacSha256 } from '../util/crypto.ts'\nimport { utf8Decoder, utf8Encoder } from '../util/text-codec.ts'\n\n/**\n * The HTTP header B2 attaches to every event-notification webhook delivery.\n * The value is a versioned HMAC-SHA256 signature of the raw request body:\n * `v1=<lowercase-hex>`.\n *\n * The lowercase form matches Node's `req.headers` normalisation and most\n * server-framework conventions. Producers must compare case-insensitively.\n */\nexport const B2_WEBHOOK_SIGNATURE_HEADER = 'x-bz-event-notification-signature'\n\n/** Current B2 event-notification signature scheme prefix. */\nconst SIGNATURE_VERSION_PREFIX = 'v1='\n\n/**\n * A single event entry inside a B2 webhook payload.\n *\n * The `events` array on the parsed body contains zero or more of these. Most\n * deliveries hold a single event, but B2 may coalesce multiple events from the\n * same bucket into one delivery for efficiency, so callers should iterate.\n *\n * @see https://www.backblaze.com/apidocs/event-notifications-overview\n */\nexport interface WebhookEvent {\n /** Account that owns the bucket the event fired on. */\n readonly accountId: string\n /** Bucket ID the event fired on. */\n readonly bucketId: string\n /** Bucket name at the moment the event fired. */\n readonly bucketName: string\n /** Globally unique event identifier (useful for idempotency on the receiver). */\n readonly eventId: string\n /** Event time in UNIX milliseconds (UTC). */\n readonly eventTimestamp: number\n /** Specific event type, e.g. `'b2:ObjectCreated:Upload'`. */\n readonly eventType: EventType\n /** Schema version of the event payload. Currently `1`. */\n readonly eventVersion: number\n /** Name of the `EventNotificationRule` on the bucket that matched. */\n readonly matchedRuleName: string\n /** B2 file name of the affected object. */\n readonly objectName: string\n /** Affected object's size in bytes, when present (absent for some delete events). */\n readonly objectSize?: number\n /** Specific file version ID, when applicable. */\n readonly objectVersionId?: string\n}\n\n/**\n * The complete JSON body B2 POSTs to a webhook endpoint after signature\n * verification succeeds. See {@link verifyWebhookSignature}.\n */\nexport interface WebhookPayload {\n /** One or more event entries. Always non-empty on real deliveries. */\n readonly events: readonly WebhookEvent[]\n}\n\n/**\n * Options for {@link verifyWebhookSignature} and {@link requireValidWebhook}.\n */\nexport interface VerifyWebhookOptions {\n /**\n * The raw request body B2 sent. MUST be the exact bytes received: any\n * JSON re-serialisation or whitespace normalisation will break the HMAC.\n *\n * Pass a `Uint8Array` whenever the surrounding framework gives you raw\n * bytes (e.g. `req.rawBody` in fastify, `req.arrayBuffer()` in Hono /\n * Workers). The string overload is provided for convenience when a\n * framework already decoded the body as UTF-8.\n */\n readonly body: string | Uint8Array\n /**\n * Value of the {@link B2_WEBHOOK_SIGNATURE_HEADER} header.\n *\n * Accepts a single string or an array (Node's `req.headers` returns\n * `string | string[] | undefined`). When an array is supplied, the first\n * non-empty element is used: B2 only ever sends one value, and rejecting\n * the multi-value case prevents callers from accidentally trusting a\n * spoofed second header injected by a buggy reverse proxy.\n */\n readonly signature: string | readonly string[] | undefined\n /**\n * The signing secret from `EventNotificationRule.targetConfiguration.hmacSha256SigningSecret`.\n *\n * This is the exact string B2 returned in the `b2_set_bucket_notification_rules`\n * response. Do NOT base64-decode or otherwise transform it.\n */\n readonly secret: string\n}\n\n/**\n * Outcome of {@link verifyWebhookSignature}.\n *\n * The verifier never throws; instead it returns a discriminated result so\n * callers can branch on `valid` and log `reason` without try/catch noise.\n * Use {@link requireValidWebhook} when an exception-based control flow is\n * more convenient.\n */\nexport interface VerifyWebhookResult {\n /** `true` only when the HMAC matched and the body parsed as a valid payload. */\n readonly valid: boolean\n /** Parsed payload when {@link valid} is `true`; otherwise `null`. */\n readonly payload: WebhookPayload | null\n /** Short reason describing why verification failed, or `null` on success. */\n readonly reason: string | null\n}\n\n/**\n * Constant-time comparison of two equal-length byte sequences.\n *\n * Web Crypto exposes no native timing-safe compare, so we do the standard\n * XOR-and-OR pattern ourselves. The early-out on length mismatch is fine\n * here because the signature length is publicly fixed (32 bytes for\n * HMAC-SHA256), so a length-mismatch branch leaks nothing about the secret.\n *\n * @param a - First byte sequence.\n * @param b - Second byte sequence.\n *\n * @returns `true` if the byte sequences are byte-for-byte equal.\n */\nfunction timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.byteLength !== b.byteLength) return false\n let diff = 0\n for (let i = 0; i < a.byteLength; i++) {\n // Non-null asserted: `a` and `b` have the same length and `i` is in range.\n diff |= (a[i] as number) ^ (b[i] as number)\n }\n return diff === 0\n}\n\n/**\n * Decode a lowercase hex string into bytes. Returns `null` on any malformed\n * input (odd length, non-hex characters). We don't surface a thrown error\n * because all malformed signatures should land in the same\n * `reason: 'bad signature format'` bucket on the verifier's caller,\n * regardless of which specific malformation triggered it.\n *\n * @param hex - The lowercase hex string to decode.\n *\n * @returns The decoded bytes, or `null` if the input was not valid hex.\n */\nfunction hexToBytes(hex: string): Uint8Array | null {\n if (hex.length % 2 !== 0) return null\n const out = new Uint8Array(hex.length / 2)\n for (let i = 0; i < out.byteLength; i++) {\n const byte = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16)\n if (Number.isNaN(byte)) return null\n out[i] = byte\n }\n return out\n}\n\n/**\n * Pick the single signature string from {@link VerifyWebhookOptions.signature}.\n * Returns `null` if absent, empty, or supplied as an empty array.\n *\n * @param raw - The signature value as supplied by the caller.\n *\n * @returns The chosen signature string, or `null` if no candidate was usable.\n */\nfunction pickSignature(raw: VerifyWebhookOptions['signature']): string | null {\n if (raw === undefined) return null\n if (Array.isArray(raw)) {\n for (const v of raw) {\n if (typeof v === 'string' && v.length > 0) return v\n }\n return null\n }\n return typeof raw === 'string' && raw.length > 0 ? raw : null\n}\n\n/**\n * Encode a UTF-8 string into bytes using a shared encoder.\n *\n * @param s - The string to encode.\n *\n * @returns The UTF-8 encoded bytes.\n */\nfunction bytes(s: string): Uint8Array {\n return utf8Encoder.encode(s)\n}\n\n/**\n * Normalise the body parameter to a `Uint8Array` view of its raw bytes.\n *\n * Strings are UTF-8 encoded. `Uint8Array` inputs pass through untouched.\n *\n * @param body - The raw request body as supplied by the caller.\n *\n * @returns The body as a byte sequence ready for HMAC computation.\n */\nfunction bodyBytes(body: string | Uint8Array): Uint8Array {\n return typeof body === 'string' ? bytes(body) : body\n}\n\n/**\n * Best-effort JSON parse that returns `null` on failure. We accept partial\n * structural mismatches (no `events` array etc.) at the caller's discretion.\n *\n * @param raw - The decoded request body string.\n *\n * @returns The parsed payload, or `null` if `raw` is not valid JSON.\n */\nfunction tryParse(raw: string): unknown {\n try {\n return JSON.parse(raw) as unknown\n } catch {\n return null\n }\n}\n\n/**\n * Narrow `value` to {@link WebhookPayload}. Currently only checks that\n * `events` is an array; per-event validation is the caller's responsibility\n * since B2 may add new optional fields and we don't want to reject deliveries\n * that include them.\n *\n * @param value - The parsed JSON value to validate.\n *\n * @returns `true` if the value matches the {@link WebhookPayload} shape.\n */\nfunction looksLikePayload(value: unknown): value is WebhookPayload {\n return (\n typeof value === 'object' &&\n value !== null &&\n Array.isArray((value as { events?: unknown }).events)\n )\n}\n\n/**\n * Verify the HMAC-SHA256 signature B2 attaches to event-notification webhooks\n * and parse the body on success.\n *\n * Never throws: returns a discriminated {@link VerifyWebhookResult}. Use this\n * when you want a single, explicit place to branch on `valid` and log the\n * `reason` on rejection.\n *\n * @param opts - The body, signature header, and signing secret. See {@link VerifyWebhookOptions}.\n *\n * @returns Verification result with optional parsed payload and failure reason.\n *\n * @example\n * ```ts\n * import { verifyWebhookSignature, B2_WEBHOOK_SIGNATURE_HEADER } from '@backblaze-labs/b2-sdk/notifications'\n *\n * app.post('/webhook', async (req, res) => {\n * const raw = await readRawBody(req) // raw bytes, NOT json\n * const result = await verifyWebhookSignature({\n * body: raw,\n * signature: req.headers[B2_WEBHOOK_SIGNATURE_HEADER],\n * secret: process.env.B2_WEBHOOK_SECRET,\n * })\n * if (!result.valid) return res.status(401).send(result.reason)\n * for (const event of result.payload.events) handleEvent(event)\n * res.status(204).end()\n * })\n * ```\n *\n */\nexport async function verifyWebhookSignature(\n opts: VerifyWebhookOptions,\n): Promise<VerifyWebhookResult> {\n if (typeof opts.secret !== 'string' || opts.secret.length === 0) {\n return { valid: false, payload: null, reason: 'missing signing secret' }\n }\n\n const signature = pickSignature(opts.signature)\n if (signature === null) {\n return { valid: false, payload: null, reason: 'missing signature header' }\n }\n\n if (!signature.startsWith(SIGNATURE_VERSION_PREFIX)) {\n return { valid: false, payload: null, reason: 'unsupported signature version' }\n }\n\n const claimed = hexToBytes(signature.slice(SIGNATURE_VERSION_PREFIX.length).toLowerCase())\n if (claimed === null) {\n return { valid: false, payload: null, reason: 'bad signature format' }\n }\n\n const raw = bodyBytes(opts.body)\n const expected = await hmacSha256(opts.secret, raw)\n\n if (!timingSafeEqual(claimed, expected)) {\n return { valid: false, payload: null, reason: 'signature mismatch' }\n }\n\n const decoded = typeof opts.body === 'string' ? opts.body : utf8Decoder.decode(raw)\n const parsed = tryParse(decoded)\n if (!looksLikePayload(parsed)) {\n return { valid: false, payload: null, reason: 'invalid payload shape' }\n }\n\n return { valid: true, payload: parsed, reason: null }\n}\n\n/**\n * Throwing wrapper around {@link verifyWebhookSignature}. Returns the parsed\n * payload on success; throws an `Error` whose message is the verifier's\n * `reason` string on any failure.\n *\n * Useful when you want the verification to integrate with your framework's\n * existing error-handling middleware rather than handling the result inline.\n *\n * @param opts - The body, signature header, and signing secret. See {@link VerifyWebhookOptions}.\n *\n * @returns The parsed webhook payload.\n *\n * @throws When verification fails for any reason.\n */\nexport async function requireValidWebhook(opts: VerifyWebhookOptions): Promise<WebhookPayload> {\n const result = await verifyWebhookSignature(opts)\n if (!result.valid || result.payload === null) {\n throw new Error(`B2 webhook verification failed: ${result.reason ?? 'unknown'}`)\n }\n return result.payload\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/notifications/webhook.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/notifications/webhook.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAI1D;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,sCAAsC,CAAA;AAK9E;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,iDAAiD;IACjD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAA;IAClC;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IAC1D;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IACvC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AA4HD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAiC9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAM7F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/notifications/webhook.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/notifications/webhook.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAI1D;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,sCAAsC,CAAA;AAK9E;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,iDAAiD;IACjD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAA;IAClC;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IAC1D;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IACvC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AA4HD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAiC9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAM7F"}
|