@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.
- package/CHANGELOG.md +5987 -0
- package/README.md +77 -0
- package/THIRD-PARTY-NOTICES.md +860 -249
- package/dist/access.js +47 -0
- package/dist/assetRef.js +13 -0
- package/dist/avatar.js +39 -0
- package/dist/byteSecurity.d.ts +3 -0
- package/dist/byteSecurity.js +7 -0
- package/dist/connection.js +99 -0
- package/dist/constraints.js +93 -0
- package/dist/custody.js +132 -0
- package/dist/derivatives.js +30 -0
- package/dist/env.js +162 -0
- package/dist/grant.js +132 -0
- package/dist/index.d.ts +525 -203
- package/dist/index.js +765 -3196
- package/dist/ingest.js +87 -0
- package/dist/ingestPolicy.js +1 -0
- package/dist/metadataError.js +19 -0
- package/dist/objectAbandon.js +16 -0
- package/dist/objectLifecycle.js +243 -0
- package/dist/objectMetadata.js +308 -0
- package/dist/objectWriter.js +87 -0
- package/dist/pathDurability.js +16 -0
- package/dist/physicalReferences.js +54 -0
- package/dist/providers.js +638 -0
- package/dist/referenceCatalogue.js +31 -0
- package/dist/renditionProof.js +82 -0
- package/dist/retainedCapture.js +391 -0
- package/dist/retainedObjects.js +190 -0
- package/dist/rpc.d.ts +3 -15
- package/dist/rpc.js +3 -1
- package/dist/scanners.js +46 -0
- package/dist/server.d.ts +153 -0
- package/dist/server.js +4 -0
- package/dist/service.js +1418 -0
- package/dist/sourceIdentity.js +14 -0
- package/dist/sourcePin.js +35 -0
- package/dist/storageNamespaces.js +80 -0
- package/dist/storageNumbers.js +13 -0
- package/dist/storageObjectNamespaces.js +243 -0
- package/dist/storageReferenceInstances.js +52 -0
- package/dist/storageRequest.js +10 -0
- package/dist/storageRouting.js +46 -0
- package/dist/streamBridge.js +33 -0
- package/dist/table.js +54 -0
- package/dist/transcodeVideo.js +91 -0
- package/dist/transform.js +94 -0
- package/dist/transformCache.js +31 -0
- package/dist/types.d.ts +116 -134
- package/dist/types.js +14 -14
- 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 {
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
},
|
|
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 {
|
|
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.
|
|
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.
|
|
47
|
-
"@voltro/env": "0.
|
|
48
|
-
"@voltro/
|
|
49
|
-
"@voltro/
|
|
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",
|