@cloudbitmaps/s3 0.23.0 → 0.24.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 +3 -10
- package/dist/backend.d.ts +2 -1
- package/dist/index.js.map +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,15 +16,6 @@ pnpm add @cloudbitmaps/roaring @cloudbitmaps/s3
|
|
|
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
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).
|
|
27
|
-
|
|
28
19
|
`@aws-sdk/client-s3` (`>=3.700.0 <4`) is a real dependency of this package, so installing it is the whole step.
|
|
29
20
|
`@cloudbitmaps/core`, the engine underneath, arrives with it and you never name it.
|
|
30
21
|
|
|
@@ -75,7 +66,9 @@ It builds its own client from your usual AWS credentials. Any other key is refus
|
|
|
75
66
|
omits it, so a fenced row write goes out unconditionally and can land over a concurrent writer's. This package's
|
|
76
67
|
own range never resolves below 3.700.0. The registry also checks, before its first request, that the client it is given sends `If-None-Match` and
|
|
77
68
|
`If-Match` on `PutObject`, and refuses a registry write it would send without its precondition; a client that does
|
|
78
|
-
not send `If-Match` on `DeleteObject` turns `conditionalDelete` off.
|
|
69
|
+
not send `If-Match` on `DeleteObject` turns `conditionalDelete` off. From 3.723.0 the SDK sends a part of 2 MiB or more
|
|
70
|
+
(`Expect: 100-continue`) on its own connection outside `maxSockets`; before it those uploads share the pool, so the
|
|
71
|
+
cap also limits them (expected, not measured: a lower upload rate at a low cap, nothing worse).
|
|
79
72
|
- **Grant `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject`, `s3:AbortMultipartUpload` and `s3:ListBucket`.** Without
|
|
80
73
|
`s3:ListBucket`, S3 answers a missing key with `403` instead of `404`.
|
|
81
74
|
- **Add a lifecycle rule that aborts incomplete multipart uploads**, and never one that expires current objects or the
|
package/dist/backend.d.ts
CHANGED
|
@@ -111,7 +111,8 @@ export declare const S3_STORAGE_OPTION_KEYS: readonly ["bucket", "prefix", "clie
|
|
|
111
111
|
/**
|
|
112
112
|
* Cap the sockets the SDK's own request handler opens, and change nothing else about it. The handler stays the SDK's
|
|
113
113
|
* default one, so its other defaults hold: its defaults-mode connection timeout, keep-alive, and a request that
|
|
114
|
-
* sends `Expect: 100-continue` (a part of 2 MiB or more) still goes on its own connection outside this pool
|
|
114
|
+
* sends `Expect: 100-continue` (a part of 2 MiB or more) still goes on its own connection outside this pool (SDK 3.723.0
|
|
115
|
+
* and later; before it those uploads share the pool, so the cap limits them too). Only the
|
|
115
116
|
* `maxSockets` of its two pooled agents is set, before the first request goes out. The handler makes its agents on
|
|
116
117
|
* that first request (the http one even later, per request, so a first burst to a plain-http endpoint would get one
|
|
117
118
|
* pool each), so the first request first runs an aborted one through it, which makes them, then sets the limit on
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/backend.ts", "../src/defined.ts", "../src/lifecycle.ts", "../src/aws-errors.ts", "../src/read-timeout.ts", "../src/scrub-error.ts", "../src/storage.ts", "../src/s3-errors.ts", "../src/send-once.ts", "../src/bounded-body.ts", "../src/client-probe.ts", "../src/registry.ts", "../src/socket-advisory.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n STORAGE_BACKEND,\n ValidationError,\n brandAsBackend,\n normalizeObjectPrefix,\n prefixPart,\n refuseUnknownOptions,\n requireBool,\n requireNonEmptyString,\n requirePositiveInt,\n} from '@cloudbitmaps/core/driver-kit';\nimport type {\n IRegistryDriver,\n IStorageDriver,\n LifecycleVerdict,\n StorageBackend,\n} from '@cloudbitmaps/core/driver-kit';\nimport { S3Client } from '@aws-sdk/client-s3';\nimport { defined } from './defined';\nimport { checkS3Lifecycle } from './lifecycle';\nimport { resolveReadTimeoutMs } from './read-timeout';\nimport { S3StorageDriver } from './storage';\nimport { S3RegistryDriver } from './registry';\nimport { SocketAdvisory, type PooledHandler } from './socket-advisory';\nimport type { IMetricsSink } from '@cloudbitmaps/core/driver-kit';\n\n/**\n * Refuse `credentials` that are present and not an object of non-empty `accessKeyId` and `secretAccessKey` strings and\n * an optional `sessionToken`, naming the key and never its value.\n */\nfunction checkCredentials(credentials: unknown): void {\n if (credentials === undefined) return;\n if (typeof credentials === 'function') {\n throw new ValidationError(\n 'S3Storage: `credentials` must be an object of `accessKeyId` and `secretAccessKey`, not a provider function; ' +\n 'pass a provider on an S3Client given as `client`',\n );\n }\n // The SDK stamps `$source` on the object it is given, and a credential provider's result carries the identity fields\n // below, so a bag that was used before, or came from a provider, still takes. Those four are tolerated, not read.\n const keys = [\n 'accessKeyId',\n 'secretAccessKey',\n 'sessionToken',\n '$source',\n 'credentialScope',\n 'accountId',\n 'expiration',\n ] as const;\n refuseUnknownOptions('S3Storage credentials', credentials, keys);\n const given = credentials as Record<string, unknown>;\n // Only the field and the type of a wrong value are named, never the value: it may be a secret.\n const needText = (key: string, value: unknown): void => {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new ValidationError(\n `S3Storage: \\`credentials.${key}\\` must be a non-empty string; got ${typeof value === 'string' ? 'an empty string' : value === null || value === undefined ? String(value) : `a ${typeof value}`}`,\n );\n }\n };\n needText('accessKeyId', given.accessKeyId);\n needText('secretAccessKey', given.secretAccessKey);\n if (given.sessionToken !== undefined) needText('sessionToken', given.sessionToken);\n}\n\n/** Refuse an endpoint the SDK would fail on at its first request: it needs a scheme, as `http://localhost:9000`. */\nfunction checkEndpointUrl(endpoint: string): void {\n let url: URL | undefined;\n try {\n url = new URL(endpoint);\n } catch {\n url = undefined;\n }\n if (url === undefined || !/^https?:$/.test(url.protocol) || url.hostname.length === 0) {\n throw new ValidationError(\n 'S3Storage: `endpoint` must be an http or https URL such as \"http://localhost:9000\"',\n );\n }\n}\n\n/**\n * What {@link S3Storage} takes: a `bucket`, and either a `client` or the five settings that build one (`region`,\n * `endpoint`, `pathStyle`, `credentials`, `maxSockets`), never both.\n */\nexport interface S3StorageOptions {\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional key prefix under which everything lives \u2014 generations and the registry alike. A string, else `ValidationError`. */\n readonly prefix?: string;\n /**\n * A constructed client. Supply one for a credential chain the SDK cannot infer; otherwise one is built. Its retry\n * applies to every request except the conditional writes, which the driver sends with that retry off whatever it is\n * configured to do: a registry row once, and a generation's object again only after a throttle, under its own backoff.\n */\n readonly client?: S3Client;\n /**\n * Region for the client built when `client` is absent (refused beside `client`): a non-empty string, else\n * `ValidationError`. Falls back to the SDK's own resolution.\n */\n readonly region?: string;\n /**\n * Endpoint for an S3-compatible store (MinIO, Ceph, R2): an http or https URL, since an empty one would send\n * requests to AWS, else `ValidationError`. Refused beside `client`, which carries its own.\n */\n readonly endpoint?: string;\n /**\n * Path-style addressing, which most S3-compatible stores require: a boolean, else `ValidationError`. Refused beside\n * `client`, which carries its own.\n */\n readonly pathStyle?: boolean;\n /**\n * Static credentials, for the S3-compatible stores that issue them (MinIO, Ceph, R2): non-empty `accessKeyId` and\n * `secretAccessKey` strings and an optional `sessionToken`, else `ValidationError`, which never prints a value. No other\n * key is taken except `$source`, `credentialScope`, `accountId` and `expiration`, which are tolerated and not read:\n * the SDK stamps `$source` on a credentials object it is given, so one used for two stores carries it, and a credential\n * provider's result carries the others. A provider function is not taken here; put it on an `S3Client` given as `client`.\n *\n * On AWS itself, leave this unset \u2014 the SDK's own chain (instance role, SSO, environment, profile) is what\n * you want, and hard-coding keys to reach it would be a downgrade. It exists because the alternative for a\n * MinIO user was to construct an `S3Client` purely to carry two strings, which is the ergonomics this class\n * is here to remove. Refused beside `client`, which carries its own.\n */\n readonly credentials?: {\n readonly accessKeyId: string;\n readonly secretAccessKey: string;\n readonly sessionToken?: string;\n };\n /**\n * Most sockets the built client opens at once to one scheme, for `https` and for a plain-`http` endpoint alike\n * (default 128). The AWS SDK's own default is 50, which a window of 32 reads per operand outgrows on the first\n * two-operand `intersect`. Only this limit differs from the SDK's own client: its request handler, keep-alive,\n * timeouts and retry are the SDK's. If a future SDK changes its handler, the built client keeps the SDK's own\n * limit rather than failing to build. A positive safe integer. Release a built client's sockets with\n * `store.client.destroy()`. Refused beside `client`, which carries its own request handler. An `eraseSubject`\n * at its default `concurrency` of 8 holds up to 64 requests open, which the default of 128 covers; raise\n * `maxSockets`, or lower `concurrency`, to fit a larger one.\n */\n readonly maxSockets?: number;\n /**\n * Largest object the backend will write and advertise. Default = `partBytes \u00D7 10,000` (\u2248 78 GiB at the default\n * 8 MiB part) \u2014 the honest ceiling reachable within S3's 10,000-part limit. Set it higher and `partBytes`\n * 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\n * larger cap, or a `partBytes` above S3's 5 GiB part limit, is refused with `ValidationError`.\n * Must be a positive safe integer.\n */\n readonly maxObjectBytes?: number;\n /** Multipart part size in bytes (default 8 MiB; a smaller value is raised to the S3 5 MiB minimum). Must be a\n * positive safe integer. Tunes peak write memory. */\n readonly partBytes?: number;\n /**\n * How long one read may take before it is abandoned, in ms. `0`, the default, sets no timeout. When set, it bounds\n * each `GetObject` and `HeadObject` either half sends, the response body included, so a connection that stops\n * answering part-way through a body is cut off too. A read that runs out of time throws `TransientError`, which the\n * store's read retry runs again. AWS's S3 guidance is to retry a GET of under 512 KB that has not answered in about\n * 2 seconds. Must be a non-negative safe integer no larger than 2,147,483,647.\n *\n * The clock starts when the read is handed to the SDK, so it also counts the time the read waits for one of the\n * client's sockets (128 by default, `maxSockets`) and the time spent fetching credentials, and under `retryMode:\n * 'adaptive'` the SDK's rate-limiter wait. A burst of concurrent reads larger than the socket pool can therefore time\n * out with nothing slow on the wire: size the timeout above the worst queueing your concurrency implies, or raise\n * `maxSockets` (the client you pass carries its own). On a client built with `cacheMiddleware: true`, a timed read\n * resolves its middleware each time.\n *\n * Writes and listings are never timed: a write that hangs needs a timeout on the client (its `requestHandler`). The\n * timeout is applied per request, so a `client` you pass gets it without being changed.\n */\n readonly readTimeoutMs?: number;\n /** Injected clock for the registry's `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n /**\n * Whether the store applies `If-Match` on a `DeleteObject`, which both halves rely on when it is on: the registry\n * removes a deleted row for good rather than leaving a tombstone a full listing reads forever, and the storage half\n * removes an erasure's holder only while it is the object the erasure searched, by its ETag. Defaults to `true` when\n * the host the client resolves is an AWS S3 host, whichever way its endpoint was set (`endpoint`,\n * `AWS_ENDPOINT_URL_S3`, `AWS_ENDPOINT_URL`, the shared config file), and to `false` for any other host: set it for an\n * S3-compatible store only once you know the store applies `If-Match` on a delete. MinIO, for one, ignores it. It is\n * never `true` for an SDK that does not send the header.\n */\n readonly conditionalDelete?: boolean;\n}\n\n/**\n * The keys `new S3Storage(options)` takes. Any other is refused by name rather than ignored: an ignored client or\n * endpoint key builds a client from ambient credentials against the **public** endpoint, and for a store pointed at\n * MinIO that is production traffic from a wiring typo.\n */\nexport const S3_STORAGE_OPTION_KEYS = [\n 'bucket',\n 'prefix',\n 'client',\n 'region',\n 'endpoint',\n 'pathStyle',\n 'credentials',\n 'maxSockets',\n 'maxObjectBytes',\n 'partBytes',\n 'readTimeoutMs',\n 'now',\n 'conditionalDelete',\n] as const;\n\n/** The settings that build a client, which a supplied `client` already carries and so cannot be given beside. */\nconst CLIENT_SETTINGS = ['region', 'endpoint', 'pathStyle', 'credentials', 'maxSockets'] as const;\n\n/** Default socket limit of a client the store builds: two operands at the default window of 32 reads each, doubled. */\nconst DEFAULT_MAX_SOCKETS = 128;\n\n/**\n * Cap the sockets the SDK's own request handler opens, and change nothing else about it. The handler stays the SDK's\n * default one, so its other defaults hold: its defaults-mode connection timeout, keep-alive, and a request that\n * sends `Expect: 100-continue` (a part of 2 MiB or more) still goes on its own connection outside this pool. Only the\n * `maxSockets` of its two pooled agents is set, before the first request goes out. The handler makes its agents on\n * that first request (the http one even later, per request, so a first burst to a plain-http endpoint would get one\n * pool each), so the first request first runs an aborted one through it, which makes them, then sets the limit on\n * the agents it made.\n */\nexport function limitSockets(client: S3Client, maxSockets: number): void {\n const handler = client.config.requestHandler as unknown as PooledHandler;\n // A handler of another shape is left as it is: a tuning setting never stops the store from being built, and the\n // client then keeps the SDK's own limit.\n if (typeof handler?.handle !== 'function' || typeof handler.httpHandlerConfigs !== 'function')\n return;\n const handle = handler.handle.bind(handler);\n const agents = handler.httpHandlerConfigs.bind(handler);\n let ready: Promise<void> | undefined;\n const warm = async (): Promise<void> => {\n const abort = new AbortController();\n abort.abort();\n try {\n // An `http:` request makes the handler build both agents; on a client that only ever speaks https the http\n // agent stays idle, and `client.destroy()` frees it with the rest.\n await handle({ protocol: 'http:' }, { abortSignal: abort.signal }).catch(() => undefined);\n const { httpAgent, httpsAgent } = agents();\n if (httpAgent) httpAgent.maxSockets = maxSockets;\n if (httpsAgent) httpsAgent.maxSockets = maxSockets;\n } catch {\n // The handler is not the shape this expects: the limit stays the SDK's own.\n }\n };\n handler.handle = async (request, options) => {\n ready ??= warm();\n await ready;\n return handle(request, options);\n };\n}\n\n/**\n * `S3Storage` \u2014 the S3 backend as one object: the generations and the pointer, in one bucket, stated once.\n *\n * Replaces two constructor calls that each repeated `client`, `bucket` and `prefix`. Repeating them is how\n * they come apart: point the registry at one prefix and the objects at another and the store answers *empty*\n * rather than *misconfigured*, which is the hardest kind of wrong answer to debug. Here the location is written\n * once and shared, so the mismatch cannot be expressed.\n *\n * **It will build a client for you**, which is the common case \u2014 `new S3Storage({ bucket })` picks up the\n * ambient credential chain and region exactly as the SDK would. Pass `client` instead when you need a\n * credential chain the SDK cannot infer (SSO, an assumed role, a custom retry strategy); pass `endpoint` +\n * `pathStyle` + `credentials` for an S3-compatible store (MinIO, Ceph, R2). Both halves stay reachable as `.storage`\n * and `.registry` for anyone wiring something the facade does not cover. `maxObjectBytes` and `partBytes` size the\n * multipart upload, and `readTimeoutMs`, when set, bounds each read both halves make (listings are not timed).\n */\nexport class S3Storage implements StorageBackend {\n /** Cross-bundle brand, stamped non-enumerably in the constructor so a spread cannot carry it. */\n declare readonly [STORAGE_BACKEND]: true;\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n /** The client both halves share \u2014 built here unless one was supplied. */\n readonly client: S3Client;\n private readonly sockets: SocketAdvisory;\n private readonly bucket: string;\n /** The key prefix every object sits under: `''`, or `trimmed/`. */\n private readonly keyPrefix: string;\n private readonly readTimeoutMs: number;\n\n /**\n * Builds the S3 backend over one bucket, which `bucket` names. Pass `client`, or `region`, `endpoint`, `pathStyle`,\n * `credentials` and `maxSockets` to have one built, not both. `prefix` puts every object under a key prefix. Throws\n * `ValidationError` for an unknown option, no bucket, a `now` that is not a function, a client setting beside\n * `client`, a `maxSockets` (with no `client`) that is not a positive integer, a `partBytes` or `maxObjectBytes` that\n * 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\n * is not a boolean, `credentials` that are not complete, or an invalid `prefix` or `readTimeoutMs`.\n */\n constructor(options: S3StorageOptions) {\n refuseUnknownOptions(\n 'S3Storage',\n options,\n S3_STORAGE_OPTION_KEYS,\n 'an S3 client goes in `client`',\n );\n if (typeof options.bucket !== 'string' || options.bucket.trim().length === 0) {\n throw new ValidationError('S3Storage needs a `bucket`: the name of one, a non-empty string');\n }\n if (options.now !== undefined && typeof options.now !== 'function') {\n throw new ValidationError(\n \"S3Storage's `now` must be a function that returns epoch milliseconds\",\n );\n }\n if (options.client !== undefined && options.client !== null) {\n // A supplied client already carries its region, endpoint, addressing style and credentials, so a setting\n // beside it is ignored, and ignoring it leaves the store talking to somewhere the caller did not mean:\n // an `endpoint` meant for MinIO, silently dropped, is production traffic from a client that was built\n // for AWS. Refuse instead of picking one.\n const ignored = CLIENT_SETTINGS.filter((k) => options[k] !== undefined);\n if (ignored.length > 0) {\n throw new ValidationError(\n `S3Storage takes \\`client\\` OR ${CLIENT_SETTINGS.map((k) => `\\`${k}\\``).join(' / ')}, not both \u2014 ` +\n `got \\`client\\` with ${ignored.map((k) => `\\`${k}\\``).join(', ')}; ` +\n 'the `client` already carries them, so configure them on the client, or drop `client`',\n );\n }\n // Only an S3Client has `send`; a bare object would build a store that fails at its first read with a TypeError.\n if (typeof (options.client as { send?: unknown }).send !== 'function') {\n throw new ValidationError(\n 'S3Storage: `client` must be an S3Client from @aws-sdk/client-s3 (an object with `send`)',\n );\n }\n this.client = options.client;\n } else {\n const maxSockets =\n requirePositiveInt('maxSockets', options.maxSockets) ?? DEFAULT_MAX_SOCKETS;\n // The SDK refuses an empty region only when the first request is built, with an untyped error, and takes a number.\n const region: unknown = options.region;\n if (region !== undefined && (typeof region !== 'string' || region.trim().length === 0)) {\n throw new ValidationError(\n \"S3Storage's `region` must be a non-empty string such as 'us-east-1'\",\n );\n }\n // An empty `endpoint` is no endpoint: the client would send to AWS, not the service meant.\n const endpoint = requireNonEmptyString('S3Storage: `endpoint`', options.endpoint);\n if (endpoint !== undefined) checkEndpointUrl(endpoint);\n requireBool('S3Storage: `pathStyle`', options.pathStyle);\n checkCredentials(options.credentials);\n // `credentials` is not the last member: the leak scan reads a property read that ends a built line as a\n // possible secret, and the bundler drops the trailing comma after the last one.\n this.client = new S3Client(\n defined({\n credentials: options.credentials,\n region: options.region,\n endpoint: options.endpoint,\n forcePathStyle: options.pathStyle,\n }),\n );\n limitSockets(this.client, maxSockets);\n }\n this.bucket = options.bucket;\n this.keyPrefix = prefixPart(normalizeObjectPrefix(options.prefix));\n this.readTimeoutMs = resolveReadTimeoutMs(options.readTimeoutMs);\n this.sockets = new SocketAdvisory(this.client, options.bucket);\n const shared = {\n client: this.client,\n bucket: options.bucket,\n ...defined({ prefix: options.prefix, readTimeoutMs: options.readTimeoutMs }),\n };\n this.storage = new S3StorageDriver({\n ...shared,\n sockets: this.sockets,\n ...defined({\n maxObjectBytes: options.maxObjectBytes,\n partBytes: options.partBytes,\n conditionalDelete: options.conditionalDelete,\n }),\n });\n this.registry = new S3RegistryDriver({\n ...shared,\n ...defined({ now: options.now, conditionalDelete: options.conditionalDelete }),\n });\n brandAsBackend(this);\n }\n\n /**\n * Called by a store given a metrics sink. After the first read, a client whose socket pool is smaller than twice the\n * default window (64) sends the sink one `advisory` event, once; see the production guide's socket sizing.\n */\n attachMetrics(sink: IMetricsSink): void {\n this.sockets.attach(sink);\n }\n\n /**\n * Reads the bucket's lifecycle configuration (one `GetBucketLifecycleConfiguration` request, plus the SDK's own\n * retries, bounded by `readTimeoutMs` when it is set) and reports a rule that would expire, or move to GLACIER,\n * DEEP_ARCHIVE or a class it does not know, the current objects under this store's prefix, generations and registry\n * rows alike. Needs `s3:GetLifecycleConfiguration`, which a load never uses (`store.preflight()`'s\n * lifecycle step does): without it the\n * answer is `unchecked`, naming the permission. A missing rule that aborts incomplete multipart uploads is advice in\n * `detail`, not a problem. Never rejects; a `detail` carries no credential. Rule reading: see the production guide.\n */\n checkLifecycle(): Promise<LifecycleVerdict> {\n return checkS3Lifecycle(this.client, this.bucket, this.keyPrefix, this.readTimeoutMs);\n }\n}\n", "/**\n * `values` without the entries that are `undefined`, typed as optional properties, so a constructor can pass on the\n * options it was given: `exactOptionalPropertyTypes` refuses `{ x: undefined }` for an optional `x`, and a sub-driver\n * must see an option the caller left out as absent.\n */\nexport function defined<T extends Record<string, unknown>>(\n values: T,\n): { [K in keyof T]?: Exclude<T[K], undefined> } {\n return Object.fromEntries(Object.entries(values).filter(([, v]) => v !== undefined)) as {\n [K in keyof T]?: Exclude<T[K], undefined>;\n };\n}\n", "/**\n * The S3 lifecycle check behind `S3Storage.checkLifecycle`: reads the bucket's lifecycle configuration (one\n * `GetBucketLifecycleConfiguration` request) and says whether a rule would expire or archive what the store keeps.\n *\n * Everything the store keeps sits under one key prefix: the generation objects at `<prefix><namespace>/segments/\u2026`\n * and the registry rows at `<prefix>registry/\u2026`. A rule is a **problem** when it is enabled, its prefix and ours are\n * one a prefix of the other (a rule on `a/` covers a store under `a/b/`, and a rule on `a/b/registry/` covers part of\n * it), it is enabled (a rule with no `Status` is read as enabled), it has no tag filter (the store tags nothing, so a\n * tag-filtered rule never matches), and it expires current objects (`Days` or `Date`) or transitions them to GLACIER or\n * DEEP_ARCHIVE, which cannot be read without a restore, or to a storage class this check does not know. A rule that\n * only expires noncurrent versions, only removes expired delete markers or only aborts incomplete uploads is safe. A\n * rule with a condition this check does not interpret (an object size filter) is judged as if the condition were\n * absent, so it can report a problem for a rule that would not match; it never hides one. When unsure, a problem.\n *\n * Advice in `detail`, never a problem by itself: a missing rule that aborts incomplete multipart uploads over the\n * generation keys; a transition to INTELLIGENT_TIERING (its archive access tiers, if the bucket turns them on, need a\n * restore before a read); a transition to an infrequent-access class or GLACIER_IR (a minimum storage duration and a\n * per-GB retrieval fee).\n *\n * The request honours the backend's `readTimeoutMs`: one that gets no answer in time is `unchecked`.\n */\nimport { GetBucketLifecycleConfigurationCommand } from '@aws-sdk/client-s3';\nimport type { LifecycleRule, S3Client } from '@aws-sdk/client-s3';\nimport type { LifecycleVerdict } from '@cloudbitmaps/core/driver-kit';\nimport { TransientError } from '@cloudbitmaps/core/driver-kit';\nimport { errorName } from './aws-errors';\nimport { timedRead } from './read-timeout';\nimport { scrubCredentials } from './scrub-error';\n\n/** The longest the message of a failed request is shown. */\nconst MAX_MESSAGE = 200;\n\n/** The longest a rule id, a rule prefix or an error name is shown. */\nconst MAX_FIELD = 64;\n\n/** How many problem rules a `detail` names before it says \"and N more\". */\nexport const MAX_NAMED_RULES = 3;\n\n/** Classes an object reads from at once, with no restore. */\nconst READABLE_CLASSES: ReadonlySet<string> = new Set([\n 'STANDARD',\n 'STANDARD_IA',\n 'ONEZONE_IA',\n 'GLACIER_IR',\n 'INTELLIGENT_TIERING',\n]);\nconst ARCHIVE_CLASSES: ReadonlySet<string> = new Set(['GLACIER', 'DEEP_ARCHIVE']);\nconst INFREQUENT_CLASSES: ReadonlySet<string> = new Set([\n 'STANDARD_IA',\n 'ONEZONE_IA',\n 'GLACIER_IR',\n]);\n\nconst cut = (text: string, max: number): string => (text.length > max ? text.slice(0, max) : text);\n\n/** Two key prefixes cover a common key when one starts with the other. */\nconst overlaps = (a: string, b: string): boolean => a.startsWith(b) || b.startsWith(a);\n\n/** The prefix a rule applies to: the filter's, the filter's `And`, or the legacy rule-level one; none is every key. */\nfunction rulePrefix(rule: LifecycleRule): string {\n return rule.Filter?.Prefix ?? rule.Filter?.And?.Prefix ?? rule.Prefix ?? '';\n}\n\nconst hasTagFilter = (rule: LifecycleRule): boolean =>\n rule.Filter?.Tag !== undefined || (rule.Filter?.And?.Tags?.length ?? 0) > 0;\n\n/** Whether a rule is in force: only an explicit `Disabled` switches it off. */\nconst isEnabled = (rule: LifecycleRule): boolean => rule.Status !== 'Disabled';\n\n/** How a rule threatens current objects, or `undefined` when it does not. */\nfunction threat(rule: LifecycleRule): string | undefined {\n if (rule.Expiration?.Days !== undefined || rule.Expiration?.Date !== undefined) {\n return 'expires current objects';\n }\n const classes = (rule.Transitions ?? []).map((t) => t.StorageClass);\n const archive = classes.find((c) => c !== undefined && ARCHIVE_CLASSES.has(c));\n if (archive !== undefined) return `moves current objects to ${archive}`;\n const unknown = (rule.Transitions ?? []).find(\n (t) => t.StorageClass === undefined || !READABLE_CLASSES.has(t.StorageClass),\n );\n return unknown === undefined\n ? undefined\n : `moves current objects to ${cut(String(unknown.StorageClass ?? 'no storage class'), MAX_FIELD)}, a storage class this check does not know`;\n}\n\nconst label = (rule: LifecycleRule): string => {\n const id = rule.ID === undefined ? '' : `${JSON.stringify(cut(rule.ID, MAX_FIELD))} `;\n return `${id}(prefix ${JSON.stringify(cut(rulePrefix(rule), MAX_FIELD))})`;\n};\n\n/** Whether a rule's prefix is the store's or longer, so narrowing it cannot exclude the store's prefix. */\nconst sitsInside = (rule: LifecycleRule, prefix: string): boolean =>\n prefix !== '' && rulePrefix(rule).startsWith(prefix);\n\n/** Whether an abort-incomplete-upload rule covers generation keys, `<prefix><ns>/segments/\u2026`, not only `<prefix>registry/`. */\nfunction coversGenerations(rule: LifecycleRule, prefix: string): boolean {\n const own = rulePrefix(rule);\n if (prefix.startsWith(own)) return true;\n return !own.slice(prefix.length).startsWith('registry/');\n}\n\nconst ABORT_ADVICE =\n 'No rule aborts incomplete multipart uploads under this prefix; add one (AbortIncompleteMultipartUpload, a few days) ' +\n 'so an upload a crashed writer abandoned stops billing for its parts.';\n\nconst INTELLIGENT_TIERING_ADVICE =\n 'A rule moves current objects to INTELLIGENT_TIERING: if the bucket turns on its archive access tiers, an object in ' +\n 'one needs a restore before it can be read, so leave them off for this prefix.';\n\nconst INFREQUENT_ADVICE =\n 'A rule moves current objects to an infrequent-access class or GLACIER_IR: they stay readable, but each has a ' +\n 'minimum storage duration and a per-GB retrieval fee that reads of the store will pay.';\n\n/**\n * What a bucket's rules mean for a store under `prefix` (`''`, or `trimmed/`, the form every key builder uses).\n * Pure: no request, no SDK object but the rule shape.\n */\nfunction judgeRules(rules: readonly LifecycleRule[], prefix: string): LifecycleVerdict {\n const live = rules.filter((r) => isEnabled(r) && overlaps(rulePrefix(r), prefix));\n const untagged = live.filter((r) => !hasTagFilter(r));\n const threats = untagged.flatMap((r) => {\n const how = threat(r);\n return how === undefined\n ? []\n : [{ text: `rule ${label(r)} ${how}`, inside: sitsInside(r, prefix) }];\n });\n const problems = threats.map((t) => t.text);\n const transitionsTo = (classes: ReadonlySet<string>): boolean =>\n untagged.some((r) =>\n (r.Transitions ?? []).some(\n (t) => t.StorageClass !== undefined && classes.has(t.StorageClass),\n ),\n );\n const advice = [\n live.some((r) => r.AbortIncompleteMultipartUpload !== undefined && coversGenerations(r, prefix))\n ? ''\n : ABORT_ADVICE,\n transitionsTo(new Set(['INTELLIGENT_TIERING'])) ? INTELLIGENT_TIERING_ADVICE : '',\n transitionsTo(INFREQUENT_CLASSES) ? INFREQUENT_ADVICE : '',\n ]\n .filter((a) => a !== '')\n .map((a) => ` ${a}`)\n .join('');\n if (problems.length > 0) {\n const named = problems.slice(0, MAX_NAMED_RULES).join('; ');\n const more =\n problems.length > MAX_NAMED_RULES ? ` and ${problems.length - MAX_NAMED_RULES} more` : '';\n const quoted = JSON.stringify(cut(prefix, MAX_FIELD));\n const narrow = `Narrow the rule's prefix to exclude ${quoted}, or restrict it to noncurrent versions.`;\n const retarget =\n `A rule that sits inside ${quoted} cannot be narrowed to exclude it: remove it or retarget it to another ` +\n 'prefix, or restrict it to noncurrent versions.';\n const where =\n prefix === ''\n ? 'The store has no prefix, so every bucket-wide or prefixed rule can reach it: give the store a prefix, ' +\n 'or scope the rule away from its paths.'\n : [\n threats.some((t) => !t.inside) ? narrow : '',\n threats.some((t) => t.inside) ? retarget : '',\n ]\n .filter((x) => x !== '')\n .join(' ');\n return {\n status: 'problem',\n detail: `${named}${more}. The store keeps current objects it must be able to read back. ${where}${advice}`,\n };\n }\n return {\n status: 'ok',\n detail: `no enabled lifecycle rule under ${JSON.stringify(cut(prefix, MAX_FIELD))} expires or archives current objects.${advice}`,\n };\n}\n\n/** A failed request as a sentence: the permission to grant for a refusal, the scrubbed error otherwise. */\nfunction unchecked(err: unknown): LifecycleVerdict {\n if (err instanceof TransientError) {\n return {\n status: 'unchecked',\n detail:\n 'the read of the bucket lifecycle configuration got no answer in time (readTimeoutMs); run the check again',\n };\n }\n const scrubbed = scrubCredentials(err);\n if (errorName(scrubbed) === 'AccessDenied') {\n return {\n status: 'unchecked',\n detail:\n 'the identity may not read the bucket lifecycle configuration: grant s3:GetLifecycleConfiguration on the ' +\n 'bucket to check it, or confirm by hand that no rule expires or archives current objects under this prefix',\n };\n }\n const name = cut(String(errorName(scrubbed) ?? 'Error'), MAX_FIELD);\n const raw = (scrubbed as { message?: unknown } | null)?.message;\n const message = typeof raw === 'string' ? `: ${raw.slice(0, MAX_MESSAGE)}` : '';\n return {\n status: 'unchecked',\n detail: `could not read the bucket lifecycle configuration (${name}${message}); check it by hand`,\n };\n}\n\n/** The check: one request (the SDK may retry it), under `readTimeoutMs` (`0`: none), never a throw. */\nexport async function checkS3Lifecycle(\n client: S3Client,\n bucket: string,\n prefix: string,\n readTimeoutMs = 0,\n): Promise<LifecycleVerdict> {\n try {\n const out = await timedRead('GetBucketLifecycleConfiguration', readTimeoutMs, (options) =>\n client.send(new GetBucketLifecycleConfigurationCommand({ Bucket: bucket }), options),\n );\n return judgeRules(out.Rules ?? [], prefix);\n } catch (err) {\n try {\n if (errorName(err) === 'NoSuchLifecycleConfiguration') {\n return judgeRules([], prefix);\n }\n return unchecked(err);\n } catch {\n return { status: 'unchecked', detail: 'could not read the bucket lifecycle configuration' };\n }\n }\n}\n", "/**\n * SDK-free helpers for classifying AWS-style errors (used by the S3 drivers).\n *\n * These only read structural shapes an AWS SDK v3 error carries \u2014 `name`, `$metadata.httpStatusCode`, a\n * lower-level `code`/`errno`, and the SDK's own `$retryable` marker \u2014 so the (subtle, easy-to-get-wrong)\n * transient-vs-fatal decision is unit-testable without a live backend or even the SDK installed. They import no SDK.\n */\nimport { isTransportFault } from '@cloudbitmaps/core/driver-kit';\n\nexport function httpStatus(err: unknown): number | undefined {\n return (err as { $metadata?: { httpStatusCode?: number } } | null)?.$metadata?.httpStatusCode;\n}\n\nexport function errorName(err: unknown): string | undefined {\n return (err as { name?: string } | null)?.name;\n}\n\n/** A lower-level transport code (e.g. `ECONNRESET`) \u2014 the Node networking layer sets `code`. */\nexport function errorCode(err: unknown): string | undefined {\n return (err as { code?: string } | null)?.code;\n}\n\n/** The AWS SDK v3 tags retryable errors with a `$retryable` object (throttling faults carry `.throttling`). */\nexport function isSdkRetryable(err: unknown): boolean {\n return (err as { $retryable?: unknown } | null)?.$retryable != null;\n}\n\n/** Any 5xx is a server-side fault that's safe to retry (the request didn't deterministically fail). */\nexport function isServerSide(err: unknown): boolean {\n const status = httpStatus(err);\n return status !== undefined && status >= 500 && status <= 599;\n}\n\nconst NETWORK_NAMES = new Set([\n 'TimeoutError',\n 'RequestTimeout',\n 'RequestTimeoutException',\n 'NetworkingError',\n 'AbortError',\n]);\n\n// Message-text fallback for when the structural signals (name/code/$metadata/$retryable) are absent. Kept\n// SPECIFIC on purpose: a loose `/timed? ?out/` matches deterministic messages like \"value timed out of\n// range\" and would wrongly retry them, so we only match timeout/network phrases anchored to a transport word\n// (connection/request/socket/read/write) plus the unambiguous standalone phrases.\nconst NETWORK_MESSAGE =\n /socket hang up|network (error|failure)|(connection|request|socket|operation|read|write)\\s+tim(e|ed)\\s?out|connection (reset|refused|aborted|closed)/i;\n\n/**\n * A dropped/timed-out connection \u2014 transient by nature; a retry on a fresh connection usually succeeds. The SDK's\n * transport error names, the codes Node gives a failed connection ({@link isTransportFault}), and the messages above.\n */\nexport function isNetworkOrTimeout(err: unknown): boolean {\n if (NETWORK_NAMES.has(errorName(err) ?? '')) return true;\n if (isTransportFault(err)) return true;\n return NETWORK_MESSAGE.test((err as { message?: string } | null)?.message ?? '');\n}\n", "/**\n * The read timeout: with `readTimeoutMs` set, each `GetObject` and `HeadObject` the S3 drivers send is cut off after\n * it. It is off by default (`0`).\n *\n * The SDK sets no timeout of its own, so a read on a connection that stops answering waits as long as the connection\n * stays open, and the store's read retry never gets a fault to retry. A read still running when its timer fires throws\n * {@link TransientError}, which the store's retry runs again, and is aborted through the `abortSignal` its\n * `client.send` was given, which ends the request and destroys the response body, letting go of the socket. The SDK\n * does not send an aborted request again.\n *\n * The timer starts when the read is handed to the SDK and covers everything until its body is read: waiting for one\n * of the client's sockets, fetching credentials, an adaptive retry mode's rate-limiter wait, the request, any retries\n * the SDK makes of it, and reading the body. A server that sends its headers and then stalls part-way through the body\n * is cut off too, and so is a read that only queued too long. The read settles when the timer fires whatever the layers\n * under the client do with the abort, because the timer's error is what the read throws.\n *\n * It is per request, never the HTTP handler's timeout, for two reasons: writes are not timed, since an upload's part can\n * rightly take longer than a read, and a `client` the caller passes in is used as it is.\n */\nimport { TransientError, ValidationError } from '@cloudbitmaps/core/driver-kit';\n\n/** No timeout unless one is set: the value a read takes stays the caller's until in-region measurements justify one. */\nconst DEFAULT_READ_TIMEOUT_MS = 0;\n\n/** The longest delay a Node timer holds. A longer one fires after 1 ms instead, so it is refused rather than passed on. */\nconst MAX_TIMER_MS = 2_147_483_647;\n\n/** Validate a caller's `readTimeoutMs`, defaulting it: an integer from 0 (no timeout) to {@link MAX_TIMER_MS}. */\nexport function resolveReadTimeoutMs(value: number | undefined): number {\n if (value === undefined) return DEFAULT_READ_TIMEOUT_MS;\n if (!Number.isSafeInteger(value) || value < 0 || value > MAX_TIMER_MS) {\n throw new ValidationError(\n `readTimeoutMs must be a non-negative safe integer no larger than ${MAX_TIMER_MS}; got ${describe(value)}`,\n );\n }\n return value;\n}\n\n/** A value as an error message can show it: a string quoted, so `'200'` is not mistaken for `200`, and never a throw. */\nfunction describe(value: unknown): string {\n if (typeof value === 'string') return JSON.stringify(value);\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n return value === null ? 'null' : typeof value;\n}\n\n/** The options a timed read passes to its `client.send`: the abort signal, or nothing when the timeout is off. */\nexport type ReadSendOptions = { readonly abortSignal: AbortSignal } | undefined;\n\n/**\n * Run `read` under a `timeoutMs` timer (`0`: none). `read` passes `options` to its `client.send` and reads the body\n * before it returns, so the timer covers both. The timer is cleared when the read settles.\n */\nexport async function timedRead<T>(\n operation: 'GetObject' | 'HeadObject' | 'GetBucketLifecycleConfiguration',\n timeoutMs: number,\n read: (options: ReadSendOptions) => Promise<T>,\n): Promise<T> {\n // No options at all, rather than an empty signal: a client built with `cacheMiddleware: true` reuses its cached\n // handler only for a `send` given none.\n if (timeoutMs === 0) return read(undefined);\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timedOut = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // Rejected before the abort, so the read settles with this error and not with the one the abort raises.\n reject(new TransientError(`S3 ${operation} timed out after ${timeoutMs} ms`));\n controller.abort();\n }, timeoutMs);\n });\n try {\n // The race also takes the read's own rejection after an abort, so it never goes unhandled.\n return await Promise.race([read({ abortSignal: controller.signal }), timedOut]);\n } finally {\n clearTimeout(timer);\n }\n}\n", "/**\n * Remove credentials from an error the AWS SDK raised, before the driver throws it or keeps it as a `cause`.\n *\n * The SDK keeps the HTTP response it got on the error (`$response`), and through its socket the request it sent, with\n * the signed `Authorization` header and `x-amz-security-token`, on properties `util.inspect` shows only with\n * `showHidden`; a body it could not parse it keeps as text (`$responseBodyText`). It also copies every field of the\n * error body onto the error, where a logger prints it. A `SignatureDoesNotMatch` body from S3 quotes the request back:\n * its signature (`SignatureProvided`) and the canonical request S3 computed (`CanonicalRequest`, in hex\n * `CanonicalRequestBytes`), which lists every signed header with its value, the session token's included. A proxy's\n * error page, parsed the same way, can quote the `Authorization` value or the token in any field. The live objects are\n * never changed (a socket still closing reads them): an error that holds a transport, or whose data shows a credential,\n * is replaced by a deep copy of its plain data. An error with neither is returned as it is.\n *\n * The copy is a native error with the original's prototype (so `instanceof S3ServiceException` holds), name, message,\n * stack, `$metadata`, `$fault`, `$retryable`, `Code`, `RequestId` and every other plain field, which is what\n * classification reads. It has no transport property, no function or accessor (a `toJSON` or `util.inspect` hook is a\n * closure over the original) and no class instance other than an error. Errors are copied wherever they sit: the `cause`\n * chain, an `AggregateError`'s `errors`, any array or field. A table of copies keeps a cycle a cycle, and anything\n * nested deeper than `MAX_DEPTH` is dropped rather than kept raw. The value under a credential key is replaced by\n * `[redacted]`: a credential header's, an SSE-C key's, an AWS credential's fields, the signature and canonical request\n * S3 quotes, and the session token it quotes in `Token-0` when it refuses one (the access key id it names is kept: it\n * names a key and signs nothing). So is the value beside a credential's name in a list (`['x-amz-security-token', v]`)\n * or a record (a header table's `{ th, td }` row, `{ name, value }`, S3's `ArgumentName` and `ArgumentValue`). In the\n * copy's text, a credential header line, a SigV4 signature (`Signature=` in an `Authorization` value, `X-Amz-Signature=`\n * in a URL) and a session token or SSE-C key written after its header name are redacted. A credential in any other form\n * is not recognised, and a text too large for these patterns is replaced whole.\n */\n\nimport { types } from 'node:util';\n\n/** Properties that hold the raw transport: the response, its unparsed body, and the request and socket reachable from it. */\nconst TRANSPORT = new Set([\n '$response',\n '$responseBodyText',\n 'socket',\n '_httpMessage',\n 'req',\n 'request',\n 'connection',\n 'agent',\n]);\n/** The headers, besides `Authorization`, whose value is a credential: the session tokens and an SSE-C key. */\nconst PRIVATE_HEADERS =\n 'x-amz-security-token|x-amz-s3session-token|x-amz-(?:copy-source-)?server-side-encryption-customer-key';\n/**\n * Header and field names whose value is a credential: the credential headers, the SDK's SSE-C members, an AWS\n * credential's fields, what S3 quotes of a request whose signature it refused, and the session token it quotes as\n * `Token-0` when it refuses the token (`ExpiredToken`, `InvalidToken`). The whole name must match: `ContinuationToken`\n * does not.\n */\nconst CREDENTIAL_NAME = new RegExp(\n `^(?:(?:proxy-)?authorization|cookie|set-cookie|x-api-key|${PRIVATE_HEADERS}|x-amz-credential|x-amz-signature|` +\n '(?:copysource)?ssecustomerkey|secretaccesskey|sessiontoken|signatureprovided|canonicalrequest(?:bytes)?|' +\n 'token(?:-\\\\d+)?)$',\n 'i',\n);\n/** Whether `text` is a credential's name, as a key, a list item or a table cell holds it: spaces and a colon aside. */\nconst isCredentialName = (text: string): boolean =>\n CREDENTIAL_NAME.test(text.trim().replace(/:$/, ''));\n/** A credential header line inside raw request or response text. */\nconst CREDENTIAL_LINES = new RegExp(\n `^((?:proxy-)?authorization|cookie|set-cookie|x-api-key|${PRIVATE_HEADERS}):[^\\\\r\\\\n]*`,\n 'gim',\n);\n/**\n * A session token or SSE-C key after its header or query name anywhere in a text: `name: value`, `name=value` (also\n * percent-encoded) or JSON's `\"name\":\"value\"`.\n */\nconst NAMED_SECRET = new RegExp(\n `((?:${PRIVATE_HEADERS})[\"']?\\\\s*(?::|=|%3D)\\\\s*[\"']?)[^\\\\s&\"'<>\\\\\\\\,;]+`,\n 'gi',\n);\n/** A SigV4 signature: an `Authorization` value's `Signature=`, a presigned URL's `X-Amz-Signature=`, also percent-encoded. */\nconst SIGNATURE = /(Signature(?:=|%3D))[0-9A-Fa-f]{16,}/gi;\nconst REDACTED = '[redacted]';\n/** How deep into errors and plain data the copy goes; anything below is dropped. */\nconst MAX_DEPTH = 10;\n\nconst isPlain = (value: object): boolean => {\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n};\n\nconst isError = (value: object): boolean => value instanceof Error || types.isNativeError(value);\n\n/**\n * `text` with every credential it shows replaced. A text the patterns cannot finish matching (one of several MiB that\n * exhausts the regular expression engine's stack) is replaced whole, so a hostile body neither leaks nor changes the\n * error the driver throws.\n */\nfunction redactText(text: string): string {\n try {\n return text\n .replace(CREDENTIAL_LINES, `$1: ${REDACTED}`)\n .replace(NAMED_SECRET, `$1${REDACTED}`)\n .replace(SIGNATURE, `$1${REDACTED}`);\n } catch {\n return REDACTED;\n }\n}\n\n/** The copies made so far, and whether any differs from its original. */\ninterface Copying {\n copies: WeakMap<object, unknown>;\n changed: boolean;\n}\n\n/** Marker for a value the copy leaves out. */\nconst OMIT = Symbol('omit');\n\n/** An array index as an own key spells it. */\nconst INDEX = /^(?:0|[1-9]\\d*)$/;\n\n/**\n * Whether `value` has an own property its copy leaves out: any key but an index and `length` on an array, any key at\n * all on a map or a set.\n */\nfunction hasNamedKeys(value: object): boolean {\n if (!Array.isArray(value)) return Reflect.ownKeys(value).length > 0;\n return Reflect.ownKeys(value).some(\n (key) =>\n key !== 'length' &&\n !(typeof key === 'string' && INDEX.test(key) && Number(key) < 2 ** 32 - 1),\n );\n}\n\n/** Define `key` on `copy` as a plain data property. */\nfunction define(copy: object, key: string | symbol, value: unknown, enumerable: boolean): void {\n Object.defineProperty(copy, key, { value, writable: true, configurable: true, enumerable });\n}\n\n/** A credential-free copy of `value`: plain data and errors only, `OMIT` for anything else. */\nfunction copyOf(value: unknown, ctx: Copying, depth: number): unknown {\n if (typeof value === 'string') {\n const clean = redactText(value);\n if (clean !== value) ctx.changed = true;\n return clean;\n }\n if (typeof value === 'function' || typeof value === 'symbol') {\n ctx.changed = true;\n return OMIT;\n }\n if (typeof value !== 'object' || value === null) return value;\n if (ctx.copies.has(value)) return ctx.copies.get(value);\n if (depth > MAX_DEPTH) {\n ctx.changed = true;\n return OMIT;\n }\n if (Array.isArray(value)) return copyList(value as unknown[], ctx, depth);\n if (value instanceof Map) return copyMap(value as Map<unknown, unknown>, ctx, depth);\n if (value instanceof Set) return copySet(value as Set<unknown>, ctx, depth);\n return copyRecord(value, ctx, depth);\n}\n\n/** A copy of an array. A hole or a named property is left out. */\nfunction copyList(value: unknown[], ctx: Copying, depth: number): unknown[] {\n const list: unknown[] = [];\n ctx.copies.set(value, list);\n if (hasNamedKeys(value)) ctx.changed = true;\n let hidden = false;\n for (const item of value) {\n // A flat list of header names and values: the value after a credential name is one.\n if (hidden && item != null) {\n list.push(REDACTED);\n if (item !== REDACTED) ctx.changed = true;\n } else {\n const kept = copyOf(item, ctx, depth + 1);\n list.push(kept === OMIT ? undefined : kept);\n }\n hidden = typeof item === 'string' && isCredentialName(item);\n }\n return list;\n}\n\n/** A copy of a map: the value under a credential name is redacted. A named property is left out. */\nfunction copyMap(value: Map<unknown, unknown>, ctx: Copying, depth: number): Map<unknown, unknown> {\n const map = new Map<unknown, unknown>();\n ctx.copies.set(value, map);\n if (hasNamedKeys(value)) ctx.changed = true;\n for (const [k, v] of value) {\n const key = copyOf(k, ctx, depth + 1);\n if (key === OMIT) continue;\n if (typeof k === 'string' && isCredentialName(k) && v != null) {\n map.set(key, REDACTED);\n if (v !== REDACTED) ctx.changed = true;\n continue;\n }\n const kept = copyOf(v, ctx, depth + 1);\n if (kept !== OMIT) map.set(key, kept);\n }\n return map;\n}\n\n/** A copy of a set. A named property is left out. */\nfunction copySet(value: Set<unknown>, ctx: Copying, depth: number): Set<unknown> {\n const set = new Set<unknown>();\n ctx.copies.set(value, set);\n if (hasNamedKeys(value)) ctx.changed = true;\n for (const v of value) {\n const kept = copyOf(v, ctx, depth + 1);\n if (kept !== OMIT) set.add(kept);\n }\n return set;\n}\n\n/** A copy of an error or a plain object, `OMIT` for any other object (a class instance: a stream, a socket). */\nfunction copyRecord(value: object, ctx: Copying, depth: number): unknown {\n const errorLike = isError(value);\n if (!errorLike && !isPlain(value)) {\n ctx.changed = true;\n return OMIT;\n }\n const copy: object = errorLike\n ? (Object.setPrototypeOf(new Error(), Object.getPrototypeOf(value) as object | null) as object)\n : (Object.create(Object.getPrototypeOf(value) as object | null) as object);\n ctx.copies.set(value, copy);\n if (errorLike) delete (copy as { stack?: unknown }).stack;\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key === 'string' && TRANSPORT.has(key)) {\n ctx.changed = true;\n } else if (errorLike && key === 'stack') {\n copyStack(value, copy, ctx, depth);\n } else {\n copyField(value, copy, key, ctx, depth);\n }\n }\n if (errorLike) keepSlotFields(value, copy, ctx, depth);\n redactPairs(value, copy, errorLike, ctx);\n return copy;\n}\n\n/**\n * Redact the value a record holds beside a credential's name: a header table's `{ th, td }` row, a `{ name, value }`\n * pair, S3's `ArgumentName` and `ArgumentValue`. On a plain record every field but the name is redacted. On an error,\n * whose other fields are what classification reads, only the `...Value` field beside a `...Name` one is.\n */\nfunction redactPairs(original: object, copy: object, errorLike: boolean, ctx: Copying): void {\n const names = Reflect.ownKeys(original).filter((key): key is string => {\n if (typeof key !== 'string') return false;\n const held = Object.getOwnPropertyDescriptor(original, key)?.value as unknown;\n return typeof held === 'string' && isCredentialName(held);\n });\n if (names.length === 0) return;\n const values = errorLike\n ? names\n .filter((key) => /name$/i.test(key))\n .map((key) => `${key.slice(0, -4)}value`.toLowerCase())\n : undefined;\n for (const key of Reflect.ownKeys(copy)) {\n if (typeof key !== 'string' || names.includes(key)) continue;\n if (values !== undefined && !values.includes(key.toLowerCase())) continue;\n const descriptor = Object.getOwnPropertyDescriptor(copy, key);\n if (descriptor?.value == null || descriptor.value === REDACTED) continue;\n // A number or a boolean is never a credential, and it is what classification reads from a record such as\n // `$metadata` (`httpStatusCode`), which a response can pair with a request id that spells a credential name.\n if (typeof descriptor.value !== 'string' && typeof descriptor.value !== 'object') continue;\n define(copy, key, REDACTED, descriptor.enumerable ?? false);\n ctx.changed = true;\n }\n}\n\n/** Carry an error's stack over, read through the original: it may be an accessor bound to the error itself. */\nfunction copyStack(original: object, copy: object, ctx: Copying, depth: number): void {\n let stack: unknown;\n try {\n stack = (original as { stack?: unknown }).stack;\n } catch {\n return;\n }\n const kept = copyOf(stack, ctx, depth + 1);\n if (kept !== OMIT) define(copy, 'stack', kept, false);\n}\n\n/** Carry one own property over, as a data property: redacted under a credential name, left out when it is not data. */\nfunction copyField(\n original: object,\n copy: object,\n key: string | symbol,\n ctx: Copying,\n depth: number,\n): void {\n const descriptor = Object.getOwnPropertyDescriptor(original, key);\n if (descriptor === undefined) return;\n // A function or accessor is a closure over the original (a `toJSON`, an inspect hook): it is not carried over.\n if (!('value' in descriptor)) {\n ctx.changed = true;\n return;\n }\n // A key is text too: one that quotes a credential is carried redacted.\n const name = typeof key === 'string' ? redactText(key) : key;\n if (name !== key) ctx.changed = true;\n let kept: unknown;\n if (typeof key === 'string' && isCredentialName(key) && descriptor.value != null) {\n kept = REDACTED;\n if (descriptor.value !== REDACTED) ctx.changed = true;\n } else {\n kept = copyOf(descriptor.value, ctx, depth + 1);\n if (kept === OMIT) return;\n }\n Object.defineProperty(copy, name, {\n value: kept,\n writable: descriptor.writable ?? true,\n configurable: true,\n enumerable: descriptor.enumerable ?? false,\n });\n}\n\n/**\n * Carry over a `name`, `message` or `code` the original reads through its prototype and the copy cannot: a\n * `DOMException`'s are getters over the instance's internal state, which throw on any other object.\n */\nfunction keepSlotFields(original: object, copy: object, ctx: Copying, depth: number): void {\n for (const key of ['name', 'message', 'code'] as const) {\n if (Object.prototype.hasOwnProperty.call(original, key)) continue;\n let held: unknown;\n try {\n held = (original as Record<string, unknown>)[key];\n } catch {\n continue;\n }\n let read: unknown = OMIT;\n try {\n read = (copy as Record<string, unknown>)[key];\n } catch {\n // the copy cannot read it\n }\n if (read === held) continue;\n const kept = copyOf(held, ctx, depth + 1);\n if (kept !== OMIT) define(copy, key, kept, false);\n }\n}\n\n/**\n * `err` as it is safe to throw: a credential-free deep copy of an error or plain object when it holds a transport or\n * shows a credential, the value itself when it holds neither, and any other value (a class instance that is not an\n * error, a primitive) as it is. The live error is never changed.\n */\nexport function scrubCredentials<T>(err: T): T {\n if (typeof err !== 'object' || err === null) return err;\n const ctx: Copying = { copies: new WeakMap(), changed: false };\n const copy = copyOf(err, ctx, 0);\n return (ctx.changed && copy !== OMIT ? copy : err) as T;\n}\n", "/**\n * `S3StorageDriver` \u2014 an {@link IStorageDriver} over S3-compatible object storage.\n *\n * Works with AWS S3 and any compatible backend (MinIO, etc.) via the official `@aws-sdk/client-s3`, a real\n * dependency of this package \u2014 installing `@cloudbitmaps/s3` is what installs it. The client is\n * **injected** (dependency injection): the driver owns no credential/region/endpoint logic, so it's thin,\n * testable against MinIO (point a client at its endpoint), and reuses the caller's existing client.\n *\n * Generations are write-once immutable objects: a conditional `PutObject` with `If-None-Match: *` makes the\n * publish atomic \u2014 a second write to the same key fails with `WriteConflictError`, never a silent overwrite\n * (hard invariant 2: storage objects are immutable and never overwritten in place), the cloud analogue of\n * the LocalFs atomic `link`. **This requires a backend that honors\n * `If-None-Match: *`** (AWS S3 \u2014 GA Aug 2024; recent MinIO): a backend that silently ignored the\n * precondition would break write-once immutability. **Writes stream:** the object is uploaded in\n * constant memory \u2014 a small object is a single conditional `PutObject`; a large one is a **multipart upload**\n * (parts flushed as the codec writes, freed as they go) finished with a conditional `CompleteMultipartUpload`,\n * so a load's footprint stays ~one part regardless of segment size, up to the advertised `maxObjectBytes`\n * (default `partBytes \u00D7 10,000` \u2014 S3's per-upload part limit). **Each conditional request is sent with the SDK's retry\n * off for it** ({@link sendOnce}): a replay of a write that landed and lost its response would find its own object and\n * read as a lost race. A lost response or a timeout throws {@link TransientError}, and the object may or may not exist.\n * A throttle (`503 SlowDown`, any other `503`, or a bare `429`) is the one answer the driver sends the commit again after, at most {@link THROTTLE_RESENDS} times with\n * backoff: every object carries a random write id in its user metadata (`x-amz-meta-cbwid`), and once the commit has\n * been sent again, a precondition failure reads the object's metadata back, so a first send that landed after all is\n * this write's own and any other object is the conflict it reads as. The unconditional requests \u2014 the reads, the\n * delete, and a multipart upload's own start, parts and abort \u2014\n * keep the SDK's retry. **Each read can be timed** ({@link timedRead}): with `readTimeoutMs` set, a `GetObject` or\n * `HeadObject` that has not finished, body included, after it throws {@link TransientError}. It is off by default, and\n * nothing else is timed. Drivers may use `node:crypto`; only `core/` is bound by the determinism lint.\n *\n * **A delete given `ifVersion` is a `DeleteObject` under `If-Match: <that ETag>`** where `conditionalDelete` holds: by\n * default when the host the client resolves is an AWS S3 host and the SDK sends the header, as for the registry's row\n * delete ({@link appliesDeleteIfMatch}), resolved once, on the first such delete; MinIO, for one, accepts the header and\n * ignores it. A tail read reports the object's `ETag` as its version, from the `GetObject` that carried the bytes, or\n * the `HeadObject` when none were asked for. A `412` is the precondition failing, and a `409` a conditional request\n * that raced another on the key, which says nothing of which object is there; the delete has removed nothing either\n * way, and both are reported as {@link WriteConflictError} unless a `HeadObject` then finds no object under the key: an\n * absent object is a no-op, whichever of `204`, `404` or `412` the service answers for it. The delete keeps the SDK's\n * retry: a copy that meets its own landed delete finds nothing, and one that meets an object stored since is refused.\n * An ETag is computed from the bytes for an object stored without SSE-KMS or SSE-C, so the condition cannot tell apart\n * two objects with the same bytes, each stored whole in one request, or each in parts of the same sizes.\n */\nimport {\n errorKind,\n IntegrityError,\n NotFoundError,\n TransientError,\n ValidationError,\n WriteConflictError,\n concatBytes,\n normalizeObjectPrefix,\n parseGenerationFromKey,\n checkDeleteOptions,\n requireBool,\n requirePositiveInt,\n segmentObjectPrefix,\n storageObjectKey,\n toTransient,\n} from '@cloudbitmaps/core/driver-kit';\nimport type {\n BlobSink,\n GenKey,\n IStorageDriver,\n SegmentRef,\n StorageCaps,\n StorageDeleteOptions,\n TailRead,\n} from '@cloudbitmaps/core/driver-kit';\nimport type { Clock, Rng } from '@cloudbitmaps/core';\nimport { createHash, randomBytes, type Hash } from 'node:crypto';\nimport {\n AbortMultipartUploadCommand,\n CompleteMultipartUploadCommand,\n CreateMultipartUploadCommand,\n DeleteObjectCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n UploadPartCommand,\n type S3Client,\n} from '@aws-sdk/client-s3';\nimport {\n isConditionalConflict,\n isInvalidRange,\n asMissingBucket,\n isNoSuchUpload,\n isNotFound,\n spanFromContentRange,\n isPreconditionFailed,\n isThrottle,\n isTransient,\n totalFromContentRange,\n transientFaultMessage,\n} from './s3-errors';\nimport { resolveReadTimeoutMs, timedRead, type ReadSendOptions } from './read-timeout';\nimport type { SocketAdvisory } from './socket-advisory';\nimport { sendOnce } from './send-once';\nimport { readBounded } from './bounded-body';\nimport { scrubCredentials } from './scrub-error';\nimport { appliesDeleteIfMatch, probeClient, type ClientFacts } from './client-probe';\n\n/** Part size for multipart uploads. \u2265 the S3 5 MiB minimum; an object that fits in one part uses a single\n * conditional PUT instead (no multipart overhead, strongest write-once). Peak write memory \u2248 one part. */\nconst S3_PART_BYTES = 8 * 1024 * 1024;\n/**\n * S3 hard limit: a multipart upload has at most 10,000 parts. This \u00D7 the part size is the object ceiling at that part\n * size.\n */\nconst S3_MAX_PARTS = 10_000;\n/** S3 hard limits on one object (5 TiB) and one part (5 GiB). */\nconst S3_MAX_OBJECT_BYTES = 5 * 1024 ** 4;\nconst S3_MAX_PART_BYTES = 5 * 1024 ** 3;\n/** The user-metadata name an object's write id is stored under (`x-amz-meta-cbwid`). Short: every write sends it. */\nconst WRITE_ID_KEY = 'cbwid';\n/** How many times a throttled commit is sent again: four sends in all, as many as the SDK's own retry makes. */\nconst THROTTLE_RESENDS = 3;\n/** The backoff ceiling before the first re-send, doubling for each one after it: the SDK's own base for a throttle. */\nconst THROTTLE_BASE_DELAY_MS = 500;\n\n/** What the throttle backoff waits on. */\ntype Sleeper = Pick<Clock, 'sleep'>;\n\nconst REAL_TIME: Sleeper = {\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\n/** A refusal this driver already typed: it passes on unmapped, where a raw SDK error is mapped. */\nfunction isTypedPassThrough(err: unknown): boolean {\n const kind = errorKind(err);\n return kind === 'ValidationError' || kind === 'WriteConflictError' || kind === 'NotFoundError';\n}\n\n/** Where the backoff's jitter comes from when none is injected. */\nconst REAL_RNG: Rng = { next: () => Math.random() };\n\ninterface S3StorageDriverOptions {\n /** A constructed S3 client (point its `endpoint` at MinIO for local/integration use). */\n readonly client: S3Client;\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional key prefix under which all objects live (e.g. `cloudbitmaps/`). */\n readonly prefix?: string;\n /**\n * Largest object this driver will write/advertise. Default = `partBytes \u00D7 10,000` (\u2248 78 GiB at the default\n * 8 MiB part) \u2014 the honest ceiling reachable within S3's 10,000-part limit. Set it higher and `partBytes`\n * auto-grows so 10,000 parts still cover it (raising peak write memory to ~one part); up to the 5 TiB S3 max.\n */\n readonly maxObjectBytes?: number;\n /** Multipart part size in bytes (default 8 MiB; a smaller value is raised to the S3 5 MiB minimum). Must be a\n * positive safe integer. Tunes peak write memory. */\n readonly partBytes?: number;\n /**\n * How long one read \u2014 a `GetObject` or `HeadObject`, its body included \u2014 may take before it is abandoned and throws\n * `TransientError`, in ms. `0`, the default, sets no timeout. Must be a non-negative safe integer no larger than\n * 2,147,483,647. Writes and listings are not timed.\n *\n * The clock starts when the read is handed to the SDK, so it also counts the time the read waits for one of the\n * client's sockets (128 by default on the client the store builds, 50 on the SDK's own) and the time spent fetching\n * credentials, and under `retryMode: 'adaptive'` the SDK's rate-limiter wait. A burst of concurrent reads larger than\n * the socket pool can therefore time out with nothing slow on the wire: size the timeout above the worst queueing\n * your concurrency implies, or raise `maxSockets` (the store's option, or your own client's). On a client built with\n * `cacheMiddleware: true`, a timed read resolves its middleware each time.\n */\n readonly readTimeoutMs?: number;\n /** The socket-pool check, run as each read finishes; absent for a driver built on its own. */\n readonly sockets?: SocketAdvisory;\n /** What the backoff before re-sending a throttled commit waits on; real time when absent. */\n readonly clock?: Sleeper;\n /** Where the jitter of that backoff is drawn from; `Math.random` when absent. */\n readonly rng?: Rng;\n /**\n * Whether a delete given `ifVersion` is sent with `If-Match`, so it removes the object only while it is the one that\n * ETag names. Defaults as the registry's option does: on when the host the client resolves is an AWS S3 host and the\n * SDK sends the header, resolved on the first such delete (until then `capabilities()` reads `false`), and off for any\n * other host, and for a client that cannot be resolved then (no region), whose deletes stay unconditional. Set it for\n * an S3-compatible store only once you know the store applies `If-Match` on a delete. A `true` never overrides an SDK\n * that does not send the header.\n */\n readonly conditionalDelete?: boolean;\n}\n\nexport class S3StorageDriver implements IStorageDriver {\n private readonly client: S3Client;\n private readonly bucket: string;\n private readonly prefix: string | undefined;\n private readonly maxObjectBytes: number;\n private readonly partBytes: number;\n private readonly readTimeoutMs: number;\n private readonly clock: Sleeper;\n private readonly rng: Rng;\n private readonly sockets: SocketAdvisory | undefined;\n private readonly explicitConditionalDelete: boolean | undefined;\n private probed: Promise<void> | undefined;\n private facts: ClientFacts | undefined;\n\n constructor(options: S3StorageDriverOptions) {\n this.client = options.client;\n this.explicitConditionalDelete = requireBool('conditionalDelete', options.conditionalDelete);\n this.sockets = options.sockets;\n this.bucket = options.bucket;\n this.prefix = normalizeObjectPrefix(options.prefix);\n this.clock = options.clock ?? REAL_TIME;\n this.rng = options.rng ?? REAL_RNG;\n // Fail fast at the boundary: `??` only guards `undefined`, so NaN, 0, a negative or a fraction would otherwise\n // reach the arithmetic below and size every part (and the advertised cap) from garbage.\n requirePositiveInt('partBytes', options.partBytes);\n requirePositiveInt('maxObjectBytes', options.maxObjectBytes);\n // Past S3's own limits a cap is not reachable: the part grown to cover it would pass the part limit, an object up\n // to that size would be held in memory whole, and one PutObject of it would be refused.\n if (options.maxObjectBytes !== undefined && options.maxObjectBytes > S3_MAX_OBJECT_BYTES) {\n throw new ValidationError(\n `maxObjectBytes must be at most S3's 5 TiB object limit (${S3_MAX_OBJECT_BYTES}); got ${options.maxObjectBytes}`,\n );\n }\n if (options.partBytes !== undefined && options.partBytes > S3_MAX_PART_BYTES) {\n throw new ValidationError(\n `partBytes must be at most S3's 5 GiB part limit (${S3_MAX_PART_BYTES}); got ${options.partBytes}`,\n );\n }\n const requestedPart = Math.max(options.partBytes ?? S3_PART_BYTES, 5 * 1024 * 1024);\n // Default the object cap to what the requested part size can actually cover within S3's 10,000-part limit;\n // if a larger cap is requested, grow the part size to keep it reachable (so the advertised cap is honest).\n this.maxObjectBytes = options.maxObjectBytes ?? requestedPart * S3_MAX_PARTS;\n this.partBytes = Math.max(requestedPart, Math.ceil(this.maxObjectBytes / S3_MAX_PARTS));\n this.readTimeoutMs = resolveReadTimeoutMs(options.readTimeoutMs);\n }\n\n capabilities(): StorageCaps {\n return {\n rangeRead: true,\n maxObjectBytes: this.maxObjectBytes,\n conditionalPut: true,\n conditionalDelete: appliesDeleteIfMatch(this.explicitConditionalDelete, this.facts),\n };\n }\n\n /** Ask the client once where its requests go and whether it sends `If-Match` on a delete; it sends nothing. */\n private settle(): Promise<void> {\n this.probed ??= probeClient(this.client, this.bucket).then((facts) => {\n this.facts = facts;\n });\n return this.probed;\n }\n\n async putImmutable(\n key: GenKey,\n write: (sink: BlobSink) => Promise<void>,\n ): Promise<{ size: number; sha256: string }> {\n const objectKey = storageObjectKey(this.prefix, key); // validates ref + generation\n const sink = new S3MultipartSink(\n this.client,\n this.bucket,\n objectKey,\n this.partBytes,\n this.maxObjectBytes,\n this.readTimeoutMs,\n this.clock,\n this.rng,\n );\n try {\n await write(sink);\n return await sink.finish();\n } catch (err) {\n // Best-effort cleanup of any in-flight multipart upload, sent after an ambiguous or exhausted completion too.\n // S3 completes an upload atomically, so the abort never tears an object: a completion that had landed survives\n // it (the abort then answers NoSuchUpload, which is swallowed), and one that had not is cancelled and its parts\n // are reclaimed. No object is deleted here, and one that survives is the load's orphan above the pointer.\n await sink.abort();\n // A lost conditional-write race \u2014 the precondition failed (412) or S3 rejected concurrent conditional\n // writes to the key (409) \u2014 is the write-once conflict, never a silent overwrite.\n if (isConditionalConflict(err)) {\n throw new WriteConflictError(\n `generation already exists (write-once): ${key.segment}.${key.generation}`,\n );\n }\n if (isTypedPassThrough(err)) throw err;\n throw this.mapError(err);\n }\n }\n\n async getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array> {\n if (!Number.isInteger(offset) || !Number.isInteger(length) || offset < 0 || length < 0) {\n throw new ValidationError(`invalid range offset=${offset} length=${length}`);\n }\n const objectKey = storageObjectKey(this.prefix, key);\n if (length === 0) return new Uint8Array(0);\n return this.read('GetObject', key, async (options) => {\n const res = await this.client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: objectKey,\n Range: `bytes=${offset}-${offset + length - 1}`,\n }),\n options,\n );\n const bytes = await readBounded(\n res.Body,\n length,\n () => this.badRead(key, 'range', `the response is longer than the ${length}B requested`),\n res.ContentLength,\n );\n // A short read means the range ran past EOF \u2014 treat as out-of-bounds, never a partial result.\n if (bytes.length !== length) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds (got ${bytes.length}B)`,\n );\n }\n // The bytes must be the ones asked for, not as many from elsewhere: a range answer names its span, and one\n // without it (a 200, the whole object) is the range only when the range starts at 0.\n const span = spanFromContentRange(res.ContentRange);\n const wanted =\n span === undefined\n ? offset === 0\n : span.start === offset && span.end === offset + length - 1;\n if (!wanted) {\n throw this.badRead(\n key,\n 'range',\n `the response holds ${res.ContentRange === undefined ? 'the object from its start' : `bytes ${res.ContentRange}`}, not [${offset}, ${offset + length})`,\n );\n }\n return bytes;\n });\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<TailRead> {\n const objectKey = storageObjectKey(this.prefix, key);\n // The rule every driver's tail read holds to: a safe integer, and zero or less an empty tail.\n if (!Number.isSafeInteger(maxBytes)) {\n throw new ValidationError(`invalid tail length ${String(maxBytes)}`);\n }\n if (maxBytes <= 0) {\n // No tail bytes wanted \u2014 just resolve the size via a HEAD.\n const head = await this.head(key, objectKey);\n return withVersion({ bytes: new Uint8Array(0), size: head.size ?? 0 }, head.version);\n }\n const read = await this.read('GetObject', key, async (options) => {\n try {\n const res = await this.client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: objectKey,\n Range: `bytes=-${maxBytes}`,\n }),\n options,\n );\n const bytes = await readBounded(\n res.Body,\n maxBytes,\n () => this.badRead(key, 'tail', `the response is longer than the ${maxBytes}B requested`),\n res.ContentLength,\n );\n return { bytes, contentRange: res.ContentRange, version: res.ETag };\n } catch (err) {\n // A zero-byte object has no suffix to satisfy, so S3 refuses the range with a 416. The HEAD below settles\n // whether that is an empty object or a real range fault.\n if (isInvalidRange(err)) return { refused: err };\n throw err;\n }\n });\n if ('refused' in read) {\n const head = await this.head(key, objectKey);\n if (head.size !== 0) throw this.mapReadError(read.refused, key);\n return withVersion({ bytes: new Uint8Array(0), size: 0 }, head.version);\n }\n const { bytes, contentRange, version } = read;\n let size = totalFromContentRange(contentRange);\n if (contentRange !== undefined && !(size === 0 && bytes.length === 0)) {\n // A ranged answer must be the suffix asked for and hold exactly its bytes, as a range read's must. An empty\n // object has no suffix: a server may answer it with `bytes 0--1/0`.\n const span = spanFromContentRange(contentRange);\n const suffix =\n span !== undefined &&\n span.end - span.start + 1 === bytes.length &&\n (size === undefined ||\n (span.end === size - 1 && span.start === Math.max(0, size - maxBytes)));\n if (!suffix) {\n throw this.badRead(\n key,\n 'tail',\n `the response holds ${contentRange} in ${bytes.length}B, not the last ${maxBytes}B of the object`,\n );\n }\n }\n if (size === undefined) {\n // A spec-compliant backend omits Content-Range only on a 200 (whole object), where bytes.length\n // IS the size. If the body is exactly maxBytes we can't rule out a clamped partial from a\n // non-compliant backend \u2014 confirm the true size with a HEAD rather than trust a possibly-short read.\n size =\n bytes.length === maxBytes\n ? ((await this.head(key, objectKey)).size ?? bytes.length)\n : bytes.length;\n }\n // The version of the object the bytes came from: the ETag of the response that carried them.\n return withVersion({ bytes, size }, version);\n }\n\n private badRead(key: GenKey, what: 'range' | 'tail', why: string): ValidationError {\n return new ValidationError(\n `S3 ${what} read of ${key.segment}.${key.generation} refused: ${why}`,\n );\n }\n\n /**\n * The object's size and ETag from a `HeadObject`, each `undefined` when the response does not carry it. A missing\n * object throws {@link NotFoundError}.\n */\n private head(\n key: GenKey,\n objectKey: string,\n ): Promise<{ size: number | undefined; version: string | undefined }> {\n return this.read('HeadObject', key, async (options) => {\n const head = await this.client.send(\n new HeadObjectCommand({ Bucket: this.bucket, Key: objectKey }),\n options,\n );\n return { size: head.ContentLength, version: head.ETag };\n });\n }\n\n /**\n * One read request under the read timeout, its failures mapped to the driver vocabulary. The timeout's own\n * `TransientError` is raised outside the mapping, so it reaches the caller as it is.\n */\n private read<T>(\n operation: 'GetObject' | 'HeadObject',\n key: GenKey,\n run: (options: ReadSendOptions) => Promise<T>,\n ): Promise<T> {\n return timedRead(operation, this.readTimeoutMs, async (options) => {\n try {\n return await run(options);\n } catch (err) {\n throw this.mapReadError(err, key);\n } finally {\n this.sockets?.afterRequest();\n }\n });\n }\n\n async delete(key: GenKey, options?: StorageDeleteOptions): Promise<void> {\n const objectKey = storageObjectKey(this.prefix, key);\n const ifVersion = checkDeleteOptions(options);\n if (ifVersion !== undefined && this.explicitConditionalDelete !== false) await this.settle();\n const conditional =\n ifVersion !== undefined && appliesDeleteIfMatch(this.explicitConditionalDelete, this.facts);\n // Idempotent: S3 DeleteObject succeeds even if the key is absent (GC may race / retry).\n try {\n await this.client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: objectKey,\n ...(conditional ? { IfMatch: ifVersion } : {}),\n }),\n );\n } catch (raw) {\n const err = scrubCredentials(raw);\n if (conditional) {\n // An absent object is a no-op whatever version was given, and a failed precondition (`412`, or `409` for a\n // conditional request racing another on the key) is a conflict only while an object is under the key: a\n // service may answer it for a key with nothing there, a copy of this delete that landed first included.\n if (isNotFound(err)) return;\n if (isConditionalConflict(err)) {\n if (!(await this.exists(key, objectKey))) return;\n throw new WriteConflictError(\n `generation ${key.segment}.${key.generation} is another object than the version given; not deleted`,\n );\n }\n }\n throw this.mapError(err);\n }\n }\n\n /** Whether an object is under the key, from one `HeadObject`. A read that fails in any other way throws. */\n private async exists(key: GenKey, objectKey: string): Promise<boolean> {\n try {\n await this.head(key, objectKey);\n return true;\n } catch (err) {\n if (errorKind(err) === 'NotFoundError') return false;\n throw err;\n }\n }\n\n async *list(ref: SegmentRef): AsyncIterable<GenKey> {\n const prefix = segmentObjectPrefix(this.prefix, ref); // validates ref\n let token: string | undefined;\n do {\n let res;\n try {\n res = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n ContinuationToken: token,\n }),\n );\n } catch (err) {\n throw this.mapError(err);\n }\n for (const obj of res.Contents ?? []) {\n if (obj.Key === undefined) continue;\n const generation = parseGenerationFromKey(prefix, obj.Key);\n if (generation !== null) {\n yield { namespace: ref.namespace, segment: ref.segment, generation };\n }\n }\n // A page that says more follow and gives no way to ask for them would end the listing short, silently.\n if (res.IsTruncated === true && res.NextContinuationToken === undefined) {\n throw new IntegrityError('S3 listing says it is truncated but gives no continuation token');\n }\n // A page that hands back the token it was asked with would be asked for again, forever.\n if (res.IsTruncated === true && res.NextContinuationToken === token) {\n throw new IntegrityError('S3 listing returned the continuation token it was given');\n }\n token = res.IsTruncated === true ? res.NextContinuationToken : undefined;\n } while (token !== undefined);\n }\n\n /** Map S3 read errors to the driver vocabulary; pass everything else through {@link mapError}. */\n private mapReadError(err: unknown, key: GenKey): unknown {\n err = scrubCredentials(err);\n if (errorKind(err) === 'ValidationError') return err;\n if (isNotFound(err)) {\n return new NotFoundError(`no such generation: ${key.segment}.${key.generation}`);\n }\n // A fully out-of-range request (start past EOF) \u2014 the BlobReader contract treats range errors as\n // ValidationError, never a short/empty read.\n if (isInvalidRange(err)) {\n return new ValidationError(`range out of bounds for ${key.segment}.${key.generation}`);\n }\n return this.mapError(err);\n }\n\n /**\n * Reclassify a transient S3 fault (throttle/5xx/dropped connection) as a retryable {@link TransientError}, so the\n * store's read retry can ride it out and a write's caller can tell it from a deterministic failure, and a missing\n * bucket as a `ValidationError` that names it; everything else propagates unchanged. The final fallback at every\n * `client.send` site, so callers and the read retry only ever see typed errors.\n */\n private mapError(err: unknown): unknown {\n const scrubbed = asMissingBucket(scrubCredentials(err), this.bucket);\n return toTransient(scrubbed, isTransient, transientFaultMessage);\n }\n}\n\n/** A tail read, with the version it was read at when the response carried one. */\nfunction withVersion(\n tail: { bytes: Uint8Array; size: number },\n version: string | undefined,\n): TailRead {\n return version === undefined ? tail : { ...tail, version };\n}\n\n/**\n * Streaming {@link BlobSink} that uploads one S3 object in **constant memory**. It buffers at most\n * one part: as the codec writes, full parts are flushed via `UploadPart` and freed. A small object that never\n * reaches one part is committed as a single conditional `PutObject`; a larger one is finished with a\n * conditional `CompleteMultipartUpload` \u2014 **both enforce write-once** via `If-None-Match: *`, and both are sent\n * with the SDK's retry off, again only after a throttle ({@link commit}). SHA-256 is hashed incrementally. On any error\n * the caller invokes {@link abort} to clean up the in-flight multipart upload.\n */\nclass S3MultipartSink implements BlobSink {\n private readonly hash: Hash = createHash('sha256');\n private readonly pending: Uint8Array[] = [];\n private pendingLen = 0;\n private total = 0;\n private uploadId: string | undefined;\n private partNumber = 0;\n private readonly parts: { ETag: string | undefined; PartNumber: number }[] = [];\n /** This write's own id, stored in the object's user metadata, outside the `.crbm` bytes: see {@link commit}. */\n private readonly writeId = randomBytes(16).toString('hex');\n\n constructor(\n private readonly client: S3Client,\n private readonly bucket: string,\n private readonly objectKey: string,\n private readonly partBytes: number,\n private readonly maxObjectBytes: number,\n private readonly readTimeoutMs: number,\n private readonly clock: Sleeper,\n private readonly rng: Rng,\n ) {}\n\n async write(bytes: Uint8Array): Promise<void> {\n if (bytes.length === 0) return;\n this.total += bytes.length;\n if (this.total > this.maxObjectBytes) {\n // Fail fast + typed, rather than a late opaque S3 error (and abort the in-flight upload via the caller).\n throw new ValidationError(`object exceeds maxObjectBytes ${this.maxObjectBytes}`);\n }\n this.hash.update(bytes);\n // A copy: the caller may reuse its buffer once this write resolves, and the part is sent later.\n this.pending.push(new Uint8Array(bytes));\n this.pendingLen += bytes.length;\n if (this.pendingLen >= this.partBytes) await this.flushPart();\n }\n\n /** Upload the buffered bytes (\u2265 one part) as a single part, freeing them. Starts the upload on first call. */\n private async flushPart(): Promise<void> {\n if (this.uploadId === undefined) {\n const res = await this.client.send(\n new CreateMultipartUploadCommand({\n Bucket: this.bucket,\n Key: this.objectKey,\n Metadata: { [WRITE_ID_KEY]: this.writeId }, // stored on the object the completion makes\n }),\n );\n if (res.UploadId === undefined) {\n throw new TransientError('S3 CreateMultipartUpload returned no UploadId');\n }\n this.uploadId = res.UploadId;\n }\n const body = concatBytes(this.pending);\n this.pending.length = 0;\n this.pendingLen = 0;\n this.partNumber += 1;\n if (this.partNumber > S3_MAX_PARTS) {\n // Unreachable for valid input (the maxObjectBytes byte-cap, sized to \u2264 S3_MAX_PARTS parts, fires first) \u2014\n // a typed guard so the S3 hard limit is never a raw 400.\n throw new ValidationError(`multipart upload exceeded the S3 ${S3_MAX_PARTS}-part limit`);\n }\n const res = await this.client.send(\n new UploadPartCommand({\n Bucket: this.bucket,\n Key: this.objectKey,\n UploadId: this.uploadId,\n PartNumber: this.partNumber,\n Body: body,\n }),\n );\n this.parts.push({ ETag: res.ETag, PartNumber: this.partNumber });\n }\n\n /** Commit the object: a single conditional PUT if it fit in one part, else complete the multipart upload. */\n async finish(): Promise<{ size: number; sha256: string }> {\n const sha256 = this.hash.digest('hex');\n if (this.uploadId === undefined) {\n const body = concatBytes(this.pending);\n await this.commit('PutObject', () =>\n sendOnce(\n this.client,\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.objectKey,\n Body: body,\n IfNoneMatch: '*', // write-once\n Metadata: { [WRITE_ID_KEY]: this.writeId },\n }),\n ),\n );\n return { size: this.total, sha256 };\n }\n if (this.pendingLen > 0) await this.flushPart(); // the final part may be < partBytes (allowed)\n const uploadId = this.uploadId;\n await this.commit('CompleteMultipartUpload', () =>\n sendOnce(\n this.client,\n new CompleteMultipartUploadCommand({\n Bucket: this.bucket,\n Key: this.objectKey,\n UploadId: uploadId,\n MultipartUpload: { Parts: this.parts },\n IfNoneMatch: '*', // write-once: fail if the object already exists\n }),\n ),\n );\n this.uploadId = undefined; // completed \u2014 nothing left to abort\n return { size: this.total, sha256 };\n }\n\n /**\n * Send the commit (`send` sends it once, with the SDK's retry off), and again after a throttle, up to\n * {@link THROTTLE_RESENDS} times, waiting a full-jitter backoff before each. Any other failure is thrown at once.\n * Once the commit has been sent again, an answer that can be an earlier send that landed or is still landing (a\n * precondition failure, a concurrent-write conflict, or an upload S3 no longer knows) is settled by reading the\n * object back. The object is never overwritten, so the id it holds says who wrote it:\n *\n * - this write's own id: a success;\n * - another's id, or none (an object an earlier release wrote): the conflict it reads as;\n * - nothing stored: a `412` still reads as the conflict it answered (an object was there, and is gone). A `409`\n * and an unknown upload are an unknown outcome, since the first send may still be applying: {@link TransientError},\n * and never a report that nothing was written.\n *\n * A failed read-back throws, and is neither. Exhausted, it throws {@link TransientError}; nothing is deleted.\n */\n private async commit(\n operation: 'PutObject' | 'CompleteMultipartUpload',\n send: () => Promise<unknown>,\n ): Promise<void> {\n for (let resent = 0; ; resent++) {\n try {\n await send();\n return;\n } catch (raw) {\n const err = scrubCredentials(raw);\n if (resent > 0 && (isConditionalConflict(err) || isNoSuchUpload(err))) {\n const stored = await this.storedObject();\n if (stored?.id === this.writeId) return;\n if (stored === undefined && !isPreconditionFailed(err)) {\n throw new TransientError(\n `S3 ${operation}: ${this.objectKey} is not stored, and the first send may still be applying`,\n { cause: err },\n );\n }\n if (isConditionalConflict(err)) throw err;\n throw new WriteConflictError(`generation already exists (write-once): ${this.objectKey}`);\n }\n if (!isThrottle(err)) throw err;\n if (resent >= THROTTLE_RESENDS) {\n throw new TransientError(\n `S3 ${operation} was throttled on each of its ${resent + 1} sends; the object may or may not exist`,\n { cause: err },\n );\n }\n await this.clock.sleep(Math.floor(this.rng.next() * THROTTLE_BASE_DELAY_MS * 2 ** resent));\n }\n }\n }\n\n /**\n * The object stored under the key: `undefined` when none is there, otherwise the write id it carries (`id` is\n * `undefined` for an object that carries none). A failed read throws.\n */\n private storedObject(): Promise<{ readonly id: string | undefined } | undefined> {\n return timedRead('HeadObject', this.readTimeoutMs, async (options) => {\n try {\n const head = await this.client.send(\n new HeadObjectCommand({ Bucket: this.bucket, Key: this.objectKey }),\n options,\n );\n return { id: head.Metadata?.[WRITE_ID_KEY] };\n } catch (err) {\n if (isNotFound(err)) return undefined;\n throw err;\n }\n });\n }\n\n /** Best-effort cleanup of an in-flight multipart upload after an error (a leaked MPU is reaped by a bucket\n * lifecycle rule; never a correctness issue). No-op if nothing was started or it already completed. It aborts an\n * upload, never an object: a completion that landed survives it. */\n async abort(): Promise<void> {\n if (this.uploadId === undefined) return;\n const id = this.uploadId;\n this.uploadId = undefined;\n try {\n await this.client.send(\n new AbortMultipartUploadCommand({ Bucket: this.bucket, Key: this.objectKey, UploadId: id }),\n );\n } catch {\n // swallow \u2014 best-effort\n }\n }\n}\n", "/**\n * Pure helpers for classifying S3 SDK errors + parsing response headers (conflict and transient classification).\n *\n * Kept SDK-free and side-effect-free (they only read structural shapes \u2014 `err.name`,\n * `$metadata.httpStatusCode`, a `Content-Range` string) so the subtle S3-specific translation logic is\n * unit-testable without a live MinIO/S3 or even the AWS SDK. The AWS error shapes come from `./aws-errors`.\n */\n\nimport { ValidationError } from '@cloudbitmaps/core/driver-kit';\nimport {\n errorName,\n httpStatus,\n isNetworkOrTimeout,\n isSdkRetryable,\n isServerSide,\n} from './aws-errors';\n\n/** A conditional `If-None-Match: *` PUT lost the write-once race (the object already existed). */\nexport function isPreconditionFailed(err: unknown): boolean {\n return errorName(err) === 'PreconditionFailed' || httpStatus(err) === 412;\n}\n\n/**\n * A conditional write (`If-None-Match: *` / `If-Match: <etag>`) lost the race \u2014 **either** outcome S3 uses:\n * the precondition evaluated false (`412 PreconditionFailed`), **or** S3 rejected concurrent conditional\n * writes to the same key to prevent a lost update (`409 ConditionalRequestConflict`, which AWS documents and\n * asks you to retry). Both mean \"you lost; re-read and retry\" \u2014 so both must map to `WriteConflictError` and\n * route through the caller's OCC path, never a blind transient retry (which would just replay a doomed PUT).\n */\nexport function isConditionalConflict(err: unknown): boolean {\n return (\n isPreconditionFailed(err) ||\n errorName(err) === 'ConditionalRequestConflict' ||\n httpStatus(err) === 409\n );\n}\n\n/**\n * The service asked the client to slow down: `503 SlowDown`, any other `503` (`ServiceUnavailable`), or a bare `429`\n * (`TooManyRequests`), which AWS S3 does not send and some S3-compatible services do. The SDK raises it with `name` set\n * to the code and `$metadata.httpStatusCode` to the status, and no `$retryable` marker. The service does not document\n * that a throttled request was not applied, so a write sent again after one must still tell a landed first send apart.\n */\nexport function isThrottle(err: unknown): boolean {\n const status = httpStatus(err);\n return errorName(err) === 'SlowDown' || status === 503 || status === 429;\n}\n\n/** A multipart upload id S3 no longer knows: completed, aborted or expired (`404 NoSuchUpload`). */\nexport function isNoSuchUpload(err: unknown): boolean {\n return errorName(err) === 'NoSuchUpload';\n}\n\n/**\n * The bucket itself does not exist (`404 NoSuchBucket`): a wiring fault, never an absent object. Read as one, a\n * misnamed or deleted bucket would answer every read as an empty segment. A HEAD carries no code, so it cannot be told\n * apart there; every request that reaches a missing bucket with a body says so.\n */\nexport function isMissingBucket(err: unknown): boolean {\n return errorName(err) === 'NoSuchBucket';\n}\n\n/**\n * `err`, or the {@link ValidationError} a missing bucket is: it names the bucket and what to check, and carries the\n * service's error as its `cause`. A wiring fault is not an absent object, so it is not a `NotFoundError`: a read\n * re-resolves on that, and would treat a misnamed bucket as a miss it can heal.\n */\nexport function asMissingBucket(err: unknown, bucket: string): unknown {\n if (!isMissingBucket(err)) return err;\n return Object.assign(\n new ValidationError(\n `the S3 bucket does not exist: ${bucket}; check the bucket name, and the region and endpoint of the client`,\n ),\n { cause: err },\n );\n}\n\n/**\n * The object / generation does not exist (GetObject \u2192 `NoSuchKey`, HeadObject \u2192 `NotFound`; both 404), in a bucket\n * that does: a 404 that says the bucket is missing is not one.\n */\nexport function isNotFound(err: unknown): boolean {\n if (isMissingBucket(err)) return false;\n const name = errorName(err);\n return name === 'NoSuchKey' || name === 'NotFound' || httpStatus(err) === 404;\n}\n\n/** A range request started past EOF (HTTP 416). */\nexport function isInvalidRange(err: unknown): boolean {\n return errorName(err) === 'InvalidRange' || httpStatus(err) === 416;\n}\n\n/**\n * S3 refused the request's signature because the client's clock was off by minutes, and the SDK has corrected the\n * clock for the next request. Nothing was applied, and a second request is signed right. The SDK's own retry treats\n * this as transient; a conditional write is sent once without that retry, so the driver has to say so itself.\n */\nfunction isClockSkewCorrected(err: unknown): boolean {\n const e = err as { $metadata?: { clockSkewCorrected?: unknown } } | null;\n return e?.$metadata?.clockSkewCorrected === true;\n}\n\n/**\n * A transient S3 fault that is safe to retry: throttling (`SlowDown` / 503 / 429), any 5xx, a dropped/timed-out\n * connection, a clock-skew refusal the SDK has corrected for, or anything the SDK itself marks retryable. Excludes\n * the deterministic outcomes above (412/404/416) \u2014 those are caller-meaningful and must never be\n * retried/reclassified.\n */\nexport function isTransient(err: unknown): boolean {\n // A conditional-write conflict (412/409) is caller-meaningful OCC, not a blind-retryable transient.\n if (isConditionalConflict(err) || isNotFound(err) || isInvalidRange(err)) return false;\n return (\n isThrottle(err) ||\n isServerSide(err) ||\n isNetworkOrTimeout(err) ||\n isSdkRetryable(err) ||\n isClockSkewCorrected(err)\n );\n}\n\n/**\n * The message a transient S3 fault is handed on with: `transient S3 fault: <name>`, the SDK error's name, which names\n * S3's error code (`SlowDown`, `InternalError`) or the transport failure (`TimeoutError`).\n */\nexport function transientFaultMessage(err: unknown): string {\n return `transient S3 fault: ${errorName(err) ?? 'unknown'}`;\n}\n\n/** The first and last byte a `Content-Range: bytes <start>-<end>/<total>` header names, or `undefined` if unparseable. */\nexport function spanFromContentRange(\n contentRange: string | undefined,\n): { start: number; end: number } | undefined {\n if (contentRange === undefined) return undefined;\n const match = /^\\s*bytes\\s+(\\d+)-(\\d+)\\//.exec(contentRange);\n if (match === null) return undefined;\n const start = Number(match[1]);\n const end = Number(match[2]);\n return Number.isSafeInteger(start) && Number.isSafeInteger(end) ? { start, end } : undefined;\n}\n\n/**\n * Parse the total object size out of a `Content-Range: bytes <start>-<end>/<total>` header, or `undefined`\n * if absent/unparseable/unsafe. The total is the part after the final `/`.\n */\nexport function totalFromContentRange(contentRange: string | undefined): number | undefined {\n if (contentRange === undefined) return undefined;\n const match = /\\/(\\d+)\\s*$/.exec(contentRange);\n if (match === null) return undefined;\n const total = Number(match[1]);\n return Number.isSafeInteger(total) ? total : undefined;\n}\n", "/**\n * `sendOnce` \u2014 send a conditional write exactly once, with the SDK's retry off for that one command.\n *\n * The SDK re-sends a request whose response it did not get: a timeout, a reset connection, a 5xx. For a conditional\n * write that is the wrong thing to do. When the write landed and only its response was lost, the second send meets\n * the first \u2014 `If-None-Match: *` finds the object it created, `If-Match` finds the ETag it replaced \u2014 and fails with\n * `412`, which the driver can only report as a lost race. The caller is told its write lost when it won. Only the\n * caller can find out which happened, by reading the pointer or listing the generations, so the write is sent once\n * and a transient failure reaches it as `TransientError`.\n *\n * **How.** The client runs its retry as one middleware, `retryMiddleware`, at high priority in the `finalizeRequest`\n * step, whatever retry strategy or `maxAttempts` it was built with. When a command is sent, its own middleware stack\n * is merged over the client's, and an entry with the same name, step and priority that sets `override` replaces the\n * client's. So this command runs with a pass-through where the retry was, and nothing about the client changes: a\n * caller's own client keeps its configuration, and every other command sent through it keeps its retry.\n *\n * The options object passed to `send` matters too. A client built with `cacheMiddleware: true` reuses the handler it\n * resolved for the first command of a class, and that handler holds the retry; `send` resolves afresh whenever it is\n * given options, so the replacement always takes effect.\n */\nimport type {\n $Command,\n S3Client,\n S3ClientResolvedConfig,\n ServiceInputTypes,\n ServiceOutputTypes,\n} from '@aws-sdk/client-s3';\n\n/** Where the client registers its retry. Overriding an entry takes the same name, step and priority. */\nconst NO_RETRY = {\n name: 'retryMiddleware',\n step: 'finalizeRequest',\n priority: 'high',\n override: true,\n} as const;\n\n/** Send `command` once: its retry step is a pass-through, and the client's stays as it is for every other command. */\nexport function sendOnce<Input extends ServiceInputTypes, Output extends ServiceOutputTypes>(\n client: S3Client,\n command: $Command<Input, Output, S3ClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes>,\n): Promise<Output> {\n command.middlewareStack.add((next) => next, NO_RETRY);\n return client.send(command, {});\n}\n", "/**\n * Read an S3 response body as a stream, counting bytes, and stop at the first byte past `maxBytes`: a server that\n * ignores `Range`, or sends a body with no `Content-Length`, cannot make the caller buffer more than it asked for.\n * The body is destroyed on every exit that leaves it unread, which closes its connection.\n *\n * A body that is not a stream (a stand-in with only `transformToByteArray`) is read whole and its length checked after.\n */\nimport { NotFoundError } from '@cloudbitmaps/core/driver-kit';\n\n/** The `GetObject` response body, narrowed to the parts used. */\ntype ResponseBody =\n | {\n transformToByteArray?: () => Promise<Uint8Array>;\n [Symbol.asyncIterator]?: () => AsyncIterator<unknown>;\n destroy?: () => void;\n }\n | undefined;\n\n/** Destroy a response body left unread, which releases its connection; a body with no `destroy` is left alone. */\nexport function destroyBody(body: unknown): void {\n (body as { destroy?: () => void } | undefined)?.destroy?.();\n}\n\n/**\n * The body's bytes, at most `maxBytes` of them. `oversize` builds the typed error thrown when the count passes\n * `maxBytes`, or when the response advertises more than that before a byte is read; any other failure of the stream\n * propagates as it is.\n */\nexport async function readBounded(\n body: ResponseBody,\n maxBytes: number,\n oversize: () => Error,\n advertised?: number,\n): Promise<Uint8Array> {\n if (body === undefined) throw new NotFoundError('S3 GetObject returned an empty body');\n if (advertised !== undefined && advertised > maxBytes) {\n destroyBody(body);\n throw oversize();\n }\n if (typeof body[Symbol.asyncIterator] !== 'function') {\n const bytes = await body.transformToByteArray!();\n if (bytes.length > maxBytes) throw oversize();\n return bytes;\n }\n const chunks: Uint8Array[] = [];\n let total = 0;\n let done = false;\n try {\n for await (const chunk of body as AsyncIterable<Uint8Array | string>) {\n const piece = typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk;\n total += piece.length;\n if (total > maxBytes) throw oversize();\n chunks.push(piece);\n }\n done = true;\n } finally {\n if (!done) destroyBody(body);\n }\n const out = new Uint8Array(total);\n let at = 0;\n for (const piece of chunks) {\n out.set(piece, at);\n at += piece.length;\n }\n return out;\n}\n", "/**\n * `probeClient` \u2014 what an `S3Client` would send for the requests the registry makes, found out without sending one.\n *\n * Two facts about a client decide whether the registry may rely on S3's conditional requests, and neither shows on the\n * client's configuration:\n *\n * - **Where a request goes.** A constructor `endpoint` is on the resolved config, but an endpoint from\n * `AWS_ENDPOINT_URL_S3`, `AWS_ENDPOINT_URL` or an `endpoint_url` in the shared config file is read only when the\n * first request is built, and the client's `isCustomEndpoint` stays `false` for all of them. A store reached that way\n * (MinIO, LocalStack) can accept `If-Match` on a delete and ignore it.\n * - **Whether the SDK sends the header at all.** The SDK serialises the members its model knows, and an SDK that\n * predates one drops it from the request without a word, so a conditional write or delete goes out unconditional.\n *\n * So the probe builds the requests the registry sends and runs them through a **second client of the same class**,\n * made from the first one's resolved configuration, with a placeholder credential, a transport that is never handed a\n * request and a silent logger. It reads each request back as it stands once the SDK has serialised it, which is before\n * it is signed or sent: a middleware added to the probe command's own stack answers there instead of passing the\n * request on. The second client is the SDK's own resolution of everything that decides where a request goes (region,\n * endpoint, the environment, the shared config file, FIPS, dual-stack, path style), and the client the caller holds is\n * never touched: nothing it has been given runs, so no middleware of the caller's counts a request that never exists,\n * no logger prints one, and no credential is looked up.\n *\n * The probe runs the command through the second client's stack itself, as its `send` would, and does not call `send`: a\n * stub of `S3Client.prototype.send` (a class-level mock, as `aws-sdk-client-mock` installs) records nothing from it.\n *\n * A middleware the caller added to its own client that changes where a request goes is therefore not seen.\n *\n * A probe that cannot run (a client with no resolved config, an unresolvable region, a stack that does not hold the\n * step the probe hooks) answers `undefined`, which callers read as \"not known\".\n */\nimport { DeleteObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';\nimport type {\n $Command,\n S3Client,\n S3ClientResolvedConfig,\n ServiceInputTypes,\n ServiceOutputTypes,\n} from '@aws-sdk/client-s3';\n\n/** What a client does with the requests the registry sends. */\nexport interface ClientFacts {\n /** The host a request to the bucket is addressed to, as the client resolves it now. */\n readonly host: string;\n /** Whether a `DeleteObject` given `IfMatch` goes out with an `If-Match` header. */\n readonly sendsDeleteIfMatch: boolean;\n /** Whether a `PutObject` given `IfMatch` goes out with an `If-Match` header: the registry's compare-and-swap. */\n readonly sendsPutIfMatch: boolean;\n /** Whether a `PutObject` given `IfNoneMatch` goes out with an `If-None-Match` header: the registry's create. */\n readonly sendsPutIfNoneMatch: boolean;\n}\n\n/** A key no registry row or generation can hold; the probe's requests are never sent, so it is never used. */\nexport const PROBE_KEY = 'cloudbitmaps-probe';\n\n/** The request as the SDK serialises it, as far as the probe reads it. */\ninterface SerialisedRequest {\n readonly hostname?: unknown;\n readonly headers?: Record<string, unknown>;\n}\n\n/** A credential no one can use: the probe signs nothing, but the client it builds wants one. */\nconst PLACEHOLDER_CREDENTIALS = { accessKeyId: 'cloudbitmaps-probe', secretAccessKey: 'unused' };\n\n/** A transport that is never handed a request: the probe answers before the step that would send one. */\nconst NEVER_SENDS = {\n handle: (): Promise<never> =>\n Promise.reject(new Error('the client probe was asked to send a request')),\n updateHttpClientConfig: (): void => {},\n httpHandlerConfigs: (): Record<string, never> => ({}),\n destroy: (): void => {},\n};\n\nconst SILENT_LOGGER = {\n debug: (): void => {},\n info: (): void => {},\n warn: (): void => {},\n error: (): void => {},\n};\n\n/**\n * A second client like `client`: its class (so the SDK that resolves where a request goes is the caller's), its resolved\n * configuration, and none of what the caller added to it. `undefined` for anything that is not a client of that shape.\n */\nfunction cloneForProbe(client: S3Client): S3Client | undefined {\n const parts = client as unknown as { config?: unknown; constructor?: unknown; send?: unknown };\n if (\n typeof parts.send !== 'function' ||\n typeof parts.config !== 'object' ||\n parts.config === null\n ) {\n return undefined;\n }\n if (typeof parts.constructor !== 'function' || parts.constructor === Object) return undefined;\n const Class = parts.constructor as new (config: object) => S3Client;\n const clone = new Class({\n ...parts.config,\n credentials: PLACEHOLDER_CREDENTIALS,\n requestHandler: NEVER_SENDS,\n logger: SILENT_LOGGER,\n });\n return typeof clone.send === 'function' && clone.middlewareStack != null ? clone : undefined;\n}\n\n/**\n * Run `command` through `client`'s stack up to the step after the SDK serialises the request, and return the request.\n * The answer is made there, so the rest of the stack (retry, signing, the transport) never runs.\n */\nasync function serialise<Input extends ServiceInputTypes, Output extends ServiceOutputTypes>(\n client: S3Client,\n command: $Command<Input, Output, S3ClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes>,\n): Promise<SerialisedRequest> {\n let seen: SerialisedRequest | undefined;\n command.middlewareStack.addRelativeTo(\n () => (args: { request?: unknown }) => {\n seen = args.request as SerialisedRequest;\n return Promise.resolve({ response: {}, output: { $metadata: {} } as unknown as Output });\n },\n { relation: 'after', toMiddleware: 'serializerMiddleware', name: 'cloudbitmapsProbe' },\n );\n // What `send` does, minus `send`: a stub of `S3Client.prototype.send` (a class-level mock) sees nothing of the probe.\n const handler = command.resolveMiddleware(client.middlewareStack, client.config, {});\n await handler(command);\n if (seen === undefined) throw new Error('the probe saw no request');\n return seen;\n}\n\nconst hasHeader = (request: SerialisedRequest, name: string): boolean =>\n request.headers?.[name] !== undefined;\n\n/**\n * What `client` does with a conditional `DeleteObject` and `PutObject` to `bucket`, or `undefined` when it cannot be\n * found out. Never throws, never sends a request, and never runs anything the caller added to `client`.\n */\nexport async function probeClient(\n client: S3Client,\n bucket: string,\n): Promise<ClientFacts | undefined> {\n let probe: S3Client | undefined;\n try {\n probe = cloneForProbe(client);\n if (probe === undefined) return undefined;\n const del = await serialise(\n probe,\n new DeleteObjectCommand({ Bucket: bucket, Key: PROBE_KEY, IfMatch: '\"probe\"' }),\n );\n const put = await serialise(\n probe,\n new PutObjectCommand({\n Bucket: bucket,\n Key: PROBE_KEY,\n Body: new Uint8Array(0),\n IfMatch: '\"probe\"',\n IfNoneMatch: '*',\n }),\n );\n if (typeof del.hostname !== 'string' || del.hostname === '') return undefined;\n return {\n host: del.hostname,\n sendsDeleteIfMatch: hasHeader(del, 'if-match'),\n sendsPutIfMatch: hasHeader(put, 'if-match'),\n sendsPutIfNoneMatch: hasHeader(put, 'if-none-match'),\n };\n } catch {\n return undefined;\n } finally {\n probe?.destroy();\n }\n}\n\n/**\n * Whether a `DeleteObject` may rely on `If-Match`, for the registry's row delete and the storage half's conditional\n * delete alike: `explicit` when the caller set it, but never for an SDK seen not to send the header; otherwise only\n * when the client resolves to an AWS S3 host, which applies the precondition, and `false` until the client has been\n * asked, since an S3-compatible store (MinIO, for one) can accept the header and ignore it.\n */\nexport function appliesDeleteIfMatch(\n explicit: boolean | undefined,\n facts: ClientFacts | undefined,\n): boolean {\n if (explicit === false) return false;\n if (explicit === true) return facts?.sendsDeleteIfMatch !== false;\n return facts !== undefined && facts.sendsDeleteIfMatch && isAwsS3Host(facts.host);\n}\n\n/** The domains AWS serves S3 from: the standard partition, and China's. */\nconst AWS_DOMAINS = ['.amazonaws.com', '.amazonaws.com.cn'];\n\n/** The labels that name S3 itself in one of its hosts. */\nconst S3_LABELS = new Set([\n 's3',\n 's3-fips',\n 's3-accesspoint',\n 's3-accesspoint-fips',\n 's3-global',\n 's3-object-lambda',\n 's3-outposts',\n 's3-external-1',\n]);\n/** The host form that joins the region to the label with a dash, which AWS also serves: `s3-us-west-2`, `s3-fips-us-gov-west-1`. */\nconst S3_DASH_REGION_LABEL = /^s3-(fips-)?[a-z]{2}(-gov)?-[a-z]+-\\d$/;\n/** A directory bucket's zonal label: `s3express-use1-az4`. */\nconst S3_EXPRESS_LABEL = /^s3express-[a-z0-9-]+$/;\nconst REGION_LABEL = /^[a-z]{2}(-[a-z]+)+-\\d+$/;\n\nconst isS3Label = (label: string): boolean =>\n S3_LABELS.has(label) || S3_DASH_REGION_LABEL.test(label) || S3_EXPRESS_LABEL.test(label);\n\n/**\n * Whether `hostname` is an AWS S3 host, by its structure: under an AWS domain, with a label that names S3 (`s3`,\n * `s3-fips`, an access point's, a multi-region access point's `s3-global`, Object Lambda's, an Outpost's, a dash-joined\n * `s3-<region>`, a directory bucket's `s3express-\u2026`), followed only by what S3's hosts put there: `dualstack`, a\n * region and `vpce`, in that order. Anything may come before it (a bucket, an access point, a VPC endpoint id). So\n * the FIPS, dual-stack, access-point and VPC interface forms all count, and another AWS service's host whose name\n * starts with `s3` does not: a load balancer, an API Gateway, a website endpoint. A host of any other domain is not,\n * whatever it speaks: a store behind one has to be vouched for by the caller.\n */\nexport function isAwsS3Host(hostname: string): boolean {\n const host = hostname.toLowerCase().replace(/\\.$/, '');\n const domain = AWS_DOMAINS.find((d) => host.endsWith(d));\n if (domain === undefined) return false;\n const labels = host.slice(0, -domain.length).split('.');\n // The last S3 label is the service's: a bucket's own name may hold one (`my.s3.bucket`).\n let at = -1;\n labels.forEach((label, i) => {\n if (isS3Label(label)) at = i;\n });\n if (at === -1) return false;\n const rest = labels.slice(at + 1);\n let k = 0;\n if (rest[k] === 'dualstack') k++;\n if (k < rest.length && REGION_LABEL.test(rest[k]!)) k++;\n if (rest[k] === 'vpce') k++;\n return k === rest.length;\n}\n", "/**\n * `S3RegistryDriver` \u2014 an {@link IRegistryDriver} over S3-compatible object storage.\n *\n * Lets a **read-mostly deployment run on S3 alone** \u2014 storage `.crbm` generations + the registry in one bucket,\n * no separate database. The protocol (an ABA-safe OCC token, the delete and its tombstone, the bounded retry, the key\n * layout) lives once in {@link ObjectStoreRegistry}; this file is only the I/O calls S3 makes, so the S3, GCS\n * and Azure registries cannot drift from one another.\n *\n * **A delete removes the row for good** with a `DeleteObject` under `If-Match: <etag>`, sent once like the writes,\n * when `conditionalDelete` is on. By default it is on when the host the client resolves is an AWS S3 host, whichever way\n * the endpoint was set (a constructor `endpoint`, `AWS_ENDPOINT_URL_S3`, `AWS_ENDPOINT_URL`, the shared config file), and\n * off for any other, since an S3-compatible store may accept the header and ignore it (MinIO does). The client is asked\n * once, before the registry's first request, and the SDK is checked to send the header at all ({@link probeClient}).\n *\n * **The atomic swap is offloaded to S3's conditional writes** (GA Nov 2024): `If-None-Match: *` for\n * create-only and `If-Match: <etag>` for compare-and-swap, so a concurrent writer between our read and our\n * PUT loses with a `412` \u2192 {@link WriteConflictError}. Each conditional PUT is sent once, with the SDK's retry off\n * for it ({@link sendOnce}), so a `412` means another write got there first, never this one meeting itself after a\n * lost response. A transient failure reaches the caller as {@link TransientError}: the write may or may not have\n * landed, and the caller re-reads the row to learn where it stands. Reads are strongly consistent (S3, since 2020),\n * satisfying the registry's `strongRead` contract, and each can be timed as the storage driver's are ({@link timedRead}):\n * with `readTimeoutMs` set, a row's `GetObject` that has not finished, body included, after it throws\n * {@link TransientError}. It is off by default, and the writes and listings are never timed. The client is **injected**, exactly like {@link S3StorageDriver}.\n *\n * **Deployment requirements** (a backend/policy that violates these silently corrupts the registry):\n * - The backend **must honor `If-Match`** (AWS S3; recent MinIO). One that returns ETags but ignores the\n * precondition degrades compare-and-swap to last-write-wins \u2192 lost `currentGen` swaps. Verified against\n * real S3 semantics by the MinIO integration lane.\n * - The IAM principal needs **`s3:ListBucket`** on the bucket. Without it, `GetObject` on a missing key\n * returns `403` (not `404`), so the \"absent segment \u2192 `null`\" contract (and `create`'s bootstrap read)\n * breaks \u2014 and `list()` needs it regardless.\n * - **Do not apply an S3 lifecycle-expiration rule to the `registry/` prefix that expires a current version**\n * (`NoncurrentVersionExpiration` is safe). See {@link ObjectStoreRegistry}.\n */\nimport {\n errorKind,\n IntegrityError,\n MAX_ROW_BYTES,\n ObjectStoreRegistry,\n ValidationError,\n WriteConflictError,\n normalizeObjectPrefix,\n requireBool,\n toTransient,\n} from '@cloudbitmaps/core/driver-kit';\nimport type { ObjectRegistryStore, ObjectRow } from '@cloudbitmaps/core/driver-kit';\nimport {\n DeleteObjectCommand,\n GetObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n type S3Client,\n} from '@aws-sdk/client-s3';\nimport { resolveReadTimeoutMs, timedRead } from './read-timeout';\nimport {\n asMissingBucket,\n isConditionalConflict,\n isNotFound,\n isTransient,\n transientFaultMessage,\n} from './s3-errors';\nimport { sendOnce } from './send-once';\nimport { destroyBody, readBounded } from './bounded-body';\nimport { scrubCredentials } from './scrub-error';\nimport { appliesDeleteIfMatch, probeClient } from './client-probe';\nimport type { ClientFacts } from './client-probe';\n\ninterface S3RegistryDriverOptions {\n /** A constructed S3 client (point its `endpoint` at MinIO for local/integration use). */\n readonly client: S3Client;\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional key prefix under which all registry objects live (e.g. `cloudbitmaps/`). */\n readonly prefix?: string;\n /** Injected clock for `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n /**\n * How long one read of a row \u2014 its `GetObject`, the body included \u2014 may take before it is abandoned and throws\n * `TransientError`, in ms. `0`, the default, sets no timeout. Must be a non-negative safe integer no larger than\n * 2,147,483,647. Writes and listings are not timed.\n *\n * The clock starts when the read is handed to the SDK, so it also counts the time the read waits for one of the\n * client's sockets (128 by default on the client the store builds, 50 on the SDK's own) and the time spent fetching\n * credentials, and under `retryMode: 'adaptive'` the SDK's rate-limiter wait. A burst of concurrent reads larger than\n * the socket pool can therefore time out with nothing slow on the wire: size the timeout above the worst queueing\n * your concurrency implies, or raise `maxSockets` (the store's option, or your own client's). On a client built with\n * `cacheMiddleware: true`, a timed read resolves its middleware each time.\n */\n readonly readTimeoutMs?: number;\n /**\n * Whether a delete removes a row for good, by a `DeleteObject` sent with `If-Match: <the ETag it read>`, rather than\n * leaving a tombstone.\n *\n * Defaults to `true` when the host the client resolves is an AWS S3 host (`*.amazonaws.com`, `*.amazonaws.com.cn`: the\n * FIPS, dual-stack, access-point and VPC interface forms included), where `If-Match` on `DeleteObject` is documented\n * for general purpose and directory buckets, and to `false` for any other host, or when the host cannot be resolved.\n * The host is the one the SDK resolves for a request, so an endpoint set through `AWS_ENDPOINT_URL_S3`,\n * `AWS_ENDPOINT_URL` or an `endpoint_url` in the shared config file counts exactly as a constructor `endpoint` does.\n * It is resolved once, lazily: until the registry's first read or listing the answer reads `false`, and a client that\n * cannot be resolved then (no region) keeps tombstoning for the life of the registry, so set the\n * option to override it.\n *\n * Set it explicitly to override the host. An S3-compatible store must apply the precondition before you set it there:\n * MinIO, for one, ignores it and deletes anyway, and on such a store two sweepers and a re-create can delete a live\n * row. A `true` never overrides the SDK: one that does not send `If-Match` on a `DeleteObject` leaves the row\n * tombstoned whatever is set.\n */\n readonly conditionalDelete?: boolean;\n}\n\n/**\n * The calls {@link ObjectStoreRegistry} needs, in S3's dialect. Exported for the tests that drive one directly.\n *\n * `conditionalDelete` is the caller's explicit setting, or `undefined` to follow the client. What the client does is\n * found out once, before the first read, listing or write ({@link probeClient}), so by the time the registry decides\n * how to delete it has been.\n */\nexport class S3RegistryStore implements ObjectRegistryStore {\n readonly label = 'S3';\n private probed: Promise<void> | undefined;\n private facts: ClientFacts | undefined;\n\n constructor(\n private readonly client: S3Client,\n private readonly bucket: string,\n private readonly readTimeoutMs: number,\n private readonly explicitConditionalDelete?: boolean,\n ) {}\n\n /**\n * Whether a delete may rely on `If-Match`: the caller's setting, else whether the client's host is AWS S3, and in\n * neither case when the SDK is seen not to send the header. `false` until the client has been asked, unless the caller\n * vouched for the store.\n */\n get conditionalDelete(): boolean {\n return appliesDeleteIfMatch(this.explicitConditionalDelete, this.facts);\n }\n\n /** Ask the client once what it does with the registry's requests; every caller waits for the same answer. */\n private settle(): Promise<void> {\n this.probed ??= probeClient(this.client, this.bucket).then((facts) => {\n this.facts = facts;\n });\n return this.probed;\n }\n\n /** Refuse a write the SDK would send without its precondition: it would land as a plain overwrite. */\n private refuseUnfencedWrites(expect: 'absent' | { version: string }): void {\n const header = expect === 'absent' ? 'If-None-Match' : 'If-Match';\n const sent =\n expect === 'absent' ? this.facts?.sendsPutIfNoneMatch : this.facts?.sendsPutIfMatch;\n if (sent === false) {\n throw new ValidationError(\n `the S3 client does not send ${header} on a PutObject: its @aws-sdk/client-s3 predates the member, so a ` +\n `registry write would overwrite whatever is there and lose a concurrent writer's change. Use a newer ` +\n `@aws-sdk/client-s3; the registry sent nothing`,\n );\n }\n }\n\n async read(key: string): Promise<ObjectRow | null> {\n await this.settle();\n return timedRead('GetObject', this.readTimeoutMs, async (options) => {\n let res;\n try {\n res = await this.client.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: key }),\n options,\n );\n } catch (err) {\n if (isNotFound(err)) return null;\n throw mapError(err, this.bucket);\n }\n try {\n // Check the advertised length BEFORE allocating, so a hostile object cannot make us buffer it first.\n if ((res.ContentLength ?? 0) > MAX_ROW_BYTES) {\n throw new IntegrityError(\n `registry object ${res.ContentLength}B exceeds cap ${MAX_ROW_BYTES}B`,\n );\n }\n if (res.Body === undefined) {\n throw new IntegrityError(`registry object has an empty body: ${key}`);\n }\n const bytes = await readBounded(\n res.Body,\n MAX_ROW_BYTES,\n () => new IntegrityError(`registry object exceeds cap ${MAX_ROW_BYTES}B`),\n );\n return { bytes, version: res.ETag ?? '' };\n } catch (err) {\n // A row refused before its body is read would otherwise hold its connection open until the server gives up\n // on it; destroying the body closes the socket. On a body that already failed it changes nothing.\n destroyBody(res.Body);\n // A body cut off part-way is a dropped connection, and transient; a refused row is not.\n throw errorKind(err) === 'IntegrityError' ? err : mapError(err, this.bucket);\n }\n });\n }\n\n async write(\n key: string,\n body: Uint8Array,\n expect: 'absent' | { version: string },\n ): Promise<void> {\n await this.settle();\n this.refuseUnfencedWrites(expect);\n try {\n // Sent once: a replay of a write that landed would fail its own precondition, and read as a lost race.\n await sendOnce(\n this.client,\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ContentType: 'application/json',\n IfNoneMatch: expect === 'absent' ? '*' : undefined,\n IfMatch: expect === 'absent' ? undefined : expect.version,\n }),\n );\n } catch (err) {\n // A lost conditional-write race (412 precondition, or 409 concurrent-conflict) is an OCC conflict.\n if (isConditionalConflict(err)) {\n throw new WriteConflictError(`registry OCC conflict for ${key}`);\n }\n throw mapError(err, this.bucket);\n }\n }\n\n async delete(key: string, expect: { version: string }): Promise<void> {\n try {\n // Sent once, as the writes are: a replay that met its own landed delete would read as a lost race.\n await sendOnce(\n this.client,\n new DeleteObjectCommand({ Bucket: this.bucket, Key: key, IfMatch: expect.version }),\n );\n } catch (err) {\n // A 412 (the object moved on), a 409 (a concurrent conditional request) or a 404 (it is gone) all mean the\n // version this delete was conditioned on is not there to delete.\n if (isConditionalConflict(err) || isNotFound(err)) {\n throw new WriteConflictError(`registry OCC conflict deleting ${key}`);\n }\n throw mapError(err, this.bucket);\n }\n }\n\n async *listKeys(prefix: string): AsyncIterable<string> {\n await this.settle();\n let token: string | undefined;\n do {\n let res;\n try {\n res = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n ContinuationToken: token,\n }),\n );\n } catch (err) {\n throw mapError(err, this.bucket);\n }\n for (const obj of res.Contents ?? []) {\n if (obj.Key !== undefined) yield obj.Key;\n }\n // A page that says more follow and gives no way to ask for them would end the listing short, silently.\n if (res.IsTruncated === true && res.NextContinuationToken === undefined) {\n throw new IntegrityError('S3 listing says it is truncated but gives no continuation token');\n }\n // A page that hands back the token it was asked with would be asked for again, forever.\n if (res.IsTruncated === true && res.NextContinuationToken === token) {\n throw new IntegrityError('S3 listing returned the continuation token it was given');\n }\n token = res.IsTruncated === true ? res.NextContinuationToken : undefined;\n } while (token !== undefined);\n }\n}\n\n/**\n * Reclassify a transient S3 fault as a retryable {@link TransientError} and a missing bucket as a `ValidationError`;\n * any other error is passed through.\n */\nfunction mapError(err: unknown, bucket: string): unknown {\n return toTransient(\n asMissingBucket(scrubCredentials(err), bucket),\n isTransient,\n transientFaultMessage,\n );\n}\n\nexport class S3RegistryDriver extends ObjectStoreRegistry {\n constructor(options: S3RegistryDriverOptions) {\n super(\n new S3RegistryStore(\n options.client,\n options.bucket,\n resolveReadTimeoutMs(options.readTimeoutMs),\n requireBool('conditionalDelete', options.conditionalDelete),\n ),\n normalizeObjectPrefix(options.prefix),\n options.now ?? ((): number => Date.now()),\n );\n }\n}\n", "/**\n * The socket-pool advisory: once the first read has run, the SDK's own request handler has made its agents and its\n * public `httpHandlerConfigs()` reports their `maxSockets`. A pool smaller than twice the default window of a combine\n * (32 ranges held ahead per operand, two operands) makes reads queue for a socket, so one `advisory` event goes to the\n * store's metrics sink, once. Nothing is logged or printed.\n *\n * A handler this cannot read (a custom one, HTTP/2, Fetch, an agent class that hides `maxSockets`) gives no event and\n * no error. The window it compares against is the default, because a combine's `concurrency` is chosen per call.\n */\nimport type { S3Client } from '@aws-sdk/client-s3';\nimport type { IMetricsSink, MetricEvent } from '@cloudbitmaps/core/driver-kit';\n\n/** The default window of a combine, which the engine owns; a test holds the two equal. */\nexport const SOCKET_ADVISORY_CONCURRENCY = 32;\n\n/** A two-operand combine holds up to this many requests open at the default window. */\nconst THRESHOLD = 2 * SOCKET_ADVISORY_CONCURRENCY;\n\n/** What of the SDK's request handler the limit and the advisory need: its `handle` and the agents it exposes once it has run. */\nexport interface PooledHandler {\n handle(request: unknown, options?: unknown): Promise<unknown>;\n httpHandlerConfigs?: () => {\n httpAgent?: { maxSockets: number };\n httpsAgent?: { maxSockets: number };\n };\n}\n\nexport class SocketAdvisory {\n /** True from the first `attach` until the one check has started: the only thing a request pays for after the first. */\n private pending = false;\n /** Every sink ever attached, so a sink is never given the event twice. */\n private readonly sinks = new Set<IMetricsSink>();\n /** The check's result, once it has started; a sink attached after that is given the result as it settles. */\n private outcome: Promise<MetricEvent | undefined> | undefined;\n\n constructor(\n private readonly client: S3Client,\n private readonly bucket: string,\n ) {}\n\n /**\n * Hand a sink to the advisory. Each sink gets the event at most once: a sink attached before the check runs gets it\n * when the first read finishes, one attached later gets it as soon as the (single) check's result is known, and\n * one attached again is ignored.\n */\n attach(sink: IMetricsSink): void {\n if (this.sinks.has(sink)) return;\n this.sinks.add(sink);\n if (this.outcome === undefined) this.pending = true;\n else void this.outcome.then((event) => event && emit(sink, event));\n }\n\n /**\n * Called as a request finishes. The first call after a sink is attached reads the pool, never throws and never\n * rejects; every later one costs one boolean. Returns the pending check (for a test to await), or `undefined` once\n * it has run.\n */\n afterRequest(): Promise<void> | undefined {\n if (!this.pending) return undefined;\n this.pending = false;\n const first = [...this.sinks];\n const outcome = this.read();\n this.outcome = outcome;\n return outcome.then((event) => {\n if (event) for (const sink of first) emit(sink, event);\n });\n }\n\n private async read(): Promise<MetricEvent | undefined> {\n try {\n const maxSockets = await this.pool();\n if (maxSockets === undefined || maxSockets >= THRESHOLD) return undefined;\n return {\n kind: 'advisory',\n code: 'socket-pool-below-window',\n driver: 's3',\n bucket: this.bucket,\n maxSockets,\n threshold: THRESHOLD,\n concurrency: SOCKET_ADVISORY_CONCURRENCY,\n };\n } catch {\n // A handler of another shape: the advisory is best-effort and never fails a read.\n return undefined;\n }\n }\n\n /**\n * The pool the reads use, or `undefined` when it cannot be read. With an endpoint on the client, that is the\n * `maxSockets` of the agent for its scheme. Without one (the SDK's own endpoints, or an endpoint set by the\n * environment, which the client does not expose) the scheme is not known, so every agent the handler has made is\n * read and the pool counts as small only if all of them are: the larger one is returned. That never warns on a pool\n * the reads do not use, at the cost of a missed warning when only the unused agent is small.\n */\n private async pool(): Promise<number | undefined> {\n const handler = this.client.config.requestHandler as unknown as PooledHandler | undefined;\n if (typeof handler?.httpHandlerConfigs !== 'function') return undefined;\n const endpoint = await this.client.config.endpoint?.();\n const agents = handler.httpHandlerConfigs();\n const candidates =\n endpoint === undefined\n ? [agents?.httpsAgent, agents?.httpAgent]\n : [endpoint.protocol === 'http:' ? agents?.httpAgent : agents?.httpsAgent];\n let largest: number | undefined;\n for (const agent of candidates) {\n const n = agent?.maxSockets;\n if (typeof n !== 'number' || Number.isNaN(n)) continue;\n if (largest === undefined || n > largest) largest = n;\n }\n return largest;\n }\n}\n\n/** Give one sink the event; a sink that throws never reaches the read. */\nfunction emit(sink: IMetricsSink, event: MetricEvent): void {\n try {\n sink.onEvent(event);\n } catch {\n // Best-effort, like every metrics sink.\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import {\n STORAGE_BACKEND,\n ValidationError,\n brandAsBackend,\n normalizeObjectPrefix,\n prefixPart,\n refuseUnknownOptions,\n requireBool,\n requireNonEmptyString,\n requirePositiveInt,\n} from '@cloudbitmaps/core/driver-kit';\nimport type {\n IRegistryDriver,\n IStorageDriver,\n LifecycleVerdict,\n StorageBackend,\n} from '@cloudbitmaps/core/driver-kit';\nimport { S3Client } from '@aws-sdk/client-s3';\nimport { defined } from './defined';\nimport { checkS3Lifecycle } from './lifecycle';\nimport { resolveReadTimeoutMs } from './read-timeout';\nimport { S3StorageDriver } from './storage';\nimport { S3RegistryDriver } from './registry';\nimport { SocketAdvisory, type PooledHandler } from './socket-advisory';\nimport type { IMetricsSink } from '@cloudbitmaps/core/driver-kit';\n\n/**\n * Refuse `credentials` that are present and not an object of non-empty `accessKeyId` and `secretAccessKey` strings and\n * an optional `sessionToken`, naming the key and never its value.\n */\nfunction checkCredentials(credentials: unknown): void {\n if (credentials === undefined) return;\n if (typeof credentials === 'function') {\n throw new ValidationError(\n 'S3Storage: `credentials` must be an object of `accessKeyId` and `secretAccessKey`, not a provider function; ' +\n 'pass a provider on an S3Client given as `client`',\n );\n }\n // The SDK stamps `$source` on the object it is given, and a credential provider's result carries the identity fields\n // below, so a bag that was used before, or came from a provider, still takes. Those four are tolerated, not read.\n const keys = [\n 'accessKeyId',\n 'secretAccessKey',\n 'sessionToken',\n '$source',\n 'credentialScope',\n 'accountId',\n 'expiration',\n ] as const;\n refuseUnknownOptions('S3Storage credentials', credentials, keys);\n const given = credentials as Record<string, unknown>;\n // Only the field and the type of a wrong value are named, never the value: it may be a secret.\n const needText = (key: string, value: unknown): void => {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new ValidationError(\n `S3Storage: \\`credentials.${key}\\` must be a non-empty string; got ${typeof value === 'string' ? 'an empty string' : value === null || value === undefined ? String(value) : `a ${typeof value}`}`,\n );\n }\n };\n needText('accessKeyId', given.accessKeyId);\n needText('secretAccessKey', given.secretAccessKey);\n if (given.sessionToken !== undefined) needText('sessionToken', given.sessionToken);\n}\n\n/** Refuse an endpoint the SDK would fail on at its first request: it needs a scheme, as `http://localhost:9000`. */\nfunction checkEndpointUrl(endpoint: string): void {\n let url: URL | undefined;\n try {\n url = new URL(endpoint);\n } catch {\n url = undefined;\n }\n if (url === undefined || !/^https?:$/.test(url.protocol) || url.hostname.length === 0) {\n throw new ValidationError(\n 'S3Storage: `endpoint` must be an http or https URL such as \"http://localhost:9000\"',\n );\n }\n}\n\n/**\n * What {@link S3Storage} takes: a `bucket`, and either a `client` or the five settings that build one (`region`,\n * `endpoint`, `pathStyle`, `credentials`, `maxSockets`), never both.\n */\nexport interface S3StorageOptions {\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional key prefix under which everything lives \u2014 generations and the registry alike. A string, else `ValidationError`. */\n readonly prefix?: string;\n /**\n * A constructed client. Supply one for a credential chain the SDK cannot infer; otherwise one is built. Its retry\n * applies to every request except the conditional writes, which the driver sends with that retry off whatever it is\n * configured to do: a registry row once, and a generation's object again only after a throttle, under its own backoff.\n */\n readonly client?: S3Client;\n /**\n * Region for the client built when `client` is absent (refused beside `client`): a non-empty string, else\n * `ValidationError`. Falls back to the SDK's own resolution.\n */\n readonly region?: string;\n /**\n * Endpoint for an S3-compatible store (MinIO, Ceph, R2): an http or https URL, since an empty one would send\n * requests to AWS, else `ValidationError`. Refused beside `client`, which carries its own.\n */\n readonly endpoint?: string;\n /**\n * Path-style addressing, which most S3-compatible stores require: a boolean, else `ValidationError`. Refused beside\n * `client`, which carries its own.\n */\n readonly pathStyle?: boolean;\n /**\n * Static credentials, for the S3-compatible stores that issue them (MinIO, Ceph, R2): non-empty `accessKeyId` and\n * `secretAccessKey` strings and an optional `sessionToken`, else `ValidationError`, which never prints a value. No other\n * key is taken except `$source`, `credentialScope`, `accountId` and `expiration`, which are tolerated and not read:\n * the SDK stamps `$source` on a credentials object it is given, so one used for two stores carries it, and a credential\n * provider's result carries the others. A provider function is not taken here; put it on an `S3Client` given as `client`.\n *\n * On AWS itself, leave this unset \u2014 the SDK's own chain (instance role, SSO, environment, profile) is what\n * you want, and hard-coding keys to reach it would be a downgrade. It exists because the alternative for a\n * MinIO user was to construct an `S3Client` purely to carry two strings, which is the ergonomics this class\n * is here to remove. Refused beside `client`, which carries its own.\n */\n readonly credentials?: {\n readonly accessKeyId: string;\n readonly secretAccessKey: string;\n readonly sessionToken?: string;\n };\n /**\n * Most sockets the built client opens at once to one scheme, for `https` and for a plain-`http` endpoint alike\n * (default 128). The AWS SDK's own default is 50, which a window of 32 reads per operand outgrows on the first\n * two-operand `intersect`. Only this limit differs from the SDK's own client: its request handler, keep-alive,\n * timeouts and retry are the SDK's. If a future SDK changes its handler, the built client keeps the SDK's own\n * limit rather than failing to build. A positive safe integer. Release a built client's sockets with\n * `store.client.destroy()`. Refused beside `client`, which carries its own request handler. An `eraseSubject`\n * at its default `concurrency` of 8 holds up to 64 requests open, which the default of 128 covers; raise\n * `maxSockets`, or lower `concurrency`, to fit a larger one.\n */\n readonly maxSockets?: number;\n /**\n * Largest object the backend will write and advertise. Default = `partBytes \u00D7 10,000` (\u2248 78 GiB at the default\n * 8 MiB part) \u2014 the honest ceiling reachable within S3's 10,000-part limit. Set it higher and `partBytes`\n * 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\n * larger cap, or a `partBytes` above S3's 5 GiB part limit, is refused with `ValidationError`.\n * Must be a positive safe integer.\n */\n readonly maxObjectBytes?: number;\n /** Multipart part size in bytes (default 8 MiB; a smaller value is raised to the S3 5 MiB minimum). Must be a\n * positive safe integer. Tunes peak write memory. */\n readonly partBytes?: number;\n /**\n * How long one read may take before it is abandoned, in ms. `0`, the default, sets no timeout. When set, it bounds\n * each `GetObject` and `HeadObject` either half sends, the response body included, so a connection that stops\n * answering part-way through a body is cut off too. A read that runs out of time throws `TransientError`, which the\n * store's read retry runs again. AWS's S3 guidance is to retry a GET of under 512 KB that has not answered in about\n * 2 seconds. Must be a non-negative safe integer no larger than 2,147,483,647.\n *\n * The clock starts when the read is handed to the SDK, so it also counts the time the read waits for one of the\n * client's sockets (128 by default, `maxSockets`) and the time spent fetching credentials, and under `retryMode:\n * 'adaptive'` the SDK's rate-limiter wait. A burst of concurrent reads larger than the socket pool can therefore time\n * out with nothing slow on the wire: size the timeout above the worst queueing your concurrency implies, or raise\n * `maxSockets` (the client you pass carries its own). On a client built with `cacheMiddleware: true`, a timed read\n * resolves its middleware each time.\n *\n * Writes and listings are never timed: a write that hangs needs a timeout on the client (its `requestHandler`). The\n * timeout is applied per request, so a `client` you pass gets it without being changed.\n */\n readonly readTimeoutMs?: number;\n /** Injected clock for the registry's `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n /**\n * Whether the store applies `If-Match` on a `DeleteObject`, which both halves rely on when it is on: the registry\n * removes a deleted row for good rather than leaving a tombstone a full listing reads forever, and the storage half\n * removes an erasure's holder only while it is the object the erasure searched, by its ETag. Defaults to `true` when\n * the host the client resolves is an AWS S3 host, whichever way its endpoint was set (`endpoint`,\n * `AWS_ENDPOINT_URL_S3`, `AWS_ENDPOINT_URL`, the shared config file), and to `false` for any other host: set it for an\n * S3-compatible store only once you know the store applies `If-Match` on a delete. MinIO, for one, ignores it. It is\n * never `true` for an SDK that does not send the header.\n */\n readonly conditionalDelete?: boolean;\n}\n\n/**\n * The keys `new S3Storage(options)` takes. Any other is refused by name rather than ignored: an ignored client or\n * endpoint key builds a client from ambient credentials against the **public** endpoint, and for a store pointed at\n * MinIO that is production traffic from a wiring typo.\n */\nexport const S3_STORAGE_OPTION_KEYS = [\n 'bucket',\n 'prefix',\n 'client',\n 'region',\n 'endpoint',\n 'pathStyle',\n 'credentials',\n 'maxSockets',\n 'maxObjectBytes',\n 'partBytes',\n 'readTimeoutMs',\n 'now',\n 'conditionalDelete',\n] as const;\n\n/** The settings that build a client, which a supplied `client` already carries and so cannot be given beside. */\nconst CLIENT_SETTINGS = ['region', 'endpoint', 'pathStyle', 'credentials', 'maxSockets'] as const;\n\n/** Default socket limit of a client the store builds: two operands at the default window of 32 reads each, doubled. */\nconst DEFAULT_MAX_SOCKETS = 128;\n\n/**\n * Cap the sockets the SDK's own request handler opens, and change nothing else about it. The handler stays the SDK's\n * default one, so its other defaults hold: its defaults-mode connection timeout, keep-alive, and a request that\n * sends `Expect: 100-continue` (a part of 2 MiB or more) still goes on its own connection outside this pool (SDK 3.723.0\n * and later; before it those uploads share the pool, so the cap limits them too). Only the\n * `maxSockets` of its two pooled agents is set, before the first request goes out. The handler makes its agents on\n * that first request (the http one even later, per request, so a first burst to a plain-http endpoint would get one\n * pool each), so the first request first runs an aborted one through it, which makes them, then sets the limit on\n * the agents it made.\n */\nexport function limitSockets(client: S3Client, maxSockets: number): void {\n const handler = client.config.requestHandler as unknown as PooledHandler;\n // A handler of another shape is left as it is: a tuning setting never stops the store from being built, and the\n // client then keeps the SDK's own limit.\n if (typeof handler?.handle !== 'function' || typeof handler.httpHandlerConfigs !== 'function')\n return;\n const handle = handler.handle.bind(handler);\n const agents = handler.httpHandlerConfigs.bind(handler);\n let ready: Promise<void> | undefined;\n const warm = async (): Promise<void> => {\n const abort = new AbortController();\n abort.abort();\n try {\n // An `http:` request makes the handler build both agents; on a client that only ever speaks https the http\n // agent stays idle, and `client.destroy()` frees it with the rest.\n await handle({ protocol: 'http:' }, { abortSignal: abort.signal }).catch(() => undefined);\n const { httpAgent, httpsAgent } = agents();\n if (httpAgent) httpAgent.maxSockets = maxSockets;\n if (httpsAgent) httpsAgent.maxSockets = maxSockets;\n } catch {\n // The handler is not the shape this expects: the limit stays the SDK's own.\n }\n };\n handler.handle = async (request, options) => {\n ready ??= warm();\n await ready;\n return handle(request, options);\n };\n}\n\n/**\n * `S3Storage` \u2014 the S3 backend as one object: the generations and the pointer, in one bucket, stated once.\n *\n * Replaces two constructor calls that each repeated `client`, `bucket` and `prefix`. Repeating them is how\n * they come apart: point the registry at one prefix and the objects at another and the store answers *empty*\n * rather than *misconfigured*, which is the hardest kind of wrong answer to debug. Here the location is written\n * once and shared, so the mismatch cannot be expressed.\n *\n * **It will build a client for you**, which is the common case \u2014 `new S3Storage({ bucket })` picks up the\n * ambient credential chain and region exactly as the SDK would. Pass `client` instead when you need a\n * credential chain the SDK cannot infer (SSO, an assumed role, a custom retry strategy); pass `endpoint` +\n * `pathStyle` + `credentials` for an S3-compatible store (MinIO, Ceph, R2). Both halves stay reachable as `.storage`\n * and `.registry` for anyone wiring something the facade does not cover. `maxObjectBytes` and `partBytes` size the\n * multipart upload, and `readTimeoutMs`, when set, bounds each read both halves make (listings are not timed).\n */\nexport class S3Storage implements StorageBackend {\n /** Cross-bundle brand, stamped non-enumerably in the constructor so a spread cannot carry it. */\n declare readonly [STORAGE_BACKEND]: true;\n readonly storage: IStorageDriver;\n readonly registry: IRegistryDriver;\n /** The client both halves share \u2014 built here unless one was supplied. */\n readonly client: S3Client;\n private readonly sockets: SocketAdvisory;\n private readonly bucket: string;\n /** The key prefix every object sits under: `''`, or `trimmed/`. */\n private readonly keyPrefix: string;\n private readonly readTimeoutMs: number;\n\n /**\n * Builds the S3 backend over one bucket, which `bucket` names. Pass `client`, or `region`, `endpoint`, `pathStyle`,\n * `credentials` and `maxSockets` to have one built, not both. `prefix` puts every object under a key prefix. Throws\n * `ValidationError` for an unknown option, no bucket, a `now` that is not a function, a client setting beside\n * `client`, a `maxSockets` (with no `client`) that is not a positive integer, a `partBytes` or `maxObjectBytes` that\n * 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\n * is not a boolean, `credentials` that are not complete, or an invalid `prefix` or `readTimeoutMs`.\n */\n constructor(options: S3StorageOptions) {\n refuseUnknownOptions(\n 'S3Storage',\n options,\n S3_STORAGE_OPTION_KEYS,\n 'an S3 client goes in `client`',\n );\n if (typeof options.bucket !== 'string' || options.bucket.trim().length === 0) {\n throw new ValidationError('S3Storage needs a `bucket`: the name of one, a non-empty string');\n }\n if (options.now !== undefined && typeof options.now !== 'function') {\n throw new ValidationError(\n \"S3Storage's `now` must be a function that returns epoch milliseconds\",\n );\n }\n if (options.client !== undefined && options.client !== null) {\n // A supplied client already carries its region, endpoint, addressing style and credentials, so a setting\n // beside it is ignored, and ignoring it leaves the store talking to somewhere the caller did not mean:\n // an `endpoint` meant for MinIO, silently dropped, is production traffic from a client that was built\n // for AWS. Refuse instead of picking one.\n const ignored = CLIENT_SETTINGS.filter((k) => options[k] !== undefined);\n if (ignored.length > 0) {\n throw new ValidationError(\n `S3Storage takes \\`client\\` OR ${CLIENT_SETTINGS.map((k) => `\\`${k}\\``).join(' / ')}, not both \u2014 ` +\n `got \\`client\\` with ${ignored.map((k) => `\\`${k}\\``).join(', ')}; ` +\n 'the `client` already carries them, so configure them on the client, or drop `client`',\n );\n }\n // Only an S3Client has `send`; a bare object would build a store that fails at its first read with a TypeError.\n if (typeof (options.client as { send?: unknown }).send !== 'function') {\n throw new ValidationError(\n 'S3Storage: `client` must be an S3Client from @aws-sdk/client-s3 (an object with `send`)',\n );\n }\n this.client = options.client;\n } else {\n const maxSockets =\n requirePositiveInt('maxSockets', options.maxSockets) ?? DEFAULT_MAX_SOCKETS;\n // The SDK refuses an empty region only when the first request is built, with an untyped error, and takes a number.\n const region: unknown = options.region;\n if (region !== undefined && (typeof region !== 'string' || region.trim().length === 0)) {\n throw new ValidationError(\n \"S3Storage's `region` must be a non-empty string such as 'us-east-1'\",\n );\n }\n // An empty `endpoint` is no endpoint: the client would send to AWS, not the service meant.\n const endpoint = requireNonEmptyString('S3Storage: `endpoint`', options.endpoint);\n if (endpoint !== undefined) checkEndpointUrl(endpoint);\n requireBool('S3Storage: `pathStyle`', options.pathStyle);\n checkCredentials(options.credentials);\n // `credentials` is not the last member: the leak scan reads a property read that ends a built line as a\n // possible secret, and the bundler drops the trailing comma after the last one.\n this.client = new S3Client(\n defined({\n credentials: options.credentials,\n region: options.region,\n endpoint: options.endpoint,\n forcePathStyle: options.pathStyle,\n }),\n );\n limitSockets(this.client, maxSockets);\n }\n this.bucket = options.bucket;\n this.keyPrefix = prefixPart(normalizeObjectPrefix(options.prefix));\n this.readTimeoutMs = resolveReadTimeoutMs(options.readTimeoutMs);\n this.sockets = new SocketAdvisory(this.client, options.bucket);\n const shared = {\n client: this.client,\n bucket: options.bucket,\n ...defined({ prefix: options.prefix, readTimeoutMs: options.readTimeoutMs }),\n };\n this.storage = new S3StorageDriver({\n ...shared,\n sockets: this.sockets,\n ...defined({\n maxObjectBytes: options.maxObjectBytes,\n partBytes: options.partBytes,\n conditionalDelete: options.conditionalDelete,\n }),\n });\n this.registry = new S3RegistryDriver({\n ...shared,\n ...defined({ now: options.now, conditionalDelete: options.conditionalDelete }),\n });\n brandAsBackend(this);\n }\n\n /**\n * Called by a store given a metrics sink. After the first read, a client whose socket pool is smaller than twice the\n * default window (64) sends the sink one `advisory` event, once; see the production guide's socket sizing.\n */\n attachMetrics(sink: IMetricsSink): void {\n this.sockets.attach(sink);\n }\n\n /**\n * Reads the bucket's lifecycle configuration (one `GetBucketLifecycleConfiguration` request, plus the SDK's own\n * retries, bounded by `readTimeoutMs` when it is set) and reports a rule that would expire, or move to GLACIER,\n * DEEP_ARCHIVE or a class it does not know, the current objects under this store's prefix, generations and registry\n * rows alike. Needs `s3:GetLifecycleConfiguration`, which a load never uses (`store.preflight()`'s\n * lifecycle step does): without it the\n * answer is `unchecked`, naming the permission. A missing rule that aborts incomplete multipart uploads is advice in\n * `detail`, not a problem. Never rejects; a `detail` carries no credential. Rule reading: see the production guide.\n */\n checkLifecycle(): Promise<LifecycleVerdict> {\n return checkS3Lifecycle(this.client, this.bucket, this.keyPrefix, this.readTimeoutMs);\n }\n}\n", "/**\n * `values` without the entries that are `undefined`, typed as optional properties, so a constructor can pass on the\n * options it was given: `exactOptionalPropertyTypes` refuses `{ x: undefined }` for an optional `x`, and a sub-driver\n * must see an option the caller left out as absent.\n */\nexport function defined<T extends Record<string, unknown>>(\n values: T,\n): { [K in keyof T]?: Exclude<T[K], undefined> } {\n return Object.fromEntries(Object.entries(values).filter(([, v]) => v !== undefined)) as {\n [K in keyof T]?: Exclude<T[K], undefined>;\n };\n}\n", "/**\n * The S3 lifecycle check behind `S3Storage.checkLifecycle`: reads the bucket's lifecycle configuration (one\n * `GetBucketLifecycleConfiguration` request) and says whether a rule would expire or archive what the store keeps.\n *\n * Everything the store keeps sits under one key prefix: the generation objects at `<prefix><namespace>/segments/\u2026`\n * and the registry rows at `<prefix>registry/\u2026`. A rule is a **problem** when it is enabled, its prefix and ours are\n * one a prefix of the other (a rule on `a/` covers a store under `a/b/`, and a rule on `a/b/registry/` covers part of\n * it), it is enabled (a rule with no `Status` is read as enabled), it has no tag filter (the store tags nothing, so a\n * tag-filtered rule never matches), and it expires current objects (`Days` or `Date`) or transitions them to GLACIER or\n * DEEP_ARCHIVE, which cannot be read without a restore, or to a storage class this check does not know. A rule that\n * only expires noncurrent versions, only removes expired delete markers or only aborts incomplete uploads is safe. A\n * rule with a condition this check does not interpret (an object size filter) is judged as if the condition were\n * absent, so it can report a problem for a rule that would not match; it never hides one. When unsure, a problem.\n *\n * Advice in `detail`, never a problem by itself: a missing rule that aborts incomplete multipart uploads over the\n * generation keys; a transition to INTELLIGENT_TIERING (its archive access tiers, if the bucket turns them on, need a\n * restore before a read); a transition to an infrequent-access class or GLACIER_IR (a minimum storage duration and a\n * per-GB retrieval fee).\n *\n * The request honours the backend's `readTimeoutMs`: one that gets no answer in time is `unchecked`.\n */\nimport { GetBucketLifecycleConfigurationCommand } from '@aws-sdk/client-s3';\nimport type { LifecycleRule, S3Client } from '@aws-sdk/client-s3';\nimport type { LifecycleVerdict } from '@cloudbitmaps/core/driver-kit';\nimport { TransientError } from '@cloudbitmaps/core/driver-kit';\nimport { errorName } from './aws-errors';\nimport { timedRead } from './read-timeout';\nimport { scrubCredentials } from './scrub-error';\n\n/** The longest the message of a failed request is shown. */\nconst MAX_MESSAGE = 200;\n\n/** The longest a rule id, a rule prefix or an error name is shown. */\nconst MAX_FIELD = 64;\n\n/** How many problem rules a `detail` names before it says \"and N more\". */\nexport const MAX_NAMED_RULES = 3;\n\n/** Classes an object reads from at once, with no restore. */\nconst READABLE_CLASSES: ReadonlySet<string> = new Set([\n 'STANDARD',\n 'STANDARD_IA',\n 'ONEZONE_IA',\n 'GLACIER_IR',\n 'INTELLIGENT_TIERING',\n]);\nconst ARCHIVE_CLASSES: ReadonlySet<string> = new Set(['GLACIER', 'DEEP_ARCHIVE']);\nconst INFREQUENT_CLASSES: ReadonlySet<string> = new Set([\n 'STANDARD_IA',\n 'ONEZONE_IA',\n 'GLACIER_IR',\n]);\n\nconst cut = (text: string, max: number): string => (text.length > max ? text.slice(0, max) : text);\n\n/** Two key prefixes cover a common key when one starts with the other. */\nconst overlaps = (a: string, b: string): boolean => a.startsWith(b) || b.startsWith(a);\n\n/** The prefix a rule applies to: the filter's, the filter's `And`, or the legacy rule-level one; none is every key. */\nfunction rulePrefix(rule: LifecycleRule): string {\n return rule.Filter?.Prefix ?? rule.Filter?.And?.Prefix ?? rule.Prefix ?? '';\n}\n\nconst hasTagFilter = (rule: LifecycleRule): boolean =>\n rule.Filter?.Tag !== undefined || (rule.Filter?.And?.Tags?.length ?? 0) > 0;\n\n/** Whether a rule is in force: only an explicit `Disabled` switches it off. */\nconst isEnabled = (rule: LifecycleRule): boolean => rule.Status !== 'Disabled';\n\n/** How a rule threatens current objects, or `undefined` when it does not. */\nfunction threat(rule: LifecycleRule): string | undefined {\n if (rule.Expiration?.Days !== undefined || rule.Expiration?.Date !== undefined) {\n return 'expires current objects';\n }\n const classes = (rule.Transitions ?? []).map((t) => t.StorageClass);\n const archive = classes.find((c) => c !== undefined && ARCHIVE_CLASSES.has(c));\n if (archive !== undefined) return `moves current objects to ${archive}`;\n const unknown = (rule.Transitions ?? []).find(\n (t) => t.StorageClass === undefined || !READABLE_CLASSES.has(t.StorageClass),\n );\n return unknown === undefined\n ? undefined\n : `moves current objects to ${cut(String(unknown.StorageClass ?? 'no storage class'), MAX_FIELD)}, a storage class this check does not know`;\n}\n\nconst label = (rule: LifecycleRule): string => {\n const id = rule.ID === undefined ? '' : `${JSON.stringify(cut(rule.ID, MAX_FIELD))} `;\n return `${id}(prefix ${JSON.stringify(cut(rulePrefix(rule), MAX_FIELD))})`;\n};\n\n/** Whether a rule's prefix is the store's or longer, so narrowing it cannot exclude the store's prefix. */\nconst sitsInside = (rule: LifecycleRule, prefix: string): boolean =>\n prefix !== '' && rulePrefix(rule).startsWith(prefix);\n\n/** Whether an abort-incomplete-upload rule covers generation keys, `<prefix><ns>/segments/\u2026`, not only `<prefix>registry/`. */\nfunction coversGenerations(rule: LifecycleRule, prefix: string): boolean {\n const own = rulePrefix(rule);\n if (prefix.startsWith(own)) return true;\n return !own.slice(prefix.length).startsWith('registry/');\n}\n\nconst ABORT_ADVICE =\n 'No rule aborts incomplete multipart uploads under this prefix; add one (AbortIncompleteMultipartUpload, a few days) ' +\n 'so an upload a crashed writer abandoned stops billing for its parts.';\n\nconst INTELLIGENT_TIERING_ADVICE =\n 'A rule moves current objects to INTELLIGENT_TIERING: if the bucket turns on its archive access tiers, an object in ' +\n 'one needs a restore before it can be read, so leave them off for this prefix.';\n\nconst INFREQUENT_ADVICE =\n 'A rule moves current objects to an infrequent-access class or GLACIER_IR: they stay readable, but each has a ' +\n 'minimum storage duration and a per-GB retrieval fee that reads of the store will pay.';\n\n/**\n * What a bucket's rules mean for a store under `prefix` (`''`, or `trimmed/`, the form every key builder uses).\n * Pure: no request, no SDK object but the rule shape.\n */\nfunction judgeRules(rules: readonly LifecycleRule[], prefix: string): LifecycleVerdict {\n const live = rules.filter((r) => isEnabled(r) && overlaps(rulePrefix(r), prefix));\n const untagged = live.filter((r) => !hasTagFilter(r));\n const threats = untagged.flatMap((r) => {\n const how = threat(r);\n return how === undefined\n ? []\n : [{ text: `rule ${label(r)} ${how}`, inside: sitsInside(r, prefix) }];\n });\n const problems = threats.map((t) => t.text);\n const transitionsTo = (classes: ReadonlySet<string>): boolean =>\n untagged.some((r) =>\n (r.Transitions ?? []).some(\n (t) => t.StorageClass !== undefined && classes.has(t.StorageClass),\n ),\n );\n const advice = [\n live.some((r) => r.AbortIncompleteMultipartUpload !== undefined && coversGenerations(r, prefix))\n ? ''\n : ABORT_ADVICE,\n transitionsTo(new Set(['INTELLIGENT_TIERING'])) ? INTELLIGENT_TIERING_ADVICE : '',\n transitionsTo(INFREQUENT_CLASSES) ? INFREQUENT_ADVICE : '',\n ]\n .filter((a) => a !== '')\n .map((a) => ` ${a}`)\n .join('');\n if (problems.length > 0) {\n const named = problems.slice(0, MAX_NAMED_RULES).join('; ');\n const more =\n problems.length > MAX_NAMED_RULES ? ` and ${problems.length - MAX_NAMED_RULES} more` : '';\n const quoted = JSON.stringify(cut(prefix, MAX_FIELD));\n const narrow = `Narrow the rule's prefix to exclude ${quoted}, or restrict it to noncurrent versions.`;\n const retarget =\n `A rule that sits inside ${quoted} cannot be narrowed to exclude it: remove it or retarget it to another ` +\n 'prefix, or restrict it to noncurrent versions.';\n const where =\n prefix === ''\n ? 'The store has no prefix, so every bucket-wide or prefixed rule can reach it: give the store a prefix, ' +\n 'or scope the rule away from its paths.'\n : [\n threats.some((t) => !t.inside) ? narrow : '',\n threats.some((t) => t.inside) ? retarget : '',\n ]\n .filter((x) => x !== '')\n .join(' ');\n return {\n status: 'problem',\n detail: `${named}${more}. The store keeps current objects it must be able to read back. ${where}${advice}`,\n };\n }\n return {\n status: 'ok',\n detail: `no enabled lifecycle rule under ${JSON.stringify(cut(prefix, MAX_FIELD))} expires or archives current objects.${advice}`,\n };\n}\n\n/** A failed request as a sentence: the permission to grant for a refusal, the scrubbed error otherwise. */\nfunction unchecked(err: unknown): LifecycleVerdict {\n if (err instanceof TransientError) {\n return {\n status: 'unchecked',\n detail:\n 'the read of the bucket lifecycle configuration got no answer in time (readTimeoutMs); run the check again',\n };\n }\n const scrubbed = scrubCredentials(err);\n if (errorName(scrubbed) === 'AccessDenied') {\n return {\n status: 'unchecked',\n detail:\n 'the identity may not read the bucket lifecycle configuration: grant s3:GetLifecycleConfiguration on the ' +\n 'bucket to check it, or confirm by hand that no rule expires or archives current objects under this prefix',\n };\n }\n const name = cut(String(errorName(scrubbed) ?? 'Error'), MAX_FIELD);\n const raw = (scrubbed as { message?: unknown } | null)?.message;\n const message = typeof raw === 'string' ? `: ${raw.slice(0, MAX_MESSAGE)}` : '';\n return {\n status: 'unchecked',\n detail: `could not read the bucket lifecycle configuration (${name}${message}); check it by hand`,\n };\n}\n\n/** The check: one request (the SDK may retry it), under `readTimeoutMs` (`0`: none), never a throw. */\nexport async function checkS3Lifecycle(\n client: S3Client,\n bucket: string,\n prefix: string,\n readTimeoutMs = 0,\n): Promise<LifecycleVerdict> {\n try {\n const out = await timedRead('GetBucketLifecycleConfiguration', readTimeoutMs, (options) =>\n client.send(new GetBucketLifecycleConfigurationCommand({ Bucket: bucket }), options),\n );\n return judgeRules(out.Rules ?? [], prefix);\n } catch (err) {\n try {\n if (errorName(err) === 'NoSuchLifecycleConfiguration') {\n return judgeRules([], prefix);\n }\n return unchecked(err);\n } catch {\n return { status: 'unchecked', detail: 'could not read the bucket lifecycle configuration' };\n }\n }\n}\n", "/**\n * SDK-free helpers for classifying AWS-style errors (used by the S3 drivers).\n *\n * These only read structural shapes an AWS SDK v3 error carries \u2014 `name`, `$metadata.httpStatusCode`, a\n * lower-level `code`/`errno`, and the SDK's own `$retryable` marker \u2014 so the (subtle, easy-to-get-wrong)\n * transient-vs-fatal decision is unit-testable without a live backend or even the SDK installed. They import no SDK.\n */\nimport { isTransportFault } from '@cloudbitmaps/core/driver-kit';\n\nexport function httpStatus(err: unknown): number | undefined {\n return (err as { $metadata?: { httpStatusCode?: number } } | null)?.$metadata?.httpStatusCode;\n}\n\nexport function errorName(err: unknown): string | undefined {\n return (err as { name?: string } | null)?.name;\n}\n\n/** A lower-level transport code (e.g. `ECONNRESET`) \u2014 the Node networking layer sets `code`. */\nexport function errorCode(err: unknown): string | undefined {\n return (err as { code?: string } | null)?.code;\n}\n\n/** The AWS SDK v3 tags retryable errors with a `$retryable` object (throttling faults carry `.throttling`). */\nexport function isSdkRetryable(err: unknown): boolean {\n return (err as { $retryable?: unknown } | null)?.$retryable != null;\n}\n\n/** Any 5xx is a server-side fault that's safe to retry (the request didn't deterministically fail). */\nexport function isServerSide(err: unknown): boolean {\n const status = httpStatus(err);\n return status !== undefined && status >= 500 && status <= 599;\n}\n\nconst NETWORK_NAMES = new Set([\n 'TimeoutError',\n 'RequestTimeout',\n 'RequestTimeoutException',\n 'NetworkingError',\n 'AbortError',\n]);\n\n// Message-text fallback for when the structural signals (name/code/$metadata/$retryable) are absent. Kept\n// SPECIFIC on purpose: a loose `/timed? ?out/` matches deterministic messages like \"value timed out of\n// range\" and would wrongly retry them, so we only match timeout/network phrases anchored to a transport word\n// (connection/request/socket/read/write) plus the unambiguous standalone phrases.\nconst NETWORK_MESSAGE =\n /socket hang up|network (error|failure)|(connection|request|socket|operation|read|write)\\s+tim(e|ed)\\s?out|connection (reset|refused|aborted|closed)/i;\n\n/**\n * A dropped/timed-out connection \u2014 transient by nature; a retry on a fresh connection usually succeeds. The SDK's\n * transport error names, the codes Node gives a failed connection ({@link isTransportFault}), and the messages above.\n */\nexport function isNetworkOrTimeout(err: unknown): boolean {\n if (NETWORK_NAMES.has(errorName(err) ?? '')) return true;\n if (isTransportFault(err)) return true;\n return NETWORK_MESSAGE.test((err as { message?: string } | null)?.message ?? '');\n}\n", "/**\n * The read timeout: with `readTimeoutMs` set, each `GetObject` and `HeadObject` the S3 drivers send is cut off after\n * it. It is off by default (`0`).\n *\n * The SDK sets no timeout of its own, so a read on a connection that stops answering waits as long as the connection\n * stays open, and the store's read retry never gets a fault to retry. A read still running when its timer fires throws\n * {@link TransientError}, which the store's retry runs again, and is aborted through the `abortSignal` its\n * `client.send` was given, which ends the request and destroys the response body, letting go of the socket. The SDK\n * does not send an aborted request again.\n *\n * The timer starts when the read is handed to the SDK and covers everything until its body is read: waiting for one\n * of the client's sockets, fetching credentials, an adaptive retry mode's rate-limiter wait, the request, any retries\n * the SDK makes of it, and reading the body. A server that sends its headers and then stalls part-way through the body\n * is cut off too, and so is a read that only queued too long. The read settles when the timer fires whatever the layers\n * under the client do with the abort, because the timer's error is what the read throws.\n *\n * It is per request, never the HTTP handler's timeout, for two reasons: writes are not timed, since an upload's part can\n * rightly take longer than a read, and a `client` the caller passes in is used as it is.\n */\nimport { TransientError, ValidationError } from '@cloudbitmaps/core/driver-kit';\n\n/** No timeout unless one is set: the value a read takes stays the caller's until in-region measurements justify one. */\nconst DEFAULT_READ_TIMEOUT_MS = 0;\n\n/** The longest delay a Node timer holds. A longer one fires after 1 ms instead, so it is refused rather than passed on. */\nconst MAX_TIMER_MS = 2_147_483_647;\n\n/** Validate a caller's `readTimeoutMs`, defaulting it: an integer from 0 (no timeout) to {@link MAX_TIMER_MS}. */\nexport function resolveReadTimeoutMs(value: number | undefined): number {\n if (value === undefined) return DEFAULT_READ_TIMEOUT_MS;\n if (!Number.isSafeInteger(value) || value < 0 || value > MAX_TIMER_MS) {\n throw new ValidationError(\n `readTimeoutMs must be a non-negative safe integer no larger than ${MAX_TIMER_MS}; got ${describe(value)}`,\n );\n }\n return value;\n}\n\n/** A value as an error message can show it: a string quoted, so `'200'` is not mistaken for `200`, and never a throw. */\nfunction describe(value: unknown): string {\n if (typeof value === 'string') return JSON.stringify(value);\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n return value === null ? 'null' : typeof value;\n}\n\n/** The options a timed read passes to its `client.send`: the abort signal, or nothing when the timeout is off. */\nexport type ReadSendOptions = { readonly abortSignal: AbortSignal } | undefined;\n\n/**\n * Run `read` under a `timeoutMs` timer (`0`: none). `read` passes `options` to its `client.send` and reads the body\n * before it returns, so the timer covers both. The timer is cleared when the read settles.\n */\nexport async function timedRead<T>(\n operation: 'GetObject' | 'HeadObject' | 'GetBucketLifecycleConfiguration',\n timeoutMs: number,\n read: (options: ReadSendOptions) => Promise<T>,\n): Promise<T> {\n // No options at all, rather than an empty signal: a client built with `cacheMiddleware: true` reuses its cached\n // handler only for a `send` given none.\n if (timeoutMs === 0) return read(undefined);\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timedOut = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // Rejected before the abort, so the read settles with this error and not with the one the abort raises.\n reject(new TransientError(`S3 ${operation} timed out after ${timeoutMs} ms`));\n controller.abort();\n }, timeoutMs);\n });\n try {\n // The race also takes the read's own rejection after an abort, so it never goes unhandled.\n return await Promise.race([read({ abortSignal: controller.signal }), timedOut]);\n } finally {\n clearTimeout(timer);\n }\n}\n", "/**\n * Remove credentials from an error the AWS SDK raised, before the driver throws it or keeps it as a `cause`.\n *\n * The SDK keeps the HTTP response it got on the error (`$response`), and through its socket the request it sent, with\n * the signed `Authorization` header and `x-amz-security-token`, on properties `util.inspect` shows only with\n * `showHidden`; a body it could not parse it keeps as text (`$responseBodyText`). It also copies every field of the\n * error body onto the error, where a logger prints it. A `SignatureDoesNotMatch` body from S3 quotes the request back:\n * its signature (`SignatureProvided`) and the canonical request S3 computed (`CanonicalRequest`, in hex\n * `CanonicalRequestBytes`), which lists every signed header with its value, the session token's included. A proxy's\n * error page, parsed the same way, can quote the `Authorization` value or the token in any field. The live objects are\n * never changed (a socket still closing reads them): an error that holds a transport, or whose data shows a credential,\n * is replaced by a deep copy of its plain data. An error with neither is returned as it is.\n *\n * The copy is a native error with the original's prototype (so `instanceof S3ServiceException` holds), name, message,\n * stack, `$metadata`, `$fault`, `$retryable`, `Code`, `RequestId` and every other plain field, which is what\n * classification reads. It has no transport property, no function or accessor (a `toJSON` or `util.inspect` hook is a\n * closure over the original) and no class instance other than an error. Errors are copied wherever they sit: the `cause`\n * chain, an `AggregateError`'s `errors`, any array or field. A table of copies keeps a cycle a cycle, and anything\n * nested deeper than `MAX_DEPTH` is dropped rather than kept raw. The value under a credential key is replaced by\n * `[redacted]`: a credential header's, an SSE-C key's, an AWS credential's fields, the signature and canonical request\n * S3 quotes, and the session token it quotes in `Token-0` when it refuses one (the access key id it names is kept: it\n * names a key and signs nothing). So is the value beside a credential's name in a list (`['x-amz-security-token', v]`)\n * or a record (a header table's `{ th, td }` row, `{ name, value }`, S3's `ArgumentName` and `ArgumentValue`). In the\n * copy's text, a credential header line, a SigV4 signature (`Signature=` in an `Authorization` value, `X-Amz-Signature=`\n * in a URL) and a session token or SSE-C key written after its header name are redacted. A credential in any other form\n * is not recognised, and a text too large for these patterns is replaced whole.\n */\n\nimport { types } from 'node:util';\n\n/** Properties that hold the raw transport: the response, its unparsed body, and the request and socket reachable from it. */\nconst TRANSPORT = new Set([\n '$response',\n '$responseBodyText',\n 'socket',\n '_httpMessage',\n 'req',\n 'request',\n 'connection',\n 'agent',\n]);\n/** The headers, besides `Authorization`, whose value is a credential: the session tokens and an SSE-C key. */\nconst PRIVATE_HEADERS =\n 'x-amz-security-token|x-amz-s3session-token|x-amz-(?:copy-source-)?server-side-encryption-customer-key';\n/**\n * Header and field names whose value is a credential: the credential headers, the SDK's SSE-C members, an AWS\n * credential's fields, what S3 quotes of a request whose signature it refused, and the session token it quotes as\n * `Token-0` when it refuses the token (`ExpiredToken`, `InvalidToken`). The whole name must match: `ContinuationToken`\n * does not.\n */\nconst CREDENTIAL_NAME = new RegExp(\n `^(?:(?:proxy-)?authorization|cookie|set-cookie|x-api-key|${PRIVATE_HEADERS}|x-amz-credential|x-amz-signature|` +\n '(?:copysource)?ssecustomerkey|secretaccesskey|sessiontoken|signatureprovided|canonicalrequest(?:bytes)?|' +\n 'token(?:-\\\\d+)?)$',\n 'i',\n);\n/** Whether `text` is a credential's name, as a key, a list item or a table cell holds it: spaces and a colon aside. */\nconst isCredentialName = (text: string): boolean =>\n CREDENTIAL_NAME.test(text.trim().replace(/:$/, ''));\n/** A credential header line inside raw request or response text. */\nconst CREDENTIAL_LINES = new RegExp(\n `^((?:proxy-)?authorization|cookie|set-cookie|x-api-key|${PRIVATE_HEADERS}):[^\\\\r\\\\n]*`,\n 'gim',\n);\n/**\n * A session token or SSE-C key after its header or query name anywhere in a text: `name: value`, `name=value` (also\n * percent-encoded) or JSON's `\"name\":\"value\"`.\n */\nconst NAMED_SECRET = new RegExp(\n `((?:${PRIVATE_HEADERS})[\"']?\\\\s*(?::|=|%3D)\\\\s*[\"']?)[^\\\\s&\"'<>\\\\\\\\,;]+`,\n 'gi',\n);\n/** A SigV4 signature: an `Authorization` value's `Signature=`, a presigned URL's `X-Amz-Signature=`, also percent-encoded. */\nconst SIGNATURE = /(Signature(?:=|%3D))[0-9A-Fa-f]{16,}/gi;\nconst REDACTED = '[redacted]';\n/** How deep into errors and plain data the copy goes; anything below is dropped. */\nconst MAX_DEPTH = 10;\n\nconst isPlain = (value: object): boolean => {\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n};\n\nconst isError = (value: object): boolean => value instanceof Error || types.isNativeError(value);\n\n/**\n * `text` with every credential it shows replaced. A text the patterns cannot finish matching (one of several MiB that\n * exhausts the regular expression engine's stack) is replaced whole, so a hostile body neither leaks nor changes the\n * error the driver throws.\n */\nfunction redactText(text: string): string {\n try {\n return text\n .replace(CREDENTIAL_LINES, `$1: ${REDACTED}`)\n .replace(NAMED_SECRET, `$1${REDACTED}`)\n .replace(SIGNATURE, `$1${REDACTED}`);\n } catch {\n return REDACTED;\n }\n}\n\n/** The copies made so far, and whether any differs from its original. */\ninterface Copying {\n copies: WeakMap<object, unknown>;\n changed: boolean;\n}\n\n/** Marker for a value the copy leaves out. */\nconst OMIT = Symbol('omit');\n\n/** An array index as an own key spells it. */\nconst INDEX = /^(?:0|[1-9]\\d*)$/;\n\n/**\n * Whether `value` has an own property its copy leaves out: any key but an index and `length` on an array, any key at\n * all on a map or a set.\n */\nfunction hasNamedKeys(value: object): boolean {\n if (!Array.isArray(value)) return Reflect.ownKeys(value).length > 0;\n return Reflect.ownKeys(value).some(\n (key) =>\n key !== 'length' &&\n !(typeof key === 'string' && INDEX.test(key) && Number(key) < 2 ** 32 - 1),\n );\n}\n\n/** Define `key` on `copy` as a plain data property. */\nfunction define(copy: object, key: string | symbol, value: unknown, enumerable: boolean): void {\n Object.defineProperty(copy, key, { value, writable: true, configurable: true, enumerable });\n}\n\n/** A credential-free copy of `value`: plain data and errors only, `OMIT` for anything else. */\nfunction copyOf(value: unknown, ctx: Copying, depth: number): unknown {\n if (typeof value === 'string') {\n const clean = redactText(value);\n if (clean !== value) ctx.changed = true;\n return clean;\n }\n if (typeof value === 'function' || typeof value === 'symbol') {\n ctx.changed = true;\n return OMIT;\n }\n if (typeof value !== 'object' || value === null) return value;\n if (ctx.copies.has(value)) return ctx.copies.get(value);\n if (depth > MAX_DEPTH) {\n ctx.changed = true;\n return OMIT;\n }\n if (Array.isArray(value)) return copyList(value as unknown[], ctx, depth);\n if (value instanceof Map) return copyMap(value as Map<unknown, unknown>, ctx, depth);\n if (value instanceof Set) return copySet(value as Set<unknown>, ctx, depth);\n return copyRecord(value, ctx, depth);\n}\n\n/** A copy of an array. A hole or a named property is left out. */\nfunction copyList(value: unknown[], ctx: Copying, depth: number): unknown[] {\n const list: unknown[] = [];\n ctx.copies.set(value, list);\n if (hasNamedKeys(value)) ctx.changed = true;\n let hidden = false;\n for (const item of value) {\n // A flat list of header names and values: the value after a credential name is one.\n if (hidden && item != null) {\n list.push(REDACTED);\n if (item !== REDACTED) ctx.changed = true;\n } else {\n const kept = copyOf(item, ctx, depth + 1);\n list.push(kept === OMIT ? undefined : kept);\n }\n hidden = typeof item === 'string' && isCredentialName(item);\n }\n return list;\n}\n\n/** A copy of a map: the value under a credential name is redacted. A named property is left out. */\nfunction copyMap(value: Map<unknown, unknown>, ctx: Copying, depth: number): Map<unknown, unknown> {\n const map = new Map<unknown, unknown>();\n ctx.copies.set(value, map);\n if (hasNamedKeys(value)) ctx.changed = true;\n for (const [k, v] of value) {\n const key = copyOf(k, ctx, depth + 1);\n if (key === OMIT) continue;\n if (typeof k === 'string' && isCredentialName(k) && v != null) {\n map.set(key, REDACTED);\n if (v !== REDACTED) ctx.changed = true;\n continue;\n }\n const kept = copyOf(v, ctx, depth + 1);\n if (kept !== OMIT) map.set(key, kept);\n }\n return map;\n}\n\n/** A copy of a set. A named property is left out. */\nfunction copySet(value: Set<unknown>, ctx: Copying, depth: number): Set<unknown> {\n const set = new Set<unknown>();\n ctx.copies.set(value, set);\n if (hasNamedKeys(value)) ctx.changed = true;\n for (const v of value) {\n const kept = copyOf(v, ctx, depth + 1);\n if (kept !== OMIT) set.add(kept);\n }\n return set;\n}\n\n/** A copy of an error or a plain object, `OMIT` for any other object (a class instance: a stream, a socket). */\nfunction copyRecord(value: object, ctx: Copying, depth: number): unknown {\n const errorLike = isError(value);\n if (!errorLike && !isPlain(value)) {\n ctx.changed = true;\n return OMIT;\n }\n const copy: object = errorLike\n ? (Object.setPrototypeOf(new Error(), Object.getPrototypeOf(value) as object | null) as object)\n : (Object.create(Object.getPrototypeOf(value) as object | null) as object);\n ctx.copies.set(value, copy);\n if (errorLike) delete (copy as { stack?: unknown }).stack;\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key === 'string' && TRANSPORT.has(key)) {\n ctx.changed = true;\n } else if (errorLike && key === 'stack') {\n copyStack(value, copy, ctx, depth);\n } else {\n copyField(value, copy, key, ctx, depth);\n }\n }\n if (errorLike) keepSlotFields(value, copy, ctx, depth);\n redactPairs(value, copy, errorLike, ctx);\n return copy;\n}\n\n/**\n * Redact the value a record holds beside a credential's name: a header table's `{ th, td }` row, a `{ name, value }`\n * pair, S3's `ArgumentName` and `ArgumentValue`. On a plain record every field but the name is redacted. On an error,\n * whose other fields are what classification reads, only the `...Value` field beside a `...Name` one is.\n */\nfunction redactPairs(original: object, copy: object, errorLike: boolean, ctx: Copying): void {\n const names = Reflect.ownKeys(original).filter((key): key is string => {\n if (typeof key !== 'string') return false;\n const held = Object.getOwnPropertyDescriptor(original, key)?.value as unknown;\n return typeof held === 'string' && isCredentialName(held);\n });\n if (names.length === 0) return;\n const values = errorLike\n ? names\n .filter((key) => /name$/i.test(key))\n .map((key) => `${key.slice(0, -4)}value`.toLowerCase())\n : undefined;\n for (const key of Reflect.ownKeys(copy)) {\n if (typeof key !== 'string' || names.includes(key)) continue;\n if (values !== undefined && !values.includes(key.toLowerCase())) continue;\n const descriptor = Object.getOwnPropertyDescriptor(copy, key);\n if (descriptor?.value == null || descriptor.value === REDACTED) continue;\n // A number or a boolean is never a credential, and it is what classification reads from a record such as\n // `$metadata` (`httpStatusCode`), which a response can pair with a request id that spells a credential name.\n if (typeof descriptor.value !== 'string' && typeof descriptor.value !== 'object') continue;\n define(copy, key, REDACTED, descriptor.enumerable ?? false);\n ctx.changed = true;\n }\n}\n\n/** Carry an error's stack over, read through the original: it may be an accessor bound to the error itself. */\nfunction copyStack(original: object, copy: object, ctx: Copying, depth: number): void {\n let stack: unknown;\n try {\n stack = (original as { stack?: unknown }).stack;\n } catch {\n return;\n }\n const kept = copyOf(stack, ctx, depth + 1);\n if (kept !== OMIT) define(copy, 'stack', kept, false);\n}\n\n/** Carry one own property over, as a data property: redacted under a credential name, left out when it is not data. */\nfunction copyField(\n original: object,\n copy: object,\n key: string | symbol,\n ctx: Copying,\n depth: number,\n): void {\n const descriptor = Object.getOwnPropertyDescriptor(original, key);\n if (descriptor === undefined) return;\n // A function or accessor is a closure over the original (a `toJSON`, an inspect hook): it is not carried over.\n if (!('value' in descriptor)) {\n ctx.changed = true;\n return;\n }\n // A key is text too: one that quotes a credential is carried redacted.\n const name = typeof key === 'string' ? redactText(key) : key;\n if (name !== key) ctx.changed = true;\n let kept: unknown;\n if (typeof key === 'string' && isCredentialName(key) && descriptor.value != null) {\n kept = REDACTED;\n if (descriptor.value !== REDACTED) ctx.changed = true;\n } else {\n kept = copyOf(descriptor.value, ctx, depth + 1);\n if (kept === OMIT) return;\n }\n Object.defineProperty(copy, name, {\n value: kept,\n writable: descriptor.writable ?? true,\n configurable: true,\n enumerable: descriptor.enumerable ?? false,\n });\n}\n\n/**\n * Carry over a `name`, `message` or `code` the original reads through its prototype and the copy cannot: a\n * `DOMException`'s are getters over the instance's internal state, which throw on any other object.\n */\nfunction keepSlotFields(original: object, copy: object, ctx: Copying, depth: number): void {\n for (const key of ['name', 'message', 'code'] as const) {\n if (Object.prototype.hasOwnProperty.call(original, key)) continue;\n let held: unknown;\n try {\n held = (original as Record<string, unknown>)[key];\n } catch {\n continue;\n }\n let read: unknown = OMIT;\n try {\n read = (copy as Record<string, unknown>)[key];\n } catch {\n // the copy cannot read it\n }\n if (read === held) continue;\n const kept = copyOf(held, ctx, depth + 1);\n if (kept !== OMIT) define(copy, key, kept, false);\n }\n}\n\n/**\n * `err` as it is safe to throw: a credential-free deep copy of an error or plain object when it holds a transport or\n * shows a credential, the value itself when it holds neither, and any other value (a class instance that is not an\n * error, a primitive) as it is. The live error is never changed.\n */\nexport function scrubCredentials<T>(err: T): T {\n if (typeof err !== 'object' || err === null) return err;\n const ctx: Copying = { copies: new WeakMap(), changed: false };\n const copy = copyOf(err, ctx, 0);\n return (ctx.changed && copy !== OMIT ? copy : err) as T;\n}\n", "/**\n * `S3StorageDriver` \u2014 an {@link IStorageDriver} over S3-compatible object storage.\n *\n * Works with AWS S3 and any compatible backend (MinIO, etc.) via the official `@aws-sdk/client-s3`, a real\n * dependency of this package \u2014 installing `@cloudbitmaps/s3` is what installs it. The client is\n * **injected** (dependency injection): the driver owns no credential/region/endpoint logic, so it's thin,\n * testable against MinIO (point a client at its endpoint), and reuses the caller's existing client.\n *\n * Generations are write-once immutable objects: a conditional `PutObject` with `If-None-Match: *` makes the\n * publish atomic \u2014 a second write to the same key fails with `WriteConflictError`, never a silent overwrite\n * (hard invariant 2: storage objects are immutable and never overwritten in place), the cloud analogue of\n * the LocalFs atomic `link`. **This requires a backend that honors\n * `If-None-Match: *`** (AWS S3 \u2014 GA Aug 2024; recent MinIO): a backend that silently ignored the\n * precondition would break write-once immutability. **Writes stream:** the object is uploaded in\n * constant memory \u2014 a small object is a single conditional `PutObject`; a large one is a **multipart upload**\n * (parts flushed as the codec writes, freed as they go) finished with a conditional `CompleteMultipartUpload`,\n * so a load's footprint stays ~one part regardless of segment size, up to the advertised `maxObjectBytes`\n * (default `partBytes \u00D7 10,000` \u2014 S3's per-upload part limit). **Each conditional request is sent with the SDK's retry\n * off for it** ({@link sendOnce}): a replay of a write that landed and lost its response would find its own object and\n * read as a lost race. A lost response or a timeout throws {@link TransientError}, and the object may or may not exist.\n * A throttle (`503 SlowDown`, any other `503`, or a bare `429`) is the one answer the driver sends the commit again after, at most {@link THROTTLE_RESENDS} times with\n * backoff: every object carries a random write id in its user metadata (`x-amz-meta-cbwid`), and once the commit has\n * been sent again, a precondition failure reads the object's metadata back, so a first send that landed after all is\n * this write's own and any other object is the conflict it reads as. The unconditional requests \u2014 the reads, the\n * delete, and a multipart upload's own start, parts and abort \u2014\n * keep the SDK's retry. **Each read can be timed** ({@link timedRead}): with `readTimeoutMs` set, a `GetObject` or\n * `HeadObject` that has not finished, body included, after it throws {@link TransientError}. It is off by default, and\n * nothing else is timed. Drivers may use `node:crypto`; only `core/` is bound by the determinism lint.\n *\n * **A delete given `ifVersion` is a `DeleteObject` under `If-Match: <that ETag>`** where `conditionalDelete` holds: by\n * default when the host the client resolves is an AWS S3 host and the SDK sends the header, as for the registry's row\n * delete ({@link appliesDeleteIfMatch}), resolved once, on the first such delete; MinIO, for one, accepts the header and\n * ignores it. A tail read reports the object's `ETag` as its version, from the `GetObject` that carried the bytes, or\n * the `HeadObject` when none were asked for. A `412` is the precondition failing, and a `409` a conditional request\n * that raced another on the key, which says nothing of which object is there; the delete has removed nothing either\n * way, and both are reported as {@link WriteConflictError} unless a `HeadObject` then finds no object under the key: an\n * absent object is a no-op, whichever of `204`, `404` or `412` the service answers for it. The delete keeps the SDK's\n * retry: a copy that meets its own landed delete finds nothing, and one that meets an object stored since is refused.\n * An ETag is computed from the bytes for an object stored without SSE-KMS or SSE-C, so the condition cannot tell apart\n * two objects with the same bytes, each stored whole in one request, or each in parts of the same sizes.\n */\nimport {\n errorKind,\n IntegrityError,\n NotFoundError,\n TransientError,\n ValidationError,\n WriteConflictError,\n concatBytes,\n normalizeObjectPrefix,\n parseGenerationFromKey,\n checkDeleteOptions,\n requireBool,\n requirePositiveInt,\n segmentObjectPrefix,\n storageObjectKey,\n toTransient,\n} from '@cloudbitmaps/core/driver-kit';\nimport type {\n BlobSink,\n GenKey,\n IStorageDriver,\n SegmentRef,\n StorageCaps,\n StorageDeleteOptions,\n TailRead,\n} from '@cloudbitmaps/core/driver-kit';\nimport type { Clock, Rng } from '@cloudbitmaps/core';\nimport { createHash, randomBytes, type Hash } from 'node:crypto';\nimport {\n AbortMultipartUploadCommand,\n CompleteMultipartUploadCommand,\n CreateMultipartUploadCommand,\n DeleteObjectCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n UploadPartCommand,\n type S3Client,\n} from '@aws-sdk/client-s3';\nimport {\n isConditionalConflict,\n isInvalidRange,\n asMissingBucket,\n isNoSuchUpload,\n isNotFound,\n spanFromContentRange,\n isPreconditionFailed,\n isThrottle,\n isTransient,\n totalFromContentRange,\n transientFaultMessage,\n} from './s3-errors';\nimport { resolveReadTimeoutMs, timedRead, type ReadSendOptions } from './read-timeout';\nimport type { SocketAdvisory } from './socket-advisory';\nimport { sendOnce } from './send-once';\nimport { readBounded } from './bounded-body';\nimport { scrubCredentials } from './scrub-error';\nimport { appliesDeleteIfMatch, probeClient, type ClientFacts } from './client-probe';\n\n/** Part size for multipart uploads. \u2265 the S3 5 MiB minimum; an object that fits in one part uses a single\n * conditional PUT instead (no multipart overhead, strongest write-once). Peak write memory \u2248 one part. */\nconst S3_PART_BYTES = 8 * 1024 * 1024;\n/**\n * S3 hard limit: a multipart upload has at most 10,000 parts. This \u00D7 the part size is the object ceiling at that part\n * size.\n */\nconst S3_MAX_PARTS = 10_000;\n/** S3 hard limits on one object (5 TiB) and one part (5 GiB). */\nconst S3_MAX_OBJECT_BYTES = 5 * 1024 ** 4;\nconst S3_MAX_PART_BYTES = 5 * 1024 ** 3;\n/** The user-metadata name an object's write id is stored under (`x-amz-meta-cbwid`). Short: every write sends it. */\nconst WRITE_ID_KEY = 'cbwid';\n/** How many times a throttled commit is sent again: four sends in all, as many as the SDK's own retry makes. */\nconst THROTTLE_RESENDS = 3;\n/** The backoff ceiling before the first re-send, doubling for each one after it: the SDK's own base for a throttle. */\nconst THROTTLE_BASE_DELAY_MS = 500;\n\n/** What the throttle backoff waits on. */\ntype Sleeper = Pick<Clock, 'sleep'>;\n\nconst REAL_TIME: Sleeper = {\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\n/** A refusal this driver already typed: it passes on unmapped, where a raw SDK error is mapped. */\nfunction isTypedPassThrough(err: unknown): boolean {\n const kind = errorKind(err);\n return kind === 'ValidationError' || kind === 'WriteConflictError' || kind === 'NotFoundError';\n}\n\n/** Where the backoff's jitter comes from when none is injected. */\nconst REAL_RNG: Rng = { next: () => Math.random() };\n\ninterface S3StorageDriverOptions {\n /** A constructed S3 client (point its `endpoint` at MinIO for local/integration use). */\n readonly client: S3Client;\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional key prefix under which all objects live (e.g. `cloudbitmaps/`). */\n readonly prefix?: string;\n /**\n * Largest object this driver will write/advertise. Default = `partBytes \u00D7 10,000` (\u2248 78 GiB at the default\n * 8 MiB part) \u2014 the honest ceiling reachable within S3's 10,000-part limit. Set it higher and `partBytes`\n * auto-grows so 10,000 parts still cover it (raising peak write memory to ~one part); up to the 5 TiB S3 max.\n */\n readonly maxObjectBytes?: number;\n /** Multipart part size in bytes (default 8 MiB; a smaller value is raised to the S3 5 MiB minimum). Must be a\n * positive safe integer. Tunes peak write memory. */\n readonly partBytes?: number;\n /**\n * How long one read \u2014 a `GetObject` or `HeadObject`, its body included \u2014 may take before it is abandoned and throws\n * `TransientError`, in ms. `0`, the default, sets no timeout. Must be a non-negative safe integer no larger than\n * 2,147,483,647. Writes and listings are not timed.\n *\n * The clock starts when the read is handed to the SDK, so it also counts the time the read waits for one of the\n * client's sockets (128 by default on the client the store builds, 50 on the SDK's own) and the time spent fetching\n * credentials, and under `retryMode: 'adaptive'` the SDK's rate-limiter wait. A burst of concurrent reads larger than\n * the socket pool can therefore time out with nothing slow on the wire: size the timeout above the worst queueing\n * your concurrency implies, or raise `maxSockets` (the store's option, or your own client's). On a client built with\n * `cacheMiddleware: true`, a timed read resolves its middleware each time.\n */\n readonly readTimeoutMs?: number;\n /** The socket-pool check, run as each read finishes; absent for a driver built on its own. */\n readonly sockets?: SocketAdvisory;\n /** What the backoff before re-sending a throttled commit waits on; real time when absent. */\n readonly clock?: Sleeper;\n /** Where the jitter of that backoff is drawn from; `Math.random` when absent. */\n readonly rng?: Rng;\n /**\n * Whether a delete given `ifVersion` is sent with `If-Match`, so it removes the object only while it is the one that\n * ETag names. Defaults as the registry's option does: on when the host the client resolves is an AWS S3 host and the\n * SDK sends the header, resolved on the first such delete (until then `capabilities()` reads `false`), and off for any\n * other host, and for a client that cannot be resolved then (no region), whose deletes stay unconditional. Set it for\n * an S3-compatible store only once you know the store applies `If-Match` on a delete. A `true` never overrides an SDK\n * that does not send the header.\n */\n readonly conditionalDelete?: boolean;\n}\n\nexport class S3StorageDriver implements IStorageDriver {\n private readonly client: S3Client;\n private readonly bucket: string;\n private readonly prefix: string | undefined;\n private readonly maxObjectBytes: number;\n private readonly partBytes: number;\n private readonly readTimeoutMs: number;\n private readonly clock: Sleeper;\n private readonly rng: Rng;\n private readonly sockets: SocketAdvisory | undefined;\n private readonly explicitConditionalDelete: boolean | undefined;\n private probed: Promise<void> | undefined;\n private facts: ClientFacts | undefined;\n\n constructor(options: S3StorageDriverOptions) {\n this.client = options.client;\n this.explicitConditionalDelete = requireBool('conditionalDelete', options.conditionalDelete);\n this.sockets = options.sockets;\n this.bucket = options.bucket;\n this.prefix = normalizeObjectPrefix(options.prefix);\n this.clock = options.clock ?? REAL_TIME;\n this.rng = options.rng ?? REAL_RNG;\n // Fail fast at the boundary: `??` only guards `undefined`, so NaN, 0, a negative or a fraction would otherwise\n // reach the arithmetic below and size every part (and the advertised cap) from garbage.\n requirePositiveInt('partBytes', options.partBytes);\n requirePositiveInt('maxObjectBytes', options.maxObjectBytes);\n // Past S3's own limits a cap is not reachable: the part grown to cover it would pass the part limit, an object up\n // to that size would be held in memory whole, and one PutObject of it would be refused.\n if (options.maxObjectBytes !== undefined && options.maxObjectBytes > S3_MAX_OBJECT_BYTES) {\n throw new ValidationError(\n `maxObjectBytes must be at most S3's 5 TiB object limit (${S3_MAX_OBJECT_BYTES}); got ${options.maxObjectBytes}`,\n );\n }\n if (options.partBytes !== undefined && options.partBytes > S3_MAX_PART_BYTES) {\n throw new ValidationError(\n `partBytes must be at most S3's 5 GiB part limit (${S3_MAX_PART_BYTES}); got ${options.partBytes}`,\n );\n }\n const requestedPart = Math.max(options.partBytes ?? S3_PART_BYTES, 5 * 1024 * 1024);\n // Default the object cap to what the requested part size can actually cover within S3's 10,000-part limit;\n // if a larger cap is requested, grow the part size to keep it reachable (so the advertised cap is honest).\n this.maxObjectBytes = options.maxObjectBytes ?? requestedPart * S3_MAX_PARTS;\n this.partBytes = Math.max(requestedPart, Math.ceil(this.maxObjectBytes / S3_MAX_PARTS));\n this.readTimeoutMs = resolveReadTimeoutMs(options.readTimeoutMs);\n }\n\n capabilities(): StorageCaps {\n return {\n rangeRead: true,\n maxObjectBytes: this.maxObjectBytes,\n conditionalPut: true,\n conditionalDelete: appliesDeleteIfMatch(this.explicitConditionalDelete, this.facts),\n };\n }\n\n /** Ask the client once where its requests go and whether it sends `If-Match` on a delete; it sends nothing. */\n private settle(): Promise<void> {\n this.probed ??= probeClient(this.client, this.bucket).then((facts) => {\n this.facts = facts;\n });\n return this.probed;\n }\n\n async putImmutable(\n key: GenKey,\n write: (sink: BlobSink) => Promise<void>,\n ): Promise<{ size: number; sha256: string }> {\n const objectKey = storageObjectKey(this.prefix, key); // validates ref + generation\n const sink = new S3MultipartSink(\n this.client,\n this.bucket,\n objectKey,\n this.partBytes,\n this.maxObjectBytes,\n this.readTimeoutMs,\n this.clock,\n this.rng,\n );\n try {\n await write(sink);\n return await sink.finish();\n } catch (err) {\n // Best-effort cleanup of any in-flight multipart upload, sent after an ambiguous or exhausted completion too.\n // S3 completes an upload atomically, so the abort never tears an object: a completion that had landed survives\n // it (the abort then answers NoSuchUpload, which is swallowed), and one that had not is cancelled and its parts\n // are reclaimed. No object is deleted here, and one that survives is the load's orphan above the pointer.\n await sink.abort();\n // A lost conditional-write race \u2014 the precondition failed (412) or S3 rejected concurrent conditional\n // writes to the key (409) \u2014 is the write-once conflict, never a silent overwrite.\n if (isConditionalConflict(err)) {\n throw new WriteConflictError(\n `generation already exists (write-once): ${key.segment}.${key.generation}`,\n );\n }\n if (isTypedPassThrough(err)) throw err;\n throw this.mapError(err);\n }\n }\n\n async getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array> {\n if (!Number.isInteger(offset) || !Number.isInteger(length) || offset < 0 || length < 0) {\n throw new ValidationError(`invalid range offset=${offset} length=${length}`);\n }\n const objectKey = storageObjectKey(this.prefix, key);\n if (length === 0) return new Uint8Array(0);\n return this.read('GetObject', key, async (options) => {\n const res = await this.client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: objectKey,\n Range: `bytes=${offset}-${offset + length - 1}`,\n }),\n options,\n );\n const bytes = await readBounded(\n res.Body,\n length,\n () => this.badRead(key, 'range', `the response is longer than the ${length}B requested`),\n res.ContentLength,\n );\n // A short read means the range ran past EOF \u2014 treat as out-of-bounds, never a partial result.\n if (bytes.length !== length) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds (got ${bytes.length}B)`,\n );\n }\n // The bytes must be the ones asked for, not as many from elsewhere: a range answer names its span, and one\n // without it (a 200, the whole object) is the range only when the range starts at 0.\n const span = spanFromContentRange(res.ContentRange);\n const wanted =\n span === undefined\n ? offset === 0\n : span.start === offset && span.end === offset + length - 1;\n if (!wanted) {\n throw this.badRead(\n key,\n 'range',\n `the response holds ${res.ContentRange === undefined ? 'the object from its start' : `bytes ${res.ContentRange}`}, not [${offset}, ${offset + length})`,\n );\n }\n return bytes;\n });\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<TailRead> {\n const objectKey = storageObjectKey(this.prefix, key);\n // The rule every driver's tail read holds to: a safe integer, and zero or less an empty tail.\n if (!Number.isSafeInteger(maxBytes)) {\n throw new ValidationError(`invalid tail length ${String(maxBytes)}`);\n }\n if (maxBytes <= 0) {\n // No tail bytes wanted \u2014 just resolve the size via a HEAD.\n const head = await this.head(key, objectKey);\n return withVersion({ bytes: new Uint8Array(0), size: head.size ?? 0 }, head.version);\n }\n const read = await this.read('GetObject', key, async (options) => {\n try {\n const res = await this.client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: objectKey,\n Range: `bytes=-${maxBytes}`,\n }),\n options,\n );\n const bytes = await readBounded(\n res.Body,\n maxBytes,\n () => this.badRead(key, 'tail', `the response is longer than the ${maxBytes}B requested`),\n res.ContentLength,\n );\n return { bytes, contentRange: res.ContentRange, version: res.ETag };\n } catch (err) {\n // A zero-byte object has no suffix to satisfy, so S3 refuses the range with a 416. The HEAD below settles\n // whether that is an empty object or a real range fault.\n if (isInvalidRange(err)) return { refused: err };\n throw err;\n }\n });\n if ('refused' in read) {\n const head = await this.head(key, objectKey);\n if (head.size !== 0) throw this.mapReadError(read.refused, key);\n return withVersion({ bytes: new Uint8Array(0), size: 0 }, head.version);\n }\n const { bytes, contentRange, version } = read;\n let size = totalFromContentRange(contentRange);\n if (contentRange !== undefined && !(size === 0 && bytes.length === 0)) {\n // A ranged answer must be the suffix asked for and hold exactly its bytes, as a range read's must. An empty\n // object has no suffix: a server may answer it with `bytes 0--1/0`.\n const span = spanFromContentRange(contentRange);\n const suffix =\n span !== undefined &&\n span.end - span.start + 1 === bytes.length &&\n (size === undefined ||\n (span.end === size - 1 && span.start === Math.max(0, size - maxBytes)));\n if (!suffix) {\n throw this.badRead(\n key,\n 'tail',\n `the response holds ${contentRange} in ${bytes.length}B, not the last ${maxBytes}B of the object`,\n );\n }\n }\n if (size === undefined) {\n // A spec-compliant backend omits Content-Range only on a 200 (whole object), where bytes.length\n // IS the size. If the body is exactly maxBytes we can't rule out a clamped partial from a\n // non-compliant backend \u2014 confirm the true size with a HEAD rather than trust a possibly-short read.\n size =\n bytes.length === maxBytes\n ? ((await this.head(key, objectKey)).size ?? bytes.length)\n : bytes.length;\n }\n // The version of the object the bytes came from: the ETag of the response that carried them.\n return withVersion({ bytes, size }, version);\n }\n\n private badRead(key: GenKey, what: 'range' | 'tail', why: string): ValidationError {\n return new ValidationError(\n `S3 ${what} read of ${key.segment}.${key.generation} refused: ${why}`,\n );\n }\n\n /**\n * The object's size and ETag from a `HeadObject`, each `undefined` when the response does not carry it. A missing\n * object throws {@link NotFoundError}.\n */\n private head(\n key: GenKey,\n objectKey: string,\n ): Promise<{ size: number | undefined; version: string | undefined }> {\n return this.read('HeadObject', key, async (options) => {\n const head = await this.client.send(\n new HeadObjectCommand({ Bucket: this.bucket, Key: objectKey }),\n options,\n );\n return { size: head.ContentLength, version: head.ETag };\n });\n }\n\n /**\n * One read request under the read timeout, its failures mapped to the driver vocabulary. The timeout's own\n * `TransientError` is raised outside the mapping, so it reaches the caller as it is.\n */\n private read<T>(\n operation: 'GetObject' | 'HeadObject',\n key: GenKey,\n run: (options: ReadSendOptions) => Promise<T>,\n ): Promise<T> {\n return timedRead(operation, this.readTimeoutMs, async (options) => {\n try {\n return await run(options);\n } catch (err) {\n throw this.mapReadError(err, key);\n } finally {\n this.sockets?.afterRequest();\n }\n });\n }\n\n async delete(key: GenKey, options?: StorageDeleteOptions): Promise<void> {\n const objectKey = storageObjectKey(this.prefix, key);\n const ifVersion = checkDeleteOptions(options);\n if (ifVersion !== undefined && this.explicitConditionalDelete !== false) await this.settle();\n const conditional =\n ifVersion !== undefined && appliesDeleteIfMatch(this.explicitConditionalDelete, this.facts);\n // Idempotent: S3 DeleteObject succeeds even if the key is absent (GC may race / retry).\n try {\n await this.client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: objectKey,\n ...(conditional ? { IfMatch: ifVersion } : {}),\n }),\n );\n } catch (raw) {\n const err = scrubCredentials(raw);\n if (conditional) {\n // An absent object is a no-op whatever version was given, and a failed precondition (`412`, or `409` for a\n // conditional request racing another on the key) is a conflict only while an object is under the key: a\n // service may answer it for a key with nothing there, a copy of this delete that landed first included.\n if (isNotFound(err)) return;\n if (isConditionalConflict(err)) {\n if (!(await this.exists(key, objectKey))) return;\n throw new WriteConflictError(\n `generation ${key.segment}.${key.generation} is another object than the version given; not deleted`,\n );\n }\n }\n throw this.mapError(err);\n }\n }\n\n /** Whether an object is under the key, from one `HeadObject`. A read that fails in any other way throws. */\n private async exists(key: GenKey, objectKey: string): Promise<boolean> {\n try {\n await this.head(key, objectKey);\n return true;\n } catch (err) {\n if (errorKind(err) === 'NotFoundError') return false;\n throw err;\n }\n }\n\n async *list(ref: SegmentRef): AsyncIterable<GenKey> {\n const prefix = segmentObjectPrefix(this.prefix, ref); // validates ref\n let token: string | undefined;\n do {\n let res;\n try {\n res = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n ContinuationToken: token,\n }),\n );\n } catch (err) {\n throw this.mapError(err);\n }\n for (const obj of res.Contents ?? []) {\n if (obj.Key === undefined) continue;\n const generation = parseGenerationFromKey(prefix, obj.Key);\n if (generation !== null) {\n yield { namespace: ref.namespace, segment: ref.segment, generation };\n }\n }\n // A page that says more follow and gives no way to ask for them would end the listing short, silently.\n if (res.IsTruncated === true && res.NextContinuationToken === undefined) {\n throw new IntegrityError('S3 listing says it is truncated but gives no continuation token');\n }\n // A page that hands back the token it was asked with would be asked for again, forever.\n if (res.IsTruncated === true && res.NextContinuationToken === token) {\n throw new IntegrityError('S3 listing returned the continuation token it was given');\n }\n token = res.IsTruncated === true ? res.NextContinuationToken : undefined;\n } while (token !== undefined);\n }\n\n /** Map S3 read errors to the driver vocabulary; pass everything else through {@link mapError}. */\n private mapReadError(err: unknown, key: GenKey): unknown {\n err = scrubCredentials(err);\n if (errorKind(err) === 'ValidationError') return err;\n if (isNotFound(err)) {\n return new NotFoundError(`no such generation: ${key.segment}.${key.generation}`);\n }\n // A fully out-of-range request (start past EOF) \u2014 the BlobReader contract treats range errors as\n // ValidationError, never a short/empty read.\n if (isInvalidRange(err)) {\n return new ValidationError(`range out of bounds for ${key.segment}.${key.generation}`);\n }\n return this.mapError(err);\n }\n\n /**\n * Reclassify a transient S3 fault (throttle/5xx/dropped connection) as a retryable {@link TransientError}, so the\n * store's read retry can ride it out and a write's caller can tell it from a deterministic failure, and a missing\n * bucket as a `ValidationError` that names it; everything else propagates unchanged. The final fallback at every\n * `client.send` site, so callers and the read retry only ever see typed errors.\n */\n private mapError(err: unknown): unknown {\n const scrubbed = asMissingBucket(scrubCredentials(err), this.bucket);\n return toTransient(scrubbed, isTransient, transientFaultMessage);\n }\n}\n\n/** A tail read, with the version it was read at when the response carried one. */\nfunction withVersion(\n tail: { bytes: Uint8Array; size: number },\n version: string | undefined,\n): TailRead {\n return version === undefined ? tail : { ...tail, version };\n}\n\n/**\n * Streaming {@link BlobSink} that uploads one S3 object in **constant memory**. It buffers at most\n * one part: as the codec writes, full parts are flushed via `UploadPart` and freed. A small object that never\n * reaches one part is committed as a single conditional `PutObject`; a larger one is finished with a\n * conditional `CompleteMultipartUpload` \u2014 **both enforce write-once** via `If-None-Match: *`, and both are sent\n * with the SDK's retry off, again only after a throttle ({@link commit}). SHA-256 is hashed incrementally. On any error\n * the caller invokes {@link abort} to clean up the in-flight multipart upload.\n */\nclass S3MultipartSink implements BlobSink {\n private readonly hash: Hash = createHash('sha256');\n private readonly pending: Uint8Array[] = [];\n private pendingLen = 0;\n private total = 0;\n private uploadId: string | undefined;\n private partNumber = 0;\n private readonly parts: { ETag: string | undefined; PartNumber: number }[] = [];\n /** This write's own id, stored in the object's user metadata, outside the `.crbm` bytes: see {@link commit}. */\n private readonly writeId = randomBytes(16).toString('hex');\n\n constructor(\n private readonly client: S3Client,\n private readonly bucket: string,\n private readonly objectKey: string,\n private readonly partBytes: number,\n private readonly maxObjectBytes: number,\n private readonly readTimeoutMs: number,\n private readonly clock: Sleeper,\n private readonly rng: Rng,\n ) {}\n\n async write(bytes: Uint8Array): Promise<void> {\n if (bytes.length === 0) return;\n this.total += bytes.length;\n if (this.total > this.maxObjectBytes) {\n // Fail fast + typed, rather than a late opaque S3 error (and abort the in-flight upload via the caller).\n throw new ValidationError(`object exceeds maxObjectBytes ${this.maxObjectBytes}`);\n }\n this.hash.update(bytes);\n // A copy: the caller may reuse its buffer once this write resolves, and the part is sent later.\n this.pending.push(new Uint8Array(bytes));\n this.pendingLen += bytes.length;\n if (this.pendingLen >= this.partBytes) await this.flushPart();\n }\n\n /** Upload the buffered bytes (\u2265 one part) as a single part, freeing them. Starts the upload on first call. */\n private async flushPart(): Promise<void> {\n if (this.uploadId === undefined) {\n const res = await this.client.send(\n new CreateMultipartUploadCommand({\n Bucket: this.bucket,\n Key: this.objectKey,\n Metadata: { [WRITE_ID_KEY]: this.writeId }, // stored on the object the completion makes\n }),\n );\n if (res.UploadId === undefined) {\n throw new TransientError('S3 CreateMultipartUpload returned no UploadId');\n }\n this.uploadId = res.UploadId;\n }\n const body = concatBytes(this.pending);\n this.pending.length = 0;\n this.pendingLen = 0;\n this.partNumber += 1;\n if (this.partNumber > S3_MAX_PARTS) {\n // Unreachable for valid input (the maxObjectBytes byte-cap, sized to \u2264 S3_MAX_PARTS parts, fires first) \u2014\n // a typed guard so the S3 hard limit is never a raw 400.\n throw new ValidationError(`multipart upload exceeded the S3 ${S3_MAX_PARTS}-part limit`);\n }\n const res = await this.client.send(\n new UploadPartCommand({\n Bucket: this.bucket,\n Key: this.objectKey,\n UploadId: this.uploadId,\n PartNumber: this.partNumber,\n Body: body,\n }),\n );\n this.parts.push({ ETag: res.ETag, PartNumber: this.partNumber });\n }\n\n /** Commit the object: a single conditional PUT if it fit in one part, else complete the multipart upload. */\n async finish(): Promise<{ size: number; sha256: string }> {\n const sha256 = this.hash.digest('hex');\n if (this.uploadId === undefined) {\n const body = concatBytes(this.pending);\n await this.commit('PutObject', () =>\n sendOnce(\n this.client,\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.objectKey,\n Body: body,\n IfNoneMatch: '*', // write-once\n Metadata: { [WRITE_ID_KEY]: this.writeId },\n }),\n ),\n );\n return { size: this.total, sha256 };\n }\n if (this.pendingLen > 0) await this.flushPart(); // the final part may be < partBytes (allowed)\n const uploadId = this.uploadId;\n await this.commit('CompleteMultipartUpload', () =>\n sendOnce(\n this.client,\n new CompleteMultipartUploadCommand({\n Bucket: this.bucket,\n Key: this.objectKey,\n UploadId: uploadId,\n MultipartUpload: { Parts: this.parts },\n IfNoneMatch: '*', // write-once: fail if the object already exists\n }),\n ),\n );\n this.uploadId = undefined; // completed \u2014 nothing left to abort\n return { size: this.total, sha256 };\n }\n\n /**\n * Send the commit (`send` sends it once, with the SDK's retry off), and again after a throttle, up to\n * {@link THROTTLE_RESENDS} times, waiting a full-jitter backoff before each. Any other failure is thrown at once.\n * Once the commit has been sent again, an answer that can be an earlier send that landed or is still landing (a\n * precondition failure, a concurrent-write conflict, or an upload S3 no longer knows) is settled by reading the\n * object back. The object is never overwritten, so the id it holds says who wrote it:\n *\n * - this write's own id: a success;\n * - another's id, or none (an object an earlier release wrote): the conflict it reads as;\n * - nothing stored: a `412` still reads as the conflict it answered (an object was there, and is gone). A `409`\n * and an unknown upload are an unknown outcome, since the first send may still be applying: {@link TransientError},\n * and never a report that nothing was written.\n *\n * A failed read-back throws, and is neither. Exhausted, it throws {@link TransientError}; nothing is deleted.\n */\n private async commit(\n operation: 'PutObject' | 'CompleteMultipartUpload',\n send: () => Promise<unknown>,\n ): Promise<void> {\n for (let resent = 0; ; resent++) {\n try {\n await send();\n return;\n } catch (raw) {\n const err = scrubCredentials(raw);\n if (resent > 0 && (isConditionalConflict(err) || isNoSuchUpload(err))) {\n const stored = await this.storedObject();\n if (stored?.id === this.writeId) return;\n if (stored === undefined && !isPreconditionFailed(err)) {\n throw new TransientError(\n `S3 ${operation}: ${this.objectKey} is not stored, and the first send may still be applying`,\n { cause: err },\n );\n }\n if (isConditionalConflict(err)) throw err;\n throw new WriteConflictError(`generation already exists (write-once): ${this.objectKey}`);\n }\n if (!isThrottle(err)) throw err;\n if (resent >= THROTTLE_RESENDS) {\n throw new TransientError(\n `S3 ${operation} was throttled on each of its ${resent + 1} sends; the object may or may not exist`,\n { cause: err },\n );\n }\n await this.clock.sleep(Math.floor(this.rng.next() * THROTTLE_BASE_DELAY_MS * 2 ** resent));\n }\n }\n }\n\n /**\n * The object stored under the key: `undefined` when none is there, otherwise the write id it carries (`id` is\n * `undefined` for an object that carries none). A failed read throws.\n */\n private storedObject(): Promise<{ readonly id: string | undefined } | undefined> {\n return timedRead('HeadObject', this.readTimeoutMs, async (options) => {\n try {\n const head = await this.client.send(\n new HeadObjectCommand({ Bucket: this.bucket, Key: this.objectKey }),\n options,\n );\n return { id: head.Metadata?.[WRITE_ID_KEY] };\n } catch (err) {\n if (isNotFound(err)) return undefined;\n throw err;\n }\n });\n }\n\n /** Best-effort cleanup of an in-flight multipart upload after an error (a leaked MPU is reaped by a bucket\n * lifecycle rule; never a correctness issue). No-op if nothing was started or it already completed. It aborts an\n * upload, never an object: a completion that landed survives it. */\n async abort(): Promise<void> {\n if (this.uploadId === undefined) return;\n const id = this.uploadId;\n this.uploadId = undefined;\n try {\n await this.client.send(\n new AbortMultipartUploadCommand({ Bucket: this.bucket, Key: this.objectKey, UploadId: id }),\n );\n } catch {\n // swallow \u2014 best-effort\n }\n }\n}\n", "/**\n * Pure helpers for classifying S3 SDK errors + parsing response headers (conflict and transient classification).\n *\n * Kept SDK-free and side-effect-free (they only read structural shapes \u2014 `err.name`,\n * `$metadata.httpStatusCode`, a `Content-Range` string) so the subtle S3-specific translation logic is\n * unit-testable without a live MinIO/S3 or even the AWS SDK. The AWS error shapes come from `./aws-errors`.\n */\n\nimport { ValidationError } from '@cloudbitmaps/core/driver-kit';\nimport {\n errorName,\n httpStatus,\n isNetworkOrTimeout,\n isSdkRetryable,\n isServerSide,\n} from './aws-errors';\n\n/** A conditional `If-None-Match: *` PUT lost the write-once race (the object already existed). */\nexport function isPreconditionFailed(err: unknown): boolean {\n return errorName(err) === 'PreconditionFailed' || httpStatus(err) === 412;\n}\n\n/**\n * A conditional write (`If-None-Match: *` / `If-Match: <etag>`) lost the race \u2014 **either** outcome S3 uses:\n * the precondition evaluated false (`412 PreconditionFailed`), **or** S3 rejected concurrent conditional\n * writes to the same key to prevent a lost update (`409 ConditionalRequestConflict`, which AWS documents and\n * asks you to retry). Both mean \"you lost; re-read and retry\" \u2014 so both must map to `WriteConflictError` and\n * route through the caller's OCC path, never a blind transient retry (which would just replay a doomed PUT).\n */\nexport function isConditionalConflict(err: unknown): boolean {\n return (\n isPreconditionFailed(err) ||\n errorName(err) === 'ConditionalRequestConflict' ||\n httpStatus(err) === 409\n );\n}\n\n/**\n * The service asked the client to slow down: `503 SlowDown`, any other `503` (`ServiceUnavailable`), or a bare `429`\n * (`TooManyRequests`), which AWS S3 does not send and some S3-compatible services do. The SDK raises it with `name` set\n * to the code and `$metadata.httpStatusCode` to the status, and no `$retryable` marker. The service does not document\n * that a throttled request was not applied, so a write sent again after one must still tell a landed first send apart.\n */\nexport function isThrottle(err: unknown): boolean {\n const status = httpStatus(err);\n return errorName(err) === 'SlowDown' || status === 503 || status === 429;\n}\n\n/** A multipart upload id S3 no longer knows: completed, aborted or expired (`404 NoSuchUpload`). */\nexport function isNoSuchUpload(err: unknown): boolean {\n return errorName(err) === 'NoSuchUpload';\n}\n\n/**\n * The bucket itself does not exist (`404 NoSuchBucket`): a wiring fault, never an absent object. Read as one, a\n * misnamed or deleted bucket would answer every read as an empty segment. A HEAD carries no code, so it cannot be told\n * apart there; every request that reaches a missing bucket with a body says so.\n */\nexport function isMissingBucket(err: unknown): boolean {\n return errorName(err) === 'NoSuchBucket';\n}\n\n/**\n * `err`, or the {@link ValidationError} a missing bucket is: it names the bucket and what to check, and carries the\n * service's error as its `cause`. A wiring fault is not an absent object, so it is not a `NotFoundError`: a read\n * re-resolves on that, and would treat a misnamed bucket as a miss it can heal.\n */\nexport function asMissingBucket(err: unknown, bucket: string): unknown {\n if (!isMissingBucket(err)) return err;\n return Object.assign(\n new ValidationError(\n `the S3 bucket does not exist: ${bucket}; check the bucket name, and the region and endpoint of the client`,\n ),\n { cause: err },\n );\n}\n\n/**\n * The object / generation does not exist (GetObject \u2192 `NoSuchKey`, HeadObject \u2192 `NotFound`; both 404), in a bucket\n * that does: a 404 that says the bucket is missing is not one.\n */\nexport function isNotFound(err: unknown): boolean {\n if (isMissingBucket(err)) return false;\n const name = errorName(err);\n return name === 'NoSuchKey' || name === 'NotFound' || httpStatus(err) === 404;\n}\n\n/** A range request started past EOF (HTTP 416). */\nexport function isInvalidRange(err: unknown): boolean {\n return errorName(err) === 'InvalidRange' || httpStatus(err) === 416;\n}\n\n/**\n * S3 refused the request's signature because the client's clock was off by minutes, and the SDK has corrected the\n * clock for the next request. Nothing was applied, and a second request is signed right. The SDK's own retry treats\n * this as transient; a conditional write is sent once without that retry, so the driver has to say so itself.\n */\nfunction isClockSkewCorrected(err: unknown): boolean {\n const e = err as { $metadata?: { clockSkewCorrected?: unknown } } | null;\n return e?.$metadata?.clockSkewCorrected === true;\n}\n\n/**\n * A transient S3 fault that is safe to retry: throttling (`SlowDown` / 503 / 429), any 5xx, a dropped/timed-out\n * connection, a clock-skew refusal the SDK has corrected for, or anything the SDK itself marks retryable. Excludes\n * the deterministic outcomes above (412/404/416) \u2014 those are caller-meaningful and must never be\n * retried/reclassified.\n */\nexport function isTransient(err: unknown): boolean {\n // A conditional-write conflict (412/409) is caller-meaningful OCC, not a blind-retryable transient.\n if (isConditionalConflict(err) || isNotFound(err) || isInvalidRange(err)) return false;\n return (\n isThrottle(err) ||\n isServerSide(err) ||\n isNetworkOrTimeout(err) ||\n isSdkRetryable(err) ||\n isClockSkewCorrected(err)\n );\n}\n\n/**\n * The message a transient S3 fault is handed on with: `transient S3 fault: <name>`, the SDK error's name, which names\n * S3's error code (`SlowDown`, `InternalError`) or the transport failure (`TimeoutError`).\n */\nexport function transientFaultMessage(err: unknown): string {\n return `transient S3 fault: ${errorName(err) ?? 'unknown'}`;\n}\n\n/** The first and last byte a `Content-Range: bytes <start>-<end>/<total>` header names, or `undefined` if unparseable. */\nexport function spanFromContentRange(\n contentRange: string | undefined,\n): { start: number; end: number } | undefined {\n if (contentRange === undefined) return undefined;\n const match = /^\\s*bytes\\s+(\\d+)-(\\d+)\\//.exec(contentRange);\n if (match === null) return undefined;\n const start = Number(match[1]);\n const end = Number(match[2]);\n return Number.isSafeInteger(start) && Number.isSafeInteger(end) ? { start, end } : undefined;\n}\n\n/**\n * Parse the total object size out of a `Content-Range: bytes <start>-<end>/<total>` header, or `undefined`\n * if absent/unparseable/unsafe. The total is the part after the final `/`.\n */\nexport function totalFromContentRange(contentRange: string | undefined): number | undefined {\n if (contentRange === undefined) return undefined;\n const match = /\\/(\\d+)\\s*$/.exec(contentRange);\n if (match === null) return undefined;\n const total = Number(match[1]);\n return Number.isSafeInteger(total) ? total : undefined;\n}\n", "/**\n * `sendOnce` \u2014 send a conditional write exactly once, with the SDK's retry off for that one command.\n *\n * The SDK re-sends a request whose response it did not get: a timeout, a reset connection, a 5xx. For a conditional\n * write that is the wrong thing to do. When the write landed and only its response was lost, the second send meets\n * the first \u2014 `If-None-Match: *` finds the object it created, `If-Match` finds the ETag it replaced \u2014 and fails with\n * `412`, which the driver can only report as a lost race. The caller is told its write lost when it won. Only the\n * caller can find out which happened, by reading the pointer or listing the generations, so the write is sent once\n * and a transient failure reaches it as `TransientError`.\n *\n * **How.** The client runs its retry as one middleware, `retryMiddleware`, at high priority in the `finalizeRequest`\n * step, whatever retry strategy or `maxAttempts` it was built with. When a command is sent, its own middleware stack\n * is merged over the client's, and an entry with the same name, step and priority that sets `override` replaces the\n * client's. So this command runs with a pass-through where the retry was, and nothing about the client changes: a\n * caller's own client keeps its configuration, and every other command sent through it keeps its retry.\n *\n * The options object passed to `send` matters too. A client built with `cacheMiddleware: true` reuses the handler it\n * resolved for the first command of a class, and that handler holds the retry; `send` resolves afresh whenever it is\n * given options, so the replacement always takes effect.\n */\nimport type {\n $Command,\n S3Client,\n S3ClientResolvedConfig,\n ServiceInputTypes,\n ServiceOutputTypes,\n} from '@aws-sdk/client-s3';\n\n/** Where the client registers its retry. Overriding an entry takes the same name, step and priority. */\nconst NO_RETRY = {\n name: 'retryMiddleware',\n step: 'finalizeRequest',\n priority: 'high',\n override: true,\n} as const;\n\n/** Send `command` once: its retry step is a pass-through, and the client's stays as it is for every other command. */\nexport function sendOnce<Input extends ServiceInputTypes, Output extends ServiceOutputTypes>(\n client: S3Client,\n command: $Command<Input, Output, S3ClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes>,\n): Promise<Output> {\n command.middlewareStack.add((next) => next, NO_RETRY);\n return client.send(command, {});\n}\n", "/**\n * Read an S3 response body as a stream, counting bytes, and stop at the first byte past `maxBytes`: a server that\n * ignores `Range`, or sends a body with no `Content-Length`, cannot make the caller buffer more than it asked for.\n * The body is destroyed on every exit that leaves it unread, which closes its connection.\n *\n * A body that is not a stream (a stand-in with only `transformToByteArray`) is read whole and its length checked after.\n */\nimport { NotFoundError } from '@cloudbitmaps/core/driver-kit';\n\n/** The `GetObject` response body, narrowed to the parts used. */\ntype ResponseBody =\n | {\n transformToByteArray?: () => Promise<Uint8Array>;\n [Symbol.asyncIterator]?: () => AsyncIterator<unknown>;\n destroy?: () => void;\n }\n | undefined;\n\n/** Destroy a response body left unread, which releases its connection; a body with no `destroy` is left alone. */\nexport function destroyBody(body: unknown): void {\n (body as { destroy?: () => void } | undefined)?.destroy?.();\n}\n\n/**\n * The body's bytes, at most `maxBytes` of them. `oversize` builds the typed error thrown when the count passes\n * `maxBytes`, or when the response advertises more than that before a byte is read; any other failure of the stream\n * propagates as it is.\n */\nexport async function readBounded(\n body: ResponseBody,\n maxBytes: number,\n oversize: () => Error,\n advertised?: number,\n): Promise<Uint8Array> {\n if (body === undefined) throw new NotFoundError('S3 GetObject returned an empty body');\n if (advertised !== undefined && advertised > maxBytes) {\n destroyBody(body);\n throw oversize();\n }\n if (typeof body[Symbol.asyncIterator] !== 'function') {\n const bytes = await body.transformToByteArray!();\n if (bytes.length > maxBytes) throw oversize();\n return bytes;\n }\n const chunks: Uint8Array[] = [];\n let total = 0;\n let done = false;\n try {\n for await (const chunk of body as AsyncIterable<Uint8Array | string>) {\n const piece = typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk;\n total += piece.length;\n if (total > maxBytes) throw oversize();\n chunks.push(piece);\n }\n done = true;\n } finally {\n if (!done) destroyBody(body);\n }\n const out = new Uint8Array(total);\n let at = 0;\n for (const piece of chunks) {\n out.set(piece, at);\n at += piece.length;\n }\n return out;\n}\n", "/**\n * `probeClient` \u2014 what an `S3Client` would send for the requests the registry makes, found out without sending one.\n *\n * Two facts about a client decide whether the registry may rely on S3's conditional requests, and neither shows on the\n * client's configuration:\n *\n * - **Where a request goes.** A constructor `endpoint` is on the resolved config, but an endpoint from\n * `AWS_ENDPOINT_URL_S3`, `AWS_ENDPOINT_URL` or an `endpoint_url` in the shared config file is read only when the\n * first request is built, and the client's `isCustomEndpoint` stays `false` for all of them. A store reached that way\n * (MinIO, LocalStack) can accept `If-Match` on a delete and ignore it.\n * - **Whether the SDK sends the header at all.** The SDK serialises the members its model knows, and an SDK that\n * predates one drops it from the request without a word, so a conditional write or delete goes out unconditional.\n *\n * So the probe builds the requests the registry sends and runs them through a **second client of the same class**,\n * made from the first one's resolved configuration, with a placeholder credential, a transport that is never handed a\n * request and a silent logger. It reads each request back as it stands once the SDK has serialised it, which is before\n * it is signed or sent: a middleware added to the probe command's own stack answers there instead of passing the\n * request on. The second client is the SDK's own resolution of everything that decides where a request goes (region,\n * endpoint, the environment, the shared config file, FIPS, dual-stack, path style), and the client the caller holds is\n * never touched: nothing it has been given runs, so no middleware of the caller's counts a request that never exists,\n * no logger prints one, and no credential is looked up.\n *\n * The probe runs the command through the second client's stack itself, as its `send` would, and does not call `send`: a\n * stub of `S3Client.prototype.send` (a class-level mock, as `aws-sdk-client-mock` installs) records nothing from it.\n *\n * A middleware the caller added to its own client that changes where a request goes is therefore not seen.\n *\n * A probe that cannot run (a client with no resolved config, an unresolvable region, a stack that does not hold the\n * step the probe hooks) answers `undefined`, which callers read as \"not known\".\n */\nimport { DeleteObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';\nimport type {\n $Command,\n S3Client,\n S3ClientResolvedConfig,\n ServiceInputTypes,\n ServiceOutputTypes,\n} from '@aws-sdk/client-s3';\n\n/** What a client does with the requests the registry sends. */\nexport interface ClientFacts {\n /** The host a request to the bucket is addressed to, as the client resolves it now. */\n readonly host: string;\n /** Whether a `DeleteObject` given `IfMatch` goes out with an `If-Match` header. */\n readonly sendsDeleteIfMatch: boolean;\n /** Whether a `PutObject` given `IfMatch` goes out with an `If-Match` header: the registry's compare-and-swap. */\n readonly sendsPutIfMatch: boolean;\n /** Whether a `PutObject` given `IfNoneMatch` goes out with an `If-None-Match` header: the registry's create. */\n readonly sendsPutIfNoneMatch: boolean;\n}\n\n/** A key no registry row or generation can hold; the probe's requests are never sent, so it is never used. */\nexport const PROBE_KEY = 'cloudbitmaps-probe';\n\n/** The request as the SDK serialises it, as far as the probe reads it. */\ninterface SerialisedRequest {\n readonly hostname?: unknown;\n readonly headers?: Record<string, unknown>;\n}\n\n/** A credential no one can use: the probe signs nothing, but the client it builds wants one. */\nconst PLACEHOLDER_CREDENTIALS = { accessKeyId: 'cloudbitmaps-probe', secretAccessKey: 'unused' };\n\n/** A transport that is never handed a request: the probe answers before the step that would send one. */\nconst NEVER_SENDS = {\n handle: (): Promise<never> =>\n Promise.reject(new Error('the client probe was asked to send a request')),\n updateHttpClientConfig: (): void => {},\n httpHandlerConfigs: (): Record<string, never> => ({}),\n destroy: (): void => {},\n};\n\nconst SILENT_LOGGER = {\n debug: (): void => {},\n info: (): void => {},\n warn: (): void => {},\n error: (): void => {},\n};\n\n/**\n * A second client like `client`: its class (so the SDK that resolves where a request goes is the caller's), its resolved\n * configuration, and none of what the caller added to it. `undefined` for anything that is not a client of that shape.\n */\nfunction cloneForProbe(client: S3Client): S3Client | undefined {\n const parts = client as unknown as { config?: unknown; constructor?: unknown; send?: unknown };\n if (\n typeof parts.send !== 'function' ||\n typeof parts.config !== 'object' ||\n parts.config === null\n ) {\n return undefined;\n }\n if (typeof parts.constructor !== 'function' || parts.constructor === Object) return undefined;\n const Class = parts.constructor as new (config: object) => S3Client;\n const clone = new Class({\n ...parts.config,\n credentials: PLACEHOLDER_CREDENTIALS,\n requestHandler: NEVER_SENDS,\n logger: SILENT_LOGGER,\n });\n return typeof clone.send === 'function' && clone.middlewareStack != null ? clone : undefined;\n}\n\n/**\n * Run `command` through `client`'s stack up to the step after the SDK serialises the request, and return the request.\n * The answer is made there, so the rest of the stack (retry, signing, the transport) never runs.\n */\nasync function serialise<Input extends ServiceInputTypes, Output extends ServiceOutputTypes>(\n client: S3Client,\n command: $Command<Input, Output, S3ClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes>,\n): Promise<SerialisedRequest> {\n let seen: SerialisedRequest | undefined;\n command.middlewareStack.addRelativeTo(\n () => (args: { request?: unknown }) => {\n seen = args.request as SerialisedRequest;\n return Promise.resolve({ response: {}, output: { $metadata: {} } as unknown as Output });\n },\n { relation: 'after', toMiddleware: 'serializerMiddleware', name: 'cloudbitmapsProbe' },\n );\n // What `send` does, minus `send`: a stub of `S3Client.prototype.send` (a class-level mock) sees nothing of the probe.\n const handler = command.resolveMiddleware(client.middlewareStack, client.config, {});\n await handler(command);\n if (seen === undefined) throw new Error('the probe saw no request');\n return seen;\n}\n\nconst hasHeader = (request: SerialisedRequest, name: string): boolean =>\n request.headers?.[name] !== undefined;\n\n/**\n * What `client` does with a conditional `DeleteObject` and `PutObject` to `bucket`, or `undefined` when it cannot be\n * found out. Never throws, never sends a request, and never runs anything the caller added to `client`.\n */\nexport async function probeClient(\n client: S3Client,\n bucket: string,\n): Promise<ClientFacts | undefined> {\n let probe: S3Client | undefined;\n try {\n probe = cloneForProbe(client);\n if (probe === undefined) return undefined;\n const del = await serialise(\n probe,\n new DeleteObjectCommand({ Bucket: bucket, Key: PROBE_KEY, IfMatch: '\"probe\"' }),\n );\n const put = await serialise(\n probe,\n new PutObjectCommand({\n Bucket: bucket,\n Key: PROBE_KEY,\n Body: new Uint8Array(0),\n IfMatch: '\"probe\"',\n IfNoneMatch: '*',\n }),\n );\n if (typeof del.hostname !== 'string' || del.hostname === '') return undefined;\n return {\n host: del.hostname,\n sendsDeleteIfMatch: hasHeader(del, 'if-match'),\n sendsPutIfMatch: hasHeader(put, 'if-match'),\n sendsPutIfNoneMatch: hasHeader(put, 'if-none-match'),\n };\n } catch {\n return undefined;\n } finally {\n probe?.destroy();\n }\n}\n\n/**\n * Whether a `DeleteObject` may rely on `If-Match`, for the registry's row delete and the storage half's conditional\n * delete alike: `explicit` when the caller set it, but never for an SDK seen not to send the header; otherwise only\n * when the client resolves to an AWS S3 host, which applies the precondition, and `false` until the client has been\n * asked, since an S3-compatible store (MinIO, for one) can accept the header and ignore it.\n */\nexport function appliesDeleteIfMatch(\n explicit: boolean | undefined,\n facts: ClientFacts | undefined,\n): boolean {\n if (explicit === false) return false;\n if (explicit === true) return facts?.sendsDeleteIfMatch !== false;\n return facts !== undefined && facts.sendsDeleteIfMatch && isAwsS3Host(facts.host);\n}\n\n/** The domains AWS serves S3 from: the standard partition, and China's. */\nconst AWS_DOMAINS = ['.amazonaws.com', '.amazonaws.com.cn'];\n\n/** The labels that name S3 itself in one of its hosts. */\nconst S3_LABELS = new Set([\n 's3',\n 's3-fips',\n 's3-accesspoint',\n 's3-accesspoint-fips',\n 's3-global',\n 's3-object-lambda',\n 's3-outposts',\n 's3-external-1',\n]);\n/** The host form that joins the region to the label with a dash, which AWS also serves: `s3-us-west-2`, `s3-fips-us-gov-west-1`. */\nconst S3_DASH_REGION_LABEL = /^s3-(fips-)?[a-z]{2}(-gov)?-[a-z]+-\\d$/;\n/** A directory bucket's zonal label: `s3express-use1-az4`. */\nconst S3_EXPRESS_LABEL = /^s3express-[a-z0-9-]+$/;\nconst REGION_LABEL = /^[a-z]{2}(-[a-z]+)+-\\d+$/;\n\nconst isS3Label = (label: string): boolean =>\n S3_LABELS.has(label) || S3_DASH_REGION_LABEL.test(label) || S3_EXPRESS_LABEL.test(label);\n\n/**\n * Whether `hostname` is an AWS S3 host, by its structure: under an AWS domain, with a label that names S3 (`s3`,\n * `s3-fips`, an access point's, a multi-region access point's `s3-global`, Object Lambda's, an Outpost's, a dash-joined\n * `s3-<region>`, a directory bucket's `s3express-\u2026`), followed only by what S3's hosts put there: `dualstack`, a\n * region and `vpce`, in that order. Anything may come before it (a bucket, an access point, a VPC endpoint id). So\n * the FIPS, dual-stack, access-point and VPC interface forms all count, and another AWS service's host whose name\n * starts with `s3` does not: a load balancer, an API Gateway, a website endpoint. A host of any other domain is not,\n * whatever it speaks: a store behind one has to be vouched for by the caller.\n */\nexport function isAwsS3Host(hostname: string): boolean {\n const host = hostname.toLowerCase().replace(/\\.$/, '');\n const domain = AWS_DOMAINS.find((d) => host.endsWith(d));\n if (domain === undefined) return false;\n const labels = host.slice(0, -domain.length).split('.');\n // The last S3 label is the service's: a bucket's own name may hold one (`my.s3.bucket`).\n let at = -1;\n labels.forEach((label, i) => {\n if (isS3Label(label)) at = i;\n });\n if (at === -1) return false;\n const rest = labels.slice(at + 1);\n let k = 0;\n if (rest[k] === 'dualstack') k++;\n if (k < rest.length && REGION_LABEL.test(rest[k]!)) k++;\n if (rest[k] === 'vpce') k++;\n return k === rest.length;\n}\n", "/**\n * `S3RegistryDriver` \u2014 an {@link IRegistryDriver} over S3-compatible object storage.\n *\n * Lets a **read-mostly deployment run on S3 alone** \u2014 storage `.crbm` generations + the registry in one bucket,\n * no separate database. The protocol (an ABA-safe OCC token, the delete and its tombstone, the bounded retry, the key\n * layout) lives once in {@link ObjectStoreRegistry}; this file is only the I/O calls S3 makes, so the S3, GCS\n * and Azure registries cannot drift from one another.\n *\n * **A delete removes the row for good** with a `DeleteObject` under `If-Match: <etag>`, sent once like the writes,\n * when `conditionalDelete` is on. By default it is on when the host the client resolves is an AWS S3 host, whichever way\n * the endpoint was set (a constructor `endpoint`, `AWS_ENDPOINT_URL_S3`, `AWS_ENDPOINT_URL`, the shared config file), and\n * off for any other, since an S3-compatible store may accept the header and ignore it (MinIO does). The client is asked\n * once, before the registry's first request, and the SDK is checked to send the header at all ({@link probeClient}).\n *\n * **The atomic swap is offloaded to S3's conditional writes** (GA Nov 2024): `If-None-Match: *` for\n * create-only and `If-Match: <etag>` for compare-and-swap, so a concurrent writer between our read and our\n * PUT loses with a `412` \u2192 {@link WriteConflictError}. Each conditional PUT is sent once, with the SDK's retry off\n * for it ({@link sendOnce}), so a `412` means another write got there first, never this one meeting itself after a\n * lost response. A transient failure reaches the caller as {@link TransientError}: the write may or may not have\n * landed, and the caller re-reads the row to learn where it stands. Reads are strongly consistent (S3, since 2020),\n * satisfying the registry's `strongRead` contract, and each can be timed as the storage driver's are ({@link timedRead}):\n * with `readTimeoutMs` set, a row's `GetObject` that has not finished, body included, after it throws\n * {@link TransientError}. It is off by default, and the writes and listings are never timed. The client is **injected**, exactly like {@link S3StorageDriver}.\n *\n * **Deployment requirements** (a backend/policy that violates these silently corrupts the registry):\n * - The backend **must honor `If-Match`** (AWS S3; recent MinIO). One that returns ETags but ignores the\n * precondition degrades compare-and-swap to last-write-wins \u2192 lost `currentGen` swaps. Verified against\n * real S3 semantics by the MinIO integration lane.\n * - The IAM principal needs **`s3:ListBucket`** on the bucket. Without it, `GetObject` on a missing key\n * returns `403` (not `404`), so the \"absent segment \u2192 `null`\" contract (and `create`'s bootstrap read)\n * breaks \u2014 and `list()` needs it regardless.\n * - **Do not apply an S3 lifecycle-expiration rule to the `registry/` prefix that expires a current version**\n * (`NoncurrentVersionExpiration` is safe). See {@link ObjectStoreRegistry}.\n */\nimport {\n errorKind,\n IntegrityError,\n MAX_ROW_BYTES,\n ObjectStoreRegistry,\n ValidationError,\n WriteConflictError,\n normalizeObjectPrefix,\n requireBool,\n toTransient,\n} from '@cloudbitmaps/core/driver-kit';\nimport type { ObjectRegistryStore, ObjectRow } from '@cloudbitmaps/core/driver-kit';\nimport {\n DeleteObjectCommand,\n GetObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n type S3Client,\n} from '@aws-sdk/client-s3';\nimport { resolveReadTimeoutMs, timedRead } from './read-timeout';\nimport {\n asMissingBucket,\n isConditionalConflict,\n isNotFound,\n isTransient,\n transientFaultMessage,\n} from './s3-errors';\nimport { sendOnce } from './send-once';\nimport { destroyBody, readBounded } from './bounded-body';\nimport { scrubCredentials } from './scrub-error';\nimport { appliesDeleteIfMatch, probeClient } from './client-probe';\nimport type { ClientFacts } from './client-probe';\n\ninterface S3RegistryDriverOptions {\n /** A constructed S3 client (point its `endpoint` at MinIO for local/integration use). */\n readonly client: S3Client;\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional key prefix under which all registry objects live (e.g. `cloudbitmaps/`). */\n readonly prefix?: string;\n /** Injected clock for `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n /**\n * How long one read of a row \u2014 its `GetObject`, the body included \u2014 may take before it is abandoned and throws\n * `TransientError`, in ms. `0`, the default, sets no timeout. Must be a non-negative safe integer no larger than\n * 2,147,483,647. Writes and listings are not timed.\n *\n * The clock starts when the read is handed to the SDK, so it also counts the time the read waits for one of the\n * client's sockets (128 by default on the client the store builds, 50 on the SDK's own) and the time spent fetching\n * credentials, and under `retryMode: 'adaptive'` the SDK's rate-limiter wait. A burst of concurrent reads larger than\n * the socket pool can therefore time out with nothing slow on the wire: size the timeout above the worst queueing\n * your concurrency implies, or raise `maxSockets` (the store's option, or your own client's). On a client built with\n * `cacheMiddleware: true`, a timed read resolves its middleware each time.\n */\n readonly readTimeoutMs?: number;\n /**\n * Whether a delete removes a row for good, by a `DeleteObject` sent with `If-Match: <the ETag it read>`, rather than\n * leaving a tombstone.\n *\n * Defaults to `true` when the host the client resolves is an AWS S3 host (`*.amazonaws.com`, `*.amazonaws.com.cn`: the\n * FIPS, dual-stack, access-point and VPC interface forms included), where `If-Match` on `DeleteObject` is documented\n * for general purpose and directory buckets, and to `false` for any other host, or when the host cannot be resolved.\n * The host is the one the SDK resolves for a request, so an endpoint set through `AWS_ENDPOINT_URL_S3`,\n * `AWS_ENDPOINT_URL` or an `endpoint_url` in the shared config file counts exactly as a constructor `endpoint` does.\n * It is resolved once, lazily: until the registry's first read or listing the answer reads `false`, and a client that\n * cannot be resolved then (no region) keeps tombstoning for the life of the registry, so set the\n * option to override it.\n *\n * Set it explicitly to override the host. An S3-compatible store must apply the precondition before you set it there:\n * MinIO, for one, ignores it and deletes anyway, and on such a store two sweepers and a re-create can delete a live\n * row. A `true` never overrides the SDK: one that does not send `If-Match` on a `DeleteObject` leaves the row\n * tombstoned whatever is set.\n */\n readonly conditionalDelete?: boolean;\n}\n\n/**\n * The calls {@link ObjectStoreRegistry} needs, in S3's dialect. Exported for the tests that drive one directly.\n *\n * `conditionalDelete` is the caller's explicit setting, or `undefined` to follow the client. What the client does is\n * found out once, before the first read, listing or write ({@link probeClient}), so by the time the registry decides\n * how to delete it has been.\n */\nexport class S3RegistryStore implements ObjectRegistryStore {\n readonly label = 'S3';\n private probed: Promise<void> | undefined;\n private facts: ClientFacts | undefined;\n\n constructor(\n private readonly client: S3Client,\n private readonly bucket: string,\n private readonly readTimeoutMs: number,\n private readonly explicitConditionalDelete?: boolean,\n ) {}\n\n /**\n * Whether a delete may rely on `If-Match`: the caller's setting, else whether the client's host is AWS S3, and in\n * neither case when the SDK is seen not to send the header. `false` until the client has been asked, unless the caller\n * vouched for the store.\n */\n get conditionalDelete(): boolean {\n return appliesDeleteIfMatch(this.explicitConditionalDelete, this.facts);\n }\n\n /** Ask the client once what it does with the registry's requests; every caller waits for the same answer. */\n private settle(): Promise<void> {\n this.probed ??= probeClient(this.client, this.bucket).then((facts) => {\n this.facts = facts;\n });\n return this.probed;\n }\n\n /** Refuse a write the SDK would send without its precondition: it would land as a plain overwrite. */\n private refuseUnfencedWrites(expect: 'absent' | { version: string }): void {\n const header = expect === 'absent' ? 'If-None-Match' : 'If-Match';\n const sent =\n expect === 'absent' ? this.facts?.sendsPutIfNoneMatch : this.facts?.sendsPutIfMatch;\n if (sent === false) {\n throw new ValidationError(\n `the S3 client does not send ${header} on a PutObject: its @aws-sdk/client-s3 predates the member, so a ` +\n `registry write would overwrite whatever is there and lose a concurrent writer's change. Use a newer ` +\n `@aws-sdk/client-s3; the registry sent nothing`,\n );\n }\n }\n\n async read(key: string): Promise<ObjectRow | null> {\n await this.settle();\n return timedRead('GetObject', this.readTimeoutMs, async (options) => {\n let res;\n try {\n res = await this.client.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: key }),\n options,\n );\n } catch (err) {\n if (isNotFound(err)) return null;\n throw mapError(err, this.bucket);\n }\n try {\n // Check the advertised length BEFORE allocating, so a hostile object cannot make us buffer it first.\n if ((res.ContentLength ?? 0) > MAX_ROW_BYTES) {\n throw new IntegrityError(\n `registry object ${res.ContentLength}B exceeds cap ${MAX_ROW_BYTES}B`,\n );\n }\n if (res.Body === undefined) {\n throw new IntegrityError(`registry object has an empty body: ${key}`);\n }\n const bytes = await readBounded(\n res.Body,\n MAX_ROW_BYTES,\n () => new IntegrityError(`registry object exceeds cap ${MAX_ROW_BYTES}B`),\n );\n return { bytes, version: res.ETag ?? '' };\n } catch (err) {\n // A row refused before its body is read would otherwise hold its connection open until the server gives up\n // on it; destroying the body closes the socket. On a body that already failed it changes nothing.\n destroyBody(res.Body);\n // A body cut off part-way is a dropped connection, and transient; a refused row is not.\n throw errorKind(err) === 'IntegrityError' ? err : mapError(err, this.bucket);\n }\n });\n }\n\n async write(\n key: string,\n body: Uint8Array,\n expect: 'absent' | { version: string },\n ): Promise<void> {\n await this.settle();\n this.refuseUnfencedWrites(expect);\n try {\n // Sent once: a replay of a write that landed would fail its own precondition, and read as a lost race.\n await sendOnce(\n this.client,\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ContentType: 'application/json',\n IfNoneMatch: expect === 'absent' ? '*' : undefined,\n IfMatch: expect === 'absent' ? undefined : expect.version,\n }),\n );\n } catch (err) {\n // A lost conditional-write race (412 precondition, or 409 concurrent-conflict) is an OCC conflict.\n if (isConditionalConflict(err)) {\n throw new WriteConflictError(`registry OCC conflict for ${key}`);\n }\n throw mapError(err, this.bucket);\n }\n }\n\n async delete(key: string, expect: { version: string }): Promise<void> {\n try {\n // Sent once, as the writes are: a replay that met its own landed delete would read as a lost race.\n await sendOnce(\n this.client,\n new DeleteObjectCommand({ Bucket: this.bucket, Key: key, IfMatch: expect.version }),\n );\n } catch (err) {\n // A 412 (the object moved on), a 409 (a concurrent conditional request) or a 404 (it is gone) all mean the\n // version this delete was conditioned on is not there to delete.\n if (isConditionalConflict(err) || isNotFound(err)) {\n throw new WriteConflictError(`registry OCC conflict deleting ${key}`);\n }\n throw mapError(err, this.bucket);\n }\n }\n\n async *listKeys(prefix: string): AsyncIterable<string> {\n await this.settle();\n let token: string | undefined;\n do {\n let res;\n try {\n res = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n ContinuationToken: token,\n }),\n );\n } catch (err) {\n throw mapError(err, this.bucket);\n }\n for (const obj of res.Contents ?? []) {\n if (obj.Key !== undefined) yield obj.Key;\n }\n // A page that says more follow and gives no way to ask for them would end the listing short, silently.\n if (res.IsTruncated === true && res.NextContinuationToken === undefined) {\n throw new IntegrityError('S3 listing says it is truncated but gives no continuation token');\n }\n // A page that hands back the token it was asked with would be asked for again, forever.\n if (res.IsTruncated === true && res.NextContinuationToken === token) {\n throw new IntegrityError('S3 listing returned the continuation token it was given');\n }\n token = res.IsTruncated === true ? res.NextContinuationToken : undefined;\n } while (token !== undefined);\n }\n}\n\n/**\n * Reclassify a transient S3 fault as a retryable {@link TransientError} and a missing bucket as a `ValidationError`;\n * any other error is passed through.\n */\nfunction mapError(err: unknown, bucket: string): unknown {\n return toTransient(\n asMissingBucket(scrubCredentials(err), bucket),\n isTransient,\n transientFaultMessage,\n );\n}\n\nexport class S3RegistryDriver extends ObjectStoreRegistry {\n constructor(options: S3RegistryDriverOptions) {\n super(\n new S3RegistryStore(\n options.client,\n options.bucket,\n resolveReadTimeoutMs(options.readTimeoutMs),\n requireBool('conditionalDelete', options.conditionalDelete),\n ),\n normalizeObjectPrefix(options.prefix),\n options.now ?? ((): number => Date.now()),\n );\n }\n}\n", "/**\n * The socket-pool advisory: once the first read has run, the SDK's own request handler has made its agents and its\n * public `httpHandlerConfigs()` reports their `maxSockets`. A pool smaller than twice the default window of a combine\n * (32 ranges held ahead per operand, two operands) makes reads queue for a socket, so one `advisory` event goes to the\n * store's metrics sink, once. Nothing is logged or printed.\n *\n * A handler this cannot read (a custom one, HTTP/2, Fetch, an agent class that hides `maxSockets`) gives no event and\n * no error. The window it compares against is the default, because a combine's `concurrency` is chosen per call.\n */\nimport type { S3Client } from '@aws-sdk/client-s3';\nimport type { IMetricsSink, MetricEvent } from '@cloudbitmaps/core/driver-kit';\n\n/** The default window of a combine, which the engine owns; a test holds the two equal. */\nexport const SOCKET_ADVISORY_CONCURRENCY = 32;\n\n/** A two-operand combine holds up to this many requests open at the default window. */\nconst THRESHOLD = 2 * SOCKET_ADVISORY_CONCURRENCY;\n\n/** What of the SDK's request handler the limit and the advisory need: its `handle` and the agents it exposes once it has run. */\nexport interface PooledHandler {\n handle(request: unknown, options?: unknown): Promise<unknown>;\n httpHandlerConfigs?: () => {\n httpAgent?: { maxSockets: number };\n httpsAgent?: { maxSockets: number };\n };\n}\n\nexport class SocketAdvisory {\n /** True from the first `attach` until the one check has started: the only thing a request pays for after the first. */\n private pending = false;\n /** Every sink ever attached, so a sink is never given the event twice. */\n private readonly sinks = new Set<IMetricsSink>();\n /** The check's result, once it has started; a sink attached after that is given the result as it settles. */\n private outcome: Promise<MetricEvent | undefined> | undefined;\n\n constructor(\n private readonly client: S3Client,\n private readonly bucket: string,\n ) {}\n\n /**\n * Hand a sink to the advisory. Each sink gets the event at most once: a sink attached before the check runs gets it\n * when the first read finishes, one attached later gets it as soon as the (single) check's result is known, and\n * one attached again is ignored.\n */\n attach(sink: IMetricsSink): void {\n if (this.sinks.has(sink)) return;\n this.sinks.add(sink);\n if (this.outcome === undefined) this.pending = true;\n else void this.outcome.then((event) => event && emit(sink, event));\n }\n\n /**\n * Called as a request finishes. The first call after a sink is attached reads the pool, never throws and never\n * rejects; every later one costs one boolean. Returns the pending check (for a test to await), or `undefined` once\n * it has run.\n */\n afterRequest(): Promise<void> | undefined {\n if (!this.pending) return undefined;\n this.pending = false;\n const first = [...this.sinks];\n const outcome = this.read();\n this.outcome = outcome;\n return outcome.then((event) => {\n if (event) for (const sink of first) emit(sink, event);\n });\n }\n\n private async read(): Promise<MetricEvent | undefined> {\n try {\n const maxSockets = await this.pool();\n if (maxSockets === undefined || maxSockets >= THRESHOLD) return undefined;\n return {\n kind: 'advisory',\n code: 'socket-pool-below-window',\n driver: 's3',\n bucket: this.bucket,\n maxSockets,\n threshold: THRESHOLD,\n concurrency: SOCKET_ADVISORY_CONCURRENCY,\n };\n } catch {\n // A handler of another shape: the advisory is best-effort and never fails a read.\n return undefined;\n }\n }\n\n /**\n * The pool the reads use, or `undefined` when it cannot be read. With an endpoint on the client, that is the\n * `maxSockets` of the agent for its scheme. Without one (the SDK's own endpoints, or an endpoint set by the\n * environment, which the client does not expose) the scheme is not known, so every agent the handler has made is\n * read and the pool counts as small only if all of them are: the larger one is returned. That never warns on a pool\n * the reads do not use, at the cost of a missed warning when only the unused agent is small.\n */\n private async pool(): Promise<number | undefined> {\n const handler = this.client.config.requestHandler as unknown as PooledHandler | undefined;\n if (typeof handler?.httpHandlerConfigs !== 'function') return undefined;\n const endpoint = await this.client.config.endpoint?.();\n const agents = handler.httpHandlerConfigs();\n const candidates =\n endpoint === undefined\n ? [agents?.httpsAgent, agents?.httpAgent]\n : [endpoint.protocol === 'http:' ? agents?.httpAgent : agents?.httpsAgent];\n let largest: number | undefined;\n for (const agent of candidates) {\n const n = agent?.maxSockets;\n if (typeof n !== 'number' || Number.isNaN(n)) continue;\n if (largest === undefined || n > largest) largest = n;\n }\n return largest;\n }\n}\n\n/** Give one sink the event; a sink that throws never reaches the read. */\nfunction emit(sink: IMetricsSink, event: MetricEvent): void {\n try {\n sink.onEvent(event);\n } catch {\n // Best-effort, like every metrics sink.\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["ValidationError", "normalizeObjectPrefix", "requireBool", "requirePositiveInt", "TransientError", "TransientError", "NotFoundError", "TransientError", "ValidationError", "DeleteObjectCommand", "PutObjectCommand", "ValidationError", "ValidationError", "label", "ValidationError", "bytes", "DeleteObjectCommand", "NotFoundError", "res", "TransientError", "PutObjectCommand", "errorKind", "IntegrityError", "ValidationError", "WriteConflictError", "normalizeObjectPrefix", "requireBool", "toTransient", "DeleteObjectCommand", "GetObjectCommand", "ListObjectsV2Command", "PutObjectCommand", "ValidationError", "GetObjectCommand", "IntegrityError", "errorKind", "PutObjectCommand", "WriteConflictError", "DeleteObjectCommand", "ListObjectsV2Command", "toTransient", "requireBool", "normalizeObjectPrefix", "ValidationError", "requirePositiveInt", "requireBool", "normalizeObjectPrefix"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbitmaps/s3",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "S3 and S3-compatible storage (R2, MinIO, Ceph, Wasabi, B2) for CloudBitmaps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudbitmaps",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@aws-sdk/client-s3": ">=3.700.0 <4",
|
|
57
|
-
"@cloudbitmaps/core": "0.
|
|
57
|
+
"@cloudbitmaps/core": "0.24.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^22.0.0",
|