@voltro/plugin-storage 0.80.0 → 0.82.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.
@@ -10789,7 +10789,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
10789
10789
  SOFTWARE.
10790
10790
  ```
10791
10791
 
10792
- ## undici@8.10.0
10792
+ ## undici@8.11.2
10793
10793
 
10794
10794
  License: MIT
10795
10795
 
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import { Effect } from 'effect';
6
6
  import { FieldDefinitions } from '@voltro/database';
7
7
  import { Layer } from 'effect';
8
8
  import { mixin } from '@voltro/database';
9
+ import { PluginEgressDeclaration } from '@voltro/protocol';
9
10
  import { Readable } from 'node:stream';
10
11
  import { Schema } from 'effect';
11
12
  import { Stream } from 'effect';
@@ -285,6 +286,16 @@ export declare interface GrantTokenPayload {
285
286
  /** Hash a file password as `salt:scryptHash` (hex). */
286
287
  export declare const hashPassword: (password: string) => string;
287
288
 
289
+ /** Where `ingestUrl` may fetch from — see `ssrfReason` in `@voltro/runtime`. */
290
+ export declare interface IngestPolicy {
291
+ /** Hosts (or `host:port`, or `*.suffix`) reachable although internal — a
292
+ * deliberate internal source, or a local test server. Everything else that
293
+ * is loopback, private, link-local (cloud metadata) or `.internal` is refused. */
294
+ readonly allowHosts?: ReadonlyArray<string>;
295
+ /** Redirect hops followed, each checked again. Default 5. */
296
+ readonly maxRedirects?: number;
297
+ }
298
+
288
299
  /**
289
300
  * Filter + page a `StorageService.listRefs` / media-library query. Every
290
301
  * filter is optional and ANDed; omit them all to page the whole (tenant-scoped)
@@ -365,7 +376,7 @@ export declare const makeStorageObjectMetadata: (store: DataStore) => {
365
376
  readonly id: string;
366
377
  readonly fingerprint: string;
367
378
  readonly generation: string;
368
- readonly state: "ready" | "reserved" | "deleting" | "deleted";
379
+ readonly state: "ready" | "reserved" | "deleted" | "deleting";
369
380
  readonly refId: string;
370
381
  readonly objectId: string;
371
382
  readonly deleteKey: string | null;
@@ -574,6 +585,12 @@ export declare interface RefStore {
574
585
  * newest-first, offset-paged. Backs `StorageService.listRefs`. Fetches
575
586
  * `limit + 1` so the service can report whether another page follows. */
576
587
  readonly listRefs: (query: ResolvedListQuery) => Effect.Effect<ReadonlyArray<StorageRef>, StorageError>;
588
+ /** Change one ref's visibility; the updated ref, or null when there is none.
589
+ * Optional: a store without it cannot back `StorageService.setVisibility`. */
590
+ readonly setVisibility?: ((id: string, visibility: StorageVisibility) => Effect.Effect<StorageRef | null, StorageError>) | undefined;
591
+ /** How many PUBLIC refs point at this provider key — whether the object must
592
+ * stay publicly readable. */
593
+ readonly countPublicByKey?: ((bucket: string, key: string) => Effect.Effect<number, StorageError>) | undefined;
577
594
  }
578
595
 
579
596
  /** One derived output a `Transcoder` produces from a source asset. Each becomes
@@ -683,6 +700,13 @@ declare const StorageAccessDenied_base: Schema.TaggedErrorClass<StorageAccessDen
683
700
  reason: typeof Schema.String;
684
701
  }>;
685
702
 
703
+ /**
704
+ * Where the bytes go, read from the same options and env `resolveProvider`
705
+ * reads: an object store is a destination outside the installation; the
706
+ * database, the filesystem and memory keep the bytes inside it.
707
+ */
708
+ export declare const storageEgressOf: (options: StoragePluginOptions) => ReadonlyArray<PluginEgressDeclaration>;
709
+
686
710
  /**
687
711
  * Storage failure. `transient` drives retry — `true` for 429 / 5xx /
688
712
  * network blips, `false` for 4xx / config / not-found. Surfaced typed so
@@ -841,8 +865,23 @@ export declare interface StoragePluginOptions {
841
865
  readonly urlExpiresInSec?: number;
842
866
  /** Access engine config — a group resolver + named custom guards. */
843
867
  readonly access?: AccessOptions;
868
+ /**
869
+ * The access policy every PRIVATE client upload (an upload ticket) is
870
+ * stored with — declared by the app, never chosen by the client. Without
871
+ * it only the uploader (the implicit owner rule) can read a private upload;
872
+ * `[{ tenant: true }]` lets everyone in the uploader's tenant read it.
873
+ */
874
+ readonly uploadAccess?: AccessPolicy;
844
875
  /** Upload constraints — size cap, content-type allow-list, magic-byte sniff. */
845
876
  readonly limits?: UploadLimits;
877
+ /**
878
+ * Where `StorageService.ingestUrl` may fetch from. Every address — the URL
879
+ * and each redirect — is checked: loopback, private, link-local (cloud
880
+ * metadata), `localhost` and `.internal`/`.local` hosts are refused with
881
+ * `StorageRejected` (`blocked-source`). `allowHosts` names deliberate
882
+ * internal sources (or a local test server).
883
+ */
884
+ readonly ingest?: IngestPolicy;
846
885
  /**
847
886
  * Let a caller with NO identity mint an upload ticket. Default `false`.
848
887
  *
@@ -933,6 +972,10 @@ export declare interface StorageProvider {
933
972
  * publicly without the app (filesystem/memory) → the service falls
934
973
  * back to the `/_voltro/storage/:id` route. */
935
974
  readonly publicUrl?: (key: string) => string | null;
975
+ /** Make an object publicly readable at the provider, or not (s3/minio with
976
+ * per-object ACLs). Absent where the app decides per request (database,
977
+ * filesystem, memory) — there a ref's visibility is the whole answer. */
978
+ readonly setObjectPublic?: (key: string, isPublic: boolean) => Effect.Effect<void, StorageError>;
936
979
  /** Presigned PUT for direct-to-bucket uploads (s3/minio). Absent on
937
980
  * providers that can't presign uploads. */
938
981
  readonly getUploadUrl?: (key: string, expiresInSec: number, contentType: string) => Effect.Effect<string, StorageError>;
@@ -1099,6 +1142,8 @@ export declare interface StorageServiceOptions {
1099
1142
  readonly access?: AccessOptions;
1100
1143
  /** Upload constraints — size cap, content-type allow-list, magic-byte sniff. */
1101
1144
  readonly limits?: UploadLimits;
1145
+ /** Where `ingestUrl` may fetch from (internal addresses are refused). */
1146
+ readonly ingest?: IngestPolicy;
1102
1147
  /** Virus scanner run on `put` before the bytes are stored. */
1103
1148
  readonly scan?: StorageScanner;
1104
1149
  /** App-supplied probe for video/audio uploads (duration + poster frame).
@@ -1231,6 +1276,30 @@ export declare interface StorageServiceShape {
1231
1276
  * caller's `tenantId` / `ownerId` for an end-user surface, and deliver any
1232
1277
  * listed private object through `mintUrl`. */
1233
1278
  readonly listRefs: (input?: ListRefsInput) => Effect.Effect<ListRefsResult, StorageError>;
1279
+ /**
1280
+ * A short-lived URL that delivers the bytes WITHOUT an access check — for
1281
+ * server code that has already decided the reader may see them (a shared
1282
+ * preview whose link was verified). Public: its public URL. Private: a
1283
+ * presigned GET (s3/minio) or the serve URL with a signed grant token. Never
1284
+ * hand one to a caller whose access you have not checked.
1285
+ */
1286
+ readonly signedUrl: (id: string, opts?: {
1287
+ readonly tenantId?: string | null;
1288
+ readonly expiresInSec?: number;
1289
+ }) => Effect.Effect<string, StorageError>;
1290
+ /**
1291
+ * Make a stored object public or private after the fact — an image that goes
1292
+ * public when its page is published, and private again when it is taken
1293
+ * down. Checks the tenant like `delete`. Where objects are served from a
1294
+ * public base (`cdnBaseUrl`) a public object cannot be taken back: that is
1295
+ * refused (`StorageRejected`, reason `visibility-unsupported`) rather than
1296
+ * left public. On s3 with per-object ACLs the object follows its refs: it
1297
+ * stays public while any ref to the same bytes is. A public object's earlier
1298
+ * responses may remain in browser and CDN caches.
1299
+ */
1300
+ readonly setVisibility: (id: string, visibility: StorageVisibility, opts?: {
1301
+ readonly tenantId?: string | null;
1302
+ }) => Effect.Effect<StorageRef, StorageError | StorageRejected>;
1234
1303
  readonly grant: (input: GrantInput) => Effect.Effect<StorageGrant, StorageError>;
1235
1304
  readonly revoke: (grantId: string) => Effect.Effect<void, StorageError>;
1236
1305
  /** One grant by id, or null. Server-side read — the RPC layer uses it to
@@ -1269,6 +1338,8 @@ export declare interface StorageServiceShape {
1269
1338
  readonly operationId?: string;
1270
1339
  readonly ownerId?: string | null;
1271
1340
  readonly visibility?: StorageVisibility;
1341
+ /** The access policy of a private object, as on `put`. */
1342
+ readonly access?: AccessPolicy;
1272
1343
  readonly key?: string;
1273
1344
  readonly folder?: string;
1274
1345
  readonly tags?: ReadonlyArray<string>;
@@ -1447,6 +1518,13 @@ export declare interface Transformed {
1447
1518
  readonly contentType: string;
1448
1519
  }
1449
1520
 
1521
+ /**
1522
+ * Why a requested transform served the original instead — `null` when it did
1523
+ * not fall back. The storage route sends it as `x-voltro-transform`, so a
1524
+ * resize or format change that did not happen is never silent.
1525
+ */
1526
+ export declare const transformFallbackReason: (contentType: string, transformed: boolean) => Promise<"not-an-image" | "no-sharp" | "failed" | null>;
1527
+
1450
1528
  /**
1451
1529
  * Transform image bytes per `params`. Returns null (→ serve original) when
1452
1530
  * the content isn't a raster image, `sharp` isn't installed, or processing
@@ -1460,6 +1538,15 @@ export declare interface TransformParams {
1460
1538
  readonly format?: string;
1461
1539
  readonly q?: number;
1462
1540
  readonly fit?: string;
1541
+ /**
1542
+ * The focal point, `fp=x,y` as fractions of the width and height (0–1). With
1543
+ * both `w` and `h` and `fit=cover` (the default), the crop keeps this point
1544
+ * as central as the image allows instead of cutting around the middle.
1545
+ */
1546
+ readonly fp?: {
1547
+ readonly x: number;
1548
+ readonly y: number;
1549
+ };
1463
1550
  }
1464
1551
 
1465
1552
  export declare interface UploadLimits {