@effect-pantry/storage 0.0.1
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 +223 -0
- package/dist/adapter.d.ts +13 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +10 -0
- package/dist/adapter.js.map +1 -0
- package/dist/errors.d.ts +62 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +60 -0
- package/dist/errors.js.map +1 -0
- package/dist/features/transfer.d.ts +50 -0
- package/dist/features/transfer.d.ts.map +1 -0
- package/dist/features/transfer.js +46 -0
- package/dist/features/transfer.js.map +1 -0
- package/dist/hooks.d.ts +26 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +2 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +32 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +38 -0
- package/dist/internal.js.map +1 -0
- package/dist/service-types.d.ts +156 -0
- package/dist/service-types.d.ts.map +1 -0
- package/dist/service-types.js +2 -0
- package/dist/service-types.js.map +1 -0
- package/dist/service.d.ts +66 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +110 -0
- package/dist/service.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 effect-pantry contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# @effect-pantry/storage
|
|
2
|
+
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> **Early-stage package** — APIs may change without notice. Not recommended for production use yet. Feedback and contributions welcome!
|
|
5
|
+
|
|
6
|
+
**Effect-native object storage** — wraps [files-sdk](https://files-sdk.dev) as typed Effect functions. Swap backends by swapping adapters — the API stays the same.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @effect-pantry/storage effect
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
> **`files-sdk` is a peer dependency.** Install your own version and choose which adapters to bring in (e.g., `files-sdk/s3`, `files-sdk/memory`). Targets **Effect v3** and **files-sdk ^1.6.0**.
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { memory } from "files-sdk/memory"
|
|
20
|
+
import { Storage, StorageAdapter } from "@effect-pantry/storage"
|
|
21
|
+
import { Effect, Layer } from "effect"
|
|
22
|
+
|
|
23
|
+
const layer = Storage.layer().pipe(
|
|
24
|
+
Layer.provide(Layer.succeed(StorageAdapter, memory())),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const program = Effect.gen(function* () {
|
|
28
|
+
const s = yield* Storage.Storage
|
|
29
|
+
|
|
30
|
+
// Upload
|
|
31
|
+
const { result } = yield* s.upload("data.json", JSON.stringify({ hello: "world" }))
|
|
32
|
+
yield* result;
|
|
33
|
+
|
|
34
|
+
// Download
|
|
35
|
+
const file = yield* s.download("data.json")
|
|
36
|
+
const text = yield* file.text()
|
|
37
|
+
|
|
38
|
+
return text
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
Effect.runPromise(Effect.provide(program, layer))
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API Reference
|
|
45
|
+
|
|
46
|
+
### `Storage.Storage` (Context.Tag)
|
|
47
|
+
|
|
48
|
+
The service tag. Inject with `yield* Storage.Storage`.
|
|
49
|
+
|
|
50
|
+
### `StorageAdapter` (Context.Tag)
|
|
51
|
+
|
|
52
|
+
Provide any `files-sdk` adapter:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { s3 } from "files-sdk/s3"
|
|
56
|
+
|
|
57
|
+
Layer.succeed(StorageAdapter, s3({
|
|
58
|
+
bucket: "my-bucket",
|
|
59
|
+
region: "us-east-1",
|
|
60
|
+
}))
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### `Storage.layer`
|
|
64
|
+
|
|
65
|
+
Creates a layer that requires `StorageAdapter` and provides `Storage.Storage`.
|
|
66
|
+
Accepts optional {@link MakeOptions} (e.g. `prefix`, `timeout`, `retries`).
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const layer = Storage.layer().pipe(
|
|
70
|
+
Layer.provide(Layer.succeed(StorageAdapter, memory())),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
// With options
|
|
74
|
+
const layerWithPrefix = Storage.layer({ prefix: "app-data/" }).pipe(
|
|
75
|
+
Layer.provide(Layer.succeed(StorageAdapter, memory())),
|
|
76
|
+
)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### `Storage.make`
|
|
80
|
+
|
|
81
|
+
Creates a `Storage` service. Reads the adapter from context.
|
|
82
|
+
Accepts optional {@link MakeOptions} forwarded to the underlying
|
|
83
|
+
`FilesSDK.Files` constructor.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const s = yield* Storage.make()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
With options:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const s = yield* Storage.make({ prefix: "uploads/", timeout: 30_000 })
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Or inline:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
const s = yield* Storage.make().pipe(
|
|
99
|
+
Effect.provideService(StorageAdapter, memory()),
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Methods
|
|
104
|
+
|
|
105
|
+
| Method | Signature | Returns |
|
|
106
|
+
|--------|-----------|---------|
|
|
107
|
+
| `upload(key, body, opts?)` | `Effect<{ result, progress }, never>` | Returns `{ result: Effect<UploadResult, StorageError>, progress: Stream<UploadProgress> }`. Consume progress concurrently, then `yield*` the result. |
|
|
108
|
+
| `download(key, opts?)` | `Effect<StoredFile, StorageError>` | File with lazy body accessors |
|
|
109
|
+
| `head(key, opts?)` | `Effect<StoredFile, StorageError>` | Metadata without the body |
|
|
110
|
+
| `exists(key, opts?)` | `Effect<boolean, StorageError>` | Existence check |
|
|
111
|
+
| `delete(key, opts?)` | `Effect<void, StorageError>` | Permanent removal |
|
|
112
|
+
| `copy(from, to, opts?)` | `Effect<void, StorageError>` | Server-side copy |
|
|
113
|
+
| `move(from, to, opts?)` | `Effect<void, StorageError>` | Rename / relocate |
|
|
114
|
+
| `list(opts?)` | `Effect<ListResult, StorageError>` | Cursor-paginated listing |
|
|
115
|
+
| `url(key, opts?)` | `Effect<string, StorageError>` | Public or signed download URL |
|
|
116
|
+
| `file(key)` | `FileHandle` | Key-bound handle for ergonomic single-key ops |
|
|
117
|
+
| `signedUploadUrl(key, opts)` | `Effect<SignedUpload, StorageError>` | Presigned upload URL |
|
|
118
|
+
| `hookStream(name)` | `Stream<HookEventMap[N]>` | Observable hook events |
|
|
119
|
+
|
|
120
|
+
### `FileHandle`
|
|
121
|
+
|
|
122
|
+
A key-bound storage handle. Every method operates on a pre-bound key.
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
const avatar = s.file("avatars/abc.png")
|
|
126
|
+
|
|
127
|
+
// Upload
|
|
128
|
+
yield* avatar.upload(body, { contentType: "image/png" })
|
|
129
|
+
|
|
130
|
+
// Check existence
|
|
131
|
+
if (yield* avatar.exists()) {
|
|
132
|
+
const meta = yield* avatar.head()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Copy / move
|
|
136
|
+
yield* avatar.copyTo("avatars/abc.bak.png")
|
|
137
|
+
yield* avatar.copyFrom("legacy/abc.png")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
| Method | Equivalent |
|
|
141
|
+
|--------|-----------|
|
|
142
|
+
| `upload(body, opts?)` | `s.upload(key, body, opts?)` |
|
|
143
|
+
| `download(opts?)` | `s.download(key, opts?)` |
|
|
144
|
+
| `head(opts?)` | `s.head(key, opts?)` |
|
|
145
|
+
| `exists(opts?)` | `s.exists(key, opts?)` |
|
|
146
|
+
| `delete(opts?)` | `s.delete(key, opts?)` |
|
|
147
|
+
| `url(opts?)` | `s.url(key, opts?)` |
|
|
148
|
+
| `signedUploadUrl(opts)` | `s.signedUploadUrl(key, opts)` |
|
|
149
|
+
| `copyTo(destKey, opts?)` | `s.copy(key, destKey, opts?)` |
|
|
150
|
+
| `copyFrom(srcKey, opts?)` | `s.copy(srcKey, key, opts?)` |
|
|
151
|
+
|
|
152
|
+
### Errors
|
|
153
|
+
|
|
154
|
+
All methods return typed, tagged errors:
|
|
155
|
+
|
|
156
|
+
| Error | Code | When |
|
|
157
|
+
|-------|------|------|
|
|
158
|
+
| `StorageNotFoundError` | `NotFound` | Key, bucket, or container missing |
|
|
159
|
+
| `StorageUnauthorizedError` | `Unauthorized` | Credentials missing, expired, or insufficient |
|
|
160
|
+
| `StorageConflictError` | `Conflict` | Precondition failed |
|
|
161
|
+
| `StorageProviderError` | `Provider` | Network, throttling, 5xx, timeout, cancellation |
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { StorageNotFoundError, StorageUnauthorizedError } from "@effect-pantry/storage"
|
|
165
|
+
|
|
166
|
+
yield* s.download("missing.txt").pipe(
|
|
167
|
+
Effect.catchTags({
|
|
168
|
+
StorageNotFoundError: (e) => Effect.succeed(null),
|
|
169
|
+
StorageUnauthorizedError: (e) => Effect.fail(new Error("Check credentials")),
|
|
170
|
+
StorageConflictError: (e) => Effect.fail(e),
|
|
171
|
+
StorageProviderError: (e) => Effect.fail(e),
|
|
172
|
+
}),
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
> Use `toStorageError(error)` to manually map unknown errors into typed `StorageError` values when working outside the `Storage` service.
|
|
177
|
+
|
|
178
|
+
### Hooks
|
|
179
|
+
|
|
180
|
+
Observe SDK events as Effect streams:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const s = yield* Storage.Storage
|
|
184
|
+
|
|
185
|
+
yield* s.hookStream("onAction").pipe(
|
|
186
|
+
Stream.runForEach((e) => Effect.log(`[${e.status}] ${e.type} ${e.key}`)),
|
|
187
|
+
Effect.forkScoped,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
yield* s.hookStream("onError").pipe(
|
|
191
|
+
Stream.runForEach((e) => Effect.log(`[${e.error.code}] ${e.key}`)),
|
|
192
|
+
Effect.forkScoped,
|
|
193
|
+
)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### `transfer(source, dest, opts?)`
|
|
197
|
+
|
|
198
|
+
Cross-provider migration using `FilesSDK.Files` instances. → [Full example](./examples/04-cross-provider-transfer.ts)
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { transfer } from "@effect-pantry/storage"
|
|
202
|
+
import * as FilesSDK from "files-sdk"
|
|
203
|
+
import { s3 } from "files-sdk/s3"
|
|
204
|
+
import { r2 } from "files-sdk/r2"
|
|
205
|
+
|
|
206
|
+
const from = new FilesSDK.Files({ adapter: s3({ bucket: "old", region: "us-east-1" }) })
|
|
207
|
+
const to = new FilesSDK.Files({ adapter: r2({ bucket: "new", accountId: "..." }) })
|
|
208
|
+
|
|
209
|
+
const { result, progress } = yield* transfer(from, to, { prefix: "uploads/" })
|
|
210
|
+
const { transferred, skipped, errors } = yield* result
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Examples
|
|
214
|
+
|
|
215
|
+
- [Basic usage](./examples/01-basic-usage.ts) — Full CRUD with the memory adapter
|
|
216
|
+
- [S3 adapter](./examples/02-s3-adapter.ts) — Real S3/R2 configuration patterns
|
|
217
|
+
- [Hooks & monitoring](./examples/03-hooks-and-monitoring.ts) — Logging, metrics, alerting
|
|
218
|
+
- [Cross-provider transfer](./examples/04-cross-provider-transfer.ts) — S3 → R2 migration with progress
|
|
219
|
+
- [Streams & progress](./examples/05-streams-and-progress.ts) — Upload/download progress, cancellation
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as FilesSDK from 'files-sdk';
|
|
2
|
+
import { Context } from 'effect';
|
|
3
|
+
declare const StorageAdapter_base: Context.TagClass<StorageAdapter, "@effect-pantry/storage/Adapter", FilesSDK.Adapter<unknown>>;
|
|
4
|
+
/**
|
|
5
|
+
* The `files-sdk` {@link FilesSDK.Adapter} powering the {@link Storage} service.
|
|
6
|
+
*
|
|
7
|
+
* Provide via `Layer.succeed(StorageAdapter, myAdapter)` to plug any of
|
|
8
|
+
* the 40+ adapters (memory, fs, S3, R2, Vercel Blob, …) into the service.
|
|
9
|
+
*/
|
|
10
|
+
export declare class StorageAdapter extends StorageAdapter_base {
|
|
11
|
+
}
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;;AAEjC;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,mBAGjC;CAAG"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Context } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* The `files-sdk` {@link FilesSDK.Adapter} powering the {@link Storage} service.
|
|
4
|
+
*
|
|
5
|
+
* Provide via `Layer.succeed(StorageAdapter, myAdapter)` to plug any of
|
|
6
|
+
* the 40+ adapters (memory, fs, S3, R2, Vercel Blob, …) into the service.
|
|
7
|
+
*/
|
|
8
|
+
export class StorageAdapter extends Context.Tag('@effect-pantry/storage/Adapter')() {
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,OAAO,cAAe,SAAQ,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,EAG9E;CAAG"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
declare const StorageNotFoundError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
2
|
+
readonly _tag: "StorageNotFoundError";
|
|
3
|
+
} & Readonly<A>;
|
|
4
|
+
/**
|
|
5
|
+
* The requested key, bucket, or container does not exist.
|
|
6
|
+
*
|
|
7
|
+
* @param cause - The original underlying error from the storage provider.
|
|
8
|
+
*/
|
|
9
|
+
export declare class StorageNotFoundError extends StorageNotFoundError_base<{
|
|
10
|
+
readonly message: string;
|
|
11
|
+
readonly cause: unknown;
|
|
12
|
+
}> {
|
|
13
|
+
}
|
|
14
|
+
declare const StorageUnauthorizedError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
15
|
+
readonly _tag: "StorageUnauthorizedError";
|
|
16
|
+
} & Readonly<A>;
|
|
17
|
+
/**
|
|
18
|
+
* Credentials are missing, expired, or have insufficient permissions.
|
|
19
|
+
*
|
|
20
|
+
* @param cause - The original underlying error from the storage provider.
|
|
21
|
+
*/
|
|
22
|
+
export declare class StorageUnauthorizedError extends StorageUnauthorizedError_base<{
|
|
23
|
+
readonly message: string;
|
|
24
|
+
readonly cause: unknown;
|
|
25
|
+
}> {
|
|
26
|
+
}
|
|
27
|
+
declare const StorageConflictError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
28
|
+
readonly _tag: "StorageConflictError";
|
|
29
|
+
} & Readonly<A>;
|
|
30
|
+
/**
|
|
31
|
+
* A precondition failed (e.g. a conditional write lost a race).
|
|
32
|
+
*
|
|
33
|
+
* @param cause - The original underlying error from the storage provider.
|
|
34
|
+
*/
|
|
35
|
+
export declare class StorageConflictError extends StorageConflictError_base<{
|
|
36
|
+
readonly message: string;
|
|
37
|
+
readonly cause: unknown;
|
|
38
|
+
}> {
|
|
39
|
+
}
|
|
40
|
+
declare const StorageProviderError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
41
|
+
readonly _tag: "StorageProviderError";
|
|
42
|
+
} & Readonly<A>;
|
|
43
|
+
/**
|
|
44
|
+
* A provider-level failure: network, throttling, 5xx, timeout, or cancellation.
|
|
45
|
+
*
|
|
46
|
+
* @param cause - The original underlying error from the storage provider.
|
|
47
|
+
*/
|
|
48
|
+
export declare class StorageProviderError extends StorageProviderError_base<{
|
|
49
|
+
readonly message: string;
|
|
50
|
+
readonly cause: unknown;
|
|
51
|
+
readonly aborted: boolean;
|
|
52
|
+
}> {
|
|
53
|
+
}
|
|
54
|
+
export type StorageError = StorageNotFoundError | StorageUnauthorizedError | StorageConflictError | StorageProviderError;
|
|
55
|
+
/**
|
|
56
|
+
* Map an unknown error (ideally an SDK `FilesError`) to a tagged Effect error.
|
|
57
|
+
*
|
|
58
|
+
* Fallback: unknown errors become `StorageProviderError` with `aborted: false`.
|
|
59
|
+
*/
|
|
60
|
+
export declare const toStorageError: (error: unknown) => StorageError;
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAGA;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,0BAAyC;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,8BAA6C;IACzF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,0BAAyC;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,0BAAyC;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B,CAAC;CAAG;AAEL,MAAM,MAAM,YAAY,GACpB,oBAAoB,GACpB,wBAAwB,GACxB,oBAAoB,GACpB,oBAAoB,CAAC;AAEzB;;;;GAIG;AACH,eAAO,MAAM,cAAc,UAAW,OAAO,KAAG,YAuB/C,CAAC"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Data } from 'effect';
|
|
2
|
+
import * as FilesSDK from 'files-sdk';
|
|
3
|
+
/**
|
|
4
|
+
* The requested key, bucket, or container does not exist.
|
|
5
|
+
*
|
|
6
|
+
* @param cause - The original underlying error from the storage provider.
|
|
7
|
+
*/
|
|
8
|
+
export class StorageNotFoundError extends Data.TaggedError('StorageNotFoundError') {
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Credentials are missing, expired, or have insufficient permissions.
|
|
12
|
+
*
|
|
13
|
+
* @param cause - The original underlying error from the storage provider.
|
|
14
|
+
*/
|
|
15
|
+
export class StorageUnauthorizedError extends Data.TaggedError('StorageUnauthorizedError') {
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A precondition failed (e.g. a conditional write lost a race).
|
|
19
|
+
*
|
|
20
|
+
* @param cause - The original underlying error from the storage provider.
|
|
21
|
+
*/
|
|
22
|
+
export class StorageConflictError extends Data.TaggedError('StorageConflictError') {
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A provider-level failure: network, throttling, 5xx, timeout, or cancellation.
|
|
26
|
+
*
|
|
27
|
+
* @param cause - The original underlying error from the storage provider.
|
|
28
|
+
*/
|
|
29
|
+
export class StorageProviderError extends Data.TaggedError('StorageProviderError') {
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Map an unknown error (ideally an SDK `FilesError`) to a tagged Effect error.
|
|
33
|
+
*
|
|
34
|
+
* Fallback: unknown errors become `StorageProviderError` with `aborted: false`.
|
|
35
|
+
*/
|
|
36
|
+
export const toStorageError = (error) => {
|
|
37
|
+
if (error instanceof FilesSDK.FilesError) {
|
|
38
|
+
const message = error.message;
|
|
39
|
+
const cause = error.cause;
|
|
40
|
+
switch (error.code) {
|
|
41
|
+
case 'NotFound':
|
|
42
|
+
return new StorageNotFoundError({ message, cause });
|
|
43
|
+
case 'Unauthorized':
|
|
44
|
+
return new StorageUnauthorizedError({ message, cause });
|
|
45
|
+
case 'Conflict':
|
|
46
|
+
return new StorageConflictError({ message, cause });
|
|
47
|
+
default:
|
|
48
|
+
// files-sdk v1.6 has exactly four codes ("NotFound", "Unauthorized",
|
|
49
|
+
// "Conflict", "Provider"). If this branch triggers, the SDK added a new
|
|
50
|
+
// error code — file an issue to add a dedicated tagged error.
|
|
51
|
+
return new StorageProviderError({ message, cause, aborted: error.aborted });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return new StorageProviderError({
|
|
55
|
+
message: error instanceof Error ? error.message : String(error),
|
|
56
|
+
cause: error,
|
|
57
|
+
aborted: false,
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,KAAK,QAAQ,MAAM,WAAW,CAAC;AAEtC;;;;GAIG;AACH,MAAM,OAAO,oBAAqB,SAAQ,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAG/E;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,wBAAyB,SAAQ,IAAI,CAAC,WAAW,CAAC,0BAA0B,CAGvF;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,oBAAqB,SAAQ,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAG/E;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,oBAAqB,SAAQ,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAI/E;CAAG;AAQL;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAc,EAAgB,EAAE;IAC7D,IAAI,KAAK,YAAY,QAAQ,CAAC,UAAU,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,UAAU;gBACb,OAAO,IAAI,oBAAoB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACtD,KAAK,cAAc;gBACjB,OAAO,IAAI,wBAAwB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1D,KAAK,UAAU;gBACb,OAAO,IAAI,oBAAoB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACtD;gBACE,qEAAqE;gBACrE,wEAAwE;gBACxE,8DAA8D;gBAC9D,OAAO,IAAI,oBAAoB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;IACD,OAAO,IAAI,oBAAoB,CAAC;QAC9B,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAC/D,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as FilesSDK from 'files-sdk';
|
|
2
|
+
/** Options for {@link transfer}, with `signal` and `onProgress` managed internally. */
|
|
3
|
+
export type TransferOptions = Omit<FilesSDK.TransferOptions, 'signal' | 'onProgress'>;
|
|
4
|
+
/**
|
|
5
|
+
* Cross-provider migration: walks every object the `source` exposes and
|
|
6
|
+
* streams each one straight to `dest`, whatever the backends are.
|
|
7
|
+
*
|
|
8
|
+
* Returns an `{ result, progress }` pair where `result` is a **deferred
|
|
9
|
+
* Effect** — the transfer does not start until you `yield*` it, and
|
|
10
|
+
* `progress` is a {@link Stream} of {@link FilesSDK.TransferProgress}
|
|
11
|
+
* events emitted while the transfer runs. Consume the stream concurrently
|
|
12
|
+
* with the result:
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { s3 } from "files-sdk/s3";
|
|
17
|
+
* import { r2 } from "files-sdk/r2";
|
|
18
|
+
* import * as FilesSDK from "files-sdk";
|
|
19
|
+
* import { transfer } from "@effect-pantry/storage";
|
|
20
|
+
*
|
|
21
|
+
* const from = new FilesSDK.Files({ adapter: s3({ bucket: "old" }) });
|
|
22
|
+
* const to = new FilesSDK.Files({ adapter: r2({ bucket: "new", ... }) });
|
|
23
|
+
*
|
|
24
|
+
* // With progress — fork the stream consumer before awaiting the result
|
|
25
|
+
* const { result, progress } = yield* transfer(from, to, { prefix: "uploads/" });
|
|
26
|
+
* yield* Stream.runForEach(progress, (p) =>
|
|
27
|
+
* Effect.log(`${p.status} ${p.key} (${p.done}/${p.total})`)
|
|
28
|
+
* ).pipe(Effect.forkScoped);
|
|
29
|
+
* const { transferred, skipped, errors } = yield* result;
|
|
30
|
+
*
|
|
31
|
+
* // Without progress — just yield the result
|
|
32
|
+
* const { transferred, skipped, errors } = yield* transfer(from, to).pipe(
|
|
33
|
+
* Effect.flatMap(({ result }) => result)
|
|
34
|
+
* );
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* Both arguments are full {@link FilesSDK.Files} instances, not raw
|
|
38
|
+
* adapters, so each leg honors its own `prefix`, retries, timeouts, and
|
|
39
|
+
* hooks.
|
|
40
|
+
*
|
|
41
|
+
* @returns An {@link Effect} resolving to `{ result, progress }` where
|
|
42
|
+
* `result` is a deferred effect that yields a
|
|
43
|
+
* {@link FilesSDK.TransferResult} when executed, and `progress` is a
|
|
44
|
+
* stream of {@link FilesSDK.TransferProgress} events.
|
|
45
|
+
*/
|
|
46
|
+
export declare const transfer: (source: FilesSDK.Files, dest: FilesSDK.Files, opts?: TransferOptions) => import("effect/Effect").Effect<{
|
|
47
|
+
result: import("effect/Effect").Effect<FilesSDK.TransferResult, import("../errors.ts").StorageError, never>;
|
|
48
|
+
progress: import("effect/Stream").Stream<FilesSDK.TransferProgress, never, never>;
|
|
49
|
+
}, never, never>;
|
|
50
|
+
//# sourceMappingURL=transfer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../../src/features/transfer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,WAAW,CAAC;AAGtC,uFAAuF;AACvF,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,QAAQ,GAAG,YAAY,CAAC,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,QAAQ,WAAY,QAAQ,CAAC,KAAK,QAAQ,QAAQ,CAAC,KAAK,SAAS,eAAe;;;gBAG1F,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as FilesSDK from 'files-sdk';
|
|
2
|
+
import { bridgeProgress } from '../internal.js';
|
|
3
|
+
/**
|
|
4
|
+
* Cross-provider migration: walks every object the `source` exposes and
|
|
5
|
+
* streams each one straight to `dest`, whatever the backends are.
|
|
6
|
+
*
|
|
7
|
+
* Returns an `{ result, progress }` pair where `result` is a **deferred
|
|
8
|
+
* Effect** — the transfer does not start until you `yield*` it, and
|
|
9
|
+
* `progress` is a {@link Stream} of {@link FilesSDK.TransferProgress}
|
|
10
|
+
* events emitted while the transfer runs. Consume the stream concurrently
|
|
11
|
+
* with the result:
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { s3 } from "files-sdk/s3";
|
|
16
|
+
* import { r2 } from "files-sdk/r2";
|
|
17
|
+
* import * as FilesSDK from "files-sdk";
|
|
18
|
+
* import { transfer } from "@effect-pantry/storage";
|
|
19
|
+
*
|
|
20
|
+
* const from = new FilesSDK.Files({ adapter: s3({ bucket: "old" }) });
|
|
21
|
+
* const to = new FilesSDK.Files({ adapter: r2({ bucket: "new", ... }) });
|
|
22
|
+
*
|
|
23
|
+
* // With progress — fork the stream consumer before awaiting the result
|
|
24
|
+
* const { result, progress } = yield* transfer(from, to, { prefix: "uploads/" });
|
|
25
|
+
* yield* Stream.runForEach(progress, (p) =>
|
|
26
|
+
* Effect.log(`${p.status} ${p.key} (${p.done}/${p.total})`)
|
|
27
|
+
* ).pipe(Effect.forkScoped);
|
|
28
|
+
* const { transferred, skipped, errors } = yield* result;
|
|
29
|
+
*
|
|
30
|
+
* // Without progress — just yield the result
|
|
31
|
+
* const { transferred, skipped, errors } = yield* transfer(from, to).pipe(
|
|
32
|
+
* Effect.flatMap(({ result }) => result)
|
|
33
|
+
* );
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Both arguments are full {@link FilesSDK.Files} instances, not raw
|
|
37
|
+
* adapters, so each leg honors its own `prefix`, retries, timeouts, and
|
|
38
|
+
* hooks.
|
|
39
|
+
*
|
|
40
|
+
* @returns An {@link Effect} resolving to `{ result, progress }` where
|
|
41
|
+
* `result` is a deferred effect that yields a
|
|
42
|
+
* {@link FilesSDK.TransferResult} when executed, and `progress` is a
|
|
43
|
+
* stream of {@link FilesSDK.TransferProgress} events.
|
|
44
|
+
*/
|
|
45
|
+
export const transfer = (source, dest, opts) => bridgeProgress((signal, onProgress) => FilesSDK.transfer(source, dest, { ...opts, signal, onProgress }));
|
|
46
|
+
//# sourceMappingURL=transfer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../src/features/transfer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,MAAsB,EAAE,IAAoB,EAAE,IAAsB,EAAE,EAAE,CAC/F,cAAc,CAAqD,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,CACxF,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CACjE,CAAC"}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FilesActionEvent, FilesErrorEvent, FilesRetryEvent } from 'files-sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Discriminated union of every hook event emitted through the PubSub.
|
|
4
|
+
*
|
|
5
|
+
* Use {@link Storage.hookStream} to subscribe to a single kind of event;
|
|
6
|
+
* the stream filters on `_tag` automatically.
|
|
7
|
+
*/
|
|
8
|
+
export type HookEvent = {
|
|
9
|
+
readonly _tag: 'onAction';
|
|
10
|
+
readonly event: FilesActionEvent;
|
|
11
|
+
} | {
|
|
12
|
+
readonly _tag: 'onError';
|
|
13
|
+
readonly event: FilesErrorEvent;
|
|
14
|
+
} | {
|
|
15
|
+
readonly _tag: 'onRetry';
|
|
16
|
+
readonly event: FilesRetryEvent;
|
|
17
|
+
};
|
|
18
|
+
/** Maps each constructor hook name to its event payload type. */
|
|
19
|
+
export interface HookEventMap {
|
|
20
|
+
readonly onAction: FilesActionEvent;
|
|
21
|
+
readonly onError: FilesErrorEvent;
|
|
22
|
+
readonly onRetry: FilesRetryEvent;
|
|
23
|
+
}
|
|
24
|
+
/** The three constructor hook names supported by files-sdk. */
|
|
25
|
+
export type HookName = keyof HookEventMap;
|
|
26
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEpF;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GAC/D;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAAE,GAC7D;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAAE,CAAC;AAElE,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;CACnC;AAED,+DAA+D;AAC/D,MAAM,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC"}
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @effect-pantry/storage — Effect-native storage wrapping files-sdk.
|
|
3
|
+
*
|
|
4
|
+
* Provides a {@link Storage} context tag backed by any files-sdk adapter
|
|
5
|
+
* (memory, fs, S3, R2, Vercel Blob, and 35+ more), with typed errors and
|
|
6
|
+
* automatic cancellation bridging.
|
|
7
|
+
*
|
|
8
|
+
* **⚠️ Early-stage package** — APIs may change without notice.
|
|
9
|
+
* Not recommended for production use yet.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
export * as Storage from './service.js';
|
|
14
|
+
export * as StorageAdapter from './adapter.js';
|
|
15
|
+
export type { FileHandle, MakeOptions, UploadOptions } from './service-types.js';
|
|
16
|
+
export { transfer } from './features/transfer.js';
|
|
17
|
+
export type { TransferOptions } from './features/transfer.js';
|
|
18
|
+
export { StorageNotFoundError, StorageUnauthorizedError, StorageConflictError, StorageProviderError, toStorageError, } from './errors.js';
|
|
19
|
+
export type { StorageError } from './errors.js';
|
|
20
|
+
export type { HookEvent, HookEventMap, HookName } from './hooks.js';
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,cAAc,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @effect-pantry/storage — Effect-native storage wrapping files-sdk.
|
|
3
|
+
*
|
|
4
|
+
* Provides a {@link Storage} context tag backed by any files-sdk adapter
|
|
5
|
+
* (memory, fs, S3, R2, Vercel Blob, and 35+ more), with typed errors and
|
|
6
|
+
* automatic cancellation bridging.
|
|
7
|
+
*
|
|
8
|
+
* **⚠️ Early-stage package** — APIs may change without notice.
|
|
9
|
+
* Not recommended for production use yet.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
export * as Storage from './service.js';
|
|
14
|
+
export * as StorageAdapter from './adapter.js';
|
|
15
|
+
export { transfer } from './features/transfer.js';
|
|
16
|
+
export { StorageNotFoundError, StorageUnauthorizedError, StorageConflictError, StorageProviderError, toStorageError, } from './errors.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,cAAc,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGlD,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,GACf,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Effect, Stream } from 'effect';
|
|
2
|
+
import { type StorageError } from './errors.js';
|
|
3
|
+
/** @internal */
|
|
4
|
+
export declare const wrapSDKCall: <A>(fn: (signal: AbortSignal) => Promise<A>) => Effect.Effect<A, StorageError, never>;
|
|
5
|
+
/** @internal
|
|
6
|
+
*
|
|
7
|
+
* Bridges a callback-style progress operation to an Effect returning both
|
|
8
|
+
* a deferred result and a progress {@link Stream}. The caller consumes
|
|
9
|
+
* the stream concurrently with the result — e.g. fork the stream consumer
|
|
10
|
+
* then `yield*` the result.
|
|
11
|
+
*
|
|
12
|
+
* Progress callbacks are pushed synchronously via {@link Queue.unsafeOffer}
|
|
13
|
+
* so no extra fibers are spawned per callback. The queue is shut down when
|
|
14
|
+
* the result effect completes, fails, or is interrupted (via `ensuring`).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const { result, progress } = yield* bridgeProgress((signal, onProgress) =>
|
|
19
|
+
* sdk.upload(key, body, { signal, onProgress })
|
|
20
|
+
* );
|
|
21
|
+
*
|
|
22
|
+
* // Fork progress consumption
|
|
23
|
+
* yield* Stream.runForEach(progress, (p) => Effect.log(p)).pipe(Effect.forkScoped);
|
|
24
|
+
* // Await result
|
|
25
|
+
* const uploadResult = yield* result;
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const bridgeProgress: <A, P>(fn: (signal: AbortSignal, onProgress: (progress: P) => void) => Promise<A>) => Effect.Effect<{
|
|
29
|
+
result: Effect.Effect<A, StorageError>;
|
|
30
|
+
progress: Stream.Stream<P>;
|
|
31
|
+
}, never>;
|
|
32
|
+
//# sourceMappingURL=internal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhE,gBAAgB;AAChB,eAAO,MAAM,WAAW,GAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,0CACf,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,MAC7B,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KACzE,MAAM,CAAC,MAAM,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;CAAE,EAAE,KAAK,CAa1F,CAAC"}
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Effect, Queue, Stream } from 'effect';
|
|
2
|
+
import { toStorageError } from './errors.js';
|
|
3
|
+
/** @internal */
|
|
4
|
+
export const wrapSDKCall = (fn) => Effect.tryPromise({ try: fn, catch: toStorageError });
|
|
5
|
+
/** @internal
|
|
6
|
+
*
|
|
7
|
+
* Bridges a callback-style progress operation to an Effect returning both
|
|
8
|
+
* a deferred result and a progress {@link Stream}. The caller consumes
|
|
9
|
+
* the stream concurrently with the result — e.g. fork the stream consumer
|
|
10
|
+
* then `yield*` the result.
|
|
11
|
+
*
|
|
12
|
+
* Progress callbacks are pushed synchronously via {@link Queue.unsafeOffer}
|
|
13
|
+
* so no extra fibers are spawned per callback. The queue is shut down when
|
|
14
|
+
* the result effect completes, fails, or is interrupted (via `ensuring`).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const { result, progress } = yield* bridgeProgress((signal, onProgress) =>
|
|
19
|
+
* sdk.upload(key, body, { signal, onProgress })
|
|
20
|
+
* );
|
|
21
|
+
*
|
|
22
|
+
* // Fork progress consumption
|
|
23
|
+
* yield* Stream.runForEach(progress, (p) => Effect.log(p)).pipe(Effect.forkScoped);
|
|
24
|
+
* // Await result
|
|
25
|
+
* const uploadResult = yield* result;
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export const bridgeProgress = (fn) => Effect.gen(function* () {
|
|
29
|
+
const queue = yield* Queue.unbounded();
|
|
30
|
+
const result = Effect.tryPromise({
|
|
31
|
+
try: (signal) => fn(signal, (progress) => {
|
|
32
|
+
queue.unsafeOffer(progress);
|
|
33
|
+
}),
|
|
34
|
+
catch: toStorageError,
|
|
35
|
+
}).pipe(Effect.ensuring(queue.shutdown));
|
|
36
|
+
return { result, progress: Stream.fromQueue(queue) };
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=internal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAqB,MAAM,aAAa,CAAC;AAEhE,gBAAgB;AAChB,MAAM,CAAC,MAAM,WAAW,GAAG,CAAI,EAAuC,EAAE,EAAE,CACxE,MAAM,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,EAA0E,EACoB,EAAE,CAChG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,SAAS,EAAK,CAAC;IAE1C,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;QAC/B,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CACd,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE;YACtB,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC,CAAC;QACJ,KAAK,EAAE,cAAc;KACtB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEzC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;AACvD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import * as FilesSDK from 'files-sdk';
|
|
2
|
+
import { Effect, Stream } from 'effect';
|
|
3
|
+
import type { StorageError } from './errors.js';
|
|
4
|
+
import type { HookEventMap, HookName } from './hooks.js';
|
|
5
|
+
/** Options for {@link Storage.upload}, with `signal` and `onProgress` managed internally. */
|
|
6
|
+
export type UploadOptions = Omit<FilesSDK.UploadOptions, 'signal' | 'onProgress'>;
|
|
7
|
+
/**
|
|
8
|
+
* A key-bound handle to a single storage object.
|
|
9
|
+
*
|
|
10
|
+
* Returned by {@link StorageInterface.file} — every method operates on the
|
|
11
|
+
* pre-bound key so callers don't need to pass it on each call. Key
|
|
12
|
+
* validation happens eagerly at construction; all I/O defers to the
|
|
13
|
+
* underlying {@link StorageInterface} methods.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const avatar = svc.file("avatars/abc.png");
|
|
18
|
+
* yield* avatar.upload(body, { contentType: "image/png" });
|
|
19
|
+
* if (yield* avatar.exists()) {
|
|
20
|
+
* const meta = yield* avatar.head();
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export interface FileHandle {
|
|
25
|
+
/** The key this handle is bound to. */
|
|
26
|
+
readonly key: string;
|
|
27
|
+
readonly upload: (body: FilesSDK.Body, opts?: UploadOptions) => Effect.Effect<{
|
|
28
|
+
result: Effect.Effect<FilesSDK.UploadResult, StorageError>;
|
|
29
|
+
progress: Stream.Stream<FilesSDK.UploadProgress>;
|
|
30
|
+
}, never>;
|
|
31
|
+
readonly download: (opts?: FilesSDK.DownloadOptions) => Effect.Effect<FilesSDK.StoredFile, StorageError>;
|
|
32
|
+
readonly head: (opts?: FilesSDK.OperationOptions) => Effect.Effect<FilesSDK.StoredFile, StorageError>;
|
|
33
|
+
readonly exists: (opts?: FilesSDK.OperationOptions) => Effect.Effect<boolean, StorageError>;
|
|
34
|
+
readonly delete: (opts?: FilesSDK.OperationOptions) => Effect.Effect<void, StorageError>;
|
|
35
|
+
readonly url: (opts?: FilesSDK.UrlOptions) => Effect.Effect<string, StorageError>;
|
|
36
|
+
readonly signedUploadUrl: (opts: FilesSDK.SignUploadOptions) => Effect.Effect<FilesSDK.SignedUpload, StorageError>;
|
|
37
|
+
/**
|
|
38
|
+
* Copy the handle's object to a new key. The handle is the source.
|
|
39
|
+
* Shortcut for `Storage.copy(handle.key, destKey, opts)`.
|
|
40
|
+
*/
|
|
41
|
+
readonly copyTo: (destKey: string, opts?: FilesSDK.OperationOptions) => Effect.Effect<void, StorageError>;
|
|
42
|
+
/**
|
|
43
|
+
* Copy from a source key into the handle's key. The handle is the destination.
|
|
44
|
+
* Shortcut for `Storage.copy(srcKey, handle.key, opts)`.
|
|
45
|
+
*/
|
|
46
|
+
readonly copyFrom: (srcKey: string, opts?: FilesSDK.OperationOptions) => Effect.Effect<void, StorageError>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Options passed to {@link Storage.make} to configure the underlying
|
|
50
|
+
* {@link FilesSDK.Files} instance.
|
|
51
|
+
*
|
|
52
|
+
* `adapter` and `hooks` are excluded — the adapter comes from the
|
|
53
|
+
* {@link StorageAdapter} context tag and hooks are wired internally to
|
|
54
|
+
* the PubSub event stream.
|
|
55
|
+
*/
|
|
56
|
+
export type MakeOptions = Omit<FilesSDK.FilesOptions<FilesSDK.Adapter>, 'adapter' | 'hooks'>;
|
|
57
|
+
/**
|
|
58
|
+
* Type interface for the Storage service — separated from the
|
|
59
|
+
* implementation to keep each file focused and under the module-size
|
|
60
|
+
* guideline.
|
|
61
|
+
*
|
|
62
|
+
* The runtime {@link Context.Tag} lives in `service.ts` alongside
|
|
63
|
+
* {@link make} and {@link layer}.
|
|
64
|
+
*/
|
|
65
|
+
export interface StorageInterface {
|
|
66
|
+
readonly upload: (
|
|
67
|
+
/** Object key (path) to store the object at. */
|
|
68
|
+
key: string,
|
|
69
|
+
/** Body content to upload (string, Buffer, ReadableStream, Blob, File, etc.). */
|
|
70
|
+
body: FilesSDK.Body,
|
|
71
|
+
/** Optional upload configuration (contentType, multipart threshold, etc.). */
|
|
72
|
+
opts?: UploadOptions) => Effect.Effect<{
|
|
73
|
+
result: Effect.Effect<FilesSDK.UploadResult, StorageError>;
|
|
74
|
+
progress: Stream.Stream<FilesSDK.UploadProgress>;
|
|
75
|
+
}, never>;
|
|
76
|
+
readonly download: (
|
|
77
|
+
/** Object key (path) to retrieve. */
|
|
78
|
+
key: string,
|
|
79
|
+
/** Optional download configuration (range, accept header, stream mode, etc.). */
|
|
80
|
+
opts?: FilesSDK.DownloadOptions) => Effect.Effect<FilesSDK.StoredFile, StorageError>;
|
|
81
|
+
readonly head: (
|
|
82
|
+
/** Object key (path) to inspect without downloading. */
|
|
83
|
+
key: string, opts?: FilesSDK.OperationOptions) => Effect.Effect<FilesSDK.StoredFile, StorageError>;
|
|
84
|
+
readonly exists: (
|
|
85
|
+
/** Object key (path) to check for existence. */
|
|
86
|
+
key: string, opts?: FilesSDK.OperationOptions) => Effect.Effect<boolean, StorageError>;
|
|
87
|
+
readonly delete: (
|
|
88
|
+
/** Object key (path) to permanently remove. */
|
|
89
|
+
key: string, opts?: FilesSDK.OperationOptions) => Effect.Effect<void, StorageError>;
|
|
90
|
+
readonly copy: (
|
|
91
|
+
/** Source key (path) to copy from. */
|
|
92
|
+
from: string,
|
|
93
|
+
/** Destination key (path) to copy to. */
|
|
94
|
+
to: string, opts?: FilesSDK.OperationOptions) => Effect.Effect<void, StorageError>;
|
|
95
|
+
readonly move: (
|
|
96
|
+
/** Source key (path) to move from. */
|
|
97
|
+
from: string,
|
|
98
|
+
/** Destination key (path) to move to. */
|
|
99
|
+
to: string, opts?: FilesSDK.OperationOptions) => Effect.Effect<void, StorageError>;
|
|
100
|
+
readonly list: (
|
|
101
|
+
/** Optional listing configuration (prefix, maxKeys, cursor, recursive, etc.). */
|
|
102
|
+
opts?: FilesSDK.ListOptions) => Effect.Effect<FilesSDK.ListResult, StorageError>;
|
|
103
|
+
readonly url: (
|
|
104
|
+
/** Object key (path) to generate a public/download URL for. */
|
|
105
|
+
key: string,
|
|
106
|
+
/** Optional URL configuration (expiresIn, etc.). */
|
|
107
|
+
opts?: FilesSDK.UrlOptions) => Effect.Effect<string, StorageError>;
|
|
108
|
+
readonly signedUploadUrl: (
|
|
109
|
+
/** Object key (path) to generate a signed upload URL for. */
|
|
110
|
+
key: string,
|
|
111
|
+
/** Required by the underlying SDK — `expiresIn` is always required;
|
|
112
|
+
* `contentType` and `maxSize` may also be needed depending on the adapter. */
|
|
113
|
+
opts: FilesSDK.SignUploadOptions) => Effect.Effect<FilesSDK.SignedUpload, StorageError>;
|
|
114
|
+
/**
|
|
115
|
+
* Create a {@link FileHandle} bound to a single key.
|
|
116
|
+
*
|
|
117
|
+
* The key is validated eagerly — an empty key throws synchronously.
|
|
118
|
+
* Every method on the handle delegates to the corresponding
|
|
119
|
+
* {@link StorageInterface} method, so adapters implement nothing extra.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* const avatar = svc.file("avatars/abc.png");
|
|
124
|
+
* yield* avatar.upload(body, { contentType: "image/png" });
|
|
125
|
+
* yield* avatar.copyTo("avatars/abc.bak.png");
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
readonly file: (key: string) => FileHandle;
|
|
129
|
+
/**
|
|
130
|
+
* Subscribe to constructor hook events as an Effect {@link Stream}.
|
|
131
|
+
*
|
|
132
|
+
* Events are emitted from an unbounded internal {@link PubSub} — every
|
|
133
|
+
* storage operation pushes into it, and the stream filters to the chosen
|
|
134
|
+
* hook kind. Multiple consumers can subscribe independently; each gets
|
|
135
|
+
* its own subscription.
|
|
136
|
+
*
|
|
137
|
+
* The stream ends when the storage layer's scope is released.
|
|
138
|
+
*
|
|
139
|
+
* **Note:** The internal PubSub is unbounded. If consumers never drain
|
|
140
|
+
* the stream, memory grows without limit. Always run the stream consumer
|
|
141
|
+
* or cancel it before producing a large volume of events.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* const svc = yield* Storage;
|
|
146
|
+
*
|
|
147
|
+
* // Stream of FilesActionEvent
|
|
148
|
+
* yield* svc.hookStream("onAction").pipe(
|
|
149
|
+
* Stream.runForEach((e) => Effect.log(`Storage ${e.type}: ${e.status}`)),
|
|
150
|
+
* Effect.forkScoped,
|
|
151
|
+
* );
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
readonly hookStream: <N extends HookName>(name: N) => Stream.Stream<HookEventMap[N], never>;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=service-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-types.d.ts","sourceRoot":"","sources":["../src/service-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-types.js","sourceRoot":"","sources":["../src/service-types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from 'effect';
|
|
2
|
+
import { StorageAdapter } from './adapter.js';
|
|
3
|
+
import type { MakeOptions, StorageInterface } from './service-types.js';
|
|
4
|
+
declare const Storage_base: Context.TagClass<Storage, "@effect-pantry/storage/Storage", StorageInterface>;
|
|
5
|
+
/**
|
|
6
|
+
* Effect-native storage service wrapping a `files-sdk` {@link FilesSDK.Files} instance.
|
|
7
|
+
*
|
|
8
|
+
* Depends on {@link StorageAdapter} from the Effect context to create the underlying
|
|
9
|
+
* SDK client. Every method bridges the SDK's promise-based API to Effect,
|
|
10
|
+
* carrying typed errors via {@link StorageError} and automatically handling
|
|
11
|
+
* cancellation through Effect's interruption model.
|
|
12
|
+
*
|
|
13
|
+
* **Note:** Bulk operations (`uploadAll`, `downloadAll`, etc.) are not yet
|
|
14
|
+
* exposed on this service. Use {@link FilesSDK.Files} directly or combine
|
|
15
|
+
* individual calls with {@link Effect.all} for concurrent operations.
|
|
16
|
+
*
|
|
17
|
+
* Provide via {@link Storage.layer}.
|
|
18
|
+
*/
|
|
19
|
+
export declare class Storage extends Storage_base {
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a {@link Storage} service from the adapter found in the Effect context.
|
|
23
|
+
*
|
|
24
|
+
* Accepts optional {@link MakeOptions} to configure the underlying
|
|
25
|
+
* {@link FilesSDK.Files} instance (e.g. `prefix`, `timeout`, `retries`).
|
|
26
|
+
* The `adapter` and `hooks` options are managed internally and cannot be
|
|
27
|
+
* overridden.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const svc = yield* Storage.make();
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example With options
|
|
35
|
+
* ```ts
|
|
36
|
+
* const svc = yield* Storage.make({ prefix: "uploads/", timeout: 30_000 });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare const make: (options?: MakeOptions) => Effect.Effect<StorageInterface, never, StorageAdapter>;
|
|
40
|
+
/**
|
|
41
|
+
* Create an {@link Effect.Layer} that requires {@link StorageAdapter} and provides
|
|
42
|
+
* {@link Storage}. The consumer supplies the adapter layer.
|
|
43
|
+
*
|
|
44
|
+
* Accepts optional {@link MakeOptions} forwarded to
|
|
45
|
+
* {@link make}.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { memory } from "files-sdk/memory";
|
|
50
|
+
* import { Storage, StorageAdapter } from "@effect-pantry/storage";
|
|
51
|
+
*
|
|
52
|
+
* const layer = Storage.layer().pipe(
|
|
53
|
+
* Layer.provide(Layer.succeed(StorageAdapter, memory())),
|
|
54
|
+
* );
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @example With options
|
|
58
|
+
* ```ts
|
|
59
|
+
* const layer = Storage.layer({ prefix: "app-data/" }).pipe(
|
|
60
|
+
* Layer.provide(Layer.succeed(StorageAdapter, memory())),
|
|
61
|
+
* );
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare const layer: (options?: MakeOptions) => Layer.Layer<Storage, never, StorageAdapter>;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAwB,MAAM,QAAQ,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,KAAK,EAAc,WAAW,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;;AAEpF;;;;;;;;;;;;;GAaG;AACH,qBAAa,OAAQ,SAAQ,YAG1B;CAAG;AAEN;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,IAAI,aAAc,WAAW,2DAyEtC,CAAC;AAEL;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,KAAK,aAAc,WAAW,gDAAyC,CAAC"}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as FilesSDK from 'files-sdk';
|
|
2
|
+
import { Context, Effect, Layer, pipe, PubSub, Stream } from 'effect';
|
|
3
|
+
import { StorageAdapter } from './adapter.js';
|
|
4
|
+
import { bridgeProgress, wrapSDKCall } from './internal.js';
|
|
5
|
+
/**
|
|
6
|
+
* Effect-native storage service wrapping a `files-sdk` {@link FilesSDK.Files} instance.
|
|
7
|
+
*
|
|
8
|
+
* Depends on {@link StorageAdapter} from the Effect context to create the underlying
|
|
9
|
+
* SDK client. Every method bridges the SDK's promise-based API to Effect,
|
|
10
|
+
* carrying typed errors via {@link StorageError} and automatically handling
|
|
11
|
+
* cancellation through Effect's interruption model.
|
|
12
|
+
*
|
|
13
|
+
* **Note:** Bulk operations (`uploadAll`, `downloadAll`, etc.) are not yet
|
|
14
|
+
* exposed on this service. Use {@link FilesSDK.Files} directly or combine
|
|
15
|
+
* individual calls with {@link Effect.all} for concurrent operations.
|
|
16
|
+
*
|
|
17
|
+
* Provide via {@link Storage.layer}.
|
|
18
|
+
*/
|
|
19
|
+
export class Storage extends Context.Tag('@effect-pantry/storage/Storage')() {
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a {@link Storage} service from the adapter found in the Effect context.
|
|
23
|
+
*
|
|
24
|
+
* Accepts optional {@link MakeOptions} to configure the underlying
|
|
25
|
+
* {@link FilesSDK.Files} instance (e.g. `prefix`, `timeout`, `retries`).
|
|
26
|
+
* The `adapter` and `hooks` options are managed internally and cannot be
|
|
27
|
+
* overridden.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const svc = yield* Storage.make();
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example With options
|
|
35
|
+
* ```ts
|
|
36
|
+
* const svc = yield* Storage.make({ prefix: "uploads/", timeout: 30_000 });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export const make = (options) => Effect.gen(function* () {
|
|
40
|
+
yield* Effect.logWarning('@effect-pantry/storage is in early development — APIs may change. Not recommended for production yet.');
|
|
41
|
+
const adapter = yield* StorageAdapter;
|
|
42
|
+
const pubsub = yield* PubSub.unbounded();
|
|
43
|
+
const offer = (event) => {
|
|
44
|
+
const ok = pubsub.unsafeOffer(event);
|
|
45
|
+
if (!ok) {
|
|
46
|
+
console.warn('[Storage] hook event dropped: PubSub not accepting');
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const files = new FilesSDK.Files({
|
|
50
|
+
...options,
|
|
51
|
+
adapter,
|
|
52
|
+
hooks: {
|
|
53
|
+
onAction: (event) => offer({ _tag: 'onAction', event }),
|
|
54
|
+
onError: (event) => offer({ _tag: 'onError', event }),
|
|
55
|
+
onRetry: (event) => offer({ _tag: 'onRetry', event }),
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const svc = {
|
|
59
|
+
upload: (key, body, opts) => bridgeProgress((signal, onProgress) => files.upload(key, body, { ...opts, signal, onProgress })),
|
|
60
|
+
download: (key, opts) => wrapSDKCall((signal) => files.download(key, { ...opts, signal })),
|
|
61
|
+
head: (key, opts) => wrapSDKCall((signal) => files.head(key, { ...opts, signal })),
|
|
62
|
+
exists: (key, opts) => wrapSDKCall((signal) => files.exists(key, { ...opts, signal })),
|
|
63
|
+
delete: (key, opts) => wrapSDKCall((signal) => files.delete(key, { ...opts, signal })),
|
|
64
|
+
copy: (from, to, opts) => wrapSDKCall((signal) => files.copy(from, to, { ...opts, signal })),
|
|
65
|
+
move: (from, to, opts) => wrapSDKCall((signal) => files.move(from, to, { ...opts, signal })),
|
|
66
|
+
list: (opts) => wrapSDKCall((signal) => files.list({ ...opts, signal })),
|
|
67
|
+
url: (key, opts) => wrapSDKCall((signal) => files.url(key, { ...opts, signal })),
|
|
68
|
+
signedUploadUrl: (key, opts) => wrapSDKCall((signal) => files.signedUploadUrl(key, { ...opts, signal })),
|
|
69
|
+
hookStream: (name) => pipe(Stream.fromPubSub(pubsub), Stream.filter((e) => e._tag === name), Stream.map((e) => e.event)),
|
|
70
|
+
file: (key) => ({
|
|
71
|
+
key,
|
|
72
|
+
upload: (body, opts) => svc.upload(key, body, opts),
|
|
73
|
+
download: (opts) => svc.download(key, opts),
|
|
74
|
+
head: (opts) => svc.head(key, opts),
|
|
75
|
+
exists: (opts) => svc.exists(key, opts),
|
|
76
|
+
delete: (opts) => svc.delete(key, opts),
|
|
77
|
+
url: (opts) => svc.url(key, opts),
|
|
78
|
+
signedUploadUrl: (opts) => svc.signedUploadUrl(key, opts),
|
|
79
|
+
copyTo: (destKey, opts) => svc.copy(key, destKey, opts),
|
|
80
|
+
copyFrom: (srcKey, opts) => svc.copy(srcKey, key, opts),
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
return Storage.of(svc);
|
|
84
|
+
});
|
|
85
|
+
/**
|
|
86
|
+
* Create an {@link Effect.Layer} that requires {@link StorageAdapter} and provides
|
|
87
|
+
* {@link Storage}. The consumer supplies the adapter layer.
|
|
88
|
+
*
|
|
89
|
+
* Accepts optional {@link MakeOptions} forwarded to
|
|
90
|
+
* {@link make}.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* import { memory } from "files-sdk/memory";
|
|
95
|
+
* import { Storage, StorageAdapter } from "@effect-pantry/storage";
|
|
96
|
+
*
|
|
97
|
+
* const layer = Storage.layer().pipe(
|
|
98
|
+
* Layer.provide(Layer.succeed(StorageAdapter, memory())),
|
|
99
|
+
* );
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* @example With options
|
|
103
|
+
* ```ts
|
|
104
|
+
* const layer = Storage.layer({ prefix: "app-data/" }).pipe(
|
|
105
|
+
* Layer.provide(Layer.succeed(StorageAdapter, memory())),
|
|
106
|
+
* );
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export const layer = (options) => Layer.effect(Storage, make(options));
|
|
110
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5D;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,OAAQ,SAAQ,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,EAGvE;CAAG;AAEN;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,OAAqB,EAAE,EAAE,CAC5C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CACtB,uGAAuG,CACxG,CAAC;IAEF,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IACtC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,SAAS,EAAa,CAAC;IAEpD,MAAM,KAAK,GAAG,CAAC,KAAgB,EAAQ,EAAE;QACvC,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,OAAO,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;QACrE,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC;QAC/B,GAAG,OAAO;QACV,OAAO;QACP,KAAK,EAAE;YACL,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,UAAmB,EAAE,KAAK,EAAE,CAAC;YAChE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAkB,EAAE,KAAK,EAAE,CAAC;YAC9D,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAkB,EAAE,KAAK,EAAE,CAAC;SAC/D;KACF,CAAC,CAAC;IAEH,MAAM,GAAG,GAAqB;QAC5B,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAC1B,cAAc,CAAiD,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,CACpF,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CACzD;QAEH,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAE1F,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAElF,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAEtF,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAEtF,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAE5F,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAE5F,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAExE,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAEhF,eAAe,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAC7B,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAE1E,UAAU,EAAE,CAAqB,IAAO,EAAyC,EAAE,CACjF,IAAI,CACF,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EACzB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EACrC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CACc;QAE5C,IAAI,EAAE,CAAC,GAAG,EAAc,EAAE,CAAC,CAAC;YAC1B,GAAG;YACH,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;YACnD,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;YAC3C,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC;YACnC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC;YACvC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC;YACvC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC;YACjC,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC;YACzD,MAAM,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC;YACvD,QAAQ,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;SACxD,CAAC;KACH,CAAC;IAEF,OAAO,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC,CAAC,CAAC;AAEL;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,OAAqB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@effect-pantry/storage",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Effect-native storage — wraps files-sdk as typed Effect functions with scope-managed lifecycle",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"effect",
|
|
7
|
+
"file-storage",
|
|
8
|
+
"files-sdk",
|
|
9
|
+
"r2",
|
|
10
|
+
"s3"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/nipakke/effect-pantry/tree/main/packages/storage#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/nipakke/effect-pantry/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/nipakke/effect-pantry.git",
|
|
20
|
+
"directory": "packages/storage"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"effect": "^3.21.2"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@effect/vitest": "^0.29.0",
|
|
42
|
+
"@types/node": "^25.8.0",
|
|
43
|
+
"@typescript/native-preview": "7.0.0-dev.20260515.1",
|
|
44
|
+
"@vitest/ui": "0.1.21",
|
|
45
|
+
"rimraf": "^6.0.1",
|
|
46
|
+
"vite-plus": "^0.1.21",
|
|
47
|
+
"vitest": "^3.2.0",
|
|
48
|
+
"@tooling/tsconfig": "0.0.1"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"files-sdk": "^1.6.0"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=22"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"clean": "rimraf dist",
|
|
58
|
+
"build": "pnpm clean && tsgo",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"check": "vp check",
|
|
61
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json"
|
|
62
|
+
}
|
|
63
|
+
}
|