@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
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/plugin.ts
|
|
2
|
+
// @streetjs/storage — plugin registration entry point (reuses the core
|
|
3
|
+
// PluginModule / SandboxedApp).
|
|
4
|
+
//
|
|
5
|
+
// `StoragePlugin.onLoad` constructs the facade via `createStorage`, which wires
|
|
6
|
+
// observability (health check + metrics) against the `metrics` / `health`
|
|
7
|
+
// registries supplied on the plugin options, applies the declarative bridge
|
|
8
|
+
// wiring, and exposes the live facade through the `storage` accessor.
|
|
9
|
+
// `onUnload` detaches the bridges and closes the facade gracefully
|
|
10
|
+
// (`storage.close()` stops the observability refresh timer).
|
|
11
|
+
//
|
|
12
|
+
// The `SandboxedApp` handed to a plugin exposes only `use` / `on` — it has no
|
|
13
|
+
// registry to attach to — so the health / metrics registries are resolved from
|
|
14
|
+
// the plugin options (mirroring how the events / queue / realtime plugins
|
|
15
|
+
// resolve theirs). The plugin never touches any existing public API: it only
|
|
16
|
+
// consumes the already-public `createStorage` surface (Requirements 20.1,
|
|
17
|
+
// 20.2, 20.3).
|
|
18
|
+
import { PluginModule } from "streetjs";
|
|
19
|
+
import { createStorage } from "./facade.js";
|
|
20
|
+
/**
|
|
21
|
+
* Plugin entry point that constructs and wires the application storage layer.
|
|
22
|
+
*
|
|
23
|
+
* Compatible with the core {@link PluginModule} and safe to register with a
|
|
24
|
+
* `SandboxedApp` (Requirements 20.1, 20.2). Once loaded, the application
|
|
25
|
+
* retrieves the live {@link Storage} facade through the {@link storage}
|
|
26
|
+
* accessor without any existing public API being modified (Requirement 20.3).
|
|
27
|
+
*
|
|
28
|
+
* @typeParam T - Optional per-application custom metadata map.
|
|
29
|
+
*/
|
|
30
|
+
export class StoragePlugin extends PluginModule {
|
|
31
|
+
name = "@streetjs/storage";
|
|
32
|
+
version = "1.0.0";
|
|
33
|
+
options;
|
|
34
|
+
storageInstance;
|
|
35
|
+
bridgeDetachers = [];
|
|
36
|
+
constructor(options) {
|
|
37
|
+
super();
|
|
38
|
+
this.options = options;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The constructed facade, or `undefined` before load / after unload. Because
|
|
42
|
+
* the `SandboxedApp` has no registry to attach to, the application retrieves
|
|
43
|
+
* the live {@link Storage} facade through this accessor after the plugin
|
|
44
|
+
* loads (Requirement 20.3).
|
|
45
|
+
*/
|
|
46
|
+
get storage() {
|
|
47
|
+
return this.storageInstance;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Construct the facade (which wires observability from the configured
|
|
51
|
+
* `metrics` / `health` registries and the structural bridges), apply the
|
|
52
|
+
* declarative bridge wiring collecting any detach functions, and expose the
|
|
53
|
+
* facade. Idempotent per load: a second `onLoad` without an intervening
|
|
54
|
+
* `onUnload` reuses the already-constructed facade.
|
|
55
|
+
*/
|
|
56
|
+
async onLoad(_app) {
|
|
57
|
+
if (this.storageInstance) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// `createStorage` resolves the driver and, when `metrics` / `health` are
|
|
61
|
+
// present on the config, registers storage observability against those
|
|
62
|
+
// registries and primes the gauges — so passing the plugin options through
|
|
63
|
+
// as the StorageConfig wires observability from the plugin-option
|
|
64
|
+
// registries (Requirement 23.1, 23.3). The structural events / queue /
|
|
65
|
+
// realtime bridges on `options.bridges` are likewise wired by the facade.
|
|
66
|
+
const storage = createStorage(this.options);
|
|
67
|
+
this.storageInstance = storage;
|
|
68
|
+
// Apply declarative bridge wiring, collecting any detach functions so
|
|
69
|
+
// onUnload can tear them down.
|
|
70
|
+
for (const attach of this.options.wireBridges ?? []) {
|
|
71
|
+
const detach = attach(storage);
|
|
72
|
+
if (typeof detach === "function") {
|
|
73
|
+
this.bridgeDetachers.push(detach);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Detach the declarative bridges and close the facade gracefully (which stops
|
|
79
|
+
* the observability refresh timer). Safe if never loaded.
|
|
80
|
+
*/
|
|
81
|
+
async onUnload(_app) {
|
|
82
|
+
const storage = this.storageInstance;
|
|
83
|
+
const detachers = this.bridgeDetachers;
|
|
84
|
+
this.storageInstance = undefined;
|
|
85
|
+
this.bridgeDetachers = [];
|
|
86
|
+
// Detach bridges first so no bridge fires during/after teardown.
|
|
87
|
+
for (const detach of detachers) {
|
|
88
|
+
try {
|
|
89
|
+
detach();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
/* best-effort detach */
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (storage) {
|
|
96
|
+
await storage.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,uEAAuE;AACvE,gCAAgC;AAChC,EAAE;AACF,gFAAgF;AAChF,0EAA0E;AAC1E,4EAA4E;AAC5E,sEAAsE;AACtE,mEAAmE;AACnE,6DAA6D;AAC7D,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;AAC1E,6EAA6E;AAC7E,0EAA0E;AAC1E,eAAe;AAEf,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;AA4C1D;;;;;;;;;GASG;AACH,MAAM,OAAO,aAEX,SAAQ,YAAY;IACX,IAAI,GAAG,mBAAmB,CAAC;IAC3B,OAAO,GAAG,OAAO,CAAC;IAER,OAAO,CAA0B;IAE5C,eAAe,CAAc;IAC7B,eAAe,GAAsB,EAAE,CAAC;IAEhD,YAAY,OAAgC;QAC1C,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED;;;;;;OAMG;IACM,KAAK,CAAC,MAAM,CAAC,IAAkB;QACtC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QAED,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,kEAAkE;QAClE,uEAAuE;QACvE,0EAA0E;QAC1E,MAAM,OAAO,GAAG,aAAa,CAAI,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;QAE/B,sEAAsE;QACtE,+BAA+B;QAC/B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;YACpD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC/B,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;gBACjC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACM,KAAK,CAAC,QAAQ,CAAC,IAAkB;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC;QACvC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACjC,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,iEAAiE;QACjE,KAAK,MAAM,MAAM,IAAI,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC;YACX,CAAC;YAAC,MAAM,CAAC;gBACP,wBAAwB;YAC1B,CAAC;QACH,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — the resumable upload manager.
|
|
3
|
+
*
|
|
4
|
+
* A {@link Resumable_Upload} is an upload session that can be interrupted and
|
|
5
|
+
* continued from the last persisted byte offset without restarting from the
|
|
6
|
+
* beginning (Requirement 7). {@link ResumableManager} provides this capability
|
|
7
|
+
* in a **provider-agnostic** way, mirroring {@link MultipartManager}:
|
|
8
|
+
*
|
|
9
|
+
* - When the backing {@link StorageDriver} implements the optional native
|
|
10
|
+
* `resumable` capability, the manager delegates to the driver so a provider
|
|
11
|
+
* with first-class resumable support (e.g. GCS resumable uploads) is used
|
|
12
|
+
* directly.
|
|
13
|
+
* - Otherwise the manager **simulates** resumable sessions over the mandatory
|
|
14
|
+
* driver primitives (`put`/`get`/`delete`). The accumulated bytes for a
|
|
15
|
+
* session are persisted under a reserved key derived from the session id
|
|
16
|
+
* (mirroring the design's `.resumable/<sessionId>` layout), and the persisted
|
|
17
|
+
* byte offset is simply the length of that accumulated content.
|
|
18
|
+
*
|
|
19
|
+
* Semantics (Requirement 7):
|
|
20
|
+
* - {@link ResumableManager.start} creates a session and returns a session id
|
|
21
|
+
* (Requirement 7.1); the persisted offset begins at zero.
|
|
22
|
+
* - {@link ResumableManager.resume} continues the upload from the last persisted
|
|
23
|
+
* offset (Requirement 7.2). The supplied stream carries the object's **full**
|
|
24
|
+
* intended content from offset zero; the manager appends only the portion of
|
|
25
|
+
* the stream that lies beyond the already-persisted offset and skips the bytes
|
|
26
|
+
* it already holds. Because the final object is exactly the full stream
|
|
27
|
+
* content regardless of how many times the upload was interrupted and resumed,
|
|
28
|
+
* completion yields an object byte-identical to an equivalent uninterrupted
|
|
29
|
+
* upload (Requirement 7.3).
|
|
30
|
+
* - {@link ResumableManager.cancel} discards the session and its persisted data,
|
|
31
|
+
* creating no object (Requirement 7.4) — **unless** the session is already
|
|
32
|
+
* completing (its stream has been fully consumed and the final object is being
|
|
33
|
+
* written), in which case the cancel is ignored and the upload is allowed to
|
|
34
|
+
* finish and create the object (Requirement 7.5).
|
|
35
|
+
*
|
|
36
|
+
* The module depends only on the driver contract, the shared type surface, the
|
|
37
|
+
* error hierarchy, and the metadata layer, keeping the dependency direction
|
|
38
|
+
* acyclic.
|
|
39
|
+
*
|
|
40
|
+
* _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5_
|
|
41
|
+
*/
|
|
42
|
+
import type { NodeReadable, StorageDriver } from "./driver.js";
|
|
43
|
+
import type { StorageObjectMetadata, WriteMetadata } from "./types.js";
|
|
44
|
+
/**
|
|
45
|
+
* Provider-agnostic resumable upload manager built on the driver contract.
|
|
46
|
+
*
|
|
47
|
+
* A single instance is held by the facade and bound to one {@link StorageDriver}
|
|
48
|
+
* for its lifetime, so the native-vs-simulated decision (based on whether the
|
|
49
|
+
* driver exposes a `resumable` capability) is stable across every call for a
|
|
50
|
+
* given session id.
|
|
51
|
+
*/
|
|
52
|
+
export declare class ResumableManager {
|
|
53
|
+
/** The driver every operation is delegated to or simulated over. */
|
|
54
|
+
private readonly driver;
|
|
55
|
+
/**
|
|
56
|
+
* In-progress simulated sessions keyed by session id. Empty when the driver
|
|
57
|
+
* provides a native `resumable` capability (that path keeps no local state).
|
|
58
|
+
*/
|
|
59
|
+
private readonly sessions;
|
|
60
|
+
constructor(driver: StorageDriver);
|
|
61
|
+
/** The driver's native resumable capability, when present. */
|
|
62
|
+
private get native();
|
|
63
|
+
/**
|
|
64
|
+
* Create a resumable upload session for `key` and return its session id
|
|
65
|
+
* (Requirement 7.1). When the driver has a native capability the driver mints
|
|
66
|
+
* the id and initializes the offset; otherwise a fresh id is generated and a
|
|
67
|
+
* local session records the target key and its write-time metadata. No
|
|
68
|
+
* reserved key is written yet — the persisted offset is zero until the first
|
|
69
|
+
* `resume` appends content.
|
|
70
|
+
*/
|
|
71
|
+
start(key: string, metadata: WriteMetadata): Promise<string>;
|
|
72
|
+
/**
|
|
73
|
+
* Continue the upload for `sessionId` from the last persisted offset and, on
|
|
74
|
+
* completion, create the final object and return its metadata (Requirements
|
|
75
|
+
* 7.2, 7.3).
|
|
76
|
+
*
|
|
77
|
+
* `stream` carries the object's full intended content from offset zero. The
|
|
78
|
+
* manager appends only the bytes lying beyond the already-persisted offset —
|
|
79
|
+
* bytes it already holds are skipped — so the final object equals the full
|
|
80
|
+
* stream content and is byte-identical to an equivalent uninterrupted upload
|
|
81
|
+
* regardless of how many prior interruptions occurred.
|
|
82
|
+
*
|
|
83
|
+
* Once the stream is fully consumed the session enters its completing phase;
|
|
84
|
+
* from that point a concurrent {@link cancel} is ignored so the object is
|
|
85
|
+
* still created (Requirement 7.5). If a cancel arrives *before* completion,
|
|
86
|
+
* the in-progress resume aborts and creates no object (Requirement 7.4).
|
|
87
|
+
*/
|
|
88
|
+
resume(sessionId: string, stream: NodeReadable): Promise<StorageObjectMetadata>;
|
|
89
|
+
/**
|
|
90
|
+
* Discard the session `sessionId` and its persisted data, creating no object
|
|
91
|
+
* (Requirement 7.4). If the session is already completing — its stream has
|
|
92
|
+
* been fully consumed and the final object is being written — the cancel is
|
|
93
|
+
* ignored so the upload finishes and creates the object (Requirement 7.5). An
|
|
94
|
+
* unknown/already-finalized session id is treated as an idempotent no-op.
|
|
95
|
+
*/
|
|
96
|
+
cancel(sessionId: string): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Native-capability resume: drive the driver's `offset`/`append`/`finish`
|
|
99
|
+
* primitives. As in the simulated path, `stream` carries the full content and
|
|
100
|
+
* only the portion beyond the driver's reported offset is appended, so the
|
|
101
|
+
* finished object is byte-identical to an uninterrupted upload.
|
|
102
|
+
*/
|
|
103
|
+
private resumeNative;
|
|
104
|
+
/**
|
|
105
|
+
* Read the accumulated bytes persisted for a simulated session, returning an
|
|
106
|
+
* empty buffer when nothing has been persisted yet (offset zero).
|
|
107
|
+
*/
|
|
108
|
+
private readPersisted;
|
|
109
|
+
/** The reserved driver key a simulated session's accumulated bytes live under. */
|
|
110
|
+
private sessionKey;
|
|
111
|
+
/**
|
|
112
|
+
* Resolve the simulated session for `sessionId`, throwing a descriptive
|
|
113
|
+
* {@link StorageError} when the id is unknown or the session has already been
|
|
114
|
+
* finalized or cancelled.
|
|
115
|
+
*/
|
|
116
|
+
private requireSession;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=resumable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resumable.d.ts","sourceRoot":"","sources":["../src/resumable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAuB,aAAa,EAAE,MAAM,aAAa,CAAC;AAGpF,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAqCvE;;;;;;;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;;;;;;;OAOG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IASlE;;;;;;;;;;;;;;;OAeG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAqDrF;;;;;;OAMG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB9C;;;;;OAKG;YACW,YAAY;IAsB1B;;;OAGG;YACW,aAAa;IAK3B,kFAAkF;IAClF,OAAO,CAAC,UAAU;IAIlB;;;;OAIG;IACH,OAAO,CAAC,cAAc;CASvB"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — the resumable upload manager.
|
|
3
|
+
*
|
|
4
|
+
* A {@link Resumable_Upload} is an upload session that can be interrupted and
|
|
5
|
+
* continued from the last persisted byte offset without restarting from the
|
|
6
|
+
* beginning (Requirement 7). {@link ResumableManager} provides this capability
|
|
7
|
+
* in a **provider-agnostic** way, mirroring {@link MultipartManager}:
|
|
8
|
+
*
|
|
9
|
+
* - When the backing {@link StorageDriver} implements the optional native
|
|
10
|
+
* `resumable` capability, the manager delegates to the driver so a provider
|
|
11
|
+
* with first-class resumable support (e.g. GCS resumable uploads) is used
|
|
12
|
+
* directly.
|
|
13
|
+
* - Otherwise the manager **simulates** resumable sessions over the mandatory
|
|
14
|
+
* driver primitives (`put`/`get`/`delete`). The accumulated bytes for a
|
|
15
|
+
* session are persisted under a reserved key derived from the session id
|
|
16
|
+
* (mirroring the design's `.resumable/<sessionId>` layout), and the persisted
|
|
17
|
+
* byte offset is simply the length of that accumulated content.
|
|
18
|
+
*
|
|
19
|
+
* Semantics (Requirement 7):
|
|
20
|
+
* - {@link ResumableManager.start} creates a session and returns a session id
|
|
21
|
+
* (Requirement 7.1); the persisted offset begins at zero.
|
|
22
|
+
* - {@link ResumableManager.resume} continues the upload from the last persisted
|
|
23
|
+
* offset (Requirement 7.2). The supplied stream carries the object's **full**
|
|
24
|
+
* intended content from offset zero; the manager appends only the portion of
|
|
25
|
+
* the stream that lies beyond the already-persisted offset and skips the bytes
|
|
26
|
+
* it already holds. Because the final object is exactly the full stream
|
|
27
|
+
* content regardless of how many times the upload was interrupted and resumed,
|
|
28
|
+
* completion yields an object byte-identical to an equivalent uninterrupted
|
|
29
|
+
* upload (Requirement 7.3).
|
|
30
|
+
* - {@link ResumableManager.cancel} discards the session and its persisted data,
|
|
31
|
+
* creating no object (Requirement 7.4) — **unless** the session is already
|
|
32
|
+
* completing (its stream has been fully consumed and the final object is being
|
|
33
|
+
* written), in which case the cancel is ignored and the upload is allowed to
|
|
34
|
+
* finish and create the object (Requirement 7.5).
|
|
35
|
+
*
|
|
36
|
+
* The module depends only on the driver contract, the shared type surface, the
|
|
37
|
+
* error hierarchy, and the metadata layer, keeping the dependency direction
|
|
38
|
+
* acyclic.
|
|
39
|
+
*
|
|
40
|
+
* _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5_
|
|
41
|
+
*/
|
|
42
|
+
import { Buffer } from "node:buffer";
|
|
43
|
+
import { randomUUID } from "node:crypto";
|
|
44
|
+
import { StorageError } from "./errors.js";
|
|
45
|
+
import { normalizeMetadata } from "./metadata.js";
|
|
46
|
+
/**
|
|
47
|
+
* Reserved key prefix under which a simulated session's accumulated bytes are
|
|
48
|
+
* persisted via the driver primitives. Session content lives at
|
|
49
|
+
* `${SESSION_KEY_PREFIX}<sessionId>` and is removed on completion/cancel, so it
|
|
50
|
+
* never remains visible after a resumable upload finalizes.
|
|
51
|
+
*/
|
|
52
|
+
const SESSION_KEY_PREFIX = ".resumable/";
|
|
53
|
+
/** Write metadata used when persisting a session's (transient) accumulated bytes. */
|
|
54
|
+
const SESSION_WRITE_METADATA = { contentType: "application/octet-stream" };
|
|
55
|
+
/**
|
|
56
|
+
* Provider-agnostic resumable upload manager built on the driver contract.
|
|
57
|
+
*
|
|
58
|
+
* A single instance is held by the facade and bound to one {@link StorageDriver}
|
|
59
|
+
* for its lifetime, so the native-vs-simulated decision (based on whether the
|
|
60
|
+
* driver exposes a `resumable` capability) is stable across every call for a
|
|
61
|
+
* given session id.
|
|
62
|
+
*/
|
|
63
|
+
export class ResumableManager {
|
|
64
|
+
/** The driver every operation is delegated to or simulated over. */
|
|
65
|
+
driver;
|
|
66
|
+
/**
|
|
67
|
+
* In-progress simulated sessions keyed by session id. Empty when the driver
|
|
68
|
+
* provides a native `resumable` capability (that path keeps no local state).
|
|
69
|
+
*/
|
|
70
|
+
sessions = new Map();
|
|
71
|
+
constructor(driver) {
|
|
72
|
+
this.driver = driver;
|
|
73
|
+
}
|
|
74
|
+
/** The driver's native resumable capability, when present. */
|
|
75
|
+
get native() {
|
|
76
|
+
return this.driver.resumable;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create a resumable upload session for `key` and return its session id
|
|
80
|
+
* (Requirement 7.1). When the driver has a native capability the driver mints
|
|
81
|
+
* the id and initializes the offset; otherwise a fresh id is generated and a
|
|
82
|
+
* local session records the target key and its write-time metadata. No
|
|
83
|
+
* reserved key is written yet — the persisted offset is zero until the first
|
|
84
|
+
* `resume` appends content.
|
|
85
|
+
*/
|
|
86
|
+
async start(key, metadata) {
|
|
87
|
+
if (this.native !== undefined) {
|
|
88
|
+
return this.native.start(key, metadata);
|
|
89
|
+
}
|
|
90
|
+
const sessionId = randomUUID();
|
|
91
|
+
this.sessions.set(sessionId, { key, metadata, completing: false, cancelled: false });
|
|
92
|
+
return sessionId;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Continue the upload for `sessionId` from the last persisted offset and, on
|
|
96
|
+
* completion, create the final object and return its metadata (Requirements
|
|
97
|
+
* 7.2, 7.3).
|
|
98
|
+
*
|
|
99
|
+
* `stream` carries the object's full intended content from offset zero. The
|
|
100
|
+
* manager appends only the bytes lying beyond the already-persisted offset —
|
|
101
|
+
* bytes it already holds are skipped — so the final object equals the full
|
|
102
|
+
* stream content and is byte-identical to an equivalent uninterrupted upload
|
|
103
|
+
* regardless of how many prior interruptions occurred.
|
|
104
|
+
*
|
|
105
|
+
* Once the stream is fully consumed the session enters its completing phase;
|
|
106
|
+
* from that point a concurrent {@link cancel} is ignored so the object is
|
|
107
|
+
* still created (Requirement 7.5). If a cancel arrives *before* completion,
|
|
108
|
+
* the in-progress resume aborts and creates no object (Requirement 7.4).
|
|
109
|
+
*/
|
|
110
|
+
async resume(sessionId, stream) {
|
|
111
|
+
if (this.native !== undefined) {
|
|
112
|
+
return this.resumeNative(this.native, sessionId, stream);
|
|
113
|
+
}
|
|
114
|
+
const session = this.requireSession(sessionId);
|
|
115
|
+
const reservedKey = this.sessionKey(sessionId);
|
|
116
|
+
// The persisted content (and therefore the offset) is the single source of
|
|
117
|
+
// truth for how many bytes have already been stored for this session.
|
|
118
|
+
let persisted = await this.readPersisted(reservedKey);
|
|
119
|
+
let seen = 0;
|
|
120
|
+
for await (const chunk of stream) {
|
|
121
|
+
// A cancel that arrived before completion aborts the resume and leaves no
|
|
122
|
+
// final object (Requirement 7.4); the reserved data has already been
|
|
123
|
+
// discarded by cancel, but delete again defensively in case this resume
|
|
124
|
+
// wrote after the cancel began.
|
|
125
|
+
if (session.cancelled) {
|
|
126
|
+
await this.driver.delete(reservedKey);
|
|
127
|
+
throw new StorageError(`Resumable upload session "${sessionId}" was cancelled during resume.`);
|
|
128
|
+
}
|
|
129
|
+
const buf = toBuffer(chunk);
|
|
130
|
+
const chunkEnd = seen + buf.byteLength;
|
|
131
|
+
// Append only the portion of this chunk that lies beyond the persisted
|
|
132
|
+
// offset; bytes at positions already persisted are skipped.
|
|
133
|
+
if (chunkEnd > persisted.byteLength) {
|
|
134
|
+
const skip = Math.max(0, persisted.byteLength - seen);
|
|
135
|
+
const toAppend = buf.subarray(skip);
|
|
136
|
+
persisted = Buffer.concat([persisted, toAppend]);
|
|
137
|
+
await this.driver.put(reservedKey, new Uint8Array(persisted), SESSION_WRITE_METADATA);
|
|
138
|
+
}
|
|
139
|
+
seen = chunkEnd;
|
|
140
|
+
}
|
|
141
|
+
// Completion phase: from here the upload is committing, so a concurrent
|
|
142
|
+
// cancel is ignored and the object is still created (Requirement 7.5).
|
|
143
|
+
session.completing = true;
|
|
144
|
+
const metadata = await this.driver.put(session.key, new Uint8Array(persisted), session.metadata);
|
|
145
|
+
await this.driver.delete(reservedKey);
|
|
146
|
+
this.sessions.delete(sessionId);
|
|
147
|
+
return normalizeMetadata(metadata);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Discard the session `sessionId` and its persisted data, creating no object
|
|
151
|
+
* (Requirement 7.4). If the session is already completing — its stream has
|
|
152
|
+
* been fully consumed and the final object is being written — the cancel is
|
|
153
|
+
* ignored so the upload finishes and creates the object (Requirement 7.5). An
|
|
154
|
+
* unknown/already-finalized session id is treated as an idempotent no-op.
|
|
155
|
+
*/
|
|
156
|
+
async cancel(sessionId) {
|
|
157
|
+
if (this.native !== undefined) {
|
|
158
|
+
await this.native.cancel(sessionId);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const session = this.sessions.get(sessionId);
|
|
162
|
+
if (session === undefined) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
// Already committing — let the in-flight resume finish and create the object.
|
|
166
|
+
if (session.completing) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
session.cancelled = true;
|
|
170
|
+
await this.driver.delete(this.sessionKey(sessionId));
|
|
171
|
+
this.sessions.delete(sessionId);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Native-capability resume: drive the driver's `offset`/`append`/`finish`
|
|
175
|
+
* primitives. As in the simulated path, `stream` carries the full content and
|
|
176
|
+
* only the portion beyond the driver's reported offset is appended, so the
|
|
177
|
+
* finished object is byte-identical to an uninterrupted upload.
|
|
178
|
+
*/
|
|
179
|
+
async resumeNative(native, sessionId, stream) {
|
|
180
|
+
let offset = await native.offset(sessionId);
|
|
181
|
+
let seen = 0;
|
|
182
|
+
for await (const chunk of stream) {
|
|
183
|
+
const buf = toBuffer(chunk);
|
|
184
|
+
const chunkEnd = seen + buf.byteLength;
|
|
185
|
+
if (chunkEnd > offset) {
|
|
186
|
+
const skip = Math.max(0, offset - seen);
|
|
187
|
+
const toAppend = buf.subarray(skip);
|
|
188
|
+
offset = await native.append(sessionId, new Uint8Array(toAppend), offset);
|
|
189
|
+
}
|
|
190
|
+
seen = chunkEnd;
|
|
191
|
+
}
|
|
192
|
+
return normalizeMetadata(await native.finish(sessionId));
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Read the accumulated bytes persisted for a simulated session, returning an
|
|
196
|
+
* empty buffer when nothing has been persisted yet (offset zero).
|
|
197
|
+
*/
|
|
198
|
+
async readPersisted(reservedKey) {
|
|
199
|
+
const result = await this.driver.get(reservedKey);
|
|
200
|
+
return result.found ? Buffer.from(result.bytes) : Buffer.alloc(0);
|
|
201
|
+
}
|
|
202
|
+
/** The reserved driver key a simulated session's accumulated bytes live under. */
|
|
203
|
+
sessionKey(sessionId) {
|
|
204
|
+
return `${SESSION_KEY_PREFIX}${sessionId}`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Resolve the simulated session for `sessionId`, throwing a descriptive
|
|
208
|
+
* {@link StorageError} when the id is unknown or the session has already been
|
|
209
|
+
* finalized or cancelled.
|
|
210
|
+
*/
|
|
211
|
+
requireSession(sessionId) {
|
|
212
|
+
const session = this.sessions.get(sessionId);
|
|
213
|
+
if (session === undefined) {
|
|
214
|
+
throw new StorageError(`Unknown or already-finalized resumable upload session "${sessionId}".`);
|
|
215
|
+
}
|
|
216
|
+
return session;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** Normalize a stream chunk to a `Buffer` without copying when already one. */
|
|
220
|
+
function toBuffer(chunk) {
|
|
221
|
+
return Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=resumable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resumable.js","sourceRoot":"","sources":["../src/resumable.ts"],"names":[],"mappings":"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"}
|
package/dist/search.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — attribute search / filtering over the flat key space
|
|
3
|
+
* (task 18.1).
|
|
4
|
+
*
|
|
5
|
+
* Object stores expose only a flat key space plus per-object metadata, so
|
|
6
|
+
* search is implemented as a provider-agnostic filter over the two mandatory
|
|
7
|
+
* driver primitives {@link StorageDriver.list} and {@link StorageDriver.stat}:
|
|
8
|
+
*
|
|
9
|
+
* 1. Enumerate candidate keys with `driver.list(prefix)` (the prefix filter is
|
|
10
|
+
* pushed down to the driver; an absent prefix lists the whole key space).
|
|
11
|
+
* 2. For each candidate, read its typed metadata with `driver.stat(key)`.
|
|
12
|
+
* 3. Keep the object only when it satisfies **every** supplied filter
|
|
13
|
+
* (conjunctive / AND semantics — Requirement 16.2); when no object satisfies
|
|
14
|
+
* the filters the result is the empty set (Requirement 16.3).
|
|
15
|
+
*
|
|
16
|
+
* The supported filters mirror {@link SearchFilters} (Requirement 16.1):
|
|
17
|
+
*
|
|
18
|
+
* - `prefix` — key begins with the prefix (pushed to `driver.list`).
|
|
19
|
+
* - `contentType` — exact content-type match.
|
|
20
|
+
* - `owner` — exact owner match.
|
|
21
|
+
* - `tenant` — exact tenant match.
|
|
22
|
+
* - `minSize` — size ≥ minSize (inclusive lower bound).
|
|
23
|
+
* - `maxSize` — size ≤ maxSize (inclusive upper bound).
|
|
24
|
+
* - `updatedAfter` — updatedAt ≥ updatedAfter (inclusive lower time bound).
|
|
25
|
+
* - `updatedBefore` — updatedAt ≤ updatedBefore (inclusive upper time bound).
|
|
26
|
+
* - `metadata` — every supplied custom field deep-equals the object's
|
|
27
|
+
* corresponding `custom` field.
|
|
28
|
+
*
|
|
29
|
+
* Reserved internal key spaces used by the cross-cutting layers
|
|
30
|
+
* (`.versions/`, `.multipart/`, `.resumable/`, `.archive/`) are excluded from
|
|
31
|
+
* results unless the supplied `prefix` explicitly targets one of them, so a
|
|
32
|
+
* broad search returns only user-visible objects and never leaks framework
|
|
33
|
+
* bookkeeping keys.
|
|
34
|
+
*
|
|
35
|
+
* The result preserves the {@link StorageListItem} shape returned by
|
|
36
|
+
* `driver.list`, so `search` composes with the rest of the facade surface.
|
|
37
|
+
*
|
|
38
|
+
* _Requirements: 16.1, 16.2, 16.3_
|
|
39
|
+
*/
|
|
40
|
+
import type { StorageDriver } from "./driver.js";
|
|
41
|
+
import type { SearchFilters, StorageListItem } from "./types.js";
|
|
42
|
+
/**
|
|
43
|
+
* Return the stored objects that satisfy **every** supplied filter, evaluated
|
|
44
|
+
* over `driver.list` + `driver.stat`.
|
|
45
|
+
*
|
|
46
|
+
* The prefix filter is delegated to `driver.list`; the remaining filters are
|
|
47
|
+
* applied against each candidate's metadata. Objects whose metadata cannot be
|
|
48
|
+
* read (e.g. removed concurrently between `list` and `stat`) are skipped. When
|
|
49
|
+
* no object satisfies the filters, an empty array is returned (Requirement
|
|
50
|
+
* 16.3).
|
|
51
|
+
*
|
|
52
|
+
* @param driver The storage driver to search over.
|
|
53
|
+
* @param filters The conjunctive set of {@link SearchFilters} to apply.
|
|
54
|
+
* @returns The matching objects as {@link StorageListItem}s.
|
|
55
|
+
*/
|
|
56
|
+
export declare function searchObjects(driver: StorageDriver, filters?: SearchFilters): Promise<StorageListItem[]>;
|
|
57
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAyB,MAAM,YAAY,CAAC;AAcxF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,eAAe,EAAE,CAAC,CA2B5B"}
|