@voltro/plugin-storage 0.85.0 → 0.87.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +5987 -0
  2. package/README.md +77 -0
  3. package/THIRD-PARTY-NOTICES.md +860 -249
  4. package/dist/access.js +47 -0
  5. package/dist/assetRef.js +13 -0
  6. package/dist/avatar.js +39 -0
  7. package/dist/byteSecurity.d.ts +3 -0
  8. package/dist/byteSecurity.js +7 -0
  9. package/dist/connection.js +99 -0
  10. package/dist/constraints.js +93 -0
  11. package/dist/custody.js +132 -0
  12. package/dist/derivatives.js +30 -0
  13. package/dist/env.js +162 -0
  14. package/dist/grant.js +132 -0
  15. package/dist/index.d.ts +525 -203
  16. package/dist/index.js +765 -3196
  17. package/dist/ingest.js +87 -0
  18. package/dist/ingestPolicy.js +1 -0
  19. package/dist/metadataError.js +19 -0
  20. package/dist/objectAbandon.js +16 -0
  21. package/dist/objectLifecycle.js +243 -0
  22. package/dist/objectMetadata.js +308 -0
  23. package/dist/objectWriter.js +87 -0
  24. package/dist/pathDurability.js +16 -0
  25. package/dist/physicalReferences.js +54 -0
  26. package/dist/providers.js +638 -0
  27. package/dist/referenceCatalogue.js +31 -0
  28. package/dist/renditionProof.js +82 -0
  29. package/dist/retainedCapture.js +391 -0
  30. package/dist/retainedObjects.js +190 -0
  31. package/dist/rpc.d.ts +3 -15
  32. package/dist/rpc.js +3 -1
  33. package/dist/scanners.js +46 -0
  34. package/dist/server.d.ts +153 -0
  35. package/dist/server.js +4 -0
  36. package/dist/service.js +1418 -0
  37. package/dist/sourceIdentity.js +14 -0
  38. package/dist/sourcePin.js +35 -0
  39. package/dist/storageNamespaces.js +80 -0
  40. package/dist/storageNumbers.js +13 -0
  41. package/dist/storageObjectNamespaces.js +243 -0
  42. package/dist/storageReferenceInstances.js +52 -0
  43. package/dist/storageRequest.js +10 -0
  44. package/dist/storageRouting.js +46 -0
  45. package/dist/streamBridge.js +33 -0
  46. package/dist/table.js +54 -0
  47. package/dist/transcodeVideo.js +91 -0
  48. package/dist/transform.js +94 -0
  49. package/dist/transformCache.js +31 -0
  50. package/dist/types.d.ts +116 -134
  51. package/dist/types.js +14 -14
  52. package/package.json +17 -6
@@ -0,0 +1,94 @@
1
+ import { createTransformCache as e } from "./transformCache.js";
2
+ import { createHash as t } from "node:crypto";
3
+ //#region src/transform.ts
4
+ var n = (e, t, n, r, i, a) => {
5
+ let o = n / r, s = Math.min(e, Math.round(t * o)), c = Math.min(t, Math.round(s / o)), l = (e, t) => Math.max(0, Math.min(t, e));
6
+ return {
7
+ left: l(Math.round(i * e - s / 2), e - s),
8
+ top: l(Math.round(a * t - c / 2), t - c),
9
+ width: s,
10
+ height: c
11
+ };
12
+ }, r = {
13
+ webp: "image/webp",
14
+ avif: "image/avif",
15
+ jpeg: "image/jpeg",
16
+ jpg: "image/jpeg",
17
+ png: "image/png"
18
+ }, i = (e) => {
19
+ if (!e) return null;
20
+ let t = new URLSearchParams(e), n = (e) => {
21
+ let n = t.get(e);
22
+ if (n == null) return;
23
+ let r = Number(n);
24
+ return Number.isFinite(r) && r > 0 ? Math.floor(r) : void 0;
25
+ }, r = n("w"), i = n("h"), a = n("q"), o = (t.get("format") ?? t.get("fm") ?? void 0)?.toLowerCase(), s = t.get("fit") ?? void 0, c = (t.get("fp") ?? "").split(",").map(Number), l = c.length === 2 && c.every((e) => Number.isFinite(e) && e >= 0 && e <= 1) ? {
26
+ x: c[0],
27
+ y: c[1]
28
+ } : void 0;
29
+ return r === void 0 && i === void 0 && !o && a === void 0 ? null : {
30
+ ...r === void 0 ? {} : { w: r },
31
+ ...i === void 0 ? {} : { h: i },
32
+ ...o ? { format: o } : {},
33
+ ...a === void 0 ? {} : { q: a },
34
+ ...s ? { fit: s } : {},
35
+ ...l ? { fp: l } : {}
36
+ };
37
+ }, a = (e) => e.startsWith("image/") && !e.includes("svg"), o, s = async () => (o ??= import("sharp").then((e) => e.default ?? e, () => null), o), c = async (e, t) => t ? null : a(e) ? await s() === null ? "no-sharp" : "failed" : "not-an-image", l = async (e, t, i) => {
38
+ if (!a(t)) return null;
39
+ let o = await s();
40
+ if (!o) return null;
41
+ try {
42
+ let a = o(Buffer.from(e), { failOn: "none" }), s = i.fit ?? "cover";
43
+ if (i.fp !== void 0 && i.w !== void 0 && i.h !== void 0 && s === "cover") {
44
+ let e = await a.metadata();
45
+ e.width !== void 0 && e.height !== void 0 && (a = a.extract(n(e.width, e.height, i.w, i.h, i.fp.x, i.fp.y)));
46
+ }
47
+ (i.w !== void 0 || i.h !== void 0) && (a = a.resize({
48
+ ...i.w === void 0 ? {} : { width: i.w },
49
+ ...i.h === void 0 ? {} : { height: i.h },
50
+ fit: i.fit ?? "cover",
51
+ withoutEnlargement: !0
52
+ }));
53
+ let c = i.format && r[i.format] ? i.format : void 0, l = t;
54
+ if (c) {
55
+ let e = c === "jpg" ? "jpeg" : c;
56
+ a = a.toFormat(e, i.q === void 0 ? {} : { quality: i.q }), l = r[c];
57
+ } else i.q !== void 0 && (t === "image/jpeg" || t === "image/webp") && (a = a.toFormat(t === "image/webp" ? "webp" : "jpeg", { quality: i.q }));
58
+ let u = await a.toBuffer();
59
+ return {
60
+ bytes: new Uint8Array(u),
61
+ contentType: l
62
+ };
63
+ } catch {
64
+ return null;
65
+ }
66
+ }, u = async (e, t, n) => {
67
+ if (!a(t) || n.rotate === void 0 && n.flip !== !0 && n.flop !== !0 && n.grayscale !== !0 && n.crop === void 0) return null;
68
+ let r = await s();
69
+ if (!r) return null;
70
+ try {
71
+ let i = r(Buffer.from(e), { failOn: "none" });
72
+ return n.crop !== void 0 && (i = i.extract(n.crop)), n.rotate !== void 0 && (i = i.rotate(n.rotate)), n.flip === !0 && (i = i.flip()), n.flop === !0 && (i = i.flop()), n.grayscale === !0 && (i = i.grayscale()), {
73
+ bytes: new Uint8Array(await i.toBuffer()),
74
+ contentType: t
75
+ };
76
+ } catch {
77
+ return null;
78
+ }
79
+ }, d = (e, t, n) => JSON.stringify([
80
+ e,
81
+ t,
82
+ n.w,
83
+ n.h,
84
+ n.format,
85
+ n.q,
86
+ n.fit,
87
+ n.fp?.x,
88
+ n.fp?.y
89
+ ]), f = () => {
90
+ let n = e();
91
+ return async (e, r, i, a) => n.getOrCreate(d(JSON.stringify([e, t("sha256").update(r).digest("hex")]), i, a), () => l(r, i, a));
92
+ }, p = f();
93
+ //#endregion
94
+ export { f as createTransformCache, u as editImage, n as focalRegion, a as isTransformable, s as loadSharp, i as parseTransformParams, p as transformCached, c as transformFallbackReason, l as transformImage, d as transformKey };
@@ -0,0 +1,31 @@
1
+ //#region src/transformCache.ts
2
+ var e = (e = {
3
+ entries: 200,
4
+ bytes: 67108864,
5
+ pending: 32
6
+ }) => {
7
+ let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = 0;
8
+ return { getOrCreate: (i, a) => {
9
+ let o = t.get(i);
10
+ if (o) return Promise.resolve(o);
11
+ let s = n.get(i);
12
+ if (s) return s;
13
+ if (n.size >= e.pending) return Promise.resolve(null);
14
+ let c = Promise.resolve().then(a).then((n) => {
15
+ if (n && e.entries > 0 && n.bytes.byteLength <= e.bytes) {
16
+ for (; t.size >= e.entries || r + n.bytes.byteLength > e.bytes;) {
17
+ let e = t.keys().next().value;
18
+ if (e === void 0) break;
19
+ r -= t.get(e).bytes.byteLength, t.delete(e);
20
+ }
21
+ t.set(i, n), r += n.bytes.byteLength;
22
+ }
23
+ return n;
24
+ }).finally(() => {
25
+ n.delete(i);
26
+ });
27
+ return n.set(i, c), c;
28
+ } };
29
+ };
30
+ //#endregion
31
+ export { e as createTransformCache };
package/dist/types.d.ts CHANGED
@@ -1,9 +1,21 @@
1
+ import { ByteStream } from '@voltro/kv/storage';
1
2
  import { Context } from 'effect';
2
3
  import { Effect } from 'effect';
4
+ import { isStorageKey } from '@voltro/kv/storage';
5
+ import { ObjectMeta } from '@voltro/kv/storage';
3
6
  import { Schema } from 'effect';
4
7
  import { StorageAccessPolicy } from '@voltro/protocol';
5
8
  import { StorageAccessRule } from '@voltro/protocol';
6
- import { Stream } from 'effect';
9
+ import { StorageAddress } from '@voltro/kv/storage';
10
+ import { storageAddressIdentity } from '@voltro/kv/storage';
11
+ import { StorageDestination } from '@voltro/kv/storage';
12
+ import { StorageDestinationRouter } from '@voltro/kv/storage';
13
+ import { StorageError } from '@voltro/kv/storage';
14
+ import { StorageObjectLocation } from '@voltro/kv/storage';
15
+ import { StorageProvider } from '@voltro/kv/storage';
16
+ import { StorageProviderName } from '@voltro/kv/storage';
17
+ import { StoredObject } from '@voltro/kv/storage';
18
+ import { StoredStream } from '@voltro/kv/storage';
7
19
  import { Subject } from '@voltro/protocol';
8
20
  import { YieldableError } from 'effect/Cause';
9
21
 
@@ -17,14 +29,7 @@ export declare type AccessPolicy = StorageAccessPolicy;
17
29
  */
18
30
  export declare type AccessRule = StorageAccessRule;
19
31
 
20
- /**
21
- * A lazy stream of bytes — the streaming counterpart to {@link StoredObject}'s
22
- * eager `bytes`. Backpressured: chunks are pulled on demand, so a multi-GB blob
23
- * never fully materialises in memory. The error channel is {@link StorageError}
24
- * so a stream handed to `putStream` carries the same typed failure the rest of
25
- * the transport does (callers map their source errors into it).
26
- */
27
- export declare type ByteStream = Stream.Stream<Uint8Array, StorageError>;
32
+ export { ByteStream }
28
33
 
29
34
  /** Input to `StorageService.grant`. */
30
35
  export declare interface GrantInput {
@@ -40,6 +45,8 @@ export declare type GrantPermission = 'read' | 'write';
40
45
 
41
46
  export declare type GrantPrincipalType = 'user' | 'group' | 'apiKey';
42
47
 
48
+ export { isStorageKey }
49
+
43
50
  /**
44
51
  * Filter + page a `StorageService.listRefs` / media-library query. Every
45
52
  * filter is optional and ANDed; omit them all to page the whole (tenant-scoped)
@@ -75,17 +82,15 @@ export declare interface ListRefsResult {
75
82
  readonly nextOffset: number | null;
76
83
  }
77
84
 
78
- /** Result of a provider `put` / `head`. */
79
- export declare interface ObjectMeta {
80
- readonly size: number;
81
- readonly etag?: string;
82
- }
85
+ export { ObjectMeta }
83
86
 
84
87
  /** A ref with the server-only `passwordHash` removed — safe to serialize. */
85
88
  export declare type PublicStorageRef = Omit<StorageRef, 'passwordHash'>;
86
89
 
87
90
  /** Input to `StorageService.put`. */
88
91
  export declare interface PutInput {
92
+ /** Server-owned logical destination; omitted new writes use this plugin's selection. */
93
+ readonly destination?: string;
89
94
  readonly bytes: Uint8Array;
90
95
  readonly contentType: string;
91
96
  /** Active org id — scopes the key prefix + the ref row. */
@@ -148,6 +153,8 @@ export declare type PutOutcome = 'created' | 'resumed' | 'joined';
148
153
  /** One derived output a `Transcoder` produces from a source asset. Each becomes
149
154
  * a normal storage ref linked back to the original via `derivedFrom`. */
150
155
  export declare interface Rendition {
156
+ /** Optional structured producer attestation; unproven outputs keep it absent. */
157
+ readonly derivation?: RenditionDerivation;
151
158
  readonly bytes: Uint8Array;
152
159
  readonly contentType: string;
153
160
  /** Derivative class — e.g. `'rendition'` (a playable variant) or `'poster'`. */
@@ -156,6 +163,39 @@ export declare interface Rendition {
156
163
  readonly label?: string;
157
164
  }
158
165
 
166
+ /** Producer attestation. Digests bind a normalized recipe and the observed
167
+ * engine build; the consumer still admits allowed recipes, engines and rights. */
168
+ export declare interface RenditionDerivation {
169
+ readonly recipe: {
170
+ readonly format: string;
171
+ readonly digest: string;
172
+ };
173
+ readonly engine: {
174
+ readonly name: string;
175
+ readonly version: string;
176
+ readonly digest: string;
177
+ };
178
+ }
179
+
180
+ /** Storage-verified input/output identity around a producer attestation.
181
+ * Not an approval, rights grant or proof of complete external dependencies. */
182
+ export declare interface RenditionProof extends RenditionDerivation {
183
+ readonly version: 1;
184
+ readonly source: {
185
+ readonly id: string;
186
+ readonly metadataDigest: string;
187
+ readonly checksum: string;
188
+ readonly size: number;
189
+ readonly contentType: string;
190
+ };
191
+ readonly output: {
192
+ readonly checksum: string;
193
+ readonly size: number;
194
+ readonly contentType: string;
195
+ };
196
+ readonly digest: string;
197
+ }
198
+
159
199
  /**
160
200
  * Access to a private object was refused. Distinct from `StorageError` so
161
201
  * handlers can tell "not allowed" (403) from "backend blew up" (5xx) and
@@ -169,20 +209,15 @@ declare const StorageAccessDenied_base: Schema.Class<StorageAccessDenied, Schema
169
209
  readonly reason: Schema.String;
170
210
  }>, YieldableError>;
171
211
 
172
- /**
173
- * Storage failure. `transient` drives retry — `true` for 429 / 5xx /
174
- * network blips, `false` for 4xx / config / not-found. Surfaced typed so
175
- * handlers can `Effect.catchTag('StorageError', …)`.
176
- */
177
- export declare class StorageError extends StorageError_base {
178
- }
212
+ export { StorageAddress }
179
213
 
180
- declare const StorageError_base: Schema.Class<StorageError, Schema.TaggedStruct<"StorageError", {
181
- readonly provider: Schema.String;
182
- readonly message: Schema.String;
183
- readonly transient: Schema.Boolean;
184
- readonly status: Schema.optional<Schema.Number>;
185
- }>, YieldableError>;
214
+ export { storageAddressIdentity }
215
+
216
+ export { StorageDestination }
217
+
218
+ export { StorageDestinationRouter }
219
+
220
+ export { StorageError }
186
221
 
187
222
  /** A persisted, explicit share of one object with one principal. */
188
223
  export declare interface StorageGrant {
@@ -202,98 +237,27 @@ export declare type StorageGuard = (ctx: {
202
237
  readonly ref: StorageRef;
203
238
  }) => boolean | Promise<boolean> | Effect.Effect<boolean>;
204
239
 
205
- /**
206
- * A backend transport. Keyed by an opaque object key (the service owns key
207
- * derivation + tenant prefixing); knows nothing about refs, tenants, or
208
- * access policy.
209
- */
210
- export declare interface StorageProvider {
211
- /**
212
- * Where a FILESYSTEM provider writes. Absent for every other provider.
213
- *
214
- * It exists so a caller can ask a question this interface otherwise cannot
215
- * answer: does what I write here survive the process? A rollback capture
216
- * written for a pod that then dies is worthless, and `filesystem` is the one
217
- * provider that can be either — a mounted volume or an ephemeral container
218
- * directory. A deployment pointed this out: their check for "storage is
219
- * configured" passed on a `/tmp` path with no volume behind it.
220
- */
221
- readonly root?: string;
222
- readonly name: string;
223
- /** True when `getUrl` returns a real presigned URL (s3/minio). When
224
- * false the service serves bytes via the `/_voltro/storage/:id`
225
- * endpoint instead (filesystem/memory). */
226
- readonly presigns: boolean;
227
- readonly put: (key: string, bytes: Uint8Array, meta: {
228
- readonly contentType: string;
229
- readonly public?: boolean;
230
- }) => Effect.Effect<ObjectMeta, StorageError>;
231
- readonly get: (key: string) => Effect.Effect<StoredObject, StorageError>;
232
- readonly delete: (key: string) => Effect.Effect<void, StorageError>;
233
- /** Presigned GET (s3/minio) for private reads. */
234
- readonly getUrl: (key: string, expiresInSec: number) => Effect.Effect<string, StorageError>;
235
- /** A direct, cacheable URL for a PUBLIC object — virtual-hosted bucket
236
- * URL or a configured CDN base. `null` when the provider can't serve
237
- * publicly without the app (filesystem/memory) → the service falls
238
- * back to the `/_voltro/storage/:id` route. */
239
- readonly publicUrl?: (key: string) => string | null;
240
- /** Make an object publicly readable at the provider, or not (s3/minio with
241
- * per-object ACLs). Absent where the app decides per request (database,
242
- * filesystem, memory) — there a ref's visibility is the whole answer. */
243
- readonly setObjectPublic?: (key: string, isPublic: boolean) => Effect.Effect<void, StorageError>;
244
- /** Presigned PUT for direct-to-bucket uploads (s3/minio). Absent on
245
- * providers that can't presign uploads. */
246
- readonly getUploadUrl?: (key: string, expiresInSec: number, contentType: string) => Effect.Effect<string, StorageError>;
247
- readonly createMultipartUpload?: (key: string, contentType: string, opts?: {
248
- readonly public?: boolean;
249
- }) => Effect.Effect<{
250
- readonly uploadId: string;
251
- }, StorageError>;
252
- readonly presignUploadPart?: (key: string, uploadId: string, partNumber: number, expiresInSec: number) => Effect.Effect<string, StorageError>;
253
- readonly completeMultipartUpload?: (key: string, uploadId: string, parts: ReadonlyArray<{
254
- readonly partNumber: number;
255
- readonly etag: string;
256
- }>) => Effect.Effect<{
257
- readonly etag?: string;
258
- }, StorageError>;
259
- readonly abortMultipartUpload?: (key: string, uploadId: string) => Effect.Effect<void, StorageError>;
260
- readonly head: (key: string) => Effect.Effect<ObjectMeta | null, StorageError>;
261
- /**
262
- * Stream an object's bytes in chunks instead of buffering the whole blob
263
- * ({@link get}). Present on backends with a real streaming read (filesystem,
264
- * s3, azure). OPTIONAL: when a provider omits it, {@link getObjectStream}
265
- * transparently falls back to `get` + a chunked in-memory stream — correct
266
- * for the small-blob backends (memory / database) that have nothing to
267
- * stream from.
268
- */
269
- readonly getStream?: (key: string) => Effect.Effect<StoredStream, StorageError>;
270
- /**
271
- * Consume a {@link ByteStream} and store it via the backend's chunked /
272
- * multipart upload, so a large upload never fully materialises in memory.
273
- * Present on filesystem (streamed atomic write), s3 (multipart via
274
- * `@aws-sdk/lib-storage`), azure (`uploadStream`). OPTIONAL: when absent,
275
- * {@link putObjectStream} buffers the stream and calls `put`. `totalSize`,
276
- * when the caller knows it (e.g. from an export manifest), lets the backend
277
- * report an accurate size without a follow-up HEAD.
278
- */
279
- readonly putStream?: (key: string, stream: ByteStream, meta: {
280
- readonly contentType: string;
281
- readonly public?: boolean;
282
- readonly totalSize?: number;
283
- }) => Effect.Effect<ObjectMeta, StorageError>;
284
- /**
285
- * Read a byte range `[start, endInclusive]` (HTTP Range / fd offset). Enables
286
- * seekable + resumable reads. OPTIONAL and best-effort: whole-object resume
287
- * already works via content-addressing (skip an asset whose hash is present
288
- * at the target), so this is an optimisation, not a correctness requirement.
289
- */
290
- readonly getRange?: (key: string, start: number, endInclusive: number) => Effect.Effect<Uint8Array, StorageError>;
291
- /** Providers that store bytes via the app's DataStore (e.g. `database`)
292
- * receive it here once it exists — bound by the plugin at boot. */
293
- readonly bindDataStore?: (store: unknown) => void;
294
- }
240
+ export { StorageObjectLocation }
241
+
242
+ /** A durable metadata observation, not a claim on the object or a provider HEAD. */
243
+ export declare type StorageOperationState = {
244
+ readonly state: 'absent' | 'reserved' | 'deleting' | 'deleted';
245
+ } | {
246
+ readonly state: 'ready';
247
+ readonly ref: StorageRef;
248
+ };
249
+
250
+ /** Metadata state qualified by the actual selected storage destination. */
251
+ export declare type StorageOperationStatus = StorageOperationState & {
252
+ /** Configured instance, actual catalogue, physical namespace and logical bucket. */
253
+ readonly scope: string;
254
+ /** Selected logical destination; retain it with the scope before minting a ticket. */
255
+ readonly destination: string;
256
+ };
295
257
 
296
- export declare type StorageProviderName = 's3' | 'minio' | 'azure' | 'filesystem' | 'memory' | 'database';
258
+ export { StorageProvider }
259
+
260
+ export { StorageProviderName }
297
261
 
298
262
  /**
299
263
  * Metadata row for a stored blob — the bytes live in object storage; this
@@ -333,8 +297,13 @@ export declare interface StorageRef {
333
297
  readonly derivedFrom?: string | null;
334
298
  /** Derivative label — e.g. `'rendition'` / `'poster'`; null on an original. */
335
299
  readonly kind?: string | null;
300
+ /** Immutable provenance for an attested transcode; absent on older/unproven refs. */
301
+ readonly renditionProof?: RenditionProof | null;
336
302
  }
337
303
 
304
+ /** Persisted destination ownership is independent of the current write selection. */
305
+ export declare const storageRefAddress: (ref: Pick<StorageRef, "bucket" | "tenantId" | "key">) => StorageAddress;
306
+
338
307
  /** An upload was rejected by a constraint (size / content-type / magic-byte
339
308
  * mismatch). `reason` is a stable code; `detail` is human-readable. */
340
309
  export declare class StorageRejected extends StorageRejected_base {
@@ -376,6 +345,20 @@ declare const StorageService_base: Context.ServiceClass<StorageService, "@voltro
376
345
 
377
346
  /** The service handlers consume: `const storage = yield* StorageService`. */
378
347
  export declare interface StorageServiceShape {
348
+ /** Trusted server read of one raw durable put identity in this configured
349
+ * instance/physical namespace/bucket and the explicitly supplied tenant.
350
+ * No access-policy check, claim, byte read, retry write or provider HEAD.
351
+ * Unbound/unsupported/corrupt metadata refuses; it never becomes absence. */
352
+ readonly operationStatus: (input: {
353
+ readonly operationId: string;
354
+ readonly tenantId: string | null;
355
+ readonly destination?: string;
356
+ }) => Effect.Effect<StorageOperationStatus, StorageError>;
357
+ /** Opaque destination binding for manually signed upload-family credentials. */
358
+ readonly uploadTicketScope: (input?: {
359
+ readonly tenantId?: string | null;
360
+ readonly destination?: string;
361
+ }) => Effect.Effect<string, StorageError>;
379
362
  /** Store bytes. The result is the ref plus this call's `outcome`. */
380
363
  readonly put: (input: PutInput) => Effect.Effect<StoredRef, StorageError | StorageRejected | StorageScanRejected>;
381
364
  /** Fetch an object's bytes by id. Enforces the tenant guard ONLY — it does
@@ -546,9 +529,9 @@ export declare interface StorageServiceShape {
546
529
  readonly contentType: string;
547
530
  readonly tenantId?: string | null;
548
531
  readonly expiresInSec?: number;
549
- }) => Effect.Effect<{
532
+ }) => Effect.Effect<StorageAddress & {
533
+ readonly writeScope: string;
550
534
  readonly uploadUrl: string;
551
- readonly key: string;
552
535
  readonly expiresInSec: number;
553
536
  }, StorageError>;
554
537
  /** Promote a blob the client PUT directly to a TEMP `key` into a real ref:
@@ -557,6 +540,7 @@ export declare interface StorageServiceShape {
557
540
  * scanned + a non-degraded ref — out of band from the direct PUT. On a scan
558
541
  * reject the temp bytes are deleted (fail-closed, nothing promoted). */
559
542
  readonly finalizeUpload: (input: {
543
+ readonly destination: string;
560
544
  readonly key: string;
561
545
  readonly contentType: string;
562
546
  readonly tenantId?: string | null;
@@ -571,6 +555,7 @@ export declare interface StorageServiceShape {
571
555
  /** Store one chunk of a resumable upload as its own durable `_incoming/<id>/
572
556
  * <index>` object. Idempotent — re-PUTting the same index overwrites it. */
573
557
  readonly putResumableChunk: (input: {
558
+ readonly destination: string;
574
559
  readonly uploadId: string;
575
560
  readonly index: number;
576
561
  readonly bytes: Uint8Array;
@@ -580,6 +565,7 @@ export declare interface StorageServiceShape {
580
565
  * 0 and returns the received byte `offset` + the `nextIndex` to send. Lets a
581
566
  * reconnecting client resume instead of restarting. */
582
567
  readonly resumableStatus: (input: {
568
+ readonly destination: string;
583
569
  readonly uploadId: string;
584
570
  readonly tenantId?: string | null;
585
571
  }) => Effect.Effect<{
@@ -590,6 +576,7 @@ export declare interface StorageServiceShape {
590
576
  * concatenate, run the FULL `put()` pipeline, then delete the chunk objects
591
577
  * (on success AND on reject — fail-closed). */
592
578
  readonly finalizeResumable: (input: {
579
+ readonly destination: string;
593
580
  readonly uploadId: string;
594
581
  readonly count: number;
595
582
  readonly contentType: string;
@@ -621,12 +608,14 @@ export declare interface StorageServiceShape {
621
608
  readonly tenantId?: string | null;
622
609
  readonly visibility?: StorageVisibility;
623
610
  readonly expiresInSec?: number;
624
- }) => Effect.Effect<{
611
+ }) => Effect.Effect<StorageAddress & {
612
+ readonly writeScope: string;
625
613
  readonly uploadId: string;
626
- readonly key: string;
627
614
  }, StorageError>;
628
615
  /** Presign a PUT for one part (1-based `partNumber`). */
629
616
  readonly signMultipartPart: (input: {
617
+ readonly destination: string;
618
+ readonly tenantId: string | null;
630
619
  readonly key: string;
631
620
  readonly uploadId: string;
632
621
  readonly partNumber: number;
@@ -636,6 +625,7 @@ export declare interface StorageServiceShape {
636
625
  * registered straight from it (size via HEAD, etag as checksum). No fetch-back,
637
626
  * so NO scan/derivatives — scan multi-GB out of band. */
638
627
  readonly completeMultipart: (input: {
628
+ readonly destination: string;
639
629
  readonly key: string;
640
630
  readonly uploadId: string;
641
631
  readonly parts: ReadonlyArray<{
@@ -654,6 +644,8 @@ export declare interface StorageServiceShape {
654
644
  }) => Effect.Effect<StorageRef, StorageError | StorageRejected>;
655
645
  /** Abort a multipart session (client cancelled) — frees the bucket's parts. */
656
646
  readonly abortMultipart: (input: {
647
+ readonly destination: string;
648
+ readonly tenantId: string | null;
657
649
  readonly key: string;
658
650
  readonly uploadId: string;
659
651
  }) => Effect.Effect<void, StorageError>;
@@ -663,11 +655,7 @@ export declare interface StorageServiceShape {
663
655
  * the ref's access policy + grants. Default for a new put is `private`. */
664
656
  export declare type StorageVisibility = 'public' | 'private';
665
657
 
666
- /** Bytes + content type as returned by a provider `get`. */
667
- export declare interface StoredObject {
668
- readonly bytes: Uint8Array;
669
- readonly contentType: string;
670
- }
658
+ export { StoredObject }
671
659
 
672
660
  /** What `put` returns: the stored ref, plus what this call did to it. The
673
661
  * `outcome` is a fact about the CALL, not the row — it is never persisted and
@@ -676,13 +664,7 @@ export declare type StoredRef = StorageRef & {
676
664
  readonly outcome: PutOutcome;
677
665
  };
678
666
 
679
- /** A streamed read from a provider — the object as a {@link ByteStream} plus
680
- * its content type and, when the backend reports it up front, its size. */
681
- export declare interface StoredStream {
682
- readonly stream: ByteStream;
683
- readonly contentType: string;
684
- readonly size?: number;
685
- }
667
+ export { StoredStream }
686
668
 
687
669
  /**
688
670
  * App-supplied (or first-party `ffmpegTranscoder`) transcode step. Given a
package/dist/types.js CHANGED
@@ -1,20 +1,20 @@
1
- import { Context as e, Schema as t } from "effect";
1
+ import { StorageAddress as e, StorageError as t, isStorageKey as n, storageAddressIdentity as r } from "@voltro/kv/storage";
2
+ import { Context as i, Schema as a } from "effect";
2
3
  //#region src/types.ts
3
- var n = class extends t.TaggedError()("StorageError", {
4
- provider: t.String,
5
- message: t.String,
6
- transient: t.Boolean,
7
- status: t.optional(t.Number)
8
- }) {}, r = class extends t.TaggedError()("StorageAccessDenied", {
9
- refId: t.String,
10
- reason: t.String
11
- }) {}, i = class extends t.TaggedError()("StorageRejected", {
12
- reason: t.String,
13
- detail: t.String
4
+ var o = class extends a.TaggedError()("StorageAccessDenied", {
5
+ refId: a.String,
6
+ reason: a.String
7
+ }) {}, s = class extends a.TaggedError()("StorageRejected", {
8
+ reason: a.String,
9
+ detail: a.String
14
10
  }) {
15
11
  get message() {
16
12
  return this.detail;
17
13
  }
18
- }, a = class extends t.TaggedError()("StorageScanRejected", { threat: t.String }) {}, o = class extends e.Service()("@voltro/plugin-storage/StorageService") {};
14
+ }, c = class extends a.TaggedError()("StorageScanRejected", { threat: a.String }) {}, l = class extends i.Service()("@voltro/plugin-storage/StorageService") {}, u = (e) => ({
15
+ destination: e.bucket,
16
+ tenantId: e.tenantId,
17
+ key: e.key
18
+ });
19
19
  //#endregion
20
- export { r as StorageAccessDenied, n as StorageError, i as StorageRejected, a as StorageScanRejected, o as StorageService };
20
+ export { o as StorageAccessDenied, e as StorageAddress, t as StorageError, s as StorageRejected, c as StorageScanRejected, l as StorageService, n as isStorageKey, r as storageAddressIdentity, u as storageRefAddress };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-storage",
3
- "version": "0.85.0",
3
+ "version": "0.87.0",
4
4
  "description": "File-storage plugin — one StorageProvider contract, switchable backends (S3 / MinIO / Azure / filesystem / memory / database). Public (CDN-direct) + private (access policy + per-object grants) objects, upload constraints, virus-scan hook, on-the-fly image transforms.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -33,7 +33,17 @@
33
33
  "import": "./dist/rpc.js",
34
34
  "default": "./dist/rpc.js"
35
35
  },
36
- "./package.json": "./package.json"
36
+ "./package.json": "./package.json",
37
+ "./server": {
38
+ "types": "./dist/server.d.ts",
39
+ "import": "./dist/server.js",
40
+ "default": "./dist/server.js"
41
+ },
42
+ "./byte-security": {
43
+ "types": "./dist/byteSecurity.d.ts",
44
+ "import": "./dist/byteSecurity.js",
45
+ "default": "./dist/byteSecurity.js"
46
+ }
37
47
  },
38
48
  "main": "./dist/index.js",
39
49
  "module": "./dist/index.js",
@@ -43,10 +53,11 @@
43
53
  "node": ">=24.0.0"
44
54
  },
45
55
  "dependencies": {
46
- "@voltro/database": "0.85.0",
47
- "@voltro/env": "0.85.0",
48
- "@voltro/protocol": "0.85.0",
49
- "@voltro/runtime": "0.85.0"
56
+ "@voltro/database": "0.87.0",
57
+ "@voltro/env": "0.87.0",
58
+ "@voltro/kv": "0.87.0",
59
+ "@voltro/protocol": "0.87.0",
60
+ "@voltro/runtime": "0.87.0"
50
61
  },
51
62
  "optionalDependencies": {
52
63
  "@aws-sdk/client-s3": "^3.1134.0",