@cloudbitmaps/gcs 0.19.1 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,7 +43,7 @@ It builds its own client from Application Default Credentials. Any other key is
43
43
  | `projectId`, `apiEndpoint` | build a client for you, such as one for fake-gcs-server; refused beside `client` |
44
44
  | `simpleUploadThresholdBytes`, `maxObjectBytes` | the size up to which an object is one simple request (default 8 MiB) and the largest object (default 5 TiB, GCS's maximum) |
45
45
  | `readTimeoutMs` | cut off a read that has run this long, in milliseconds (see below); `0`, the default, sets no timeout |
46
- | `conditionalDelete` | whether the registry removes a deleted row with an object delete under `ifGenerationMatch`, rather than leaving a tombstone every full listing reads. Off by default, on the public endpoint too: whether real GCS applies `ifGenerationMatch` to a delete has not been verified by a run against the service, and fake-gcs-server ignores it, so CI cannot show it. Set `true` to remove rows for good. Needs delete permission on the registry prefix |
46
+ | `conditionalDelete` | whether the service applies `ifGenerationMatch` on an object delete: the registry then removes a deleted row rather than leaving a tombstone every full listing reads, and the storage half deletes an erasure's holder only while it is the object the erasure read, by its generation. Off by default, on the public endpoint too: whether real GCS applies `ifGenerationMatch` to a delete has not been verified by a run against the service, and fake-gcs-server ignores it, so CI cannot show it. Set `true` to remove rows for good, and to close, on GCS, the window in which an erasure's stalled delete takes a generation a load put under the number since: while it is off, that window stays open. Needs delete permission on the registry prefix |
47
47
 
48
48
  ## Before production
49
49
 
package/dist/backend.d.ts CHANGED
@@ -61,10 +61,13 @@ export interface GcsStorageOptions {
61
61
  */
62
62
  readonly readTimeoutMs?: number;
63
63
  /**
64
- * Whether the registry removes a deleted row for good, by an object delete sent with `ifGenerationMatch`, rather
65
- * than leaving a tombstone a full listing reads forever. Defaults to `false`, on the public endpoint too: whether real
66
- * GCS applies `ifGenerationMatch` to a delete has not been verified by a run against the service, and fake-gcs-server
67
- * accepts the precondition and ignores it, so CI cannot show it. Set `true` to remove rows for good.
64
+ * Whether the service applies `ifGenerationMatch` on an object delete, which both halves rely on when it is set: the
65
+ * registry removes a deleted row for good rather than leaving a tombstone a full listing reads forever, and the
66
+ * storage half removes an erasure's holder only while it is the object the erasure searched, by its generation.
67
+ * Defaults to `false`, on the public endpoint too: whether real GCS applies `ifGenerationMatch` to a delete has not
68
+ * been verified by a run against the service, and fake-gcs-server accepts the precondition and ignores it, so CI
69
+ * cannot show it. Set `true` once you know it does. Until then, the window in which an erasure's stalled delete
70
+ * takes a generation a load put under the number since stays open on GCS.
68
71
  */
69
72
  readonly conditionalDelete?: boolean;
70
73
  }
package/dist/index.js CHANGED
@@ -429,8 +429,15 @@ var GcsStorageDriver = class {
429
429
  readTimeoutMs;
430
430
  clock;
431
431
  presence;
432
+ conditionalDelete;
432
433
  constructor(options) {
433
434
  this.storage = options.storage;
435
+ if (options.conditionalDelete !== void 0 && typeof options.conditionalDelete !== "boolean") {
436
+ throw new ValidationError3(
437
+ `conditionalDelete must be a boolean; got ${String(options.conditionalDelete)}`
438
+ );
439
+ }
440
+ this.conditionalDelete = options.conditionalDelete ?? false;
434
441
  this.clock = options.clock ?? REAL_TIME;
435
442
  this.readStorage = options.readStorage ?? options.storage;
436
443
  this.bucket = options.bucket;
@@ -454,7 +461,12 @@ var GcsStorageDriver = class {
454
461
  this.presence = new BucketPresence(this.storage, this.bucket);
455
462
  }
456
463
  capabilities() {
457
- return { rangeRead: true, maxObjectBytes: this.maxObjectBytes, conditionalPut: true };
464
+ return {
465
+ rangeRead: true,
466
+ maxObjectBytes: this.maxObjectBytes,
467
+ conditionalPut: true,
468
+ conditionalDelete: this.conditionalDelete
469
+ };
458
470
  }
459
471
  file(name) {
460
472
  return this.storage.bucket(this.bucket).file(name);
@@ -556,8 +568,10 @@ var GcsStorageDriver = class {
556
568
  const objectName = storageObjectName(this.prefix, key);
557
569
  const deadline = this.deadline("tail read", key);
558
570
  try {
559
- if (maxBytes <= 0)
560
- return { bytes: new Uint8Array(0), size: await this.sizeOf(objectName, deadline) };
571
+ if (maxBytes <= 0) {
572
+ const meta = await this.metadataOf(objectName, deadline);
573
+ return withVersion({ bytes: new Uint8Array(0), size: meta.size }, meta.version);
574
+ }
561
575
  if (!Number.isSafeInteger(maxBytes)) {
562
576
  throw new ValidationError3(`invalid tail length ${maxBytes}`);
563
577
  }
@@ -575,25 +589,32 @@ var GcsStorageDriver = class {
575
589
  );
576
590
  } catch (err) {
577
591
  if (!isInvalidRange(err)) throw err;
578
- const size = await this.sizeOf(objectName, deadline);
579
- if (size !== 0) throw err;
580
- return { bytes: new Uint8Array(0), size };
592
+ const meta = await this.metadataOf(objectName, deadline);
593
+ if (meta.size !== 0) throw err;
594
+ return withVersion({ bytes: new Uint8Array(0), size: 0 }, meta.version);
581
595
  }
582
596
  if (res.bytes.length === 0) {
583
- const size = await this.sizeOf(objectName, deadline);
584
- if (size !== 0) throw this.badTail(key, `no bytes returned for an object of ${size}B`);
585
- return { bytes: res.bytes, size };
597
+ const meta = await this.metadataOf(objectName, deadline);
598
+ if (meta.size !== 0) {
599
+ throw this.badTail(key, `no bytes returned for an object of ${meta.size}B`);
600
+ }
601
+ return withVersion({ bytes: res.bytes, size: 0 }, meta.version);
586
602
  }
587
- return { bytes: res.bytes, size: this.tailSize(res, maxBytes, key) };
603
+ const size = this.tailSize(res, maxBytes, key);
604
+ return withVersion(
605
+ { bytes: res.bytes, size },
606
+ objectGeneration(singleHeader(res.headers, "x-goog-generation"))
607
+ );
588
608
  } catch (err) {
589
609
  throw await this.readFailure(err, key);
590
610
  }
591
611
  }
592
612
  /**
593
- * The object's size from its metadata, validated. Read on the download client and retried as a download is, within
594
- * the calling read's `deadline` ({@link withDeadline}), so nothing but the one request in flight runs past it.
613
+ * The object's size from its metadata, validated, and its generation when the metadata names a usable one. Read on the
614
+ * download client and retried as a download is, within the calling read's `deadline` ({@link withDeadline}), so
615
+ * nothing but the one request in flight runs past it.
595
616
  */
596
- async sizeOf(objectName, deadline) {
617
+ async metadataOf(objectName, deadline) {
597
618
  const [meta] = await retryDownload(
598
619
  () => withDeadline(() => this.downloadable(objectName).getMetadata(), deadline),
599
620
  deadline
@@ -602,7 +623,12 @@ var GcsStorageDriver = class {
602
623
  if (!Number.isSafeInteger(size) || size < 0) {
603
624
  throw new ValidationError3(`GCS returned an invalid object size: ${String(meta.size)}`);
604
625
  }
605
- return size;
626
+ return {
627
+ size,
628
+ version: objectGeneration(
629
+ meta.generation === void 0 ? void 0 : String(meta.generation)
630
+ )
631
+ };
606
632
  }
607
633
  /**
608
634
  * The object's total size from a tail response, refused unless every header agrees with the bytes received. A 206
@@ -635,14 +661,38 @@ var GcsStorageDriver = class {
635
661
  badTail(key, why) {
636
662
  return new ValidationError3(`GCS tail read of ${key.segment}.${key.generation} refused: ${why}`);
637
663
  }
638
- async delete(key) {
664
+ async delete(key, options) {
665
+ const objectName = storageObjectName(this.prefix, key);
666
+ const ifVersion = options?.ifVersion;
667
+ const conditional = ifVersion !== void 0 && this.conditionalDelete;
639
668
  try {
640
- await this.file(storageObjectName(this.prefix, key)).delete();
669
+ await this.file(objectName).delete(
670
+ ...conditional ? [{ ifGenerationMatch: generationOf(ifVersion, key) }] : []
671
+ );
641
672
  } catch (err) {
642
673
  if (isNotFound(err)) return this.presence.confirm();
674
+ if (conditional && isPreconditionFailed(err)) {
675
+ if (!await this.exists(objectName)) return;
676
+ throw new WriteConflictError(
677
+ `generation ${key.segment}.${key.generation} is another object than the version given; not deleted`
678
+ );
679
+ }
643
680
  throw this.mapError(err);
644
681
  }
645
682
  }
683
+ /** Whether an object is under the name, from its metadata. A read that fails in any other way throws. */
684
+ async exists(objectName) {
685
+ try {
686
+ await this.downloadable(objectName).getMetadata();
687
+ return true;
688
+ } catch (err) {
689
+ if (isNotFound(err)) {
690
+ await this.presence.confirm();
691
+ return false;
692
+ }
693
+ throw this.mapError(scrubCredentials(err));
694
+ }
695
+ }
646
696
  async *list(ref) {
647
697
  const prefix = segmentObjectPrefix(this.prefix, ref);
648
698
  let files;
@@ -700,6 +750,20 @@ var GcsStorageDriver = class {
700
750
  return err;
701
751
  }
702
752
  };
753
+ function withVersion(tail, version) {
754
+ return version === void 0 ? tail : { ...tail, version };
755
+ }
756
+ function objectGeneration(value) {
757
+ return value !== void 0 && /^[1-9]\d*$/.test(value) && Number.isSafeInteger(Number(value)) ? value : void 0;
758
+ }
759
+ function generationOf(version, key) {
760
+ if (objectGeneration(version) === void 0) {
761
+ throw new ValidationError3(
762
+ `ifVersion for ${key.segment}.${key.generation} is not a GCS object generation: ${version}`
763
+ );
764
+ }
765
+ return Number(version);
766
+ }
703
767
  function concatBytes(parts, total) {
704
768
  const out = new Uint8Array(total);
705
769
  let offset = 0;
@@ -1209,7 +1273,8 @@ var GcsStorage = class {
1209
1273
  this.storage = new GcsStorageDriver({
1210
1274
  ...shared,
1211
1275
  ...options.maxObjectBytes === void 0 ? {} : { maxObjectBytes: options.maxObjectBytes },
1212
- ...options.simpleUploadThresholdBytes === void 0 ? {} : { simpleUploadThresholdBytes: options.simpleUploadThresholdBytes }
1276
+ ...options.simpleUploadThresholdBytes === void 0 ? {} : { simpleUploadThresholdBytes: options.simpleUploadThresholdBytes },
1277
+ ...options.conditionalDelete === void 0 ? {} : { conditionalDelete: options.conditionalDelete }
1213
1278
  });
1214
1279
  this.registry = new GcsRegistryDriver({
1215
1280
  ...shared,
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/backend.ts", "../src/storage.ts", "../src/bucket-presence.ts", "../src/gcs-errors.ts", "../src/scrub-error.ts", "../src/keys.ts", "../src/read-timeout.ts", "../src/download-retry.ts", "../src/send-once.ts", "../src/read-once.ts", "../src/registry.ts", "../src/crc32c.ts", "../src/read-client.ts"],
4
- "sourcesContent": ["/**\n * `GcsStorage` \u2014 the Google Cloud Storage backend as one object: generations and pointer, one bucket, stated once.\n *\n * See {@link StorageBackend} for why the two halves are configured together rather than separately.\n *\n * **A note on the word.** `@google-cloud/storage` names its own client class `Storage`, so the driver option\n * that takes it is also called `storage` \u2014 which, since this library calls its durable tier storage too, reads\n * as the same word meaning two things. That is why this class builds the client itself: `new GcsStorage({ bucket })`\n * never makes you write `{ storage: storage }`. Supply `client` when you need your own.\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 { Storage as GcsClient } from '@google-cloud/storage';\nimport { GcsStorageDriver } from './storage';\nimport { GcsRegistryDriver } from './registry';\nimport { downloadClient } from './read-client';\n\nexport interface GcsStorageOptions {\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional object-name prefix under which everything lives \u2014 generations and the registry alike. */\n readonly prefix?: string;\n /**\n * A constructed `@google-cloud/storage` client. One is built from the ambient credentials when absent. Its retry\n * options apply to every request except the single-request conditional writes, which the driver sends without\n * that retry whatever they say (a registry row once, and a generation's object again only after a throttle, under\n * its own backoff), and the downloads. In `@google-cloud/storage` 7.x and 8.x a download the SDK retries after a 408,\n * 429, 500, 502, 503 or 504 can crash the process with `ERR_STREAM_UNABLE_TO_PIPE`, so downloads go through a twin of\n * this client, built from its own class with the same credentials object, endpoint and settings and the SDK's retries\n * off over any its class set, and the driver retries them itself. A client whose twin would use other credentials or\n * another endpoint (a subclass that builds from options of its own), whose class overrides `bucket` (a test double\n * built on `Storage`), or whose `bucket` is replaced on the instance (a test stub; stub `Storage.prototype` instead)\n * is refused at construction. A test double that is not a `Storage` client is used as it is when it has\n * `retryOptions: { autoRetry: false }`, and refused without it. What is set on the client after the backend was\n * built, other than an interceptor, is not carried to the twin.\n */\n readonly client?: GcsClient;\n /** Project id for the client built when `client` is absent (refused beside `client`). Falls back to the SDK's own resolution. */\n readonly projectId?: string;\n /** Endpoint override \u2014 point it at fake-gcs-server locally. Refused beside `client`, which carries its own. */\n readonly apiEndpoint?: string;\n /** Largest object the backend will write and advertise (default = GCS's 5 TiB max). Must be a positive safe integer. */\n readonly maxObjectBytes?: number;\n /** Bytes at/under which a single non-resumable upload is used instead of a resumable stream (default 8 MiB).\n * Must be a positive safe integer. */\n readonly simpleUploadThresholdBytes?: number;\n /** Injected clock for the registry's `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n /**\n * Cut off a read that has run this long, in milliseconds. `0`, the default, sets no timeout. A client's own `timeout`\n * does not bound a download on `@google-cloud/storage` 8.x (the SDK hands it to an HTTP client that has no such\n * option), so this is what does.\n *\n * It bounds each read as a whole \u2014 a generation's tail (with the metadata read it falls back on for an empty\n * object), a range of it, a registry row \u2014 with one deadline across every attempt the driver makes and the backoff\n * between them. The clock starts at the call into the driver, so a credential fetch and any wait for a socket count\n * (Node's agents set no socket limit unless your process sets one), and runs until the whole body has arrived. It\n * counts time the process spends busy too: Node runs a due timer before it reads a socket, so a synchronous stretch\n * longer than the timeout fails the reads in flight even when their responses have arrived. When it passes, the read\n * throws `TransientError` naming the read and the timeout and no further attempt starts, for the store's read retry\n * to run again: about 8.35 s in all at `2_000` with the default retry policy. Uploads, deletes, listings and the\n * conditional writes are not timed. The SDK cannot cancel a request whose response has not begun, so a read timed\n * out before its server answers leaves that connection open until the server answers or closes it: one per read, up\n * to four per call through the store's retry. A non-negative safe integer no larger than 2,147,483,647.\n */\n readonly readTimeoutMs?: number;\n /**\n * Whether the registry removes a deleted row for good, by an object delete sent with `ifGenerationMatch`, rather\n * than leaving a tombstone a full listing reads forever. Defaults to `false`, on the public endpoint too: whether real\n * GCS applies `ifGenerationMatch` to a delete has not been verified by a run against the service, and fake-gcs-server\n * accepts the precondition and ignores it, so CI cannot show it. Set `true` to remove rows for good.\n */\n readonly conditionalDelete?: boolean;\n}\n\n/**\n * The keys `new GcsStorage(options)` takes. Any other is refused by name rather than ignored: an ignored client key\n * \u2014 `storage`, as the lower-level storage driver calls it \u2014 falls back to ambient credentials and the **public** endpoint, and\n * for a client pointed at an emulator that is production traffic from a wiring typo.\n */\nexport const GCS_STORAGE_OPTION_KEYS = [\n 'bucket',\n 'prefix',\n 'client',\n 'projectId',\n 'apiEndpoint',\n 'maxObjectBytes',\n 'simpleUploadThresholdBytes',\n 'now',\n 'readTimeoutMs',\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 = ['projectId', 'apiEndpoint'] as const;\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 GcsStorage 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: GcsClient;\n\n constructor(options: GcsStorageOptions) {\n let readClient: GcsClient;\n refuseUnknown(\n 'GcsStorage',\n options,\n GCS_STORAGE_OPTION_KEYS,\n 'a @google-cloud/storage client goes in `client`',\n );\n if (typeof options.bucket !== 'string' || options.bucket.length === 0) {\n throw new ValidationError('GcsStorage needs a `bucket`: the name of one, a non-empty string');\n }\n if (options.now !== undefined && typeof options.now !== 'function') {\n throw new ValidationError(\n \"GcsStorage's `now` must be a function that returns epoch milliseconds\",\n );\n }\n if (options.client !== undefined && options.client !== null) {\n // A supplied client already carries its project and endpoint, so a setting beside it is ignored, and\n // ignoring it leaves the store talking to somewhere the caller did not mean: an `apiEndpoint` meant for an\n // emulator, silently dropped, is production traffic from a client that was built for the public endpoint.\n // 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 `GcsStorage 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 readClient = downloadClient(options.client, true);\n } else {\n const settings = {\n ...(options.projectId === undefined ? {} : { projectId: options.projectId }),\n ...(options.apiEndpoint === undefined ? {} : { apiEndpoint: options.apiEndpoint }),\n };\n this.client = new GcsClient(settings);\n // Downloads go through a twin that sends each once (see `read-client.ts`); it shares this client's credentials\n // object, so one token fetch and one hourly refresh serve both.\n readClient = downloadClient(this.client, false);\n }\n const shared = {\n storage: this.client,\n readStorage: readClient,\n bucket: options.bucket,\n ...(options.prefix === undefined ? {} : { prefix: options.prefix }),\n ...(options.readTimeoutMs === undefined ? {} : { readTimeoutMs: options.readTimeoutMs }),\n };\n this.storage = new GcsStorageDriver({\n ...shared,\n ...(options.maxObjectBytes === undefined ? {} : { maxObjectBytes: options.maxObjectBytes }),\n ...(options.simpleUploadThresholdBytes === undefined\n ? {}\n : { simpleUploadThresholdBytes: options.simpleUploadThresholdBytes }),\n });\n this.registry = new GcsRegistryDriver({\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 * `GcsStorageDriver` \u2014 an {@link IStorageDriver} over Google Cloud Storage.\n *\n * Uses the official `@google-cloud/storage`, a real dependency of this package \u2014 installing\n * `@cloudbitmaps/gcs` is what installs it. The `Storage` client is **injected** (dependency injection): the driver owns\n * no credential/project/endpoint logic, so it's thin, reuses the caller's client, and is testable against the\n * `fake-gcs-server` emulator (point a `Storage` at its `apiEndpoint`).\n *\n * Generations are write-once immutable objects: a resumable upload with the **`ifGenerationMatch: 0`**\n * precondition (\"create only if it does not exist\") makes the publish atomic \u2014 a second write to the same\n * object fails with 412 \u2192 {@link WriteConflictError}, never a silent overwrite (hard invariant 2: storage\n * objects are immutable and never overwritten in place), the GCS analogue of S3's `If-None-Match: *` and\n * LocalFs's atomic `link`. **Writes stream in constant memory:** the codec's bytes are\n * piped into a GCS resumable-upload `Writable` (chunked/freed by the SDK as they go), so a load's write\n * footprint stays bounded regardless of segment size, up to the advertised `maxObjectBytes`. Every object carries a\n * random write id in its custom metadata (`cbwid`), so a `412` that may be the write meeting itself can be read back:\n * a resumable upload's session is retried by the SDK, and a single-request upload is sent again by the driver after a\n * `429` or `503`, at most {@link THROTTLE_RESENDS} times with backoff, and after nothing else. Drivers may use\n * `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 type { Writable } from 'node:stream';\nimport { once } from 'node:events';\nimport type { Storage } from '@google-cloud/storage';\nimport { BucketPresence } from './bucket-presence';\nimport {\n storageObjectName,\n normalizeGcsPrefix,\n parseGenerationFromName,\n segmentObjectPrefix,\n} from './keys';\nimport {\n isInvalidRange,\n isNotFound,\n isPreconditionFailed,\n isThrottle,\n isTransient,\n isTransportFault,\n} from './gcs-errors';\nimport { retryDownload } from './download-retry';\nimport { scrubCredentials } from './scrub-error';\nimport { saveOnce } from './send-once';\nimport { downloadFile, readOnce, singleHeader, type ObjectRead } from './read-once';\nimport {\n ReadTimedOut,\n resolveReadTimeoutMs,\n startDeadline,\n withDeadline,\n type Deadline,\n} from './read-timeout';\n\n/** Default object ceiling: GCS's 5 TiB per-object hard max. Set lower to fail fast on a runaway write. */\nconst DEFAULT_MAX_OBJECT_BYTES = 5 * 1024 * 1024 * 1024 * 1024;\n\n/** The custom-metadata name an object's write id is stored under. Short: it travels with every write. */\nconst WRITE_ID_KEY = 'cbwid';\n/** How many times a throttled single-request upload is sent again: four sends in all, 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. */\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\n/**\n * Objects at/under this size are uploaded in a **single simple (non-resumable) request**; larger ones switch to\n * a **resumable stream** (constant memory). Mirrors S3's single-PUT-vs-multipart split \u2014 the simple path buffers\n * only \u2264 this many bytes, the resumable path streams. Default 8 MiB (\u2248 S3's part size), so peak write memory is\n * ~one threshold's worth regardless of object size.\n */\nconst DEFAULT_UPLOAD_THRESHOLD_BYTES = 8 * 1024 * 1024;\n\nexport interface GcsStorageDriverOptions {\n /** A constructed `@google-cloud/storage` `Storage` client (point `apiEndpoint` at fake-gcs-server locally). */\n readonly storage: Storage;\n /**\n * The client the downloads go through; `storage` when absent. `GcsStorage` passes one built with the SDK's request\n * retries off (the read client built in its constructor), because a download the SDK retries can crash the process;\n * the driver retries a download itself instead.\n */\n readonly readStorage?: Storage;\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional object-name prefix under which all objects live (e.g. `cloudbitmaps/`). */\n readonly prefix?: string;\n /** Largest object this driver will write/advertise (default = GCS's 5 TiB max). Must be a positive safe integer. */\n readonly maxObjectBytes?: number;\n /** Bytes at/under which a single non-resumable upload is used instead of a resumable stream (default 8 MiB).\n * Must be a positive safe integer. */\n readonly simpleUploadThresholdBytes?: number;\n /**\n * Cut off a read that has run this long, in milliseconds: a tail read (with the metadata read it falls back on for\n * an empty object) or a range read, every attempt and the backoff between them included. `0` (the default) sets no\n * timeout. The clock starts at the call into the driver, so a credential fetch counts, and runs until the body has\n * ended; when it passes the read throws `TransientError` and no further attempt starts. Uploads, deletes and\n * listings are not timed. A non-negative safe integer no larger than 2,147,483,647.\n */\n readonly readTimeoutMs?: number;\n /** What the backoff before re-sending a throttled upload waits on; real time when absent. */\n readonly clock?: Sleeper;\n}\n\nexport class GcsStorageDriver implements IStorageDriver {\n private readonly storage: Storage;\n private readonly readStorage: Storage;\n private readonly bucket: string;\n private readonly prefix: string | undefined;\n private readonly maxObjectBytes: number;\n private readonly threshold: number;\n private readonly readTimeoutMs: number;\n private readonly clock: Sleeper;\n private readonly presence: BucketPresence;\n\n constructor(options: GcsStorageDriverOptions) {\n this.storage = options.storage;\n this.clock = options.clock ?? REAL_TIME;\n this.readStorage = options.readStorage ?? options.storage;\n this.bucket = options.bucket;\n this.prefix = normalizeGcsPrefix(options.prefix);\n // Fail fast at the boundary: `??` only guards `undefined`, so NaN, 0, a negative or a fraction would otherwise\n // reject every write (cap) or corrupt the upload-path choice (threshold).\n for (const [name, value] of [\n ['maxObjectBytes', options.maxObjectBytes],\n ['simpleUploadThresholdBytes', options.simpleUploadThresholdBytes],\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 // GCS's own limit on one object: a cap past it is advertised and never reachable.\n if (options.maxObjectBytes !== undefined && options.maxObjectBytes > 5 * 1024 ** 4) {\n throw new ValidationError(\n `maxObjectBytes must be at most GCS's 5 TiB object limit (${5 * 1024 ** 4}); got ${options.maxObjectBytes}`,\n );\n }\n this.maxObjectBytes = options.maxObjectBytes ?? DEFAULT_MAX_OBJECT_BYTES;\n this.threshold = options.simpleUploadThresholdBytes ?? DEFAULT_UPLOAD_THRESHOLD_BYTES;\n this.readTimeoutMs = resolveReadTimeoutMs(options.readTimeoutMs);\n this.presence = new BucketPresence(this.storage, this.bucket);\n }\n\n capabilities(): StorageCaps {\n return { rangeRead: true, maxObjectBytes: this.maxObjectBytes, conditionalPut: true };\n }\n\n private file(name: string) {\n return this.storage.bucket(this.bucket).file(name);\n }\n\n /** The same object on the download client, off the SDK's shared agent ({@link downloadFile}). */\n private downloadable(name: string) {\n return downloadFile(this.readStorage, this.bucket, name);\n }\n\n /** The deadline a read of `key` made now runs under, every attempt included, named for its error; none when off. */\n private deadline(read: string, key: GenKey): Deadline | undefined {\n return startDeadline(this.readTimeoutMs, `${read} of ${key.segment}.${key.generation}`);\n }\n\n async putImmutable(\n key: GenKey,\n write: (sink: BlobSink) => Promise<void>,\n ): Promise<{ size: number; sha256: string }> {\n const objectName = storageObjectName(this.prefix, key); // validates ref + generation\n const sink = new GcsUploadSink(\n this.file(objectName),\n this.maxObjectBytes,\n this.threshold,\n this.clock,\n );\n try {\n await write(sink);\n return await sink.finish();\n } catch (err) {\n await sink.abort(); // best-effort teardown of the in-flight upload\n // A lost write-once race \u2014 the object already existed, so `ifGenerationMatch: 0` failed with 412.\n if (isPreconditionFailed(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)) throw err;\n throw this.mapError(err);\n }\n }\n\n async getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array> {\n if (!Number.isInteger(offset) || !Number.isInteger(length) || offset < 0 || length < 0) {\n throw new ValidationError(`invalid range offset=${offset} length=${length}`);\n }\n if (length === 0) return new Uint8Array(0);\n const objectName = storageObjectName(this.prefix, key);\n try {\n // One request, buffering at most the bytes asked for. GCS `end` is inclusive. `decompress: false`, as for the tail:\n // the offsets are into the bytes as stored.\n const deadline = this.deadline('range read', key);\n const res = await retryDownload(\n () =>\n readOnce(\n this.downloadable(objectName),\n { start: offset, end: offset + length - 1, decompress: false },\n length,\n () => this.badRange(key, `the response is longer than the ${length}B requested`),\n deadline,\n ),\n deadline,\n );\n this.checkRange(res, key, offset, length);\n return res.bytes;\n } catch (err) {\n throw await this.readFailure(err, key);\n }\n }\n\n /**\n * Refuse a range response unless it is exactly the bytes asked for. A 206 must name them in `Content-Range: bytes\n * a-b/total`, starting at `offset`, and hold that many; a 200 (a server that ignored the range) is the whole object,\n * which is the range only when the range starts at 0. A short read means the range ran past the end of the object:\n * out of bounds, never a partial result.\n */\n private checkRange(res: ObjectRead, key: GenKey, offset: number, length: number): void {\n const received = res.bytes.length;\n if (res.status === 200) {\n if (offset !== 0)\n throw this.badRange(key, `whole object returned for a range from ${offset}`);\n } else if (res.status === 206) {\n const header = singleHeader(res.headers, 'content-range');\n const m = header === undefined ? null : /^bytes (\\d+)-(\\d+)\\/(\\d+)$/.exec(header);\n if (m === null) throw this.badRange(key, 'Content-Range is missing or malformed');\n const [first, last, total] = [Number(m[1]), Number(m[2]), Number(m[3])];\n if (![first, last, total].every(Number.isSafeInteger)) {\n throw this.badRange(key, 'Content-Range holds a number that is not a safe integer');\n }\n if (first !== offset || last - first + 1 !== received || last >= total) {\n throw this.badRange(\n key,\n `Content-Range ${first}-${last}/${total} disagrees with ${received}B received from ${offset}`,\n );\n }\n } else {\n throw this.badRange(key, `unexpected HTTP ${res.status}`);\n }\n if (received !== length) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds (got ${received}B)`,\n );\n }\n }\n\n private badRange(key: GenKey, why: string): ValidationError {\n return new ValidationError(\n `GCS range read of ${key.segment}.${key.generation} refused: ${why}`,\n );\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<{ bytes: Uint8Array; size: number }> {\n const objectName = storageObjectName(this.prefix, key);\n // One deadline for the whole call: the tail's attempts, and the metadata read it may fall back on.\n const deadline = this.deadline('tail read', key);\n try {\n // Nothing to read: only the size is wanted, which the metadata answers in one request.\n if (maxBytes <= 0)\n return { bytes: new Uint8Array(0), size: await this.sizeOf(objectName, deadline) };\n if (!Number.isSafeInteger(maxBytes)) {\n throw new ValidationError(`invalid tail length ${maxBytes}`);\n }\n // One request: a suffix range (`Range: bytes=-N`, \"the last N bytes\") answers with the tail and, in\n // `Content-Range: bytes a-b/total`, the object's size. An object shorter than N comes back whole.\n let res;\n try {\n res = await retryDownload(\n () =>\n readOnce(\n this.downloadable(objectName),\n { end: -maxBytes, decompress: false },\n maxBytes,\n () => this.badTail(key, `the response is longer than the ${maxBytes}B requested`),\n deadline,\n ),\n deadline,\n );\n } catch (err) {\n // A zero-byte object has no suffix to satisfy, and a server may refuse the range with a 416. The metadata\n // settles whether that is an empty object (a valid, empty tail) or a real range fault.\n if (!isInvalidRange(err)) throw err;\n const size = await this.sizeOf(objectName, deadline);\n if (size !== 0) throw err;\n return { bytes: new Uint8Array(0), size };\n }\n if (res.bytes.length === 0) {\n // No bytes came back for a positive request: only an empty object may do that.\n const size = await this.sizeOf(objectName, deadline);\n if (size !== 0) throw this.badTail(key, `no bytes returned for an object of ${size}B`);\n return { bytes: res.bytes, size };\n }\n return { bytes: res.bytes, size: this.tailSize(res, maxBytes, key) };\n } catch (err) {\n throw await this.readFailure(err, key);\n }\n }\n\n /**\n * The object's size from its metadata, validated. Read on the download client and retried as a download is, within\n * the calling read's `deadline` ({@link withDeadline}), so nothing but the one request in flight runs past it.\n */\n private async sizeOf(objectName: string, deadline: Deadline | undefined): Promise<number> {\n const [meta] = await retryDownload(\n () => withDeadline(() => this.downloadable(objectName).getMetadata(), deadline),\n deadline,\n );\n const size = Number(meta.size ?? 0);\n if (!Number.isSafeInteger(size) || size < 0) {\n throw new ValidationError(`GCS returned an invalid object size: ${String(meta.size)}`);\n }\n return size;\n }\n\n /**\n * The object's total size from a tail response, refused unless every header agrees with the bytes received. A 206\n * must carry `Content-Range: bytes a-b/total` that names exactly the suffix asked for and exactly the bytes held;\n * a 200 (a server that ignored the range) is the whole object, and is accepted only when it fits in the request.\n */\n private tailSize(res: ObjectRead, maxBytes: number, key: GenKey): number {\n const received = res.bytes.length;\n if (res.status === 200) {\n if (received > maxBytes)\n throw this.badTail(key, 'whole object returned past the requested length');\n return received;\n }\n if (res.status !== 206) throw this.badTail(key, `unexpected HTTP ${res.status}`);\n const header = singleHeader(res.headers, 'content-range');\n const m = header === undefined ? null : /^bytes (\\d+)-(\\d+)\\/(\\d+)$/.exec(header);\n if (m === null) throw this.badTail(key, `Content-Range is missing or malformed`);\n const [first, last, total] = [Number(m[1]), Number(m[2]), Number(m[3])];\n if (![first, last, total].every(Number.isSafeInteger)) {\n throw this.badTail(key, 'Content-Range holds a number that is not a safe integer');\n }\n if (\n last !== total - 1 ||\n first !== Math.max(0, total - maxBytes) ||\n last - first + 1 !== received\n ) {\n throw this.badTail(\n key,\n `Content-Range ${first}-${last}/${total} disagrees with ${received}B received for a ${maxBytes}B tail`,\n );\n }\n return total;\n }\n\n private badTail(key: GenKey, why: string): ValidationError {\n return new ValidationError(`GCS tail read of ${key.segment}.${key.generation} refused: ${why}`);\n }\n\n async delete(key: GenKey): Promise<void> {\n // Idempotent: a racing/retried GC sweep of an absent object is a no-op. Not the SDK's `ignoreNotFound`, which takes\n // every 404 and so would report a delete in a missing bucket as done.\n try {\n await this.file(storageObjectName(this.prefix, key)).delete();\n } catch (err) {\n if (isNotFound(err)) return this.presence.confirm();\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 files;\n try {\n // autoPaginate (default) drains every page; a segment has few generations, so the set is small.\n [files] = await this.storage.bucket(this.bucket).getFiles({ prefix });\n } catch (err) {\n throw this.mapError(err);\n }\n for (const f of files) {\n const generation = parseGenerationFromName(prefix, f.name);\n if (generation !== null) {\n yield { namespace: ref.namespace, segment: ref.segment, generation };\n }\n }\n }\n\n /** A read's error, once a 404 is known to be the object's and not the bucket's ({@link BucketPresence}). */\n private async readFailure(err: unknown, key: GenKey): Promise<unknown> {\n if (isNotFound(err)) await this.presence.confirm();\n return this.mapReadError(err, key);\n }\n\n /** Map GCS 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 // 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 // Timed out on every attempt: transient, in words that name the read and the timeout.\n if (err instanceof ReadTimedOut) return new TransientError(err.message, { cause: err });\n // A connection that failed or was cut off, after the driver's retries: transient, whichever code Node gave it.\n if (isTransportFault(err)) {\n return new TransientError(\n `transient GCS fault: ${String((err as { code?: unknown }).code)}`,\n {\n cause: err,\n },\n );\n }\n return this.mapError(err);\n }\n\n /**\n * Reclassify a transient GCS 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-call site: an HTTP status and a fault\n * in transit arrive typed, and anything else (a checksum mismatch, a credentials failure) as the SDK raised it.\n */\n private mapError(err: unknown): unknown {\n err = scrubCredentials(err);\n if (isTransient(err)) {\n return new TransientError(\n `transient GCS fault: ${(err as { code?: unknown } | null)?.code ?? 'unknown'}`,\n { cause: err },\n );\n }\n return err;\n }\n}\n\ntype GcsFile = ReturnType<ReturnType<Storage['bucket']>['file']>;\n\n/** Concatenate 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 * {@link BlobSink} that uploads one GCS object write-once. It **buffers up to a threshold**: an object that\n * finishes at/under it is committed in a **single simple (non-resumable) request** ({@link saveOnce}) \u2014 the write-once\n * path both real GCS and `fake-gcs-server` enforce; a larger one switches to a **resumable stream**, flushing the\n * buffer then piping the rest in constant memory (peak \u2248 one threshold). SHA-256 is hashed incrementally. Both\n * paths carry `ifGenerationMatch: 0` (create-only-if-absent) \u2192 a conflict is a 412, mapped by the driver to\n * {@link WriteConflictError}. On error the caller invokes {@link abort}.\n *\n * **The simple upload is sent with no SDK retry** ({@link saveOnce}): a replay of one that landed and lost its response\n * would find its own object and read as a lost race, so a lost response or a timeout throws {@link TransientError}. A\n * `429` or `503` is the one answer it is sent again after, by {@link sendSimple}. The resumable upload is a session of\n * requests, which the SDK retries within by asking the session how much it holds; the SDK takes that retry from the\n * client's options, not the call's, so it stays on. Each upload, of either kind, tags its object with a random id in\n * custom metadata, and a `412` that may be the upload meeting itself is read back: an object that carries this\n * upload's id is a success, and any other is the lost race. The read is paid only on that `412`.\n */\nclass GcsUploadSink implements BlobSink {\n private readonly hash: Hash = createHash('sha256');\n private total = 0;\n private readonly buffered: Uint8Array[] = [];\n private bufferedLen = 0;\n private stream: Writable | undefined; // set once we cross the threshold \u2192 resumable mode\n private done: Promise<unknown[]> | undefined;\n private failure: unknown;\n\n constructor(\n private readonly file: GcsFile,\n private readonly maxObjectBytes: number,\n private readonly threshold: number,\n private readonly clock: Sleeper,\n ) {}\n\n private static readonly WRITE_OPTS = {\n preconditionOpts: { ifGenerationMatch: 0 as const }, // write-once: create only if absent\n metadata: { contentType: 'application/octet-stream' },\n };\n\n /**\n * This write's own id, stored in the object's custom metadata (outside the `.crbm` bytes) so a conflict can be told\n * from a replay of this same write: the resumable session's, or the simple upload's re-send after a throttle.\n */\n private readonly writeId = randomBytes(16).toString('hex');\n\n /** The upload options with this write's id in the object's custom metadata. */\n private tagged(): typeof GcsUploadSink.WRITE_OPTS & {\n metadata: { metadata: Record<string, string> };\n } {\n return {\n ...GcsUploadSink.WRITE_OPTS,\n metadata: {\n ...GcsUploadSink.WRITE_OPTS.metadata,\n metadata: { [WRITE_ID_KEY]: this.writeId },\n },\n };\n }\n\n async write(bytes: Uint8Array): Promise<void> {\n if (this.failure !== undefined) throw this.failure;\n if (bytes.length === 0) return;\n this.total += bytes.length;\n if (this.total > this.maxObjectBytes) {\n throw new ValidationError(`object exceeds maxObjectBytes ${this.maxObjectBytes}`);\n }\n this.hash.update(bytes);\n if (this.stream !== undefined) {\n // Resumable mode: honor backpressure. `events.once(stream,'drain')` REJECTS if the stream emits 'error'\n // while we wait (that's its documented behavior for any awaited event except 'error'), and auto-removes\n // both listeners on settle \u2014 so there's no listener accumulation across drain cycles.\n // A copy, here and below: the caller may reuse its buffer once this write resolves, and the stream or the\n // buffered upload still holds what it was given.\n if (!this.stream.write(Buffer.from(bytes))) await once(this.stream, 'drain');\n return;\n }\n this.buffered.push(new Uint8Array(bytes));\n this.bufferedLen += bytes.length;\n if (this.bufferedLen > this.threshold) this.startResumable();\n }\n\n /** Cross into resumable streaming: open the stream, flush the buffered bytes, keep only ~one threshold resident. */\n private startResumable(): void {\n const stream = this.file.createWriteStream({ resumable: true, ...this.tagged() });\n this.stream = stream;\n this.done = once(stream, 'finish'); // resolves on a clean commit; rejects on 'error' (e.g. 412)\n stream.on('error', (e: unknown) => {\n this.failure ??= e; // a persistent listener so a stray 'error' is never an unhandled 'error' event\n });\n this.done.catch(() => undefined); // finish() awaits this; guard against unhandled rejection until then\n const buf = concatBytes(this.buffered, this.bufferedLen);\n this.buffered.length = 0;\n this.bufferedLen = 0;\n stream.write(buf); // \u2264 threshold+one-chunk; backpressure is absorbed by the next write()'s drain-await / end()\n }\n\n async finish(): Promise<{ size: number; sha256: string }> {\n const sha256 = this.hash.digest('hex');\n if (this.stream === undefined) {\n // Small object: one simple (non-resumable) upload \u2014 write-once enforced everywhere, the emulator too.\n await this.sendSimple(concatBytes(this.buffered, this.bufferedLen));\n return { size: this.total, sha256 };\n }\n try {\n if (this.failure !== undefined) throw this.failure;\n this.stream.end();\n await this.done; // rejects if the commit fails (e.g. 412 write-once conflict)\n } catch (err) {\n // The SDK retries a resumable session, so a commit that landed and lost its response can be answered `412` by\n // its own replay. Nothing overwrites a write-once object, so the id it holds says who wrote it. A failed\n // read-back throws, and reaches the caller as `TransientError` when the fault is transient: neither a success\n // nor a conflict.\n if (!isPreconditionFailed(err) || (await this.storedWriteId()) !== this.writeId) throw err;\n }\n return { size: this.total, sha256 };\n }\n\n /**\n * Send the simple upload once, and again after a `429` or `503`, up to {@link THROTTLE_RESENDS} times, waiting a\n * full-jitter backoff before each. Any other failure is thrown at once. Once it has been sent again, a `412` can be\n * an earlier send that landed after all, so the object's write id decides: this write's own is a success, and any\n * other object, or one with no id, is the conflict. A failed read-back throws, and is neither. Exhausted, it throws\n * {@link TransientError}; nothing is deleted.\n */\n private async sendSimple(body: Uint8Array): Promise<void> {\n for (let resent = 0; ; resent++) {\n try {\n await saveOnce(this.file, body, this.tagged());\n return;\n } catch (err) {\n if (resent > 0 && isPreconditionFailed(err)) {\n if ((await this.storedWriteId()) === this.writeId) return;\n throw err;\n }\n if (!isThrottle(err)) throw err;\n if (resent >= THROTTLE_RESENDS) {\n throw new TransientError(\n `GCS upload 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 /** The id the stored object carries in its custom metadata, or `undefined` when it carries none or is gone. */\n private async storedWriteId(): Promise<string | undefined> {\n try {\n const [meta] = await this.file.getMetadata();\n return meta.metadata?.[WRITE_ID_KEY] as string | undefined;\n } catch (err) {\n if (isNotFound(err)) return undefined;\n throw err;\n }\n }\n\n async abort(): Promise<void> {\n if (this.stream !== undefined && !this.stream.destroyed) this.stream.destroy();\n }\n}\n", "/**\n * Whether the bucket a driver names exists, asked when a request first answers 404.\n *\n * GCS answers a missing bucket and a missing object with the same status and reason (`404 notFound`), and a\n * GCS-compatible server may not even word them apart. Read as a missing object, a misnamed or deleted bucket would\n * answer every read as an empty segment. An object listing tells them apart: it names no object, so its 404 can only\n * be the bucket's.\n *\n * ```\n * a request answers 404 \u2500\u2500\u25B6 confirm() \u2500\u2500\u25B6 bucket seen before? \u2500\u2500 yes \u2500\u2500\u25B6 the 404 is an object's\n * \u2502 no\n * \u25BC\n * list one object in the bucket\n * \u251C\u2500 answered \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25B6 remember the bucket; the 404 is an object's\n * \u251C\u2500 404 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25B6 reject: the bucket does not exist\n * \u251C\u2500 transient \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25B6 reject: TransientError (asked again next time)\n * \u251C\u2500 403 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25B6 remember it as unknowable; the 404 is an object's\n * \u2514\u2500 anything else \u2500\u2500\u2500\u2500\u2500\u25B6 the 404 is an object's (asked again next time)\n * ```\n *\n * A process pays one listing at most while its bucket exists, and concurrent 404s share it. An identity without\n * `storage.objects.list` is answered `403`, which says nothing about the bucket, so its 404s read as they always did.\n * Any other failure (a credential that could not be fetched, a 401) says nothing either, but may not last, so it is\n * not remembered. Once seen, the bucket is not asked about again: one deleted later in the process's life reads as an\n * empty store, as it did before this check, while every write to it fails.\n */\n\nimport { TransientError } from '@cloudbitmaps/core/driver-kit';\nimport type { Storage } from '@google-cloud/storage';\nimport { isTransient } from './gcs-errors';\nimport { scrubCredentials } from './scrub-error';\n\nexport class BucketPresence {\n private settled = false;\n private asking: Promise<void> | undefined;\n\n constructor(\n private readonly storage: Storage,\n private readonly bucket: string,\n ) {}\n\n /** Resolve when a 404 can be read as an object's; reject when the bucket does not exist or the listing failed in transit. */\n async confirm(): Promise<void> {\n if (this.settled) return;\n this.asking ??= this.ask().finally(() => {\n this.asking = undefined;\n });\n return this.asking;\n }\n\n private async ask(): Promise<void> {\n let failure: { raised: unknown } | undefined;\n try {\n await this.storage.bucket(this.bucket).getFiles({ maxResults: 1, autoPaginate: false });\n } catch (raised) {\n failure = { raised };\n }\n if (failure !== undefined) {\n // The cause is the credential-free copy, never the SDK's error as raised.\n const err = scrubCredentials(failure.raised);\n const status = (err as { code?: unknown } | null)?.code;\n if (status === 404) {\n throw new Error(`the GCS bucket does not exist: ${this.bucket}`, { cause: err });\n }\n if (isTransient(err)) {\n throw new TransientError(\n `transient GCS fault confirming the bucket exists: ${String(status)}`,\n {\n cause: err,\n },\n );\n }\n // Refused or otherwise unanswerable: nothing is known about the bucket, so a 404 reads as an object's as it did\n // before this check. Only a refusal (no list permission) is sure to answer the same again, so only it is kept.\n if (status !== 403) return;\n }\n this.settled = true;\n }\n}\n", "/**\n * Pure helpers for classifying Google Cloud Storage SDK errors (the transient class mirrors S3's).\n *\n * SDK-free + side-effect-free \u2014 they only read structural shapes off the thrown value (`err.code`,\n * `err.response.status`, `err.name`), so the GCS-specific translation is unit-testable without a live GCS or\n * an emulator, and without importing `@google-cloud/storage`. GCS's `ApiError` carries the HTTP status on\n * `.code` (a number); dropped/timed-out sockets surface as a Node error with a string `.code` (`ECONNRESET`,\n * `ETIMEDOUT`, \u2026).\n */\n\n/** The HTTP status of a GCS `ApiError`, if present (`err.code` as a number, or `err.response.status`). */\nfunction httpStatus(err: unknown): number | undefined {\n const e = err as { code?: unknown; response?: { status?: unknown } } | null;\n if (typeof e?.code === 'number') return e.code;\n if (typeof e?.response?.status === 'number') return e.response.status;\n return undefined;\n}\n\n/** A network-level error `code` string (e.g. `ECONNRESET`, `ETIMEDOUT`, `EAI_AGAIN`, `EPIPE`). */\nfunction networkCode(err: unknown): string | undefined {\n const code = (err as { code?: unknown } | null)?.code;\n return typeof code === 'string' ? code : undefined;\n}\n\n/**\n * A conditional `ifGenerationMatch: 0` write lost the write-once race \u2014 the object already existed, so GCS\n * returns **412 Precondition Failed**. Maps to `WriteConflictError` (caller OCC), never a blind retry.\n */\nexport function isPreconditionFailed(err: unknown): boolean {\n return httpStatus(err) === 412;\n}\n\n/**\n * GCS asked the client to slow down: `429` (`rateLimitExceeded`) or `503` (`backendError`), which the SDK raises as an\n * `ApiError` with the status as a numeric `code`. GCS does not document that a throttled request was not applied, so\n * an upload sent again after one must still tell a landed first send apart.\n */\nexport function isThrottle(err: unknown): boolean {\n const status = httpStatus(err);\n return status === 429 || status === 503;\n}\n\n/** The object / generation does not exist (GCS returns 404), in a bucket that does. */\nexport function isNotFound(err: unknown): boolean {\n return httpStatus(err) === 404 && !isMissingBucket(err);\n}\n\n/**\n * The bucket itself does not exist: a wiring fault, never an absent object. Read as one, a misnamed or deleted bucket\n * would answer every read as an empty segment. GCS gives both the same status and reason (`404 notFound`), so only its\n * message tells them apart: on a download the message is the response body as it came, JSON or text, and elsewhere\n * the message and each `errors[].message` are GCS's own. A wording this does not match reads as an absent object.\n */\nexport function isMissingBucket(err: unknown): boolean {\n if (httpStatus(err) !== 404) return false;\n const e = err as { message?: unknown; errors?: unknown };\n const says = (m: unknown): boolean => typeof m === 'string' && /bucket does not exist/i.test(m);\n if (says(e.message)) return true;\n return (\n Array.isArray(e.errors) &&\n e.errors.some((inner) => says((inner as { message?: unknown } | null)?.message))\n );\n}\n\n/** A range request started past EOF (HTTP 416 Requested Range Not Satisfiable). */\nexport function isInvalidRange(err: unknown): boolean {\n return httpStatus(err) === 416;\n}\n\n/** The statuses the SDK's own retry predicate retries (`RETRYABLE_ERR_FN_DEFAULT` in `@google-cloud/storage`). */\nconst SDK_RETRIED_STATUSES = [408, 429, 500, 502, 503, 504];\n\n/** The connection faults the same predicate names, matched on a lower-cased code or reason as it does. */\nfunction isSdkConnectionProblem(reason: string): boolean {\n return (\n reason.includes('eai_again') ||\n reason === 'econnreset' ||\n reason === 'unexpected connection closure' ||\n reason === 'epipe' ||\n reason === 'socket connection timeout'\n );\n}\n\n/**\n * The codes Node gives a connection that failed or was cut off: refused, reset, aborted, timed out, a DNS failure, an\n * unreachable host or network, a broken pipe, and a chunked body cut off mid-stream (`ERR_STREAM_PREMATURE_CLOSE`).\n * Only these: a file the credentials name that is missing or unreadable (`ENOENT`, `EACCES`) and a TLS failure\n * (`EPROTO`, a certificate error) carry codes of the same shape, and would fail the same way again.\n */\nconst TRANSPORT_CODES = new Set([\n 'ECONNRESET',\n 'ECONNREFUSED',\n 'ECONNABORTED',\n 'ETIMEDOUT',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n 'EPIPE',\n 'ERR_STREAM_PREMATURE_CLOSE',\n]);\n\n/** A download that failed in transit rather than with an HTTP status: one of {@link TRANSPORT_CODES}. */\nexport function isTransportFault(err: unknown): boolean {\n if (httpStatus(err) !== undefined) return false;\n const code = networkCode(err);\n return code !== undefined && TRANSPORT_CODES.has(code);\n}\n\n/**\n * Whether the driver retries a failed download, which it does in the SDK's place: the statuses the SDK's predicate\n * retries (408, 429, 500, 502, 503 and 504, as a number or a string `code`), the connection faults it names in any\n * `errors[].reason`, and every {@link isTransportFault}, which the SDK retried before any response came. A 404, 412,\n * 416 or 403, a 501 or 505, a credentials or TLS failure, and every other answer reach the caller on the first attempt.\n */\nexport function isDownloadRetryable(err: unknown): boolean {\n if (err === null || typeof err !== 'object') return false;\n if (isTransportFault(err)) return true;\n const e = err as { code?: unknown; errors?: unknown };\n if (typeof e.code === 'number' && SDK_RETRIED_STATUSES.includes(e.code)) return true;\n if (typeof e.code === 'string') {\n if (SDK_RETRIED_STATUSES.map(String).includes(e.code)) return true;\n if (isSdkConnectionProblem(e.code.toLowerCase())) return true;\n }\n if (Array.isArray(e.errors)) {\n for (const inner of e.errors as Array<{ reason?: unknown } | null>) {\n const reason = inner?.reason?.toString().toLowerCase();\n if (reason !== undefined && isSdkConnectionProblem(reason)) return true;\n }\n }\n return false;\n}\n\n/**\n * A transient GCS fault that is safe to retry: throttling (429), a request timeout (408), any 5xx, or a dropped/timed-out socket.\n * Excludes the deterministic, caller-meaningful outcomes (412/404/416) \u2014 those must never be reclassified as\n * a blind transient (a retried doomed conditional write would just fail again, and mask an OCC conflict).\n */\nexport function isTransient(err: unknown): boolean {\n if (isPreconditionFailed(err) || isNotFound(err) || isInvalidRange(err)) return false;\n const status = httpStatus(err);\n if (status === 408 || status === 429 || (status !== undefined && status >= 500 && status < 600))\n return true;\n // The same connection faults a download is retried on: one list, so a write and a read cannot disagree on one.\n const net = networkCode(err);\n return net !== undefined && TRANSPORT_CODES.has(net);\n}\n", "/**\n * Make an error the SDK raised safe to throw, or to keep as a `cause`: a copy of it with no credential and none of the\n * live transport.\n *\n * The SDK keeps the request it sent on the error (`response.request`, `config.headers`, the raw request line), and\n * through that request the HTTP agent it shares with every other call, whose sockets hold the raw text, bearer token\n * included, of requests still in flight. An application that logs the error or its cause chain would write a live\n * access token. The error is therefore replaced by a copy built from what it is: its prototype, name, message, stack,\n * code, status and other plain data; `response` reduced to its status; the request, agent, socket and config dropped.\n * Nothing live is ever changed, only read, and the copy is made from plain data alone, so no depth or size limit can\n * stop short of a secret. A credential header or header line in the plain data that is kept is redacted.\n */\n\n/** Properties that hold the raw transport or the request's configuration. They are not copied. */\nconst DROPPED = new Set([\n 'request',\n 'agent',\n 'socket',\n '_httpMessage',\n 'connection',\n 'req',\n 'res',\n 'config',\n]);\n/** Header and field names whose value is a credential. */\nconst CREDENTIAL_NAME = /^(?:proxy-)?authorization$|^cookie$|^set-cookie$|^x-api-key$/i;\n/** A credential header inside a raw request or response text. */\nconst CREDENTIAL_LINES = /^((?:proxy-)?authorization|cookie|set-cookie|x-api-key):[^\\r\\n]*/gim;\nconst REDACTED = '[redacted]';\n\nconst isPlain = (value: object): boolean => {\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n};\n\n/** The part of an HTTP response worth keeping: what the server said, as numbers and text. */\nfunction responseSummary(response: object): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of ['status', 'statusText']) {\n const value: unknown = (response as Record<string, unknown>)[key];\n if (typeof value === 'number' || typeof value === 'string') out[key] = value;\n }\n return out;\n}\n\n/** A copy of `value` from plain data only: other objects (streams, headers, sockets) are left out. */\nfunction clean(value: unknown, copies: WeakMap<object, unknown>): unknown {\n if (typeof value === 'string') return value.replace(CREDENTIAL_LINES, `$1: ${REDACTED}`);\n if (typeof value !== 'object' || value === null) {\n return typeof value === 'function' || typeof value === 'symbol' ? undefined : value;\n }\n if (copies.has(value)) return copies.get(value);\n if (Array.isArray(value)) {\n const list: unknown[] = [];\n copies.set(value, list);\n for (const item of value) list.push(clean(item, copies));\n return list;\n }\n const isError = value instanceof Error;\n if (!isError && !isPlain(value)) return undefined;\n const copy = Object.create(Object.getPrototypeOf(value) as object | null) as Record<\n string | symbol,\n unknown\n >;\n copies.set(value, copy);\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key === 'string' && DROPPED.has(key)) continue;\n let held: unknown;\n try {\n held = (value as Record<string | symbol, unknown>)[key];\n } catch {\n continue; // a getter that throws\n }\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n let kept: unknown;\n if (typeof key === 'string' && CREDENTIAL_NAME.test(key)) kept = held == null ? held : REDACTED;\n else if (key === 'response' && typeof held === 'object' && held !== null) {\n kept = responseSummary(held);\n } else kept = clean(held, copies);\n if (kept === undefined && held !== undefined) continue; // an object this copy leaves out\n Object.defineProperty(copy, key, {\n value: kept,\n writable: true,\n configurable: true,\n enumerable: descriptor?.enumerable ?? true,\n });\n }\n return copy;\n}\n\n/** Whether `err`, or an error in its `cause` chain, holds a response, a request or the rest of the transport. */\nfunction holdsTransport(err: object, seen: Set<object>): boolean {\n if (seen.has(err)) return false;\n seen.add(err);\n if (Object.getOwnPropertyNames(err).some((key) => DROPPED.has(key) || key === 'response')) {\n return true;\n }\n const cause: unknown = (err as { cause?: unknown }).cause;\n return typeof cause === 'object' && cause !== null && holdsTransport(cause, seen);\n}\n\n/**\n * `err` as it is safe to throw: a credential-free copy of an error that holds the SDK's request or response, and any\n * other value as it is (a timeout, a connection fault or an error of the driver's own holds no credential, and keeps\n * its identity).\n */\nexport function scrubCredentials<T>(err: T): T {\n if (typeof err !== 'object' || err === null) return err;\n return holdsTransport(err, new Set()) ? (clean(err, new WeakMap()) as T) : err;\n}\n", "/**\n * Logical-ref \u2192 GCS object-name mapping for {@link GcsStorageDriver}.\n *\n * Pure string logic, no SDK dependency \u2014 unit-testable without GCS or an emulator. Uses the **same\n * backend-agnostic `.crbm` object-name scheme** as the S3 + LocalFs storage drivers\n * (`<prefix><ns>/segments/<segment>.<gen>.crbm`), so a segment reads identically whichever storage backend holds\n * it. The default (absent) namespace maps to `_default`, which cannot collide with a real namespace because a\n * caller's `_default` encodes to `%5Fdefault` while the sentinel is emitted literally.\n *\n * The storage-key layout is shared, not copied: `prefixPart` and the name encoders come from\n * `@cloudbitmaps/core/driver-kit`, so a segment written by one driver reads identically under another. Two\n * drivers disagreeing about how a name becomes a key would be a silent cross-driver incompatibility on the\n * same bucket, which is exactly the kind of thing a local copy drifts into.\n */\n\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 normalizeGcsPrefix } from '@cloudbitmaps/core/driver-kit';\n\n/**\n * The GCS object-name prefix shared by all of a segment's generations: `<prefix><ns>/segments/<segment>.`.\n * Used both as the `getFiles` prefix and as the string stripped by {@link parseGenerationFromName}.\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 GCS object name of one `.crbm` generation: `<segmentPrefix><gen>.crbm`. */\nexport function storageObjectName(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 name, 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. Also rejects a *different* segment whose name merely shares the prefix (its middle\n * isn't all digits).\n */\nexport function parseGenerationFromName(segmentPrefix: string, objectName: string): number | null {\n if (!objectName.startsWith(segmentPrefix) || !objectName.endsWith(SUFFIX)) return null;\n const middle = objectName.slice(segmentPrefix.length, objectName.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 * The read timeout: `readTimeoutMs`, off (`0`) unless set. It bounds one driver read \u2014 a tail read (with the metadata\n * read it can fall back on), a range read, a registry read \u2014 as a whole: one deadline, started when the driver is\n * called, covers every attempt the driver makes at it and the backoff between them.\n *\n * `@google-cloud/storage` 8.x sets no working timeout of its own: it hands its `timeout` to an HTTP client that has no\n * such option, so a read on a connection that stops answering waits forever, and nothing above it ever gets a fault to\n * retry. When the deadline passes, the request in flight fails with {@link ReadTimedOut} (coded `ETIMEDOUT`), no\n * further attempt starts, and a backoff in progress ends with the same error; the driver reports it as\n * `TransientError`, its message naming the read and the timeout, for the store's read retry to run again.\n *\n * **What the clock counts.** Everything from the call into the driver: fetching or refreshing a credential, resolving\n * the project, waiting for a socket (Node's global agents, which the downloads use, set no socket limit unless the\n * process sets one, so by default there is no queue), each request, its headers and its whole body, and the driver's\n * backoff between attempts. It counts time the process spends busy too: Node runs a due timer before it reads a socket,\n * so a synchronous stretch longer than the timeout fails the reads in flight even when their responses have arrived.\n *\n * Uploads, deletes, listings and the conditional writes are not timed: an upload can rightly take longer than a read,\n * and a write cut off may still land.\n */\nimport { ValidationError } from '@cloudbitmaps/core/driver-kit';\n\n/** The default: no timeout. */\nconst DEFAULT_READ_TIMEOUT_MS = 0;\n\n/** The longest delay a Node timer holds. A longer one fires after 1 ms instead, so it is refused rather than passed on. */\nconst MAX_TIMER_MS = 2_147_483_647;\n\n/** Validate a caller's `readTimeoutMs`, defaulting it: an integer from 0 (no timeout) to {@link MAX_TIMER_MS}. */\nexport function resolveReadTimeoutMs(value: unknown): number {\n if (value === undefined) return DEFAULT_READ_TIMEOUT_MS;\n if (\n typeof value !== 'number' ||\n !Number.isSafeInteger(value) ||\n value < 0 ||\n value > MAX_TIMER_MS\n ) {\n throw new ValidationError(\n `readTimeoutMs must be a non-negative safe integer no larger than ${MAX_TIMER_MS}; got ${describe(value)}`,\n );\n }\n return value;\n}\n\n/** A value as an error message can show it: a string quoted, so `'200'` is not mistaken for `200`, and never a throw. */\nfunction describe(value: unknown): string {\n if (typeof value === 'string') return JSON.stringify(value);\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n return value === null ? 'null' : typeof value;\n}\n\n/** A read the driver gave up on when its deadline passed. Coded `ETIMEDOUT`, as a timed-out connection is. */\nexport class ReadTimedOut extends Error {\n readonly code = 'ETIMEDOUT';\n constructor(read: string, ms: number, cause?: unknown) {\n super(`GCS ${read} timed out after ${ms} ms`, cause === undefined ? undefined : { cause });\n this.name = 'ReadTimedOut';\n }\n}\n\n/** One driver read's deadline: `ms` from when it is made. `read` names the read in the error, e.g. `tail read of s.3`. */\nexport class Deadline {\n private readonly endsAt: number;\n\n constructor(\n readonly ms: number,\n readonly read: string,\n ) {\n this.endsAt = performance.now() + ms;\n }\n\n /** What is left of it, in milliseconds: 0 once it has passed. */\n remaining(): number {\n return Math.max(0, this.endsAt - performance.now());\n }\n\n /** The error a read fails with once this has passed; `cause` is the last attempt's own error, if one failed. */\n expired(cause?: unknown): ReadTimedOut {\n return new ReadTimedOut(this.read, this.ms, cause);\n }\n}\n\n/** The deadline a read made now runs under, or none when `ms` is 0 (no timeout). */\nexport function startDeadline(ms: number, read: string): Deadline | undefined {\n return ms === 0 ? undefined : new Deadline(ms, read);\n}\n\n/**\n * Make the request `send` starts, and settle with it, or with {@link ReadTimedOut} once `deadline` has passed; a\n * deadline already passed sends nothing. For a request the SDK cannot cancel: a metadata read is one callback-style\n * request that returns no handle to abort it, so at the deadline this stops waiting and the request runs on until it is\n * answered or its connection closes. The race keeps a handler on the request, so what it settles with then is dropped\n * rather than raised.\n */\nexport function withDeadline<T>(\n send: () => Promise<T>,\n deadline: Deadline | undefined,\n): Promise<T> {\n if (deadline === undefined) return send();\n if (deadline.remaining() === 0) return Promise.reject(deadline.expired());\n const request = send();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timedOut = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(deadline.expired()), deadline.remaining());\n });\n return Promise.race([request, timedOut]).finally(() => clearTimeout(timer));\n}\n", "/**\n * `retryDownload` \u2014 run one download again after the faults the SDK itself would have retried.\n *\n * The client `GcsStorage` builds sends a download once (see `backend.ts`: a download the SDK retries can crash the\n * process), which also took away the SDK's quiet retry of a connection fault and of a 408, 429, 500, 502, 503 or 504.\n * This puts that retry back inside the driver, so every caller keeps it: the store's admin and write-path registry\n * reads, and a store built with `retry: false`, included. Which faults: `isDownloadRetryable`. How many: three\n * retries, the SDK's default, with full-jitter backoff of at most about 0.7 s a call. A chunk read that the store's\n * own retry also repeats makes at most 16 requests, as it did with the SDK's retry. A 404, 412, 403 and every other\n * answer reach the caller on the first attempt. What the last attempt raised is rethrown as it is, for the driver to\n * classify.\n *\n * **Under a read timeout** (`readTimeoutMs`), one {@link Deadline} covers every attempt: each attempt passes it to its\n * request, so the request in flight when it passes fails with {@link ReadTimedOut}; no attempt starts after it; and a\n * backoff that would outlast it ends when it passes, with the same error. Nor is a retry sent with less than\n * {@link MIN_RETRY_MS} left: no answer could arrive in time, and the request would only be one more held open. So a\n * server that never answers is sent one request per call, not four.\n */\nimport { isDownloadRetryable } from './gcs-errors';\nimport { ReadTimedOut, type Deadline } from './read-timeout';\n\nconst RETRIES = 3;\nconst BASE_DELAY_MS = 100;\nconst MAX_DELAY_MS = 1_000;\n/** Under a deadline, a retry is not sent with less than this left of it. */\nexport const MIN_RETRY_MS = 10;\n\n/**\n * Call `download`, and again after each retryable fault, up to {@link RETRIES} more times, within `deadline` when there\n * is one; rethrow the last error, or the deadline's once it has passed.\n */\nexport async function retryDownload<T>(\n download: () => Promise<T>,\n deadline?: Deadline,\n): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await download();\n } catch (err) {\n // The deadline passed during the attempt: nothing more is sent.\n if (err instanceof ReadTimedOut) throw err;\n if (attempt >= RETRIES || !isDownloadRetryable(err)) throw err;\n const left = deadline?.remaining() ?? Infinity;\n if (deadline !== undefined && left < MIN_RETRY_MS) throw deadline.expired(err);\n const ceiling = Math.min(MAX_DELAY_MS, BASE_DELAY_MS * 2 ** attempt);\n const delay = Math.random() * ceiling;\n await new Promise((resolve) => setTimeout(resolve, Math.min(delay, left)));\n if (deadline !== undefined && deadline.remaining() < MIN_RETRY_MS)\n throw deadline.expired(err);\n }\n }\n}\n", "/**\n * `saveOnce` \u2014 upload one object in a single request, sent once, with no SDK retry around it.\n *\n * `file.save()` runs a single-request upload inside a retry loop, and it counts an upload that carries\n * `ifGenerationMatch` as safe to repeat, so after a lost response \u2014 a timeout, a reset connection, a 5xx \u2014 it sends\n * the upload again. When the first had landed, the second meets it: `ifGenerationMatch: 0` finds the object it\n * created, a generation fence finds the generation it replaced, and GCS answers `412`, which the driver can only report\n * as a lost race. The caller is told its write lost when it won. Only the caller can find out which happened, by\n * reading the pointer or listing the generations, so the write is sent once and a transient failure reaches it as\n * `TransientError`.\n *\n * `createWriteStream({ resumable: false })` is that same single multipart request without the loop: the SDK sends a\n * streamed upload with no retries of its own, since a stream it has read cannot be read again. The client is not\n * touched, so a caller's own client keeps its retry options for every other call.\n *\n * **`resumable: false` is load-bearing twice.** It is what makes this one request, and it is the path on which\n * fake-gcs-server enforces `ifGenerationMatch`, so the integration lane tests the precondition rather than passing over\n * a write with none.\n */\nimport { once } from 'node:events';\nimport type { CreateWriteStreamOptions, Storage } from '@google-cloud/storage';\n\ntype GcsFile = ReturnType<ReturnType<Storage['bucket']>['file']>;\n\n/** Upload `body` to `file` in one request, sent once; resolves once GCS has stored it and its checksum matched. */\nexport async function saveOnce(\n file: GcsFile,\n body: Uint8Array,\n options: CreateWriteStreamOptions,\n): Promise<void> {\n const stream = file.createWriteStream({ ...options, resumable: false });\n const stored = once(stream, 'finish'); // rejects on 'error', which is how a failed upload surfaces\n stream.end(body);\n await stored;\n}\n", "/**\n * `readOnce` \u2014 read one object, or one range of it, in a single GET, with the response headers in hand.\n *\n * `file.download()` returns only the bytes. The headers carry what a caller needs alongside them \u2014 the object's\n * `generation` (the registry's version fence) and, on a ranged read, the object's total size in `Content-Range` \u2014 so\n * this reads through `createReadStream`, which announces the raw response before any data flows. Both come from the\n * same response as the bytes, so they describe one observation of the object.\n *\n * **Everything on the wire is untrusted.** The advertised `Content-Length` is checked against `maxBytes` before the\n * first byte is buffered, and the bytes are counted as they arrive, so a response that understates its length (or\n * omits it) still cannot make the caller hold more than `maxBytes`. The headers are returned as received; each caller\n * parses the ones it relies on strictly.\n *\n * **Every download stays off the SDK's shared connection pool.** When a download fails part-way \u2014 a body cut off, a\n * refused response, a timeout \u2014 the SDK destroys the HTTP agent the request went out on, to free its socket. By default\n * that is one keep-alive agent the whole process shares (teeny-request's, for every request with `forever: true`), so\n * every other request in flight on any client is reset with it, uploads included. {@link downloadFile} sets\n * `forever: false` on its own requests, through the SDK's request interceptors: they then go out on Node's global\n * agent, which the SDK never destroys, and on `@google-cloud/storage` 8.x a destroyed download closes its own socket\n * and nothing else. The cost: the global agent closes a connection idle for 5 seconds, where the SDK's pool keeps it,\n * so a read after a longer pause opens a new one; and it is shared with the process's other `http` and `https` code.\n */\nimport type { Storage } from '@google-cloud/storage';\nimport type { Deadline } from './read-timeout';\n\ntype GcsFile = ReturnType<ReturnType<Storage['bucket']>['file']>;\ntype Interceptor = GcsFile['interceptors'][number];\n/** What the SDK hands an interceptor: its typing says less, but every request it decorates carries a `uri`. */\ntype RequestOptions = ReturnType<Interceptor['request']>;\n\n/** Sends a request on Node's global agent rather than the SDK's shared keep-alive one (see the module comment). */\nconst OFF_THE_SHARED_AGENT: Interceptor = {\n request: (reqOpts) => ({ ...(reqOpts as RequestOptions), forever: false }),\n};\n\n/** A handle on `name` for downloading: its requests stay off the SDK's shared agent. The client is not touched. */\nexport function downloadFile(storage: Storage, bucket: string, name: string): GcsFile {\n const file = storage.bucket(bucket).file(name);\n file.interceptors.push(OFF_THE_SHARED_AGENT);\n return file;\n}\n\nexport interface ObjectRead {\n /** The HTTP status of the response (200 for a whole object, 206 for a range). */\n readonly status: number;\n /** The response headers, lower-cased as Node delivers them. */\n readonly headers: Readonly<Record<string, string | string[] | undefined>>;\n /** The body, at most `maxBytes` long. */\n readonly bytes: Uint8Array;\n}\n\n/**\n * The response the SDK announces. It is the HTTP layer's own body stream with the status and headers laid on it, and\n * while that body is in flight the SDK and the HTTP layer each run a pipeline over it, which with its own listeners is\n * eleven or twelve error and close listeners: one past Node's default limit of ten, so Node prints a possible-leak\n * warning for every read that is still arriving when it is let go. Nothing grows with the number of attempts, because\n * each attempt has a body of its own. The limit is raised on that one stream, to {@link BODY_LISTENER_LIMIT}.\n */\ninterface RawResponse {\n statusCode?: unknown;\n headers?: Record<string, string | string[] | undefined>;\n setMaxListeners?: (n: number) => unknown;\n}\n\n/** Room for the listeners the SDK's and the HTTP layer's pipelines put on one response body, with a few to spare. */\nconst BODY_LISTENER_LIMIT = 16;\n\n/** The value of a header that must appear once, or `undefined` when it is absent, repeated or empty. */\nexport function singleHeader(headers: ObjectRead['headers'], name: string): string | undefined {\n const value = headers[name];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * GET `file` (with the SDK's `createReadStream` options), buffering at most `maxBytes`. `oversize` builds the error\n * thrown when the advertised or actual length passes the cap, so each caller reports it in its own vocabulary.\n * Transport and HTTP errors reject as the SDK raises them (a 404 carries `code: 404`).\n *\n * With a `deadline`, a read still unsettled when it passes rejects with its `ReadTimedOut`; one asked for after it has\n * passed is not sent. The deadline belongs to the whole driver read, so an attempt gets only what is left of it.\n */\nexport function readOnce(\n file: GcsFile,\n options: { start?: number; end?: number; validation?: false; decompress?: false },\n maxBytes: number,\n oversize: (size: number | undefined) => Error,\n deadline?: Deadline,\n): Promise<ObjectRead> {\n if (deadline !== undefined && deadline.remaining() === 0)\n return Promise.reject(deadline.expired());\n return new Promise<ObjectRead>((resolve, reject) => {\n const stream = file.createReadStream(options);\n const chunks: Buffer[] = [];\n let total = 0;\n let status = 0;\n let headers: ObjectRead['headers'] = {};\n let responded = false;\n let settled = false;\n // The SDK makes no request until the stream is read from, in a later turn, so a timer set here covers all of it.\n const timer =\n deadline === undefined\n ? undefined\n : setTimeout(() => fail(deadline.expired()), deadline.remaining());\n\n // Deferred: the SDK builds its response pipeline right after announcing the response, and destroying the stream\n // before that makes the pipeline throw out of an event handler, where nothing can catch it, and ends the process.\n // One turn later the same destroy ends the read without a throw. So a read is let go only once its response has\n // been announced, never before: one that settles first (a timeout before any answer) is destroyed when its\n // response comes, if one ever does. Until then the SDK has no way to cancel it, so its request stays open until\n // the server answers or the connection closes.\n const release = (): void => void setImmediate(() => stream.destroy());\n\n const fail = (err: unknown): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (responded) release();\n reject(err);\n };\n\n stream.on('response', (res: RawResponse) => {\n responded = true;\n res.setMaxListeners?.(BODY_LISTENER_LIMIT);\n if (settled) return release();\n status = typeof res.statusCode === 'number' ? res.statusCode : 0;\n headers = res.headers ?? {};\n if (status < 200 || status > 299) return; // the SDK raises the error itself, with its own status\n const advertised = singleHeader(headers, 'content-length');\n if (advertised !== undefined && /^\\d+$/.test(advertised) && Number(advertised) > maxBytes) {\n fail(oversize(Number(advertised)));\n }\n });\n stream.on('data', (chunk: Buffer) => {\n if (settled) return;\n total += chunk.length;\n if (total > maxBytes) return fail(oversize(undefined));\n chunks.push(chunk);\n });\n stream.on('error', fail);\n stream.on('end', () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve({ status, headers, bytes: new Uint8Array(Buffer.concat(chunks, total)) });\n });\n // A net under the SDK: it reports a cut-off body as an 'error' itself, but a stream that closes without ending\n // or erring was cut off all the same, so it is a dropped connection, and retryable.\n stream.on('close', () =>\n fail(\n Object.assign(new Error('GCS read ended before the response completed'), {\n code: 'ECONNRESET',\n }),\n ),\n );\n });\n}\n", "/**\n * `GcsRegistryDriver` \u2014 an {@link IRegistryDriver} over Google Cloud Storage.\n *\n * Lets a **GCS deployment run on one bucket alone** \u2014 storage `.crbm` generations and the registry in the same\n * place, with no second cloud involved.\n *\n * The protocol (an ABA-safe OCC token, the delete and its tombstone, the bounded retry, the key layout) lives once\n * in {@link ObjectStoreRegistry}; this file is only the I/O calls GCS makes. A delete removes a row for good, under\n * `ifGenerationMatch: <generation>`, when `conditionalDelete` is `true`. It is off by default, on the public endpoint too:\n * no run against real GCS has verified that the service applies the precondition to a delete, and fake-gcs-server\n * accepts it and ignores it, so CI cannot show it.\n *\n * **The atomic swap is offloaded to GCS's object preconditions.** `ifGenerationMatch: 0` is create-only\n * (\"only if it does not exist\") and `ifGenerationMatch: <generation>` is compare-and-swap \u2014 the same pair S3\n * spells `If-None-Match: *` / `If-Match: <etag>`. A lost race returns `412` \u2192 {@link WriteConflictError}. Each write\n * is one request sent once ({@link saveOnce}), so a `412` means another write got there first, never this one meeting\n * itself after a lost response. A transient failure reaches the caller as {@link TransientError}: the write may or may\n * not have landed, and the caller re-reads the row to learn where it stands.\n *\n * **The version fence is the object's `generation`, not its ETag**: GCS mutates the generation on every\n * overwrite and it is the value `ifGenerationMatch` compares, so it is the only correct fence here. (Note\n * the unrelated collision of vocabulary \u2014 a GCS *object generation* has nothing to do with a CloudBitmaps\n * `.crbm` *generation*.) Reads are strongly consistent, satisfying the registry's `strongRead` contract.\n *\n * **Deployment requirements** (a policy that violates these silently corrupts the registry):\n * - The principal needs `storage.objects.get`, `create`, `update`, `list` and, to remove rows, `delete` on the\n * bucket. Without `list`\n * the registry cannot enumerate, and a missing-object read may surface as `403` rather than `404`.\n * - **Do not apply an Object Lifecycle rule to the `registry/` prefix that deletes a live object** (one on\n * noncurrent versions only is safe), and do not enable a retention\n * policy that blocks overwrite or deletion; see {@link ObjectStoreRegistry}.\n * - Object versioning is neither required nor used; the driver always reads the live generation. A read is one\n * GET whose `x-goog-generation` header and body describe the same object, so a concurrent overwrite is simply\n * observed as the older or the newer row, never as a torn pair or a missing one.\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 type { Storage } from '@google-cloud/storage';\nimport { isNotFound, isPreconditionFailed, isTransient, isTransportFault } from './gcs-errors';\nimport { BucketPresence } from './bucket-presence';\nimport { crc32cBase64 } from './crc32c';\nimport { retryDownload } from './download-retry';\nimport { scrubCredentials } from './scrub-error';\nimport { downloadFile, readOnce, singleHeader } from './read-once';\nimport { ReadTimedOut, resolveReadTimeoutMs, startDeadline } from './read-timeout';\nimport { saveOnce } from './send-once';\n\nexport interface GcsRegistryDriverOptions {\n /** A constructed `@google-cloud/storage` `Storage` client (point `apiEndpoint` at fake-gcs-server locally). */\n readonly storage: Storage;\n /** The client the downloads go through; `storage` when absent (see `GcsStorageDriverOptions.readStorage`). */\n readonly readStorage?: Storage;\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional object-name 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 * Cut off a read of a registry row once it has run this long, in milliseconds, every attempt included; `0` (the\n * default) sets no timeout. Timed as the storage driver's reads are (see `GcsStorageDriverOptions.readTimeoutMs`);\n * the writes and listings are not timed.\n */\n readonly readTimeoutMs?: number;\n /**\n * Whether a delete removes a row for good, by an object delete sent with `ifGenerationMatch: <the generation it\n * read>`, rather than leaving a tombstone. Only a row born with an incarnation id is removed; a row a release before\n * 0.12 wrote is always tombstoned. Defaults to `false`, on the public endpoint too: whether real GCS applies\n * `ifGenerationMatch` to a delete has not been verified by a run against the service, and fake-gcs-server accepts the\n * precondition and ignores it, so CI cannot show it. Set `true` to remove rows for good.\n */\n readonly conditionalDelete?: boolean;\n}\n\n/** The calls {@link ObjectStoreRegistry} needs, in GCS's dialect. Exported for the tests that drive one directly. */\nexport class GcsRegistryStore implements ObjectRegistryStore {\n readonly label = 'GCS';\n private readonly presence: BucketPresence;\n\n constructor(\n private readonly storage: Storage,\n private readonly readStorage: Storage,\n private readonly bucket: string,\n private readonly readTimeoutMs: number,\n readonly conditionalDelete: boolean,\n ) {\n this.presence = new BucketPresence(storage, bucket);\n }\n\n /** A handle on one registry object. */\n private file(name: string) {\n return this.storage.bucket(this.bucket).file(name);\n }\n\n /** The same file handle on the download client, off the SDK's shared agent ({@link downloadFile}). */\n private downloadable(name: string) {\n return downloadFile(this.readStorage, this.bucket, name);\n }\n\n async read(key: string): Promise<ObjectRow | null> {\n // One GET: the response headers carry the `generation` fence and the length, and the body is the same\n // observation of the object, so the pair cannot straddle a concurrent overwrite.\n let res;\n try {\n // One deadline for the read, every attempt included.\n const deadline = startDeadline(this.readTimeoutMs, `registry read of ${key}`);\n res = await retryDownload(\n () =>\n readOnce(\n this.downloadable(key),\n {},\n MAX_ROW_BYTES,\n (size) =>\n new IntegrityError(\n size === undefined\n ? `registry object exceeds cap ${MAX_ROW_BYTES}B`\n : `registry object ${size}B exceeds cap ${MAX_ROW_BYTES}B`,\n ),\n deadline,\n ),\n deadline,\n );\n } catch (err) {\n if (isNotFound(err)) {\n await this.presence.confirm(); // a missing bucket is an error, not an absent row\n return null;\n }\n throw mapReadError(err);\n }\n if (res.status !== 200) {\n throw new IntegrityError(`registry read answered HTTP ${res.status}, not 200: ${key}`);\n }\n // The fence is compared by `ifGenerationMatch` on the next write, so it is parsed here, where a header that\n // is missing or malformed can be refused before it is mistaken for a version.\n const generation = singleHeader(res.headers, 'x-goog-generation');\n if (\n generation === undefined ||\n !/^[1-9]\\d*$/.test(generation) ||\n !Number.isSafeInteger(Number(generation))\n ) {\n throw new IntegrityError(`registry read carries no usable x-goog-generation: ${key}`);\n }\n return { bytes: res.bytes, version: generation };\n }\n\n async write(\n key: string,\n body: Uint8Array,\n expect: 'absent' | { version: string },\n ): Promise<void> {\n try {\n // One request, sent once: a replay of a write that landed would fail its own precondition, and read as a lost\n // race. A registry row is a few hundred bytes, so a resumable session would also carry one round trip's worth\n // of data over two.\n await saveOnce(this.file(key), body, {\n contentType: 'application/json',\n // The checksum goes with the upload, for GCS to check before it stores anything. The SDK's own check runs\n // after the upload instead, and on a mismatch, or an answer that names no checksum, deletes the object by\n // name with no precondition: for a row, that is the live row, another writer's newer one included.\n validation: false,\n metadata: { crc32c: crc32cBase64(body) },\n preconditionOpts: {\n ifGenerationMatch: expect === 'absent' ? 0 : generationFence(expect.version, key),\n },\n });\n } catch (err) {\n if (isPreconditionFailed(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 // The SDK may send this again after a lost response, as it does any request with a precondition. The\n // precondition names one generation, so a second copy removes nothing the first could not; one that meets the\n // first's landed delete is a 404, a conflict, and the registry re-reads and finds the row gone.\n await this.file(key).delete({ ifGenerationMatch: generationFence(expect.version, key) });\n } catch (err) {\n // A 412 (the generation moved on) or a 404 (the object is gone): the version to delete is not there.\n if (isPreconditionFailed(err) || isNotFound(err)) {\n if (isNotFound(err)) await this.presence.confirm();\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 let pageToken: string | undefined;\n do {\n let files;\n let next: { pageToken?: string } | null | undefined;\n try {\n // Explicit paging rather than autoPaginate: a registry can hold far more rows than a segment holds\n // generations, and draining every page into memory first is what `list()` exists to avoid.\n [files, next] = await this.storage\n .bucket(this.bucket)\n .getFiles({ prefix, autoPaginate: false, maxResults: 1000, pageToken });\n } catch (err) {\n throw mapError(err);\n }\n for (const f of files ?? []) yield f.name;\n // A page that hands back the token it was asked with would be asked for again, forever.\n if (next?.pageToken !== undefined && next.pageToken === pageToken) {\n throw new IntegrityError('GCS listing returned the page token it was given');\n }\n pageToken = next?.pageToken;\n } while (pageToken !== undefined);\n }\n}\n\n/**\n * Narrow a version fence back to the number `ifGenerationMatch` takes. The fence always originates as\n * the `x-goog-generation` header in {@link GcsRegistryStore.read}, so this cannot fire in practice \u2014 but `Number()` answers `NaN`\n * for anything non-numeric, and `ifGenerationMatch: NaN` serializes to a precondition GCS ignores. That\n * failure is invisible (writes simply stop being fenced), so it is checked rather than assumed.\n */\nfunction generationFence(version: string, key: string): number {\n const generation = Number(version);\n if (!Number.isSafeInteger(generation) || generation <= 0) {\n throw new IntegrityError(`registry version fence is not a GCS generation: ${key}`);\n }\n return generation;\n}\n\n/** A read's error: a connection that failed or was cut off is transient too, after the driver's retries. */\nfunction mapReadError(err: unknown): unknown {\n err = scrubCredentials(err);\n // Timed out on every attempt: transient, in words that name the read and the timeout.\n if (err instanceof ReadTimedOut) return new TransientError(err.message, { cause: err });\n if (isTransportFault(err)) {\n return new TransientError(`transient GCS fault: ${String((err as { code?: unknown }).code)}`, {\n cause: err,\n });\n }\n return mapError(err);\n}\n\n/** Reclassify a transient GCS 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 GCS fault: ${(err as { code?: unknown } | null)?.code ?? (err as { name?: string } | null)?.name ?? 'unknown'}`,\n { cause: err },\n );\n }\n return err;\n}\n\n/** {@link GcsRegistryDriverOptions.conditionalDelete}, checked, or its default: off. */\nfunction resolveConditionalDelete(value: unknown): boolean {\n if (value === undefined) {\n return false;\n }\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 GcsRegistryDriver extends ObjectStoreRegistry {\n constructor(options: GcsRegistryDriverOptions) {\n super(\n new GcsRegistryStore(\n options.storage,\n options.readStorage ?? options.storage,\n options.bucket,\n resolveReadTimeoutMs(options.readTimeoutMs),\n resolveConditionalDelete(options.conditionalDelete),\n ),\n normalizeObjectPrefix(options.prefix),\n options.now ?? ((): number => Date.now()),\n );\n }\n}\n", "/**\n * CRC32C (Castagnoli) of some bytes, in the form GCS takes in an object's `crc32c` metadata: the 4-byte big-endian\n * value, base64-encoded. Computed here rather than through the SDK so it is the same on every major the driver accepts.\n */\n\nconst TABLE = (() => {\n const table = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0x82f63b78 ^ (c >>> 1) : c >>> 1;\n table[n] = c >>> 0;\n }\n return table;\n})();\n\nexport function crc32cBase64(bytes: Uint8Array): string {\n let crc = 0xffffffff;\n for (const byte of bytes) crc = TABLE[(crc ^ byte) & 0xff]! ^ (crc >>> 8);\n const value = (crc ^ 0xffffffff) >>> 0;\n const out = Buffer.alloc(4);\n out.writeUInt32BE(value);\n return out.toString('base64');\n}\n", "/**\n * The client a GCS backend sends its downloads on: one that never retries a download itself.\n *\n * `@google-cloud/storage` 7.x and 8.x retry a failed download by default, and when the retried request succeeds they\n * throw `ERR_STREAM_UNABLE_TO_PIPE` outside any promise, which ends the process whatever the caller wrote around the\n * call. So a download is sent once, and the driver retries it itself (`download-retry.ts`). The other requests keep the\n * client as it was built: a resumable upload's session, a listing, a delete (the conditional writes are sent once by\n * `send-once.ts` either way).\n *\n * ```\n * the client\n * \u2502\n * \u251C\u2500\u2500 not a Storage client (a test double) \u2500\u2500\u25B6 its retries off? yes: used as it is; no: refused\n * \u251C\u2500\u2500 its `bucket` replaced on the instance (a test stub), or overridden by its class \u2500\u2500\u25B6 refused\n * \u25BC\n * a twin, from the client's own class, with:\n * the same credentials object (one token cache), endpoint, universe, project, user agent, timeout, checksum\n * generator and retry settings but `autoRetry: false`, and one interceptor that applies the client's own\n * interceptors as they are when each request is sent (a later one too); then its own retry settings turned off,\n * over any its class set\n * \u2502\n * \u25BC\n * a second client, with a `bucket` method, the same credentials \u2500\u2500 yes \u2500\u2500\u25B6 downloads use the twin\n * object, retry settings of its own and off, the same URL and endpoint?\n * \u2502 no, or it could not be built\n * \u25BC\n * refused at construction, saying what differed\n * ```\n *\n * A `Storage` client is never used as it is, even one whose retries read off: the SDK turns a client's `autoRetry` off\n * while a delete or an upload with no precondition is in flight and back on when it ends, so that value, read once,\n * does not say how the client was built. The twin's retry settings are its own object, and only reads are sent on it,\n * which never change them; the SDK reads that object on every request.\n *\n * The twin is built from the client's own class, not this package's import: a caller on another major of the SDK hands\n * in a client whose credentials object is another copy's class, which this package's class would wrap in a second one.\n */\nimport { ValidationError } from '@cloudbitmaps/core/driver-kit';\nimport type { Storage, StorageOptions } from '@google-cloud/storage';\nimport { scrubCredentials } from './scrub-error';\n\n/** An interceptor's request hook, as the client's constructor takes one; `getRequestInterceptors` types them `Function`. */\ntype Intercept = NonNullable<StorageOptions['interceptors_']>[number]['request'];\n\n/**\n * A twin of `client` that sends each download once, or `client` itself when it is a test double whose retries are off.\n * `supplied`: the caller handed `client` in, rather than the backend building it.\n */\nexport function downloadClient(client: Storage, supplied: boolean): Storage {\n // A test double has nothing a twin could copy, and is used as it is when it says its retries are off.\n if (!isSdkClient(client)) {\n if (client.retryOptions?.autoRetry === false) return client;\n throw supplied\n ? refusal(\n true,\n 'it is not a `Storage` client (a test double, say)',\n 'Give it `retryOptions: { autoRetry: false }` to have it used as it is',\n )\n : refusal(\n false,\n \"the `Storage` it was built from is not the SDK's (a mocked module, say)\",\n 'Pass the mock as `client`, with `retryOptions: { autoRetry: false }`',\n );\n }\n const instead =\n 'Stub `Storage.prototype.bucket` instead, which reaches that client too, or pass a test double that does not ' +\n 'extend `Storage`, with `retryOptions: { autoRetry: false }`';\n if (Object.hasOwn(client, 'bucket')) {\n throw refusal(\n supplied,\n 'its `bucket` method is replaced on the instance, as by a test stub, which a client built from its class would ' +\n 'not have',\n instead,\n );\n }\n if (overridesBucket(client)) {\n throw refusal(\n supplied,\n 'its class overrides `bucket`, as a test double built on `Storage` does, so a client built from its class need ' +\n 'not hold what this one holds',\n instead,\n );\n }\n const second = supplied\n ? 'a second client built from its class with its settings'\n : 'a second client built from the same settings';\n const remedy = supplied\n ? 'Pass a client its class builds from the options it is given, as `Storage` does, or leave out `client`'\n : undefined;\n let twin: Storage;\n try {\n twin = twinOf(client);\n } catch (err) {\n throw refusal(supplied, `${second} could not be built`, remedy, err);\n }\n const why = unfaithful(client, twin);\n if (why !== undefined) throw refusal(supplied, `${second} ${why}`, remedy);\n return twin;\n}\n\n/**\n * Whether `client` is the SDK's own: its constructor sets `authClient` from the request factory it builds. A mock that\n * answers every property with a function, as an auto-mocking library's does, answers that one too, but not with the\n * same credentials object.\n */\nfunction isSdkClient(client: Storage): boolean {\n const factory: unknown = (client as { makeAuthenticatedRequest?: unknown })\n .makeAuthenticatedRequest;\n return (\n client.authClient !== undefined &&\n typeof factory === 'function' &&\n (factory as { authClient?: unknown }).authClient === client.authClient\n );\n}\n\n/** Whether a subclass of the SDK's client defines `bucket` over the SDK's own. */\nfunction overridesBucket(client: Storage): boolean {\n let defined = 0;\n for (let proto: unknown = Object.getPrototypeOf(client); proto !== null;) {\n if (Object.hasOwn(proto as object, 'bucket')) defined++;\n proto = Object.getPrototypeOf(proto);\n }\n return defined > 1;\n}\n\nfunction twinOf(client: Storage): Storage {\n const Own = client.constructor as new (options: StorageOptions) => Storage;\n const twin = new Own({\n authClient: client.authClient as StorageOptions['authClient'],\n // The SDK treats an endpoint spelled other than its default as a custom one, whose requests go without credentials,\n // and then stores it with any trailing slash taken off. A client given the default in another spelling is custom\n // with the default's URL, which that URL with a slash reproduces.\n apiEndpoint: client.customEndpoint === true ? `${client.apiEndpoint}/` : client.apiEndpoint,\n universeDomain: client.universeDomain,\n projectId: client.projectId,\n ...(client.useAuthWithCustomEndpoint === undefined\n ? {}\n : { useAuthWithCustomEndpoint: client.useAuthWithCustomEndpoint }),\n ...(client.providedUserAgent === undefined ? {} : { userAgent: client.providedUserAgent }),\n ...(client.timeout === undefined ? {} : { timeout: client.timeout }),\n crc32cGenerator: client.crc32cGenerator,\n retryOptions: { ...client.retryOptions, autoRetry: false },\n // A view, not a copy: the client's interceptors as they are when each request is sent.\n interceptors_: [\n {\n request: (options: Parameters<Intercept>[0]) =>\n (client.getRequestInterceptors() as Intercept[]).reduce(\n (sent, intercept) => intercept(sent),\n options as ReturnType<Intercept>,\n ),\n },\n ],\n });\n // A subclass may set retry settings of its own over the ones it is given. Turned off here, as the SDK turns them off\n // for a request it must not retry, on the twin's own settings only: never on an object the client holds too.\n if (\n twin !== client &&\n twin.retryOptions !== undefined &&\n twin.retryOptions !== client.retryOptions\n ) {\n twin.retryOptions.autoRetry = false;\n }\n return twin;\n}\n\n/**\n * What a twin did not take of the settings that decide where and how a download is sent, or `undefined`. A subclass may\n * build from options of its own, and would send downloads with other credentials, with the SDK's retries, or elsewhere.\n * Interceptors are carried as a view and not checked.\n */\nfunction unfaithful(client: Storage, twin: Storage): string | undefined {\n if (twin === client) return 'is the same client';\n if (typeof twin.bucket !== 'function') return 'has no `bucket` method';\n if (twin.authClient === undefined || twin.authClient !== client.authClient) {\n return 'would use other credentials';\n }\n if (twin.retryOptions === client.retryOptions) return 'would share its retry settings';\n if (twin.retryOptions?.autoRetry !== false) return \"keeps the SDK's retries\";\n // `baseUrl` prefers STORAGE_EMULATOR_HOST over `apiEndpoint` as each client is built.\n if (twin.baseUrl !== client.baseUrl) {\n return 'would send to another URL, as when `STORAGE_EMULATOR_HOST` changed after the client was built or is not a URL';\n }\n if (twin.apiEndpoint !== client.apiEndpoint) return 'would name another endpoint';\n if (twin.customEndpoint !== client.customEndpoint) {\n return 'would decide otherwise whether requests carry credentials';\n }\n return undefined;\n}\n\nfunction refusal(\n supplied: boolean,\n why: string,\n remedy: string | undefined,\n cause?: unknown,\n): ValidationError {\n const err = new ValidationError(\n (supplied\n ? `GcsStorage cannot send the supplied client's downloads without the SDK's retries: ${why}`\n : `GcsStorage cannot build a client that sends downloads without the SDK's retries: ${why}`) +\n (remedy === undefined ? '' : `. ${remedy}`),\n );\n // The credential-free copy, never the SDK's error as raised.\n if (cause !== undefined) err.cause = scrubCredentials(cause);\n return err;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * `GcsStorage` \u2014 the Google Cloud Storage backend as one object: generations and pointer, one bucket, stated once.\n *\n * See {@link StorageBackend} for why the two halves are configured together rather than separately.\n *\n * **A note on the word.** `@google-cloud/storage` names its own client class `Storage`, so the driver option\n * that takes it is also called `storage` \u2014 which, since this library calls its durable tier storage too, reads\n * as the same word meaning two things. That is why this class builds the client itself: `new GcsStorage({ bucket })`\n * never makes you write `{ storage: storage }`. Supply `client` when you need your own.\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 { Storage as GcsClient } from '@google-cloud/storage';\nimport { GcsStorageDriver } from './storage';\nimport { GcsRegistryDriver } from './registry';\nimport { downloadClient } from './read-client';\n\nexport interface GcsStorageOptions {\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional object-name prefix under which everything lives \u2014 generations and the registry alike. */\n readonly prefix?: string;\n /**\n * A constructed `@google-cloud/storage` client. One is built from the ambient credentials when absent. Its retry\n * options apply to every request except the single-request conditional writes, which the driver sends without\n * that retry whatever they say (a registry row once, and a generation's object again only after a throttle, under\n * its own backoff), and the downloads. In `@google-cloud/storage` 7.x and 8.x a download the SDK retries after a 408,\n * 429, 500, 502, 503 or 504 can crash the process with `ERR_STREAM_UNABLE_TO_PIPE`, so downloads go through a twin of\n * this client, built from its own class with the same credentials object, endpoint and settings and the SDK's retries\n * off over any its class set, and the driver retries them itself. A client whose twin would use other credentials or\n * another endpoint (a subclass that builds from options of its own), whose class overrides `bucket` (a test double\n * built on `Storage`), or whose `bucket` is replaced on the instance (a test stub; stub `Storage.prototype` instead)\n * is refused at construction. A test double that is not a `Storage` client is used as it is when it has\n * `retryOptions: { autoRetry: false }`, and refused without it. What is set on the client after the backend was\n * built, other than an interceptor, is not carried to the twin.\n */\n readonly client?: GcsClient;\n /** Project id for the client built when `client` is absent (refused beside `client`). Falls back to the SDK's own resolution. */\n readonly projectId?: string;\n /** Endpoint override \u2014 point it at fake-gcs-server locally. Refused beside `client`, which carries its own. */\n readonly apiEndpoint?: string;\n /** Largest object the backend will write and advertise (default = GCS's 5 TiB max). Must be a positive safe integer. */\n readonly maxObjectBytes?: number;\n /** Bytes at/under which a single non-resumable upload is used instead of a resumable stream (default 8 MiB).\n * Must be a positive safe integer. */\n readonly simpleUploadThresholdBytes?: number;\n /** Injected clock for the registry's `createdAt`/`updatedAt`; defaults to `Date.now`. */\n readonly now?: () => number;\n /**\n * Cut off a read that has run this long, in milliseconds. `0`, the default, sets no timeout. A client's own `timeout`\n * does not bound a download on `@google-cloud/storage` 8.x (the SDK hands it to an HTTP client that has no such\n * option), so this is what does.\n *\n * It bounds each read as a whole \u2014 a generation's tail (with the metadata read it falls back on for an empty\n * object), a range of it, a registry row \u2014 with one deadline across every attempt the driver makes and the backoff\n * between them. The clock starts at the call into the driver, so a credential fetch and any wait for a socket count\n * (Node's agents set no socket limit unless your process sets one), and runs until the whole body has arrived. It\n * counts time the process spends busy too: Node runs a due timer before it reads a socket, so a synchronous stretch\n * longer than the timeout fails the reads in flight even when their responses have arrived. When it passes, the read\n * throws `TransientError` naming the read and the timeout and no further attempt starts, for the store's read retry\n * to run again: about 8.35 s in all at `2_000` with the default retry policy. Uploads, deletes, listings and the\n * conditional writes are not timed. The SDK cannot cancel a request whose response has not begun, so a read timed\n * out before its server answers leaves that connection open until the server answers or closes it: one per read, up\n * to four per call through the store's retry. A non-negative safe integer no larger than 2,147,483,647.\n */\n readonly readTimeoutMs?: number;\n /**\n * Whether the service applies `ifGenerationMatch` on an object delete, which both halves rely on when it is set: the\n * registry removes a deleted row for good rather than leaving a tombstone a full listing reads forever, and the\n * storage half removes an erasure's holder only while it is the object the erasure searched, by its generation.\n * Defaults to `false`, on the public endpoint too: whether real GCS applies `ifGenerationMatch` to a delete has not\n * been verified by a run against the service, and fake-gcs-server accepts the precondition and ignores it, so CI\n * cannot show it. Set `true` once you know it does. Until then, the window in which an erasure's stalled delete\n * takes a generation a load put under the number since stays open on GCS.\n */\n readonly conditionalDelete?: boolean;\n}\n\n/**\n * The keys `new GcsStorage(options)` takes. Any other is refused by name rather than ignored: an ignored client key\n * \u2014 `storage`, as the lower-level storage driver calls it \u2014 falls back to ambient credentials and the **public** endpoint, and\n * for a client pointed at an emulator that is production traffic from a wiring typo.\n */\nexport const GCS_STORAGE_OPTION_KEYS = [\n 'bucket',\n 'prefix',\n 'client',\n 'projectId',\n 'apiEndpoint',\n 'maxObjectBytes',\n 'simpleUploadThresholdBytes',\n 'now',\n 'readTimeoutMs',\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 = ['projectId', 'apiEndpoint'] as const;\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 GcsStorage 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: GcsClient;\n\n constructor(options: GcsStorageOptions) {\n let readClient: GcsClient;\n refuseUnknown(\n 'GcsStorage',\n options,\n GCS_STORAGE_OPTION_KEYS,\n 'a @google-cloud/storage client goes in `client`',\n );\n if (typeof options.bucket !== 'string' || options.bucket.length === 0) {\n throw new ValidationError('GcsStorage needs a `bucket`: the name of one, a non-empty string');\n }\n if (options.now !== undefined && typeof options.now !== 'function') {\n throw new ValidationError(\n \"GcsStorage's `now` must be a function that returns epoch milliseconds\",\n );\n }\n if (options.client !== undefined && options.client !== null) {\n // A supplied client already carries its project and endpoint, so a setting beside it is ignored, and\n // ignoring it leaves the store talking to somewhere the caller did not mean: an `apiEndpoint` meant for an\n // emulator, silently dropped, is production traffic from a client that was built for the public endpoint.\n // 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 `GcsStorage 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 readClient = downloadClient(options.client, true);\n } else {\n const settings = {\n ...(options.projectId === undefined ? {} : { projectId: options.projectId }),\n ...(options.apiEndpoint === undefined ? {} : { apiEndpoint: options.apiEndpoint }),\n };\n this.client = new GcsClient(settings);\n // Downloads go through a twin that sends each once (see `read-client.ts`); it shares this client's credentials\n // object, so one token fetch and one hourly refresh serve both.\n readClient = downloadClient(this.client, false);\n }\n const shared = {\n storage: this.client,\n readStorage: readClient,\n bucket: options.bucket,\n ...(options.prefix === undefined ? {} : { prefix: options.prefix }),\n ...(options.readTimeoutMs === undefined ? {} : { readTimeoutMs: options.readTimeoutMs }),\n };\n this.storage = new GcsStorageDriver({\n ...shared,\n ...(options.maxObjectBytes === undefined ? {} : { maxObjectBytes: options.maxObjectBytes }),\n ...(options.simpleUploadThresholdBytes === undefined\n ? {}\n : { simpleUploadThresholdBytes: options.simpleUploadThresholdBytes }),\n ...(options.conditionalDelete === undefined\n ? {}\n : { conditionalDelete: options.conditionalDelete }),\n });\n this.registry = new GcsRegistryDriver({\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 * `GcsStorageDriver` \u2014 an {@link IStorageDriver} over Google Cloud Storage.\n *\n * Uses the official `@google-cloud/storage`, a real dependency of this package \u2014 installing\n * `@cloudbitmaps/gcs` is what installs it. The `Storage` client is **injected** (dependency injection): the driver owns\n * no credential/project/endpoint logic, so it's thin, reuses the caller's client, and is testable against the\n * `fake-gcs-server` emulator (point a `Storage` at its `apiEndpoint`).\n *\n * Generations are write-once immutable objects: a resumable upload with the **`ifGenerationMatch: 0`**\n * precondition (\"create only if it does not exist\") makes the publish atomic \u2014 a second write to the same\n * object fails with 412 \u2192 {@link WriteConflictError}, never a silent overwrite (hard invariant 2: storage\n * objects are immutable and never overwritten in place), the GCS analogue of S3's `If-None-Match: *` and\n * LocalFs's atomic `link`. **Writes stream in constant memory:** the codec's bytes are\n * piped into a GCS resumable-upload `Writable` (chunked/freed by the SDK as they go), so a load's write\n * footprint stays bounded regardless of segment size, up to the advertised `maxObjectBytes`. Every object carries a\n * random write id in its custom metadata (`cbwid`), so a `412` that may be the write meeting itself can be read back:\n * a resumable upload's session is retried by the SDK, and a single-request upload is sent again by the driver after a\n * `429` or `503`, at most {@link THROTTLE_RESENDS} times with backoff, and after nothing else. Drivers may use\n * `node:crypto`; only `core/` is bound by the determinism lint.\n *\n * **A delete given `ifVersion` is sent with `ifGenerationMatch: <that object generation>`** when `conditionalDelete` is\n * set, which it is not by default, as for the registry: no run against real GCS has shown the service applies the\n * precondition to a delete, and fake-gcs-server accepts it and ignores it. A tail read reports the object's generation\n * as its version, from the `x-goog-generation` header of the GET that carried the bytes, or the metadata when none were\n * asked for. A `412` is the precondition failing, and is reported as {@link WriteConflictError} unless the metadata then\n * shows no object under the name: an absent object is a no-op, whether the service answers it `404` or `412`. The\n * delete keeps the SDK's retry, which re-sends a request with a precondition: a copy that meets its own landed delete\n * finds nothing, and one that meets an object stored since is refused.\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 StorageDeleteOptions,\n TailRead,\n} from '@cloudbitmaps/core/driver-kit';\nimport { createHash, randomBytes, type Hash } from 'node:crypto';\nimport type { Writable } from 'node:stream';\nimport { once } from 'node:events';\nimport type { Storage } from '@google-cloud/storage';\nimport { BucketPresence } from './bucket-presence';\nimport {\n storageObjectName,\n normalizeGcsPrefix,\n parseGenerationFromName,\n segmentObjectPrefix,\n} from './keys';\nimport {\n isInvalidRange,\n isNotFound,\n isPreconditionFailed,\n isThrottle,\n isTransient,\n isTransportFault,\n} from './gcs-errors';\nimport { retryDownload } from './download-retry';\nimport { scrubCredentials } from './scrub-error';\nimport { saveOnce } from './send-once';\nimport { downloadFile, readOnce, singleHeader, type ObjectRead } from './read-once';\nimport {\n ReadTimedOut,\n resolveReadTimeoutMs,\n startDeadline,\n withDeadline,\n type Deadline,\n} from './read-timeout';\n\n/** Default object ceiling: GCS's 5 TiB per-object hard max. Set lower to fail fast on a runaway write. */\nconst DEFAULT_MAX_OBJECT_BYTES = 5 * 1024 * 1024 * 1024 * 1024;\n\n/** The custom-metadata name an object's write id is stored under. Short: it travels with every write. */\nconst WRITE_ID_KEY = 'cbwid';\n/** How many times a throttled single-request upload is sent again: four sends in all, 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. */\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\n/**\n * Objects at/under this size are uploaded in a **single simple (non-resumable) request**; larger ones switch to\n * a **resumable stream** (constant memory). Mirrors S3's single-PUT-vs-multipart split \u2014 the simple path buffers\n * only \u2264 this many bytes, the resumable path streams. Default 8 MiB (\u2248 S3's part size), so peak write memory is\n * ~one threshold's worth regardless of object size.\n */\nconst DEFAULT_UPLOAD_THRESHOLD_BYTES = 8 * 1024 * 1024;\n\nexport interface GcsStorageDriverOptions {\n /** A constructed `@google-cloud/storage` `Storage` client (point `apiEndpoint` at fake-gcs-server locally). */\n readonly storage: Storage;\n /**\n * The client the downloads go through; `storage` when absent. `GcsStorage` passes one built with the SDK's request\n * retries off (the read client built in its constructor), because a download the SDK retries can crash the process;\n * the driver retries a download itself instead.\n */\n readonly readStorage?: Storage;\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional object-name prefix under which all objects live (e.g. `cloudbitmaps/`). */\n readonly prefix?: string;\n /** Largest object this driver will write/advertise (default = GCS's 5 TiB max). Must be a positive safe integer. */\n readonly maxObjectBytes?: number;\n /** Bytes at/under which a single non-resumable upload is used instead of a resumable stream (default 8 MiB).\n * Must be a positive safe integer. */\n readonly simpleUploadThresholdBytes?: number;\n /**\n * Cut off a read that has run this long, in milliseconds: a tail read (with the metadata read it falls back on for\n * an empty object) or a range read, every attempt and the backoff between them included. `0` (the default) sets no\n * timeout. The clock starts at the call into the driver, so a credential fetch counts, and runs until the body has\n * ended; when it passes the read throws `TransientError` and no further attempt starts. Uploads, deletes and\n * listings are not timed. A non-negative safe integer no larger than 2,147,483,647.\n */\n readonly readTimeoutMs?: number;\n /** What the backoff before re-sending a throttled upload waits on; real time when absent. */\n readonly clock?: Sleeper;\n /**\n * Whether a delete given `ifVersion` is sent with `ifGenerationMatch`, so it removes the object only while it is the\n * one that generation names. Defaults to `false`, on the public endpoint too, as the registry's option does: whether\n * real GCS applies `ifGenerationMatch` to a delete has not been verified by a run against the service, and\n * fake-gcs-server accepts the precondition and ignores it. Set `true` once you have. Until it is set, a delete\n * decided from one read of an object removes whatever is under the name by then: on GCS the window in which an\n * erasure's stalled delete takes a generation a load put under the number since stays open.\n */\n readonly conditionalDelete?: boolean;\n}\n\nexport class GcsStorageDriver implements IStorageDriver {\n private readonly storage: Storage;\n private readonly readStorage: Storage;\n private readonly bucket: string;\n private readonly prefix: string | undefined;\n private readonly maxObjectBytes: number;\n private readonly threshold: number;\n private readonly readTimeoutMs: number;\n private readonly clock: Sleeper;\n private readonly presence: BucketPresence;\n private readonly conditionalDelete: boolean;\n\n constructor(options: GcsStorageDriverOptions) {\n this.storage = options.storage;\n if (options.conditionalDelete !== undefined && typeof options.conditionalDelete !== 'boolean') {\n throw new ValidationError(\n `conditionalDelete must be a boolean; got ${String(options.conditionalDelete)}`,\n );\n }\n this.conditionalDelete = options.conditionalDelete ?? false;\n this.clock = options.clock ?? REAL_TIME;\n this.readStorage = options.readStorage ?? options.storage;\n this.bucket = options.bucket;\n this.prefix = normalizeGcsPrefix(options.prefix);\n // Fail fast at the boundary: `??` only guards `undefined`, so NaN, 0, a negative or a fraction would otherwise\n // reject every write (cap) or corrupt the upload-path choice (threshold).\n for (const [name, value] of [\n ['maxObjectBytes', options.maxObjectBytes],\n ['simpleUploadThresholdBytes', options.simpleUploadThresholdBytes],\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 // GCS's own limit on one object: a cap past it is advertised and never reachable.\n if (options.maxObjectBytes !== undefined && options.maxObjectBytes > 5 * 1024 ** 4) {\n throw new ValidationError(\n `maxObjectBytes must be at most GCS's 5 TiB object limit (${5 * 1024 ** 4}); got ${options.maxObjectBytes}`,\n );\n }\n this.maxObjectBytes = options.maxObjectBytes ?? DEFAULT_MAX_OBJECT_BYTES;\n this.threshold = options.simpleUploadThresholdBytes ?? DEFAULT_UPLOAD_THRESHOLD_BYTES;\n this.readTimeoutMs = resolveReadTimeoutMs(options.readTimeoutMs);\n this.presence = new BucketPresence(this.storage, this.bucket);\n }\n\n capabilities(): StorageCaps {\n return {\n rangeRead: true,\n maxObjectBytes: this.maxObjectBytes,\n conditionalPut: true,\n conditionalDelete: this.conditionalDelete,\n };\n }\n\n private file(name: string) {\n return this.storage.bucket(this.bucket).file(name);\n }\n\n /** The same object on the download client, off the SDK's shared agent ({@link downloadFile}). */\n private downloadable(name: string) {\n return downloadFile(this.readStorage, this.bucket, name);\n }\n\n /** The deadline a read of `key` made now runs under, every attempt included, named for its error; none when off. */\n private deadline(read: string, key: GenKey): Deadline | undefined {\n return startDeadline(this.readTimeoutMs, `${read} of ${key.segment}.${key.generation}`);\n }\n\n async putImmutable(\n key: GenKey,\n write: (sink: BlobSink) => Promise<void>,\n ): Promise<{ size: number; sha256: string }> {\n const objectName = storageObjectName(this.prefix, key); // validates ref + generation\n const sink = new GcsUploadSink(\n this.file(objectName),\n this.maxObjectBytes,\n this.threshold,\n this.clock,\n );\n try {\n await write(sink);\n return await sink.finish();\n } catch (err) {\n await sink.abort(); // best-effort teardown of the in-flight upload\n // A lost write-once race \u2014 the object already existed, so `ifGenerationMatch: 0` failed with 412.\n if (isPreconditionFailed(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)) throw err;\n throw this.mapError(err);\n }\n }\n\n async getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array> {\n if (!Number.isInteger(offset) || !Number.isInteger(length) || offset < 0 || length < 0) {\n throw new ValidationError(`invalid range offset=${offset} length=${length}`);\n }\n if (length === 0) return new Uint8Array(0);\n const objectName = storageObjectName(this.prefix, key);\n try {\n // One request, buffering at most the bytes asked for. GCS `end` is inclusive. `decompress: false`, as for the tail:\n // the offsets are into the bytes as stored.\n const deadline = this.deadline('range read', key);\n const res = await retryDownload(\n () =>\n readOnce(\n this.downloadable(objectName),\n { start: offset, end: offset + length - 1, decompress: false },\n length,\n () => this.badRange(key, `the response is longer than the ${length}B requested`),\n deadline,\n ),\n deadline,\n );\n this.checkRange(res, key, offset, length);\n return res.bytes;\n } catch (err) {\n throw await this.readFailure(err, key);\n }\n }\n\n /**\n * Refuse a range response unless it is exactly the bytes asked for. A 206 must name them in `Content-Range: bytes\n * a-b/total`, starting at `offset`, and hold that many; a 200 (a server that ignored the range) is the whole object,\n * which is the range only when the range starts at 0. A short read means the range ran past the end of the object:\n * out of bounds, never a partial result.\n */\n private checkRange(res: ObjectRead, key: GenKey, offset: number, length: number): void {\n const received = res.bytes.length;\n if (res.status === 200) {\n if (offset !== 0)\n throw this.badRange(key, `whole object returned for a range from ${offset}`);\n } else if (res.status === 206) {\n const header = singleHeader(res.headers, 'content-range');\n const m = header === undefined ? null : /^bytes (\\d+)-(\\d+)\\/(\\d+)$/.exec(header);\n if (m === null) throw this.badRange(key, 'Content-Range is missing or malformed');\n const [first, last, total] = [Number(m[1]), Number(m[2]), Number(m[3])];\n if (![first, last, total].every(Number.isSafeInteger)) {\n throw this.badRange(key, 'Content-Range holds a number that is not a safe integer');\n }\n if (first !== offset || last - first + 1 !== received || last >= total) {\n throw this.badRange(\n key,\n `Content-Range ${first}-${last}/${total} disagrees with ${received}B received from ${offset}`,\n );\n }\n } else {\n throw this.badRange(key, `unexpected HTTP ${res.status}`);\n }\n if (received !== length) {\n throw new ValidationError(\n `range [${offset}, ${offset + length}) out of bounds (got ${received}B)`,\n );\n }\n }\n\n private badRange(key: GenKey, why: string): ValidationError {\n return new ValidationError(\n `GCS range read of ${key.segment}.${key.generation} refused: ${why}`,\n );\n }\n\n async getTail(key: GenKey, maxBytes: number): Promise<TailRead> {\n const objectName = storageObjectName(this.prefix, key);\n // One deadline for the whole call: the tail's attempts, and the metadata read it may fall back on.\n const deadline = this.deadline('tail read', key);\n try {\n // Nothing to read: only the size is wanted, which the metadata answers in one request.\n if (maxBytes <= 0) {\n const meta = await this.metadataOf(objectName, deadline);\n return withVersion({ bytes: new Uint8Array(0), size: meta.size }, meta.version);\n }\n if (!Number.isSafeInteger(maxBytes)) {\n throw new ValidationError(`invalid tail length ${maxBytes}`);\n }\n // One request: a suffix range (`Range: bytes=-N`, \"the last N bytes\") answers with the tail and, in\n // `Content-Range: bytes a-b/total`, the object's size. An object shorter than N comes back whole.\n let res;\n try {\n res = await retryDownload(\n () =>\n readOnce(\n this.downloadable(objectName),\n { end: -maxBytes, decompress: false },\n maxBytes,\n () => this.badTail(key, `the response is longer than the ${maxBytes}B requested`),\n deadline,\n ),\n deadline,\n );\n } catch (err) {\n // A zero-byte object has no suffix to satisfy, and a server may refuse the range with a 416. The metadata\n // settles whether that is an empty object (a valid, empty tail) or a real range fault.\n if (!isInvalidRange(err)) throw err;\n const meta = await this.metadataOf(objectName, deadline);\n if (meta.size !== 0) throw err;\n return withVersion({ bytes: new Uint8Array(0), size: 0 }, meta.version);\n }\n if (res.bytes.length === 0) {\n // No bytes came back for a positive request: only an empty object may do that.\n const meta = await this.metadataOf(objectName, deadline);\n if (meta.size !== 0) {\n throw this.badTail(key, `no bytes returned for an object of ${meta.size}B`);\n }\n return withVersion({ bytes: res.bytes, size: 0 }, meta.version);\n }\n // The version of the object the bytes came from: the generation the GET that carried them names.\n const size = this.tailSize(res, maxBytes, key);\n return withVersion(\n { bytes: res.bytes, size },\n objectGeneration(singleHeader(res.headers, 'x-goog-generation')),\n );\n } catch (err) {\n throw await this.readFailure(err, key);\n }\n }\n\n /**\n * The object's size from its metadata, validated, and its generation when the metadata names a usable one. Read on the\n * download client and retried as a download is, within the calling read's `deadline` ({@link withDeadline}), so\n * nothing but the one request in flight runs past it.\n */\n private async metadataOf(\n objectName: string,\n deadline: Deadline | undefined,\n ): Promise<{ size: number; version: string | undefined }> {\n const [meta] = await retryDownload(\n () => withDeadline(() => this.downloadable(objectName).getMetadata(), deadline),\n deadline,\n );\n const size = Number(meta.size ?? 0);\n if (!Number.isSafeInteger(size) || size < 0) {\n throw new ValidationError(`GCS returned an invalid object size: ${String(meta.size)}`);\n }\n return {\n size,\n version: objectGeneration(\n meta.generation === undefined ? undefined : String(meta.generation),\n ),\n };\n }\n\n /**\n * The object's total size from a tail response, refused unless every header agrees with the bytes received. A 206\n * must carry `Content-Range: bytes a-b/total` that names exactly the suffix asked for and exactly the bytes held;\n * a 200 (a server that ignored the range) is the whole object, and is accepted only when it fits in the request.\n */\n private tailSize(res: ObjectRead, maxBytes: number, key: GenKey): number {\n const received = res.bytes.length;\n if (res.status === 200) {\n if (received > maxBytes)\n throw this.badTail(key, 'whole object returned past the requested length');\n return received;\n }\n if (res.status !== 206) throw this.badTail(key, `unexpected HTTP ${res.status}`);\n const header = singleHeader(res.headers, 'content-range');\n const m = header === undefined ? null : /^bytes (\\d+)-(\\d+)\\/(\\d+)$/.exec(header);\n if (m === null) throw this.badTail(key, `Content-Range is missing or malformed`);\n const [first, last, total] = [Number(m[1]), Number(m[2]), Number(m[3])];\n if (![first, last, total].every(Number.isSafeInteger)) {\n throw this.badTail(key, 'Content-Range holds a number that is not a safe integer');\n }\n if (\n last !== total - 1 ||\n first !== Math.max(0, total - maxBytes) ||\n last - first + 1 !== received\n ) {\n throw this.badTail(\n key,\n `Content-Range ${first}-${last}/${total} disagrees with ${received}B received for a ${maxBytes}B tail`,\n );\n }\n return total;\n }\n\n private badTail(key: GenKey, why: string): ValidationError {\n return new ValidationError(`GCS tail read of ${key.segment}.${key.generation} refused: ${why}`);\n }\n\n async delete(key: GenKey, options?: StorageDeleteOptions): Promise<void> {\n const objectName = storageObjectName(this.prefix, key);\n const ifVersion = options?.ifVersion;\n const conditional = ifVersion !== undefined && this.conditionalDelete;\n // Idempotent: a racing/retried GC sweep of an absent object is a no-op. Not the SDK's `ignoreNotFound`, which takes\n // every 404 and so would report a delete in a missing bucket as done.\n try {\n await this.file(objectName).delete(\n ...(conditional ? [{ ifGenerationMatch: generationOf(ifVersion, key) }] : []),\n );\n } catch (err) {\n if (isNotFound(err)) return this.presence.confirm();\n // A failed precondition is a conflict only while an object is under the name: a service may answer it for a name\n // with nothing there, a copy of this delete that landed first included.\n if (conditional && isPreconditionFailed(err)) {\n if (!(await this.exists(objectName))) return;\n throw new WriteConflictError(\n `generation ${key.segment}.${key.generation} is another object than the version given; not deleted`,\n );\n }\n throw this.mapError(err);\n }\n }\n\n /** Whether an object is under the name, from its metadata. A read that fails in any other way throws. */\n private async exists(objectName: string): Promise<boolean> {\n try {\n await this.downloadable(objectName).getMetadata();\n return true;\n } catch (err) {\n if (isNotFound(err)) {\n await this.presence.confirm();\n return false;\n }\n throw this.mapError(scrubCredentials(err));\n }\n }\n\n async *list(ref: SegmentRef): AsyncIterable<GenKey> {\n const prefix = segmentObjectPrefix(this.prefix, ref); // validates ref\n let files;\n try {\n // autoPaginate (default) drains every page; a segment has few generations, so the set is small.\n [files] = await this.storage.bucket(this.bucket).getFiles({ prefix });\n } catch (err) {\n throw this.mapError(err);\n }\n for (const f of files) {\n const generation = parseGenerationFromName(prefix, f.name);\n if (generation !== null) {\n yield { namespace: ref.namespace, segment: ref.segment, generation };\n }\n }\n }\n\n /** A read's error, once a 404 is known to be the object's and not the bucket's ({@link BucketPresence}). */\n private async readFailure(err: unknown, key: GenKey): Promise<unknown> {\n if (isNotFound(err)) await this.presence.confirm();\n return this.mapReadError(err, key);\n }\n\n /** Map GCS 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 // 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 // Timed out on every attempt: transient, in words that name the read and the timeout.\n if (err instanceof ReadTimedOut) return new TransientError(err.message, { cause: err });\n // A connection that failed or was cut off, after the driver's retries: transient, whichever code Node gave it.\n if (isTransportFault(err)) {\n return new TransientError(\n `transient GCS fault: ${String((err as { code?: unknown }).code)}`,\n {\n cause: err,\n },\n );\n }\n return this.mapError(err);\n }\n\n /**\n * Reclassify a transient GCS 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-call site: an HTTP status and a fault\n * in transit arrive typed, and anything else (a checksum mismatch, a credentials failure) as the SDK raised it.\n */\n private mapError(err: unknown): unknown {\n err = scrubCredentials(err);\n if (isTransient(err)) {\n return new TransientError(\n `transient GCS fault: ${(err as { code?: unknown } | null)?.code ?? 'unknown'}`,\n { cause: err },\n );\n }\n return err;\n }\n}\n\ntype GcsFile = ReturnType<ReturnType<Storage['bucket']>['file']>;\n\n/** A tail read, with the version it was read at when there is one. */\nfunction withVersion(\n tail: { bytes: Uint8Array; size: number },\n version: string | undefined,\n): TailRead {\n return version === undefined ? tail : { ...tail, version };\n}\n\n/**\n * An object generation as GCS spells it, a positive decimal integer, or `undefined` for anything else: the version a\n * delete is conditioned on goes back to the service as a number, so one that does not parse as one is never reported.\n */\nfunction objectGeneration(value: string | undefined): string | undefined {\n return value !== undefined && /^[1-9]\\d*$/.test(value) && Number.isSafeInteger(Number(value))\n ? value\n : undefined;\n}\n\n/** The generation `ifGenerationMatch` takes, from a version this driver reported; any other is refused. */\nfunction generationOf(version: string, key: GenKey): number {\n if (objectGeneration(version) === undefined) {\n throw new ValidationError(\n `ifVersion for ${key.segment}.${key.generation} is not a GCS object generation: ${version}`,\n );\n }\n return Number(version);\n}\n\n/** Concatenate 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 * {@link BlobSink} that uploads one GCS object write-once. It **buffers up to a threshold**: an object that\n * finishes at/under it is committed in a **single simple (non-resumable) request** ({@link saveOnce}) \u2014 the write-once\n * path both real GCS and `fake-gcs-server` enforce; a larger one switches to a **resumable stream**, flushing the\n * buffer then piping the rest in constant memory (peak \u2248 one threshold). SHA-256 is hashed incrementally. Both\n * paths carry `ifGenerationMatch: 0` (create-only-if-absent) \u2192 a conflict is a 412, mapped by the driver to\n * {@link WriteConflictError}. On error the caller invokes {@link abort}.\n *\n * **The simple upload is sent with no SDK retry** ({@link saveOnce}): a replay of one that landed and lost its response\n * would find its own object and read as a lost race, so a lost response or a timeout throws {@link TransientError}. A\n * `429` or `503` is the one answer it is sent again after, by {@link sendSimple}. The resumable upload is a session of\n * requests, which the SDK retries within by asking the session how much it holds; the SDK takes that retry from the\n * client's options, not the call's, so it stays on. Each upload, of either kind, tags its object with a random id in\n * custom metadata, and a `412` that may be the upload meeting itself is read back: an object that carries this\n * upload's id is a success, and any other is the lost race. The read is paid only on that `412`.\n */\nclass GcsUploadSink implements BlobSink {\n private readonly hash: Hash = createHash('sha256');\n private total = 0;\n private readonly buffered: Uint8Array[] = [];\n private bufferedLen = 0;\n private stream: Writable | undefined; // set once we cross the threshold \u2192 resumable mode\n private done: Promise<unknown[]> | undefined;\n private failure: unknown;\n\n constructor(\n private readonly file: GcsFile,\n private readonly maxObjectBytes: number,\n private readonly threshold: number,\n private readonly clock: Sleeper,\n ) {}\n\n private static readonly WRITE_OPTS = {\n preconditionOpts: { ifGenerationMatch: 0 as const }, // write-once: create only if absent\n metadata: { contentType: 'application/octet-stream' },\n };\n\n /**\n * This write's own id, stored in the object's custom metadata (outside the `.crbm` bytes) so a conflict can be told\n * from a replay of this same write: the resumable session's, or the simple upload's re-send after a throttle.\n */\n private readonly writeId = randomBytes(16).toString('hex');\n\n /** The upload options with this write's id in the object's custom metadata. */\n private tagged(): typeof GcsUploadSink.WRITE_OPTS & {\n metadata: { metadata: Record<string, string> };\n } {\n return {\n ...GcsUploadSink.WRITE_OPTS,\n metadata: {\n ...GcsUploadSink.WRITE_OPTS.metadata,\n metadata: { [WRITE_ID_KEY]: this.writeId },\n },\n };\n }\n\n async write(bytes: Uint8Array): Promise<void> {\n if (this.failure !== undefined) throw this.failure;\n if (bytes.length === 0) return;\n this.total += bytes.length;\n if (this.total > this.maxObjectBytes) {\n throw new ValidationError(`object exceeds maxObjectBytes ${this.maxObjectBytes}`);\n }\n this.hash.update(bytes);\n if (this.stream !== undefined) {\n // Resumable mode: honor backpressure. `events.once(stream,'drain')` REJECTS if the stream emits 'error'\n // while we wait (that's its documented behavior for any awaited event except 'error'), and auto-removes\n // both listeners on settle \u2014 so there's no listener accumulation across drain cycles.\n // A copy, here and below: the caller may reuse its buffer once this write resolves, and the stream or the\n // buffered upload still holds what it was given.\n if (!this.stream.write(Buffer.from(bytes))) await once(this.stream, 'drain');\n return;\n }\n this.buffered.push(new Uint8Array(bytes));\n this.bufferedLen += bytes.length;\n if (this.bufferedLen > this.threshold) this.startResumable();\n }\n\n /** Cross into resumable streaming: open the stream, flush the buffered bytes, keep only ~one threshold resident. */\n private startResumable(): void {\n const stream = this.file.createWriteStream({ resumable: true, ...this.tagged() });\n this.stream = stream;\n this.done = once(stream, 'finish'); // resolves on a clean commit; rejects on 'error' (e.g. 412)\n stream.on('error', (e: unknown) => {\n this.failure ??= e; // a persistent listener so a stray 'error' is never an unhandled 'error' event\n });\n this.done.catch(() => undefined); // finish() awaits this; guard against unhandled rejection until then\n const buf = concatBytes(this.buffered, this.bufferedLen);\n this.buffered.length = 0;\n this.bufferedLen = 0;\n stream.write(buf); // \u2264 threshold+one-chunk; backpressure is absorbed by the next write()'s drain-await / end()\n }\n\n async finish(): Promise<{ size: number; sha256: string }> {\n const sha256 = this.hash.digest('hex');\n if (this.stream === undefined) {\n // Small object: one simple (non-resumable) upload \u2014 write-once enforced everywhere, the emulator too.\n await this.sendSimple(concatBytes(this.buffered, this.bufferedLen));\n return { size: this.total, sha256 };\n }\n try {\n if (this.failure !== undefined) throw this.failure;\n this.stream.end();\n await this.done; // rejects if the commit fails (e.g. 412 write-once conflict)\n } catch (err) {\n // The SDK retries a resumable session, so a commit that landed and lost its response can be answered `412` by\n // its own replay. Nothing overwrites a write-once object, so the id it holds says who wrote it. A failed\n // read-back throws, and reaches the caller as `TransientError` when the fault is transient: neither a success\n // nor a conflict.\n if (!isPreconditionFailed(err) || (await this.storedWriteId()) !== this.writeId) throw err;\n }\n return { size: this.total, sha256 };\n }\n\n /**\n * Send the simple upload once, and again after a `429` or `503`, up to {@link THROTTLE_RESENDS} times, waiting a\n * full-jitter backoff before each. Any other failure is thrown at once. Once it has been sent again, a `412` can be\n * an earlier send that landed after all, so the object's write id decides: this write's own is a success, and any\n * other object, or one with no id, is the conflict. A failed read-back throws, and is neither. Exhausted, it throws\n * {@link TransientError}; nothing is deleted.\n */\n private async sendSimple(body: Uint8Array): Promise<void> {\n for (let resent = 0; ; resent++) {\n try {\n await saveOnce(this.file, body, this.tagged());\n return;\n } catch (err) {\n if (resent > 0 && isPreconditionFailed(err)) {\n if ((await this.storedWriteId()) === this.writeId) return;\n throw err;\n }\n if (!isThrottle(err)) throw err;\n if (resent >= THROTTLE_RESENDS) {\n throw new TransientError(\n `GCS upload 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 /** The id the stored object carries in its custom metadata, or `undefined` when it carries none or is gone. */\n private async storedWriteId(): Promise<string | undefined> {\n try {\n const [meta] = await this.file.getMetadata();\n return meta.metadata?.[WRITE_ID_KEY] as string | undefined;\n } catch (err) {\n if (isNotFound(err)) return undefined;\n throw err;\n }\n }\n\n async abort(): Promise<void> {\n if (this.stream !== undefined && !this.stream.destroyed) this.stream.destroy();\n }\n}\n", "/**\n * Whether the bucket a driver names exists, asked when a request first answers 404.\n *\n * GCS answers a missing bucket and a missing object with the same status and reason (`404 notFound`), and a\n * GCS-compatible server may not even word them apart. Read as a missing object, a misnamed or deleted bucket would\n * answer every read as an empty segment. An object listing tells them apart: it names no object, so its 404 can only\n * be the bucket's.\n *\n * ```\n * a request answers 404 \u2500\u2500\u25B6 confirm() \u2500\u2500\u25B6 bucket seen before? \u2500\u2500 yes \u2500\u2500\u25B6 the 404 is an object's\n * \u2502 no\n * \u25BC\n * list one object in the bucket\n * \u251C\u2500 answered \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25B6 remember the bucket; the 404 is an object's\n * \u251C\u2500 404 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25B6 reject: the bucket does not exist\n * \u251C\u2500 transient \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25B6 reject: TransientError (asked again next time)\n * \u251C\u2500 403 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25B6 remember it as unknowable; the 404 is an object's\n * \u2514\u2500 anything else \u2500\u2500\u2500\u2500\u2500\u25B6 the 404 is an object's (asked again next time)\n * ```\n *\n * A process pays one listing at most while its bucket exists, and concurrent 404s share it. An identity without\n * `storage.objects.list` is answered `403`, which says nothing about the bucket, so its 404s read as they always did.\n * Any other failure (a credential that could not be fetched, a 401) says nothing either, but may not last, so it is\n * not remembered. Once seen, the bucket is not asked about again: one deleted later in the process's life reads as an\n * empty store, as it did before this check, while every write to it fails.\n */\n\nimport { TransientError } from '@cloudbitmaps/core/driver-kit';\nimport type { Storage } from '@google-cloud/storage';\nimport { isTransient } from './gcs-errors';\nimport { scrubCredentials } from './scrub-error';\n\nexport class BucketPresence {\n private settled = false;\n private asking: Promise<void> | undefined;\n\n constructor(\n private readonly storage: Storage,\n private readonly bucket: string,\n ) {}\n\n /** Resolve when a 404 can be read as an object's; reject when the bucket does not exist or the listing failed in transit. */\n async confirm(): Promise<void> {\n if (this.settled) return;\n this.asking ??= this.ask().finally(() => {\n this.asking = undefined;\n });\n return this.asking;\n }\n\n private async ask(): Promise<void> {\n let failure: { raised: unknown } | undefined;\n try {\n await this.storage.bucket(this.bucket).getFiles({ maxResults: 1, autoPaginate: false });\n } catch (raised) {\n failure = { raised };\n }\n if (failure !== undefined) {\n // The cause is the credential-free copy, never the SDK's error as raised.\n const err = scrubCredentials(failure.raised);\n const status = (err as { code?: unknown } | null)?.code;\n if (status === 404) {\n throw new Error(`the GCS bucket does not exist: ${this.bucket}`, { cause: err });\n }\n if (isTransient(err)) {\n throw new TransientError(\n `transient GCS fault confirming the bucket exists: ${String(status)}`,\n {\n cause: err,\n },\n );\n }\n // Refused or otherwise unanswerable: nothing is known about the bucket, so a 404 reads as an object's as it did\n // before this check. Only a refusal (no list permission) is sure to answer the same again, so only it is kept.\n if (status !== 403) return;\n }\n this.settled = true;\n }\n}\n", "/**\n * Pure helpers for classifying Google Cloud Storage SDK errors (the transient class mirrors S3's).\n *\n * SDK-free + side-effect-free \u2014 they only read structural shapes off the thrown value (`err.code`,\n * `err.response.status`, `err.name`), so the GCS-specific translation is unit-testable without a live GCS or\n * an emulator, and without importing `@google-cloud/storage`. GCS's `ApiError` carries the HTTP status on\n * `.code` (a number); dropped/timed-out sockets surface as a Node error with a string `.code` (`ECONNRESET`,\n * `ETIMEDOUT`, \u2026).\n */\n\n/** The HTTP status of a GCS `ApiError`, if present (`err.code` as a number, or `err.response.status`). */\nfunction httpStatus(err: unknown): number | undefined {\n const e = err as { code?: unknown; response?: { status?: unknown } } | null;\n if (typeof e?.code === 'number') return e.code;\n if (typeof e?.response?.status === 'number') return e.response.status;\n return undefined;\n}\n\n/** A network-level error `code` string (e.g. `ECONNRESET`, `ETIMEDOUT`, `EAI_AGAIN`, `EPIPE`). */\nfunction networkCode(err: unknown): string | undefined {\n const code = (err as { code?: unknown } | null)?.code;\n return typeof code === 'string' ? code : undefined;\n}\n\n/**\n * A conditional `ifGenerationMatch: 0` write lost the write-once race \u2014 the object already existed, so GCS\n * returns **412 Precondition Failed**. Maps to `WriteConflictError` (caller OCC), never a blind retry.\n */\nexport function isPreconditionFailed(err: unknown): boolean {\n return httpStatus(err) === 412;\n}\n\n/**\n * GCS asked the client to slow down: `429` (`rateLimitExceeded`) or `503` (`backendError`), which the SDK raises as an\n * `ApiError` with the status as a numeric `code`. GCS does not document that a throttled request was not applied, so\n * an upload sent again after one must still tell a landed first send apart.\n */\nexport function isThrottle(err: unknown): boolean {\n const status = httpStatus(err);\n return status === 429 || status === 503;\n}\n\n/** The object / generation does not exist (GCS returns 404), in a bucket that does. */\nexport function isNotFound(err: unknown): boolean {\n return httpStatus(err) === 404 && !isMissingBucket(err);\n}\n\n/**\n * The bucket itself does not exist: a wiring fault, never an absent object. Read as one, a misnamed or deleted bucket\n * would answer every read as an empty segment. GCS gives both the same status and reason (`404 notFound`), so only its\n * message tells them apart: on a download the message is the response body as it came, JSON or text, and elsewhere\n * the message and each `errors[].message` are GCS's own. A wording this does not match reads as an absent object.\n */\nexport function isMissingBucket(err: unknown): boolean {\n if (httpStatus(err) !== 404) return false;\n const e = err as { message?: unknown; errors?: unknown };\n const says = (m: unknown): boolean => typeof m === 'string' && /bucket does not exist/i.test(m);\n if (says(e.message)) return true;\n return (\n Array.isArray(e.errors) &&\n e.errors.some((inner) => says((inner as { message?: unknown } | null)?.message))\n );\n}\n\n/** A range request started past EOF (HTTP 416 Requested Range Not Satisfiable). */\nexport function isInvalidRange(err: unknown): boolean {\n return httpStatus(err) === 416;\n}\n\n/** The statuses the SDK's own retry predicate retries (`RETRYABLE_ERR_FN_DEFAULT` in `@google-cloud/storage`). */\nconst SDK_RETRIED_STATUSES = [408, 429, 500, 502, 503, 504];\n\n/** The connection faults the same predicate names, matched on a lower-cased code or reason as it does. */\nfunction isSdkConnectionProblem(reason: string): boolean {\n return (\n reason.includes('eai_again') ||\n reason === 'econnreset' ||\n reason === 'unexpected connection closure' ||\n reason === 'epipe' ||\n reason === 'socket connection timeout'\n );\n}\n\n/**\n * The codes Node gives a connection that failed or was cut off: refused, reset, aborted, timed out, a DNS failure, an\n * unreachable host or network, a broken pipe, and a chunked body cut off mid-stream (`ERR_STREAM_PREMATURE_CLOSE`).\n * Only these: a file the credentials name that is missing or unreadable (`ENOENT`, `EACCES`) and a TLS failure\n * (`EPROTO`, a certificate error) carry codes of the same shape, and would fail the same way again.\n */\nconst TRANSPORT_CODES = new Set([\n 'ECONNRESET',\n 'ECONNREFUSED',\n 'ECONNABORTED',\n 'ETIMEDOUT',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n 'EPIPE',\n 'ERR_STREAM_PREMATURE_CLOSE',\n]);\n\n/** A download that failed in transit rather than with an HTTP status: one of {@link TRANSPORT_CODES}. */\nexport function isTransportFault(err: unknown): boolean {\n if (httpStatus(err) !== undefined) return false;\n const code = networkCode(err);\n return code !== undefined && TRANSPORT_CODES.has(code);\n}\n\n/**\n * Whether the driver retries a failed download, which it does in the SDK's place: the statuses the SDK's predicate\n * retries (408, 429, 500, 502, 503 and 504, as a number or a string `code`), the connection faults it names in any\n * `errors[].reason`, and every {@link isTransportFault}, which the SDK retried before any response came. A 404, 412,\n * 416 or 403, a 501 or 505, a credentials or TLS failure, and every other answer reach the caller on the first attempt.\n */\nexport function isDownloadRetryable(err: unknown): boolean {\n if (err === null || typeof err !== 'object') return false;\n if (isTransportFault(err)) return true;\n const e = err as { code?: unknown; errors?: unknown };\n if (typeof e.code === 'number' && SDK_RETRIED_STATUSES.includes(e.code)) return true;\n if (typeof e.code === 'string') {\n if (SDK_RETRIED_STATUSES.map(String).includes(e.code)) return true;\n if (isSdkConnectionProblem(e.code.toLowerCase())) return true;\n }\n if (Array.isArray(e.errors)) {\n for (const inner of e.errors as Array<{ reason?: unknown } | null>) {\n const reason = inner?.reason?.toString().toLowerCase();\n if (reason !== undefined && isSdkConnectionProblem(reason)) return true;\n }\n }\n return false;\n}\n\n/**\n * A transient GCS fault that is safe to retry: throttling (429), a request timeout (408), any 5xx, or a dropped/timed-out socket.\n * Excludes the deterministic, caller-meaningful outcomes (412/404/416) \u2014 those must never be reclassified as\n * a blind transient (a retried doomed conditional write would just fail again, and mask an OCC conflict).\n */\nexport function isTransient(err: unknown): boolean {\n if (isPreconditionFailed(err) || isNotFound(err) || isInvalidRange(err)) return false;\n const status = httpStatus(err);\n if (status === 408 || status === 429 || (status !== undefined && status >= 500 && status < 600))\n return true;\n // The same connection faults a download is retried on: one list, so a write and a read cannot disagree on one.\n const net = networkCode(err);\n return net !== undefined && TRANSPORT_CODES.has(net);\n}\n", "/**\n * Make an error the SDK raised safe to throw, or to keep as a `cause`: a copy of it with no credential and none of the\n * live transport.\n *\n * The SDK keeps the request it sent on the error (`response.request`, `config.headers`, the raw request line), and\n * through that request the HTTP agent it shares with every other call, whose sockets hold the raw text, bearer token\n * included, of requests still in flight. An application that logs the error or its cause chain would write a live\n * access token. The error is therefore replaced by a copy built from what it is: its prototype, name, message, stack,\n * code, status and other plain data; `response` reduced to its status; the request, agent, socket and config dropped.\n * Nothing live is ever changed, only read, and the copy is made from plain data alone, so no depth or size limit can\n * stop short of a secret. A credential header or header line in the plain data that is kept is redacted.\n */\n\n/** Properties that hold the raw transport or the request's configuration. They are not copied. */\nconst DROPPED = new Set([\n 'request',\n 'agent',\n 'socket',\n '_httpMessage',\n 'connection',\n 'req',\n 'res',\n 'config',\n]);\n/** Header and field names whose value is a credential. */\nconst CREDENTIAL_NAME = /^(?:proxy-)?authorization$|^cookie$|^set-cookie$|^x-api-key$/i;\n/** A credential header inside a raw request or response text. */\nconst CREDENTIAL_LINES = /^((?:proxy-)?authorization|cookie|set-cookie|x-api-key):[^\\r\\n]*/gim;\nconst REDACTED = '[redacted]';\n\nconst isPlain = (value: object): boolean => {\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n};\n\n/** The part of an HTTP response worth keeping: what the server said, as numbers and text. */\nfunction responseSummary(response: object): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of ['status', 'statusText']) {\n const value: unknown = (response as Record<string, unknown>)[key];\n if (typeof value === 'number' || typeof value === 'string') out[key] = value;\n }\n return out;\n}\n\n/** A copy of `value` from plain data only: other objects (streams, headers, sockets) are left out. */\nfunction clean(value: unknown, copies: WeakMap<object, unknown>): unknown {\n if (typeof value === 'string') return value.replace(CREDENTIAL_LINES, `$1: ${REDACTED}`);\n if (typeof value !== 'object' || value === null) {\n return typeof value === 'function' || typeof value === 'symbol' ? undefined : value;\n }\n if (copies.has(value)) return copies.get(value);\n if (Array.isArray(value)) {\n const list: unknown[] = [];\n copies.set(value, list);\n for (const item of value) list.push(clean(item, copies));\n return list;\n }\n const isError = value instanceof Error;\n if (!isError && !isPlain(value)) return undefined;\n const copy = Object.create(Object.getPrototypeOf(value) as object | null) as Record<\n string | symbol,\n unknown\n >;\n copies.set(value, copy);\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key === 'string' && DROPPED.has(key)) continue;\n let held: unknown;\n try {\n held = (value as Record<string | symbol, unknown>)[key];\n } catch {\n continue; // a getter that throws\n }\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n let kept: unknown;\n if (typeof key === 'string' && CREDENTIAL_NAME.test(key)) kept = held == null ? held : REDACTED;\n else if (key === 'response' && typeof held === 'object' && held !== null) {\n kept = responseSummary(held);\n } else kept = clean(held, copies);\n if (kept === undefined && held !== undefined) continue; // an object this copy leaves out\n Object.defineProperty(copy, key, {\n value: kept,\n writable: true,\n configurable: true,\n enumerable: descriptor?.enumerable ?? true,\n });\n }\n return copy;\n}\n\n/** Whether `err`, or an error in its `cause` chain, holds a response, a request or the rest of the transport. */\nfunction holdsTransport(err: object, seen: Set<object>): boolean {\n if (seen.has(err)) return false;\n seen.add(err);\n if (Object.getOwnPropertyNames(err).some((key) => DROPPED.has(key) || key === 'response')) {\n return true;\n }\n const cause: unknown = (err as { cause?: unknown }).cause;\n return typeof cause === 'object' && cause !== null && holdsTransport(cause, seen);\n}\n\n/**\n * `err` as it is safe to throw: a credential-free copy of an error that holds the SDK's request or response, and any\n * other value as it is (a timeout, a connection fault or an error of the driver's own holds no credential, and keeps\n * its identity).\n */\nexport function scrubCredentials<T>(err: T): T {\n if (typeof err !== 'object' || err === null) return err;\n return holdsTransport(err, new Set()) ? (clean(err, new WeakMap()) as T) : err;\n}\n", "/**\n * Logical-ref \u2192 GCS object-name mapping for {@link GcsStorageDriver}.\n *\n * Pure string logic, no SDK dependency \u2014 unit-testable without GCS or an emulator. Uses the **same\n * backend-agnostic `.crbm` object-name scheme** as the S3 + LocalFs storage drivers\n * (`<prefix><ns>/segments/<segment>.<gen>.crbm`), so a segment reads identically whichever storage backend holds\n * it. The default (absent) namespace maps to `_default`, which cannot collide with a real namespace because a\n * caller's `_default` encodes to `%5Fdefault` while the sentinel is emitted literally.\n *\n * The storage-key layout is shared, not copied: `prefixPart` and the name encoders come from\n * `@cloudbitmaps/core/driver-kit`, so a segment written by one driver reads identically under another. Two\n * drivers disagreeing about how a name becomes a key would be a silent cross-driver incompatibility on the\n * same bucket, which is exactly the kind of thing a local copy drifts into.\n */\n\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 normalizeGcsPrefix } from '@cloudbitmaps/core/driver-kit';\n\n/**\n * The GCS object-name prefix shared by all of a segment's generations: `<prefix><ns>/segments/<segment>.`.\n * Used both as the `getFiles` prefix and as the string stripped by {@link parseGenerationFromName}.\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 GCS object name of one `.crbm` generation: `<segmentPrefix><gen>.crbm`. */\nexport function storageObjectName(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 name, 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. Also rejects a *different* segment whose name merely shares the prefix (its middle\n * isn't all digits).\n */\nexport function parseGenerationFromName(segmentPrefix: string, objectName: string): number | null {\n if (!objectName.startsWith(segmentPrefix) || !objectName.endsWith(SUFFIX)) return null;\n const middle = objectName.slice(segmentPrefix.length, objectName.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 * The read timeout: `readTimeoutMs`, off (`0`) unless set. It bounds one driver read \u2014 a tail read (with the metadata\n * read it can fall back on), a range read, a registry read \u2014 as a whole: one deadline, started when the driver is\n * called, covers every attempt the driver makes at it and the backoff between them.\n *\n * `@google-cloud/storage` 8.x sets no working timeout of its own: it hands its `timeout` to an HTTP client that has no\n * such option, so a read on a connection that stops answering waits forever, and nothing above it ever gets a fault to\n * retry. When the deadline passes, the request in flight fails with {@link ReadTimedOut} (coded `ETIMEDOUT`), no\n * further attempt starts, and a backoff in progress ends with the same error; the driver reports it as\n * `TransientError`, its message naming the read and the timeout, for the store's read retry to run again.\n *\n * **What the clock counts.** Everything from the call into the driver: fetching or refreshing a credential, resolving\n * the project, waiting for a socket (Node's global agents, which the downloads use, set no socket limit unless the\n * process sets one, so by default there is no queue), each request, its headers and its whole body, and the driver's\n * backoff between attempts. It counts time the process spends busy too: Node runs a due timer before it reads a socket,\n * so a synchronous stretch longer than the timeout fails the reads in flight even when their responses have arrived.\n *\n * Uploads, deletes, listings and the conditional writes are not timed: an upload can rightly take longer than a read,\n * and a write cut off may still land.\n */\nimport { ValidationError } from '@cloudbitmaps/core/driver-kit';\n\n/** The default: no timeout. */\nconst DEFAULT_READ_TIMEOUT_MS = 0;\n\n/** The longest delay a Node timer holds. A longer one fires after 1 ms instead, so it is refused rather than passed on. */\nconst MAX_TIMER_MS = 2_147_483_647;\n\n/** Validate a caller's `readTimeoutMs`, defaulting it: an integer from 0 (no timeout) to {@link MAX_TIMER_MS}. */\nexport function resolveReadTimeoutMs(value: unknown): number {\n if (value === undefined) return DEFAULT_READ_TIMEOUT_MS;\n if (\n typeof value !== 'number' ||\n !Number.isSafeInteger(value) ||\n value < 0 ||\n value > MAX_TIMER_MS\n ) {\n throw new ValidationError(\n `readTimeoutMs must be a non-negative safe integer no larger than ${MAX_TIMER_MS}; got ${describe(value)}`,\n );\n }\n return value;\n}\n\n/** A value as an error message can show it: a string quoted, so `'200'` is not mistaken for `200`, and never a throw. */\nfunction describe(value: unknown): string {\n if (typeof value === 'string') return JSON.stringify(value);\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n return value === null ? 'null' : typeof value;\n}\n\n/** A read the driver gave up on when its deadline passed. Coded `ETIMEDOUT`, as a timed-out connection is. */\nexport class ReadTimedOut extends Error {\n readonly code = 'ETIMEDOUT';\n constructor(read: string, ms: number, cause?: unknown) {\n super(`GCS ${read} timed out after ${ms} ms`, cause === undefined ? undefined : { cause });\n this.name = 'ReadTimedOut';\n }\n}\n\n/** One driver read's deadline: `ms` from when it is made. `read` names the read in the error, e.g. `tail read of s.3`. */\nexport class Deadline {\n private readonly endsAt: number;\n\n constructor(\n readonly ms: number,\n readonly read: string,\n ) {\n this.endsAt = performance.now() + ms;\n }\n\n /** What is left of it, in milliseconds: 0 once it has passed. */\n remaining(): number {\n return Math.max(0, this.endsAt - performance.now());\n }\n\n /** The error a read fails with once this has passed; `cause` is the last attempt's own error, if one failed. */\n expired(cause?: unknown): ReadTimedOut {\n return new ReadTimedOut(this.read, this.ms, cause);\n }\n}\n\n/** The deadline a read made now runs under, or none when `ms` is 0 (no timeout). */\nexport function startDeadline(ms: number, read: string): Deadline | undefined {\n return ms === 0 ? undefined : new Deadline(ms, read);\n}\n\n/**\n * Make the request `send` starts, and settle with it, or with {@link ReadTimedOut} once `deadline` has passed; a\n * deadline already passed sends nothing. For a request the SDK cannot cancel: a metadata read is one callback-style\n * request that returns no handle to abort it, so at the deadline this stops waiting and the request runs on until it is\n * answered or its connection closes. The race keeps a handler on the request, so what it settles with then is dropped\n * rather than raised.\n */\nexport function withDeadline<T>(\n send: () => Promise<T>,\n deadline: Deadline | undefined,\n): Promise<T> {\n if (deadline === undefined) return send();\n if (deadline.remaining() === 0) return Promise.reject(deadline.expired());\n const request = send();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timedOut = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(deadline.expired()), deadline.remaining());\n });\n return Promise.race([request, timedOut]).finally(() => clearTimeout(timer));\n}\n", "/**\n * `retryDownload` \u2014 run one download again after the faults the SDK itself would have retried.\n *\n * The client `GcsStorage` builds sends a download once (see `backend.ts`: a download the SDK retries can crash the\n * process), which also took away the SDK's quiet retry of a connection fault and of a 408, 429, 500, 502, 503 or 504.\n * This puts that retry back inside the driver, so every caller keeps it: the store's admin and write-path registry\n * reads, and a store built with `retry: false`, included. Which faults: `isDownloadRetryable`. How many: three\n * retries, the SDK's default, with full-jitter backoff of at most about 0.7 s a call. A chunk read that the store's\n * own retry also repeats makes at most 16 requests, as it did with the SDK's retry. A 404, 412, 403 and every other\n * answer reach the caller on the first attempt. What the last attempt raised is rethrown as it is, for the driver to\n * classify.\n *\n * **Under a read timeout** (`readTimeoutMs`), one {@link Deadline} covers every attempt: each attempt passes it to its\n * request, so the request in flight when it passes fails with {@link ReadTimedOut}; no attempt starts after it; and a\n * backoff that would outlast it ends when it passes, with the same error. Nor is a retry sent with less than\n * {@link MIN_RETRY_MS} left: no answer could arrive in time, and the request would only be one more held open. So a\n * server that never answers is sent one request per call, not four.\n */\nimport { isDownloadRetryable } from './gcs-errors';\nimport { ReadTimedOut, type Deadline } from './read-timeout';\n\nconst RETRIES = 3;\nconst BASE_DELAY_MS = 100;\nconst MAX_DELAY_MS = 1_000;\n/** Under a deadline, a retry is not sent with less than this left of it. */\nexport const MIN_RETRY_MS = 10;\n\n/**\n * Call `download`, and again after each retryable fault, up to {@link RETRIES} more times, within `deadline` when there\n * is one; rethrow the last error, or the deadline's once it has passed.\n */\nexport async function retryDownload<T>(\n download: () => Promise<T>,\n deadline?: Deadline,\n): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await download();\n } catch (err) {\n // The deadline passed during the attempt: nothing more is sent.\n if (err instanceof ReadTimedOut) throw err;\n if (attempt >= RETRIES || !isDownloadRetryable(err)) throw err;\n const left = deadline?.remaining() ?? Infinity;\n if (deadline !== undefined && left < MIN_RETRY_MS) throw deadline.expired(err);\n const ceiling = Math.min(MAX_DELAY_MS, BASE_DELAY_MS * 2 ** attempt);\n const delay = Math.random() * ceiling;\n await new Promise((resolve) => setTimeout(resolve, Math.min(delay, left)));\n if (deadline !== undefined && deadline.remaining() < MIN_RETRY_MS)\n throw deadline.expired(err);\n }\n }\n}\n", "/**\n * `saveOnce` \u2014 upload one object in a single request, sent once, with no SDK retry around it.\n *\n * `file.save()` runs a single-request upload inside a retry loop, and it counts an upload that carries\n * `ifGenerationMatch` as safe to repeat, so after a lost response \u2014 a timeout, a reset connection, a 5xx \u2014 it sends\n * the upload again. When the first had landed, the second meets it: `ifGenerationMatch: 0` finds the object it\n * created, a generation fence finds the generation it replaced, and GCS answers `412`, which the driver can only report\n * as a lost race. The caller is told its write lost when it won. Only the caller can find out which happened, by\n * reading the pointer or listing the generations, so the write is sent once and a transient failure reaches it as\n * `TransientError`.\n *\n * `createWriteStream({ resumable: false })` is that same single multipart request without the loop: the SDK sends a\n * streamed upload with no retries of its own, since a stream it has read cannot be read again. The client is not\n * touched, so a caller's own client keeps its retry options for every other call.\n *\n * **`resumable: false` is load-bearing twice.** It is what makes this one request, and it is the path on which\n * fake-gcs-server enforces `ifGenerationMatch`, so the integration lane tests the precondition rather than passing over\n * a write with none.\n */\nimport { once } from 'node:events';\nimport type { CreateWriteStreamOptions, Storage } from '@google-cloud/storage';\n\ntype GcsFile = ReturnType<ReturnType<Storage['bucket']>['file']>;\n\n/** Upload `body` to `file` in one request, sent once; resolves once GCS has stored it and its checksum matched. */\nexport async function saveOnce(\n file: GcsFile,\n body: Uint8Array,\n options: CreateWriteStreamOptions,\n): Promise<void> {\n const stream = file.createWriteStream({ ...options, resumable: false });\n const stored = once(stream, 'finish'); // rejects on 'error', which is how a failed upload surfaces\n stream.end(body);\n await stored;\n}\n", "/**\n * `readOnce` \u2014 read one object, or one range of it, in a single GET, with the response headers in hand.\n *\n * `file.download()` returns only the bytes. The headers carry what a caller needs alongside them \u2014 the object's\n * `generation` (the registry's version fence) and, on a ranged read, the object's total size in `Content-Range` \u2014 so\n * this reads through `createReadStream`, which announces the raw response before any data flows. Both come from the\n * same response as the bytes, so they describe one observation of the object.\n *\n * **Everything on the wire is untrusted.** The advertised `Content-Length` is checked against `maxBytes` before the\n * first byte is buffered, and the bytes are counted as they arrive, so a response that understates its length (or\n * omits it) still cannot make the caller hold more than `maxBytes`. The headers are returned as received; each caller\n * parses the ones it relies on strictly.\n *\n * **Every download stays off the SDK's shared connection pool.** When a download fails part-way \u2014 a body cut off, a\n * refused response, a timeout \u2014 the SDK destroys the HTTP agent the request went out on, to free its socket. By default\n * that is one keep-alive agent the whole process shares (teeny-request's, for every request with `forever: true`), so\n * every other request in flight on any client is reset with it, uploads included. {@link downloadFile} sets\n * `forever: false` on its own requests, through the SDK's request interceptors: they then go out on Node's global\n * agent, which the SDK never destroys, and on `@google-cloud/storage` 8.x a destroyed download closes its own socket\n * and nothing else. The cost: the global agent closes a connection idle for 5 seconds, where the SDK's pool keeps it,\n * so a read after a longer pause opens a new one; and it is shared with the process's other `http` and `https` code.\n */\nimport type { Storage } from '@google-cloud/storage';\nimport type { Deadline } from './read-timeout';\n\ntype GcsFile = ReturnType<ReturnType<Storage['bucket']>['file']>;\ntype Interceptor = GcsFile['interceptors'][number];\n/** What the SDK hands an interceptor: its typing says less, but every request it decorates carries a `uri`. */\ntype RequestOptions = ReturnType<Interceptor['request']>;\n\n/** Sends a request on Node's global agent rather than the SDK's shared keep-alive one (see the module comment). */\nconst OFF_THE_SHARED_AGENT: Interceptor = {\n request: (reqOpts) => ({ ...(reqOpts as RequestOptions), forever: false }),\n};\n\n/** A handle on `name` for downloading: its requests stay off the SDK's shared agent. The client is not touched. */\nexport function downloadFile(storage: Storage, bucket: string, name: string): GcsFile {\n const file = storage.bucket(bucket).file(name);\n file.interceptors.push(OFF_THE_SHARED_AGENT);\n return file;\n}\n\nexport interface ObjectRead {\n /** The HTTP status of the response (200 for a whole object, 206 for a range). */\n readonly status: number;\n /** The response headers, lower-cased as Node delivers them. */\n readonly headers: Readonly<Record<string, string | string[] | undefined>>;\n /** The body, at most `maxBytes` long. */\n readonly bytes: Uint8Array;\n}\n\n/**\n * The response the SDK announces. It is the HTTP layer's own body stream with the status and headers laid on it, and\n * while that body is in flight the SDK and the HTTP layer each run a pipeline over it, which with its own listeners is\n * eleven or twelve error and close listeners: one past Node's default limit of ten, so Node prints a possible-leak\n * warning for every read that is still arriving when it is let go. Nothing grows with the number of attempts, because\n * each attempt has a body of its own. The limit is raised on that one stream, to {@link BODY_LISTENER_LIMIT}.\n */\ninterface RawResponse {\n statusCode?: unknown;\n headers?: Record<string, string | string[] | undefined>;\n setMaxListeners?: (n: number) => unknown;\n}\n\n/** Room for the listeners the SDK's and the HTTP layer's pipelines put on one response body, with a few to spare. */\nconst BODY_LISTENER_LIMIT = 16;\n\n/** The value of a header that must appear once, or `undefined` when it is absent, repeated or empty. */\nexport function singleHeader(headers: ObjectRead['headers'], name: string): string | undefined {\n const value = headers[name];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * GET `file` (with the SDK's `createReadStream` options), buffering at most `maxBytes`. `oversize` builds the error\n * thrown when the advertised or actual length passes the cap, so each caller reports it in its own vocabulary.\n * Transport and HTTP errors reject as the SDK raises them (a 404 carries `code: 404`).\n *\n * With a `deadline`, a read still unsettled when it passes rejects with its `ReadTimedOut`; one asked for after it has\n * passed is not sent. The deadline belongs to the whole driver read, so an attempt gets only what is left of it.\n */\nexport function readOnce(\n file: GcsFile,\n options: { start?: number; end?: number; validation?: false; decompress?: false },\n maxBytes: number,\n oversize: (size: number | undefined) => Error,\n deadline?: Deadline,\n): Promise<ObjectRead> {\n if (deadline !== undefined && deadline.remaining() === 0)\n return Promise.reject(deadline.expired());\n return new Promise<ObjectRead>((resolve, reject) => {\n const stream = file.createReadStream(options);\n const chunks: Buffer[] = [];\n let total = 0;\n let status = 0;\n let headers: ObjectRead['headers'] = {};\n let responded = false;\n let settled = false;\n // The SDK makes no request until the stream is read from, in a later turn, so a timer set here covers all of it.\n const timer =\n deadline === undefined\n ? undefined\n : setTimeout(() => fail(deadline.expired()), deadline.remaining());\n\n // Deferred: the SDK builds its response pipeline right after announcing the response, and destroying the stream\n // before that makes the pipeline throw out of an event handler, where nothing can catch it, and ends the process.\n // One turn later the same destroy ends the read without a throw. So a read is let go only once its response has\n // been announced, never before: one that settles first (a timeout before any answer) is destroyed when its\n // response comes, if one ever does. Until then the SDK has no way to cancel it, so its request stays open until\n // the server answers or the connection closes.\n const release = (): void => void setImmediate(() => stream.destroy());\n\n const fail = (err: unknown): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (responded) release();\n reject(err);\n };\n\n stream.on('response', (res: RawResponse) => {\n responded = true;\n res.setMaxListeners?.(BODY_LISTENER_LIMIT);\n if (settled) return release();\n status = typeof res.statusCode === 'number' ? res.statusCode : 0;\n headers = res.headers ?? {};\n if (status < 200 || status > 299) return; // the SDK raises the error itself, with its own status\n const advertised = singleHeader(headers, 'content-length');\n if (advertised !== undefined && /^\\d+$/.test(advertised) && Number(advertised) > maxBytes) {\n fail(oversize(Number(advertised)));\n }\n });\n stream.on('data', (chunk: Buffer) => {\n if (settled) return;\n total += chunk.length;\n if (total > maxBytes) return fail(oversize(undefined));\n chunks.push(chunk);\n });\n stream.on('error', fail);\n stream.on('end', () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve({ status, headers, bytes: new Uint8Array(Buffer.concat(chunks, total)) });\n });\n // A net under the SDK: it reports a cut-off body as an 'error' itself, but a stream that closes without ending\n // or erring was cut off all the same, so it is a dropped connection, and retryable.\n stream.on('close', () =>\n fail(\n Object.assign(new Error('GCS read ended before the response completed'), {\n code: 'ECONNRESET',\n }),\n ),\n );\n });\n}\n", "/**\n * `GcsRegistryDriver` \u2014 an {@link IRegistryDriver} over Google Cloud Storage.\n *\n * Lets a **GCS deployment run on one bucket alone** \u2014 storage `.crbm` generations and the registry in the same\n * place, with no second cloud involved.\n *\n * The protocol (an ABA-safe OCC token, the delete and its tombstone, the bounded retry, the key layout) lives once\n * in {@link ObjectStoreRegistry}; this file is only the I/O calls GCS makes. A delete removes a row for good, under\n * `ifGenerationMatch: <generation>`, when `conditionalDelete` is `true`. It is off by default, on the public endpoint too:\n * no run against real GCS has verified that the service applies the precondition to a delete, and fake-gcs-server\n * accepts it and ignores it, so CI cannot show it.\n *\n * **The atomic swap is offloaded to GCS's object preconditions.** `ifGenerationMatch: 0` is create-only\n * (\"only if it does not exist\") and `ifGenerationMatch: <generation>` is compare-and-swap \u2014 the same pair S3\n * spells `If-None-Match: *` / `If-Match: <etag>`. A lost race returns `412` \u2192 {@link WriteConflictError}. Each write\n * is one request sent once ({@link saveOnce}), so a `412` means another write got there first, never this one meeting\n * itself after a lost response. A transient failure reaches the caller as {@link TransientError}: the write may or may\n * not have landed, and the caller re-reads the row to learn where it stands.\n *\n * **The version fence is the object's `generation`, not its ETag**: GCS mutates the generation on every\n * overwrite and it is the value `ifGenerationMatch` compares, so it is the only correct fence here. (Note\n * the unrelated collision of vocabulary \u2014 a GCS *object generation* has nothing to do with a CloudBitmaps\n * `.crbm` *generation*.) Reads are strongly consistent, satisfying the registry's `strongRead` contract.\n *\n * **Deployment requirements** (a policy that violates these silently corrupts the registry):\n * - The principal needs `storage.objects.get`, `create`, `update`, `list` and, to remove rows, `delete` on the\n * bucket. Without `list`\n * the registry cannot enumerate, and a missing-object read may surface as `403` rather than `404`.\n * - **Do not apply an Object Lifecycle rule to the `registry/` prefix that deletes a live object** (one on\n * noncurrent versions only is safe), and do not enable a retention\n * policy that blocks overwrite or deletion; see {@link ObjectStoreRegistry}.\n * - Object versioning is neither required nor used; the driver always reads the live generation. A read is one\n * GET whose `x-goog-generation` header and body describe the same object, so a concurrent overwrite is simply\n * observed as the older or the newer row, never as a torn pair or a missing one.\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 type { Storage } from '@google-cloud/storage';\nimport { isNotFound, isPreconditionFailed, isTransient, isTransportFault } from './gcs-errors';\nimport { BucketPresence } from './bucket-presence';\nimport { crc32cBase64 } from './crc32c';\nimport { retryDownload } from './download-retry';\nimport { scrubCredentials } from './scrub-error';\nimport { downloadFile, readOnce, singleHeader } from './read-once';\nimport { ReadTimedOut, resolveReadTimeoutMs, startDeadline } from './read-timeout';\nimport { saveOnce } from './send-once';\n\nexport interface GcsRegistryDriverOptions {\n /** A constructed `@google-cloud/storage` `Storage` client (point `apiEndpoint` at fake-gcs-server locally). */\n readonly storage: Storage;\n /** The client the downloads go through; `storage` when absent (see `GcsStorageDriverOptions.readStorage`). */\n readonly readStorage?: Storage;\n /** Target bucket (must already exist). */\n readonly bucket: string;\n /** Optional object-name 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 * Cut off a read of a registry row once it has run this long, in milliseconds, every attempt included; `0` (the\n * default) sets no timeout. Timed as the storage driver's reads are (see `GcsStorageDriverOptions.readTimeoutMs`);\n * the writes and listings are not timed.\n */\n readonly readTimeoutMs?: number;\n /**\n * Whether a delete removes a row for good, by an object delete sent with `ifGenerationMatch: <the generation it\n * read>`, rather than leaving a tombstone. Defaults to `false`, on the public endpoint too: whether real GCS applies\n * `ifGenerationMatch` to a delete has not been verified by a run against the service, and fake-gcs-server accepts the\n * precondition and ignores it, so CI cannot show it. Set `true` to remove rows for good.\n */\n readonly conditionalDelete?: boolean;\n}\n\n/** The calls {@link ObjectStoreRegistry} needs, in GCS's dialect. Exported for the tests that drive one directly. */\nexport class GcsRegistryStore implements ObjectRegistryStore {\n readonly label = 'GCS';\n private readonly presence: BucketPresence;\n\n constructor(\n private readonly storage: Storage,\n private readonly readStorage: Storage,\n private readonly bucket: string,\n private readonly readTimeoutMs: number,\n readonly conditionalDelete: boolean,\n ) {\n this.presence = new BucketPresence(storage, bucket);\n }\n\n /** A handle on one registry object. */\n private file(name: string) {\n return this.storage.bucket(this.bucket).file(name);\n }\n\n /** The same file handle on the download client, off the SDK's shared agent ({@link downloadFile}). */\n private downloadable(name: string) {\n return downloadFile(this.readStorage, this.bucket, name);\n }\n\n async read(key: string): Promise<ObjectRow | null> {\n // One GET: the response headers carry the `generation` fence and the length, and the body is the same\n // observation of the object, so the pair cannot straddle a concurrent overwrite.\n let res;\n try {\n // One deadline for the read, every attempt included.\n const deadline = startDeadline(this.readTimeoutMs, `registry read of ${key}`);\n res = await retryDownload(\n () =>\n readOnce(\n this.downloadable(key),\n {},\n MAX_ROW_BYTES,\n (size) =>\n new IntegrityError(\n size === undefined\n ? `registry object exceeds cap ${MAX_ROW_BYTES}B`\n : `registry object ${size}B exceeds cap ${MAX_ROW_BYTES}B`,\n ),\n deadline,\n ),\n deadline,\n );\n } catch (err) {\n if (isNotFound(err)) {\n await this.presence.confirm(); // a missing bucket is an error, not an absent row\n return null;\n }\n throw mapReadError(err);\n }\n if (res.status !== 200) {\n throw new IntegrityError(`registry read answered HTTP ${res.status}, not 200: ${key}`);\n }\n // The fence is compared by `ifGenerationMatch` on the next write, so it is parsed here, where a header that\n // is missing or malformed can be refused before it is mistaken for a version.\n const generation = singleHeader(res.headers, 'x-goog-generation');\n if (\n generation === undefined ||\n !/^[1-9]\\d*$/.test(generation) ||\n !Number.isSafeInteger(Number(generation))\n ) {\n throw new IntegrityError(`registry read carries no usable x-goog-generation: ${key}`);\n }\n return { bytes: res.bytes, version: generation };\n }\n\n async write(\n key: string,\n body: Uint8Array,\n expect: 'absent' | { version: string },\n ): Promise<void> {\n try {\n // One request, sent once: a replay of a write that landed would fail its own precondition, and read as a lost\n // race. A registry row is a few hundred bytes, so a resumable session would also carry one round trip's worth\n // of data over two.\n await saveOnce(this.file(key), body, {\n contentType: 'application/json',\n // The checksum goes with the upload, for GCS to check before it stores anything. The SDK's own check runs\n // after the upload instead, and on a mismatch, or an answer that names no checksum, deletes the object by\n // name with no precondition: for a row, that is the live row, another writer's newer one included.\n validation: false,\n metadata: { crc32c: crc32cBase64(body) },\n preconditionOpts: {\n ifGenerationMatch: expect === 'absent' ? 0 : generationFence(expect.version, key),\n },\n });\n } catch (err) {\n if (isPreconditionFailed(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 // The SDK may send this again after a lost response, as it does any request with a precondition. The\n // precondition names one generation, so a second copy removes nothing the first could not; one that meets the\n // first's landed delete is a 404, a conflict, and the registry re-reads and finds the row gone.\n await this.file(key).delete({ ifGenerationMatch: generationFence(expect.version, key) });\n } catch (err) {\n // A 412 (the generation moved on) or a 404 (the object is gone): the version to delete is not there.\n if (isPreconditionFailed(err) || isNotFound(err)) {\n if (isNotFound(err)) await this.presence.confirm();\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 let pageToken: string | undefined;\n do {\n let files;\n let next: { pageToken?: string } | null | undefined;\n try {\n // Explicit paging rather than autoPaginate: a registry can hold far more rows than a segment holds\n // generations, and draining every page into memory first is what `list()` exists to avoid.\n [files, next] = await this.storage\n .bucket(this.bucket)\n .getFiles({ prefix, autoPaginate: false, maxResults: 1000, pageToken });\n } catch (err) {\n throw mapError(err);\n }\n for (const f of files ?? []) yield f.name;\n // A page that hands back the token it was asked with would be asked for again, forever.\n if (next?.pageToken !== undefined && next.pageToken === pageToken) {\n throw new IntegrityError('GCS listing returned the page token it was given');\n }\n pageToken = next?.pageToken;\n } while (pageToken !== undefined);\n }\n}\n\n/**\n * Narrow a version fence back to the number `ifGenerationMatch` takes. The fence always originates as\n * the `x-goog-generation` header in {@link GcsRegistryStore.read}, so this cannot fire in practice \u2014 but `Number()` answers `NaN`\n * for anything non-numeric, and `ifGenerationMatch: NaN` serializes to a precondition GCS ignores. That\n * failure is invisible (writes simply stop being fenced), so it is checked rather than assumed.\n */\nfunction generationFence(version: string, key: string): number {\n const generation = Number(version);\n if (!Number.isSafeInteger(generation) || generation <= 0) {\n throw new IntegrityError(`registry version fence is not a GCS generation: ${key}`);\n }\n return generation;\n}\n\n/** A read's error: a connection that failed or was cut off is transient too, after the driver's retries. */\nfunction mapReadError(err: unknown): unknown {\n err = scrubCredentials(err);\n // Timed out on every attempt: transient, in words that name the read and the timeout.\n if (err instanceof ReadTimedOut) return new TransientError(err.message, { cause: err });\n if (isTransportFault(err)) {\n return new TransientError(`transient GCS fault: ${String((err as { code?: unknown }).code)}`, {\n cause: err,\n });\n }\n return mapError(err);\n}\n\n/** Reclassify a transient GCS 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 GCS fault: ${(err as { code?: unknown } | null)?.code ?? (err as { name?: string } | null)?.name ?? 'unknown'}`,\n { cause: err },\n );\n }\n return err;\n}\n\n/** {@link GcsRegistryDriverOptions.conditionalDelete}, checked, or its default: off. */\nfunction resolveConditionalDelete(value: unknown): boolean {\n if (value === undefined) {\n return false;\n }\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 GcsRegistryDriver extends ObjectStoreRegistry {\n constructor(options: GcsRegistryDriverOptions) {\n super(\n new GcsRegistryStore(\n options.storage,\n options.readStorage ?? options.storage,\n options.bucket,\n resolveReadTimeoutMs(options.readTimeoutMs),\n resolveConditionalDelete(options.conditionalDelete),\n ),\n normalizeObjectPrefix(options.prefix),\n options.now ?? ((): number => Date.now()),\n );\n }\n}\n", "/**\n * CRC32C (Castagnoli) of some bytes, in the form GCS takes in an object's `crc32c` metadata: the 4-byte big-endian\n * value, base64-encoded. Computed here rather than through the SDK so it is the same on every major the driver accepts.\n */\n\nconst TABLE = (() => {\n const table = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0x82f63b78 ^ (c >>> 1) : c >>> 1;\n table[n] = c >>> 0;\n }\n return table;\n})();\n\nexport function crc32cBase64(bytes: Uint8Array): string {\n let crc = 0xffffffff;\n for (const byte of bytes) crc = TABLE[(crc ^ byte) & 0xff]! ^ (crc >>> 8);\n const value = (crc ^ 0xffffffff) >>> 0;\n const out = Buffer.alloc(4);\n out.writeUInt32BE(value);\n return out.toString('base64');\n}\n", "/**\n * The client a GCS backend sends its downloads on: one that never retries a download itself.\n *\n * `@google-cloud/storage` 7.x and 8.x retry a failed download by default, and when the retried request succeeds they\n * throw `ERR_STREAM_UNABLE_TO_PIPE` outside any promise, which ends the process whatever the caller wrote around the\n * call. So a download is sent once, and the driver retries it itself (`download-retry.ts`). The other requests keep the\n * client as it was built: a resumable upload's session, a listing, a delete (the conditional writes are sent once by\n * `send-once.ts` either way).\n *\n * ```\n * the client\n * \u2502\n * \u251C\u2500\u2500 not a Storage client (a test double) \u2500\u2500\u25B6 its retries off? yes: used as it is; no: refused\n * \u251C\u2500\u2500 its `bucket` replaced on the instance (a test stub), or overridden by its class \u2500\u2500\u25B6 refused\n * \u25BC\n * a twin, from the client's own class, with:\n * the same credentials object (one token cache), endpoint, universe, project, user agent, timeout, checksum\n * generator and retry settings but `autoRetry: false`, and one interceptor that applies the client's own\n * interceptors as they are when each request is sent (a later one too); then its own retry settings turned off,\n * over any its class set\n * \u2502\n * \u25BC\n * a second client, with a `bucket` method, the same credentials \u2500\u2500 yes \u2500\u2500\u25B6 downloads use the twin\n * object, retry settings of its own and off, the same URL and endpoint?\n * \u2502 no, or it could not be built\n * \u25BC\n * refused at construction, saying what differed\n * ```\n *\n * A `Storage` client is never used as it is, even one whose retries read off: the SDK turns a client's `autoRetry` off\n * while a delete or an upload with no precondition is in flight and back on when it ends, so that value, read once,\n * does not say how the client was built. The twin's retry settings are its own object, and only reads are sent on it,\n * which never change them; the SDK reads that object on every request.\n *\n * The twin is built from the client's own class, not this package's import: a caller on another major of the SDK hands\n * in a client whose credentials object is another copy's class, which this package's class would wrap in a second one.\n */\nimport { ValidationError } from '@cloudbitmaps/core/driver-kit';\nimport type { Storage, StorageOptions } from '@google-cloud/storage';\nimport { scrubCredentials } from './scrub-error';\n\n/** An interceptor's request hook, as the client's constructor takes one; `getRequestInterceptors` types them `Function`. */\ntype Intercept = NonNullable<StorageOptions['interceptors_']>[number]['request'];\n\n/**\n * A twin of `client` that sends each download once, or `client` itself when it is a test double whose retries are off.\n * `supplied`: the caller handed `client` in, rather than the backend building it.\n */\nexport function downloadClient(client: Storage, supplied: boolean): Storage {\n // A test double has nothing a twin could copy, and is used as it is when it says its retries are off.\n if (!isSdkClient(client)) {\n if (client.retryOptions?.autoRetry === false) return client;\n throw supplied\n ? refusal(\n true,\n 'it is not a `Storage` client (a test double, say)',\n 'Give it `retryOptions: { autoRetry: false }` to have it used as it is',\n )\n : refusal(\n false,\n \"the `Storage` it was built from is not the SDK's (a mocked module, say)\",\n 'Pass the mock as `client`, with `retryOptions: { autoRetry: false }`',\n );\n }\n const instead =\n 'Stub `Storage.prototype.bucket` instead, which reaches that client too, or pass a test double that does not ' +\n 'extend `Storage`, with `retryOptions: { autoRetry: false }`';\n if (Object.hasOwn(client, 'bucket')) {\n throw refusal(\n supplied,\n 'its `bucket` method is replaced on the instance, as by a test stub, which a client built from its class would ' +\n 'not have',\n instead,\n );\n }\n if (overridesBucket(client)) {\n throw refusal(\n supplied,\n 'its class overrides `bucket`, as a test double built on `Storage` does, so a client built from its class need ' +\n 'not hold what this one holds',\n instead,\n );\n }\n const second = supplied\n ? 'a second client built from its class with its settings'\n : 'a second client built from the same settings';\n const remedy = supplied\n ? 'Pass a client its class builds from the options it is given, as `Storage` does, or leave out `client`'\n : undefined;\n let twin: Storage;\n try {\n twin = twinOf(client);\n } catch (err) {\n throw refusal(supplied, `${second} could not be built`, remedy, err);\n }\n const why = unfaithful(client, twin);\n if (why !== undefined) throw refusal(supplied, `${second} ${why}`, remedy);\n return twin;\n}\n\n/**\n * Whether `client` is the SDK's own: its constructor sets `authClient` from the request factory it builds. A mock that\n * answers every property with a function, as an auto-mocking library's does, answers that one too, but not with the\n * same credentials object.\n */\nfunction isSdkClient(client: Storage): boolean {\n const factory: unknown = (client as { makeAuthenticatedRequest?: unknown })\n .makeAuthenticatedRequest;\n return (\n client.authClient !== undefined &&\n typeof factory === 'function' &&\n (factory as { authClient?: unknown }).authClient === client.authClient\n );\n}\n\n/** Whether a subclass of the SDK's client defines `bucket` over the SDK's own. */\nfunction overridesBucket(client: Storage): boolean {\n let defined = 0;\n for (let proto: unknown = Object.getPrototypeOf(client); proto !== null;) {\n if (Object.hasOwn(proto as object, 'bucket')) defined++;\n proto = Object.getPrototypeOf(proto);\n }\n return defined > 1;\n}\n\nfunction twinOf(client: Storage): Storage {\n const Own = client.constructor as new (options: StorageOptions) => Storage;\n const twin = new Own({\n authClient: client.authClient as StorageOptions['authClient'],\n // The SDK treats an endpoint spelled other than its default as a custom one, whose requests go without credentials,\n // and then stores it with any trailing slash taken off. A client given the default in another spelling is custom\n // with the default's URL, which that URL with a slash reproduces.\n apiEndpoint: client.customEndpoint === true ? `${client.apiEndpoint}/` : client.apiEndpoint,\n universeDomain: client.universeDomain,\n projectId: client.projectId,\n ...(client.useAuthWithCustomEndpoint === undefined\n ? {}\n : { useAuthWithCustomEndpoint: client.useAuthWithCustomEndpoint }),\n ...(client.providedUserAgent === undefined ? {} : { userAgent: client.providedUserAgent }),\n ...(client.timeout === undefined ? {} : { timeout: client.timeout }),\n crc32cGenerator: client.crc32cGenerator,\n retryOptions: { ...client.retryOptions, autoRetry: false },\n // A view, not a copy: the client's interceptors as they are when each request is sent.\n interceptors_: [\n {\n request: (options: Parameters<Intercept>[0]) =>\n (client.getRequestInterceptors() as Intercept[]).reduce(\n (sent, intercept) => intercept(sent),\n options as ReturnType<Intercept>,\n ),\n },\n ],\n });\n // A subclass may set retry settings of its own over the ones it is given. Turned off here, as the SDK turns them off\n // for a request it must not retry, on the twin's own settings only: never on an object the client holds too.\n if (\n twin !== client &&\n twin.retryOptions !== undefined &&\n twin.retryOptions !== client.retryOptions\n ) {\n twin.retryOptions.autoRetry = false;\n }\n return twin;\n}\n\n/**\n * What a twin did not take of the settings that decide where and how a download is sent, or `undefined`. A subclass may\n * build from options of its own, and would send downloads with other credentials, with the SDK's retries, or elsewhere.\n * Interceptors are carried as a view and not checked.\n */\nfunction unfaithful(client: Storage, twin: Storage): string | undefined {\n if (twin === client) return 'is the same client';\n if (typeof twin.bucket !== 'function') return 'has no `bucket` method';\n if (twin.authClient === undefined || twin.authClient !== client.authClient) {\n return 'would use other credentials';\n }\n if (twin.retryOptions === client.retryOptions) return 'would share its retry settings';\n if (twin.retryOptions?.autoRetry !== false) return \"keeps the SDK's retries\";\n // `baseUrl` prefers STORAGE_EMULATOR_HOST over `apiEndpoint` as each client is built.\n if (twin.baseUrl !== client.baseUrl) {\n return 'would send to another URL, as when `STORAGE_EMULATOR_HOST` changed after the client was built or is not a URL';\n }\n if (twin.apiEndpoint !== client.apiEndpoint) return 'would name another endpoint';\n if (twin.customEndpoint !== client.customEndpoint) {\n return 'would decide otherwise whether requests carry credentials';\n }\n return undefined;\n}\n\nfunction refusal(\n supplied: boolean,\n why: string,\n remedy: string | undefined,\n cause?: unknown,\n): ValidationError {\n const err = new ValidationError(\n (supplied\n ? `GcsStorage cannot send the supplied client's downloads without the SDK's retries: ${why}`\n : `GcsStorage cannot build a client that sends downloads without the SDK's retries: ${why}`) +\n (remedy === undefined ? '' : `. ${remedy}`),\n );\n // The credential-free copy, never the SDK's error as raised.\n if (cause !== undefined) err.cause = scrubCredentials(cause);\n return err;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["ValidationError", "TransientError", "ValidationError", "once", "ValidationError", "ValidationError", "TransientError", "once", "TransientError", "ValidationError", "WriteConflictError", "normalizeObjectPrefix", "WriteConflictError", "TransientError", "ValidationError", "normalizeObjectPrefix", "ValidationError", "ValidationError", "ValidationError"]
7
7
  }
@@ -55,8 +55,7 @@ export interface GcsRegistryDriverOptions {
55
55
  readonly readTimeoutMs?: number;
56
56
  /**
57
57
  * Whether a delete removes a row for good, by an object delete sent with `ifGenerationMatch: <the generation it
58
- * read>`, rather than leaving a tombstone. Only a row born with an incarnation id is removed; a row a release before
59
- * 0.12 wrote is always tombstoned. Defaults to `false`, on the public endpoint too: whether real GCS applies
58
+ * read>`, rather than leaving a tombstone. Defaults to `false`, on the public endpoint too: whether real GCS applies
60
59
  * `ifGenerationMatch` to a delete has not been verified by a run against the service, and fake-gcs-server accepts the
61
60
  * precondition and ignores it, so CI cannot show it. Set `true` to remove rows for good.
62
61
  */
package/dist/storage.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BlobSink, GenKey, IStorageDriver, SegmentRef, StorageCaps } from '@cloudbitmaps/core/driver-kit';
1
+ import type { BlobSink, GenKey, IStorageDriver, SegmentRef, StorageCaps, StorageDeleteOptions, TailRead } from '@cloudbitmaps/core/driver-kit';
2
2
  import type { Storage } from '@google-cloud/storage';
3
3
  /** What the throttle backoff waits on. */
4
4
  interface Sleeper {
@@ -32,6 +32,15 @@ export interface GcsStorageDriverOptions {
32
32
  readonly readTimeoutMs?: number;
33
33
  /** What the backoff before re-sending a throttled upload waits on; real time when absent. */
34
34
  readonly clock?: Sleeper;
35
+ /**
36
+ * Whether a delete given `ifVersion` is sent with `ifGenerationMatch`, so it removes the object only while it is the
37
+ * one that generation names. Defaults to `false`, on the public endpoint too, as the registry's option does: whether
38
+ * real GCS applies `ifGenerationMatch` to a delete has not been verified by a run against the service, and
39
+ * fake-gcs-server accepts the precondition and ignores it. Set `true` once you have. Until it is set, a delete
40
+ * decided from one read of an object removes whatever is under the name by then: on GCS the window in which an
41
+ * erasure's stalled delete takes a generation a load put under the number since stays open.
42
+ */
43
+ readonly conditionalDelete?: boolean;
35
44
  }
36
45
  export declare class GcsStorageDriver implements IStorageDriver {
37
46
  private readonly storage;
@@ -43,6 +52,7 @@ export declare class GcsStorageDriver implements IStorageDriver {
43
52
  private readonly readTimeoutMs;
44
53
  private readonly clock;
45
54
  private readonly presence;
55
+ private readonly conditionalDelete;
46
56
  constructor(options: GcsStorageDriverOptions);
47
57
  capabilities(): StorageCaps;
48
58
  private file;
@@ -63,15 +73,13 @@ export declare class GcsStorageDriver implements IStorageDriver {
63
73
  */
64
74
  private checkRange;
65
75
  private badRange;
66
- getTail(key: GenKey, maxBytes: number): Promise<{
67
- bytes: Uint8Array;
68
- size: number;
69
- }>;
76
+ getTail(key: GenKey, maxBytes: number): Promise<TailRead>;
70
77
  /**
71
- * The object's size from its metadata, validated. Read on the download client and retried as a download is, within
72
- * the calling read's `deadline` ({@link withDeadline}), so nothing but the one request in flight runs past it.
78
+ * The object's size from its metadata, validated, and its generation when the metadata names a usable one. Read on the
79
+ * download client and retried as a download is, within the calling read's `deadline` ({@link withDeadline}), so
80
+ * nothing but the one request in flight runs past it.
73
81
  */
74
- private sizeOf;
82
+ private metadataOf;
75
83
  /**
76
84
  * The object's total size from a tail response, refused unless every header agrees with the bytes received. A 206
77
85
  * must carry `Content-Range: bytes a-b/total` that names exactly the suffix asked for and exactly the bytes held;
@@ -79,7 +87,9 @@ export declare class GcsStorageDriver implements IStorageDriver {
79
87
  */
80
88
  private tailSize;
81
89
  private badTail;
82
- delete(key: GenKey): Promise<void>;
90
+ delete(key: GenKey, options?: StorageDeleteOptions): Promise<void>;
91
+ /** Whether an object is under the name, from its metadata. A read that fails in any other way throws. */
92
+ private exists;
83
93
  list(ref: SegmentRef): AsyncIterable<GenKey>;
84
94
  /** A read's error, once a 404 is known to be the object's and not the bucket's ({@link BucketPresence}). */
85
95
  private readFailure;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbitmaps/gcs",
3
- "version": "0.19.1",
3
+ "version": "0.20.0",
4
4
  "description": "Google Cloud Storage for CloudBitmaps",
5
5
  "keywords": [
6
6
  "cloudbitmaps",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@google-cloud/storage": "^7 || ^8",
52
- "@cloudbitmaps/core": "^0.19.1"
52
+ "@cloudbitmaps/core": "^0.20.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/node": "^22.0.0",