@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,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — the lifecycle engine.
|
|
3
|
+
*
|
|
4
|
+
* Lifecycle rules act on objects automatically based on their age or state, so
|
|
5
|
+
* stored objects are cleaned up, archived, or tiered over time (Requirement
|
|
6
|
+
* 13). {@link LifecycleEngine} evaluates a single {@link LifecycleRule} and
|
|
7
|
+
* applies its action to every qualifying object in a **provider-agnostic** way:
|
|
8
|
+
*
|
|
9
|
+
* - When the backing {@link StorageDriver} implements the optional native
|
|
10
|
+
* `lifecycle` capability, the engine delegates evaluation to the driver so a
|
|
11
|
+
* provider with first-class lifecycle policies (e.g. S3 lifecycle
|
|
12
|
+
* configuration) is used directly.
|
|
13
|
+
* - Otherwise the engine **simulates** the rule over the mandatory driver
|
|
14
|
+
* primitives (`list`/`stat`/`get`/`put`/`delete`). It enumerates candidate
|
|
15
|
+
* keys with `list`, reads each object's `createdAt` with `stat`, computes the
|
|
16
|
+
* object's age relative to the injected {@link Clock}, and applies the rule's
|
|
17
|
+
* action to the objects whose age meets the rule's threshold. This is what
|
|
18
|
+
* lets the zero-dependency drivers simulate lifecycle entirely in memory /
|
|
19
|
+
* over the local filesystem (Requirement 13.3).
|
|
20
|
+
*
|
|
21
|
+
* Supported rule types (Requirement 13.1):
|
|
22
|
+
* - `delete-after-days` — delete objects older than `days`.
|
|
23
|
+
* - `archive-after-months` — archive objects older than `months` by relocating
|
|
24
|
+
* them under the reserved {@link ARCHIVE_KEY_PREFIX}.
|
|
25
|
+
* - `expire-temp-uploads` — delete transient multipart-part / resumable-session
|
|
26
|
+
* state older than `afterMs` (the reserved temp-upload key spaces).
|
|
27
|
+
* - `move-to-cold` — relocate objects older than `afterDays` under `coldPrefix`.
|
|
28
|
+
*
|
|
29
|
+
* **Exactly-once (Requirement 13.2).** Each action removes the qualifying object
|
|
30
|
+
* from the space that a subsequent evaluation scans: `delete`/`expire` remove
|
|
31
|
+
* the object outright, and `archive`/`move-to-cold` relocate it to a prefix that
|
|
32
|
+
* the scan excludes (the reserved archive prefix, or `coldPrefix`). Because the
|
|
33
|
+
* relocated copy is written with a fresh `createdAt` at the evaluation instant,
|
|
34
|
+
* it is also too young to re-qualify at the same `now`. The net effect is that a
|
|
35
|
+
* second evaluation produces no further action on an already-actioned object.
|
|
36
|
+
*
|
|
37
|
+
* The module depends only on the driver contract, the shared type surface, the
|
|
38
|
+
* metadata layer, and the core `Clock`, keeping the dependency direction
|
|
39
|
+
* acyclic. Event publication for applied actions (Requirement 13.4) is wired
|
|
40
|
+
* separately by the Events bridge (task 21.1); this engine only performs the
|
|
41
|
+
* actions and returns their {@link LifecycleOutcome} descriptors.
|
|
42
|
+
*
|
|
43
|
+
* _Requirements: 13.1, 13.2, 13.3_
|
|
44
|
+
*/
|
|
45
|
+
import { systemClock } from "streetjs";
|
|
46
|
+
import { toWriteMetadata } from "./metadata.js";
|
|
47
|
+
/** Milliseconds in a single day, used to convert day-based thresholds. */
|
|
48
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
49
|
+
/**
|
|
50
|
+
* The number of days used to approximate one month when converting a
|
|
51
|
+
* month-based threshold to milliseconds. Lifecycle thresholds are coarse
|
|
52
|
+
* age policies rather than calendar arithmetic, so a fixed 30-day month keeps
|
|
53
|
+
* the simulation deterministic and provider-agnostic.
|
|
54
|
+
*/
|
|
55
|
+
const DAYS_PER_MONTH = 30;
|
|
56
|
+
/**
|
|
57
|
+
* Reserved key prefix under which objects archived by an `archive-after-months`
|
|
58
|
+
* rule are relocated. An archived `<key>` lives at `${ARCHIVE_KEY_PREFIX}<key>`.
|
|
59
|
+
* The prefix is excluded from every lifecycle scan so an archived object is
|
|
60
|
+
* never re-actioned (Requirement 13.2).
|
|
61
|
+
*/
|
|
62
|
+
export const ARCHIVE_KEY_PREFIX = ".archive/";
|
|
63
|
+
/**
|
|
64
|
+
* Reserved key prefixes that hold transient upload state (multipart parts and
|
|
65
|
+
* resumable session bytes), mirroring the `.multipart/<uploadId>/…` and
|
|
66
|
+
* `.resumable/<sessionId>` layouts used by the session managers. These are the
|
|
67
|
+
* key spaces an `expire-temp-uploads` rule scans and expires.
|
|
68
|
+
*/
|
|
69
|
+
const TEMP_UPLOAD_PREFIXES = [".multipart/", ".resumable/"];
|
|
70
|
+
/**
|
|
71
|
+
* Reserved key prefixes that are internal bookkeeping rather than
|
|
72
|
+
* application-visible objects. Age-based rules (`delete-after-days`,
|
|
73
|
+
* `archive-after-months`, `move-to-cold`) never touch keys under these prefixes
|
|
74
|
+
* so lifecycle policies act only on real objects, not on version snapshots,
|
|
75
|
+
* multipart parts, resumable session bytes, or already-archived copies.
|
|
76
|
+
*/
|
|
77
|
+
const RESERVED_KEY_PREFIXES = [
|
|
78
|
+
".versions/",
|
|
79
|
+
".multipart/",
|
|
80
|
+
".resumable/",
|
|
81
|
+
ARCHIVE_KEY_PREFIX,
|
|
82
|
+
];
|
|
83
|
+
/**
|
|
84
|
+
* Provider-agnostic lifecycle engine built on the driver contract.
|
|
85
|
+
*
|
|
86
|
+
* A single instance is held by the facade and bound to one {@link StorageDriver}
|
|
87
|
+
* for its lifetime, so the native-vs-simulated decision (based on whether the
|
|
88
|
+
* driver exposes a `lifecycle` capability) is stable across every call.
|
|
89
|
+
*/
|
|
90
|
+
export class LifecycleEngine {
|
|
91
|
+
/** The driver every action is delegated to or simulated over. */
|
|
92
|
+
driver;
|
|
93
|
+
/** Injected clock used to compute object age at evaluation time. */
|
|
94
|
+
clock;
|
|
95
|
+
constructor(options) {
|
|
96
|
+
this.driver = options.driver;
|
|
97
|
+
this.clock = options.clock ?? systemClock;
|
|
98
|
+
}
|
|
99
|
+
/** The driver's native lifecycle capability, when present. */
|
|
100
|
+
get native() {
|
|
101
|
+
return this.driver.lifecycle;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Evaluate `rule` against the current object set and apply its action to every
|
|
105
|
+
* qualifying object, returning one {@link LifecycleOutcome} per actioned
|
|
106
|
+
* object (Requirements 13.1, 13.2). Object age is measured relative to `now`,
|
|
107
|
+
* which defaults to the injected clock. When the driver exposes a native
|
|
108
|
+
* `lifecycle` capability the evaluation is delegated to it; otherwise the rule
|
|
109
|
+
* is simulated over the driver primitives.
|
|
110
|
+
*/
|
|
111
|
+
async apply(rule, now = this.clock()) {
|
|
112
|
+
if (this.native !== undefined) {
|
|
113
|
+
return this.native.apply(rule, now);
|
|
114
|
+
}
|
|
115
|
+
switch (rule.type) {
|
|
116
|
+
case "delete-after-days":
|
|
117
|
+
return this.applyDelete(rule.prefix ?? "", rule.days * MS_PER_DAY, now);
|
|
118
|
+
case "archive-after-months":
|
|
119
|
+
return this.applyArchive(rule.prefix ?? "", rule.months * DAYS_PER_MONTH * MS_PER_DAY, now);
|
|
120
|
+
case "expire-temp-uploads":
|
|
121
|
+
return this.applyExpire(rule.afterMs, now);
|
|
122
|
+
case "move-to-cold":
|
|
123
|
+
return this.applyMoveToCold(rule.prefix ?? "", rule.coldPrefix, rule.afterDays * MS_PER_DAY, now);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Delete every managed object under `prefix` whose age is at least
|
|
128
|
+
* `thresholdMs`. The deletion removes the object from the scanned space, so a
|
|
129
|
+
* repeated evaluation finds nothing further to delete (Requirement 13.2).
|
|
130
|
+
*/
|
|
131
|
+
async applyDelete(prefix, thresholdMs, now) {
|
|
132
|
+
const keys = await this.collectQualifying(prefix, thresholdMs, now, (key) => this.isManagedKey(key));
|
|
133
|
+
const outcomes = [];
|
|
134
|
+
for (const key of keys) {
|
|
135
|
+
await this.driver.delete(key);
|
|
136
|
+
outcomes.push({ key, action: "deleted" });
|
|
137
|
+
}
|
|
138
|
+
return outcomes;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Archive every managed object under `prefix` whose age is at least
|
|
142
|
+
* `thresholdMs` by relocating it under {@link ARCHIVE_KEY_PREFIX}. The archive
|
|
143
|
+
* prefix is excluded from the scan, so an archived object is never re-archived
|
|
144
|
+
* (Requirement 13.2).
|
|
145
|
+
*/
|
|
146
|
+
async applyArchive(prefix, thresholdMs, now) {
|
|
147
|
+
const keys = await this.collectQualifying(prefix, thresholdMs, now, (key) => this.isManagedKey(key));
|
|
148
|
+
const outcomes = [];
|
|
149
|
+
for (const key of keys) {
|
|
150
|
+
const moved = await this.relocate(key, `${ARCHIVE_KEY_PREFIX}${key}`);
|
|
151
|
+
if (moved) {
|
|
152
|
+
outcomes.push({ key, action: "archived" });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return outcomes;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Expire (delete) transient upload state older than `afterMs` across the
|
|
159
|
+
* reserved temp-upload key spaces (multipart parts and resumable session
|
|
160
|
+
* bytes). Because expiration deletes the state, a repeated evaluation finds
|
|
161
|
+
* nothing further to expire (Requirement 13.2).
|
|
162
|
+
*/
|
|
163
|
+
async applyExpire(afterMs, now) {
|
|
164
|
+
const outcomes = [];
|
|
165
|
+
for (const prefix of TEMP_UPLOAD_PREFIXES) {
|
|
166
|
+
// Every key under a temp-upload prefix is transient state, so no further
|
|
167
|
+
// managed-key filtering is applied here.
|
|
168
|
+
const keys = await this.collectQualifying(prefix, afterMs, now, () => true);
|
|
169
|
+
for (const key of keys) {
|
|
170
|
+
await this.driver.delete(key);
|
|
171
|
+
outcomes.push({ key, action: "expired" });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return outcomes;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Relocate every managed object under `prefix` whose age is at least
|
|
178
|
+
* `thresholdMs` to `${coldPrefix}<key>`. Keys already residing under
|
|
179
|
+
* `coldPrefix` are excluded from the scan, so a repeated evaluation does not
|
|
180
|
+
* move an already-tiered object again (Requirement 13.2).
|
|
181
|
+
*/
|
|
182
|
+
async applyMoveToCold(prefix, coldPrefix, thresholdMs, now) {
|
|
183
|
+
const keys = await this.collectQualifying(prefix, thresholdMs, now, (key) => this.isManagedKey(key) && !key.startsWith(coldPrefix));
|
|
184
|
+
const outcomes = [];
|
|
185
|
+
for (const key of keys) {
|
|
186
|
+
const moved = await this.relocate(key, `${coldPrefix}${key}`);
|
|
187
|
+
if (moved) {
|
|
188
|
+
outcomes.push({ key, action: "moved" });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return outcomes;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* List candidate keys under `prefix`, keep those accepted by `accept`, and
|
|
195
|
+
* return the subset whose age (`now - createdAt`, read via `stat`) is at least
|
|
196
|
+
* `thresholdMs`. Keys that vanish between `list` and `stat` are skipped.
|
|
197
|
+
*/
|
|
198
|
+
async collectQualifying(prefix, thresholdMs, now, accept) {
|
|
199
|
+
const items = await this.driver.list(prefix);
|
|
200
|
+
const qualifying = [];
|
|
201
|
+
for (const item of items) {
|
|
202
|
+
if (!accept(item.key)) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const metadata = await this.driver.stat(item.key);
|
|
206
|
+
if (metadata === null) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (now - metadata.createdAt >= thresholdMs) {
|
|
210
|
+
qualifying.push(item.key);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return qualifying;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Move the object at `sourceKey` to `destinationKey` over the driver
|
|
217
|
+
* primitives: the content and preserved write-time metadata are written to the
|
|
218
|
+
* destination and the source is then removed. Returns `false` without writing
|
|
219
|
+
* when the source has vanished.
|
|
220
|
+
*/
|
|
221
|
+
async relocate(sourceKey, destinationKey) {
|
|
222
|
+
const result = await this.driver.get(sourceKey);
|
|
223
|
+
if (!result.found) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
await this.driver.put(destinationKey, result.bytes, toWriteMetadata(result.metadata));
|
|
227
|
+
await this.driver.delete(sourceKey);
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Report whether `key` is an application-visible object (as opposed to
|
|
232
|
+
* internal bookkeeping such as version snapshots, multipart parts, resumable
|
|
233
|
+
* session bytes, or already-archived copies). Age-based rules act only on
|
|
234
|
+
* managed keys.
|
|
235
|
+
*/
|
|
236
|
+
isManagedKey(key) {
|
|
237
|
+
return !RESERVED_KEY_PREFIXES.some((reserved) => key.startsWith(reserved));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — the typed metadata layer (single source of truth).
|
|
3
|
+
*
|
|
4
|
+
* Every stored object carries a typed {@link StorageObjectMetadata} field set:
|
|
5
|
+
* `key`, `size`, `contentType`, `etag`, `checksum`, `owner`, `tenant`,
|
|
6
|
+
* `accessLevel`, `createdAt`, `updatedAt`, `custom` (and an optional
|
|
7
|
+
* `versionId`). Requirement 10 states this field set must be **complete**,
|
|
8
|
+
* **consistent across providers**, and must **round-trip** through write and
|
|
9
|
+
* read unchanged. Historically each driver assembled that object literal on its
|
|
10
|
+
* own and duplicated the field defaults (`contentType` →
|
|
11
|
+
* `application/octet-stream`, `accessLevel` → `private`, `custom` → `{}`), which
|
|
12
|
+
* risked the shape drifting between drivers over time.
|
|
13
|
+
*
|
|
14
|
+
* This module centralizes that shape in one place so it is preserved
|
|
15
|
+
* identically no matter which {@link StorageDriver} backs the facade:
|
|
16
|
+
*
|
|
17
|
+
* - {@link buildObjectMetadata} is the single constructor for the metadata
|
|
18
|
+
* field set. Drivers compute the provider-specific identity fields
|
|
19
|
+
* (`etag`/`checksum`/`size`) and timestamps (`createdAt`/`updatedAt`) and hand
|
|
20
|
+
* them here together with the caller's {@link WriteMetadata}; this function
|
|
21
|
+
* applies the canonical defaults and produces the complete, typed object. It
|
|
22
|
+
* never recomputes the identity fields, so driver-computed values are the
|
|
23
|
+
* source of truth for content hashing (Requirement 10.1, 10.2).
|
|
24
|
+
* - {@link normalizeMetadata} guarantees the full field set is present on any
|
|
25
|
+
* metadata surfaced by the facade (`put`/`stat`/`get`), filling the canonical
|
|
26
|
+
* defaults for any field a driver (e.g. a future cloud driver) left
|
|
27
|
+
* unpopulated while leaving identity/timestamp fields untouched. It is
|
|
28
|
+
* idempotent for metadata already produced by {@link buildObjectMetadata}
|
|
29
|
+
* (Requirement 10.1, 10.3).
|
|
30
|
+
* - {@link toWriteMetadata} projects a stored object's metadata back onto the
|
|
31
|
+
* write-time subset, used when copying/moving so the destination preserves the
|
|
32
|
+
* source's content type, ownership, tenancy, access level, and custom fields.
|
|
33
|
+
*
|
|
34
|
+
* The module depends only on the shared type surface (`types.ts`), keeping the
|
|
35
|
+
* dependency direction acyclic (leaf module → types).
|
|
36
|
+
*
|
|
37
|
+
* _Requirements: 10.1, 10.2, 10.3_
|
|
38
|
+
*/
|
|
39
|
+
import type { AccessLevel, StorageObjectMetadata, WriteMetadata } from "./types.js";
|
|
40
|
+
/** Default content type applied when a write does not specify one. */
|
|
41
|
+
export declare const DEFAULT_CONTENT_TYPE: "application/octet-stream";
|
|
42
|
+
/** Default access level applied when a write does not specify one. */
|
|
43
|
+
export declare const DEFAULT_ACCESS_LEVEL: AccessLevel;
|
|
44
|
+
/**
|
|
45
|
+
* The canonical, ordered set of typed metadata field names every object carries
|
|
46
|
+
* (Requirement 10.1). `versionId` is intentionally excluded — it is an optional
|
|
47
|
+
* field present only on versioned objects, not part of the guaranteed base set.
|
|
48
|
+
*/
|
|
49
|
+
export declare const STORAGE_METADATA_FIELDS: readonly ["key", "size", "contentType", "etag", "checksum", "owner", "tenant", "accessLevel", "createdAt", "updatedAt", "custom"];
|
|
50
|
+
/**
|
|
51
|
+
* The inputs a driver supplies to build an object's metadata. The driver is
|
|
52
|
+
* responsible for the provider-specific identity fields and timestamps; this
|
|
53
|
+
* layer owns the field-set shape and the caller-facing defaults.
|
|
54
|
+
*/
|
|
55
|
+
export interface BuildMetadataInput {
|
|
56
|
+
/** The object key. */
|
|
57
|
+
readonly key: string;
|
|
58
|
+
/** Byte length of the stored content, computed by the driver. */
|
|
59
|
+
readonly size: number;
|
|
60
|
+
/** Content hash (e.g. sha-256 hex), computed by the driver. */
|
|
61
|
+
readonly checksum: string;
|
|
62
|
+
/** Entity tag; defaults to {@link checksum} when the driver omits it. */
|
|
63
|
+
readonly etag?: string;
|
|
64
|
+
/** Creation timestamp (epoch ms) from the injected clock. */
|
|
65
|
+
readonly createdAt: number;
|
|
66
|
+
/** Last-updated timestamp (epoch ms) from the injected clock. */
|
|
67
|
+
readonly updatedAt: number;
|
|
68
|
+
/** The caller-supplied write-time metadata. */
|
|
69
|
+
readonly write: WriteMetadata;
|
|
70
|
+
/** Optional version identifier for versioned objects. */
|
|
71
|
+
readonly versionId?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Build the complete, typed {@link StorageObjectMetadata} field set from the
|
|
75
|
+
* driver-computed identity/timestamp values and the caller's
|
|
76
|
+
* {@link WriteMetadata}. This is the single place the metadata shape and its
|
|
77
|
+
* defaults are defined, so every driver produces an identical field set
|
|
78
|
+
* (Requirement 10.1, 10.2, 10.3).
|
|
79
|
+
*
|
|
80
|
+
* The identity fields (`etag`/`checksum`/`size`) and timestamps are used exactly
|
|
81
|
+
* as provided — this function never recomputes them — so the driver's computed
|
|
82
|
+
* values remain the source of truth and cannot diverge from what was persisted.
|
|
83
|
+
*/
|
|
84
|
+
export declare function buildObjectMetadata(input: BuildMetadataInput): StorageObjectMetadata;
|
|
85
|
+
/**
|
|
86
|
+
* Return a copy of `metadata` with the complete typed field set guaranteed
|
|
87
|
+
* present, applying the canonical defaults for any missing `contentType`,
|
|
88
|
+
* `accessLevel`, or `custom` field while preserving the driver-computed
|
|
89
|
+
* identity fields (`etag`/`checksum`/`size`) and timestamps untouched
|
|
90
|
+
* (Requirement 10.1, 10.3).
|
|
91
|
+
*
|
|
92
|
+
* The facade runs the metadata it surfaces on `put`/`stat`/`get` through this
|
|
93
|
+
* function so the full field set is consistently present regardless of which
|
|
94
|
+
* driver produced it. It is idempotent for metadata already built by
|
|
95
|
+
* {@link buildObjectMetadata}, so wiring it in adds no behavioral change for the
|
|
96
|
+
* zero-dependency drivers while shielding the public surface from a future
|
|
97
|
+
* driver that returns a partially populated record.
|
|
98
|
+
*/
|
|
99
|
+
export declare function normalizeMetadata(metadata: StorageObjectMetadata): StorageObjectMetadata;
|
|
100
|
+
/**
|
|
101
|
+
* Project a stored object's {@link StorageObjectMetadata} onto the write-time
|
|
102
|
+
* {@link WriteMetadata} subset. Used when copying/moving an object so the
|
|
103
|
+
* destination preserves the source's content type, ownership, tenancy, access
|
|
104
|
+
* level, and custom fields. The identity/timestamp fields (`etag`/`checksum`/
|
|
105
|
+
* `size`/`createdAt`/`updatedAt`) are intentionally omitted — the driver
|
|
106
|
+
* recomputes them for the destination object.
|
|
107
|
+
*/
|
|
108
|
+
export declare function toWriteMetadata(metadata: StorageObjectMetadata): WriteMetadata;
|
|
109
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIpF,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,EAAG,0BAAmC,CAAC;AAExE,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,EAAE,WAAuB,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,mIAY1B,CAAC;AAIX;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,sBAAsB;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,qBAAqB,CAiBpF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,qBAAqB,CAgBxF;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,qBAAqB,GAAG,aAAa,CAQ9E"}
|
package/dist/metadata.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — the typed metadata layer (single source of truth).
|
|
3
|
+
*
|
|
4
|
+
* Every stored object carries a typed {@link StorageObjectMetadata} field set:
|
|
5
|
+
* `key`, `size`, `contentType`, `etag`, `checksum`, `owner`, `tenant`,
|
|
6
|
+
* `accessLevel`, `createdAt`, `updatedAt`, `custom` (and an optional
|
|
7
|
+
* `versionId`). Requirement 10 states this field set must be **complete**,
|
|
8
|
+
* **consistent across providers**, and must **round-trip** through write and
|
|
9
|
+
* read unchanged. Historically each driver assembled that object literal on its
|
|
10
|
+
* own and duplicated the field defaults (`contentType` →
|
|
11
|
+
* `application/octet-stream`, `accessLevel` → `private`, `custom` → `{}`), which
|
|
12
|
+
* risked the shape drifting between drivers over time.
|
|
13
|
+
*
|
|
14
|
+
* This module centralizes that shape in one place so it is preserved
|
|
15
|
+
* identically no matter which {@link StorageDriver} backs the facade:
|
|
16
|
+
*
|
|
17
|
+
* - {@link buildObjectMetadata} is the single constructor for the metadata
|
|
18
|
+
* field set. Drivers compute the provider-specific identity fields
|
|
19
|
+
* (`etag`/`checksum`/`size`) and timestamps (`createdAt`/`updatedAt`) and hand
|
|
20
|
+
* them here together with the caller's {@link WriteMetadata}; this function
|
|
21
|
+
* applies the canonical defaults and produces the complete, typed object. It
|
|
22
|
+
* never recomputes the identity fields, so driver-computed values are the
|
|
23
|
+
* source of truth for content hashing (Requirement 10.1, 10.2).
|
|
24
|
+
* - {@link normalizeMetadata} guarantees the full field set is present on any
|
|
25
|
+
* metadata surfaced by the facade (`put`/`stat`/`get`), filling the canonical
|
|
26
|
+
* defaults for any field a driver (e.g. a future cloud driver) left
|
|
27
|
+
* unpopulated while leaving identity/timestamp fields untouched. It is
|
|
28
|
+
* idempotent for metadata already produced by {@link buildObjectMetadata}
|
|
29
|
+
* (Requirement 10.1, 10.3).
|
|
30
|
+
* - {@link toWriteMetadata} projects a stored object's metadata back onto the
|
|
31
|
+
* write-time subset, used when copying/moving so the destination preserves the
|
|
32
|
+
* source's content type, ownership, tenancy, access level, and custom fields.
|
|
33
|
+
*
|
|
34
|
+
* The module depends only on the shared type surface (`types.ts`), keeping the
|
|
35
|
+
* dependency direction acyclic (leaf module → types).
|
|
36
|
+
*
|
|
37
|
+
* _Requirements: 10.1, 10.2, 10.3_
|
|
38
|
+
*/
|
|
39
|
+
// ── Canonical field-set defaults ──────────────────────────────────────────────
|
|
40
|
+
/** Default content type applied when a write does not specify one. */
|
|
41
|
+
export const DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
42
|
+
/** Default access level applied when a write does not specify one. */
|
|
43
|
+
export const DEFAULT_ACCESS_LEVEL = "private";
|
|
44
|
+
/**
|
|
45
|
+
* The canonical, ordered set of typed metadata field names every object carries
|
|
46
|
+
* (Requirement 10.1). `versionId` is intentionally excluded — it is an optional
|
|
47
|
+
* field present only on versioned objects, not part of the guaranteed base set.
|
|
48
|
+
*/
|
|
49
|
+
export const STORAGE_METADATA_FIELDS = [
|
|
50
|
+
"key",
|
|
51
|
+
"size",
|
|
52
|
+
"contentType",
|
|
53
|
+
"etag",
|
|
54
|
+
"checksum",
|
|
55
|
+
"owner",
|
|
56
|
+
"tenant",
|
|
57
|
+
"accessLevel",
|
|
58
|
+
"createdAt",
|
|
59
|
+
"updatedAt",
|
|
60
|
+
"custom",
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* Build the complete, typed {@link StorageObjectMetadata} field set from the
|
|
64
|
+
* driver-computed identity/timestamp values and the caller's
|
|
65
|
+
* {@link WriteMetadata}. This is the single place the metadata shape and its
|
|
66
|
+
* defaults are defined, so every driver produces an identical field set
|
|
67
|
+
* (Requirement 10.1, 10.2, 10.3).
|
|
68
|
+
*
|
|
69
|
+
* The identity fields (`etag`/`checksum`/`size`) and timestamps are used exactly
|
|
70
|
+
* as provided — this function never recomputes them — so the driver's computed
|
|
71
|
+
* values remain the source of truth and cannot diverge from what was persisted.
|
|
72
|
+
*/
|
|
73
|
+
export function buildObjectMetadata(input) {
|
|
74
|
+
const { key, size, checksum, createdAt, updatedAt, write, versionId } = input;
|
|
75
|
+
const metadata = {
|
|
76
|
+
key,
|
|
77
|
+
size,
|
|
78
|
+
contentType: write.contentType ?? DEFAULT_CONTENT_TYPE,
|
|
79
|
+
etag: input.etag ?? checksum,
|
|
80
|
+
checksum,
|
|
81
|
+
owner: write.owner,
|
|
82
|
+
tenant: write.tenant,
|
|
83
|
+
accessLevel: write.accessLevel ?? DEFAULT_ACCESS_LEVEL,
|
|
84
|
+
createdAt,
|
|
85
|
+
updatedAt,
|
|
86
|
+
custom: write.custom ?? {},
|
|
87
|
+
...(versionId !== undefined ? { versionId } : {}),
|
|
88
|
+
};
|
|
89
|
+
return metadata;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Return a copy of `metadata` with the complete typed field set guaranteed
|
|
93
|
+
* present, applying the canonical defaults for any missing `contentType`,
|
|
94
|
+
* `accessLevel`, or `custom` field while preserving the driver-computed
|
|
95
|
+
* identity fields (`etag`/`checksum`/`size`) and timestamps untouched
|
|
96
|
+
* (Requirement 10.1, 10.3).
|
|
97
|
+
*
|
|
98
|
+
* The facade runs the metadata it surfaces on `put`/`stat`/`get` through this
|
|
99
|
+
* function so the full field set is consistently present regardless of which
|
|
100
|
+
* driver produced it. It is idempotent for metadata already built by
|
|
101
|
+
* {@link buildObjectMetadata}, so wiring it in adds no behavioral change for the
|
|
102
|
+
* zero-dependency drivers while shielding the public surface from a future
|
|
103
|
+
* driver that returns a partially populated record.
|
|
104
|
+
*/
|
|
105
|
+
export function normalizeMetadata(metadata) {
|
|
106
|
+
const normalized = {
|
|
107
|
+
key: metadata.key,
|
|
108
|
+
size: metadata.size,
|
|
109
|
+
contentType: metadata.contentType ?? DEFAULT_CONTENT_TYPE,
|
|
110
|
+
etag: metadata.etag,
|
|
111
|
+
checksum: metadata.checksum,
|
|
112
|
+
owner: metadata.owner,
|
|
113
|
+
tenant: metadata.tenant,
|
|
114
|
+
accessLevel: metadata.accessLevel ?? DEFAULT_ACCESS_LEVEL,
|
|
115
|
+
createdAt: metadata.createdAt,
|
|
116
|
+
updatedAt: metadata.updatedAt,
|
|
117
|
+
custom: metadata.custom ?? {},
|
|
118
|
+
...(metadata.versionId !== undefined ? { versionId: metadata.versionId } : {}),
|
|
119
|
+
};
|
|
120
|
+
return normalized;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Project a stored object's {@link StorageObjectMetadata} onto the write-time
|
|
124
|
+
* {@link WriteMetadata} subset. Used when copying/moving an object so the
|
|
125
|
+
* destination preserves the source's content type, ownership, tenancy, access
|
|
126
|
+
* level, and custom fields. The identity/timestamp fields (`etag`/`checksum`/
|
|
127
|
+
* `size`/`createdAt`/`updatedAt`) are intentionally omitted — the driver
|
|
128
|
+
* recomputes them for the destination object.
|
|
129
|
+
*/
|
|
130
|
+
export function toWriteMetadata(metadata) {
|
|
131
|
+
return {
|
|
132
|
+
contentType: metadata.contentType,
|
|
133
|
+
owner: metadata.owner,
|
|
134
|
+
tenant: metadata.tenant,
|
|
135
|
+
accessLevel: metadata.accessLevel,
|
|
136
|
+
custom: metadata.custom,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,iFAAiF;AAEjF,sEAAsE;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAG,0BAAmC,CAAC;AAExE,sEAAsE;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAgB,SAAS,CAAC;AAE3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,KAAK;IACL,MAAM;IACN,aAAa;IACb,MAAM;IACN,UAAU;IACV,OAAO;IACP,QAAQ;IACR,aAAa;IACb,WAAW;IACX,WAAW;IACX,QAAQ;CACA,CAAC;AA4BX;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAyB;IAC3D,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC9E,MAAM,QAAQ,GAA0B;QACtC,GAAG;QACH,IAAI;QACJ,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,oBAAoB;QACtD,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,QAAQ;QAC5B,QAAQ;QACR,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,oBAAoB;QACtD,SAAS;QACT,SAAS;QACT,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;QAC1B,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAA+B;IAC/D,MAAM,UAAU,GAA0B;QACxC,GAAG,EAAE,QAAQ,CAAC,GAAG;QACjB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,oBAAoB;QACzD,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,oBAAoB;QACzD,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,EAAE;QAC7B,GAAG,CAAC,QAAQ,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/E,CAAC;IACF,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,QAA+B;IAC7D,OAAO;QACL,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,MAAM,EAAE,QAAQ,CAAC,MAAM;KACxB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — the multipart upload manager.
|
|
3
|
+
*
|
|
4
|
+
* A {@link Multipart_Upload} splits a large object into independently uploaded
|
|
5
|
+
* parts that are later assembled into a single object whose content equals the
|
|
6
|
+
* concatenation of those parts in order (Requirement 6). {@link MultipartManager}
|
|
7
|
+
* provides this capability in a **provider-agnostic** way:
|
|
8
|
+
*
|
|
9
|
+
* - When the backing {@link StorageDriver} implements the optional native
|
|
10
|
+
* `multipart` capability, the manager delegates each operation to the driver
|
|
11
|
+
* so a provider that has first-class multipart support (e.g. S3) is used
|
|
12
|
+
* directly.
|
|
13
|
+
* - Otherwise the manager **simulates** multipart over the mandatory driver
|
|
14
|
+
* primitives (`put`/`get`/`putStream`/`delete`). Each `uploadPart` persists
|
|
15
|
+
* the part bytes under a reserved key derived from `(uploadId, partNumber)`
|
|
16
|
+
* (mirroring the design's `.multipart/<uploadId>/<partNumber>` layout), and
|
|
17
|
+
* `complete` streams the persisted parts back in the supplied order into the
|
|
18
|
+
* final object. Because completion assembles the object by reading one part at
|
|
19
|
+
* a time into a `Readable` and piping it through `driver.putStream`, the whole
|
|
20
|
+
* object is never required to be materialized in a single buffer by this
|
|
21
|
+
* layer — the design streams the concatenation and therefore imposes **no
|
|
22
|
+
* artificial size cap** (objects of a gigabyte or more are supported,
|
|
23
|
+
* Requirement 6.5).
|
|
24
|
+
*
|
|
25
|
+
* Semantics (Requirement 6):
|
|
26
|
+
* - {@link MultipartManager.create} mints and returns an upload identifier.
|
|
27
|
+
* - {@link MultipartManager.uploadPart} persists a part keyed by
|
|
28
|
+
* `(uploadId, partNumber)` and returns a {@link StoredPart} descriptor.
|
|
29
|
+
* - {@link MultipartManager.complete} concatenates the parts in the supplied
|
|
30
|
+
* order into the final object — equivalent to a single `put` of the
|
|
31
|
+
* concatenation — and returns the resulting metadata.
|
|
32
|
+
* - {@link MultipartManager.abort} discards every persisted part and creates no
|
|
33
|
+
* object.
|
|
34
|
+
*
|
|
35
|
+
* The module depends only on the driver contract, the shared type surface, the
|
|
36
|
+
* error hierarchy, and the metadata layer, keeping the dependency direction
|
|
37
|
+
* acyclic.
|
|
38
|
+
*
|
|
39
|
+
* _Requirements: 6.1, 6.2, 6.3, 6.4, 6.5_
|
|
40
|
+
*/
|
|
41
|
+
import type { StorageDriver, StoredPart } from "./driver.js";
|
|
42
|
+
import type { StorageObjectMetadata, WriteMetadata } from "./types.js";
|
|
43
|
+
/**
|
|
44
|
+
* Provider-agnostic multipart upload manager built on the driver contract.
|
|
45
|
+
*
|
|
46
|
+
* A single instance is held by the facade and bound to one {@link StorageDriver}
|
|
47
|
+
* for its lifetime, so the native-vs-simulated decision (based on whether the
|
|
48
|
+
* driver exposes a `multipart` capability) is stable across every call for a
|
|
49
|
+
* given upload identifier.
|
|
50
|
+
*/
|
|
51
|
+
export declare class MultipartManager {
|
|
52
|
+
/** The driver every operation is delegated to or simulated over. */
|
|
53
|
+
private readonly driver;
|
|
54
|
+
/**
|
|
55
|
+
* In-progress simulated sessions keyed by upload id. Empty when the driver
|
|
56
|
+
* provides a native `multipart` capability (that path keeps no local state).
|
|
57
|
+
*/
|
|
58
|
+
private readonly sessions;
|
|
59
|
+
constructor(driver: StorageDriver);
|
|
60
|
+
/** The driver's native multipart capability, when present. */
|
|
61
|
+
private get native();
|
|
62
|
+
/**
|
|
63
|
+
* Begin a multipart upload for `key` and return its upload identifier
|
|
64
|
+
* (Requirement 6.1). When the driver has a native capability the driver mints
|
|
65
|
+
* the id; otherwise a fresh id is generated and a local session records the
|
|
66
|
+
* target key and its write-time metadata for assembly at completion.
|
|
67
|
+
*/
|
|
68
|
+
create(key: string, metadata: WriteMetadata): Promise<string>;
|
|
69
|
+
/**
|
|
70
|
+
* Persist a single part for `uploadId` and return its {@link StoredPart}
|
|
71
|
+
* descriptor (Requirement 6.2). The part bytes are copied defensively before
|
|
72
|
+
* persistence so later mutation of the caller's buffer cannot corrupt a stored
|
|
73
|
+
* part. The returned `etag` is the sha-256 hex digest of the part bytes,
|
|
74
|
+
* matching the checksum scheme the drivers use elsewhere.
|
|
75
|
+
*/
|
|
76
|
+
uploadPart(uploadId: string, partNumber: number, bytes: Uint8Array): Promise<StoredPart>;
|
|
77
|
+
/**
|
|
78
|
+
* Assemble the supplied `parts` in order into the final object and return its
|
|
79
|
+
* metadata (Requirement 6.3). The assembled content equals the concatenation
|
|
80
|
+
* of the parts in the given order — equivalent to a single `put` of that
|
|
81
|
+
* concatenation.
|
|
82
|
+
*
|
|
83
|
+
* For the simulated path the parts are streamed back one at a time from the
|
|
84
|
+
* driver into a `Readable` piped through `driver.putStream`, so this layer
|
|
85
|
+
* never buffers the full object at once (Requirement 6.5). Every persisted
|
|
86
|
+
* part (whether or not it appears in `parts`) is discarded afterward and the
|
|
87
|
+
* session is closed.
|
|
88
|
+
*/
|
|
89
|
+
complete(uploadId: string, parts: readonly StoredPart[]): Promise<StorageObjectMetadata>;
|
|
90
|
+
/**
|
|
91
|
+
* Discard every persisted part for `uploadId` and create no completed object
|
|
92
|
+
* (Requirement 6.4). Delegates to the native capability when present; for the
|
|
93
|
+
* simulated path it deletes all recorded part keys and closes the session. An
|
|
94
|
+
* unknown/already-finalized upload id is treated as an idempotent no-op.
|
|
95
|
+
*/
|
|
96
|
+
abort(uploadId: string): Promise<void>;
|
|
97
|
+
/** Delete every persisted part for a simulated upload id. */
|
|
98
|
+
private discardParts;
|
|
99
|
+
/** The reserved driver key a simulated part is persisted under. */
|
|
100
|
+
private partKey;
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the simulated session for `uploadId`, throwing a descriptive
|
|
103
|
+
* {@link StorageError} when the id is unknown or the upload has already been
|
|
104
|
+
* finalized.
|
|
105
|
+
*/
|
|
106
|
+
private requireSession;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=multipart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../src/multipart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAMH,OAAO,KAAK,EAAuB,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGlF,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA6BvE;;;;;;;GAOG;AACH,qBAAa,gBAAgB;IAC3B,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IAEvC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;gBAEpD,MAAM,EAAE,aAAa;IAIjC,8DAA8D;IAC9D,OAAO,KAAK,MAAM,GAEjB;IAED;;;;;OAKG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IASnE;;;;;;OAMG;IACG,UAAU,CACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,UAAU,CAAC;IActB;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAmC9F;;;;;OAKG;IACG,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C,6DAA6D;YAC/C,YAAY;IAM1B,mEAAmE;IACnE,OAAO,CAAC,OAAO;IAIf;;;;OAIG;IACH,OAAO,CAAC,cAAc;CASvB"}
|