@cloudbitmaps/gcs 0.19.1 → 0.21.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.
@@ -36,7 +36,7 @@
36
36
  import { ObjectStoreRegistry } from '@cloudbitmaps/core/driver-kit';
37
37
  import type { ObjectRegistryStore, ObjectRow } from '@cloudbitmaps/core/driver-kit';
38
38
  import type { Storage } from '@google-cloud/storage';
39
- export interface GcsRegistryDriverOptions {
39
+ interface GcsRegistryDriverOptions {
40
40
  /** A constructed `@google-cloud/storage` `Storage` client (point `apiEndpoint` at fake-gcs-server locally). */
41
41
  readonly storage: Storage;
42
42
  /** The client the downloads go through; `storage` when absent (see `GcsStorageDriverOptions.readStorage`). */
@@ -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
  */
@@ -88,3 +87,4 @@ export declare class GcsRegistryStore implements ObjectRegistryStore {
88
87
  export declare class GcsRegistryDriver extends ObjectStoreRegistry {
89
88
  constructor(options: GcsRegistryDriverOptions);
90
89
  }
90
+ export {};
package/dist/storage.d.ts CHANGED
@@ -1,10 +1,9 @@
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
+ import type { Clock, Rng } from '@cloudbitmaps/core';
2
3
  import type { Storage } from '@google-cloud/storage';
3
4
  /** What the throttle backoff waits on. */
4
- interface Sleeper {
5
- sleep(ms: number): Promise<void>;
6
- }
7
- export interface GcsStorageDriverOptions {
5
+ type Sleeper = Pick<Clock, 'sleep'>;
6
+ interface GcsStorageDriverOptions {
8
7
  /** A constructed `@google-cloud/storage` `Storage` client (point `apiEndpoint` at fake-gcs-server locally). */
9
8
  readonly storage: Storage;
10
9
  /**
@@ -32,6 +31,17 @@ export interface GcsStorageDriverOptions {
32
31
  readonly readTimeoutMs?: number;
33
32
  /** What the backoff before re-sending a throttled upload waits on; real time when absent. */
34
33
  readonly clock?: Sleeper;
34
+ /** Where the jitter of that backoff is drawn from; `Math.random` when absent. */
35
+ readonly rng?: Rng;
36
+ /**
37
+ * Whether a delete given `ifVersion` is sent with `ifGenerationMatch`, so it removes the object only while it is the
38
+ * one that generation names. Defaults to `false`, on the public endpoint too, as the registry's option does: whether
39
+ * real GCS applies `ifGenerationMatch` to a delete has not been verified by a run against the service, and
40
+ * fake-gcs-server accepts the precondition and ignores it. Set `true` once you have. Until it is set, a delete
41
+ * decided from one read of an object removes whatever is under the name by then: on GCS the window in which an
42
+ * erasure's stalled delete takes a generation a load put under the number since stays open.
43
+ */
44
+ readonly conditionalDelete?: boolean;
35
45
  }
36
46
  export declare class GcsStorageDriver implements IStorageDriver {
37
47
  private readonly storage;
@@ -42,7 +52,9 @@ export declare class GcsStorageDriver implements IStorageDriver {
42
52
  private readonly threshold;
43
53
  private readonly readTimeoutMs;
44
54
  private readonly clock;
55
+ private readonly rng;
45
56
  private readonly presence;
57
+ private readonly conditionalDelete;
46
58
  constructor(options: GcsStorageDriverOptions);
47
59
  capabilities(): StorageCaps;
48
60
  private file;
@@ -63,15 +75,13 @@ export declare class GcsStorageDriver implements IStorageDriver {
63
75
  */
64
76
  private checkRange;
65
77
  private badRange;
66
- getTail(key: GenKey, maxBytes: number): Promise<{
67
- bytes: Uint8Array;
68
- size: number;
69
- }>;
78
+ getTail(key: GenKey, maxBytes: number): Promise<TailRead>;
70
79
  /**
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.
80
+ * The object's size from its metadata, validated, and its generation when the metadata names a usable one. Read on the
81
+ * download client and retried as a download is, within the calling read's `deadline` ({@link withDeadline}), so
82
+ * nothing but the one request in flight runs past it.
73
83
  */
74
- private sizeOf;
84
+ private metadataOf;
75
85
  /**
76
86
  * The object's total size from a tail response, refused unless every header agrees with the bytes received. A 206
77
87
  * must carry `Content-Range: bytes a-b/total` that names exactly the suffix asked for and exactly the bytes held;
@@ -79,17 +89,20 @@ export declare class GcsStorageDriver implements IStorageDriver {
79
89
  */
80
90
  private tailSize;
81
91
  private badTail;
82
- delete(key: GenKey): Promise<void>;
92
+ delete(key: GenKey, options?: StorageDeleteOptions): Promise<void>;
93
+ /** Whether an object is under the name, from its metadata. A read that fails in any other way throws. */
94
+ private exists;
83
95
  list(ref: SegmentRef): AsyncIterable<GenKey>;
84
96
  /** A read's error, once a 404 is known to be the object's and not the bucket's ({@link BucketPresence}). */
85
97
  private readFailure;
86
98
  /** Map GCS read errors to the driver vocabulary; pass everything else through {@link mapError}. */
87
99
  private mapReadError;
88
100
  /**
89
- * Reclassify a transient GCS fault (throttle/5xx/dropped connection) as a retryable {@link TransientError},
90
- * so the store's read retry can ride it out and a write's caller can tell it from a deterministic failure;
91
- * everything else propagates unchanged. The final fallback at every client-call site: an HTTP status and a fault
92
- * in transit arrive typed, and anything else (a checksum mismatch, a credentials failure) as the SDK raised it.
101
+ * Reclassify a transient GCS fault (throttle/5xx/dropped connection) as a retryable {@link TransientError}, so the
102
+ * store's read retry can ride it out and a write's caller can tell it from a deterministic failure, and a missing
103
+ * bucket as a `ValidationError` that names it; everything else propagates unchanged. The final fallback at every
104
+ * client-call site: an HTTP status and a fault in transit arrive typed, and anything else (a checksum mismatch, a
105
+ * credentials failure) as the SDK raised it.
93
106
  */
94
107
  private mapError;
95
108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbitmaps/gcs",
3
- "version": "0.19.1",
3
+ "version": "0.21.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.21.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/node": "^22.0.0",
package/dist/keys.d.ts DELETED
@@ -1,31 +0,0 @@
1
- /**
2
- * Logical-ref → GCS object-name mapping for {@link GcsStorageDriver}.
3
- *
4
- * Pure string logic, no SDK dependency — unit-testable without GCS or an emulator. Uses the **same
5
- * backend-agnostic `.crbm` object-name scheme** as the S3 + LocalFs storage drivers
6
- * (`<prefix><ns>/segments/<segment>.<gen>.crbm`), so a segment reads identically whichever storage backend holds
7
- * it. The default (absent) namespace maps to `_default`, which cannot collide with a real namespace because a
8
- * caller's `_default` encodes to `%5Fdefault` while the sentinel is emitted literally.
9
- *
10
- * The storage-key layout is shared, not copied: `prefixPart` and the name encoders come from
11
- * `@cloudbitmaps/core/driver-kit`, so a segment written by one driver reads identically under another. Two
12
- * drivers disagreeing about how a name becomes a key would be a silent cross-driver incompatibility on the
13
- * same bucket, which is exactly the kind of thing a local copy drifts into.
14
- */
15
- import type { GenKey, SegmentRef } from '@cloudbitmaps/core/driver-kit';
16
- /** Validate a caller-supplied key prefix. The rule is shared with every other object store. */
17
- export { normalizeObjectPrefix as normalizeGcsPrefix } from '@cloudbitmaps/core/driver-kit';
18
- /**
19
- * The GCS object-name prefix shared by all of a segment's generations: `<prefix><ns>/segments/<segment>.`.
20
- * Used both as the `getFiles` prefix and as the string stripped by {@link parseGenerationFromName}.
21
- */
22
- export declare function segmentObjectPrefix(prefix: string | undefined, ref: SegmentRef): string;
23
- /** The full GCS object name of one `.crbm` generation: `<segmentPrefix><gen>.crbm`. */
24
- export declare function storageObjectName(prefix: string | undefined, key: GenKey): string;
25
- /**
26
- * Parse a generation number out of a full object name, given its segment prefix, or `null` if it doesn't
27
- * match. Canonical decimal only — no leading zeros (so `…s.07.crbm` can't alias `…s.7.crbm`) and within
28
- * safe-integer range. Also rejects a *different* segment whose name merely shares the prefix (its middle
29
- * isn't all digits).
30
- */
31
- export declare function parseGenerationFromName(segmentPrefix: string, objectName: string): number | null;