@backblaze-labs/b2-sdk 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -1
- package/README.md +18 -5
- package/dist/_virtual/_b2-sdk-version-json.cjs +1 -1
- package/dist/_virtual/_b2-sdk-version-json.js +1 -1
- package/dist/auth/reauth-coalescer.cjs +65 -0
- package/dist/auth/reauth-coalescer.cjs.map +1 -0
- package/dist/auth/reauth-coalescer.d.cts +34 -0
- package/dist/auth/reauth-coalescer.d.cts.map +1 -0
- package/dist/auth/reauth-coalescer.d.ts +34 -0
- package/dist/auth/reauth-coalescer.d.ts.map +1 -0
- package/dist/auth/reauth-coalescer.js +65 -0
- package/dist/auth/reauth-coalescer.js.map +1 -0
- package/dist/backup/raw.cjs +37 -15
- package/dist/backup/raw.cjs.map +1 -1
- package/dist/backup/raw.d.cts +2 -2
- package/dist/backup/raw.d.cts.map +1 -1
- package/dist/backup/raw.d.ts +2 -2
- package/dist/backup/raw.d.ts.map +1 -1
- package/dist/backup/raw.js +37 -15
- package/dist/backup/raw.js.map +1 -1
- package/dist/bucket-validation.cjs +111 -0
- package/dist/bucket-validation.cjs.map +1 -0
- package/dist/bucket-validation.d.cts +27 -0
- package/dist/bucket-validation.d.cts.map +1 -0
- package/dist/bucket-validation.d.ts +27 -0
- package/dist/bucket-validation.d.ts.map +1 -0
- package/dist/bucket-validation.js +109 -0
- package/dist/bucket-validation.js.map +1 -0
- package/dist/bucket.cjs +123 -73
- package/dist/bucket.cjs.map +1 -1
- package/dist/bucket.d.cts +156 -61
- package/dist/bucket.d.cts.map +1 -1
- package/dist/bucket.d.ts +156 -61
- package/dist/bucket.d.ts.map +1 -1
- package/dist/bucket.js +123 -73
- package/dist/bucket.js.map +1 -1
- package/dist/client.cjs +29 -8
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +30 -9
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.ts +30 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +29 -8
- package/dist/client.js.map +1 -1
- package/dist/download/single.cjs +38 -13
- package/dist/download/single.cjs.map +1 -1
- package/dist/download/single.d.cts +10 -4
- package/dist/download/single.d.cts.map +1 -1
- package/dist/download/single.d.ts +10 -4
- package/dist/download/single.d.ts.map +1 -1
- package/dist/download/single.js +38 -13
- package/dist/download/single.js.map +1 -1
- package/dist/errors/index.cjs +27 -4
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +15 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +15 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +27 -5
- package/dist/errors/index.js.map +1 -1
- package/dist/http/non-idempotent-mutations.cjs +38 -0
- package/dist/http/non-idempotent-mutations.cjs.map +1 -0
- package/dist/http/non-idempotent-mutations.d.cts +25 -0
- package/dist/http/non-idempotent-mutations.d.cts.map +1 -0
- package/dist/http/non-idempotent-mutations.d.ts +25 -0
- package/dist/http/non-idempotent-mutations.d.ts.map +1 -0
- package/dist/http/non-idempotent-mutations.js +35 -0
- package/dist/http/non-idempotent-mutations.js.map +1 -0
- package/dist/http/transport.cjs +9 -8
- package/dist/http/transport.cjs.map +1 -1
- package/dist/http/transport.d.cts +5 -0
- package/dist/http/transport.d.cts.map +1 -1
- package/dist/http/transport.d.ts +5 -0
- package/dist/http/transport.d.ts.map +1 -1
- package/dist/http/transport.js +9 -8
- package/dist/http/transport.js.map +1 -1
- package/dist/http/user-agent.cjs +12 -1
- package/dist/http/user-agent.cjs.map +1 -1
- package/dist/http/user-agent.d.cts +8 -0
- package/dist/http/user-agent.d.cts.map +1 -1
- package/dist/http/user-agent.d.ts +8 -0
- package/dist/http/user-agent.d.ts.map +1 -1
- package/dist/http/user-agent.js +13 -3
- package/dist/http/user-agent.js.map +1 -1
- package/dist/index.cjs +25 -0
- package/dist/index.d.cts +10 -9
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/notifications/index.cjs +3 -0
- package/dist/notifications/index.d.cts +2 -0
- package/dist/notifications/index.d.cts.map +1 -1
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +2 -1
- package/dist/object.cjs +12 -1
- package/dist/object.cjs.map +1 -1
- package/dist/object.d.cts +7 -1
- package/dist/object.d.cts.map +1 -1
- package/dist/object.d.ts +7 -1
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +12 -1
- package/dist/object.js.map +1 -1
- package/dist/partner/auth-core.cjs +4 -23
- package/dist/partner/auth-core.cjs.map +1 -1
- package/dist/partner/auth-core.d.cts +1 -1
- package/dist/partner/auth-core.d.cts.map +1 -1
- package/dist/partner/auth-core.d.ts +1 -1
- package/dist/partner/auth-core.d.ts.map +1 -1
- package/dist/partner/auth-core.js +5 -24
- package/dist/partner/auth-core.js.map +1 -1
- package/dist/partner/client.cjs +25 -10
- package/dist/partner/client.cjs.map +1 -1
- package/dist/partner/client.d.cts +32 -10
- package/dist/partner/client.d.cts.map +1 -1
- package/dist/partner/client.d.ts +32 -10
- package/dist/partner/client.d.ts.map +1 -1
- package/dist/partner/client.js +25 -10
- package/dist/partner/client.js.map +1 -1
- package/dist/partner/endpoint-url.cjs +1 -0
- package/dist/partner/endpoint-url.cjs.map +1 -1
- package/dist/partner/endpoint-url.d.cts +2 -0
- package/dist/partner/endpoint-url.d.cts.map +1 -1
- package/dist/partner/endpoint-url.d.ts +2 -0
- package/dist/partner/endpoint-url.d.ts.map +1 -1
- package/dist/partner/endpoint-url.js +1 -0
- package/dist/partner/endpoint-url.js.map +1 -1
- package/dist/partner/in-memory.cjs.map +1 -1
- package/dist/partner/in-memory.js.map +1 -1
- package/dist/partner/index.cjs +9 -0
- package/dist/partner/index.d.cts +11 -2
- package/dist/partner/index.d.cts.map +1 -1
- package/dist/partner/index.d.ts +11 -2
- package/dist/partner/index.d.ts.map +1 -1
- package/dist/partner/index.js +3 -1
- package/dist/partner/raw.cjs +52 -44
- package/dist/partner/raw.cjs.map +1 -1
- package/dist/partner/raw.d.cts +16 -19
- package/dist/partner/raw.d.cts.map +1 -1
- package/dist/partner/raw.d.ts +16 -19
- package/dist/partner/raw.d.ts.map +1 -1
- package/dist/partner/raw.js +52 -44
- package/dist/partner/raw.js.map +1 -1
- package/dist/partner/redaction.cjs +133 -108
- package/dist/partner/redaction.cjs.map +1 -1
- package/dist/partner/redaction.d.cts +47 -13
- package/dist/partner/redaction.d.cts.map +1 -1
- package/dist/partner/redaction.d.ts +47 -13
- package/dist/partner/redaction.d.ts.map +1 -1
- package/dist/partner/redaction.js +133 -109
- package/dist/partner/redaction.js.map +1 -1
- package/dist/raw/encoding.cjs +29 -10
- package/dist/raw/encoding.cjs.map +1 -1
- package/dist/raw/encoding.d.cts +3 -2
- package/dist/raw/encoding.d.cts.map +1 -1
- package/dist/raw/encoding.d.ts +3 -2
- package/dist/raw/encoding.d.ts.map +1 -1
- package/dist/raw/encoding.js +29 -10
- package/dist/raw/encoding.js.map +1 -1
- package/dist/raw/index.cjs +64 -39
- package/dist/raw/index.cjs.map +1 -1
- package/dist/raw/index.d.cts +21 -19
- package/dist/raw/index.d.cts.map +1 -1
- package/dist/raw/index.d.ts +21 -19
- package/dist/raw/index.d.ts.map +1 -1
- package/dist/raw/index.js +47 -40
- package/dist/raw/index.js.map +1 -1
- package/dist/raw/url.cjs.map +1 -1
- package/dist/raw/url.d.cts +3 -3
- package/dist/raw/url.d.ts +3 -3
- package/dist/raw/url.js.map +1 -1
- package/dist/simulator/index.cjs +456 -125
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.cts +16 -1
- package/dist/simulator/index.d.cts.map +1 -1
- package/dist/simulator/index.d.ts +16 -1
- package/dist/simulator/index.d.ts.map +1 -1
- package/dist/simulator/index.js +449 -130
- package/dist/simulator/index.js.map +1 -1
- package/dist/simulator/partner-capabilities.cjs +77 -0
- package/dist/simulator/partner-capabilities.cjs.map +1 -0
- package/dist/simulator/partner-capabilities.d.cts +53 -0
- package/dist/simulator/partner-capabilities.d.cts.map +1 -0
- package/dist/simulator/partner-capabilities.d.ts +53 -0
- package/dist/simulator/partner-capabilities.d.ts.map +1 -0
- package/dist/simulator/partner-capabilities.js +73 -0
- package/dist/simulator/partner-capabilities.js.map +1 -0
- package/dist/simulator/partner.cjs +55 -61
- package/dist/simulator/partner.cjs.map +1 -1
- package/dist/simulator/partner.d.cts +5 -2
- package/dist/simulator/partner.d.cts.map +1 -1
- package/dist/simulator/partner.d.ts +5 -2
- package/dist/simulator/partner.d.ts.map +1 -1
- package/dist/simulator/partner.js +56 -61
- package/dist/simulator/partner.js.map +1 -1
- package/dist/simulator/validation.cjs +160 -30
- package/dist/simulator/validation.cjs.map +1 -1
- package/dist/simulator/validation.d.cts +28 -6
- package/dist/simulator/validation.d.cts.map +1 -1
- package/dist/simulator/validation.d.ts +28 -6
- package/dist/simulator/validation.d.ts.map +1 -1
- package/dist/simulator/validation.js +148 -31
- 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/hash.cjs +246 -13
- package/dist/streams/hash.cjs.map +1 -1
- package/dist/streams/hash.d.cts +37 -12
- package/dist/streams/hash.d.cts.map +1 -1
- package/dist/streams/hash.d.ts +37 -12
- package/dist/streams/hash.d.ts.map +1 -1
- package/dist/streams/hash.js +246 -15
- package/dist/streams/hash.js.map +1 -1
- package/dist/streams/index.cjs +2 -0
- package/dist/streams/index.d.cts +1 -1
- package/dist/streams/index.d.cts.map +1 -1
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.d.ts.map +1 -1
- package/dist/streams/index.js +2 -2
- package/dist/streams/progress.cjs +63 -4
- package/dist/streams/progress.cjs.map +1 -1
- package/dist/streams/progress.d.cts +28 -2
- package/dist/streams/progress.d.cts.map +1 -1
- package/dist/streams/progress.d.ts +28 -2
- package/dist/streams/progress.d.ts.map +1 -1
- package/dist/streams/progress.js +63 -4
- package/dist/streams/progress.js.map +1 -1
- package/dist/sync/download-staging.cjs +147 -35
- package/dist/sync/download-staging.cjs.map +1 -1
- package/dist/sync/download-staging.d.cts +25 -2
- package/dist/sync/download-staging.d.cts.map +1 -1
- package/dist/sync/download-staging.d.ts +25 -2
- package/dist/sync/download-staging.d.ts.map +1 -1
- package/dist/sync/download-staging.js +146 -36
- package/dist/sync/download-staging.js.map +1 -1
- package/dist/sync/index.d.cts +1 -1
- package/dist/sync/index.d.cts.map +1 -1
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/local-file-io.cjs +5 -1
- package/dist/sync/local-file-io.cjs.map +1 -1
- package/dist/sync/local-file-io.d.cts +2 -0
- package/dist/sync/local-file-io.d.cts.map +1 -1
- package/dist/sync/local-file-io.d.ts +2 -0
- package/dist/sync/local-file-io.d.ts.map +1 -1
- package/dist/sync/local-file-io.js +5 -1
- package/dist/sync/local-file-io.js.map +1 -1
- package/dist/sync/pairing.cjs +1 -0
- package/dist/sync/pairing.cjs.map +1 -1
- package/dist/sync/pairing.js +1 -0
- package/dist/sync/pairing.js.map +1 -1
- package/dist/sync/scanners/b2.cjs +2 -0
- package/dist/sync/scanners/b2.cjs.map +1 -1
- package/dist/sync/scanners/b2.d.cts.map +1 -1
- package/dist/sync/scanners/b2.d.ts.map +1 -1
- package/dist/sync/scanners/b2.js +2 -0
- package/dist/sync/scanners/b2.js.map +1 -1
- package/dist/sync/scanners/local.cjs +150 -33
- package/dist/sync/scanners/local.cjs.map +1 -1
- package/dist/sync/scanners/local.d.cts +6 -0
- package/dist/sync/scanners/local.d.cts.map +1 -1
- package/dist/sync/scanners/local.d.ts +6 -0
- package/dist/sync/scanners/local.d.ts.map +1 -1
- package/dist/sync/scanners/local.js +150 -33
- package/dist/sync/scanners/local.js.map +1 -1
- package/dist/sync/synchronizer.cjs +76 -29
- package/dist/sync/synchronizer.cjs.map +1 -1
- package/dist/sync/synchronizer.d.cts +1 -0
- package/dist/sync/synchronizer.d.cts.map +1 -1
- package/dist/sync/synchronizer.d.ts +1 -0
- package/dist/sync/synchronizer.d.ts.map +1 -1
- package/dist/sync/synchronizer.js +76 -29
- package/dist/sync/synchronizer.js.map +1 -1
- package/dist/sync/types.d.cts +19 -3
- package/dist/sync/types.d.cts.map +1 -1
- package/dist/sync/types.d.ts +19 -3
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/types/backup.d.cts +6 -1
- package/dist/types/backup.d.cts.map +1 -1
- package/dist/types/backup.d.ts +6 -1
- package/dist/types/backup.d.ts.map +1 -1
- package/dist/types/bucket.cjs +81 -11
- package/dist/types/bucket.cjs.map +1 -1
- package/dist/types/bucket.d.cts +190 -39
- package/dist/types/bucket.d.cts.map +1 -1
- package/dist/types/bucket.d.ts +190 -39
- package/dist/types/bucket.d.ts.map +1 -1
- package/dist/types/bucket.js +67 -12
- package/dist/types/bucket.js.map +1 -1
- package/dist/types/download.cjs.map +1 -1
- package/dist/types/download.d.cts +3 -17
- package/dist/types/download.d.cts.map +1 -1
- package/dist/types/download.d.ts +3 -17
- package/dist/types/download.d.ts.map +1 -1
- package/dist/types/download.js.map +1 -1
- package/dist/types/encryption.cjs +18 -0
- package/dist/types/encryption.cjs.map +1 -1
- package/dist/types/encryption.d.cts +21 -11
- package/dist/types/encryption.d.cts.map +1 -1
- package/dist/types/encryption.d.ts +21 -11
- package/dist/types/encryption.d.ts.map +1 -1
- package/dist/types/encryption.js +18 -0
- package/dist/types/encryption.js.map +1 -1
- package/dist/types/file.cjs +3 -0
- package/dist/types/file.cjs.map +1 -1
- package/dist/types/file.d.cts +125 -16
- package/dist/types/file.d.cts.map +1 -1
- package/dist/types/file.d.ts +125 -16
- package/dist/types/file.d.ts.map +1 -1
- package/dist/types/file.js +3 -1
- package/dist/types/file.js.map +1 -1
- package/dist/types/index.d.cts +7 -7
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/key.d.cts +4 -3
- package/dist/types/key.d.cts.map +1 -1
- package/dist/types/key.d.ts +4 -3
- package/dist/types/key.d.ts.map +1 -1
- package/dist/types/notifications.cjs +30 -0
- package/dist/types/notifications.cjs.map +1 -1
- package/dist/types/notifications.d.cts +26 -1
- package/dist/types/notifications.d.cts.map +1 -1
- package/dist/types/notifications.d.ts +26 -1
- package/dist/types/notifications.d.ts.map +1 -1
- package/dist/types/notifications.js +29 -1
- package/dist/types/notifications.js.map +1 -1
- package/dist/types/partner.cjs.map +1 -1
- package/dist/types/partner.d.cts +28 -36
- package/dist/types/partner.d.cts.map +1 -1
- package/dist/types/partner.d.ts +28 -36
- package/dist/types/partner.d.ts.map +1 -1
- package/dist/types/partner.js.map +1 -1
- package/dist/types/upload.d.cts +4 -0
- package/dist/types/upload.d.cts.map +1 -1
- package/dist/types/upload.d.ts +4 -0
- package/dist/types/upload.d.ts.map +1 -1
- package/dist/upload/large.cjs +10 -7
- package/dist/upload/large.cjs.map +1 -1
- package/dist/upload/large.d.cts +15 -6
- package/dist/upload/large.d.cts.map +1 -1
- package/dist/upload/large.d.ts +15 -6
- package/dist/upload/large.d.ts.map +1 -1
- package/dist/upload/large.js +10 -7
- 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 +4 -3
- package/dist/upload/resume.cjs.map +1 -1
- package/dist/upload/resume.d.cts +6 -4
- package/dist/upload/resume.d.cts.map +1 -1
- package/dist/upload/resume.d.ts +6 -4
- package/dist/upload/resume.d.ts.map +1 -1
- package/dist/upload/resume.js +4 -3
- package/dist/upload/resume.js.map +1 -1
- package/dist/upload/retry.cjs +17 -10
- package/dist/upload/retry.cjs.map +1 -1
- package/dist/upload/retry.d.cts.map +1 -1
- package/dist/upload/retry.d.ts.map +1 -1
- package/dist/upload/retry.js +17 -10
- package/dist/upload/retry.js.map +1 -1
- package/dist/upload/single.cjs +35 -31
- 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 +35 -31
- package/dist/upload/single.js.map +1 -1
- package/dist/upload/stream.cjs +12 -1
- 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 +12 -1
- package/dist/upload/stream.js.map +1 -1
- package/dist/version.cjs +56 -4
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +53 -4
- package/dist/version.d.cts.map +1 -1
- package/dist/version.d.ts +53 -4
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +52 -5
- package/dist/version.js.map +1 -1
- package/package.json +11 -4
package/dist/backup/raw.js
CHANGED
|
@@ -1,16 +1,42 @@
|
|
|
1
1
|
import { b2Url } from "../raw/url.js";
|
|
2
2
|
import { endpointAllowedSuffixes, nonRetryingMutationRequestOptions, validatePartnerEndpointUrl, withQueryString } from "../partner/endpoint-url.js";
|
|
3
3
|
//#region src/backup/raw.ts
|
|
4
|
-
var
|
|
4
|
+
var BACKUP_API = {
|
|
5
5
|
prefix: "api/backup",
|
|
6
6
|
version: "v1"
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes the `bz_delete_computer` wire response to the array-shaped schema.
|
|
10
|
+
*
|
|
11
|
+
* The Backblaze apidocs response *schema* declares a JSON array, but the
|
|
12
|
+
* accompanying *example* shows a single bare object. This tolerates both: an
|
|
13
|
+
* array passes through unchanged, and a lone object is wrapped in a
|
|
14
|
+
* single-element array, so callers always receive a {@link DeleteComputerResponse}
|
|
15
|
+
* regardless of which shape live B2 returns.
|
|
16
|
+
*
|
|
17
|
+
* @param raw - The parsed `bz_delete_computer` JSON body.
|
|
18
|
+
*
|
|
19
|
+
* @returns The response as the canonical array shape.
|
|
20
|
+
*
|
|
21
|
+
* @throws TypeError When the body is neither an array nor an object.
|
|
22
|
+
*/
|
|
23
|
+
function normalizeDeleteComputerResponse(raw) {
|
|
24
|
+
if (Array.isArray(raw)) return raw;
|
|
25
|
+
if (raw !== null && typeof raw === "object") return [raw];
|
|
26
|
+
throw new TypeError("bz_delete_computer returned an unexpected response body shape");
|
|
27
|
+
}
|
|
8
28
|
function backupEndpointAllowedSuffixes(transport, authorizedSuffixes) {
|
|
9
29
|
return endpointAllowedSuffixes(transport, authorizedSuffixes, {
|
|
10
30
|
missingGuard: "Computer Backup endpoint requests require BackupClient.authorize() or a locked URL guard before sending Partner tokens",
|
|
11
31
|
unlockedGuard: "Computer Backup endpoint requests require a locked URL guard before sending Partner tokens"
|
|
12
32
|
});
|
|
13
33
|
}
|
|
34
|
+
function backupEndpointUrl(backupApiUrl, endpoint) {
|
|
35
|
+
return b2Url(backupApiUrl, {
|
|
36
|
+
...BACKUP_API,
|
|
37
|
+
endpoint
|
|
38
|
+
});
|
|
39
|
+
}
|
|
14
40
|
/**
|
|
15
41
|
* Low-level client for Computer Backup API endpoint bindings.
|
|
16
42
|
*
|
|
@@ -81,10 +107,10 @@ var BackupRawClient = class {
|
|
|
81
107
|
* @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.
|
|
82
108
|
*/
|
|
83
109
|
async deleteComputer(backupApiUrl, authToken, request, options) {
|
|
84
|
-
return this.
|
|
110
|
+
return normalizeDeleteComputerResponse(await this.postNonRetryingMutationJson(backupApiUrl, authToken, "bz_delete_computer", {
|
|
85
111
|
accountId: request.accountId,
|
|
86
112
|
computerId: request.computerId
|
|
87
|
-
},
|
|
113
|
+
}, options));
|
|
88
114
|
}
|
|
89
115
|
safeBackupApiUrl(backupApiUrl) {
|
|
90
116
|
return validatePartnerEndpointUrl(backupApiUrl, "backupApiUrl", backupEndpointAllowedSuffixes(this.transport, this.authorizedEndpointSuffixes));
|
|
@@ -102,10 +128,7 @@ var BackupRawClient = class {
|
|
|
102
128
|
*/
|
|
103
129
|
async getJson(backupApiUrl, authToken, endpoint, query, options) {
|
|
104
130
|
return (await this.transport.send({
|
|
105
|
-
url: withQueryString(
|
|
106
|
-
...BACKUP_API_V1,
|
|
107
|
-
endpoint
|
|
108
|
-
}), query),
|
|
131
|
+
url: withQueryString(backupEndpointUrl(this.safeBackupApiUrl(backupApiUrl), endpoint), query),
|
|
109
132
|
method: "GET",
|
|
110
133
|
headers: { Authorization: authToken },
|
|
111
134
|
...options?.signal !== void 0 ? { signal: options.signal } : {},
|
|
@@ -113,7 +136,7 @@ var BackupRawClient = class {
|
|
|
113
136
|
})).json();
|
|
114
137
|
}
|
|
115
138
|
/**
|
|
116
|
-
* Sends a JSON POST request to the specified Computer Backup API endpoint.
|
|
139
|
+
* Sends a non-retrying JSON mutation POST request to the specified Computer Backup API endpoint.
|
|
117
140
|
*
|
|
118
141
|
* @param backupApiUrl - The Computer Backup API base URL.
|
|
119
142
|
* @param authToken - The Partner authorization token.
|
|
@@ -123,20 +146,19 @@ var BackupRawClient = class {
|
|
|
123
146
|
*
|
|
124
147
|
* @returns The parsed JSON response.
|
|
125
148
|
*/
|
|
126
|
-
async
|
|
149
|
+
async postNonRetryingMutationJson(backupApiUrl, authToken, endpoint, body, options) {
|
|
150
|
+
const requestOptions = nonRetryingMutationRequestOptions(options);
|
|
127
151
|
return (await this.transport.send({
|
|
128
|
-
url:
|
|
129
|
-
...BACKUP_API_V1,
|
|
130
|
-
endpoint
|
|
131
|
-
}),
|
|
152
|
+
url: backupEndpointUrl(this.safeBackupApiUrl(backupApiUrl), endpoint),
|
|
132
153
|
method: "POST",
|
|
133
154
|
headers: {
|
|
134
155
|
Authorization: authToken,
|
|
135
156
|
"Content-Type": "application/json"
|
|
136
157
|
},
|
|
137
158
|
body: JSON.stringify(body),
|
|
138
|
-
...
|
|
139
|
-
...
|
|
159
|
+
...requestOptions.signal !== void 0 ? { signal: requestOptions.signal } : {},
|
|
160
|
+
...requestOptions.idempotent !== void 0 ? { idempotent: requestOptions.idempotent } : {},
|
|
161
|
+
...requestOptions.retry !== void 0 ? { retry: requestOptions.retry } : {}
|
|
140
162
|
})).json();
|
|
141
163
|
}
|
|
142
164
|
};
|
package/dist/backup/raw.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw.js","names":[],"sources":["../../src/backup/raw.ts"],"sourcesContent":["import type { RetryOptions } from '../http/retry.ts'\nimport type { HttpTransport } from '../http/transport.ts'\nimport {\n endpointAllowedSuffixes,\n nonRetryingMutationRequestOptions,\n type QueryParams,\n validatePartnerEndpointUrl,\n withQueryString,\n} from '../partner/endpoint-url.ts'\nimport { type B2EndpointUrlOptions, b2Url } from '../raw/url.ts'\nimport type {\n DeleteComputerRequest,\n DeleteComputerResponse,\n ListComputersRequest,\n ListComputersResponse,\n} from '../types/backup.ts'\n\nconst BACKUP_API_V1: B2EndpointUrlOptions = { prefix: 'api/backup', version: 'v1' }\n\n/** Configuration for constructing a {@link BackupRawClient}. */\nexport interface BackupRawClientOptions {\n /**\n * The HTTP transport used to send requests.\n *\n * Computer Backup endpoint calls validate URLs against suffixes recorded by\n * Partner authorization. A rehydrated client that skips authorization must\n * use `authorizedBackupEndpointSuffixes` or a transport with a locked\n * `urlGuard`. Because Partner authorize responses redact\n * `authorizationToken` under `JSON.stringify`, durable caches should persist\n * `partnerAuthorizeResponseForPersistence(auth)` only to encrypted or\n * otherwise credential-grade storage, or use another secure token-preserving\n * representation.\n */\n readonly transport: HttpTransport\n /**\n * Validated Computer Backup endpoint host suffixes restored from cached\n * Partner authorization. Facades normally manage this after authorization;\n * direct raw clients can pass trusted suffixes derived from\n * `derivePartnerAllowedSuffixes()`. Do not derive this cache with\n * `JSON.stringify(authorizePartner())`, which intentionally stores a redacted\n * token placeholder instead of a usable Partner token. Rehydrating that\n * placeholder fails fast.\n */\n readonly authorizedBackupEndpointSuffixes?: readonly string[]\n}\n\n/** Optional controls for raw Computer Backup API requests. */\nexport interface BackupRawRequestOptions {\n /** Abort signal for cancelling the request. */\n readonly signal?: AbortSignal\n /** Per-request retry override. */\n readonly retry?: Partial<RetryOptions>\n}\n\nfunction backupEndpointAllowedSuffixes(\n transport: HttpTransport,\n authorizedSuffixes: readonly string[],\n): readonly string[] {\n return endpointAllowedSuffixes(transport, authorizedSuffixes, {\n missingGuard:\n 'Computer Backup endpoint requests require BackupClient.authorize() or a locked URL guard before sending Partner tokens',\n unlockedGuard:\n 'Computer Backup endpoint requests require a locked URL guard before sending Partner tokens',\n })\n}\n\n/**\n * Low-level client for Computer Backup API endpoint bindings.\n *\n * Each method maps directly to one `bz_*` HTTP call under the\n * `/api/backup/v1/` route using the `backupApiUrl` returned by Partner\n * authorization.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\nexport class BackupRawClient {\n /** @internal */\n private readonly transport: HttpTransport\n private authorizedEndpointSuffixes: readonly string[]\n\n /**\n * Creates a new BackupRawClient with the given transport.\n *\n * @param options - The constructor configuration.\n */\n constructor(options: BackupRawClientOptions) {\n this.transport = options.transport\n this.authorizedEndpointSuffixes = Array.from(options.authorizedBackupEndpointSuffixes ?? [])\n }\n\n /**\n * Replaces the validated Computer Backup endpoint host suffixes.\n *\n * @param suffixes - Host suffixes derived from Partner authorization.\n *\n * @internal\n */\n setAuthorizedEndpointSuffixes(suffixes: readonly string[]): void {\n this.authorizedEndpointSuffixes = Array.from(suffixes)\n }\n\n /**\n * Calls `bz_list_computers`.\n *\n * Uses the documented GET form. The wire response is a single JSON object\n * with a `nextComputerId` cursor and active computer backup records.\n *\n * @param backupApiUrl - The Computer Backup API base URL from `authorizePartner`.\n * @param authToken - The Partner authorization token.\n * @param request - The computer listing query parameters.\n * @param options - Optional abort and per-request retry settings.\n *\n * @returns The single-object computer backup wire response.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\n async listComputers(\n backupApiUrl: string,\n authToken: string,\n request: ListComputersRequest,\n options?: BackupRawRequestOptions,\n ): Promise<ListComputersResponse> {\n return this.getJson<ListComputersResponse>(\n backupApiUrl,\n authToken,\n 'bz_list_computers',\n {\n accountId: request.accountId,\n ...(request.startComputerId !== undefined\n ? { startComputerId: request.startComputerId }\n : {}),\n ...(request.maxComputerCount !== undefined\n ? { maxComputerCount: request.maxComputerCount }\n : {}),\n },\n options,\n )\n }\n\n /**\n * Calls `bz_delete_computer`.\n *\n * The documented wire response is a JSON array of deletion records. This\n * destructive mutation is not automatically retried or reauthorized in place.\n *\n * @param backupApiUrl - The Computer Backup API base URL from `authorizePartner`.\n * @param authToken - The Partner authorization token.\n * @param request - The computer deletion request body.\n * @param options - Optional abort and per-request retry settings.\n *\n * @returns The deleted computer backup record array.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\n async deleteComputer(\n backupApiUrl: string,\n authToken: string,\n request: DeleteComputerRequest,\n options?: BackupRawRequestOptions,\n ): Promise<DeleteComputerResponse> {\n return this.postJson<DeleteComputerResponse>(\n backupApiUrl,\n authToken,\n 'bz_delete_computer',\n {\n accountId: request.accountId,\n computerId: request.computerId,\n },\n nonRetryingMutationRequestOptions(options),\n )\n }\n\n private safeBackupApiUrl(backupApiUrl: string): string {\n return validatePartnerEndpointUrl(\n backupApiUrl,\n 'backupApiUrl',\n backupEndpointAllowedSuffixes(this.transport, this.authorizedEndpointSuffixes),\n )\n }\n\n /**\n * Sends a JSON GET request to the specified Computer Backup API endpoint.\n *\n * @param backupApiUrl - The Computer Backup API base URL.\n * @param authToken - The Partner authorization token.\n * @param endpoint - The Computer Backup API endpoint name.\n * @param query - The query-string parameters.\n * @param options - Optional abort and per-request retry settings.\n *\n * @returns The parsed JSON response.\n */\n private async getJson<T>(\n backupApiUrl: string,\n authToken: string,\n endpoint: string,\n query: QueryParams,\n options?: BackupRawRequestOptions,\n ): Promise<T> {\n const response = await this.transport.send({\n url: withQueryString(\n b2Url(this.safeBackupApiUrl(backupApiUrl), { ...BACKUP_API_V1, endpoint }),\n query,\n ),\n method: 'GET',\n headers: {\n Authorization: authToken,\n },\n ...(options?.signal !== undefined ? { signal: options.signal } : {}),\n ...(options?.retry !== undefined ? { retry: options.retry } : {}),\n })\n return response.json<T>()\n }\n\n /**\n * Sends a JSON POST request to the specified Computer Backup API endpoint.\n *\n * @param backupApiUrl - The Computer Backup API base URL.\n * @param authToken - The Partner authorization token.\n * @param endpoint - The Computer Backup API endpoint name.\n * @param body - The JSON request body.\n * @param options - Optional abort and per-request retry settings.\n *\n * @returns The parsed JSON response.\n */\n private async postJson<T>(\n backupApiUrl: string,\n authToken: string,\n endpoint: string,\n body: unknown,\n options?: BackupRawRequestOptions,\n ): Promise<T> {\n const response = await this.transport.send({\n url: b2Url(this.safeBackupApiUrl(backupApiUrl), { ...BACKUP_API_V1, endpoint }),\n method: 'POST',\n headers: {\n Authorization: authToken,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n ...(options?.signal !== undefined ? { signal: options.signal } : {}),\n ...(options?.retry !== undefined ? { retry: options.retry } : {}),\n })\n return response.json<T>()\n }\n}\n"],"mappings":";;;AAiBA,IAAM,gBAAsC;CAAE,QAAQ;CAAc,SAAS;AAAK;AAqClF,SAAS,8BACP,WACA,oBACmB;CACnB,OAAO,wBAAwB,WAAW,oBAAoB;EAC5D,cACE;EACF,eACE;CACJ,CAAC;AACH;;;;;;;;;;AAWA,IAAa,kBAAb,MAA6B;;CAE3B;CACA;;;;;;CAOA,YAAY,SAAiC;EAC3C,KAAK,YAAY,QAAQ;EACzB,KAAK,6BAA6B,MAAM,KAAK,QAAQ,oCAAoC,CAAC,CAAC;CAC7F;;;;;;;;CASA,8BAA8B,UAAmC;EAC/D,KAAK,6BAA6B,MAAM,KAAK,QAAQ;CACvD;;;;;;;;;;;;;;;;CAiBA,MAAM,cACJ,cACA,WACA,SACA,SACgC;EAChC,OAAO,KAAK,QACV,cACA,WACA,qBACA;GACE,WAAW,QAAQ;GACnB,GAAI,QAAQ,oBAAoB,KAAA,IAC5B,EAAE,iBAAiB,QAAQ,gBAAgB,IAC3C,CAAC;GACL,GAAI,QAAQ,qBAAqB,KAAA,IAC7B,EAAE,kBAAkB,QAAQ,iBAAiB,IAC7C,CAAC;EACP,GACA,OACF;CACF;;;;;;;;;;;;;;;;CAiBA,MAAM,eACJ,cACA,WACA,SACA,SACiC;EACjC,OAAO,KAAK,SACV,cACA,WACA,sBACA;GACE,WAAW,QAAQ;GACnB,YAAY,QAAQ;EACtB,GACA,kCAAkC,OAAO,CAC3C;CACF;CAEA,iBAAyB,cAA8B;EACrD,OAAO,2BACL,cACA,gBACA,8BAA8B,KAAK,WAAW,KAAK,0BAA0B,CAC/E;CACF;;;;;;;;;;;;CAaA,MAAc,QACZ,cACA,WACA,UACA,OACA,SACY;EAaZ,QAAO,MAZgB,KAAK,UAAU,KAAK;GACzC,KAAK,gBACH,MAAM,KAAK,iBAAiB,YAAY,GAAG;IAAE,GAAG;IAAe;GAAS,CAAC,GACzE,KACF;GACA,QAAQ;GACR,SAAS,EACP,eAAe,UACjB;GACA,GAAI,SAAS,WAAW,KAAA,IAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;GAClE,GAAI,SAAS,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;EACjE,CAAC,EAAA,CACe,KAAQ;CAC1B;;;;;;;;;;;;CAaA,MAAc,SACZ,cACA,WACA,UACA,MACA,SACY;EAYZ,QAAO,MAXgB,KAAK,UAAU,KAAK;GACzC,KAAK,MAAM,KAAK,iBAAiB,YAAY,GAAG;IAAE,GAAG;IAAe;GAAS,CAAC;GAC9E,QAAQ;GACR,SAAS;IACP,eAAe;IACf,gBAAgB;GAClB;GACA,MAAM,KAAK,UAAU,IAAI;GACzB,GAAI,SAAS,WAAW,KAAA,IAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;GAClE,GAAI,SAAS,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;EACjE,CAAC,EAAA,CACe,KAAQ;CAC1B;AACF"}
|
|
1
|
+
{"version":3,"file":"raw.js","names":[],"sources":["../../src/backup/raw.ts"],"sourcesContent":["import type { BackupNonIdempotentMutationEndpoint } from '../http/non-idempotent-mutations.ts'\nimport type { RetryOptions } from '../http/retry.ts'\nimport type { HttpTransport } from '../http/transport.ts'\nimport {\n endpointAllowedSuffixes,\n nonRetryingMutationRequestOptions,\n type QueryParams,\n validatePartnerEndpointUrl,\n withQueryString,\n} from '../partner/endpoint-url.ts'\nimport { type B2EndpointUrlOptions, b2Url } from '../raw/url.ts'\nimport type {\n DeleteComputerRequest,\n DeleteComputerResponse,\n DeleteComputerResult,\n ListComputersRequest,\n ListComputersResponse,\n} from '../types/backup.ts'\n\nconst BACKUP_API: B2EndpointUrlOptions = { prefix: 'api/backup', version: 'v1' }\n\n/**\n * Normalizes the `bz_delete_computer` wire response to the array-shaped schema.\n *\n * The Backblaze apidocs response *schema* declares a JSON array, but the\n * accompanying *example* shows a single bare object. This tolerates both: an\n * array passes through unchanged, and a lone object is wrapped in a\n * single-element array, so callers always receive a {@link DeleteComputerResponse}\n * regardless of which shape live B2 returns.\n *\n * @param raw - The parsed `bz_delete_computer` JSON body.\n *\n * @returns The response as the canonical array shape.\n *\n * @throws TypeError When the body is neither an array nor an object.\n */\nfunction normalizeDeleteComputerResponse(raw: unknown): DeleteComputerResponse {\n if (Array.isArray(raw)) return raw as DeleteComputerResponse\n if (raw !== null && typeof raw === 'object') return [raw as DeleteComputerResult]\n throw new TypeError('bz_delete_computer returned an unexpected response body shape')\n}\n\n/** Configuration for constructing a {@link BackupRawClient}. */\nexport interface BackupRawClientOptions {\n /**\n * The HTTP transport used to send requests.\n *\n * Computer Backup endpoint calls validate URLs against suffixes recorded by\n * Partner authorization. A rehydrated client that skips authorization must\n * use `authorizedBackupEndpointSuffixes` or a transport with a locked\n * `urlGuard`. Because Partner authorize responses redact\n * `authorizationToken` under `JSON.stringify`, durable caches should persist\n * `partnerAuthorizeResponseForPersistence(auth)` only to encrypted or\n * otherwise credential-grade storage, or use another secure token-preserving\n * representation.\n */\n readonly transport: HttpTransport\n /**\n * Validated Computer Backup endpoint host suffixes restored from cached\n * Partner authorization. Facades normally manage this after authorization;\n * direct raw clients can pass trusted suffixes derived from\n * `derivePartnerAllowedSuffixes()`. Do not derive this cache with\n * `JSON.stringify(authorizePartner())`, which intentionally stores a redacted\n * token placeholder instead of a usable Partner token. Rehydrating that\n * placeholder fails fast.\n */\n readonly authorizedBackupEndpointSuffixes?: readonly string[]\n}\n\n/** Optional controls for raw Computer Backup API requests. */\nexport interface BackupRawRequestOptions {\n /** Abort signal for cancelling the request. */\n readonly signal?: AbortSignal\n /** Per-request retry override. */\n readonly retry?: Partial<RetryOptions>\n}\n\nfunction backupEndpointAllowedSuffixes(\n transport: HttpTransport,\n authorizedSuffixes: readonly string[],\n): readonly string[] {\n return endpointAllowedSuffixes(transport, authorizedSuffixes, {\n missingGuard:\n 'Computer Backup endpoint requests require BackupClient.authorize() or a locked URL guard before sending Partner tokens',\n unlockedGuard:\n 'Computer Backup endpoint requests require a locked URL guard before sending Partner tokens',\n })\n}\n\nfunction backupEndpointUrl(backupApiUrl: string, endpoint: string): string {\n return b2Url(backupApiUrl, { ...BACKUP_API, endpoint })\n}\n\n/**\n * Low-level client for Computer Backup API endpoint bindings.\n *\n * Each method maps directly to one `bz_*` HTTP call under the\n * `/api/backup/v1/` route using the `backupApiUrl` returned by Partner\n * authorization.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\nexport class BackupRawClient {\n /** @internal */\n private readonly transport: HttpTransport\n private authorizedEndpointSuffixes: readonly string[]\n\n /**\n * Creates a new BackupRawClient with the given transport.\n *\n * @param options - The constructor configuration.\n */\n constructor(options: BackupRawClientOptions) {\n this.transport = options.transport\n this.authorizedEndpointSuffixes = Array.from(options.authorizedBackupEndpointSuffixes ?? [])\n }\n\n /**\n * Replaces the validated Computer Backup endpoint host suffixes.\n *\n * @param suffixes - Host suffixes derived from Partner authorization.\n *\n * @internal\n */\n setAuthorizedEndpointSuffixes(suffixes: readonly string[]): void {\n this.authorizedEndpointSuffixes = Array.from(suffixes)\n }\n\n /**\n * Calls `bz_list_computers`.\n *\n * Uses the documented GET form. The wire response is a single JSON object\n * with a `nextComputerId` cursor and active computer backup records.\n *\n * @param backupApiUrl - The Computer Backup API base URL from `authorizePartner`.\n * @param authToken - The Partner authorization token.\n * @param request - The computer listing query parameters.\n * @param options - Optional abort and per-request retry settings.\n *\n * @returns The single-object computer backup wire response.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\n async listComputers(\n backupApiUrl: string,\n authToken: string,\n request: ListComputersRequest,\n options?: BackupRawRequestOptions,\n ): Promise<ListComputersResponse> {\n return this.getJson<ListComputersResponse>(\n backupApiUrl,\n authToken,\n 'bz_list_computers',\n {\n accountId: request.accountId,\n ...(request.startComputerId !== undefined\n ? { startComputerId: request.startComputerId }\n : {}),\n ...(request.maxComputerCount !== undefined\n ? { maxComputerCount: request.maxComputerCount }\n : {}),\n },\n options,\n )\n }\n\n /**\n * Calls `bz_delete_computer`.\n *\n * The documented wire response is a JSON array of deletion records. This\n * destructive mutation is not automatically retried or reauthorized in place.\n *\n * @param backupApiUrl - The Computer Backup API base URL from `authorizePartner`.\n * @param authToken - The Partner authorization token.\n * @param request - The computer deletion request body.\n * @param options - Optional abort and per-request retry settings.\n *\n * @returns The deleted computer backup record array.\n *\n * @experimental Computer Backup API surface; shape may change as the Backup API docs evolve.\n */\n async deleteComputer(\n backupApiUrl: string,\n authToken: string,\n request: DeleteComputerRequest,\n options?: BackupRawRequestOptions,\n ): Promise<DeleteComputerResponse> {\n const response = await this.postNonRetryingMutationJson<unknown>(\n backupApiUrl,\n authToken,\n 'bz_delete_computer',\n {\n accountId: request.accountId,\n computerId: request.computerId,\n },\n options,\n )\n return normalizeDeleteComputerResponse(response)\n }\n\n private safeBackupApiUrl(backupApiUrl: string): string {\n return validatePartnerEndpointUrl(\n backupApiUrl,\n 'backupApiUrl',\n backupEndpointAllowedSuffixes(this.transport, this.authorizedEndpointSuffixes),\n )\n }\n\n /**\n * Sends a JSON GET request to the specified Computer Backup API endpoint.\n *\n * @param backupApiUrl - The Computer Backup API base URL.\n * @param authToken - The Partner authorization token.\n * @param endpoint - The Computer Backup API endpoint name.\n * @param query - The query-string parameters.\n * @param options - Optional abort and per-request retry settings.\n *\n * @returns The parsed JSON response.\n */\n private async getJson<T>(\n backupApiUrl: string,\n authToken: string,\n endpoint: string,\n query: QueryParams,\n options?: BackupRawRequestOptions,\n ): Promise<T> {\n const response = await this.transport.send({\n url: withQueryString(backupEndpointUrl(this.safeBackupApiUrl(backupApiUrl), endpoint), query),\n method: 'GET',\n headers: {\n Authorization: authToken,\n },\n ...(options?.signal !== undefined ? { signal: options.signal } : {}),\n ...(options?.retry !== undefined ? { retry: options.retry } : {}),\n })\n return response.json<T>()\n }\n\n /**\n * Sends a non-retrying JSON mutation POST request to the specified Computer Backup API endpoint.\n *\n * @param backupApiUrl - The Computer Backup API base URL.\n * @param authToken - The Partner authorization token.\n * @param endpoint - The Computer Backup API endpoint name.\n * @param body - The JSON request body.\n * @param options - Optional abort and per-request retry settings.\n *\n * @returns The parsed JSON response.\n */\n private async postNonRetryingMutationJson<T>(\n backupApiUrl: string,\n authToken: string,\n endpoint: BackupNonIdempotentMutationEndpoint,\n body: unknown,\n options?: BackupRawRequestOptions,\n ): Promise<T> {\n const requestOptions = nonRetryingMutationRequestOptions(options)\n const response = await this.transport.send({\n url: backupEndpointUrl(this.safeBackupApiUrl(backupApiUrl), endpoint),\n method: 'POST',\n headers: {\n Authorization: authToken,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n ...(requestOptions.signal !== undefined ? { signal: requestOptions.signal } : {}),\n ...(requestOptions.idempotent !== undefined ? { idempotent: requestOptions.idempotent } : {}),\n ...(requestOptions.retry !== undefined ? { retry: requestOptions.retry } : {}),\n })\n return response.json<T>()\n }\n}\n"],"mappings":";;;AAmBA,IAAM,aAAmC;CAAE,QAAQ;CAAc,SAAS;AAAK;;;;;;;;;;;;;;;;AAiB/E,SAAS,gCAAgC,KAAsC;CAC7E,IAAI,MAAM,QAAQ,GAAG,GAAG,OAAO;CAC/B,IAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU,OAAO,CAAC,GAA2B;CAChF,MAAM,IAAI,UAAU,+DAA+D;AACrF;AAqCA,SAAS,8BACP,WACA,oBACmB;CACnB,OAAO,wBAAwB,WAAW,oBAAoB;EAC5D,cACE;EACF,eACE;CACJ,CAAC;AACH;AAEA,SAAS,kBAAkB,cAAsB,UAA0B;CACzE,OAAO,MAAM,cAAc;EAAE,GAAG;EAAY;CAAS,CAAC;AACxD;;;;;;;;;;AAWA,IAAa,kBAAb,MAA6B;;CAE3B;CACA;;;;;;CAOA,YAAY,SAAiC;EAC3C,KAAK,YAAY,QAAQ;EACzB,KAAK,6BAA6B,MAAM,KAAK,QAAQ,oCAAoC,CAAC,CAAC;CAC7F;;;;;;;;CASA,8BAA8B,UAAmC;EAC/D,KAAK,6BAA6B,MAAM,KAAK,QAAQ;CACvD;;;;;;;;;;;;;;;;CAiBA,MAAM,cACJ,cACA,WACA,SACA,SACgC;EAChC,OAAO,KAAK,QACV,cACA,WACA,qBACA;GACE,WAAW,QAAQ;GACnB,GAAI,QAAQ,oBAAoB,KAAA,IAC5B,EAAE,iBAAiB,QAAQ,gBAAgB,IAC3C,CAAC;GACL,GAAI,QAAQ,qBAAqB,KAAA,IAC7B,EAAE,kBAAkB,QAAQ,iBAAiB,IAC7C,CAAC;EACP,GACA,OACF;CACF;;;;;;;;;;;;;;;;CAiBA,MAAM,eACJ,cACA,WACA,SACA,SACiC;EAWjC,OAAO,gCAAgC,MAVhB,KAAK,4BAC1B,cACA,WACA,sBACA;GACE,WAAW,QAAQ;GACnB,YAAY,QAAQ;EACtB,GACA,OACF,CAC+C;CACjD;CAEA,iBAAyB,cAA8B;EACrD,OAAO,2BACL,cACA,gBACA,8BAA8B,KAAK,WAAW,KAAK,0BAA0B,CAC/E;CACF;;;;;;;;;;;;CAaA,MAAc,QACZ,cACA,WACA,UACA,OACA,SACY;EAUZ,QAAO,MATgB,KAAK,UAAU,KAAK;GACzC,KAAK,gBAAgB,kBAAkB,KAAK,iBAAiB,YAAY,GAAG,QAAQ,GAAG,KAAK;GAC5F,QAAQ;GACR,SAAS,EACP,eAAe,UACjB;GACA,GAAI,SAAS,WAAW,KAAA,IAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;GAClE,GAAI,SAAS,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;EACjE,CAAC,EAAA,CACe,KAAQ;CAC1B;;;;;;;;;;;;CAaA,MAAc,4BACZ,cACA,WACA,UACA,MACA,SACY;EACZ,MAAM,iBAAiB,kCAAkC,OAAO;EAahE,QAAO,MAZgB,KAAK,UAAU,KAAK;GACzC,KAAK,kBAAkB,KAAK,iBAAiB,YAAY,GAAG,QAAQ;GACpE,QAAQ;GACR,SAAS;IACP,eAAe;IACf,gBAAgB;GAClB;GACA,MAAM,KAAK,UAAU,IAAI;GACzB,GAAI,eAAe,WAAW,KAAA,IAAY,EAAE,QAAQ,eAAe,OAAO,IAAI,CAAC;GAC/E,GAAI,eAAe,eAAe,KAAA,IAAY,EAAE,YAAY,eAAe,WAAW,IAAI,CAAC;GAC3F,GAAI,eAAe,UAAU,KAAA,IAAY,EAAE,OAAO,eAAe,MAAM,IAAI,CAAC;EAC9E,CAAC,EAAA,CACe,KAAQ;CAC1B;AACF"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const require_errors_index = require("./errors/index.cjs");
|
|
2
|
+
const require_text_codec = require("./util/text-codec.cjs");
|
|
3
|
+
const require_bucket = require("./types/bucket.cjs");
|
|
4
|
+
//#region src/bucket-validation.ts
|
|
5
|
+
var BUCKET_INFO_KEY_REGEX = new RegExp(require_bucket.BUCKET_INFO_KEY_PATTERN);
|
|
6
|
+
var CORS_RULE_NAME_REGEX = new RegExp(require_bucket.CORS_RULE_NAME_PATTERN);
|
|
7
|
+
var CORS_OPERATION_SET = new Set(require_bucket.CORS_ALLOWED_OPERATIONS);
|
|
8
|
+
/**
|
|
9
|
+
* Validates a bucketInfo record against B2's documented bucket metadata limits.
|
|
10
|
+
*
|
|
11
|
+
* @param bucketInfo - Caller-supplied bucketInfo record.
|
|
12
|
+
*
|
|
13
|
+
* @throws {@link B2BucketConfigurationError} When the record violates B2 limits.
|
|
14
|
+
*/
|
|
15
|
+
function assertValidBucketInfo(bucketInfo) {
|
|
16
|
+
if (!isObjectRecord(bucketInfo)) throw new require_errors_index.B2BucketConfigurationError("bucketInfo", "bucketInfo must be an object", "invalid_bucket_info");
|
|
17
|
+
let totalValueBytes = 0;
|
|
18
|
+
for (const [key, value] of Object.entries(bucketInfo)) {
|
|
19
|
+
const keyBytes = utf8ByteLength(key);
|
|
20
|
+
if (keyBytes < 1 || keyBytes > 50) throw new require_errors_index.B2BucketConfigurationError("bucketInfo", `bucketInfo key "${key}" must be 1-50 UTF-8 bytes`, "invalid_bucket_info");
|
|
21
|
+
if (key.startsWith("b2-")) throw new require_errors_index.B2BucketConfigurationError("bucketInfo", `bucketInfo key "${key}" must not start with reserved prefix "b2-"`, "invalid_bucket_info");
|
|
22
|
+
if (!matchesEntirePattern(BUCKET_INFO_KEY_REGEX, key)) throw new require_errors_index.B2BucketConfigurationError("bucketInfo", `bucketInfo key "${key}" must match ${require_bucket.BUCKET_INFO_KEY_PATTERN}`, "invalid_bucket_info");
|
|
23
|
+
if (typeof value !== "string") throw new require_errors_index.B2BucketConfigurationError("bucketInfo", `bucketInfo value for "${key}" must be a string`, "invalid_bucket_info");
|
|
24
|
+
totalValueBytes += utf8ByteLength(value);
|
|
25
|
+
if (totalValueBytes > 1e4) throw new require_errors_index.B2BucketConfigurationError("bucketInfo", `bucketInfo values total ${totalValueBytes} UTF-8 bytes exceeds the ${require_bucket.BUCKET_INFO_VALUES_MAX_BYTES}-byte limit`, "invalid_bucket_info");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validates CORS rules against B2's documented bucket CORS limits.
|
|
30
|
+
*
|
|
31
|
+
* @param corsRules - Caller-supplied CORS rule list.
|
|
32
|
+
*
|
|
33
|
+
* @throws {@link B2BucketConfigurationError} When any rule violates B2 limits.
|
|
34
|
+
*/
|
|
35
|
+
function assertValidCorsRules(corsRules) {
|
|
36
|
+
if (!Array.isArray(corsRules)) throw new require_errors_index.B2BucketConfigurationError("corsRules", "corsRules must be an array");
|
|
37
|
+
if (corsRules.length > 100) throw new require_errors_index.B2BucketConfigurationError("corsRules", `corsRules cannot have more than 100 rules (got ${corsRules.length})`);
|
|
38
|
+
const names = /* @__PURE__ */ new Set();
|
|
39
|
+
for (const [index, rule] of corsRules.entries()) {
|
|
40
|
+
const rulePath = `corsRules[${index}]`;
|
|
41
|
+
if (!isObjectRecord(rule)) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath} must be an object`);
|
|
42
|
+
const ruleName = readRequiredString(rule, "corsRuleName", `${rulePath}.corsRuleName`);
|
|
43
|
+
if (ruleName.length < 6 || ruleName.length > 63) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath}.corsRuleName must be 6-63 characters`);
|
|
44
|
+
if (!matchesEntirePattern(CORS_RULE_NAME_REGEX, ruleName)) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath}.corsRuleName must match ${require_bucket.CORS_RULE_NAME_PATTERN}`);
|
|
45
|
+
if (ruleName.startsWith("b2-")) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath}.corsRuleName must not start with reserved prefix "b2-"`);
|
|
46
|
+
if (names.has(ruleName)) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath}.corsRuleName must be unique within the bucket`);
|
|
47
|
+
names.add(ruleName);
|
|
48
|
+
const allowedOrigins = readRequiredStringArray(rule, "allowedOrigins", `${rulePath}.allowedOrigins`);
|
|
49
|
+
if (allowedOrigins.length === 0) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath}.allowedOrigins must contain at least one item`);
|
|
50
|
+
const allowedOperations = readRequiredStringArray(rule, "allowedOperations", `${rulePath}.allowedOperations`);
|
|
51
|
+
if (allowedOperations.length === 0) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath}.allowedOperations must contain at least one item`);
|
|
52
|
+
for (const operation of allowedOperations) if (!CORS_OPERATION_SET.has(operation)) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath}.allowedOperations contains unsupported operation "${operation}"`);
|
|
53
|
+
const allowedHeaders = readNullableStringArray(rule, "allowedHeaders", `${rulePath}.allowedHeaders`);
|
|
54
|
+
const exposeHeaders = readNullableStringArray(rule, "exposeHeaders", `${rulePath}.exposeHeaders`);
|
|
55
|
+
const maxAgeSeconds = rule["maxAgeSeconds"];
|
|
56
|
+
if (!Number.isSafeInteger(maxAgeSeconds) || maxAgeSeconds < 0) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath}.maxAgeSeconds must be a non-negative safe integer`);
|
|
57
|
+
if (maxAgeSeconds > 86400) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath}.maxAgeSeconds must be at most ${require_bucket.CORS_MAX_AGE_SECONDS_MAX}`);
|
|
58
|
+
const ruleBytes = utf8ByteLength(ruleName) + sumStringBytes(allowedOrigins) + sumStringBytes(allowedOperations) + sumStringBytes(allowedHeaders ?? []) + sumStringBytes(exposeHeaders ?? []);
|
|
59
|
+
if (ruleBytes >= 1e3) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${rulePath} size ${ruleBytes} UTF-8 bytes must be less than ${require_bucket.CORS_RULE_MAX_BYTES} bytes`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validates the bucket configuration fields common to create and update calls.
|
|
64
|
+
*
|
|
65
|
+
* @param request - Bucket create/update request.
|
|
66
|
+
*
|
|
67
|
+
* @throws {@link B2BucketConfigurationError} When any provided bucketInfo or
|
|
68
|
+
* CORS field violates B2 limits.
|
|
69
|
+
*/
|
|
70
|
+
function assertValidBucketConfiguration(request) {
|
|
71
|
+
if (request.bucketInfo !== void 0) assertValidBucketInfo(request.bucketInfo);
|
|
72
|
+
if (request.corsRules !== void 0) assertValidCorsRules(request.corsRules);
|
|
73
|
+
}
|
|
74
|
+
function isObjectRecord(value) {
|
|
75
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
76
|
+
}
|
|
77
|
+
function matchesEntirePattern(regex, value) {
|
|
78
|
+
return regex.exec(value)?.[0] === value;
|
|
79
|
+
}
|
|
80
|
+
function readRequiredString(record, key, path) {
|
|
81
|
+
const value = record[key];
|
|
82
|
+
if (typeof value !== "string") throw new require_errors_index.B2BucketConfigurationError("corsRules", `${path} must be a string`);
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
function readRequiredStringArray(record, key, path) {
|
|
86
|
+
const value = record[key];
|
|
87
|
+
if (!Array.isArray(value)) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${path} must be an array`);
|
|
88
|
+
for (const [index, item] of value.entries()) if (typeof item !== "string") throw new require_errors_index.B2BucketConfigurationError("corsRules", `${path}[${index}] must be a string`);
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
function readNullableStringArray(record, key, path) {
|
|
92
|
+
const value = record[key];
|
|
93
|
+
if (value === null || value === void 0) return null;
|
|
94
|
+
if (!Array.isArray(value)) throw new require_errors_index.B2BucketConfigurationError("corsRules", `${path} must be an array, null, or omitted`);
|
|
95
|
+
for (const [index, item] of value.entries()) if (typeof item !== "string") throw new require_errors_index.B2BucketConfigurationError("corsRules", `${path}[${index}] must be a string`);
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
function sumStringBytes(values) {
|
|
99
|
+
let total = 0;
|
|
100
|
+
for (const value of values) total += utf8ByteLength(value);
|
|
101
|
+
return total;
|
|
102
|
+
}
|
|
103
|
+
function utf8ByteLength(value) {
|
|
104
|
+
return require_text_codec.utf8Encoder.encode(value).byteLength;
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
exports.assertValidBucketConfiguration = assertValidBucketConfiguration;
|
|
108
|
+
exports.assertValidBucketInfo = assertValidBucketInfo;
|
|
109
|
+
exports.assertValidCorsRules = assertValidCorsRules;
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=bucket-validation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bucket-validation.cjs","names":[],"sources":["../src/bucket-validation.ts"],"sourcesContent":["import { B2BucketConfigurationError } from './errors/index.ts'\nimport {\n BUCKET_INFO_KEY_MAX_BYTES,\n BUCKET_INFO_KEY_MIN_BYTES,\n BUCKET_INFO_KEY_PATTERN,\n BUCKET_INFO_RESERVED_PREFIX,\n BUCKET_INFO_VALUES_MAX_BYTES,\n CORS_ALLOWED_OPERATIONS,\n CORS_MAX_AGE_SECONDS_MAX,\n CORS_RULE_MAX_BYTES,\n CORS_RULE_NAME_MAX_LENGTH,\n CORS_RULE_NAME_MIN_LENGTH,\n CORS_RULE_NAME_PATTERN,\n CORS_RULE_NAME_RESERVED_PREFIX,\n CORS_RULES_MAX_COUNT,\n type CorsRule,\n type CreateBucketRequest,\n type UpdateBucketRequest,\n} from './types/bucket.ts'\nimport { utf8Encoder } from './util/text-codec.ts'\n\nconst BUCKET_INFO_KEY_REGEX = new RegExp(BUCKET_INFO_KEY_PATTERN)\nconst CORS_RULE_NAME_REGEX = new RegExp(CORS_RULE_NAME_PATTERN)\nconst CORS_OPERATION_SET = new Set<string>(CORS_ALLOWED_OPERATIONS)\n\n/**\n * Validates a bucketInfo record against B2's documented bucket metadata limits.\n *\n * @param bucketInfo - Caller-supplied bucketInfo record.\n *\n * @throws {@link B2BucketConfigurationError} When the record violates B2 limits.\n */\nexport function assertValidBucketInfo(bucketInfo: Record<string, string>): void {\n if (!isObjectRecord(bucketInfo)) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n 'bucketInfo must be an object',\n 'invalid_bucket_info',\n )\n }\n\n let totalValueBytes = 0\n for (const [key, value] of Object.entries(bucketInfo)) {\n const keyBytes = utf8ByteLength(key)\n if (keyBytes < BUCKET_INFO_KEY_MIN_BYTES || keyBytes > BUCKET_INFO_KEY_MAX_BYTES) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo key \"${key}\" must be ${BUCKET_INFO_KEY_MIN_BYTES}-${BUCKET_INFO_KEY_MAX_BYTES} UTF-8 bytes`,\n 'invalid_bucket_info',\n )\n }\n if (key.startsWith(BUCKET_INFO_RESERVED_PREFIX)) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo key \"${key}\" must not start with reserved prefix \"${BUCKET_INFO_RESERVED_PREFIX}\"`,\n 'invalid_bucket_info',\n )\n }\n if (!matchesEntirePattern(BUCKET_INFO_KEY_REGEX, key)) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo key \"${key}\" must match ${BUCKET_INFO_KEY_PATTERN}`,\n 'invalid_bucket_info',\n )\n }\n if (typeof value !== 'string') {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo value for \"${key}\" must be a string`,\n 'invalid_bucket_info',\n )\n }\n\n totalValueBytes += utf8ByteLength(value)\n if (totalValueBytes > BUCKET_INFO_VALUES_MAX_BYTES) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo values total ${totalValueBytes} UTF-8 bytes exceeds the ${BUCKET_INFO_VALUES_MAX_BYTES}-byte limit`,\n 'invalid_bucket_info',\n )\n }\n }\n}\n\n/**\n * Validates CORS rules against B2's documented bucket CORS limits.\n *\n * @param corsRules - Caller-supplied CORS rule list.\n *\n * @throws {@link B2BucketConfigurationError} When any rule violates B2 limits.\n */\nexport function assertValidCorsRules(corsRules: readonly CorsRule[]): void {\n if (!Array.isArray(corsRules)) {\n throw new B2BucketConfigurationError('corsRules', 'corsRules must be an array')\n }\n if (corsRules.length > CORS_RULES_MAX_COUNT) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `corsRules cannot have more than ${CORS_RULES_MAX_COUNT} rules (got ${corsRules.length})`,\n )\n }\n\n const names = new Set<string>()\n for (const [index, rule] of corsRules.entries()) {\n const rulePath = `corsRules[${index}]`\n if (!isObjectRecord(rule)) {\n throw new B2BucketConfigurationError('corsRules', `${rulePath} must be an object`)\n }\n\n const ruleName = readRequiredString(rule, 'corsRuleName', `${rulePath}.corsRuleName`)\n if (\n ruleName.length < CORS_RULE_NAME_MIN_LENGTH ||\n ruleName.length > CORS_RULE_NAME_MAX_LENGTH\n ) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.corsRuleName must be ${CORS_RULE_NAME_MIN_LENGTH}-${CORS_RULE_NAME_MAX_LENGTH} characters`,\n )\n }\n if (!matchesEntirePattern(CORS_RULE_NAME_REGEX, ruleName)) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.corsRuleName must match ${CORS_RULE_NAME_PATTERN}`,\n )\n }\n if (ruleName.startsWith(CORS_RULE_NAME_RESERVED_PREFIX)) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.corsRuleName must not start with reserved prefix \"${CORS_RULE_NAME_RESERVED_PREFIX}\"`,\n )\n }\n if (names.has(ruleName)) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.corsRuleName must be unique within the bucket`,\n )\n }\n names.add(ruleName)\n\n const allowedOrigins = readRequiredStringArray(\n rule,\n 'allowedOrigins',\n `${rulePath}.allowedOrigins`,\n )\n if (allowedOrigins.length === 0) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.allowedOrigins must contain at least one item`,\n )\n }\n\n const allowedOperations = readRequiredStringArray(\n rule,\n 'allowedOperations',\n `${rulePath}.allowedOperations`,\n )\n if (allowedOperations.length === 0) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.allowedOperations must contain at least one item`,\n )\n }\n for (const operation of allowedOperations) {\n if (!CORS_OPERATION_SET.has(operation)) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.allowedOperations contains unsupported operation \"${operation}\"`,\n )\n }\n }\n\n const allowedHeaders = readNullableStringArray(\n rule,\n 'allowedHeaders',\n `${rulePath}.allowedHeaders`,\n )\n const exposeHeaders = readNullableStringArray(\n rule,\n 'exposeHeaders',\n `${rulePath}.exposeHeaders`,\n )\n const maxAgeSeconds = (rule as Record<string, unknown>)['maxAgeSeconds']\n if (!Number.isSafeInteger(maxAgeSeconds) || (maxAgeSeconds as number) < 0) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.maxAgeSeconds must be a non-negative safe integer`,\n )\n }\n if ((maxAgeSeconds as number) > CORS_MAX_AGE_SECONDS_MAX) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.maxAgeSeconds must be at most ${CORS_MAX_AGE_SECONDS_MAX}`,\n )\n }\n\n const ruleBytes =\n utf8ByteLength(ruleName) +\n sumStringBytes(allowedOrigins) +\n sumStringBytes(allowedOperations) +\n sumStringBytes(allowedHeaders ?? []) +\n sumStringBytes(exposeHeaders ?? [])\n if (ruleBytes >= CORS_RULE_MAX_BYTES) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath} size ${ruleBytes} UTF-8 bytes must be less than ${CORS_RULE_MAX_BYTES} bytes`,\n )\n }\n }\n}\n\n/**\n * Validates the bucket configuration fields common to create and update calls.\n *\n * @param request - Bucket create/update request.\n *\n * @throws {@link B2BucketConfigurationError} When any provided bucketInfo or\n * CORS field violates B2 limits.\n */\nexport function assertValidBucketConfiguration(\n request: Pick<CreateBucketRequest | UpdateBucketRequest, 'bucketInfo' | 'corsRules'>,\n): void {\n if (request.bucketInfo !== undefined) assertValidBucketInfo(request.bucketInfo)\n if (request.corsRules !== undefined) assertValidCorsRules(request.corsRules)\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction matchesEntirePattern(regex: RegExp, value: string): boolean {\n return regex.exec(value)?.[0] === value\n}\n\nfunction readRequiredString(record: Record<string, unknown>, key: string, path: string): string {\n const value = record[key]\n if (typeof value !== 'string') {\n throw new B2BucketConfigurationError('corsRules', `${path} must be a string`)\n }\n return value\n}\n\nfunction readRequiredStringArray(\n record: Record<string, unknown>,\n key: string,\n path: string,\n): readonly string[] {\n const value = record[key]\n if (!Array.isArray(value)) {\n throw new B2BucketConfigurationError('corsRules', `${path} must be an array`)\n }\n for (const [index, item] of value.entries()) {\n if (typeof item !== 'string') {\n throw new B2BucketConfigurationError('corsRules', `${path}[${index}] must be a string`)\n }\n }\n return value\n}\n\nfunction readNullableStringArray(\n record: Record<string, unknown>,\n key: string,\n path: string,\n): readonly string[] | null {\n const value = record[key]\n if (value === null || value === undefined) return null\n if (!Array.isArray(value)) {\n throw new B2BucketConfigurationError('corsRules', `${path} must be an array, null, or omitted`)\n }\n for (const [index, item] of value.entries()) {\n if (typeof item !== 'string') {\n throw new B2BucketConfigurationError('corsRules', `${path}[${index}] must be a string`)\n }\n }\n return value\n}\n\nfunction sumStringBytes(values: readonly string[]): number {\n let total = 0\n for (const value of values) total += utf8ByteLength(value)\n return total\n}\n\nfunction utf8ByteLength(value: string): number {\n return utf8Encoder.encode(value).byteLength\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CorsRule, CreateBucketRequest, UpdateBucketRequest } from './types/bucket.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates a bucketInfo record against B2's documented bucket metadata limits.
|
|
4
|
+
*
|
|
5
|
+
* @param bucketInfo - Caller-supplied bucketInfo record.
|
|
6
|
+
*
|
|
7
|
+
* @throws {@link B2BucketConfigurationError} When the record violates B2 limits.
|
|
8
|
+
*/
|
|
9
|
+
export declare function assertValidBucketInfo(bucketInfo: Record<string, string>): void;
|
|
10
|
+
/**
|
|
11
|
+
* Validates CORS rules against B2's documented bucket CORS limits.
|
|
12
|
+
*
|
|
13
|
+
* @param corsRules - Caller-supplied CORS rule list.
|
|
14
|
+
*
|
|
15
|
+
* @throws {@link B2BucketConfigurationError} When any rule violates B2 limits.
|
|
16
|
+
*/
|
|
17
|
+
export declare function assertValidCorsRules(corsRules: readonly CorsRule[]): void;
|
|
18
|
+
/**
|
|
19
|
+
* Validates the bucket configuration fields common to create and update calls.
|
|
20
|
+
*
|
|
21
|
+
* @param request - Bucket create/update request.
|
|
22
|
+
*
|
|
23
|
+
* @throws {@link B2BucketConfigurationError} When any provided bucketInfo or
|
|
24
|
+
* CORS field violates B2 limits.
|
|
25
|
+
*/
|
|
26
|
+
export declare function assertValidBucketConfiguration(request: Pick<CreateBucketRequest | UpdateBucketRequest, 'bucketInfo' | 'corsRules'>): void;
|
|
27
|
+
//# sourceMappingURL=bucket-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bucket-validation.d.ts","sourceRoot":"","sources":["../src/bucket-validation.js"],"names":[],"mappings":"AACA,OAAO,EAcL,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACzB,MAAM,mBAAmB,CAAA;AAO1B;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAkD9E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,CAqHzE;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,EAAE,YAAY,GAAG,WAAW,CAAC,GACnF,IAAI,CAGN"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CorsRule, CreateBucketRequest, UpdateBucketRequest } from './types/bucket.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates a bucketInfo record against B2's documented bucket metadata limits.
|
|
4
|
+
*
|
|
5
|
+
* @param bucketInfo - Caller-supplied bucketInfo record.
|
|
6
|
+
*
|
|
7
|
+
* @throws {@link B2BucketConfigurationError} When the record violates B2 limits.
|
|
8
|
+
*/
|
|
9
|
+
export declare function assertValidBucketInfo(bucketInfo: Record<string, string>): void;
|
|
10
|
+
/**
|
|
11
|
+
* Validates CORS rules against B2's documented bucket CORS limits.
|
|
12
|
+
*
|
|
13
|
+
* @param corsRules - Caller-supplied CORS rule list.
|
|
14
|
+
*
|
|
15
|
+
* @throws {@link B2BucketConfigurationError} When any rule violates B2 limits.
|
|
16
|
+
*/
|
|
17
|
+
export declare function assertValidCorsRules(corsRules: readonly CorsRule[]): void;
|
|
18
|
+
/**
|
|
19
|
+
* Validates the bucket configuration fields common to create and update calls.
|
|
20
|
+
*
|
|
21
|
+
* @param request - Bucket create/update request.
|
|
22
|
+
*
|
|
23
|
+
* @throws {@link B2BucketConfigurationError} When any provided bucketInfo or
|
|
24
|
+
* CORS field violates B2 limits.
|
|
25
|
+
*/
|
|
26
|
+
export declare function assertValidBucketConfiguration(request: Pick<CreateBucketRequest | UpdateBucketRequest, 'bucketInfo' | 'corsRules'>): void;
|
|
27
|
+
//# sourceMappingURL=bucket-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bucket-validation.d.ts","sourceRoot":"","sources":["../src/bucket-validation.js"],"names":[],"mappings":"AACA,OAAO,EAcL,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACzB,MAAM,mBAAmB,CAAA;AAO1B;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAkD9E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,CAqHzE;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,EAAE,YAAY,GAAG,WAAW,CAAC,GACnF,IAAI,CAGN"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { B2BucketConfigurationError } from "./errors/index.js";
|
|
2
|
+
import { utf8Encoder } from "./util/text-codec.js";
|
|
3
|
+
import { BUCKET_INFO_KEY_PATTERN, BUCKET_INFO_VALUES_MAX_BYTES, CORS_ALLOWED_OPERATIONS, CORS_MAX_AGE_SECONDS_MAX, CORS_RULE_MAX_BYTES, CORS_RULE_NAME_PATTERN } from "./types/bucket.js";
|
|
4
|
+
//#region src/bucket-validation.ts
|
|
5
|
+
var BUCKET_INFO_KEY_REGEX = new RegExp(BUCKET_INFO_KEY_PATTERN);
|
|
6
|
+
var CORS_RULE_NAME_REGEX = new RegExp(CORS_RULE_NAME_PATTERN);
|
|
7
|
+
var CORS_OPERATION_SET = new Set(CORS_ALLOWED_OPERATIONS);
|
|
8
|
+
/**
|
|
9
|
+
* Validates a bucketInfo record against B2's documented bucket metadata limits.
|
|
10
|
+
*
|
|
11
|
+
* @param bucketInfo - Caller-supplied bucketInfo record.
|
|
12
|
+
*
|
|
13
|
+
* @throws {@link B2BucketConfigurationError} When the record violates B2 limits.
|
|
14
|
+
*/
|
|
15
|
+
function assertValidBucketInfo(bucketInfo) {
|
|
16
|
+
if (!isObjectRecord(bucketInfo)) throw new B2BucketConfigurationError("bucketInfo", "bucketInfo must be an object", "invalid_bucket_info");
|
|
17
|
+
let totalValueBytes = 0;
|
|
18
|
+
for (const [key, value] of Object.entries(bucketInfo)) {
|
|
19
|
+
const keyBytes = utf8ByteLength(key);
|
|
20
|
+
if (keyBytes < 1 || keyBytes > 50) throw new B2BucketConfigurationError("bucketInfo", `bucketInfo key "${key}" must be 1-50 UTF-8 bytes`, "invalid_bucket_info");
|
|
21
|
+
if (key.startsWith("b2-")) throw new B2BucketConfigurationError("bucketInfo", `bucketInfo key "${key}" must not start with reserved prefix "b2-"`, "invalid_bucket_info");
|
|
22
|
+
if (!matchesEntirePattern(BUCKET_INFO_KEY_REGEX, key)) throw new B2BucketConfigurationError("bucketInfo", `bucketInfo key "${key}" must match ${BUCKET_INFO_KEY_PATTERN}`, "invalid_bucket_info");
|
|
23
|
+
if (typeof value !== "string") throw new B2BucketConfigurationError("bucketInfo", `bucketInfo value for "${key}" must be a string`, "invalid_bucket_info");
|
|
24
|
+
totalValueBytes += utf8ByteLength(value);
|
|
25
|
+
if (totalValueBytes > 1e4) throw new B2BucketConfigurationError("bucketInfo", `bucketInfo values total ${totalValueBytes} UTF-8 bytes exceeds the ${BUCKET_INFO_VALUES_MAX_BYTES}-byte limit`, "invalid_bucket_info");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validates CORS rules against B2's documented bucket CORS limits.
|
|
30
|
+
*
|
|
31
|
+
* @param corsRules - Caller-supplied CORS rule list.
|
|
32
|
+
*
|
|
33
|
+
* @throws {@link B2BucketConfigurationError} When any rule violates B2 limits.
|
|
34
|
+
*/
|
|
35
|
+
function assertValidCorsRules(corsRules) {
|
|
36
|
+
if (!Array.isArray(corsRules)) throw new B2BucketConfigurationError("corsRules", "corsRules must be an array");
|
|
37
|
+
if (corsRules.length > 100) throw new B2BucketConfigurationError("corsRules", `corsRules cannot have more than 100 rules (got ${corsRules.length})`);
|
|
38
|
+
const names = /* @__PURE__ */ new Set();
|
|
39
|
+
for (const [index, rule] of corsRules.entries()) {
|
|
40
|
+
const rulePath = `corsRules[${index}]`;
|
|
41
|
+
if (!isObjectRecord(rule)) throw new B2BucketConfigurationError("corsRules", `${rulePath} must be an object`);
|
|
42
|
+
const ruleName = readRequiredString(rule, "corsRuleName", `${rulePath}.corsRuleName`);
|
|
43
|
+
if (ruleName.length < 6 || ruleName.length > 63) throw new B2BucketConfigurationError("corsRules", `${rulePath}.corsRuleName must be 6-63 characters`);
|
|
44
|
+
if (!matchesEntirePattern(CORS_RULE_NAME_REGEX, ruleName)) throw new B2BucketConfigurationError("corsRules", `${rulePath}.corsRuleName must match ${CORS_RULE_NAME_PATTERN}`);
|
|
45
|
+
if (ruleName.startsWith("b2-")) throw new B2BucketConfigurationError("corsRules", `${rulePath}.corsRuleName must not start with reserved prefix "b2-"`);
|
|
46
|
+
if (names.has(ruleName)) throw new B2BucketConfigurationError("corsRules", `${rulePath}.corsRuleName must be unique within the bucket`);
|
|
47
|
+
names.add(ruleName);
|
|
48
|
+
const allowedOrigins = readRequiredStringArray(rule, "allowedOrigins", `${rulePath}.allowedOrigins`);
|
|
49
|
+
if (allowedOrigins.length === 0) throw new B2BucketConfigurationError("corsRules", `${rulePath}.allowedOrigins must contain at least one item`);
|
|
50
|
+
const allowedOperations = readRequiredStringArray(rule, "allowedOperations", `${rulePath}.allowedOperations`);
|
|
51
|
+
if (allowedOperations.length === 0) throw new B2BucketConfigurationError("corsRules", `${rulePath}.allowedOperations must contain at least one item`);
|
|
52
|
+
for (const operation of allowedOperations) if (!CORS_OPERATION_SET.has(operation)) throw new B2BucketConfigurationError("corsRules", `${rulePath}.allowedOperations contains unsupported operation "${operation}"`);
|
|
53
|
+
const allowedHeaders = readNullableStringArray(rule, "allowedHeaders", `${rulePath}.allowedHeaders`);
|
|
54
|
+
const exposeHeaders = readNullableStringArray(rule, "exposeHeaders", `${rulePath}.exposeHeaders`);
|
|
55
|
+
const maxAgeSeconds = rule["maxAgeSeconds"];
|
|
56
|
+
if (!Number.isSafeInteger(maxAgeSeconds) || maxAgeSeconds < 0) throw new B2BucketConfigurationError("corsRules", `${rulePath}.maxAgeSeconds must be a non-negative safe integer`);
|
|
57
|
+
if (maxAgeSeconds > 86400) throw new B2BucketConfigurationError("corsRules", `${rulePath}.maxAgeSeconds must be at most ${CORS_MAX_AGE_SECONDS_MAX}`);
|
|
58
|
+
const ruleBytes = utf8ByteLength(ruleName) + sumStringBytes(allowedOrigins) + sumStringBytes(allowedOperations) + sumStringBytes(allowedHeaders ?? []) + sumStringBytes(exposeHeaders ?? []);
|
|
59
|
+
if (ruleBytes >= 1e3) throw new B2BucketConfigurationError("corsRules", `${rulePath} size ${ruleBytes} UTF-8 bytes must be less than ${CORS_RULE_MAX_BYTES} bytes`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validates the bucket configuration fields common to create and update calls.
|
|
64
|
+
*
|
|
65
|
+
* @param request - Bucket create/update request.
|
|
66
|
+
*
|
|
67
|
+
* @throws {@link B2BucketConfigurationError} When any provided bucketInfo or
|
|
68
|
+
* CORS field violates B2 limits.
|
|
69
|
+
*/
|
|
70
|
+
function assertValidBucketConfiguration(request) {
|
|
71
|
+
if (request.bucketInfo !== void 0) assertValidBucketInfo(request.bucketInfo);
|
|
72
|
+
if (request.corsRules !== void 0) assertValidCorsRules(request.corsRules);
|
|
73
|
+
}
|
|
74
|
+
function isObjectRecord(value) {
|
|
75
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
76
|
+
}
|
|
77
|
+
function matchesEntirePattern(regex, value) {
|
|
78
|
+
return regex.exec(value)?.[0] === value;
|
|
79
|
+
}
|
|
80
|
+
function readRequiredString(record, key, path) {
|
|
81
|
+
const value = record[key];
|
|
82
|
+
if (typeof value !== "string") throw new B2BucketConfigurationError("corsRules", `${path} must be a string`);
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
function readRequiredStringArray(record, key, path) {
|
|
86
|
+
const value = record[key];
|
|
87
|
+
if (!Array.isArray(value)) throw new B2BucketConfigurationError("corsRules", `${path} must be an array`);
|
|
88
|
+
for (const [index, item] of value.entries()) if (typeof item !== "string") throw new B2BucketConfigurationError("corsRules", `${path}[${index}] must be a string`);
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
function readNullableStringArray(record, key, path) {
|
|
92
|
+
const value = record[key];
|
|
93
|
+
if (value === null || value === void 0) return null;
|
|
94
|
+
if (!Array.isArray(value)) throw new B2BucketConfigurationError("corsRules", `${path} must be an array, null, or omitted`);
|
|
95
|
+
for (const [index, item] of value.entries()) if (typeof item !== "string") throw new B2BucketConfigurationError("corsRules", `${path}[${index}] must be a string`);
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
function sumStringBytes(values) {
|
|
99
|
+
let total = 0;
|
|
100
|
+
for (const value of values) total += utf8ByteLength(value);
|
|
101
|
+
return total;
|
|
102
|
+
}
|
|
103
|
+
function utf8ByteLength(value) {
|
|
104
|
+
return utf8Encoder.encode(value).byteLength;
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
export { assertValidBucketConfiguration, assertValidBucketInfo, assertValidCorsRules };
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=bucket-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bucket-validation.js","names":[],"sources":["../src/bucket-validation.ts"],"sourcesContent":["import { B2BucketConfigurationError } from './errors/index.ts'\nimport {\n BUCKET_INFO_KEY_MAX_BYTES,\n BUCKET_INFO_KEY_MIN_BYTES,\n BUCKET_INFO_KEY_PATTERN,\n BUCKET_INFO_RESERVED_PREFIX,\n BUCKET_INFO_VALUES_MAX_BYTES,\n CORS_ALLOWED_OPERATIONS,\n CORS_MAX_AGE_SECONDS_MAX,\n CORS_RULE_MAX_BYTES,\n CORS_RULE_NAME_MAX_LENGTH,\n CORS_RULE_NAME_MIN_LENGTH,\n CORS_RULE_NAME_PATTERN,\n CORS_RULE_NAME_RESERVED_PREFIX,\n CORS_RULES_MAX_COUNT,\n type CorsRule,\n type CreateBucketRequest,\n type UpdateBucketRequest,\n} from './types/bucket.ts'\nimport { utf8Encoder } from './util/text-codec.ts'\n\nconst BUCKET_INFO_KEY_REGEX = new RegExp(BUCKET_INFO_KEY_PATTERN)\nconst CORS_RULE_NAME_REGEX = new RegExp(CORS_RULE_NAME_PATTERN)\nconst CORS_OPERATION_SET = new Set<string>(CORS_ALLOWED_OPERATIONS)\n\n/**\n * Validates a bucketInfo record against B2's documented bucket metadata limits.\n *\n * @param bucketInfo - Caller-supplied bucketInfo record.\n *\n * @throws {@link B2BucketConfigurationError} When the record violates B2 limits.\n */\nexport function assertValidBucketInfo(bucketInfo: Record<string, string>): void {\n if (!isObjectRecord(bucketInfo)) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n 'bucketInfo must be an object',\n 'invalid_bucket_info',\n )\n }\n\n let totalValueBytes = 0\n for (const [key, value] of Object.entries(bucketInfo)) {\n const keyBytes = utf8ByteLength(key)\n if (keyBytes < BUCKET_INFO_KEY_MIN_BYTES || keyBytes > BUCKET_INFO_KEY_MAX_BYTES) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo key \"${key}\" must be ${BUCKET_INFO_KEY_MIN_BYTES}-${BUCKET_INFO_KEY_MAX_BYTES} UTF-8 bytes`,\n 'invalid_bucket_info',\n )\n }\n if (key.startsWith(BUCKET_INFO_RESERVED_PREFIX)) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo key \"${key}\" must not start with reserved prefix \"${BUCKET_INFO_RESERVED_PREFIX}\"`,\n 'invalid_bucket_info',\n )\n }\n if (!matchesEntirePattern(BUCKET_INFO_KEY_REGEX, key)) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo key \"${key}\" must match ${BUCKET_INFO_KEY_PATTERN}`,\n 'invalid_bucket_info',\n )\n }\n if (typeof value !== 'string') {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo value for \"${key}\" must be a string`,\n 'invalid_bucket_info',\n )\n }\n\n totalValueBytes += utf8ByteLength(value)\n if (totalValueBytes > BUCKET_INFO_VALUES_MAX_BYTES) {\n throw new B2BucketConfigurationError(\n 'bucketInfo',\n `bucketInfo values total ${totalValueBytes} UTF-8 bytes exceeds the ${BUCKET_INFO_VALUES_MAX_BYTES}-byte limit`,\n 'invalid_bucket_info',\n )\n }\n }\n}\n\n/**\n * Validates CORS rules against B2's documented bucket CORS limits.\n *\n * @param corsRules - Caller-supplied CORS rule list.\n *\n * @throws {@link B2BucketConfigurationError} When any rule violates B2 limits.\n */\nexport function assertValidCorsRules(corsRules: readonly CorsRule[]): void {\n if (!Array.isArray(corsRules)) {\n throw new B2BucketConfigurationError('corsRules', 'corsRules must be an array')\n }\n if (corsRules.length > CORS_RULES_MAX_COUNT) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `corsRules cannot have more than ${CORS_RULES_MAX_COUNT} rules (got ${corsRules.length})`,\n )\n }\n\n const names = new Set<string>()\n for (const [index, rule] of corsRules.entries()) {\n const rulePath = `corsRules[${index}]`\n if (!isObjectRecord(rule)) {\n throw new B2BucketConfigurationError('corsRules', `${rulePath} must be an object`)\n }\n\n const ruleName = readRequiredString(rule, 'corsRuleName', `${rulePath}.corsRuleName`)\n if (\n ruleName.length < CORS_RULE_NAME_MIN_LENGTH ||\n ruleName.length > CORS_RULE_NAME_MAX_LENGTH\n ) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.corsRuleName must be ${CORS_RULE_NAME_MIN_LENGTH}-${CORS_RULE_NAME_MAX_LENGTH} characters`,\n )\n }\n if (!matchesEntirePattern(CORS_RULE_NAME_REGEX, ruleName)) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.corsRuleName must match ${CORS_RULE_NAME_PATTERN}`,\n )\n }\n if (ruleName.startsWith(CORS_RULE_NAME_RESERVED_PREFIX)) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.corsRuleName must not start with reserved prefix \"${CORS_RULE_NAME_RESERVED_PREFIX}\"`,\n )\n }\n if (names.has(ruleName)) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.corsRuleName must be unique within the bucket`,\n )\n }\n names.add(ruleName)\n\n const allowedOrigins = readRequiredStringArray(\n rule,\n 'allowedOrigins',\n `${rulePath}.allowedOrigins`,\n )\n if (allowedOrigins.length === 0) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.allowedOrigins must contain at least one item`,\n )\n }\n\n const allowedOperations = readRequiredStringArray(\n rule,\n 'allowedOperations',\n `${rulePath}.allowedOperations`,\n )\n if (allowedOperations.length === 0) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.allowedOperations must contain at least one item`,\n )\n }\n for (const operation of allowedOperations) {\n if (!CORS_OPERATION_SET.has(operation)) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.allowedOperations contains unsupported operation \"${operation}\"`,\n )\n }\n }\n\n const allowedHeaders = readNullableStringArray(\n rule,\n 'allowedHeaders',\n `${rulePath}.allowedHeaders`,\n )\n const exposeHeaders = readNullableStringArray(\n rule,\n 'exposeHeaders',\n `${rulePath}.exposeHeaders`,\n )\n const maxAgeSeconds = (rule as Record<string, unknown>)['maxAgeSeconds']\n if (!Number.isSafeInteger(maxAgeSeconds) || (maxAgeSeconds as number) < 0) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.maxAgeSeconds must be a non-negative safe integer`,\n )\n }\n if ((maxAgeSeconds as number) > CORS_MAX_AGE_SECONDS_MAX) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath}.maxAgeSeconds must be at most ${CORS_MAX_AGE_SECONDS_MAX}`,\n )\n }\n\n const ruleBytes =\n utf8ByteLength(ruleName) +\n sumStringBytes(allowedOrigins) +\n sumStringBytes(allowedOperations) +\n sumStringBytes(allowedHeaders ?? []) +\n sumStringBytes(exposeHeaders ?? [])\n if (ruleBytes >= CORS_RULE_MAX_BYTES) {\n throw new B2BucketConfigurationError(\n 'corsRules',\n `${rulePath} size ${ruleBytes} UTF-8 bytes must be less than ${CORS_RULE_MAX_BYTES} bytes`,\n )\n }\n }\n}\n\n/**\n * Validates the bucket configuration fields common to create and update calls.\n *\n * @param request - Bucket create/update request.\n *\n * @throws {@link B2BucketConfigurationError} When any provided bucketInfo or\n * CORS field violates B2 limits.\n */\nexport function assertValidBucketConfiguration(\n request: Pick<CreateBucketRequest | UpdateBucketRequest, 'bucketInfo' | 'corsRules'>,\n): void {\n if (request.bucketInfo !== undefined) assertValidBucketInfo(request.bucketInfo)\n if (request.corsRules !== undefined) assertValidCorsRules(request.corsRules)\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction matchesEntirePattern(regex: RegExp, value: string): boolean {\n return regex.exec(value)?.[0] === value\n}\n\nfunction readRequiredString(record: Record<string, unknown>, key: string, path: string): string {\n const value = record[key]\n if (typeof value !== 'string') {\n throw new B2BucketConfigurationError('corsRules', `${path} must be a string`)\n }\n return value\n}\n\nfunction readRequiredStringArray(\n record: Record<string, unknown>,\n key: string,\n path: string,\n): readonly string[] {\n const value = record[key]\n if (!Array.isArray(value)) {\n throw new B2BucketConfigurationError('corsRules', `${path} must be an array`)\n }\n for (const [index, item] of value.entries()) {\n if (typeof item !== 'string') {\n throw new B2BucketConfigurationError('corsRules', `${path}[${index}] must be a string`)\n }\n }\n return value\n}\n\nfunction readNullableStringArray(\n record: Record<string, unknown>,\n key: string,\n path: string,\n): readonly string[] | null {\n const value = record[key]\n if (value === null || value === undefined) return null\n if (!Array.isArray(value)) {\n throw new B2BucketConfigurationError('corsRules', `${path} must be an array, null, or omitted`)\n }\n for (const [index, item] of value.entries()) {\n if (typeof item !== 'string') {\n throw new B2BucketConfigurationError('corsRules', `${path}[${index}] must be a string`)\n }\n }\n return value\n}\n\nfunction sumStringBytes(values: readonly string[]): number {\n let total = 0\n for (const value of values) total += utf8ByteLength(value)\n return total\n}\n\nfunction utf8ByteLength(value: string): number {\n return utf8Encoder.encode(value).byteLength\n}\n"],"mappings":"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"}
|