@cloudbitmaps/s3 0.21.0 → 0.23.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/README.md +55 -15
- package/dist/backend.d.ts +64 -24
- package/dist/index.js +528 -173
- package/dist/index.js.map +4 -4
- package/package.json +4 -3
- package/dist/aws-errors.d.ts +0 -13
- package/dist/bounded-body.d.ts +0 -15
- package/dist/client-probe.d.ts +0 -36
- package/dist/defined.d.ts +0 -8
- package/dist/read-timeout.d.ts +0 -11
- package/dist/registry.d.ts +0 -118
- package/dist/s3-errors.d.ts +0 -66
- package/dist/scrub-error.d.ts +0 -12
- package/dist/send-once.d.ts +0 -23
- package/dist/socket-advisory.d.ts +0 -57
- package/dist/storage.d.ts +0 -100
package/README.md
CHANGED
|
@@ -14,13 +14,23 @@ service to run.
|
|
|
14
14
|
pnpm add @cloudbitmaps/roaring @cloudbitmaps/s3
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Install the same version of every `@cloudbitmaps/*` package: they release together, and each pins the exact version of `core` it was built with.
|
|
18
|
+
|
|
19
|
+
`roaring` needs its one install script to fetch its native binary, and newer clients run a dependency's script only if you allow it. Allow it before you install:
|
|
20
|
+
|
|
21
|
+
- **npm 12:** add `{ "allowScripts": { "roaring": true } }` to your `package.json`. Without it the install exits 0 and the package throws at `import`. npm 11 runs the script but warns until you allow it the same way.
|
|
22
|
+
- **pnpm 11 and later:** put `allowBuilds: { roaring: true }` in a `pnpm-workspace.yaml` beside your `package.json`. Those versions no longer read the `package.json` `pnpm` key. Without it the install exits 1 with `ERR_PNPM_IGNORED_BUILDS`.
|
|
23
|
+
- **pnpm 10:** add `{ "pnpm": { "onlyBuiltDependencies": ["roaring"] } }` to your `package.json`. Without it the install exits 0 and the package throws at `import`.
|
|
24
|
+
- **pnpm 9:** nothing extra.
|
|
25
|
+
|
|
26
|
+
[Details](https://github.com/cloudbitmaps/cloudbitmaps/blob/main/docs/guide/getting-started.md#cannot-find-module-buildreleaseroaringnode-after-a-successful-install).
|
|
18
27
|
|
|
19
28
|
`@aws-sdk/client-s3` (`>=3.700.0 <4`) is a real dependency of this package, so installing it is the whole step.
|
|
20
29
|
`@cloudbitmaps/core`, the engine underneath, arrives with it and you never name it.
|
|
21
30
|
|
|
22
31
|
## Use
|
|
23
32
|
|
|
33
|
+
<!-- typecheck -->
|
|
24
34
|
```ts
|
|
25
35
|
import { CloudRoaring } from '@cloudbitmaps/roaring';
|
|
26
36
|
import { S3Storage } from '@cloudbitmaps/s3';
|
|
@@ -42,15 +52,21 @@ It builds its own client from your usual AWS credentials. Any other key is refus
|
|
|
42
52
|
| `client` | your own `S3Client`; it carries its own region, endpoint and credentials |
|
|
43
53
|
| `region`, `endpoint`, `pathStyle`, `credentials` | build a client for you, such as one for MinIO; refused beside `client` |
|
|
44
54
|
| `maxSockets` | most sockets the client you do not pass opens at once, for `https` and plain `http` alike; a positive integer, default 128 (the SDK's own is 50; if a future SDK changes its handler, the built client keeps the SDK's own limit); refused beside `client` |
|
|
45
|
-
| `partBytes`, `maxObjectBytes` | multipart sizing: part size (default 8 MiB; the upload buffers one part at a time) and the largest object (about
|
|
55
|
+
| `partBytes`, `maxObjectBytes` | multipart sizing: part size (default 8 MiB; the upload buffers one part at a time and copies it into one body to send it, so it holds up to about twice the part size at a flush, 16 MiB at the default, derived from the driver's code) and the largest object (about 78 GiB by default, up to S3's 5 TiB). A cap above 5 TiB, or a part above S3's 5 GiB part limit, is a `ValidationError` |
|
|
46
56
|
| `readTimeoutMs` | how long one read (a `GetObject` or `HeadObject`, its body included) may take before it throws `TransientError` and the store retries it; `0`, the default, sets no timeout. Writes are not timed |
|
|
47
57
|
| `conditionalDelete` | whether the store applies `If-Match` on a `DeleteObject`: the registry then removes a deleted row rather than leaving a tombstone, and the storage half deletes an erasure's holder only while it is the object the erasure read, by its ETag. On by default when the host the client resolves is an AWS S3 host, however its endpoint was set (`endpoint`, `AWS_ENDPOINT_URL_S3`, `AWS_ENDPOINT_URL`, the shared config file); off for any other host: set it for an S3-compatible store only once you know it applies the header (MinIO ignores it) |
|
|
48
58
|
|
|
49
59
|
## Before production
|
|
50
60
|
|
|
61
|
+
- **Run `await store.preflight()` once before the first load.** It reads the registry, writes one probe object, writes the
|
|
62
|
+
same key again (which must be refused), reads it back, lists it, deletes it and checks the lifecycle rules, with
|
|
63
|
+
`s3:ListBucket`, `s3:GetObject`, `s3:PutObject` and `s3:DeleteObject`, and `s3:GetLifecycleConfiguration` for the last. It does
|
|
64
|
+
not check `s3:AbortMultipartUpload`, the registry's `If-Match`, or a read after a delete, among others: [the full list](https://github.com/cloudbitmaps/cloudbitmaps/blob/main/docs/guide/production.md#what-preflight-does-not-check). It
|
|
65
|
+
shows an SDK of 3.640.0 or older as a `write-once` problem, and cannot see one from 3.641.0 to 3.699.0, so the floor below stays yours to check. The probe sits at `<prefix>cbm.due.preflight/`, outside your application's namespaces: a grant scoped per namespace shows as a `write` problem, and as a `registry` problem too when the registry grant is scoped per namespace.
|
|
51
66
|
- **The bucket must honor conditional writes**: `If-None-Match: *` for a write-once generation and `If-Match` for the
|
|
52
67
|
pointer. AWS S3 does, and the test suite runs against MinIO. Another S3-compatible service that ignores the headers
|
|
53
|
-
turns write-once into overwrite, so check yours before you depend on it.
|
|
68
|
+
turns write-once into overwrite, so check yours before you depend on it. It must also be strongly consistent: a read
|
|
69
|
+
or a listing made after a write or a delete returns sees it. AWS S3 is.
|
|
54
70
|
- **Use `@aws-sdk/client-s3` 3.700.0 or later** if you pin the SDK or pass your own `client`. This floor is a
|
|
55
71
|
correctness floor, not a preference. An SDK sends only the conditional headers it models, and drops one it does
|
|
56
72
|
not, without an error. `If-None-Match`, which write-once is built on, is modelled from 3.641.0: measured against
|
|
@@ -66,18 +82,26 @@ It builds its own client from your usual AWS credentials. Any other key is refus
|
|
|
66
82
|
`registry/` prefix. An expired registry row takes the segment's pointer with it, and the token every publish is fenced
|
|
67
83
|
on, so its generations look unreferenced; an expired generation makes a live segment unreadable
|
|
68
84
|
([bucket lifecycle](https://github.com/cloudbitmaps/cloudbitmaps/blob/main/docs/guide/production.md#bucket-lifecycle)).
|
|
69
|
-
- **
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
- **Check the bucket's lifecycle rules with `await backend.checkLifecycle()`.** It reads the configuration with one
|
|
86
|
+
request (bounded by `readTimeoutMs`, when set) and answers `ok`, `problem` (an enabled, untagged rule over your prefix that expires current objects or moves
|
|
87
|
+
them to `GLACIER`, `DEEP_ARCHIVE` or a class it does not know) or `unchecked`. It needs `s3:GetLifecycleConfiguration`, which a load never uses and
|
|
88
|
+
`store.preflight()`'s lifecycle step does: without it the answer is `unchecked`, naming the permission. A missing abort-incomplete-upload rule is
|
|
89
|
+
advice in `detail`, never a `problem`
|
|
90
|
+
([check the lifecycle rules from code](https://github.com/cloudbitmaps/cloudbitmaps/blob/main/docs/guide/production.md#check-the-lifecycle-rules-from-code)).
|
|
91
|
+
- **A transient failure of a write throws `TransientError`, and the write may or may not have landed.** Re-run the call,
|
|
92
|
+
or check `store.generations(ref)`. The SDK's own retry is off for conditional writes, so a write that landed is not
|
|
93
|
+
reported as a conflict. The driver [sends each registry write
|
|
94
|
+
once](https://github.com/cloudbitmaps/cloudbitmaps/blob/main/docs/guide/correctness.md#sdk-retry); a load that gets no
|
|
95
|
+
answer [reads the row and sends a fresh compare-and-swap from
|
|
96
|
+
it](https://github.com/cloudbitmaps/cloudbitmaps/blob/main/docs/guide/correctness.md#row-write-settled), at most three
|
|
97
|
+
times. A generation's object is sent again, up to three more times, only after a `503` (`SlowDown` is the usual one) or a bare `429` ([why that is
|
|
98
|
+
safe](https://github.com/cloudbitmaps/cloudbitmaps/blob/main/docs/guide/correctness.md#object-write-resent));
|
|
99
|
+
it carries a random id in its user metadata (`x-amz-meta-cbwid`), so a first send that landed is told from another
|
|
100
|
+
writer's object. A load whose row writes all go unanswered throws `TransientError` and keeps its object. The waits
|
|
101
|
+
before an object's re-sends use real timers: the store's injected clock does not reach them, and `S3Storage` takes no
|
|
102
|
+
clock, so a test that throttles one object write three times waits about 3.5 s at most (the publish's waits before a
|
|
103
|
+
fresh row write do use the store's clock). **A bare `429`** (AWS S3 does not send one; some S3-compatible services do)
|
|
104
|
+
**is a throttle, handled exactly as a `503` is**, and one that ends a call is a `TransientError`.
|
|
81
105
|
- **Reads can be timed, and writes are not.** Nothing is timed unless you set `readTimeoutMs`. Set, a read that has
|
|
82
106
|
not finished in that many ms throws `TransientError`, and the store runs it again; AWS's guidance is to retry a GET
|
|
83
107
|
of under 512 KB after about 2 seconds. The timer covers the body as well, so a connection that sends its headers and
|
|
@@ -87,6 +111,22 @@ It builds its own client from your usual AWS credentials. Any other key is refus
|
|
|
87
111
|
For the writes and listings, give your client a timeout of its own.
|
|
88
112
|
|
|
89
113
|
The [production checklist](https://github.com/cloudbitmaps/cloudbitmaps/blob/main/docs/guide/production.md) covers each item, with a sample IAM policy, and the ones every backend shares: a request timeout on your client for the requests the library does not time, backups of the data and the registry, and a schedule for your loads.
|
|
114
|
+
- **The driver redacts the credentials it recognises from an error before it throws it.** The AWS SDK keeps the
|
|
115
|
+
response it got on the error (`$response`), and through its socket the request it sent, with the signed
|
|
116
|
+
`Authorization` header and `x-amz-security-token`. It also puts every field of the error body on the error, and S3
|
|
117
|
+
quotes the request back when it refuses it: its signature and canonical request (`SignatureDoesNotMatch`), or its
|
|
118
|
+
session token (`Token-0`, on `ExpiredToken` and `InvalidToken`). In the error the driver throws, the value under a
|
|
119
|
+
credential header or field name is replaced by `[redacted]` (`authorization`, `x-amz-security-token`,
|
|
120
|
+
`x-amz-s3session-token`, an SSE-C key, `SignatureProvided`, `CanonicalRequest`, `Token-0` and the like; the access
|
|
121
|
+
key id S3 names is kept), and so are the value beside a credential's name in a list or a record (a table row that
|
|
122
|
+
parses to `{ th, td }` with one name and one value, a `{ name, value }` pair, `ArgumentName` and `ArgumentValue`), a credential header line, a SigV4 signature
|
|
123
|
+
(`Signature=`, `X-Amz-Signature=`) and a session token or SSE-C key after its header name, anywhere in its text. A
|
|
124
|
+
credential in another form is not recognised, and a text too large for these patterns is replaced whole. An SDK
|
|
125
|
+
error that carries its transport or shows a credential, and every error it reaches (its `cause` chain, an
|
|
126
|
+
`AggregateError`'s `errors`), is thrown as a copy: a native error with the same prototype, name, message, stack,
|
|
127
|
+
`$metadata`, `$fault`, `Code` and request id, so classification is unchanged, without `$response`, the body the SDK
|
|
128
|
+
could not parse (`$responseBodyText`), any function or accessor property, or anything nested deeper than ten levels.
|
|
129
|
+
The live error is never changed, and an error with no transport and no credential in it is thrown as it is.
|
|
90
130
|
|
|
91
131
|
## Documentation
|
|
92
132
|
|
package/dist/backend.d.ts
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `S3Storage` — the S3 backend as one object: the generations and the pointer, in one bucket, stated once.
|
|
3
|
-
*
|
|
4
|
-
* Replaces two constructor calls that each repeated `client`, `bucket` and `prefix`. Repeating them is how
|
|
5
|
-
* they come apart: point the registry at one prefix and the objects at another and the store answers *empty*
|
|
6
|
-
* rather than *misconfigured*, which is the hardest kind of wrong answer to debug. Here the location is written
|
|
7
|
-
* once and shared, so the mismatch cannot be expressed.
|
|
8
|
-
*
|
|
9
|
-
* **It will build a client for you**, which is the common case — `new S3Storage({ bucket })` picks up the
|
|
10
|
-
* ambient credential chain and region exactly as the SDK would. Pass `client` instead when you need a
|
|
11
|
-
* credential chain the SDK cannot infer (SSO, an assumed role, a custom retry strategy); pass `endpoint` +
|
|
12
|
-
* `pathStyle` + `credentials` for an S3-compatible store (MinIO, Ceph, R2). Both halves stay reachable as `.storage` and
|
|
13
|
-
* `.registry` for anyone wiring something the facade does not cover. `maxObjectBytes` and `partBytes` size the
|
|
14
|
-
* multipart upload, and `readTimeoutMs`, when set, bounds each read both halves make.
|
|
15
|
-
*/
|
|
16
1
|
import { STORAGE_BACKEND } from '@cloudbitmaps/core/driver-kit';
|
|
17
|
-
import type { IRegistryDriver, IStorageDriver, StorageBackend } from '@cloudbitmaps/core/driver-kit';
|
|
2
|
+
import type { IRegistryDriver, IStorageDriver, LifecycleVerdict, StorageBackend } from '@cloudbitmaps/core/driver-kit';
|
|
18
3
|
import { S3Client } from '@aws-sdk/client-s3';
|
|
19
4
|
import type { IMetricsSink } from '@cloudbitmaps/core/driver-kit';
|
|
5
|
+
/**
|
|
6
|
+
* What {@link S3Storage} takes: a `bucket`, and either a `client` or the five settings that build one (`region`,
|
|
7
|
+
* `endpoint`, `pathStyle`, `credentials`, `maxSockets`), never both.
|
|
8
|
+
*/
|
|
20
9
|
export interface S3StorageOptions {
|
|
21
10
|
/** Target bucket (must already exist). */
|
|
22
11
|
readonly bucket: string;
|
|
23
|
-
/** Optional key prefix under which everything lives — generations and the registry alike. */
|
|
12
|
+
/** Optional key prefix under which everything lives — generations and the registry alike. A string, else `ValidationError`. */
|
|
24
13
|
readonly prefix?: string;
|
|
25
14
|
/**
|
|
26
15
|
* A constructed client. Supply one for a credential chain the SDK cannot infer; otherwise one is built. Its retry
|
|
@@ -28,14 +17,27 @@ export interface S3StorageOptions {
|
|
|
28
17
|
* configured to do: a registry row once, and a generation's object again only after a throttle, under its own backoff.
|
|
29
18
|
*/
|
|
30
19
|
readonly client?: S3Client;
|
|
31
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Region for the client built when `client` is absent (refused beside `client`): a non-empty string, else
|
|
22
|
+
* `ValidationError`. Falls back to the SDK's own resolution.
|
|
23
|
+
*/
|
|
32
24
|
readonly region?: string;
|
|
33
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Endpoint for an S3-compatible store (MinIO, Ceph, R2): an http or https URL, since an empty one would send
|
|
27
|
+
* requests to AWS, else `ValidationError`. Refused beside `client`, which carries its own.
|
|
28
|
+
*/
|
|
34
29
|
readonly endpoint?: string;
|
|
35
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Path-style addressing, which most S3-compatible stores require: a boolean, else `ValidationError`. Refused beside
|
|
32
|
+
* `client`, which carries its own.
|
|
33
|
+
*/
|
|
36
34
|
readonly pathStyle?: boolean;
|
|
37
35
|
/**
|
|
38
|
-
* Static credentials, for the S3-compatible stores that issue them (MinIO, Ceph, R2)
|
|
36
|
+
* Static credentials, for the S3-compatible stores that issue them (MinIO, Ceph, R2): non-empty `accessKeyId` and
|
|
37
|
+
* `secretAccessKey` strings and an optional `sessionToken`, else `ValidationError`, which never prints a value. No other
|
|
38
|
+
* key is taken except `$source`, `credentialScope`, `accountId` and `expiration`, which are tolerated and not read:
|
|
39
|
+
* the SDK stamps `$source` on a credentials object it is given, so one used for two stores carries it, and a credential
|
|
40
|
+
* provider's result carries the others. A provider function is not taken here; put it on an `S3Client` given as `client`.
|
|
39
41
|
*
|
|
40
42
|
* On AWS itself, leave this unset — the SDK's own chain (instance role, SSO, environment, profile) is what
|
|
41
43
|
* you want, and hard-coding keys to reach it would be a downgrade. It exists because the alternative for a
|
|
@@ -53,12 +55,13 @@ export interface S3StorageOptions {
|
|
|
53
55
|
* two-operand `intersect`. Only this limit differs from the SDK's own client: its request handler, keep-alive,
|
|
54
56
|
* timeouts and retry are the SDK's. If a future SDK changes its handler, the built client keeps the SDK's own
|
|
55
57
|
* limit rather than failing to build. A positive safe integer. Release a built client's sockets with
|
|
56
|
-
* `store.client.destroy()`. Refused beside `client`, which carries its own request handler.
|
|
57
|
-
*
|
|
58
|
+
* `store.client.destroy()`. Refused beside `client`, which carries its own request handler. An `eraseSubject`
|
|
59
|
+
* at its default `concurrency` of 8 holds up to 64 requests open, which the default of 128 covers; raise
|
|
60
|
+
* `maxSockets`, or lower `concurrency`, to fit a larger one.
|
|
58
61
|
*/
|
|
59
62
|
readonly maxSockets?: number;
|
|
60
63
|
/**
|
|
61
|
-
* Largest object the backend will write and advertise. Default = `partBytes × 10,000` (≈
|
|
64
|
+
* Largest object the backend will write and advertise. Default = `partBytes × 10,000` (≈ 78 GiB at the default
|
|
62
65
|
* 8 MiB part) — the honest ceiling reachable within S3's 10,000-part limit. Set it higher and `partBytes`
|
|
63
66
|
* auto-grows so 10,000 parts still cover it (raising peak write memory to ~one part); up to the 5 TiB S3 max, and a
|
|
64
67
|
* larger cap, or a `partBytes` above S3's 5 GiB part limit, is refused with `ValidationError`.
|
|
@@ -115,6 +118,21 @@ export declare const S3_STORAGE_OPTION_KEYS: readonly ["bucket", "prefix", "clie
|
|
|
115
118
|
* the agents it made.
|
|
116
119
|
*/
|
|
117
120
|
export declare function limitSockets(client: S3Client, maxSockets: number): void;
|
|
121
|
+
/**
|
|
122
|
+
* `S3Storage` — the S3 backend as one object: the generations and the pointer, in one bucket, stated once.
|
|
123
|
+
*
|
|
124
|
+
* Replaces two constructor calls that each repeated `client`, `bucket` and `prefix`. Repeating them is how
|
|
125
|
+
* they come apart: point the registry at one prefix and the objects at another and the store answers *empty*
|
|
126
|
+
* rather than *misconfigured*, which is the hardest kind of wrong answer to debug. Here the location is written
|
|
127
|
+
* once and shared, so the mismatch cannot be expressed.
|
|
128
|
+
*
|
|
129
|
+
* **It will build a client for you**, which is the common case — `new S3Storage({ bucket })` picks up the
|
|
130
|
+
* ambient credential chain and region exactly as the SDK would. Pass `client` instead when you need a
|
|
131
|
+
* credential chain the SDK cannot infer (SSO, an assumed role, a custom retry strategy); pass `endpoint` +
|
|
132
|
+
* `pathStyle` + `credentials` for an S3-compatible store (MinIO, Ceph, R2). Both halves stay reachable as `.storage`
|
|
133
|
+
* and `.registry` for anyone wiring something the facade does not cover. `maxObjectBytes` and `partBytes` size the
|
|
134
|
+
* multipart upload, and `readTimeoutMs`, when set, bounds each read both halves make (listings are not timed).
|
|
135
|
+
*/
|
|
118
136
|
export declare class S3Storage implements StorageBackend {
|
|
119
137
|
/** Cross-bundle brand, stamped non-enumerably in the constructor so a spread cannot carry it. */
|
|
120
138
|
readonly [STORAGE_BACKEND]: true;
|
|
@@ -123,10 +141,32 @@ export declare class S3Storage implements StorageBackend {
|
|
|
123
141
|
/** The client both halves share — built here unless one was supplied. */
|
|
124
142
|
readonly client: S3Client;
|
|
125
143
|
private readonly sockets;
|
|
144
|
+
private readonly bucket;
|
|
145
|
+
/** The key prefix every object sits under: `''`, or `trimmed/`. */
|
|
146
|
+
private readonly keyPrefix;
|
|
147
|
+
private readonly readTimeoutMs;
|
|
148
|
+
/**
|
|
149
|
+
* Builds the S3 backend over one bucket, which `bucket` names. Pass `client`, or `region`, `endpoint`, `pathStyle`,
|
|
150
|
+
* `credentials` and `maxSockets` to have one built, not both. `prefix` puts every object under a key prefix. Throws
|
|
151
|
+
* `ValidationError` for an unknown option, no bucket, a `now` that is not a function, a client setting beside
|
|
152
|
+
* `client`, a `maxSockets` (with no `client`) that is not a positive integer, a `partBytes` or `maxObjectBytes` that
|
|
153
|
+
* is not a positive integer, a `conditionalDelete` that is not a boolean, a `region` or `endpoint` that is empty or not a string, a `pathStyle` that
|
|
154
|
+
* is not a boolean, `credentials` that are not complete, or an invalid `prefix` or `readTimeoutMs`.
|
|
155
|
+
*/
|
|
126
156
|
constructor(options: S3StorageOptions);
|
|
127
157
|
/**
|
|
128
158
|
* Called by a store given a metrics sink. After the first read, a client whose socket pool is smaller than twice the
|
|
129
159
|
* default window (64) sends the sink one `advisory` event, once; see the production guide's socket sizing.
|
|
130
160
|
*/
|
|
131
161
|
attachMetrics(sink: IMetricsSink): void;
|
|
162
|
+
/**
|
|
163
|
+
* Reads the bucket's lifecycle configuration (one `GetBucketLifecycleConfiguration` request, plus the SDK's own
|
|
164
|
+
* retries, bounded by `readTimeoutMs` when it is set) and reports a rule that would expire, or move to GLACIER,
|
|
165
|
+
* DEEP_ARCHIVE or a class it does not know, the current objects under this store's prefix, generations and registry
|
|
166
|
+
* rows alike. Needs `s3:GetLifecycleConfiguration`, which a load never uses (`store.preflight()`'s
|
|
167
|
+
* lifecycle step does): without it the
|
|
168
|
+
* answer is `unchecked`, naming the permission. A missing rule that aborts incomplete multipart uploads is advice in
|
|
169
|
+
* `detail`, not a problem. Never rejects; a `detail` carries no credential. Rule reading: see the production guide.
|
|
170
|
+
*/
|
|
171
|
+
checkLifecycle(): Promise<LifecycleVerdict>;
|
|
132
172
|
}
|