@voltro/plugin-storage 0.70.0 → 0.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,12 +3,14 @@ import { ColumnType } from '@voltro/database';
3
3
  import { Context } from 'effect';
4
4
  import { DataStore } from '@voltro/database';
5
5
  import { Effect } from 'effect';
6
+ import { FieldDefinitions } from '@voltro/database';
6
7
  import { Layer } from 'effect';
7
8
  import { mixin } from '@voltro/database';
8
9
  import { Readable } from 'node:stream';
9
10
  import { Schema } from 'effect';
10
11
  import { Stream } from 'effect';
11
12
  import { Subject } from '@voltro/protocol';
13
+ import { Table } from '@voltro/database';
12
14
  import { TableLike } from '@voltro/database';
13
15
  import { VoltroPlugin } from '@voltro/protocol';
14
16
 
@@ -420,7 +422,20 @@ export declare const putObjectStream: (provider: StorageProvider, key: string, s
420
422
  export declare const refMatchesListQuery: (ref: StorageRef, q: ResolvedListQuery) => boolean;
421
423
 
422
424
  /** Where blob METADATA rows live (the bytes are in the provider). */
425
+ export declare interface RefStatistics {
426
+ readonly byTenant: Record<string, {
427
+ count: number;
428
+ bytes: number;
429
+ }>;
430
+ readonly visibility: {
431
+ public: number;
432
+ private: number;
433
+ };
434
+ }
435
+
423
436
  export declare interface RefStore {
437
+ /** Aggregate the complete reference index without a recent-object limit. */
438
+ readonly statistics: () => Effect.Effect<RefStatistics, StorageError>;
424
439
  readonly insert: (ref: StorageRef) => Effect.Effect<void, StorageError>;
425
440
  readonly getById: (id: string) => Effect.Effect<StorageRef | null, StorageError>;
426
441
  /** Remove one ref row. Returns whether a row was ACTUALLY removed — the
@@ -479,6 +494,7 @@ export declare interface Rendition {
479
494
  /** A `ListRefsInput` after the service normalizes its defaults / clamps — what
480
495
  * a `RefStore.listRefs` impl receives. `limit` is already the `+1` probe. */
481
496
  export declare interface ResolvedListQuery {
497
+ readonly search?: string;
482
498
  readonly tenantId: string | null | undefined;
483
499
  readonly folder: string | undefined;
484
500
  readonly tags: ReadonlyArray<string>;
@@ -606,6 +622,18 @@ export declare type StorageGuard = (ctx: {
606
622
  readonly ref: StorageRef;
607
623
  }) => boolean | Promise<boolean> | Effect.Effect<boolean>;
608
624
 
625
+ declare interface StorageObjectIdentity {
626
+ readonly tenantId: string | null;
627
+ readonly bucket: string;
628
+ readonly operationId: string;
629
+ /** Hash of all immutable put inputs, including content and access metadata. */
630
+ readonly fingerprint: string;
631
+ /** SHA-256 of the actual normalized provider bytes, separate from metadata. */
632
+ readonly checksum: string;
633
+ /** Already tenant-scoped logical provider key. */
634
+ readonly logicalKey: string;
635
+ }
636
+
609
637
  export declare const storagePlugin: (options?: StoragePluginOptions) => VoltroPlugin;
610
638
 
611
639
  export declare interface StoragePluginOptions {
@@ -815,6 +843,13 @@ export declare interface StorageProvider {
815
843
 
816
844
  export declare type StorageProviderName = 's3' | 'minio' | 'azure' | 'filesystem' | 'memory' | 'database';
817
845
 
846
+ export declare const storagePutIntentsTable: Table<"_voltro_storage_put_intents", FieldDefinitions<{
847
+ readonly id: ColumnBuilder<string, "text", boolean>;
848
+ readonly refId: ColumnBuilder<string, "text", boolean>;
849
+ readonly identity: ColumnBuilder<StorageObjectIdentity, "json", boolean>;
850
+ readonly ref: ColumnBuilder<StorageRef, "json", boolean>;
851
+ }>, false, never>;
852
+
818
853
  /** Per-tenant upload budget (see `StorageServiceOptions.quota`). */
819
854
  export declare interface StorageQuota {
820
855
  readonly maxBytes?: number;
@@ -938,6 +973,8 @@ export declare interface StorageServiceOptions {
938
973
  * the app: a browser `<img>` and server-to-server AI-gateway fetches both
939
974
  * need an absolute, reachable URL. Omit for same-origin (relative URL). */
940
975
  readonly publicBaseUrl?: string;
976
+ /** Instance-specific HTTP mount supplied by the storage plugin. */
977
+ readonly httpPath?: string;
941
978
  /** Normalize raster images on upload: apply EXIF orientation, then strip ALL
942
979
  * metadata (GPS/camera) and re-encode. Off by default (it re-encodes, which
943
980
  * changes the bytes + content-address). Width/height/placeholder are always
@@ -965,6 +1002,15 @@ export declare interface StorageServiceShape {
965
1002
  readonly bytes: Uint8Array;
966
1003
  readonly ref: StorageRef;
967
1004
  }, StorageError>;
1005
+ /** Trusted server-side streamed read with the same tenant guard as get.
1006
+ * Provider acquisition is lazy; callers must authorize private-object access.
1007
+ * Providers without getStream fall back to a buffered provider read. */
1008
+ readonly getStream: (id: string, opts?: {
1009
+ readonly tenantId?: string | null;
1010
+ }) => Effect.Effect<{
1011
+ readonly stream: ByteStream;
1012
+ readonly ref: StorageRef;
1013
+ }, StorageError>;
968
1014
  /** Direct/presigned URL for an object. For PUBLIC objects this is the
969
1015
  * CDN/bucket URL (no app round-trip).
970
1016
  *
@@ -1054,8 +1100,12 @@ export declare interface StorageServiceShape {
1054
1100
  * migration / "adopt this remote image"). The content-type comes from the
1055
1101
  * response unless overridden. NOTE: this fetches an arbitrary URL from the
1056
1102
  * server — pass only trusted URLs (SSRF); front it with an allow-list if the
1057
- * URL is user-supplied. */
1103
+ * URL is user-supplied. Reads are bounded by `limits.maxBytes` (64 MiB when
1104
+ * absent) before the scanning/checksum/put pipeline. This is bounded
1105
+ * buffering, not a zero-copy provider stream. Effect interruption cancels
1106
+ * the HTTP read; an optional caller signal interrupts the entire ingest. */
1058
1107
  readonly ingestUrl: (url: string, opts?: {
1108
+ readonly abortSignal?: AbortSignal;
1059
1109
  readonly contentType?: string;
1060
1110
  readonly tenantId?: string | null;
1061
1111
  readonly ownerId?: string | null;
@@ -1247,7 +1297,8 @@ export declare interface TransformParams {
1247
1297
  }
1248
1298
 
1249
1299
  export declare interface UploadLimits {
1250
- /** Reject uploads larger than this many bytes. */
1300
+ /** Reject uploads larger than this many bytes. URL ingestion enforces this
1301
+ * while reading the response; absent a limit, ingestion caps at 64 MiB. */
1251
1302
  readonly maxBytes?: number;
1252
1303
  /** Allowed content types — exact (`application/pdf`) or wildcard
1253
1304
  * (`image/*`). Omit to allow any. */
@@ -1260,6 +1311,8 @@ export declare interface UploadLimits {
1260
1311
  /** What an upload ticket authorizes: a single through-app upload bound to an
1261
1312
  * owner/tenant/content-type/visibility, until `exp`. */
1262
1313
  export declare interface UploadTicketPayload {
1314
+ /** Exact storage plugin instance authorized to accept the bytes. */
1315
+ readonly instance: string;
1263
1316
  /** ownerId (subject id) the stored object is attributed to. */
1264
1317
  readonly sub: string | null;
1265
1318
  /** tenantId that scopes the key prefix + ref row. */