@cloudbitmaps/s3 0.18.1 → 0.18.3

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.
@@ -0,0 +1,14 @@
1
+ /** The `GetObject` response body, narrowed to the parts used. */
2
+ export type ResponseBody = {
3
+ transformToByteArray?: () => Promise<Uint8Array>;
4
+ [Symbol.asyncIterator]?: () => AsyncIterator<unknown>;
5
+ destroy?: () => void;
6
+ } | undefined;
7
+ /** Destroy a response body left unread, which releases its connection; a body with no `destroy` is left alone. */
8
+ export declare function destroyBody(body: unknown): void;
9
+ /**
10
+ * The body's bytes, at most `maxBytes` of them. `oversize` builds the typed error thrown when the count passes
11
+ * `maxBytes`, or when the response advertises more than that before a byte is read; any other failure of the stream
12
+ * propagates as it is.
13
+ */
14
+ export declare function readBounded(body: ResponseBody, maxBytes: number, oversize: () => Error, advertised?: number): Promise<Uint8Array>;
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { S3Client } from "@aws-sdk/client-s3";
4
4
 
5
5
  // src/storage.ts
6
6
  import {
7
- NotFoundError,
7
+ NotFoundError as NotFoundError2,
8
8
  TransientError as TransientError2,
9
9
  ValidationError as ValidationError3,
10
10
  WriteConflictError,
@@ -178,6 +178,128 @@ function sendOnce(client, command) {
178
178
  return client.send(command, {});
179
179
  }
180
180
 
181
+ // src/bounded-body.ts
182
+ import { NotFoundError } from "@cloudbitmaps/core";
183
+ function destroyBody(body) {
184
+ body?.destroy?.();
185
+ }
186
+ async function readBounded(body, maxBytes, oversize, advertised) {
187
+ if (body === void 0) throw new NotFoundError("S3 GetObject returned an empty body");
188
+ if (advertised !== void 0 && advertised > maxBytes) {
189
+ destroyBody(body);
190
+ throw oversize();
191
+ }
192
+ if (typeof body[Symbol.asyncIterator] !== "function") {
193
+ const bytes = await body.transformToByteArray();
194
+ if (bytes.length > maxBytes) throw oversize();
195
+ return bytes;
196
+ }
197
+ const chunks = [];
198
+ let total = 0;
199
+ let done = false;
200
+ try {
201
+ for await (const chunk of body) {
202
+ const piece = typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk;
203
+ total += piece.length;
204
+ if (total > maxBytes) throw oversize();
205
+ chunks.push(piece);
206
+ }
207
+ done = true;
208
+ } finally {
209
+ if (!done) destroyBody(body);
210
+ }
211
+ const out = new Uint8Array(total);
212
+ let at = 0;
213
+ for (const piece of chunks) {
214
+ out.set(piece, at);
215
+ at += piece.length;
216
+ }
217
+ return out;
218
+ }
219
+
220
+ // src/scrub-error.ts
221
+ var TRANSPORT = /* @__PURE__ */ new Set([
222
+ "$response",
223
+ "socket",
224
+ "_httpMessage",
225
+ "req",
226
+ "request",
227
+ "connection",
228
+ "agent"
229
+ ]);
230
+ var CREDENTIAL_NAME = /^(?:proxy-)?authorization$|^cookie$|^set-cookie$|^x-api-key$|^x-amz-security-token$|^x-amz-credential$|^x-amz-signature$/i;
231
+ var CREDENTIAL_LINE = new RegExp(
232
+ "^((?:proxy-)?authorization|cookie|set-cookie|x-api-key|x-amz-security-token):[^\\r\\n]*",
233
+ "im"
234
+ );
235
+ var CREDENTIAL_LINES = new RegExp(CREDENTIAL_LINE.source, "gim");
236
+ var REDACTED = "[redacted]";
237
+ var MAX_DEPTH = 6;
238
+ var isPlain = (value) => {
239
+ const proto = Object.getPrototypeOf(value);
240
+ return proto === Object.prototype || proto === null || Array.isArray(value);
241
+ };
242
+ function redact(node, seen, depth) {
243
+ if (seen.has(node) || depth > MAX_DEPTH) return;
244
+ seen.add(node);
245
+ if (Array.isArray(node)) {
246
+ for (let i = 0; i + 1 < node.length; i++) {
247
+ const item = node[i];
248
+ if (typeof item === "string" && CREDENTIAL_NAME.test(item)) node[i + 1] = REDACTED;
249
+ }
250
+ }
251
+ for (const key of Reflect.ownKeys(node)) {
252
+ try {
253
+ const record = node;
254
+ const value = record[key];
255
+ if (typeof key === "string" && CREDENTIAL_NAME.test(key) && value != null) {
256
+ record[key] = REDACTED;
257
+ } else if (typeof value === "string" && CREDENTIAL_LINE.test(value)) {
258
+ record[key] = value.replace(CREDENTIAL_LINES, `$1: ${REDACTED}`);
259
+ } else if (typeof value === "object" && value !== null && isPlain(value)) {
260
+ redact(value, seen, depth + 1);
261
+ }
262
+ } catch {
263
+ }
264
+ }
265
+ }
266
+ function copyOf(err, depth) {
267
+ const copy = Object.create(Object.getPrototypeOf(err));
268
+ for (const key of Reflect.ownKeys(err)) {
269
+ if (typeof key === "string" && TRANSPORT.has(key)) continue;
270
+ const descriptor = Object.getOwnPropertyDescriptor(err, key);
271
+ if (descriptor === void 0) continue;
272
+ if (key === "cause" && "value" in descriptor) {
273
+ const cause = descriptor.value;
274
+ if (typeof cause === "object" && cause !== null && depth < MAX_DEPTH) {
275
+ descriptor.value = copyOf(cause, depth + 1);
276
+ }
277
+ }
278
+ if (key === "stack") {
279
+ Object.defineProperty(copy, key, {
280
+ value: err.stack,
281
+ writable: true,
282
+ configurable: true,
283
+ enumerable: false
284
+ });
285
+ continue;
286
+ }
287
+ Object.defineProperty(copy, key, descriptor);
288
+ }
289
+ return copy;
290
+ }
291
+ function holdsTransport(err, depth) {
292
+ if (Object.getOwnPropertyNames(err).some((key) => TRANSPORT.has(key))) return true;
293
+ const cause = err.cause;
294
+ return typeof cause === "object" && cause !== null && depth < MAX_DEPTH ? holdsTransport(cause, depth + 1) : false;
295
+ }
296
+ function scrubCredentials(err) {
297
+ if (typeof err !== "object" || err === null) return err;
298
+ const safe = holdsTransport(err, 0) ? copyOf(err, 0) : err;
299
+ redact(safe, /* @__PURE__ */ new WeakSet(), 0);
300
+ return safe;
301
+ }
302
+
181
303
  // src/storage.ts
182
304
  var S3_PART_BYTES = 8 * 1024 * 1024;
183
305
  var S3_MAX_PARTS = 1e4;
@@ -260,7 +382,12 @@ var S3StorageDriver = class {
260
382
  }),
261
383
  options
262
384
  );
263
- const bytes = await collect(res.Body);
385
+ const bytes = await readBounded(
386
+ res.Body,
387
+ length,
388
+ () => this.badRead(key, "range", `the response is longer than the ${length}B requested`),
389
+ res.ContentLength
390
+ );
264
391
  if (bytes.length !== length) {
265
392
  throw new ValidationError3(
266
393
  `range [${offset}, ${offset + length}) out of bounds (got ${bytes.length}B)`
@@ -284,7 +411,13 @@ var S3StorageDriver = class {
284
411
  }),
285
412
  options
286
413
  );
287
- return { bytes: await collect(res.Body), contentRange: res.ContentRange };
414
+ const bytes2 = await readBounded(
415
+ res.Body,
416
+ maxBytes,
417
+ () => this.badRead(key, "tail", `the response is longer than the ${maxBytes}B requested`),
418
+ res.ContentLength
419
+ );
420
+ return { bytes: bytes2, contentRange: res.ContentRange };
288
421
  } catch (err) {
289
422
  if (isInvalidRange(err)) return { refused: err };
290
423
  throw err;
@@ -301,6 +434,11 @@ var S3StorageDriver = class {
301
434
  }
302
435
  return { bytes, size };
303
436
  }
437
+ badRead(key, what, why) {
438
+ return new ValidationError3(
439
+ `S3 ${what} read of ${key.segment}.${key.generation} refused: ${why}`
440
+ );
441
+ }
304
442
  /** The object's size from a `HeadObject`, or `undefined` when the response does not carry one. */
305
443
  headSize(key, objectKey) {
306
444
  return this.read("HeadObject", key, async (options) => {
@@ -363,9 +501,10 @@ var S3StorageDriver = class {
363
501
  }
364
502
  /** Map S3 read errors to the driver vocabulary; pass everything else through {@link mapError}. */
365
503
  mapReadError(err, key) {
504
+ err = scrubCredentials(err);
366
505
  if (isValidationError(err)) return err;
367
506
  if (isNotFound(err)) {
368
- return new NotFoundError(`no such generation: ${key.segment}.${key.generation}`);
507
+ return new NotFoundError2(`no such generation: ${key.segment}.${key.generation}`);
369
508
  }
370
509
  if (isInvalidRange(err)) {
371
510
  return new ValidationError3(`range out of bounds for ${key.segment}.${key.generation}`);
@@ -379,6 +518,7 @@ var S3StorageDriver = class {
379
518
  * read retry only ever see typed errors.
380
519
  */
381
520
  mapError(err) {
521
+ err = scrubCredentials(err);
382
522
  if (isTransient(err)) {
383
523
  return new TransientError2(
384
524
  `transient S3 fault: ${err?.name ?? "unknown"}`,
@@ -528,7 +668,8 @@ var S3MultipartSink = class {
528
668
  try {
529
669
  await send();
530
670
  return;
531
- } catch (err) {
671
+ } catch (raw) {
672
+ const err = scrubCredentials(raw);
532
673
  if (resent > 0 && (isConditionalConflict(err) || isNoSuchUpload(err))) {
533
674
  const stored = await this.storedObject();
534
675
  if (stored?.id === this.writeId) return;
@@ -585,12 +726,6 @@ var S3MultipartSink = class {
585
726
  }
586
727
  }
587
728
  };
588
- async function collect(body) {
589
- if (body === void 0) {
590
- throw new NotFoundError("S3 GetObject returned an empty body");
591
- }
592
- return body.transformToByteArray();
593
- }
594
729
 
595
730
  // src/registry.ts
596
731
  import {
@@ -769,7 +904,11 @@ var S3RegistryStore = class {
769
904
  if (res.Body === void 0) {
770
905
  throw new IntegrityError(`registry object has an empty body: ${key}`);
771
906
  }
772
- const bytes = await res.Body.transformToByteArray();
907
+ const bytes = await readBounded(
908
+ res.Body,
909
+ MAX_ROW_BYTES,
910
+ () => new IntegrityError(`registry object exceeds cap ${MAX_ROW_BYTES}B`)
911
+ );
773
912
  return { bytes, version: res.ETag ?? "" };
774
913
  } catch (err) {
775
914
  destroyBody(res.Body);
@@ -835,10 +974,8 @@ var S3RegistryStore = class {
835
974
  } while (token !== void 0);
836
975
  }
837
976
  };
838
- function destroyBody(body) {
839
- body?.destroy?.();
840
- }
841
977
  function mapError(err) {
978
+ err = scrubCredentials(err);
842
979
  if (isTransient(err)) {
843
980
  return new TransientError3(
844
981
  `transient S3 fault: ${err?.name ?? "unknown"}`,
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/backend.ts", "../src/storage.ts", "../src/keys.ts", "../src/aws-errors.ts", "../src/s3-errors.ts", "../src/read-timeout.ts", "../src/send-once.ts", "../src/registry.ts", "../src/client-probe.ts", "../src/socket-advisory.ts"],
4
- "sourcesContent": ["/**\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` and\n * `.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.\n */\nimport { STORAGE_BACKEND, ValidationError, brandAsBackend } from '@cloudbitmaps/core/driver-kit';\nimport type {\n IRegistryDriver,\n IStorageDriver,\n StorageBackend,\n} from '@cloudbitmaps/core/driver-kit';\nimport { S3Client } from '@aws-sdk/client-s3';\nimport { S3StorageDriver } from './storage';\nimport { S3RegistryDriver } from './registry';\nimport { describe } from './read-timeout';\nimport { SocketAdvisory, type PooledHandler } from './socket-advisory';\nimport type { IMetricsSink } from '@cloudbitmaps/core/driver-kit';\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. */\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 /** Region for the client built when `client` is absent (refused beside `client`). Falls back to the SDK's own resolution. */\n readonly region?: string;\n /** Endpoint for an S3-compatible store (MinIO, Ceph, R2). Refused beside `client`, which carries its own. */\n readonly endpoint?: string;\n /** Path-style addressing, which most S3-compatible stores require. Refused beside `client`, which carries its own. */\n readonly pathStyle?: boolean;\n /**\n * Static credentials, for the S3-compatible stores that issue them (MinIO, Ceph, R2).\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. A deployment\n * that runs `eraseSubject`'s 256 reads at once needs `256`, or a lower `concurrency`.\n */\n readonly maxSockets?: number;\n /**\n * Largest object the backend will write and advertise. Default = `partBytes \u00D7 10,000` (\u2248 80 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 * 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 registry removes a deleted row for good, by a `DeleteObject` sent with `If-Match`, rather than leaving\n * a tombstone a full listing reads forever. Defaults to `true` when the host the client resolves is an AWS S3 host,\n * whichever way its endpoint was set (`endpoint`, `AWS_ENDPOINT_URL_S3`, `AWS_ENDPOINT_URL`, the shared config file),\n * and to `false` for any other host: set it for an S3-compatible store only once you know the store applies `If-Match`\n * on a delete. MinIO, for one, ignores it. It is 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/** Refuse an options bag that is not an object, or that holds a key not in `keys`, naming each such key. */\nfunction refuseUnknown(\n name: string,\n options: unknown,\n keys: readonly string[],\n hint: string,\n): void {\n if (options === null || typeof options !== 'object') {\n throw new ValidationError(\n `${name} needs an options object \u2014 got ${options === null ? 'null' : typeof options}`,\n );\n }\n const unknown = Object.keys(options).filter((k) => !keys.includes(k));\n if (unknown.length > 0) {\n const list = (ks: readonly string[]): string => ks.map((k) => `\\`${k}\\``).join(', ');\n throw new ValidationError(\n `${name} does not take ${list(unknown)}. It takes ${list(keys)}; ${hint}.`,\n );\n }\n}\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\n constructor(options: S3StorageOptions) {\n refuseUnknown('S3Storage', options, S3_STORAGE_OPTION_KEYS, 'an S3 client goes in `client`');\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 this.client = options.client;\n } else {\n const { maxSockets = DEFAULT_MAX_SOCKETS } = options;\n if (!Number.isSafeInteger(maxSockets) || maxSockets < 1) {\n throw new ValidationError(\n `maxSockets must be a positive safe integer; got ${describe(options.maxSockets)}`,\n );\n }\n this.client = new S3Client({\n ...(options.region === undefined ? {} : { region: options.region }),\n ...(options.endpoint === undefined ? {} : { endpoint: options.endpoint }),\n ...(options.pathStyle === undefined ? {} : { forcePathStyle: options.pathStyle }),\n ...(options.credentials === undefined ? {} : { credentials: options.credentials }),\n });\n limitSockets(this.client, maxSockets);\n }\n this.sockets = new SocketAdvisory(this.client, options.bucket);\n const shared = {\n client: this.client,\n bucket: options.bucket,\n ...(options.prefix === undefined ? {} : { prefix: options.prefix }),\n ...(options.readTimeoutMs === undefined ? {} : { readTimeoutMs: options.readTimeoutMs }),\n };\n this.storage = new S3StorageDriver({\n ...shared,\n sockets: this.sockets,\n ...(options.maxObjectBytes === undefined ? {} : { maxObjectBytes: options.maxObjectBytes }),\n ...(options.partBytes === undefined ? {} : { partBytes: options.partBytes }),\n });\n this.registry = new S3RegistryDriver({\n ...shared,\n ...(options.now === undefined ? {} : { now: options.now }),\n ...(options.conditionalDelete === undefined\n ? {}\n : { 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 * `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 `503 SlowDown` 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 */\nimport {\n NotFoundError,\n TransientError,\n ValidationError,\n WriteConflictError,\n isNotFoundError,\n isValidationError,\n isWriteConflictError,\n} from '@cloudbitmaps/core/driver-kit';\nimport type {\n BlobSink,\n GenKey,\n IStorageDriver,\n SegmentRef,\n StorageCaps,\n} from '@cloudbitmaps/core/driver-kit';\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 storageObjectKey,\n normalizeS3Prefix,\n parseGenerationFromKey,\n segmentObjectPrefix,\n} from './keys';\nimport {\n isConditionalConflict,\n isInvalidRange,\n isNoSuchUpload,\n isNotFound,\n isPreconditionFailed,\n isThrottle,\n isTransient,\n totalFromContentRange,\n} from './s3-errors';\nimport { resolveReadTimeoutMs, timedRead, type ReadSendOptions } from './read-timeout';\nimport type { SocketAdvisory } from './socket-advisory';\nimport { sendOnce } from './send-once';\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/** S3 hard limit: a multipart upload has at most 10,000 parts. This \u00D7 the part size is the real object ceiling. */\nconst S3_MAX_PARTS = 10_000;\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. */\ninterface Sleeper {\n sleep(ms: number): Promise<void>;\n}\n\nconst REAL_TIME: Sleeper = {\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\nexport interface 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 80 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}\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 sockets: SocketAdvisory | undefined;\n\n constructor(options: S3StorageDriverOptions) {\n this.client = options.client;\n this.sockets = options.sockets;\n this.bucket = options.bucket;\n this.prefix = normalizeS3Prefix(options.prefix);\n this.clock = options.clock ?? REAL_TIME;\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 for (const [name, value] of [\n ['partBytes', options.partBytes],\n ['maxObjectBytes', options.maxObjectBytes],\n ] as const) {\n if (value !== undefined && (!Number.isSafeInteger(value) || value < 1)) {\n throw new ValidationError(`${name} must be a positive safe integer; got ${value}`);\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 { rangeRead: true, maxObjectBytes: this.maxObjectBytes, conditionalPut: true };\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 );\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 (isValidationError(err) || isWriteConflictError(err) || isNotFoundError(err)) {\n throw err;\n }\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 collect(res.Body);\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 return bytes;\n });\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n const objectKey = storageObjectKey(this.prefix, key);\n if (maxBytes <= 0) {\n // No tail bytes wanted \u2014 just resolve the size via a HEAD.\n return { bytes: new Uint8Array(0), size: (await this.headSize(key, objectKey)) ?? 0 };\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 return { bytes: await collect(res.Body), contentRange: res.ContentRange };\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 if ((await this.headSize(key, objectKey)) !== 0) throw this.mapReadError(read.refused, key);\n return { bytes: new Uint8Array(0), size: 0 };\n }\n const { bytes, contentRange } = read;\n let size = totalFromContentRange(contentRange);\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.headSize(key, objectKey)) ?? bytes.length)\n : bytes.length;\n }\n return { bytes, size };\n }\n\n /** The object's size from a `HeadObject`, or `undefined` when the response does not carry one. */\n private headSize(key: GenKey, objectKey: string): Promise<number | 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 head.ContentLength;\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): Promise<void> {\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({ Bucket: this.bucket, Key: storageObjectKey(this.prefix, key) }),\n );\n } catch (err) {\n throw this.mapError(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 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 if (isValidationError(err)) 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},\n * so the store's read retry can ride it out and a write's caller can tell it from a deterministic failure;\n * everything else propagates unchanged. The final fallback at every `client.send` site, so callers and the\n * read retry only ever see typed errors.\n */\n private mapError(err: unknown): unknown {\n if (isTransient(err)) {\n return new TransientError(\n `transient S3 fault: ${(err as { name?: string } | null)?.name ?? 'unknown'}`,\n { cause: err },\n );\n }\n return err;\n }\n}\n\n/** Concatenate a list of byte chunks of known total length into one buffer. */\nfunction concatBytes(parts: readonly Uint8Array[], total: number): Uint8Array {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const p of parts) {\n out.set(p, offset);\n offset += p.length;\n }\n return out;\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 ) {}\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 this.pending.push(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, this.pendingLen);\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, this.pendingLen);\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 (err) {\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(Math.random() * 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/**\n * Collect an S3 response body into a `Uint8Array`. `transformToByteArray` is added at runtime to the SDK's\n * Node stream by `@aws-sdk`'s sdk-stream-mixin, so the structural cast is sound on Node.\n */\nasync function collect(body: GetObjectCommandBody): Promise<Uint8Array> {\n if (body === undefined) {\n throw new NotFoundError('S3 GetObject returned an empty body');\n }\n return body.transformToByteArray();\n}\n\n/** The S3 `GetObject` Body type, narrowed to the part we use (`transformToByteArray`). */\ntype GetObjectCommandBody = { transformToByteArray(): Promise<Uint8Array> } | undefined;\n", "/**\n * Logical-ref \u2192 S3 object-key mapping for {@link S3StorageDriver}.\n *\n * Pure string logic with no SDK dependency, so it's unit-testable without S3/MinIO. Mirrors the LocalFs\n * layout (`<namespace>/segments/<segment>.<gen>.crbm`) under an optional caller prefix, and re-validates\n * names at the boundary \u2014 defense in depth, because a driver can be constructed and driven directly rather\n * than through the engine that would otherwise have validated for it. The default\n * (absent) namespace maps to `_default`, which cannot collide with a real namespace because a caller's\n * `_default` encodes to `%5Fdefault` while the sentinel is emitted literally.\n */\n// `prefixPart` is imported, never redefined: the storage and registry layouts sit under the SAME caller\n// prefix, so they must normalize it identically \u2014 a second copy of that three-line function is how the two\n// halves of one bucket drift apart.\nimport {\n ValidationError,\n encodeNameForKey,\n namespaceKeyPart,\n prefixPart,\n validateSegmentRef,\n} from '@cloudbitmaps/core/driver-kit';\nimport type { GenKey, SegmentRef } from '@cloudbitmaps/core/driver-kit';\n\nconst SUFFIX = '.crbm';\n\n/** Validate a caller-supplied key prefix. The rule is shared with every other object store. */\nexport { normalizeObjectPrefix as normalizeS3Prefix } from '@cloudbitmaps/core/driver-kit';\n\n/**\n * The S3 key prefix shared by all of a segment's generations: `<prefix><ns>/segments/<segment>.`. Used\n * both as the `ListObjectsV2` prefix and as the string stripped by {@link parseGenerationFromKey}.\n */\nexport function segmentObjectPrefix(prefix: string | undefined, ref: SegmentRef): string {\n validateSegmentRef(ref);\n return `${prefixPart(prefix)}${namespaceKeyPart(ref.namespace)}/segments/${encodeNameForKey(ref.segment)}.`;\n}\n\n/** The full S3 key of one `.crbm` generation: `<segmentPrefix><gen>.crbm`. */\nexport function storageObjectKey(prefix: string | undefined, key: GenKey): string {\n if (!Number.isInteger(key.generation) || key.generation < 0) {\n throw new ValidationError(`generation must be a non-negative integer; got ${key.generation}`);\n }\n return `${segmentObjectPrefix(prefix, key)}${key.generation}${SUFFIX}`;\n}\n\n/**\n * Parse a generation number out of a full object key, given its segment prefix, or `null` if it doesn't\n * match. Canonical decimal only \u2014 no leading zeros (so `\u2026s.07.crbm` can't alias `\u2026s.7.crbm`) and within\n * safe-integer range. This also rejects a *different* segment whose name merely shares the prefix (e.g. a\n * key for segment `s.x` won't parse under segment `s`'s prefix, since its middle isn't all digits).\n */\nexport function parseGenerationFromKey(segmentPrefix: string, objectKey: string): number | null {\n if (!objectKey.startsWith(segmentPrefix) || !objectKey.endsWith(SUFFIX)) return null;\n const middle = objectKey.slice(segmentPrefix.length, objectKey.length - SUFFIX.length);\n if (!/^(0|[1-9]\\d*)$/.test(middle)) return null;\n const generation = Number(middle);\n return Number.isSafeInteger(generation) ? generation : null;\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 */\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]);\nconst NETWORK_CODES = new Set([\n 'ETIMEDOUT',\n 'ECONNRESET',\n 'ECONNREFUSED',\n 'EPIPE',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n 'ECONNABORTED',\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/** A dropped/timed-out connection \u2014 transient by nature; a retry on a fresh connection usually succeeds. */\nexport function isNetworkOrTimeout(err: unknown): boolean {\n if (NETWORK_NAMES.has(errorName(err) ?? '')) return true;\n if (NETWORK_CODES.has(errorCode(err) ?? '')) return true;\n return NETWORK_MESSAGE.test((err as { message?: string } | null)?.message ?? '');\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 {\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 * S3 asked the client to slow down: `503 SlowDown`, or any other `503` (`ServiceUnavailable`). The SDK raises it with\n * `name` set to the code and `$metadata.httpStatusCode` to 503, and no `$retryable` marker. S3 does not document that a\n * 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 return errorName(err) === 'SlowDown' || httpStatus(err) === 503;\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/** The object / generation does not exist (GetObject \u2192 `NoSuchKey`, HeadObject \u2192 `NotFound`; both 404). */\nexport function isNotFound(err: unknown): boolean {\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), 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 errorName(err) === 'SlowDown' ||\n isServerSide(err) ||\n isNetworkOrTimeout(err) ||\n isSdkRetryable(err) ||\n isClockSkewCorrected(err)\n );\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 * 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. */\nexport const 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. */\nexport function 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',\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 * `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 * `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 IntegrityError,\n MAX_ROW_BYTES,\n ObjectStoreRegistry,\n TransientError,\n ValidationError,\n WriteConflictError,\n normalizeObjectPrefix,\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 { isConditionalConflict, isNotFound, isTransient } from './s3-errors';\nimport { sendOnce } from './send-once';\nimport { isAwsS3Host, probeClient } from './client-probe';\nimport type { ClientFacts } from './client-probe';\n\nexport interface 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. Only a row born with an incarnation id is removed; a row a release before 0.12 wrote is\n * always tombstoned.\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 if (this.explicitConditionalDelete === false) return false;\n const facts = this.facts;\n if (this.explicitConditionalDelete === true) return facts?.sendsDeleteIfMatch !== false;\n return facts !== undefined && facts.sendsDeleteIfMatch && isAwsS3Host(facts.host);\n }\n\n /** {@link ObjectRegistryStore.resolveCapabilities}: the client's answer, which sends no request. */\n resolveCapabilities(): Promise<void> {\n return this.settle();\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);\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 (\n res.Body as { transformToByteArray(): Promise<Uint8Array> }\n ).transformToByteArray();\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 err instanceof IntegrityError ? err : mapError(err);\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);\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);\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);\n }\n for (const obj of res.Contents ?? []) {\n if (obj.Key !== undefined) yield obj.Key;\n }\n token = res.IsTruncated === true ? res.NextContinuationToken : undefined;\n } while (token !== undefined);\n }\n}\n\n/** Destroy a response body left unread, which releases its connection; a body with no `destroy` is left alone. */\nfunction destroyBody(body: unknown): void {\n (body as { destroy?: () => void } | undefined)?.destroy?.();\n}\n\n/** Reclassify a transient S3 fault as a retryable {@link TransientError}; pass everything else through. */\nfunction mapError(err: unknown): unknown {\n if (isTransient(err)) {\n return new TransientError(\n `transient S3 fault: ${(err as { name?: string } | null)?.name ?? 'unknown'}`,\n { cause: err },\n );\n }\n return err;\n}\n\n/** {@link S3RegistryDriverOptions.conditionalDelete}, checked: `undefined` leaves the decision to the client. */\nfunction checkConditionalDelete(value: unknown): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'boolean') {\n throw new ValidationError(`conditionalDelete must be a boolean; got ${String(value)}`);\n }\n return value;\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 checkConditionalDelete(options.conditionalDelete),\n ),\n normalizeObjectPrefix(options.prefix),\n options.now ?? ((): number => Date.now()),\n );\n }\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/** The domains AWS serves S3 from: the standard partition, and China's. */\nconst AWS_DOMAINS = ['.amazonaws.com', '.amazonaws.com.cn'];\n\n/**\n * Whether `hostname` is an AWS S3 host: under an AWS domain and naming S3 in one of its labels (`s3`, `s3-fips`,\n * `s3-accesspoint`, `s3express-\u2026`), so the FIPS, dual-stack, access-point and VPC interface forms all count, and another\n * AWS service's host does not. A host of any other domain is not, whatever it speaks: a store behind one has to be\n * vouched for by the caller.\n */\nexport function isAwsS3Host(hostname: string): boolean {\n const host = hostname.toLowerCase().replace(/\\.$/, '');\n if (!AWS_DOMAINS.some((domain) => host.endsWith(domain))) return false;\n return host\n .split('.')\n .some((label) => label === 's3' || label.startsWith('s3-') || label.startsWith('s3express-'));\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
- "names": ["ValidationError", "TransientError", "ValidationError", "ValidationError", "ValidationError", "TransientError", "res", "TransientError", "ValidationError", "WriteConflictError", "normalizeObjectPrefix", "DeleteObjectCommand", "GetObjectCommand", "ListObjectsV2Command", "PutObjectCommand", "DeleteObjectCommand", "PutObjectCommand", "ValidationError", "GetObjectCommand", "PutObjectCommand", "WriteConflictError", "DeleteObjectCommand", "ListObjectsV2Command", "TransientError", "normalizeObjectPrefix", "ValidationError"]
3
+ "sources": ["../src/backend.ts", "../src/storage.ts", "../src/keys.ts", "../src/aws-errors.ts", "../src/s3-errors.ts", "../src/read-timeout.ts", "../src/send-once.ts", "../src/bounded-body.ts", "../src/scrub-error.ts", "../src/registry.ts", "../src/client-probe.ts", "../src/socket-advisory.ts"],
4
+ "sourcesContent": ["/**\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` and\n * `.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.\n */\nimport { STORAGE_BACKEND, ValidationError, brandAsBackend } from '@cloudbitmaps/core/driver-kit';\nimport type {\n IRegistryDriver,\n IStorageDriver,\n StorageBackend,\n} from '@cloudbitmaps/core/driver-kit';\nimport { S3Client } from '@aws-sdk/client-s3';\nimport { S3StorageDriver } from './storage';\nimport { S3RegistryDriver } from './registry';\nimport { describe } from './read-timeout';\nimport { SocketAdvisory, type PooledHandler } from './socket-advisory';\nimport type { IMetricsSink } from '@cloudbitmaps/core/driver-kit';\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. */\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 /** Region for the client built when `client` is absent (refused beside `client`). Falls back to the SDK's own resolution. */\n readonly region?: string;\n /** Endpoint for an S3-compatible store (MinIO, Ceph, R2). Refused beside `client`, which carries its own. */\n readonly endpoint?: string;\n /** Path-style addressing, which most S3-compatible stores require. Refused beside `client`, which carries its own. */\n readonly pathStyle?: boolean;\n /**\n * Static credentials, for the S3-compatible stores that issue them (MinIO, Ceph, R2).\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. A deployment\n * that runs `eraseSubject`'s 256 reads at once needs `256`, or a lower `concurrency`.\n */\n readonly maxSockets?: number;\n /**\n * Largest object the backend will write and advertise. Default = `partBytes \u00D7 10,000` (\u2248 80 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 * 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 registry removes a deleted row for good, by a `DeleteObject` sent with `If-Match`, rather than leaving\n * a tombstone a full listing reads forever. Defaults to `true` when the host the client resolves is an AWS S3 host,\n * whichever way its endpoint was set (`endpoint`, `AWS_ENDPOINT_URL_S3`, `AWS_ENDPOINT_URL`, the shared config file),\n * and to `false` for any other host: set it for an S3-compatible store only once you know the store applies `If-Match`\n * on a delete. MinIO, for one, ignores it. It is 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/** Refuse an options bag that is not an object, or that holds a key not in `keys`, naming each such key. */\nfunction refuseUnknown(\n name: string,\n options: unknown,\n keys: readonly string[],\n hint: string,\n): void {\n if (options === null || typeof options !== 'object') {\n throw new ValidationError(\n `${name} needs an options object \u2014 got ${options === null ? 'null' : typeof options}`,\n );\n }\n const unknown = Object.keys(options).filter((k) => !keys.includes(k));\n if (unknown.length > 0) {\n const list = (ks: readonly string[]): string => ks.map((k) => `\\`${k}\\``).join(', ');\n throw new ValidationError(\n `${name} does not take ${list(unknown)}. It takes ${list(keys)}; ${hint}.`,\n );\n }\n}\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\n constructor(options: S3StorageOptions) {\n refuseUnknown('S3Storage', options, S3_STORAGE_OPTION_KEYS, 'an S3 client goes in `client`');\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 this.client = options.client;\n } else {\n const { maxSockets = DEFAULT_MAX_SOCKETS } = options;\n if (!Number.isSafeInteger(maxSockets) || maxSockets < 1) {\n throw new ValidationError(\n `maxSockets must be a positive safe integer; got ${describe(options.maxSockets)}`,\n );\n }\n this.client = new S3Client({\n ...(options.region === undefined ? {} : { region: options.region }),\n ...(options.endpoint === undefined ? {} : { endpoint: options.endpoint }),\n ...(options.pathStyle === undefined ? {} : { forcePathStyle: options.pathStyle }),\n ...(options.credentials === undefined ? {} : { credentials: options.credentials }),\n });\n limitSockets(this.client, maxSockets);\n }\n this.sockets = new SocketAdvisory(this.client, options.bucket);\n const shared = {\n client: this.client,\n bucket: options.bucket,\n ...(options.prefix === undefined ? {} : { prefix: options.prefix }),\n ...(options.readTimeoutMs === undefined ? {} : { readTimeoutMs: options.readTimeoutMs }),\n };\n this.storage = new S3StorageDriver({\n ...shared,\n sockets: this.sockets,\n ...(options.maxObjectBytes === undefined ? {} : { maxObjectBytes: options.maxObjectBytes }),\n ...(options.partBytes === undefined ? {} : { partBytes: options.partBytes }),\n });\n this.registry = new S3RegistryDriver({\n ...shared,\n ...(options.now === undefined ? {} : { now: options.now }),\n ...(options.conditionalDelete === undefined\n ? {}\n : { 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 * `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 `503 SlowDown` 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 */\nimport {\n NotFoundError,\n TransientError,\n ValidationError,\n WriteConflictError,\n isNotFoundError,\n isValidationError,\n isWriteConflictError,\n} from '@cloudbitmaps/core/driver-kit';\nimport type {\n BlobSink,\n GenKey,\n IStorageDriver,\n SegmentRef,\n StorageCaps,\n} from '@cloudbitmaps/core/driver-kit';\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 storageObjectKey,\n normalizeS3Prefix,\n parseGenerationFromKey,\n segmentObjectPrefix,\n} from './keys';\nimport {\n isConditionalConflict,\n isInvalidRange,\n isNoSuchUpload,\n isNotFound,\n isPreconditionFailed,\n isThrottle,\n isTransient,\n totalFromContentRange,\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';\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/** S3 hard limit: a multipart upload has at most 10,000 parts. This \u00D7 the part size is the real object ceiling. */\nconst S3_MAX_PARTS = 10_000;\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. */\ninterface Sleeper {\n sleep(ms: number): Promise<void>;\n}\n\nconst REAL_TIME: Sleeper = {\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\nexport interface 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 80 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}\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 sockets: SocketAdvisory | undefined;\n\n constructor(options: S3StorageDriverOptions) {\n this.client = options.client;\n this.sockets = options.sockets;\n this.bucket = options.bucket;\n this.prefix = normalizeS3Prefix(options.prefix);\n this.clock = options.clock ?? REAL_TIME;\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 for (const [name, value] of [\n ['partBytes', options.partBytes],\n ['maxObjectBytes', options.maxObjectBytes],\n ] as const) {\n if (value !== undefined && (!Number.isSafeInteger(value) || value < 1)) {\n throw new ValidationError(`${name} must be a positive safe integer; got ${value}`);\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 { rangeRead: true, maxObjectBytes: this.maxObjectBytes, conditionalPut: true };\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 );\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 (isValidationError(err) || isWriteConflictError(err) || isNotFoundError(err)) {\n throw err;\n }\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 return bytes;\n });\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n const objectKey = storageObjectKey(this.prefix, key);\n if (maxBytes <= 0) {\n // No tail bytes wanted \u2014 just resolve the size via a HEAD.\n return { bytes: new Uint8Array(0), size: (await this.headSize(key, objectKey)) ?? 0 };\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 };\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 if ((await this.headSize(key, objectKey)) !== 0) throw this.mapReadError(read.refused, key);\n return { bytes: new Uint8Array(0), size: 0 };\n }\n const { bytes, contentRange } = read;\n let size = totalFromContentRange(contentRange);\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.headSize(key, objectKey)) ?? bytes.length)\n : bytes.length;\n }\n return { bytes, size };\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 /** The object's size from a `HeadObject`, or `undefined` when the response does not carry one. */\n private headSize(key: GenKey, objectKey: string): Promise<number | 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 head.ContentLength;\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): Promise<void> {\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({ Bucket: this.bucket, Key: storageObjectKey(this.prefix, key) }),\n );\n } catch (err) {\n throw this.mapError(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 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 (isValidationError(err)) 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},\n * so the store's read retry can ride it out and a write's caller can tell it from a deterministic failure;\n * everything else propagates unchanged. The final fallback at every `client.send` site, so callers and the\n * read retry only ever see typed errors.\n */\n private mapError(err: unknown): unknown {\n err = scrubCredentials(err);\n if (isTransient(err)) {\n return new TransientError(\n `transient S3 fault: ${(err as { name?: string } | null)?.name ?? 'unknown'}`,\n { cause: err },\n );\n }\n return err;\n }\n}\n\n/** Concatenate a list of byte chunks of known total length into one buffer. */\nfunction concatBytes(parts: readonly Uint8Array[], total: number): Uint8Array {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const p of parts) {\n out.set(p, offset);\n offset += p.length;\n }\n return out;\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 ) {}\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 this.pending.push(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, this.pendingLen);\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, this.pendingLen);\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(Math.random() * 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 * Logical-ref \u2192 S3 object-key mapping for {@link S3StorageDriver}.\n *\n * Pure string logic with no SDK dependency, so it's unit-testable without S3/MinIO. Mirrors the LocalFs\n * layout (`<namespace>/segments/<segment>.<gen>.crbm`) under an optional caller prefix, and re-validates\n * names at the boundary \u2014 defense in depth, because a driver can be constructed and driven directly rather\n * than through the engine that would otherwise have validated for it. The default\n * (absent) namespace maps to `_default`, which cannot collide with a real namespace because a caller's\n * `_default` encodes to `%5Fdefault` while the sentinel is emitted literally.\n */\n// `prefixPart` is imported, never redefined: the storage and registry layouts sit under the SAME caller\n// prefix, so they must normalize it identically \u2014 a second copy of that three-line function is how the two\n// halves of one bucket drift apart.\nimport {\n ValidationError,\n encodeNameForKey,\n namespaceKeyPart,\n prefixPart,\n validateSegmentRef,\n} from '@cloudbitmaps/core/driver-kit';\nimport type { GenKey, SegmentRef } from '@cloudbitmaps/core/driver-kit';\n\nconst SUFFIX = '.crbm';\n\n/** Validate a caller-supplied key prefix. The rule is shared with every other object store. */\nexport { normalizeObjectPrefix as normalizeS3Prefix } from '@cloudbitmaps/core/driver-kit';\n\n/**\n * The S3 key prefix shared by all of a segment's generations: `<prefix><ns>/segments/<segment>.`. Used\n * both as the `ListObjectsV2` prefix and as the string stripped by {@link parseGenerationFromKey}.\n */\nexport function segmentObjectPrefix(prefix: string | undefined, ref: SegmentRef): string {\n validateSegmentRef(ref);\n return `${prefixPart(prefix)}${namespaceKeyPart(ref.namespace)}/segments/${encodeNameForKey(ref.segment)}.`;\n}\n\n/** The full S3 key of one `.crbm` generation: `<segmentPrefix><gen>.crbm`. */\nexport function storageObjectKey(prefix: string | undefined, key: GenKey): string {\n if (!Number.isInteger(key.generation) || key.generation < 0) {\n throw new ValidationError(`generation must be a non-negative integer; got ${key.generation}`);\n }\n return `${segmentObjectPrefix(prefix, key)}${key.generation}${SUFFIX}`;\n}\n\n/**\n * Parse a generation number out of a full object key, given its segment prefix, or `null` if it doesn't\n * match. Canonical decimal only \u2014 no leading zeros (so `\u2026s.07.crbm` can't alias `\u2026s.7.crbm`) and within\n * safe-integer range. This also rejects a *different* segment whose name merely shares the prefix (e.g. a\n * key for segment `s.x` won't parse under segment `s`'s prefix, since its middle isn't all digits).\n */\nexport function parseGenerationFromKey(segmentPrefix: string, objectKey: string): number | null {\n if (!objectKey.startsWith(segmentPrefix) || !objectKey.endsWith(SUFFIX)) return null;\n const middle = objectKey.slice(segmentPrefix.length, objectKey.length - SUFFIX.length);\n if (!/^(0|[1-9]\\d*)$/.test(middle)) return null;\n const generation = Number(middle);\n return Number.isSafeInteger(generation) ? generation : null;\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 */\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]);\nconst NETWORK_CODES = new Set([\n 'ETIMEDOUT',\n 'ECONNRESET',\n 'ECONNREFUSED',\n 'EPIPE',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n 'ECONNABORTED',\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/** A dropped/timed-out connection \u2014 transient by nature; a retry on a fresh connection usually succeeds. */\nexport function isNetworkOrTimeout(err: unknown): boolean {\n if (NETWORK_NAMES.has(errorName(err) ?? '')) return true;\n if (NETWORK_CODES.has(errorCode(err) ?? '')) return true;\n return NETWORK_MESSAGE.test((err as { message?: string } | null)?.message ?? '');\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 {\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 * S3 asked the client to slow down: `503 SlowDown`, or any other `503` (`ServiceUnavailable`). The SDK raises it with\n * `name` set to the code and `$metadata.httpStatusCode` to 503, and no `$retryable` marker. S3 does not document that a\n * 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 return errorName(err) === 'SlowDown' || httpStatus(err) === 503;\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/** The object / generation does not exist (GetObject \u2192 `NoSuchKey`, HeadObject \u2192 `NotFound`; both 404). */\nexport function isNotFound(err: unknown): boolean {\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), 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 errorName(err) === 'SlowDown' ||\n isServerSide(err) ||\n isNetworkOrTimeout(err) ||\n isSdkRetryable(err) ||\n isClockSkewCorrected(err)\n );\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 * 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. */\nexport const 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. */\nexport function 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',\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 * `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';\n\n/** The `GetObject` response body, narrowed to the parts used. */\nexport type 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 * 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`. Those objects are live (a socket still closing reads them), so they are never changed: the error is\n * replaced by a copy that has none of them (an error with none is returned as it is). The copy keeps its prototype, name, message, stack, status, code, request\n * id and every other plain field, so classification by name, code or status works on it as on the original. A\n * credential header or header line left anywhere in the copy's plain data is redacted.\n */\n\n/** Properties that hold the raw transport: the response, and the request and socket reachable from it. */\nconst TRANSPORT = new Set([\n '$response',\n 'socket',\n '_httpMessage',\n 'req',\n 'request',\n 'connection',\n 'agent',\n]);\n/** Header and field names whose value is a credential. */\nconst CREDENTIAL_NAME =\n /^(?:proxy-)?authorization$|^cookie$|^set-cookie$|^x-api-key$|^x-amz-security-token$|^x-amz-credential$|^x-amz-signature$/i;\nconst CREDENTIAL_LINE = new RegExp(\n '^((?:proxy-)?authorization|cookie|set-cookie|x-api-key|x-amz-security-token):[^\\\\r\\\\n]*',\n 'im',\n);\nconst CREDENTIAL_LINES = new RegExp(CREDENTIAL_LINE.source, 'gim');\nconst REDACTED = '[redacted]';\n/** How far down a `cause` chain, and into plain data, to look. */\nconst MAX_DEPTH = 6;\n\nconst isPlain = (value: object): boolean => {\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null || Array.isArray(value);\n};\n\n/** Redact credential values in plain data, in place. Class instances (live objects) are left alone. */\nfunction redact(node: object, seen: WeakSet<object>, depth: number): void {\n if (seen.has(node) || depth > MAX_DEPTH) return;\n seen.add(node);\n if (Array.isArray(node)) {\n // A flat list of header names and values.\n for (let i = 0; i + 1 < node.length; i++) {\n const item: unknown = node[i];\n if (typeof item === 'string' && CREDENTIAL_NAME.test(item)) node[i + 1] = REDACTED;\n }\n }\n for (const key of Reflect.ownKeys(node)) {\n try {\n const record = node as Record<string | symbol, unknown>;\n const value = record[key];\n if (typeof key === 'string' && CREDENTIAL_NAME.test(key) && value != null) {\n record[key] = REDACTED;\n } else if (typeof value === 'string' && CREDENTIAL_LINE.test(value)) {\n record[key] = value.replace(CREDENTIAL_LINES, `$1: ${REDACTED}`);\n } else if (typeof value === 'object' && value !== null && isPlain(value)) {\n redact(value, seen, depth + 1);\n }\n } catch {\n // a getter that throws, or a property that is read-only\n }\n }\n}\n\n/** A copy of `err` without the transport properties, its `cause` chain copied the same way. */\nfunction copyOf(err: object, depth: number): object {\n const copy = Object.create(Object.getPrototypeOf(err) as object | null) as Record<\n string,\n unknown\n >;\n for (const key of Reflect.ownKeys(err)) {\n if (typeof key === 'string' && TRANSPORT.has(key)) continue;\n const descriptor = Object.getOwnPropertyDescriptor(err, key);\n if (descriptor === undefined) continue;\n if (key === 'cause' && 'value' in descriptor) {\n const cause: unknown = descriptor.value;\n if (typeof cause === 'object' && cause !== null && depth < MAX_DEPTH) {\n descriptor.value = copyOf(cause, depth + 1);\n }\n }\n if (key === 'stack') {\n // Read through the original: the stack may be an accessor bound to the error itself.\n Object.defineProperty(copy, key, {\n value: (err as { stack?: unknown }).stack,\n writable: true,\n configurable: true,\n enumerable: false,\n });\n continue;\n }\n Object.defineProperty(copy, key, descriptor);\n }\n return copy;\n}\n\n/** Whether `err`, or an error in its `cause` chain, holds a raw transport object. */\nfunction holdsTransport(err: object, depth: number): boolean {\n if (Object.getOwnPropertyNames(err).some((key) => TRANSPORT.has(key))) return true;\n const cause: unknown = (err as { cause?: unknown }).cause;\n return typeof cause === 'object' && cause !== null && depth < MAX_DEPTH\n ? holdsTransport(cause, depth + 1)\n : false;\n}\n\n/** `err` as it is safe to throw: a credential-free copy of an error object, any other value as it is. */\nexport function scrubCredentials<T>(err: T): T {\n if (typeof err !== 'object' || err === null) return err;\n // An error with no transport on it is the caller's or ours, and keeps its identity.\n const safe = holdsTransport(err, 0) ? copyOf(err, 0) : err;\n redact(safe, new WeakSet(), 0);\n return safe as T;\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 IntegrityError,\n MAX_ROW_BYTES,\n ObjectStoreRegistry,\n TransientError,\n ValidationError,\n WriteConflictError,\n normalizeObjectPrefix,\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 { isConditionalConflict, isNotFound, isTransient } from './s3-errors';\nimport { sendOnce } from './send-once';\nimport { destroyBody, readBounded } from './bounded-body';\nimport { scrubCredentials } from './scrub-error';\nimport { isAwsS3Host, probeClient } from './client-probe';\nimport type { ClientFacts } from './client-probe';\n\nexport interface 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. Only a row born with an incarnation id is removed; a row a release before 0.12 wrote is\n * always tombstoned.\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 if (this.explicitConditionalDelete === false) return false;\n const facts = this.facts;\n if (this.explicitConditionalDelete === true) return facts?.sendsDeleteIfMatch !== false;\n return facts !== undefined && facts.sendsDeleteIfMatch && isAwsS3Host(facts.host);\n }\n\n /** {@link ObjectRegistryStore.resolveCapabilities}: the client's answer, which sends no request. */\n resolveCapabilities(): Promise<void> {\n return this.settle();\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);\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 err instanceof IntegrityError ? err : mapError(err);\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);\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);\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);\n }\n for (const obj of res.Contents ?? []) {\n if (obj.Key !== undefined) yield obj.Key;\n }\n token = res.IsTruncated === true ? res.NextContinuationToken : undefined;\n } while (token !== undefined);\n }\n}\n\n/** Reclassify a transient S3 fault as a retryable {@link TransientError}; pass everything else through. */\nfunction mapError(err: unknown): unknown {\n err = scrubCredentials(err);\n if (isTransient(err)) {\n return new TransientError(\n `transient S3 fault: ${(err as { name?: string } | null)?.name ?? 'unknown'}`,\n { cause: err },\n );\n }\n return err;\n}\n\n/** {@link S3RegistryDriverOptions.conditionalDelete}, checked: `undefined` leaves the decision to the client. */\nfunction checkConditionalDelete(value: unknown): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'boolean') {\n throw new ValidationError(`conditionalDelete must be a boolean; got ${String(value)}`);\n }\n return value;\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 checkConditionalDelete(options.conditionalDelete),\n ),\n normalizeObjectPrefix(options.prefix),\n options.now ?? ((): number => Date.now()),\n );\n }\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/** The domains AWS serves S3 from: the standard partition, and China's. */\nconst AWS_DOMAINS = ['.amazonaws.com', '.amazonaws.com.cn'];\n\n/**\n * Whether `hostname` is an AWS S3 host: under an AWS domain and naming S3 in one of its labels (`s3`, `s3-fips`,\n * `s3-accesspoint`, `s3express-\u2026`), so the FIPS, dual-stack, access-point and VPC interface forms all count, and another\n * AWS service's host does not. A host of any other domain is not, whatever it speaks: a store behind one has to be\n * vouched for by the caller.\n */\nexport function isAwsS3Host(hostname: string): boolean {\n const host = hostname.toLowerCase().replace(/\\.$/, '');\n if (!AWS_DOMAINS.some((domain) => host.endsWith(domain))) return false;\n return host\n .split('.')\n .some((label) => label === 's3' || label.startsWith('s3-') || label.startsWith('s3express-'));\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
+ "names": ["ValidationError", "NotFoundError", "TransientError", "ValidationError", "ValidationError", "ValidationError", "bytes", "NotFoundError", "TransientError", "res", "TransientError", "ValidationError", "WriteConflictError", "normalizeObjectPrefix", "DeleteObjectCommand", "GetObjectCommand", "ListObjectsV2Command", "PutObjectCommand", "DeleteObjectCommand", "PutObjectCommand", "ValidationError", "GetObjectCommand", "PutObjectCommand", "WriteConflictError", "DeleteObjectCommand", "ListObjectsV2Command", "TransientError", "normalizeObjectPrefix", "ValidationError"]
7
7
  }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Remove credentials from an error the AWS SDK raised, before the driver throws it or keeps it as a `cause`.
3
+ *
4
+ * The SDK keeps the HTTP response it got on the error (`$response`), and through its socket the request it sent, with
5
+ * the signed `Authorization` header and `x-amz-security-token`, on properties `util.inspect` shows only with
6
+ * `showHidden`. Those objects are live (a socket still closing reads them), so they are never changed: the error is
7
+ * replaced by a copy that has none of them (an error with none is returned as it is). The copy keeps its prototype, name, message, stack, status, code, request
8
+ * id and every other plain field, so classification by name, code or status works on it as on the original. A
9
+ * credential header or header line left anywhere in the copy's plain data is redacted.
10
+ */
11
+ /** `err` as it is safe to throw: a credential-free copy of an error object, any other value as it is. */
12
+ export declare function scrubCredentials<T>(err: T): T;
package/dist/storage.d.ts CHANGED
@@ -59,6 +59,7 @@ export declare class S3StorageDriver implements IStorageDriver {
59
59
  bytes: Uint8Array;
60
60
  size: number;
61
61
  }>;
62
+ private badRead;
62
63
  /** The object's size from a `HeadObject`, or `undefined` when the response does not carry one. */
63
64
  private headSize;
64
65
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbitmaps/s3",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "description": "S3 and S3-compatible storage (R2, MinIO, Ceph, Wasabi, B2) for CloudBitmaps",
5
5
  "keywords": [
6
6
  "cloudbitmaps",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "@aws-sdk/client-s3": ">=3.700.0 <4",
56
- "@cloudbitmaps/core": "^0.18.1"
56
+ "@cloudbitmaps/core": "^0.18.3"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@types/node": "^22.0.0",