@zeruxjs/files 0.0.0-stage → 0.0.1

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/http.js ADDED
@@ -0,0 +1,157 @@
1
+ import { chunks, validateAddress } from './validation.js';
2
+ // eslint-disable-next-line no-control-regex -- Intentionally matches control characters for validation or output sanitization.
3
+ function filename(value) { const safe = value.replace(/[\x00-\x1f\x7f"\\/]/g, '_').slice(0, 180) || 'file'; return `attachment; filename="${safe}"; filename*=UTF-8''${encodeURIComponent(safe)}`; }
4
+ function rangeFrom(header, size) {
5
+ const found = /^bytes=(\d*)-(\d*)$/.exec(header.trim());
6
+ if (!found || (!found[1] && !found[2]) || size === 0)
7
+ return null;
8
+ const start = found[1] ? Number(found[1]) : Math.max(0, size - Number(found[2]));
9
+ const end = found[1] ? (found[2] ? Number(found[2]) : size - 1) : size - 1;
10
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || start >= size || end < start)
11
+ return null;
12
+ return { start, end: Math.min(end, size - 1) };
13
+ }
14
+ function iterableBody(iterator) {
15
+ const reader = iterator[Symbol.asyncIterator]();
16
+ return new ReadableStream({ async pull(c) { try {
17
+ const next = await reader.next();
18
+ if (next.done)
19
+ c.close();
20
+ else
21
+ c.enqueue(next.value);
22
+ }
23
+ catch (err) {
24
+ c.error(err);
25
+ } }, async cancel() { await reader.return?.(); } });
26
+ }
27
+ export async function createFileResponse(manager, address, request, options = {}) {
28
+ validateAddress(address);
29
+ if (!['GET', 'HEAD'].includes(request.method))
30
+ return new Response(null, { status: 405, headers: { Allow: 'GET, HEAD' } });
31
+ const info = await manager.stat(address);
32
+ if (!info)
33
+ return new Response('Not found', { status: 404 });
34
+ if (info.visibility === 'private' && !options.authorize)
35
+ return new Response('Forbidden', { status: 403 });
36
+ if (options.authorize && !await options.authorize(request, address, 'read', info))
37
+ return new Response('Forbidden', { status: 403 });
38
+ const safeInline = /^(?:image\/(?:png|jpeg|gif|webp|avif)|audio\/[A-Za-z0-9.+-]+|video\/[A-Za-z0-9.+-]+|application\/pdf|text\/plain)$/.test(info.contentType.toLowerCase());
39
+ const disposition = options.inline && safeInline ? 'inline; ' + filename(options.downloadName ?? address.key.split('/').at(-1)) : info.contentDisposition?.toLowerCase().startsWith('attachment') ? info.contentDisposition : filename(options.downloadName ?? address.key.split('/').at(-1));
40
+ const headers = new Headers({ 'Content-Type': info.contentType, 'ETag': info.etag, 'Last-Modified': new Date(info.modifiedAt).toUTCString(), 'X-Content-Type-Options': 'nosniff', 'Cache-Control': info.visibility === 'private' ? 'private, no-store' : options.cacheControl ?? info.cacheControl ?? 'public, max-age=60', 'Content-Disposition': disposition, 'Accept-Ranges': info.rangeSupported === false ? 'none' : 'bytes' });
41
+ const match = request.headers.get('if-none-match');
42
+ if (match && match.split(',').some(item => item.trim() === info.etag || item.trim() === '*'))
43
+ return new Response(null, { status: 304, headers });
44
+ const modified = request.headers.get('if-modified-since');
45
+ if (!match && modified && Number.isFinite(Date.parse(modified)) && Date.parse(modified) >= new Date(info.modifiedAt).setMilliseconds(0))
46
+ return new Response(null, { status: 304, headers });
47
+ const rangeHeader = request.headers.get('range');
48
+ const ifRange = request.headers.get('if-range');
49
+ const useRange = info.rangeSupported !== false && rangeHeader && (!ifRange || ifRange === info.etag || (Number.isFinite(Date.parse(ifRange)) && Date.parse(ifRange) >= Date.parse(info.modifiedAt)));
50
+ const range = useRange ? rangeFrom(rangeHeader, info.size) : undefined;
51
+ if (useRange && !range) {
52
+ headers.set('Content-Range', `bytes */${info.size}`);
53
+ return new Response(null, { status: 416, headers });
54
+ }
55
+ const start = range?.start ?? 0, end = range?.end ?? Math.max(0, info.size - 1);
56
+ const status = range ? 206 : 200;
57
+ headers.set('Content-Length', String(range ? end - start + 1 : info.size));
58
+ if (range)
59
+ headers.set('Content-Range', `bytes ${start}-${end}/${info.size}`);
60
+ if (request.method === 'HEAD')
61
+ return new Response(null, { status, headers });
62
+ const iterable = await manager.read(address, range ? { start, end, signal: request.signal } : { signal: request.signal });
63
+ return new Response(iterableBody(iterable), { status, headers });
64
+ }
65
+ /** Binary request bodies, no multipart parsing; authentication belongs to the route. */
66
+ export async function uploadFileRequest(manager, address, request, options = {}) {
67
+ if (!['PUT', 'POST'].includes(request.method) || !request.body)
68
+ throw new TypeError('PUT/POST body required');
69
+ if (options.authorize && !await options.authorize(request, address, 'write'))
70
+ throw Object.assign(new Error('Forbidden'), { code: 'FORBIDDEN' });
71
+ // Require explicit authorization when this helper is used as a public HTTP endpoint.
72
+ const encoded = request.headers.get('content-encoding');
73
+ if (encoded && encoded !== 'identity')
74
+ throw new TypeError('Encoded upload unsupported');
75
+ const length = request.headers.get('content-length');
76
+ if (length && (!/^\d+$/.test(length) || !Number.isSafeInteger(Number(length))))
77
+ throw new RangeError('Invalid Content-Length');
78
+ if (options.ifNoneMatch === false)
79
+ throw new TypeError('HTTP upload helper requires create-only semantics');
80
+ const max = options.maxBytes ?? 16 * 1024 * 1024;
81
+ if (!Number.isSafeInteger(max) || max < 1)
82
+ throw new RangeError('Invalid upload limit');
83
+ if (length && Number(length) > max)
84
+ throw new RangeError('File exceeds upload limit');
85
+ const contentType = options.contentType ?? request.headers.get('content-type')?.split(';')[0].trim() ?? 'application/octet-stream';
86
+ // Validate the declared length *during* streaming, before a storage driver
87
+ // commits an object. Deleting after a completed upload is unsafe: another
88
+ // writer could have replaced that key in the meantime.
89
+ const declared = length === null ? undefined : Number(length);
90
+ async function* verifiedBody() {
91
+ let bytes = 0;
92
+ for await (const part of chunks(request.body, request.signal)) {
93
+ bytes += part.byteLength;
94
+ if (!Number.isSafeInteger(bytes) || bytes > max)
95
+ throw new RangeError('File exceeds upload limit');
96
+ if (declared !== undefined && bytes > declared)
97
+ throw new RangeError('Upload length mismatch');
98
+ yield part;
99
+ }
100
+ if (declared !== undefined && bytes !== declared)
101
+ throw new RangeError('Upload length mismatch');
102
+ }
103
+ return manager.put(address, verifiedBody(), { ...options, contentType, maxBytes: max, signal: request.signal });
104
+ }
105
+ export function createFileHttpHandler(manager, options) {
106
+ if (!options?.bucket)
107
+ throw new TypeError('Fixed bucket required');
108
+ const prefix = options.prefix ?? '';
109
+ return async (request) => {
110
+ let key;
111
+ try {
112
+ key = decodeURIComponent(new URL(request.url).pathname.replace(/^\/+/, ''));
113
+ if (prefix)
114
+ key = prefix + key;
115
+ validateAddress({ storage: options.storage, bucket: options.bucket, key });
116
+ }
117
+ catch {
118
+ return new Response('Invalid object key', { status: 400 });
119
+ }
120
+ const address = { storage: options.storage, bucket: options.bucket, key };
121
+ try {
122
+ if (['GET', 'HEAD'].includes(request.method))
123
+ return createFileResponse(manager, address, request, { authorize: options.authorize, cacheControl: options.cacheControl });
124
+ if (['POST', 'PUT'].includes(request.method)) {
125
+ // HTTP writes always require an auth callback; private-by-default.
126
+ if (!options.authorize || !await options.authorize(request, address, 'write'))
127
+ return new Response('Forbidden', { status: 403 });
128
+ const info = await uploadFileRequest(manager, address, request, { maxBytes: options.maxUploadBytes, contentType: request.headers.get('content-type')?.split(';')[0].trim() || 'application/octet-stream' });
129
+ return Response.json(info, { status: 201, headers: { 'Cache-Control': 'no-store' } });
130
+ }
131
+ if (request.method === 'DELETE' && options.allowDelete) {
132
+ if (!options.authorize || !await options.authorize(request, address, 'delete'))
133
+ return new Response('Forbidden', { status: 403 });
134
+ await manager.delete(address);
135
+ return new Response(null, { status: 204 });
136
+ }
137
+ return new Response(null, { status: 405, headers: { Allow: options.allowDelete ? 'GET, HEAD, POST, PUT, DELETE' : 'GET, HEAD, POST, PUT' } });
138
+ }
139
+ catch (err) {
140
+ const code = err.code;
141
+ if (code === 'FILE_EXISTS')
142
+ return new Response('File exists', { status: 409 });
143
+ if (code === 'ENOENT')
144
+ return new Response('Not found', { status: 404 });
145
+ if (code === 'FILE_EXPIRED')
146
+ return new Response('File expired', { status: 410 });
147
+ if (code === 'FORBIDDEN')
148
+ return new Response('Forbidden', { status: 403 });
149
+ if (err instanceof RangeError)
150
+ return new Response('File too large or invalid range', { status: 413 });
151
+ if (err instanceof TypeError)
152
+ return new Response('Invalid file request', { status: 400 });
153
+ throw err;
154
+ }
155
+ };
156
+ }
157
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export * from './types.js';
2
+ export * from './validation.js';
3
+ export * from './cache.js';
4
+ export * from './manager.js';
5
+ export * from './drivers/local.js';
6
+ export * from './drivers/s3.js';
7
+ export * from './http.js';
8
+ export * from './aws-credentials.js';
9
+ export * from './authorization.js';
10
+ export * from './client.js';
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export * from './types.js';
2
+ export * from './validation.js';
3
+ export * from './cache.js';
4
+ export * from './manager.js';
5
+ export * from './drivers/local.js';
6
+ export * from './drivers/s3.js';
7
+ export * from './http.js';
8
+ export * from './aws-credentials.js';
9
+ export * from './authorization.js';
10
+ export * from './client.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { StorageManager, StorageManagerOptions } from './types.js';
2
+ export declare function createFileManager(options: StorageManagerOptions): StorageManager;
@@ -0,0 +1,171 @@
1
+ import { chunks, validateAddress, validateBucket, validateKey, validatePut, validateRange, ensureNotExpired } from './validation.js';
2
+ import { FileByteCache } from './cache.js';
3
+ export function createFileManager(options) {
4
+ if (!options?.stores?.length)
5
+ throw new TypeError('At least one storage connection required');
6
+ const stores = new Map();
7
+ for (const store of options.stores) {
8
+ if (!/^[a-z][a-z0-9_-]{0,63}$/.test(store.name) || stores.has(store.name) || !store.driver)
9
+ throw new TypeError('Invalid or duplicate storage connection');
10
+ if (store.allowedBuckets && (!store.allowedBuckets.length || store.allowedBuckets.some(b => { try {
11
+ validateBucket(b);
12
+ return false;
13
+ }
14
+ catch {
15
+ return true;
16
+ } })))
17
+ throw new TypeError('Invalid bucket policy');
18
+ if (store.keyPrefix !== undefined && store.keyPrefix !== '' && !store.keyPrefix.endsWith('/'))
19
+ throw new TypeError('Key prefix must end with /');
20
+ stores.set(store.name, store);
21
+ }
22
+ const fallback = options.defaultStorage ?? options.stores[0].name;
23
+ if (!stores.has(fallback))
24
+ throw new TypeError('Unknown default storage');
25
+ const cache = options.cache ? new FileByteCache(options.cache.maxEntries, options.cache.maxBytes, options.cache.ttlMs) : null;
26
+ let closed = false;
27
+ function resolve(address) {
28
+ if (closed)
29
+ throw new Error('File manager closed');
30
+ validateAddress(address);
31
+ const name = address.storage ?? fallback;
32
+ const connection = stores.get(name);
33
+ if (!connection)
34
+ throw new Error(`Unknown storage: ${name}`);
35
+ if (connection.allowedBuckets && !connection.allowedBuckets.includes(address.bucket))
36
+ throw new Error('Bucket not allowed by storage policy');
37
+ if (connection.keyPrefix && !address.key.startsWith(connection.keyPrefix))
38
+ throw new Error('Key not allowed by storage policy');
39
+ return { name, connection, address: { ...address, storage: name } };
40
+ }
41
+ const cacheKey = (a) => `${a.storage ?? fallback}\0${a.bucket}\0${a.key}`;
42
+ const invalidate = (a) => a ? cache?.deletePrefix(cacheKey(a) + ':') : cache?.clear();
43
+ const manager = {
44
+ getDriver(name) { if (closed)
45
+ throw new Error('File manager closed'); const result = stores.get(name ?? fallback); if (!result)
46
+ throw new Error('Unknown storage'); return result.driver; },
47
+ async put(address, data, opts = {}) {
48
+ const { name, connection, address: a } = resolve(address);
49
+ validatePut(opts);
50
+ if (connection.readonly)
51
+ throw new Error('Storage is read-only');
52
+ if (connection.allowedTypes?.length && !connection.allowedTypes.some(pattern => pattern.endsWith('/*') ? opts.contentType?.startsWith(pattern.slice(0, -1)) : opts.contentType === pattern))
53
+ throw new Error('Content type not allowed');
54
+ const max = Math.min(opts.maxBytes ?? Number.MAX_SAFE_INTEGER, connection.maxBytes ?? Number.MAX_SAFE_INTEGER);
55
+ if (!Number.isSafeInteger(max) || max < 1)
56
+ throw new RangeError('Invalid storage size limit');
57
+ async function* bounded() { let count = 0; for await (const part of chunks(data, opts.signal)) {
58
+ count += part.byteLength;
59
+ if (!Number.isSafeInteger(count) || count > max)
60
+ throw new RangeError('File size exceeds limit');
61
+ yield part;
62
+ } }
63
+ const stored = await connection.driver.put(a, bounded(), { ...opts, maxBytes: max });
64
+ invalidate(a);
65
+ return { ...stored, storage: name };
66
+ },
67
+ async stat(address) { const { name, connection, address: a } = resolve(address); const info = await connection.driver.stat(a); if (!info)
68
+ return null; ensureNotExpired(info); return { ...info, storage: name }; },
69
+ async read(address, opts = {}) { validateRange(opts); const { connection, address: a } = resolve(address); const info = await manager.stat(a); if (!info)
70
+ throw Object.assign(new Error('File not found'), { code: 'ENOENT' }); return connection.driver.read(a, opts); },
71
+ async get(address, maxBytes = 16 * 1024 * 1024) {
72
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1)
73
+ throw new RangeError('Invalid read limit');
74
+ const info = await manager.stat(address);
75
+ if (!info)
76
+ throw Object.assign(new Error('File not found'), { code: 'ENOENT' });
77
+ if (info.size > maxBytes)
78
+ throw new RangeError('File exceeds read limit');
79
+ const key = cacheKey(address) + ':' + info.etag;
80
+ if (cache && info.visibility === 'public' && !info.encryption) {
81
+ const found = cache.get(key);
82
+ if (found)
83
+ return found;
84
+ }
85
+ const iterable = await manager.read(address);
86
+ let size = 0;
87
+ const parts = [];
88
+ for await (const part of iterable) {
89
+ size += part.byteLength;
90
+ if (size > maxBytes)
91
+ throw new RangeError('File exceeds read limit');
92
+ parts.push(part);
93
+ }
94
+ const result = Buffer.concat(parts, size);
95
+ if (size !== info.size)
96
+ throw new Error('File changed during read');
97
+ if (cache && info.visibility === 'public' && !info.encryption)
98
+ cache.set(key, result);
99
+ return result;
100
+ },
101
+ async exists(address) { return (await manager.stat(address)) !== null; },
102
+ async setVisibility(address, visibility) {
103
+ if (!['private', 'public'].includes(visibility))
104
+ throw new TypeError('Invalid visibility');
105
+ return manager.copy(address, address, { visibility, ifNoneMatch: false });
106
+ },
107
+ async move(from, to, opts = {}) {
108
+ if (cacheKey(from) === cacheKey(to))
109
+ throw new TypeError('Cannot move a file onto itself');
110
+ const result = await manager.copy(from, to, opts);
111
+ // Not an atomic transaction across stores: if source deletion fails,
112
+ // the copied destination is preserved and the error is propagated.
113
+ await manager.delete(from);
114
+ return result;
115
+ },
116
+ async delete(address) { const { connection, address: a } = resolve(address); if (connection.readonly)
117
+ throw new Error('Storage is read-only'); const result = await connection.driver.delete(a); invalidate(a); return result; },
118
+ async list(bucket, opts = {}) {
119
+ validateBucket(bucket);
120
+ if (closed)
121
+ throw new Error('File manager closed');
122
+ const name = opts.storage ?? fallback, connection = stores.get(name);
123
+ if (!connection)
124
+ throw new Error('Unknown storage');
125
+ if (connection.allowedBuckets && !connection.allowedBuckets.includes(bucket))
126
+ throw new Error('Bucket not allowed by storage policy');
127
+ if (opts.prefix)
128
+ validateKey(opts.prefix.endsWith('/') ? opts.prefix.slice(0, -1) : opts.prefix);
129
+ if (opts.limit !== undefined && (!Number.isSafeInteger(opts.limit) || opts.limit < 1 || opts.limit > 1000))
130
+ throw new RangeError('Invalid list limit');
131
+ if (connection.keyPrefix && opts.prefix && !opts.prefix.startsWith(connection.keyPrefix))
132
+ throw new Error('Prefix not allowed by storage policy');
133
+ const listed = await connection.driver.list(bucket, { ...opts, prefix: opts.prefix ?? connection.keyPrefix });
134
+ return { ...listed, items: listed.items.filter(i => (!i.expiresAt || Date.parse(i.expiresAt) > Date.now()) && (!connection.keyPrefix || i.key.startsWith(connection.keyPrefix))).map(i => ({ ...i, storage: name })) };
135
+ },
136
+ async copy(from, to, opts = {}) {
137
+ validatePut(opts);
138
+ const src = resolve(from), dst = resolve(to);
139
+ if (dst.connection.readonly)
140
+ throw new Error('Storage is read-only');
141
+ const info = await manager.stat(from);
142
+ if (!info)
143
+ throw Object.assign(new Error('File not found'), { code: 'ENOENT' });
144
+ // Native storage-side COPY must obey the same destination policies as
145
+ // PUT, even when no data is streamed through the manager.
146
+ const kind = opts.contentType ?? info.contentType;
147
+ if (dst.connection.allowedTypes?.length && !dst.connection.allowedTypes.some(pattern => pattern.endsWith('/*') ? kind.startsWith(pattern.slice(0, -1)) : kind === pattern))
148
+ throw new Error('Content type not allowed');
149
+ const max = Math.min(opts.maxBytes ?? Number.MAX_SAFE_INTEGER, dst.connection.maxBytes ?? Number.MAX_SAFE_INTEGER);
150
+ if (!Number.isSafeInteger(max) || max < 1 || info.size > max)
151
+ throw new RangeError('File size exceeds limit');
152
+ // Streaming fallback across different drivers; never materializes the full object.
153
+ if (src.name === dst.name && src.connection.driver.copy) {
154
+ const result = await src.connection.driver.copy(src.address, dst.address, opts);
155
+ invalidate(dst.address);
156
+ return result;
157
+ }
158
+ const stream = await manager.read(from);
159
+ return manager.put(to, stream, { contentType: info.contentType, visibility: info.visibility, cacheControl: info.cacheControl, metadata: info.metadata, ...opts, maxBytes: max });
160
+ },
161
+ async signedUrl(method, address, seconds, opts) { const { connection, address: a } = resolve(address); if (!Number.isSafeInteger(seconds) || seconds < 1 || seconds > 604800)
162
+ throw new RangeError('Invalid signing lifetime'); if (!connection.driver.signedUrl)
163
+ throw new Error('Signed URL unsupported by storage'); return connection.driver.signedUrl(method, a, seconds, opts); },
164
+ publicUrl(address) { const { connection, address: a } = resolve(address); return connection.driver.publicUrl?.(a); },
165
+ cacheStats() { return cache?.stats() ?? { entries: 0, bytes: 0, hits: 0, misses: 0 }; }, invalidate,
166
+ async close() { if (closed)
167
+ return; closed = true; invalidate(); await Promise.all([...new Set([...stores.values()].map(s => s.driver))].map(s => s.close?.())); }
168
+ };
169
+ return manager;
170
+ }
171
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ /** Storage-neutral binary objects; bucket and key are separate from the connection name. */
2
+ export interface FileAddress {
3
+ storage?: string;
4
+ bucket: string;
5
+ key: string;
6
+ }
7
+ export type FileVisibility = 'private' | 'public';
8
+ export interface FileInfo extends FileAddress {
9
+ size: number;
10
+ etag: string;
11
+ contentType: string;
12
+ modifiedAt: string;
13
+ visibility: FileVisibility;
14
+ cacheControl?: string;
15
+ contentDisposition?: string;
16
+ metadata: Record<string, string>;
17
+ versionId?: string;
18
+ expiresAt?: string;
19
+ encryption?: string;
20
+ /** Whether byte ranges are meaningful after storage-side decryption. */
21
+ rangeSupported?: boolean;
22
+ }
23
+ export type FileSource = Uint8Array | AsyncIterable<Uint8Array> | Iterable<Uint8Array> | ReadableStream<Uint8Array>;
24
+ export interface PutFileOptions {
25
+ contentType?: string;
26
+ contentDisposition?: string;
27
+ visibility?: FileVisibility;
28
+ cacheControl?: string;
29
+ metadata?: Record<string, string>;
30
+ maxBytes?: number;
31
+ /** Fail rather than overwrite an existing object. Defaults to true. */
32
+ ifNoneMatch?: boolean;
33
+ expiresAt?: string;
34
+ signal?: AbortSignal;
35
+ }
36
+ export interface ReadFileOptions {
37
+ start?: number;
38
+ end?: number;
39
+ signal?: AbortSignal;
40
+ }
41
+ export interface ListFileOptions {
42
+ prefix?: string;
43
+ limit?: number;
44
+ cursor?: string;
45
+ }
46
+ export interface FileListing {
47
+ items: FileInfo[];
48
+ nextCursor?: string;
49
+ }
50
+ export interface StorageDriver {
51
+ readonly kind: string;
52
+ put(address: FileAddress, data: FileSource, options?: PutFileOptions): Promise<FileInfo>;
53
+ stat(address: FileAddress): Promise<FileInfo | null>;
54
+ read(address: FileAddress, options?: ReadFileOptions): Promise<AsyncIterable<Uint8Array>>;
55
+ delete(address: FileAddress): Promise<boolean>;
56
+ list(bucket: string, options?: ListFileOptions): Promise<FileListing>;
57
+ copy?(from: FileAddress, to: FileAddress, options?: PutFileOptions): Promise<FileInfo>;
58
+ signedUrl?(method: 'GET' | 'PUT', address: FileAddress, expiresSeconds: number, options?: PutFileOptions): Promise<string>;
59
+ publicUrl?(address: FileAddress): string | undefined;
60
+ close?(): Promise<void>;
61
+ }
62
+ export interface StorageDefinition {
63
+ name: string;
64
+ driver: StorageDriver;
65
+ readonly?: boolean;
66
+ allowedTypes?: string[];
67
+ allowedBuckets?: string[];
68
+ keyPrefix?: string;
69
+ maxBytes?: number;
70
+ }
71
+ export interface StorageManagerOptions {
72
+ stores: StorageDefinition[];
73
+ defaultStorage?: string;
74
+ cache?: {
75
+ maxEntries?: number;
76
+ maxBytes?: number;
77
+ ttlMs?: number;
78
+ };
79
+ }
80
+ export interface StorageManager {
81
+ put(address: FileAddress, data: FileSource, options?: PutFileOptions): Promise<FileInfo>;
82
+ stat(address: FileAddress): Promise<FileInfo | null>;
83
+ read(address: FileAddress, options?: ReadFileOptions): Promise<AsyncIterable<Uint8Array>>;
84
+ get(address: FileAddress, maxBytes?: number): Promise<Uint8Array>;
85
+ exists(address: FileAddress): Promise<boolean>;
86
+ setVisibility(address: FileAddress, visibility: FileVisibility): Promise<FileInfo>;
87
+ move(from: FileAddress, to: FileAddress, options?: PutFileOptions): Promise<FileInfo>;
88
+ delete(address: FileAddress): Promise<boolean>;
89
+ list(bucket: string, options?: ListFileOptions & {
90
+ storage?: string;
91
+ }): Promise<FileListing>;
92
+ copy(from: FileAddress, to: FileAddress, options?: PutFileOptions): Promise<FileInfo>;
93
+ signedUrl(method: 'GET' | 'PUT', address: FileAddress, expiresSeconds: number, options?: PutFileOptions): Promise<string>;
94
+ publicUrl(address: FileAddress): string | undefined;
95
+ getDriver(storage?: string): StorageDriver;
96
+ cacheStats(): {
97
+ entries: number;
98
+ bytes: number;
99
+ hits: number;
100
+ misses: number;
101
+ };
102
+ invalidate(address?: FileAddress): void;
103
+ close(): Promise<void>;
104
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import type { FileAddress, FileInfo, FileSource, PutFileOptions, ReadFileOptions } from './types.js';
2
+ export declare function validateBucket(bucket: string): string;
3
+ export declare function validateKey(key: string): string;
4
+ export declare function validateAddress(address: FileAddress): FileAddress;
5
+ export declare function validatePut(options?: PutFileOptions): void;
6
+ export declare function validateRange(options?: ReadFileOptions): void;
7
+ export declare function chunks(source: FileSource, signal?: AbortSignal): AsyncGenerator<Uint8Array>;
8
+ export declare function expired(meta: FileInfo): boolean;
9
+ export declare function ensureNotExpired(meta: FileInfo): void;
@@ -0,0 +1,84 @@
1
+ export function validateBucket(bucket) {
2
+ if (typeof bucket !== 'string' || !/^[a-z0-9][a-z0-9._-]{1,61}[a-z0-9]$/.test(bucket) || bucket.includes('..'))
3
+ throw new TypeError('Invalid bucket name');
4
+ return bucket;
5
+ }
6
+ export function validateKey(key) {
7
+ // eslint-disable-next-line no-control-regex -- Intentionally matches control characters for validation or output sanitization.
8
+ if (typeof key !== 'string' || !key || key.length > 1024 || /[\\\x00-\x1f\x7f]/.test(key) || key.startsWith('/') || key.endsWith('/') || key.split('/').some(p => p === '' || p === '.' || p === '..'))
9
+ throw new TypeError('Unsafe file key');
10
+ return key;
11
+ }
12
+ export function validateAddress(address) {
13
+ if (!address || typeof address !== 'object')
14
+ throw new TypeError('File address required');
15
+ validateBucket(address.bucket);
16
+ validateKey(address.key);
17
+ if (address.storage !== undefined && !/^[a-z][a-z0-9_-]{0,63}$/.test(address.storage))
18
+ throw new TypeError('Invalid storage name');
19
+ return address;
20
+ }
21
+ export function validatePut(options = {}) {
22
+ if (options.maxBytes !== undefined && (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 1))
23
+ throw new RangeError('Invalid upload size');
24
+ if (options.contentType !== undefined && !/^[\w!#$&^.+-]+\/[\w!#$&^.+-]+$/.test(options.contentType))
25
+ throw new TypeError('Invalid content type');
26
+ if (options.visibility !== undefined && options.visibility !== 'public' && options.visibility !== 'private')
27
+ throw new TypeError('Invalid visibility');
28
+ // eslint-disable-next-line no-control-regex -- Intentionally matches control characters for validation or output sanitization.
29
+ for (const x of [options.cacheControl, options.contentDisposition])
30
+ if (x !== undefined && (x.length > 512 || /[\r\n\x00]/.test(x)))
31
+ throw new TypeError('Unsafe header value');
32
+ if (options.expiresAt !== undefined && (!Number.isFinite(Date.parse(options.expiresAt)) || Date.parse(options.expiresAt) <= Date.now()))
33
+ throw new RangeError('Invalid expiration');
34
+ const entries = Object.entries(options.metadata ?? {});
35
+ // eslint-disable-next-line no-control-regex -- Intentionally matches control characters for validation or output sanitization.
36
+ if (entries.length > 32 || entries.some(([k, v]) => k.startsWith('zfiles-') || !/^[a-zA-Z0-9_-]{1,64}$/.test(k) || typeof v !== 'string' || v.length > 1024 || /[\r\n\x00]/.test(v)))
37
+ throw new TypeError('Invalid metadata');
38
+ }
39
+ export function validateRange(options = {}) {
40
+ if (options.start !== undefined && (!Number.isSafeInteger(options.start) || options.start < 0))
41
+ throw new RangeError('Invalid start');
42
+ if (options.end !== undefined && (!Number.isSafeInteger(options.end) || options.end < (options.start ?? 0)))
43
+ throw new RangeError('Invalid end');
44
+ }
45
+ export async function* chunks(source, signal) {
46
+ if (source instanceof Uint8Array) {
47
+ if (signal?.aborted)
48
+ throw signal.reason;
49
+ yield source;
50
+ return;
51
+ }
52
+ if (source instanceof ReadableStream) {
53
+ const reader = source.getReader();
54
+ try {
55
+ while (true) {
56
+ if (signal?.aborted)
57
+ throw signal.reason;
58
+ const next = await reader.read();
59
+ if (next.done)
60
+ return;
61
+ if (!(next.value instanceof Uint8Array))
62
+ throw new TypeError('Invalid file chunk');
63
+ yield next.value;
64
+ }
65
+ }
66
+ finally {
67
+ await reader.cancel().catch(() => { });
68
+ reader.releaseLock();
69
+ }
70
+ }
71
+ else {
72
+ for await (const chunk of source) {
73
+ if (signal?.aborted)
74
+ throw signal.reason;
75
+ if (!(chunk instanceof Uint8Array))
76
+ throw new TypeError('Invalid file chunk');
77
+ yield chunk;
78
+ }
79
+ }
80
+ }
81
+ export function expired(meta) { return !!meta.expiresAt && Date.parse(meta.expiresAt) <= Date.now(); }
82
+ export function ensureNotExpired(meta) { if (expired(meta))
83
+ throw Object.assign(new Error('File expired'), { code: 'FILE_EXPIRED' }); }
84
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}