@streetjs/storage 1.0.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/LICENSE +21 -0
- package/README.md +719 -0
- package/dist/access.d.ts +95 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +114 -0
- package/dist/access.js.map +1 -0
- package/dist/cli/commands.d.ts +88 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +369 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/generators.d.ts +41 -0
- package/dist/cli/generators.d.ts.map +1 -0
- package/dist/cli/generators.js +119 -0
- package/dist/cli/generators.js.map +1 -0
- package/dist/directory.d.ts +88 -0
- package/dist/directory.d.ts.map +1 -0
- package/dist/directory.js +148 -0
- package/dist/directory.js.map +1 -0
- package/dist/driver.d.ts +83 -0
- package/dist/driver.d.ts.map +1 -0
- package/dist/driver.js +16 -0
- package/dist/driver.js.map +1 -0
- package/dist/drivers/azure.d.ts +239 -0
- package/dist/drivers/azure.d.ts.map +1 -0
- package/dist/drivers/azure.js +476 -0
- package/dist/drivers/azure.js.map +1 -0
- package/dist/drivers/backblaze.d.ts +105 -0
- package/dist/drivers/backblaze.d.ts.map +1 -0
- package/dist/drivers/backblaze.js +279 -0
- package/dist/drivers/backblaze.js.map +1 -0
- package/dist/drivers/gcs.d.ts +239 -0
- package/dist/drivers/gcs.d.ts.map +1 -0
- package/dist/drivers/gcs.js +454 -0
- package/dist/drivers/gcs.js.map +1 -0
- package/dist/drivers/local.d.ts +126 -0
- package/dist/drivers/local.d.ts.map +1 -0
- package/dist/drivers/local.js +321 -0
- package/dist/drivers/local.js.map +1 -0
- package/dist/drivers/memory.d.ts +104 -0
- package/dist/drivers/memory.d.ts.map +1 -0
- package/dist/drivers/memory.js +197 -0
- package/dist/drivers/memory.js.map +1 -0
- package/dist/drivers/minio.d.ts +90 -0
- package/dist/drivers/minio.d.ts.map +1 -0
- package/dist/drivers/minio.js +276 -0
- package/dist/drivers/minio.js.map +1 -0
- package/dist/drivers/r2.d.ts +100 -0
- package/dist/drivers/r2.d.ts.map +1 -0
- package/dist/drivers/r2.js +240 -0
- package/dist/drivers/r2.js.map +1 -0
- package/dist/drivers/s3-base.d.ts +270 -0
- package/dist/drivers/s3-base.d.ts.map +1 -0
- package/dist/drivers/s3-base.js +436 -0
- package/dist/drivers/s3-base.js.map +1 -0
- package/dist/drivers/s3.d.ts +111 -0
- package/dist/drivers/s3.d.ts.map +1 -0
- package/dist/drivers/s3.js +311 -0
- package/dist/drivers/s3.js.map +1 -0
- package/dist/drivers/supabase.d.ts +246 -0
- package/dist/drivers/supabase.d.ts.map +1 -0
- package/dist/drivers/supabase.js +502 -0
- package/dist/drivers/supabase.js.map +1 -0
- package/dist/errors.d.ts +115 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +131 -0
- package/dist/errors.js.map +1 -0
- package/dist/examples/storage-demo.d.ts +53 -0
- package/dist/examples/storage-demo.d.ts.map +1 -0
- package/dist/examples/storage-demo.js +242 -0
- package/dist/examples/storage-demo.js.map +1 -0
- package/dist/facade.d.ts +192 -0
- package/dist/facade.d.ts.map +1 -0
- package/dist/facade.js +898 -0
- package/dist/facade.js.map +1 -0
- package/dist/image.d.ts +94 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +224 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/events.d.ts +108 -0
- package/dist/integrations/events.d.ts.map +1 -0
- package/dist/integrations/events.js +111 -0
- package/dist/integrations/events.js.map +1 -0
- package/dist/integrations/queue.d.ts +89 -0
- package/dist/integrations/queue.d.ts.map +1 -0
- package/dist/integrations/queue.js +91 -0
- package/dist/integrations/queue.js.map +1 -0
- package/dist/integrations/realtime.d.ts +95 -0
- package/dist/integrations/realtime.d.ts.map +1 -0
- package/dist/integrations/realtime.js +90 -0
- package/dist/integrations/realtime.js.map +1 -0
- package/dist/lifecycle.d.ts +137 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +240 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/metadata.d.ts +109 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +139 -0
- package/dist/metadata.js.map +1 -0
- package/dist/multipart.d.ts +108 -0
- package/dist/multipart.d.ts.map +1 -0
- package/dist/multipart.js +200 -0
- package/dist/multipart.js.map +1 -0
- package/dist/observability.d.ts +82 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +175 -0
- package/dist/observability.js.map +1 -0
- package/dist/plugin.d.ts +82 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +100 -0
- package/dist/plugin.js.map +1 -0
- package/dist/resumable.d.ts +118 -0
- package/dist/resumable.d.ts.map +1 -0
- package/dist/resumable.js +223 -0
- package/dist/resumable.js.map +1 -0
- package/dist/search.d.ts +57 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +172 -0
- package/dist/search.js.map +1 -0
- package/dist/signed-url.d.ts +110 -0
- package/dist/signed-url.d.ts.map +1 -0
- package/dist/signed-url.js +237 -0
- package/dist/signed-url.js.map +1 -0
- package/dist/testing/index.d.ts +219 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +393 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tests/access.test.d.ts +2 -0
- package/dist/tests/access.test.d.ts.map +1 -0
- package/dist/tests/access.test.js +270 -0
- package/dist/tests/access.test.js.map +1 -0
- package/dist/tests/azure-driver.test.d.ts +2 -0
- package/dist/tests/azure-driver.test.d.ts.map +1 -0
- package/dist/tests/azure-driver.test.js +183 -0
- package/dist/tests/azure-driver.test.js.map +1 -0
- package/dist/tests/backblaze-driver.test.d.ts +2 -0
- package/dist/tests/backblaze-driver.test.d.ts.map +1 -0
- package/dist/tests/backblaze-driver.test.js +185 -0
- package/dist/tests/backblaze-driver.test.js.map +1 -0
- package/dist/tests/bridge-degradation.test.d.ts +2 -0
- package/dist/tests/bridge-degradation.test.d.ts.map +1 -0
- package/dist/tests/bridge-degradation.test.js +138 -0
- package/dist/tests/bridge-degradation.test.js.map +1 -0
- package/dist/tests/cli.test.d.ts +2 -0
- package/dist/tests/cli.test.d.ts.map +1 -0
- package/dist/tests/cli.test.js +127 -0
- package/dist/tests/cli.test.js.map +1 -0
- package/dist/tests/contract.d.ts +131 -0
- package/dist/tests/contract.d.ts.map +1 -0
- package/dist/tests/contract.js +222 -0
- package/dist/tests/contract.js.map +1 -0
- package/dist/tests/contract.test.d.ts +2 -0
- package/dist/tests/contract.test.d.ts.map +1 -0
- package/dist/tests/contract.test.js +110 -0
- package/dist/tests/contract.test.js.map +1 -0
- package/dist/tests/directory.test.d.ts +2 -0
- package/dist/tests/directory.test.d.ts.map +1 -0
- package/dist/tests/directory.test.js +125 -0
- package/dist/tests/directory.test.js.map +1 -0
- package/dist/tests/errors.test.d.ts +2 -0
- package/dist/tests/errors.test.d.ts.map +1 -0
- package/dist/tests/errors.test.js +93 -0
- package/dist/tests/errors.test.js.map +1 -0
- package/dist/tests/events-bridge.test.d.ts +2 -0
- package/dist/tests/events-bridge.test.d.ts.map +1 -0
- package/dist/tests/events-bridge.test.js +228 -0
- package/dist/tests/events-bridge.test.js.map +1 -0
- package/dist/tests/example-smoke.test.d.ts +2 -0
- package/dist/tests/example-smoke.test.d.ts.map +1 -0
- package/dist/tests/example-smoke.test.js +34 -0
- package/dist/tests/example-smoke.test.js.map +1 -0
- package/dist/tests/gcs-driver.test.d.ts +2 -0
- package/dist/tests/gcs-driver.test.d.ts.map +1 -0
- package/dist/tests/gcs-driver.test.js +197 -0
- package/dist/tests/gcs-driver.test.js.map +1 -0
- package/dist/tests/image.test.d.ts +2 -0
- package/dist/tests/image.test.d.ts.map +1 -0
- package/dist/tests/image.test.js +102 -0
- package/dist/tests/image.test.js.map +1 -0
- package/dist/tests/lifecycle.test.d.ts +2 -0
- package/dist/tests/lifecycle.test.d.ts.map +1 -0
- package/dist/tests/lifecycle.test.js +171 -0
- package/dist/tests/lifecycle.test.js.map +1 -0
- package/dist/tests/local-driver.test.d.ts +2 -0
- package/dist/tests/local-driver.test.d.ts.map +1 -0
- package/dist/tests/local-driver.test.js +153 -0
- package/dist/tests/local-driver.test.js.map +1 -0
- package/dist/tests/local-streaming.test.d.ts +2 -0
- package/dist/tests/local-streaming.test.d.ts.map +1 -0
- package/dist/tests/local-streaming.test.js +121 -0
- package/dist/tests/local-streaming.test.js.map +1 -0
- package/dist/tests/memory-driver.test.d.ts +2 -0
- package/dist/tests/memory-driver.test.d.ts.map +1 -0
- package/dist/tests/memory-driver.test.js +180 -0
- package/dist/tests/memory-driver.test.js.map +1 -0
- package/dist/tests/memory-streaming.test.d.ts +2 -0
- package/dist/tests/memory-streaming.test.d.ts.map +1 -0
- package/dist/tests/memory-streaming.test.js +92 -0
- package/dist/tests/memory-streaming.test.js.map +1 -0
- package/dist/tests/minio-driver.test.d.ts +2 -0
- package/dist/tests/minio-driver.test.d.ts.map +1 -0
- package/dist/tests/minio-driver.test.js +146 -0
- package/dist/tests/minio-driver.test.js.map +1 -0
- package/dist/tests/non-s3-integration.test.d.ts +2 -0
- package/dist/tests/non-s3-integration.test.d.ts.map +1 -0
- package/dist/tests/non-s3-integration.test.js +201 -0
- package/dist/tests/non-s3-integration.test.js.map +1 -0
- package/dist/tests/observability.registration.test.d.ts +2 -0
- package/dist/tests/observability.registration.test.d.ts.map +1 -0
- package/dist/tests/observability.registration.test.js +103 -0
- package/dist/tests/observability.registration.test.js.map +1 -0
- package/dist/tests/observability.test.d.ts +2 -0
- package/dist/tests/observability.test.d.ts.map +1 -0
- package/dist/tests/observability.test.js +240 -0
- package/dist/tests/observability.test.js.map +1 -0
- package/dist/tests/plugin.test.d.ts +2 -0
- package/dist/tests/plugin.test.d.ts.map +1 -0
- package/dist/tests/plugin.test.js +112 -0
- package/dist/tests/plugin.test.js.map +1 -0
- package/dist/tests/prop-concurrent.test.d.ts +2 -0
- package/dist/tests/prop-concurrent.test.d.ts.map +1 -0
- package/dist/tests/prop-concurrent.test.js +59 -0
- package/dist/tests/prop-concurrent.test.js.map +1 -0
- package/dist/tests/prop-copy.test.d.ts +2 -0
- package/dist/tests/prop-copy.test.d.ts.map +1 -0
- package/dist/tests/prop-copy.test.js +63 -0
- package/dist/tests/prop-copy.test.js.map +1 -0
- package/dist/tests/prop-delete.test.d.ts +2 -0
- package/dist/tests/prop-delete.test.d.ts.map +1 -0
- package/dist/tests/prop-delete.test.js +35 -0
- package/dist/tests/prop-delete.test.js.map +1 -0
- package/dist/tests/prop-directory.test.d.ts +2 -0
- package/dist/tests/prop-directory.test.d.ts.map +1 -0
- package/dist/tests/prop-directory.test.js +118 -0
- package/dist/tests/prop-directory.test.js.map +1 -0
- package/dist/tests/prop-driver-conformance.test.d.ts +2 -0
- package/dist/tests/prop-driver-conformance.test.d.ts.map +1 -0
- package/dist/tests/prop-driver-conformance.test.js +110 -0
- package/dist/tests/prop-driver-conformance.test.js.map +1 -0
- package/dist/tests/prop-driver-equivalence.test.d.ts +2 -0
- package/dist/tests/prop-driver-equivalence.test.d.ts.map +1 -0
- package/dist/tests/prop-driver-equivalence.test.js +163 -0
- package/dist/tests/prop-driver-equivalence.test.js.map +1 -0
- package/dist/tests/prop-lifecycle.test.d.ts +2 -0
- package/dist/tests/prop-lifecycle.test.d.ts.map +1 -0
- package/dist/tests/prop-lifecycle.test.js +83 -0
- package/dist/tests/prop-lifecycle.test.js.map +1 -0
- package/dist/tests/prop-list.test.d.ts +2 -0
- package/dist/tests/prop-list.test.d.ts.map +1 -0
- package/dist/tests/prop-list.test.js +53 -0
- package/dist/tests/prop-list.test.js.map +1 -0
- package/dist/tests/prop-metadata.test.d.ts +2 -0
- package/dist/tests/prop-metadata.test.d.ts.map +1 -0
- package/dist/tests/prop-metadata.test.js +87 -0
- package/dist/tests/prop-metadata.test.js.map +1 -0
- package/dist/tests/prop-move.test.d.ts +2 -0
- package/dist/tests/prop-move.test.d.ts.map +1 -0
- package/dist/tests/prop-move.test.js +67 -0
- package/dist/tests/prop-move.test.js.map +1 -0
- package/dist/tests/prop-multipart-abort.test.d.ts +2 -0
- package/dist/tests/prop-multipart-abort.test.d.ts.map +1 -0
- package/dist/tests/prop-multipart-abort.test.js +48 -0
- package/dist/tests/prop-multipart-abort.test.js.map +1 -0
- package/dist/tests/prop-multipart.test.d.ts +2 -0
- package/dist/tests/prop-multipart.test.d.ts.map +1 -0
- package/dist/tests/prop-multipart.test.js +65 -0
- package/dist/tests/prop-multipart.test.js.map +1 -0
- package/dist/tests/prop-resumable.test.d.ts +2 -0
- package/dist/tests/prop-resumable.test.d.ts.map +1 -0
- package/dist/tests/prop-resumable.test.js +78 -0
- package/dist/tests/prop-resumable.test.js.map +1 -0
- package/dist/tests/prop-search.test.d.ts +2 -0
- package/dist/tests/prop-search.test.d.ts.map +1 -0
- package/dist/tests/prop-search.test.js +120 -0
- package/dist/tests/prop-search.test.js.map +1 -0
- package/dist/tests/prop-signed-url.test.d.ts +2 -0
- package/dist/tests/prop-signed-url.test.d.ts.map +1 -0
- package/dist/tests/prop-signed-url.test.js +74 -0
- package/dist/tests/prop-signed-url.test.js.map +1 -0
- package/dist/tests/prop-streaming.test.d.ts +2 -0
- package/dist/tests/prop-streaming.test.d.ts.map +1 -0
- package/dist/tests/prop-streaming.test.js +34 -0
- package/dist/tests/prop-streaming.test.js.map +1 -0
- package/dist/tests/prop-upload-download.test.d.ts +2 -0
- package/dist/tests/prop-upload-download.test.d.ts.map +1 -0
- package/dist/tests/prop-upload-download.test.js +30 -0
- package/dist/tests/prop-upload-download.test.js.map +1 -0
- package/dist/tests/prop-validation.test.d.ts +2 -0
- package/dist/tests/prop-validation.test.d.ts.map +1 -0
- package/dist/tests/prop-validation.test.js +50 -0
- package/dist/tests/prop-validation.test.js.map +1 -0
- package/dist/tests/prop-versioning.test.d.ts +2 -0
- package/dist/tests/prop-versioning.test.d.ts.map +1 -0
- package/dist/tests/prop-versioning.test.js +74 -0
- package/dist/tests/prop-versioning.test.js.map +1 -0
- package/dist/tests/queue-bridge.test.d.ts +2 -0
- package/dist/tests/queue-bridge.test.d.ts.map +1 -0
- package/dist/tests/queue-bridge.test.js +111 -0
- package/dist/tests/queue-bridge.test.js.map +1 -0
- package/dist/tests/r2-driver.test.d.ts +2 -0
- package/dist/tests/r2-driver.test.d.ts.map +1 -0
- package/dist/tests/r2-driver.test.js +143 -0
- package/dist/tests/r2-driver.test.js.map +1 -0
- package/dist/tests/realtime-bridge.test.d.ts +2 -0
- package/dist/tests/realtime-bridge.test.d.ts.map +1 -0
- package/dist/tests/realtime-bridge.test.js +106 -0
- package/dist/tests/realtime-bridge.test.js.map +1 -0
- package/dist/tests/resumable.test.d.ts +2 -0
- package/dist/tests/resumable.test.d.ts.map +1 -0
- package/dist/tests/resumable.test.js +104 -0
- package/dist/tests/resumable.test.js.map +1 -0
- package/dist/tests/s3-base.test.d.ts +2 -0
- package/dist/tests/s3-base.test.d.ts.map +1 -0
- package/dist/tests/s3-base.test.js +289 -0
- package/dist/tests/s3-base.test.js.map +1 -0
- package/dist/tests/s3-integration.test.d.ts +2 -0
- package/dist/tests/s3-integration.test.d.ts.map +1 -0
- package/dist/tests/s3-integration.test.js +270 -0
- package/dist/tests/s3-integration.test.js.map +1 -0
- package/dist/tests/s3.test.d.ts +2 -0
- package/dist/tests/s3.test.d.ts.map +1 -0
- package/dist/tests/s3.test.js +203 -0
- package/dist/tests/s3.test.js.map +1 -0
- package/dist/tests/scaffold.test.d.ts +2 -0
- package/dist/tests/scaffold.test.d.ts.map +1 -0
- package/dist/tests/scaffold.test.js +13 -0
- package/dist/tests/scaffold.test.js.map +1 -0
- package/dist/tests/search.test.d.ts +2 -0
- package/dist/tests/search.test.d.ts.map +1 -0
- package/dist/tests/search.test.js +106 -0
- package/dist/tests/search.test.js.map +1 -0
- package/dist/tests/supabase-driver.test.d.ts +2 -0
- package/dist/tests/supabase-driver.test.d.ts.map +1 -0
- package/dist/tests/supabase-driver.test.js +196 -0
- package/dist/tests/supabase-driver.test.js.map +1 -0
- package/dist/tests/testing-utilities.test.d.ts +2 -0
- package/dist/tests/testing-utilities.test.d.ts.map +1 -0
- package/dist/tests/testing-utilities.test.js +138 -0
- package/dist/tests/testing-utilities.test.js.map +1 -0
- package/dist/tests/validation.test.d.ts +2 -0
- package/dist/tests/validation.test.d.ts.map +1 -0
- package/dist/tests/validation.test.js +135 -0
- package/dist/tests/validation.test.js.map +1 -0
- package/dist/tests/versioning.test.d.ts +2 -0
- package/dist/tests/versioning.test.d.ts.map +1 -0
- package/dist/tests/versioning.test.js +128 -0
- package/dist/tests/versioning.test.js.map +1 -0
- package/dist/types.d.ts +232 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +105 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +225 -0
- package/dist/validation.js.map +1 -0
- package/dist/versioning.d.ts +100 -0
- package/dist/versioning.d.ts.map +1 -0
- package/dist/versioning.js +179 -0
- package/dist/versioning.js.map +1 -0
- package/package.json +137 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — GoogleCloudStorageDriver (submodule `@streetjs/storage/gcs`).
|
|
3
|
+
*
|
|
4
|
+
* Google Cloud Storage is **not** an S3-wire-compatible provider — its SDK
|
|
5
|
+
* exposes a bucket/file object model (`bucket.file(name).save/download/delete/
|
|
6
|
+
* exists/getMetadata`, `bucket.getFiles({ prefix })`) rather than the
|
|
7
|
+
* `putObject`/`getObject` verbs the S3-style base is built on. So, unlike
|
|
8
|
+
* `r2.ts`/`minio.ts`/`backblaze.ts`, this driver does **not** extend the shared
|
|
9
|
+
* S3 base ({@link ../drivers/s3-base}); it maps the {@link StorageDriver}
|
|
10
|
+
* contract directly onto a purpose-built structural {@link GcsClientLike} that
|
|
11
|
+
* mirrors just the GCS calls it needs.
|
|
12
|
+
*
|
|
13
|
+
* ## Two ways to obtain a driver
|
|
14
|
+
*
|
|
15
|
+
* - {@link createGoogleCloudStorageDriver} wraps an **already-constructed,
|
|
16
|
+
* injected** {@link GcsClientLike} and returns a driver synchronously. This is
|
|
17
|
+
* the SDK-free path: no provider SDK is touched, so it is fully testable with
|
|
18
|
+
* an in-memory fake and needs no `@google-cloud/storage` install.
|
|
19
|
+
* - {@link connectGoogleCloudStorageDriver} **builds its own client** from GCS
|
|
20
|
+
* connection config. It resolves `@google-cloud/storage` through a **lazy
|
|
21
|
+
* dynamic `import()` performed inside the function** (never at module top
|
|
22
|
+
* level), so the optional peer dependency is only required when this path is
|
|
23
|
+
* actually used. If the SDK is not installed and no client was injected, it
|
|
24
|
+
* throws {@link StorageConfigError} (Requirement 1.5).
|
|
25
|
+
*
|
|
26
|
+
* ## Capability delegation (Requirement 2.3)
|
|
27
|
+
*
|
|
28
|
+
* The driver implements only the **mandatory primitives** (`put`/`get`/`exists`/
|
|
29
|
+
* `delete`/`stat`/`list`/`putStream`/`getStream`), producing every
|
|
30
|
+
* {@link StorageObjectMetadata} through the shared metadata layer
|
|
31
|
+
* ({@link buildObjectMetadata}) so the field set is identical to Memory/Local/S3,
|
|
32
|
+
* and reporting a missing key consistently ({@link MaybeObject} `{ found: false }`
|
|
33
|
+
* for `get`, `null` for `stat`, {@link NotFoundError} for `getStream`). Advanced
|
|
34
|
+
* capabilities (`multipart`/`resumable`/`versioning`/`signedUrl`/`lifecycle`) are
|
|
35
|
+
* intentionally left `undefined` so the facade simulates them over the
|
|
36
|
+
* primitives, keeping behavior identical across providers.
|
|
37
|
+
*
|
|
38
|
+
* ## SDK isolation (Requirements 2.1, 3.3)
|
|
39
|
+
*
|
|
40
|
+
* This module imports **no provider SDK at the top level** — only Node built-ins,
|
|
41
|
+
* `streetjs` (for the injected {@link Clock}), and this package's own type
|
|
42
|
+
* surface. `@google-cloud/storage` is referenced solely via a dynamic `import()`
|
|
43
|
+
* with a non-literal specifier inside {@link connectGoogleCloudStorageDriver}, so
|
|
44
|
+
* `tsc` never requires the SDK to be present and `streetjs` stays the only
|
|
45
|
+
* runtime dependency. This driver is a submodule-only export
|
|
46
|
+
* (`./gcs` → `dist/drivers/gcs.js`); it is intentionally not re-exported from the
|
|
47
|
+
* package's main `index.ts`.
|
|
48
|
+
*
|
|
49
|
+
* _Requirements: 2.1, 2.3, 3.3_
|
|
50
|
+
*/
|
|
51
|
+
import { createHash } from "node:crypto";
|
|
52
|
+
import { Readable, Writable } from "node:stream";
|
|
53
|
+
import { pipeline } from "node:stream/promises";
|
|
54
|
+
import { systemClock } from "streetjs";
|
|
55
|
+
import { NotFoundError, StorageConfigError } from "../errors.js";
|
|
56
|
+
import { buildObjectMetadata } from "../metadata.js";
|
|
57
|
+
/** The stable driver name surfaced as {@link StorageDriver.name} for GCS. */
|
|
58
|
+
const GCS_DRIVER_NAME = "gcs";
|
|
59
|
+
// ── Reserved user-metadata keys ────────────────────────────────────────────────
|
|
60
|
+
/**
|
|
61
|
+
* Reserved custom-metadata keys used to round-trip the typed metadata field set
|
|
62
|
+
* through GCS's string→string object-metadata map. Prefixed so they never
|
|
63
|
+
* collide with a caller's own custom fields.
|
|
64
|
+
*/
|
|
65
|
+
const MK = {
|
|
66
|
+
checksum: "x-street-checksum",
|
|
67
|
+
createdAt: "x-street-created-at",
|
|
68
|
+
updatedAt: "x-street-updated-at",
|
|
69
|
+
owner: "x-street-owner",
|
|
70
|
+
tenant: "x-street-tenant",
|
|
71
|
+
accessLevel: "x-street-access-level",
|
|
72
|
+
custom: "x-street-custom",
|
|
73
|
+
};
|
|
74
|
+
// ── The GCS driver ─────────────────────────────────────────────────────────────
|
|
75
|
+
/**
|
|
76
|
+
* Maps the {@link StorageDriver} contract onto an injected {@link GcsClientLike}.
|
|
77
|
+
*
|
|
78
|
+
* Object identity fields (`etag`/`checksum`/`size`) and timestamps are computed
|
|
79
|
+
* locally at write time — the checksum/etag are the sha-256 hex digest of the
|
|
80
|
+
* stored bytes, the size is the byte length, and timestamps come from the
|
|
81
|
+
* injected {@link Clock} — then encoded into the object's custom metadata so they
|
|
82
|
+
* round-trip on read. Advanced capabilities are left `undefined` for facade
|
|
83
|
+
* simulation.
|
|
84
|
+
*/
|
|
85
|
+
export class GoogleCloudStorageDriver {
|
|
86
|
+
/** Stable driver name. */
|
|
87
|
+
name = GCS_DRIVER_NAME;
|
|
88
|
+
client;
|
|
89
|
+
clock;
|
|
90
|
+
constructor(client, options = {}) {
|
|
91
|
+
this.client = client;
|
|
92
|
+
this.clock = options.clock ?? systemClock;
|
|
93
|
+
}
|
|
94
|
+
// ── Mandatory primitives ──────────────────────────────────────────────────
|
|
95
|
+
/**
|
|
96
|
+
* Store `bytes` under `key`, computing size and sha-256 checksum locally and
|
|
97
|
+
* taking timestamps from the injected clock. When overwriting, the original
|
|
98
|
+
* `createdAt` is preserved (a preceding attribute read). The typed metadata is
|
|
99
|
+
* encoded into the object's custom-metadata map so it round-trips on read
|
|
100
|
+
* (Requirements 4.1, 10.1, 10.2).
|
|
101
|
+
*/
|
|
102
|
+
async put(key, bytes, metadata) {
|
|
103
|
+
const stored = bytes.slice();
|
|
104
|
+
const checksum = sha256Hex(stored);
|
|
105
|
+
const now = this.clock();
|
|
106
|
+
const existing = await this.stat(key);
|
|
107
|
+
const objectMetadata = buildObjectMetadata({
|
|
108
|
+
key,
|
|
109
|
+
size: stored.byteLength,
|
|
110
|
+
checksum,
|
|
111
|
+
createdAt: existing?.createdAt ?? now,
|
|
112
|
+
updatedAt: now,
|
|
113
|
+
write: metadata,
|
|
114
|
+
});
|
|
115
|
+
await this.client.save(key, stored, {
|
|
116
|
+
contentType: objectMetadata.contentType,
|
|
117
|
+
metadata: encodeUserMetadata(objectMetadata),
|
|
118
|
+
});
|
|
119
|
+
return objectMetadata;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Read the object at `key`, returning a discriminated {@link MaybeObject} so
|
|
123
|
+
* absence is reported rather than thrown (Requirements 4.2, 2.4). Metadata is
|
|
124
|
+
* rebuilt from the object's attributes and encoded custom metadata via the
|
|
125
|
+
* shared metadata layer.
|
|
126
|
+
*/
|
|
127
|
+
async get(key) {
|
|
128
|
+
const bytes = await this.client.download(key);
|
|
129
|
+
if (bytes === null) {
|
|
130
|
+
return { found: false };
|
|
131
|
+
}
|
|
132
|
+
const attributes = await this.client.getMetadata(key);
|
|
133
|
+
const metadata = this.decodeMeta(key, bytes.byteLength, attributes);
|
|
134
|
+
return { found: true, bytes: bytes.slice(), metadata };
|
|
135
|
+
}
|
|
136
|
+
/** Report whether an object is stored under `key` (Requirement 4.3). */
|
|
137
|
+
async exists(key) {
|
|
138
|
+
return this.client.exists(key);
|
|
139
|
+
}
|
|
140
|
+
/** Delete the object at `key`; deleting a missing key is a no-op (Requirement 4.4). */
|
|
141
|
+
async delete(key) {
|
|
142
|
+
await this.client.delete(key);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Return the metadata for `key` without its content, or `null` when absent
|
|
146
|
+
* (Requirement 4.10).
|
|
147
|
+
*/
|
|
148
|
+
async stat(key) {
|
|
149
|
+
const attributes = await this.client.getMetadata(key);
|
|
150
|
+
if (attributes === null) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const size = parseSize(attributes.size);
|
|
154
|
+
return this.decodeMeta(key, size, attributes);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* List objects whose key begins with `prefix`, sorted by key for deterministic
|
|
158
|
+
* ordering (Requirement 4.9). Honors optional `cursor` (exclusive resume
|
|
159
|
+
* point), `limit`, and `delimiter` (collapse to immediate children on `/`).
|
|
160
|
+
*/
|
|
161
|
+
async list(prefix, options) {
|
|
162
|
+
const entries = await this.client.getFiles({
|
|
163
|
+
prefix,
|
|
164
|
+
delimiter: options?.delimiter === true ? "/" : undefined,
|
|
165
|
+
});
|
|
166
|
+
let items = entries
|
|
167
|
+
.filter((entry) => typeof entry.name === "string")
|
|
168
|
+
.map((entry) => ({
|
|
169
|
+
key: entry.name,
|
|
170
|
+
size: parseSize(entry.attributes?.size),
|
|
171
|
+
updatedAt: parseIso(entry.attributes?.updated) ?? 0,
|
|
172
|
+
}))
|
|
173
|
+
.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
|
174
|
+
const cursor = options?.cursor;
|
|
175
|
+
if (cursor !== undefined) {
|
|
176
|
+
items = items.filter((item) => item.key > cursor);
|
|
177
|
+
}
|
|
178
|
+
return typeof options?.limit === "number" ? items.slice(0, options.limit) : items;
|
|
179
|
+
}
|
|
180
|
+
// ── Streaming ───────────────────────────────────────────────────────────────
|
|
181
|
+
/**
|
|
182
|
+
* Consume a Node {@link Readable} and persist the assembled bytes under `key`
|
|
183
|
+
* with the same computed metadata as {@link put} (Requirement 5.1). Chunks are
|
|
184
|
+
* drained through a `pipeline` (correct backpressure and error propagation),
|
|
185
|
+
* then handed to {@link put}.
|
|
186
|
+
*/
|
|
187
|
+
async putStream(key, stream, metadata) {
|
|
188
|
+
const chunks = [];
|
|
189
|
+
const collector = new Writable({
|
|
190
|
+
write(chunk, _encoding, callback) {
|
|
191
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
192
|
+
callback();
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
await pipeline(stream, collector);
|
|
196
|
+
return this.put(key, new Uint8Array(Buffer.concat(chunks)), metadata);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Return a Node {@link Readable} of the stored bytes at `key` (Requirement
|
|
200
|
+
* 5.2). Throws {@link NotFoundError} for a missing key (Requirement 5.5).
|
|
201
|
+
*/
|
|
202
|
+
async getStream(key) {
|
|
203
|
+
const bytes = await this.client.download(key);
|
|
204
|
+
if (bytes === null) {
|
|
205
|
+
throw new NotFoundError(key);
|
|
206
|
+
}
|
|
207
|
+
return Readable.from([Buffer.from(bytes)]);
|
|
208
|
+
}
|
|
209
|
+
// ── Internal helpers ──────────────────────────────────────────────────────
|
|
210
|
+
/**
|
|
211
|
+
* Rebuild the typed {@link StorageObjectMetadata} field set from the object's
|
|
212
|
+
* attributes and the encoded custom-metadata map, applying the shared layer's
|
|
213
|
+
* canonical defaults. Provider-supplied values are used as best-effort
|
|
214
|
+
* fallbacks when the encoded fields are absent (e.g. an object written outside
|
|
215
|
+
* this driver): `etag`/`md5Hash`/`timeCreated`/`updated` back-fill the
|
|
216
|
+
* identity/timestamp fields, keeping the shape consistent across providers.
|
|
217
|
+
*/
|
|
218
|
+
decodeMeta(key, size, attributes) {
|
|
219
|
+
const attrs = attributes ?? {};
|
|
220
|
+
const um = attrs.metadata ?? {};
|
|
221
|
+
const now = this.clock();
|
|
222
|
+
const checksum = um[MK.checksum] ?? attrs.etag ?? attrs.md5Hash ?? "";
|
|
223
|
+
const createdAt = parseEpoch(um[MK.createdAt]) ?? parseIso(attrs.timeCreated) ?? now;
|
|
224
|
+
const updatedAt = parseEpoch(um[MK.updatedAt]) ?? parseIso(attrs.updated) ?? createdAt;
|
|
225
|
+
const write = {
|
|
226
|
+
contentType: attrs.contentType,
|
|
227
|
+
owner: um[MK.owner],
|
|
228
|
+
tenant: um[MK.tenant],
|
|
229
|
+
accessLevel: decodeAccessLevel(um[MK.accessLevel]),
|
|
230
|
+
custom: parseCustom(um[MK.custom]),
|
|
231
|
+
};
|
|
232
|
+
return buildObjectMetadata({
|
|
233
|
+
key,
|
|
234
|
+
size,
|
|
235
|
+
checksum,
|
|
236
|
+
etag: attrs.etag ?? checksum,
|
|
237
|
+
createdAt,
|
|
238
|
+
updatedAt,
|
|
239
|
+
write,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Wrap an injected {@link GcsClientLike} as a GCS driver. Synchronous and
|
|
245
|
+
* SDK-free: the caller supplies the client, so no provider SDK is resolved here.
|
|
246
|
+
*
|
|
247
|
+
* @throws {StorageConfigError} when no client is injected — use
|
|
248
|
+
* {@link connectGoogleCloudStorageDriver} to build a client from connection
|
|
249
|
+
* options instead.
|
|
250
|
+
*/
|
|
251
|
+
export function createGoogleCloudStorageDriver(client, options = {}) {
|
|
252
|
+
if (client === undefined || client === null) {
|
|
253
|
+
throw new StorageConfigError("createGoogleCloudStorageDriver requires an injected GcsClientLike. " +
|
|
254
|
+
"Pass a structural client, or use connectGoogleCloudStorageDriver(config) " +
|
|
255
|
+
"to build one from a bucket using the optional '@google-cloud/storage' peer dependency.", { provider: GCS_DRIVER_NAME });
|
|
256
|
+
}
|
|
257
|
+
return new GoogleCloudStorageDriver(client, options);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Build a GCS driver that owns its client, resolving `@google-cloud/storage`
|
|
261
|
+
* lazily.
|
|
262
|
+
*
|
|
263
|
+
* The SDK is loaded through a dynamic `import()` with a non-literal specifier so
|
|
264
|
+
* it is never a static/top-level dependency: `tsc` does not require it to be
|
|
265
|
+
* installed, and it is only resolved when this function runs. When the SDK
|
|
266
|
+
* cannot be resolved, a {@link StorageConfigError} is thrown (Requirement 1.5) —
|
|
267
|
+
* callers that cannot install the SDK should use
|
|
268
|
+
* {@link createGoogleCloudStorageDriver} with their own {@link GcsClientLike}
|
|
269
|
+
* instead.
|
|
270
|
+
*/
|
|
271
|
+
export async function connectGoogleCloudStorageDriver(config) {
|
|
272
|
+
if (!config.bucket) {
|
|
273
|
+
throw new StorageConfigError("GoogleCloudStorage requires a bucket name.", { provider: GCS_DRIVER_NAME });
|
|
274
|
+
}
|
|
275
|
+
// Lazy, non-literal dynamic import so the GCS SDK stays an optional peer that
|
|
276
|
+
// `tsc` never resolves statically and Node only loads on this path.
|
|
277
|
+
const specifier = "@google-cloud/storage";
|
|
278
|
+
let sdk;
|
|
279
|
+
try {
|
|
280
|
+
sdk = (await import(specifier));
|
|
281
|
+
}
|
|
282
|
+
catch (cause) {
|
|
283
|
+
throw new StorageConfigError('GoogleCloudStorage driver requires the optional peer dependency "@google-cloud/storage". ' +
|
|
284
|
+
"Install it, or inject your own GcsClientLike via createGoogleCloudStorageDriver().", { provider: GCS_DRIVER_NAME, cause });
|
|
285
|
+
}
|
|
286
|
+
let storage;
|
|
287
|
+
try {
|
|
288
|
+
storage = new sdk.Storage({
|
|
289
|
+
projectId: config.projectId,
|
|
290
|
+
keyFilename: config.keyFilename,
|
|
291
|
+
credentials: config.credentials,
|
|
292
|
+
apiEndpoint: config.apiEndpoint,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
catch (cause) {
|
|
296
|
+
throw new StorageConfigError(`Failed to construct a Google Cloud Storage client for bucket "${config.bucket}".`, { provider: GCS_DRIVER_NAME, cause });
|
|
297
|
+
}
|
|
298
|
+
const client = adaptGcsSdkBucket(storage.bucket(config.bucket));
|
|
299
|
+
return createGoogleCloudStorageDriver(client, { clock: config.clock });
|
|
300
|
+
}
|
|
301
|
+
// ── SDK → GcsClientLike adapter ────────────────────────────────────────────────
|
|
302
|
+
/**
|
|
303
|
+
* Adapt a `@google-cloud/storage` `Bucket` into the structural
|
|
304
|
+
* {@link GcsClientLike} this driver consumes. Only the methods the driver uses
|
|
305
|
+
* are mapped; the SDK response shapes are narrowed through the local structural
|
|
306
|
+
* types below so this module never imports concrete SDK types. A missing object
|
|
307
|
+
* (`code === 404`) is mapped to `null` so not-found semantics stay consistent.
|
|
308
|
+
*/
|
|
309
|
+
function adaptGcsSdkBucket(bucket) {
|
|
310
|
+
return {
|
|
311
|
+
async save(name, bytes, options) {
|
|
312
|
+
await bucket.file(name).save(Buffer.from(bytes), {
|
|
313
|
+
contentType: options?.contentType,
|
|
314
|
+
metadata: { metadata: options?.metadata },
|
|
315
|
+
});
|
|
316
|
+
},
|
|
317
|
+
async download(name) {
|
|
318
|
+
try {
|
|
319
|
+
const [buffer] = await bucket.file(name).download();
|
|
320
|
+
return new Uint8Array(buffer);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
if (isNotFound(error)) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
async delete(name) {
|
|
330
|
+
await bucket.file(name).delete({ ignoreNotFound: true });
|
|
331
|
+
},
|
|
332
|
+
async exists(name) {
|
|
333
|
+
const [exists] = await bucket.file(name).exists();
|
|
334
|
+
return exists;
|
|
335
|
+
},
|
|
336
|
+
async getMetadata(name) {
|
|
337
|
+
try {
|
|
338
|
+
const [metadata] = await bucket.file(name).getMetadata();
|
|
339
|
+
return metadata;
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
if (isNotFound(error)) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
async getFiles(input) {
|
|
349
|
+
const [files] = await bucket.getFiles({
|
|
350
|
+
prefix: input.prefix,
|
|
351
|
+
delimiter: input.delimiter,
|
|
352
|
+
maxResults: input.maxResults,
|
|
353
|
+
});
|
|
354
|
+
return files.map((file) => ({
|
|
355
|
+
name: file.name,
|
|
356
|
+
attributes: file.metadata,
|
|
357
|
+
}));
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Recognize the GCS "object does not exist" condition so `download`/`getMetadata`
|
|
363
|
+
* can return `null` (Requirement 4.2/4.10) rather than throwing. GCS surfaces a
|
|
364
|
+
* `code === 404` on the ApiError.
|
|
365
|
+
*/
|
|
366
|
+
function isNotFound(error) {
|
|
367
|
+
if (error === null || typeof error !== "object") {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
const code = error.code;
|
|
371
|
+
return code === 404;
|
|
372
|
+
}
|
|
373
|
+
// ── Module-private encoding helpers ────────────────────────────────────────────
|
|
374
|
+
/** Compute the lowercase sha-256 hex digest of `bytes`. */
|
|
375
|
+
function sha256Hex(bytes) {
|
|
376
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
377
|
+
}
|
|
378
|
+
/** Encode a built object's typed metadata into the GCS custom-metadata map. */
|
|
379
|
+
function encodeUserMetadata(metadata) {
|
|
380
|
+
const map = {
|
|
381
|
+
[MK.checksum]: metadata.checksum,
|
|
382
|
+
[MK.createdAt]: String(metadata.createdAt),
|
|
383
|
+
[MK.updatedAt]: String(metadata.updatedAt),
|
|
384
|
+
[MK.accessLevel]: metadata.accessLevel,
|
|
385
|
+
[MK.custom]: JSON.stringify(metadata.custom ?? {}),
|
|
386
|
+
};
|
|
387
|
+
if (metadata.owner !== undefined) {
|
|
388
|
+
map[MK.owner] = metadata.owner;
|
|
389
|
+
}
|
|
390
|
+
if (metadata.tenant !== undefined) {
|
|
391
|
+
map[MK.tenant] = metadata.tenant;
|
|
392
|
+
}
|
|
393
|
+
return map;
|
|
394
|
+
}
|
|
395
|
+
/** Parse a GCS `size` (numeric string or number), defaulting to `0`. */
|
|
396
|
+
function parseSize(value) {
|
|
397
|
+
if (typeof value === "number") {
|
|
398
|
+
return Number.isFinite(value) ? value : 0;
|
|
399
|
+
}
|
|
400
|
+
if (typeof value === "string") {
|
|
401
|
+
const parsed = Number(value);
|
|
402
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
403
|
+
}
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
/** Parse an epoch-ms string, returning `undefined` when absent or non-numeric. */
|
|
407
|
+
function parseEpoch(value) {
|
|
408
|
+
if (value === undefined) {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
const parsed = Number(value);
|
|
412
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
413
|
+
}
|
|
414
|
+
/** Parse an RFC-3339 / ISO-8601 timestamp to epoch ms, or `undefined`. */
|
|
415
|
+
function parseIso(value) {
|
|
416
|
+
if (value === undefined) {
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
const parsed = Date.parse(value);
|
|
420
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
421
|
+
}
|
|
422
|
+
/** Parse the encoded custom-field JSON, tolerating malformed values. */
|
|
423
|
+
function parseCustom(value) {
|
|
424
|
+
if (value === undefined) {
|
|
425
|
+
return {};
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
const parsed = JSON.parse(value);
|
|
429
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
430
|
+
return parsed;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
catch {
|
|
434
|
+
// Fall through to the empty default on malformed JSON.
|
|
435
|
+
}
|
|
436
|
+
return {};
|
|
437
|
+
}
|
|
438
|
+
/** The recognized access levels, used to validate the decoded value. */
|
|
439
|
+
const ACCESS_LEVELS = [
|
|
440
|
+
"public",
|
|
441
|
+
"private",
|
|
442
|
+
"signed",
|
|
443
|
+
"authenticated",
|
|
444
|
+
"role-based",
|
|
445
|
+
"tenant-aware",
|
|
446
|
+
];
|
|
447
|
+
/** Decode an access-level string, returning `undefined` for an unknown value. */
|
|
448
|
+
function decodeAccessLevel(value) {
|
|
449
|
+
if (value !== undefined && ACCESS_LEVELS.includes(value)) {
|
|
450
|
+
return value;
|
|
451
|
+
}
|
|
452
|
+
return undefined;
|
|
453
|
+
}
|
|
454
|
+
//# sourceMappingURL=gcs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcs.js","sourceRoot":"","sources":["../../src/drivers/gcs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — LocalStorageDriver (zero-dependency).
|
|
3
|
+
*
|
|
4
|
+
* A filesystem-backed implementation of the {@link StorageDriver} contract that
|
|
5
|
+
* uses only Node's built-in `fs` module (part of the runtime, not an external
|
|
6
|
+
* dependency — Requirement 3.2). Object bytes are written to `root/<key>` and
|
|
7
|
+
* the computed {@link StorageObjectMetadata} is persisted alongside them in a
|
|
8
|
+
* sidecar file at `root/<key>.meta.json`. Parent directories are created on
|
|
9
|
+
* demand so nested key paths (e.g. `a/b/c.txt`) work transparently.
|
|
10
|
+
*
|
|
11
|
+
* The Local driver is observationally equivalent to
|
|
12
|
+
* {@link MemoryStorageDriver} (Requirement 2.2): identical `etag`/`checksum`
|
|
13
|
+
* (sha-256 hex of the stored bytes via `node:crypto`), `size` (byte length),
|
|
14
|
+
* timestamps sourced from an injected {@link Clock} (default `systemClock` from
|
|
15
|
+
* `streetjs`), and defaults (`contentType` `application/octet-stream`,
|
|
16
|
+
* `accessLevel` `private`, `custom` `{}`). Overwriting a key preserves the
|
|
17
|
+
* original `createdAt` and only advances `updatedAt` (Requirements 4.1, 10.1).
|
|
18
|
+
*
|
|
19
|
+
* This module (task 4.1) implements the mandatory primitive object operations:
|
|
20
|
+
* `put` / `get` / `exists` / `delete` / `stat` / `list`. `get` returns a
|
|
21
|
+
* discriminated {@link MaybeObject} so absence is reported consistently rather
|
|
22
|
+
* than thrown (Requirements 4.2, 2.4). `list` returns {@link StorageListItem}s
|
|
23
|
+
* sorted by key and never surfaces the `.meta.json` sidecar files as objects
|
|
24
|
+
* (Requirement 4.9).
|
|
25
|
+
*
|
|
26
|
+
* Streaming (task 4.2) uses `fs.createReadStream` / `fs.createWriteStream` so
|
|
27
|
+
* large files are never fully buffered in memory (Requirement 5.3). `putStream`
|
|
28
|
+
* pipes the incoming stream through a sha-256 hash on the way to disk, tallying
|
|
29
|
+
* the byte length as it goes, then writes the sidecar with the same metadata
|
|
30
|
+
* semantics as `put` (preserving the original `createdAt` on overwrite).
|
|
31
|
+
* `getStream` returns a `fs.createReadStream` of the object and throws
|
|
32
|
+
* {@link NotFoundError} for a missing key (Requirement 5.5).
|
|
33
|
+
*
|
|
34
|
+
* _Requirements: 2.1, 2.2, 3.2, 4.1, 4.2, 4.3, 4.4, 4.9, 4.10, 5.1, 5.2, 5.3, 5.5, 10.1_
|
|
35
|
+
*/
|
|
36
|
+
import { type Clock } from "streetjs";
|
|
37
|
+
import type { MaybeObject, NodeReadable, StorageDriver } from "../driver.js";
|
|
38
|
+
import type { ListOptions, StorageListItem, StorageObjectMetadata, WriteMetadata } from "../types.js";
|
|
39
|
+
/** Options for constructing a {@link LocalStorageDriver}. */
|
|
40
|
+
export interface LocalStorageDriverOptions {
|
|
41
|
+
/** Filesystem root under which object bytes and sidecars are stored. */
|
|
42
|
+
readonly root: string;
|
|
43
|
+
/** Injected clock for deterministic timestamps in tests. Default `systemClock`. */
|
|
44
|
+
readonly clock?: Clock;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Zero-dependency filesystem {@link StorageDriver}.
|
|
48
|
+
*
|
|
49
|
+
* Object bytes live at `root/<key>`; their metadata is persisted in a sidecar
|
|
50
|
+
* `root/<key>.meta.json`. Observable behavior matches
|
|
51
|
+
* {@link MemoryStorageDriver} so the two drivers are interchangeable.
|
|
52
|
+
*/
|
|
53
|
+
export declare class LocalStorageDriver implements StorageDriver {
|
|
54
|
+
/** Stable driver name. */
|
|
55
|
+
readonly name = "local";
|
|
56
|
+
/** Filesystem root under which objects are stored. */
|
|
57
|
+
private readonly root;
|
|
58
|
+
/** Injected clock used for `createdAt` / `updatedAt` timestamps. */
|
|
59
|
+
private readonly clock;
|
|
60
|
+
constructor(options: LocalStorageDriverOptions);
|
|
61
|
+
/**
|
|
62
|
+
* Persist `bytes` under `key`, computing size, sha-256 checksum/etag, and
|
|
63
|
+
* timestamps. Parent directories are created as needed for nested keys. When
|
|
64
|
+
* overwriting an existing key, the original `createdAt` is preserved and only
|
|
65
|
+
* `updatedAt` advances (Requirements 4.1, 10.1).
|
|
66
|
+
*/
|
|
67
|
+
put(key: string, bytes: Uint8Array, metadata: WriteMetadata): Promise<StorageObjectMetadata>;
|
|
68
|
+
/**
|
|
69
|
+
* Read the object at `key`. Returns a discriminated {@link MaybeObject} so
|
|
70
|
+
* absence is reported consistently rather than thrown (Requirements 4.2, 2.4).
|
|
71
|
+
*/
|
|
72
|
+
get(key: string): Promise<MaybeObject>;
|
|
73
|
+
/** Report whether an object is stored under `key` (Requirement 4.3). */
|
|
74
|
+
exists(key: string): Promise<boolean>;
|
|
75
|
+
/**
|
|
76
|
+
* Remove the object at `key` and its metadata sidecar. Deleting a missing key
|
|
77
|
+
* is a no-op so that a subsequent `exists` returns false either way
|
|
78
|
+
* (Requirement 4.4).
|
|
79
|
+
*/
|
|
80
|
+
delete(key: string): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Return the metadata for `key` without its content, or `null` if absent
|
|
83
|
+
* (Requirement 4.10).
|
|
84
|
+
*/
|
|
85
|
+
stat(key: string): Promise<StorageObjectMetadata | null>;
|
|
86
|
+
/**
|
|
87
|
+
* Return list items for every key beginning with `prefix`, sorted by key for
|
|
88
|
+
* deterministic ordering (Requirement 4.9). Metadata sidecar files
|
|
89
|
+
* (`.meta.json`) are never surfaced as objects. Honors optional `cursor`
|
|
90
|
+
* (exclusive resume point), `limit`, and `delimiter` (collapse to immediate
|
|
91
|
+
* children on `/`) when supplied, matching {@link MemoryStorageDriver}.
|
|
92
|
+
*/
|
|
93
|
+
list(prefix: string, options?: ListOptions): Promise<StorageListItem[]>;
|
|
94
|
+
/**
|
|
95
|
+
* Persist a streamed upload to `root/<key>` using `fs.createWriteStream` so
|
|
96
|
+
* the object is never fully buffered in memory (Requirements 5.1, 5.3).
|
|
97
|
+
* Parent directories are created on demand. As bytes flow to disk they are
|
|
98
|
+
* piped through a sha-256 hash and the running byte count is tallied, giving
|
|
99
|
+
* the `checksum`/`etag`/`size` without a second pass. Once the stream is
|
|
100
|
+
* drained the metadata sidecar is written with the same semantics as
|
|
101
|
+
* {@link put}: an existing key's original `createdAt` is preserved and only
|
|
102
|
+
* `updatedAt` advances (Requirements 4.1, 10.1).
|
|
103
|
+
*/
|
|
104
|
+
putStream(key: string, stream: NodeReadable, metadata: WriteMetadata): Promise<StorageObjectMetadata>;
|
|
105
|
+
/**
|
|
106
|
+
* Return a `fs.createReadStream` of the object at `key` so readers pull bytes
|
|
107
|
+
* incrementally without buffering the whole object (Requirements 5.2, 5.3).
|
|
108
|
+
* Throws {@link NotFoundError} when the key is missing (Requirement 5.5).
|
|
109
|
+
*/
|
|
110
|
+
getStream(key: string): Promise<NodeReadable>;
|
|
111
|
+
/** Absolute filesystem path to the object bytes for `key`. */
|
|
112
|
+
private objectPath;
|
|
113
|
+
/** Absolute filesystem path to the metadata sidecar for `key`. */
|
|
114
|
+
private metaPath;
|
|
115
|
+
/** Read and parse the metadata sidecar for `key`, or `null` when absent. */
|
|
116
|
+
private readMeta;
|
|
117
|
+
/** Unlink a path, ignoring the case where it does not exist. */
|
|
118
|
+
private unlinkIfExists;
|
|
119
|
+
/**
|
|
120
|
+
* Walk the storage root and return every object key (the relative path from
|
|
121
|
+
* `root`, using `/` separators). Metadata sidecar files are excluded so they
|
|
122
|
+
* are never surfaced as objects.
|
|
123
|
+
*/
|
|
124
|
+
private collectKeys;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=local.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/drivers/local.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAQH,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,UAAU,CAAC;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7E,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,qBAAqB,EACrB,aAAa,EACd,MAAM,aAAa,CAAC;AAErB,6DAA6D;AAC7D,MAAM,WAAW,yBAAyB;IACxC,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAKD;;;;;;GAMG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IACtD,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,WAAW;IAExB,sDAAsD;IACtD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAE9B,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAElB,OAAO,EAAE,yBAAyB;IAO9C;;;;;OAKG;IACG,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,aAAa,GACtB,OAAO,CAAC,qBAAqB,CAAC;IA2BjC;;;OAGG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAiB5C,wEAAwE;IAClE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS3C;;;;OAIG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC;;;OAGG;IACG,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;IAI9D;;;;;;OAMG;IACG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAgD7E;;;;;;;;;OASG;IACG,SAAS,CACb,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,aAAa,GACtB,OAAO,CAAC,qBAAqB,CAAC;IA+CjC;;;;OAIG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAenD,8DAA8D;IAC9D,OAAO,CAAC,UAAU;IAIlB,kEAAkE;IAClE,OAAO,CAAC,QAAQ;IAIhB,4EAA4E;YAC9D,QAAQ;IAatB,gEAAgE;YAClD,cAAc;IAU5B;;;;OAIG;YACW,WAAW;CAoB1B"}
|