@voltro/plugin-storage 0.84.0 → 0.85.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/rpc.js CHANGED
@@ -1,17 +1,21 @@
1
1
  import { StorageAccessDenied as e, StorageError as t, StorageRejected as n, StorageScanRejected as r } from "./types.js";
2
2
  import { Schema as i } from "effect";
3
- import { defineAction as a, defineMutation as o, defineQuery as s } from "@voltro/protocol";
3
+ import { StorageAccessPolicySchema as a, defineAction as o, defineMutation as s, defineQuery as c } from "@voltro/protocol";
4
4
  //#region src/rpc.ts
5
- var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u = i.Struct({
5
+ var l = i.Literals([
6
+ "user",
7
+ "group",
8
+ "apiKey"
9
+ ]), u = i.Literals(["read", "write"]), d = i.Struct({
6
10
  id: i.String,
7
11
  refId: i.String,
8
- principalType: c,
12
+ principalType: l,
9
13
  principalId: i.String,
10
- permission: l,
14
+ permission: u,
11
15
  createdAt: i.String,
12
16
  expiresAt: i.NullOr(i.String),
13
17
  createdBy: i.NullOr(i.String)
14
- }), d = i.Struct({
18
+ }), f = i.Struct({
15
19
  id: i.String,
16
20
  tenantId: i.NullOr(i.String),
17
21
  ownerId: i.NullOr(i.String),
@@ -20,7 +24,7 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
20
24
  contentType: i.String,
21
25
  size: i.Number,
22
26
  checksum: i.String,
23
- visibility: i.Literal("public", "private"),
27
+ visibility: i.Literals(["public", "private"]),
24
28
  hasPassword: i.Boolean,
25
29
  createdAt: i.String,
26
30
  width: i.NullOr(i.Number),
@@ -33,7 +37,7 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
33
37
  caption: i.NullOr(i.String),
34
38
  derivedFrom: i.NullOr(i.String),
35
39
  kind: i.NullOr(i.String)
36
- }), f = s({
40
+ }), p = c({
37
41
  name: "storage.listRefs",
38
42
  input: i.Struct({
39
43
  folder: i.optional(i.String),
@@ -44,12 +48,12 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
44
48
  offset: i.optional(i.Number)
45
49
  }),
46
50
  output: i.Struct({
47
- refs: i.Array(d),
51
+ refs: i.Array(f),
48
52
  nextOffset: i.NullOr(i.Number)
49
53
  }),
50
54
  error: t,
51
55
  guards: [{ scope: "storage:browse" }]
52
- }), p = a({
56
+ }), m = o({
53
57
  name: "storage.mintUrl",
54
58
  input: i.Struct({
55
59
  id: i.String,
@@ -57,9 +61,9 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
57
61
  expiresInSec: i.optional(i.Number)
58
62
  }),
59
63
  output: i.Struct({ url: i.String }),
60
- error: i.Union(t, e),
64
+ error: i.Union([t, e]),
61
65
  openAccess: "per-object authorization: the access policy + grants decide this subject's read (StorageAccessDenied on refusal); a scope here would double-gate what the policy already decides per object"
62
- }), m = a({
66
+ }), h = o({
63
67
  name: "storage.mintUploadUrl",
64
68
  input: i.Struct({
65
69
  key: i.String,
@@ -69,11 +73,12 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
69
73
  output: i.Struct({ url: i.String }),
70
74
  error: t,
71
75
  guards: [{ scope: "storage:manage" }]
72
- }), h = a({
76
+ }), g = o({
73
77
  name: "storage.mintUploadTicket",
74
78
  input: i.Struct({
75
79
  contentType: i.String,
76
- visibility: i.optional(i.Literal("public", "private")),
80
+ visibility: i.optional(i.Literals(["public", "private"])),
81
+ access: i.optional(a),
77
82
  key: i.optional(i.String),
78
83
  folder: i.optional(i.String),
79
84
  tags: i.optional(i.Array(i.String)),
@@ -87,11 +92,12 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
87
92
  }),
88
93
  error: t,
89
94
  openAccess: "self-service upload: the ticket binds the calling subject + tenant, caps size, and the finalize path runs limits + scan; an unidentified caller is refused in-handler unless the app set `anonymousUploads`"
90
- }), g = a({
95
+ }), _ = o({
91
96
  name: "storage.mintPresignedUpload",
92
97
  input: i.Struct({
93
98
  contentType: i.String,
94
- visibility: i.optional(i.Literal("public", "private")),
99
+ visibility: i.optional(i.Literals(["public", "private"])),
100
+ access: i.optional(a),
95
101
  folder: i.optional(i.String),
96
102
  tags: i.optional(i.Array(i.String)),
97
103
  alt: i.optional(i.String),
@@ -105,7 +111,7 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
105
111
  }),
106
112
  error: t,
107
113
  openAccess: "self-service upload: the finalize token binds the calling subject + tenant; finalizeUpload refuses another subject's token and runs limits + scan; an unidentified caller is refused in-handler unless the app set `anonymousUploads`"
108
- }), _ = a({
114
+ }), v = o({
109
115
  name: "storage.finalizeUpload",
110
116
  input: i.Struct({ finalizeToken: i.String }),
111
117
  output: i.Struct({
@@ -116,14 +122,19 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
116
122
  width: i.optional(i.Number),
117
123
  height: i.optional(i.Number)
118
124
  }),
119
- error: i.Union(t, n, r),
125
+ error: i.Union([
126
+ t,
127
+ n,
128
+ r
129
+ ]),
120
130
  openAccess: "authorization is the signed finalize token itself: it binds the minting subject where there was one (403 for another caller; an anonymous ticket is bound by possession of the short-lived token alone) and the finalize runs limits + scan"
121
- }), v = a({
131
+ }), y = o({
122
132
  name: "storage.beginResumableUpload",
123
133
  input: i.Struct({
124
134
  contentType: i.String,
125
135
  size: i.Number,
126
- visibility: i.optional(i.Literal("public", "private")),
136
+ visibility: i.optional(i.Literals(["public", "private"])),
137
+ access: i.optional(a),
127
138
  folder: i.optional(i.String),
128
139
  tags: i.optional(i.Array(i.String)),
129
140
  alt: i.optional(i.String),
@@ -140,12 +151,13 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
140
151
  }),
141
152
  error: t,
142
153
  openAccess: "self-service upload: the resumable token binds the calling subject + tenant and the assembled object runs limits + scan; an unidentified caller is refused in-handler unless the app set `anonymousUploads`"
143
- }), y = a({
154
+ }), b = o({
144
155
  name: "storage.beginMultipartUpload",
145
156
  input: i.Struct({
146
157
  contentType: i.String,
147
158
  size: i.Number,
148
- visibility: i.optional(i.Literal("public", "private")),
159
+ visibility: i.optional(i.Literals(["public", "private"])),
160
+ access: i.optional(a),
149
161
  folder: i.optional(i.String),
150
162
  tags: i.optional(i.Array(i.String)),
151
163
  alt: i.optional(i.String),
@@ -160,7 +172,7 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
160
172
  }),
161
173
  error: t,
162
174
  openAccess: "self-service upload: the multipart token binds the calling subject + tenant; completion refuses another subject's token; an unidentified caller is refused in-handler unless the app set `anonymousUploads`"
163
- }), b = a({
175
+ }), x = o({
164
176
  name: "storage.signMultipartPart",
165
177
  input: i.Struct({
166
178
  multipartToken: i.String,
@@ -169,7 +181,7 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
169
181
  output: i.Struct({ url: i.String }),
170
182
  error: t,
171
183
  openAccess: "authorization is the signed multipart token: parts can only be signed for a session a subject legitimately began"
172
- }), x = a({
184
+ }), S = o({
173
185
  name: "storage.completeMultipartUpload",
174
186
  input: i.Struct({
175
187
  multipartToken: i.String,
@@ -183,20 +195,21 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
183
195
  url: i.String,
184
196
  size: i.Number
185
197
  }),
186
- error: i.Union(t, n),
198
+ error: i.Union([t, n]),
187
199
  openAccess: "authorization is the signed multipart token: completion refuses a token minted by another subject, and where there was none the binding is possession of the short-lived token alone"
188
- }), S = a({
200
+ }), C = o({
189
201
  name: "storage.abortMultipartUpload",
190
202
  input: i.Struct({ multipartToken: i.String }),
191
203
  output: i.Struct({ ok: i.Boolean }),
192
204
  error: t,
193
205
  openAccess: "authorization is the signed multipart token: aborting releases the caller's own upload session"
194
- }), C = a({
206
+ }), w = o({
195
207
  name: "storage.upload",
196
208
  input: i.Struct({
197
209
  bytesBase64: i.String,
198
210
  contentType: i.String,
199
- visibility: i.optional(i.Literal("public", "private")),
211
+ visibility: i.optional(i.Literals(["public", "private"])),
212
+ access: i.optional(a),
200
213
  key: i.optional(i.String),
201
214
  password: i.optional(i.String)
202
215
  }),
@@ -204,13 +217,17 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
204
217
  id: i.String,
205
218
  url: i.String
206
219
  }),
207
- error: i.Union(t, n, r),
220
+ error: i.Union([
221
+ t,
222
+ n,
223
+ r
224
+ ]),
208
225
  openAccess: "self-service upload: ownerId/tenant come from the calling subject and the put runs limits + scan; an unidentified caller is refused in-handler unless the app set `anonymousUploads`"
209
- }), w = a({
226
+ }), T = o({
210
227
  name: "storage.ingestUrl",
211
228
  input: i.Struct({
212
229
  url: i.String,
213
- visibility: i.optional(i.Literal("public", "private")),
230
+ visibility: i.optional(i.Literals(["public", "private"])),
214
231
  folder: i.optional(i.String),
215
232
  tags: i.optional(i.Array(i.String)),
216
233
  alt: i.optional(i.String),
@@ -221,33 +238,37 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
221
238
  id: i.String,
222
239
  url: i.String
223
240
  }),
224
- error: i.Union(t, n, r),
241
+ error: i.Union([
242
+ t,
243
+ n,
244
+ r
245
+ ]),
225
246
  guards: [{ scope: "storage:manage" }]
226
- }), T = o({
247
+ }), E = s({
227
248
  name: "storage.share",
228
249
  input: i.Struct({
229
250
  refId: i.String,
230
- principalType: c,
251
+ principalType: l,
231
252
  principalId: i.String,
232
- permission: i.optional(l),
253
+ permission: i.optional(u),
233
254
  expiresInDays: i.optional(i.Number)
234
255
  }),
235
256
  output: i.Struct({ grantId: i.String }),
236
257
  error: t,
237
258
  openAccess: "owner-or-admin enforced in-handler (assertMayAdministerGrants): only the ref's owner or admin:full may grant, which is stricter per object than any global scope"
238
- }), E = o({
259
+ }), D = s({
239
260
  name: "storage.revoke",
240
261
  input: i.Struct({ grantId: i.String }),
241
262
  output: i.Struct({ ok: i.Boolean }),
242
263
  error: t,
243
264
  openAccess: "owner-or-admin enforced in-handler: only the owning subject (or admin:full) of the grant's object may revoke; unknown and unowned grants report the same 403"
244
- }), D = s({
265
+ }), O = c({
245
266
  name: "storage.listGrants",
246
267
  input: i.Struct({ refId: i.String }),
247
- output: i.Struct({ grants: i.Array(u) }),
268
+ output: i.Struct({ grants: i.Array(d) }),
248
269
  error: t,
249
270
  openAccess: "owner-or-admin enforced in-handler: who an object is shared with is readable only by the object's owner or admin:full"
250
- }), O = [
271
+ }), k = [
251
272
  {
252
273
  tag: "storage.mintUrl",
253
274
  kind: "action",
@@ -378,4 +399,4 @@ var c = i.Literal("user", "group", "apiKey"), l = i.Literal("read", "write"), u
378
399
  }
379
400
  ];
380
401
  //#endregion
381
- export { S as abortMultipartUploadDescriptor, y as beginMultipartUploadDescriptor, v as beginResumableUploadDescriptor, x as completeMultipartUploadDescriptor, _ as finalizeUploadDescriptor, w as ingestUrlDescriptor, D as listGrantsDescriptor, f as listRefsDescriptor, g as mintPresignedUploadDescriptor, h as mintUploadTicketDescriptor, m as mintUploadUrlDescriptor, p as mintUrlDescriptor, E as revokeDescriptor, T as shareDescriptor, b as signMultipartPartDescriptor, O as storageRpcClientImports, C as uploadDescriptor };
402
+ export { C as abortMultipartUploadDescriptor, b as beginMultipartUploadDescriptor, y as beginResumableUploadDescriptor, S as completeMultipartUploadDescriptor, v as finalizeUploadDescriptor, T as ingestUrlDescriptor, O as listGrantsDescriptor, p as listRefsDescriptor, _ as mintPresignedUploadDescriptor, g as mintUploadTicketDescriptor, h as mintUploadUrlDescriptor, m as mintUrlDescriptor, D as revokeDescriptor, E as shareDescriptor, x as signMultipartPartDescriptor, k as storageRpcClientImports, w as uploadDescriptor };
package/dist/types.d.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  import { Context } from 'effect';
2
2
  import { Effect } from 'effect';
3
3
  import { Schema } from 'effect';
4
+ import { StorageAccessPolicy } from '@voltro/protocol';
5
+ import { StorageAccessRule } from '@voltro/protocol';
4
6
  import { Stream } from 'effect';
5
7
  import { Subject } from '@voltro/protocol';
8
+ import { YieldableError } from 'effect/Cause';
6
9
 
7
- export declare type AccessPolicy = ReadonlyArray<AccessRule>;
10
+ export declare type AccessPolicy = StorageAccessPolicy;
8
11
 
9
12
  /**
10
13
  * One access rule. A rule GRANTS when EVERY present condition is satisfied
@@ -12,24 +15,7 @@ export declare type AccessPolicy = ReadonlyArray<AccessRule>;
12
15
  * grants (OR). An empty policy on a private object means owner-only (the
13
16
  * implicit owner check always applies).
14
17
  */
15
- export declare interface AccessRule {
16
- /** `subject.id === ref.ownerId`. */
17
- readonly owner?: boolean;
18
- /** `subject.metadata.roles` intersects these. */
19
- readonly roles?: ReadonlyArray<string>;
20
- /** resolved groups (default `subject.metadata.groups`) intersect these. */
21
- readonly groups?: ReadonlyArray<string>;
22
- /** `subject.scopes` intersects these. */
23
- readonly scopes?: ReadonlyArray<string>;
24
- /** `subject.tenantId === ref.tenantId`. */
25
- readonly tenant?: boolean;
26
- /** subject is any authenticated api-key. */
27
- readonly apiKey?: boolean;
28
- /** a correct password was supplied (checked at mint, against passwordHash). */
29
- readonly password?: boolean;
30
- /** a named custom guard (`options.guards[name]`) returns true. */
31
- readonly guard?: string;
32
- }
18
+ export declare type AccessRule = StorageAccessRule;
33
19
 
34
20
  /**
35
21
  * A lazy stream of bytes — the streaming counterpart to {@link StoredObject}'s
@@ -178,12 +164,10 @@ export declare interface Rendition {
178
164
  export declare class StorageAccessDenied extends StorageAccessDenied_base {
179
165
  }
180
166
 
181
- declare const StorageAccessDenied_base: Schema.TaggedErrorClass<StorageAccessDenied, "StorageAccessDenied", {
182
- readonly _tag: Schema.tag<"StorageAccessDenied">;
183
- } & {
184
- refId: typeof Schema.String;
185
- reason: typeof Schema.String;
186
- }>;
167
+ declare const StorageAccessDenied_base: Schema.Class<StorageAccessDenied, Schema.TaggedStruct<"StorageAccessDenied", {
168
+ readonly refId: Schema.String;
169
+ readonly reason: Schema.String;
170
+ }>, YieldableError>;
187
171
 
188
172
  /**
189
173
  * Storage failure. `transient` drives retry — `true` for 429 / 5xx /
@@ -193,14 +177,12 @@ declare const StorageAccessDenied_base: Schema.TaggedErrorClass<StorageAccessDen
193
177
  export declare class StorageError extends StorageError_base {
194
178
  }
195
179
 
196
- declare const StorageError_base: Schema.TaggedErrorClass<StorageError, "StorageError", {
197
- readonly _tag: Schema.tag<"StorageError">;
198
- } & {
199
- provider: typeof Schema.String;
200
- message: typeof Schema.String;
201
- transient: typeof Schema.Boolean;
202
- status: Schema.optional<typeof Schema.Number>;
203
- }>;
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>;
204
186
 
205
187
  /** A persisted, explicit share of one object with one principal. */
206
188
  export declare interface StorageGrant {
@@ -356,15 +338,14 @@ export declare interface StorageRef {
356
338
  /** An upload was rejected by a constraint (size / content-type / magic-byte
357
339
  * mismatch). `reason` is a stable code; `detail` is human-readable. */
358
340
  export declare class StorageRejected extends StorageRejected_base {
341
+ get message(): string;
359
342
  }
360
343
 
361
- declare const StorageRejected_base: Schema.TaggedErrorClass<StorageRejected, "StorageRejected", {
362
- readonly _tag: Schema.tag<"StorageRejected">;
363
- } & {
344
+ declare const StorageRejected_base: Schema.Class<StorageRejected, Schema.TaggedStruct<"StorageRejected", {
364
345
  /** `'too-large' | 'content-type-not-allowed' | 'content-mismatch'` */
365
- reason: typeof Schema.String;
366
- detail: typeof Schema.String;
367
- }>;
346
+ readonly reason: Schema.String;
347
+ readonly detail: Schema.String;
348
+ }>, YieldableError>;
368
349
 
369
350
  /** Scans bytes before they're stored. Return `{ clean:false, threat }` to
370
351
  * reject the upload (fails with `StorageScanRejected`, nothing is stored). */
@@ -378,11 +359,9 @@ export declare type StorageScanner = (input: {
378
359
  export declare class StorageScanRejected extends StorageScanRejected_base {
379
360
  }
380
361
 
381
- declare const StorageScanRejected_base: Schema.TaggedErrorClass<StorageScanRejected, "StorageScanRejected", {
382
- readonly _tag: Schema.tag<"StorageScanRejected">;
383
- } & {
384
- threat: typeof Schema.String;
385
- }>;
362
+ declare const StorageScanRejected_base: Schema.Class<StorageScanRejected, Schema.TaggedStruct<"StorageScanRejected", {
363
+ readonly threat: Schema.String;
364
+ }>, YieldableError>;
386
365
 
387
366
  export declare interface StorageScanResult {
388
367
  readonly clean: boolean;
@@ -393,7 +372,7 @@ export declare interface StorageScanResult {
393
372
  export declare class StorageService extends StorageService_base {
394
373
  }
395
374
 
396
- declare const StorageService_base: Context.TagClass<StorageService, "@voltro/plugin-storage/StorageService", StorageServiceShape>;
375
+ declare const StorageService_base: Context.ServiceClass<StorageService, "@voltro/plugin-storage/StorageService", StorageServiceShape>;
397
376
 
398
377
  /** The service handlers consume: `const storage = yield* StorageService`. */
399
378
  export declare interface StorageServiceShape {
@@ -583,6 +562,7 @@ export declare interface StorageServiceShape {
583
562
  readonly tenantId?: string | null;
584
563
  readonly ownerId?: string | null;
585
564
  readonly visibility?: StorageVisibility;
565
+ readonly access?: AccessPolicy;
586
566
  readonly folder?: string;
587
567
  readonly tags?: ReadonlyArray<string>;
588
568
  readonly alt?: string;
@@ -616,6 +596,7 @@ export declare interface StorageServiceShape {
616
596
  readonly tenantId?: string | null;
617
597
  readonly ownerId?: string | null;
618
598
  readonly visibility?: StorageVisibility;
599
+ readonly access?: AccessPolicy;
619
600
  readonly folder?: string;
620
601
  readonly tags?: ReadonlyArray<string>;
621
602
  readonly alt?: string;
@@ -665,6 +646,7 @@ export declare interface StorageServiceShape {
665
646
  readonly tenantId?: string | null;
666
647
  readonly ownerId?: string | null;
667
648
  readonly visibility?: StorageVisibility;
649
+ readonly access?: AccessPolicy;
668
650
  readonly folder?: string;
669
651
  readonly tags?: ReadonlyArray<string>;
670
652
  readonly alt?: string;
package/dist/types.js CHANGED
@@ -11,6 +11,10 @@ var n = class extends t.TaggedError()("StorageError", {
11
11
  }) {}, i = class extends t.TaggedError()("StorageRejected", {
12
12
  reason: t.String,
13
13
  detail: t.String
14
- }) {}, a = class extends t.TaggedError()("StorageScanRejected", { threat: t.String }) {}, o = class extends e.Tag("@voltro/plugin-storage/StorageService")() {};
14
+ }) {
15
+ get message() {
16
+ return this.detail;
17
+ }
18
+ }, a = class extends t.TaggedError()("StorageScanRejected", { threat: t.String }) {}, o = class extends e.Service()("@voltro/plugin-storage/StorageService") {};
15
19
  //#endregion
16
20
  export { r as StorageAccessDenied, n as StorageError, i as StorageRejected, a as StorageScanRejected, o as StorageService };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-storage",
3
- "version": "0.84.0",
3
+ "version": "0.85.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",
@@ -43,10 +43,10 @@
43
43
  "node": ">=24.0.0"
44
44
  },
45
45
  "dependencies": {
46
- "@voltro/database": "0.84.0",
47
- "@voltro/env": "0.84.0",
48
- "@voltro/protocol": "0.84.0",
49
- "@voltro/runtime": "0.84.0"
46
+ "@voltro/database": "0.85.0",
47
+ "@voltro/env": "0.85.0",
48
+ "@voltro/protocol": "0.85.0",
49
+ "@voltro/runtime": "0.85.0"
50
50
  },
51
51
  "optionalDependencies": {
52
52
  "@aws-sdk/client-s3": "^3.1134.0",
@@ -56,7 +56,7 @@
56
56
  "sharp": "^0.35.5"
57
57
  },
58
58
  "peerDependencies": {
59
- "effect": "^3.22.2"
59
+ "effect": "4.0.1"
60
60
  },
61
61
  "publishConfig": {
62
62
  "access": "public"