@od-oneapp/storage 2026.2.1501-canary.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/README.md +854 -0
- package/client-next.d.mts +60 -0
- package/client-next.d.mts.map +1 -0
- package/client-next.mjs +111 -0
- package/client-next.mjs.map +1 -0
- package/client.d.mts +66 -0
- package/client.d.mts.map +1 -0
- package/client.mjs +183 -0
- package/client.mjs.map +1 -0
- package/env-BVHLmQdh.mjs +128 -0
- package/env-BVHLmQdh.mjs.map +1 -0
- package/env.mjs +3 -0
- package/health-check-Bn4qWNOt.d.mts +143 -0
- package/health-check-Bn4qWNOt.d.mts.map +1 -0
- package/health-check-DbHmYLDb.mjs +848 -0
- package/health-check-DbHmYLDb.mjs.map +1 -0
- package/index.d.mts +60 -0
- package/index.d.mts.map +1 -0
- package/index.mjs +3 -0
- package/keys.d.mts +37 -0
- package/keys.d.mts.map +1 -0
- package/keys.mjs +253 -0
- package/keys.mjs.map +1 -0
- package/package.json +123 -0
- package/server-edge.d.mts +27 -0
- package/server-edge.d.mts.map +1 -0
- package/server-edge.mjs +88 -0
- package/server-edge.mjs.map +1 -0
- package/server-next.d.mts +182 -0
- package/server-next.d.mts.map +1 -0
- package/server-next.mjs +1352 -0
- package/server-next.mjs.map +1 -0
- package/server.d.mts +68 -0
- package/server.d.mts.map +1 -0
- package/server.mjs +371 -0
- package/server.mjs.map +1 -0
- package/types.d.mts +314 -0
- package/types.d.mts.map +1 -0
- package/types.mjs +3 -0
- package/validation.d.mts +101 -0
- package/validation.d.mts.map +1 -0
- package/validation.mjs +590 -0
- package/validation.mjs.map +1 -0
- package/vercel-blob-HSvMatlk.mjs +156 -0
- package/vercel-blob-HSvMatlk.mjs.map +1 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { del, head, list, put } from "@vercel/blob";
|
|
2
|
+
|
|
3
|
+
//#region providers/vercel-blob.ts
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview Vercel Blob storage provider
|
|
6
|
+
*
|
|
7
|
+
* Direct implementation (no @integrations/* dependency) to keep
|
|
8
|
+
* @od-oneapp/storage publishable/portable.
|
|
9
|
+
*/
|
|
10
|
+
/** Default download timeout in ms */
|
|
11
|
+
const DEFAULT_DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
12
|
+
var VercelBlobProvider = class {
|
|
13
|
+
token;
|
|
14
|
+
constructor(token) {
|
|
15
|
+
if (!token) throw new Error("Vercel Blob token is required");
|
|
16
|
+
this.token = token;
|
|
17
|
+
}
|
|
18
|
+
async delete(key) {
|
|
19
|
+
if (!key || key.trim() === "") throw new Error("Blob key cannot be empty");
|
|
20
|
+
await del(key, { token: this.token });
|
|
21
|
+
}
|
|
22
|
+
async download(key) {
|
|
23
|
+
if (!key || key.trim() === "") throw new Error("Blob key cannot be empty");
|
|
24
|
+
const controller = new AbortController();
|
|
25
|
+
const timeoutId = setTimeout(() => controller.abort(), DEFAULT_DOWNLOAD_TIMEOUT_MS);
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(key, {
|
|
28
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
29
|
+
signal: controller.signal
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) throw new Error(`Failed to download blob: ${response.statusText}`);
|
|
32
|
+
return response.blob();
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (error instanceof Error && error.name === "AbortError") throw new Error(`Download timeout: Request exceeded ${DEFAULT_DOWNLOAD_TIMEOUT_MS}ms`);
|
|
35
|
+
throw error;
|
|
36
|
+
} finally {
|
|
37
|
+
clearTimeout(timeoutId);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async exists(key) {
|
|
41
|
+
if (!key || key.trim() === "") throw new Error("Blob key cannot be empty");
|
|
42
|
+
try {
|
|
43
|
+
await head(key, { token: this.token });
|
|
44
|
+
return true;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
47
|
+
if (error?.status === 404 || message.includes("not found") || message.includes("404")) return false;
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async getMetadata(key) {
|
|
52
|
+
if (!key || key.trim() === "") throw new Error("Blob key cannot be empty");
|
|
53
|
+
const blob = await head(key, { token: this.token });
|
|
54
|
+
return {
|
|
55
|
+
access: "public",
|
|
56
|
+
contentType: blob.contentType,
|
|
57
|
+
downloadUrl: blob.downloadUrl,
|
|
58
|
+
etag: void 0,
|
|
59
|
+
key: blob.pathname,
|
|
60
|
+
lastModified: new Date(blob.uploadedAt),
|
|
61
|
+
size: blob.size,
|
|
62
|
+
url: blob.url
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async getUrl(key, _options) {
|
|
66
|
+
if (!key || key.trim() === "") throw new Error("Blob key cannot be empty");
|
|
67
|
+
return (await head(key, { token: this.token })).url;
|
|
68
|
+
}
|
|
69
|
+
async list(options) {
|
|
70
|
+
return (await list({
|
|
71
|
+
cursor: options?.cursor,
|
|
72
|
+
limit: options?.limit,
|
|
73
|
+
prefix: options?.prefix,
|
|
74
|
+
token: this.token
|
|
75
|
+
})).blobs.map((blob) => ({
|
|
76
|
+
contentType: blob.contentType ?? "application/octet-stream",
|
|
77
|
+
etag: void 0,
|
|
78
|
+
key: blob.pathname,
|
|
79
|
+
lastModified: new Date(blob.uploadedAt),
|
|
80
|
+
size: blob.size,
|
|
81
|
+
downloadUrl: void 0,
|
|
82
|
+
url: blob.url
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
async upload(key, data, options) {
|
|
86
|
+
if (!key || key.trim() === "") throw new Error("Blob key cannot be empty");
|
|
87
|
+
const access = "public";
|
|
88
|
+
const onUploadProgress = options?.onProgress ? (event) => {
|
|
89
|
+
const total = event.total ?? event.loaded;
|
|
90
|
+
options.onProgress?.({
|
|
91
|
+
key,
|
|
92
|
+
loaded: event.loaded,
|
|
93
|
+
total,
|
|
94
|
+
percentage: event.percentage ?? (total ? event.loaded / total * 100 : void 0)
|
|
95
|
+
});
|
|
96
|
+
} : void 0;
|
|
97
|
+
const result = await put(key, data, {
|
|
98
|
+
access,
|
|
99
|
+
addRandomSuffix: options?.addRandomSuffix ?? false,
|
|
100
|
+
allowOverwrite: options?.allowOverwrite,
|
|
101
|
+
cacheControlMaxAge: options?.cacheControlMaxAge,
|
|
102
|
+
contentType: options?.contentType,
|
|
103
|
+
multipart: options?.multipart,
|
|
104
|
+
abortSignal: options?.abortSignal,
|
|
105
|
+
onUploadProgress,
|
|
106
|
+
...options?.clientPayload && { clientPayload: options.clientPayload },
|
|
107
|
+
token: this.token
|
|
108
|
+
});
|
|
109
|
+
const metadata = await head(result.url, { token: this.token });
|
|
110
|
+
return {
|
|
111
|
+
access,
|
|
112
|
+
contentType: metadata.contentType,
|
|
113
|
+
downloadUrl: metadata.downloadUrl,
|
|
114
|
+
etag: void 0,
|
|
115
|
+
key: result.pathname,
|
|
116
|
+
lastModified: new Date(metadata.uploadedAt),
|
|
117
|
+
metadata: options?.metadata,
|
|
118
|
+
size: metadata.size,
|
|
119
|
+
url: metadata.url ?? result.url
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async createMultipartUpload(key, _options) {
|
|
123
|
+
return {
|
|
124
|
+
uploadId: `vercel-blob-${crypto.randomUUID()}`,
|
|
125
|
+
key
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async uploadPart() {
|
|
129
|
+
throw new Error("Vercel Blob does not support manual multipart uploads. Use multipart: true in upload options instead.");
|
|
130
|
+
}
|
|
131
|
+
async completeMultipartUpload() {
|
|
132
|
+
throw new Error("Vercel Blob does not support manual multipart uploads. Use multipart: true in upload options instead.");
|
|
133
|
+
}
|
|
134
|
+
async abortMultipartUpload() {
|
|
135
|
+
throw new Error("Vercel Blob does not support manual multipart uploads. Use multipart: true in upload options instead.");
|
|
136
|
+
}
|
|
137
|
+
async getPresignedUploadUrl() {
|
|
138
|
+
throw new Error("Vercel Blob does not support presigned URLs. Use direct upload with handleUploadUrl instead.");
|
|
139
|
+
}
|
|
140
|
+
getCapabilities() {
|
|
141
|
+
return {
|
|
142
|
+
multipart: false,
|
|
143
|
+
presignedUrls: false,
|
|
144
|
+
progressTracking: true,
|
|
145
|
+
abortSignal: true,
|
|
146
|
+
metadata: true,
|
|
147
|
+
versioning: false,
|
|
148
|
+
encryption: false,
|
|
149
|
+
directoryListing: true
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
//#endregion
|
|
155
|
+
export { VercelBlobProvider as t };
|
|
156
|
+
//# sourceMappingURL=vercel-blob-HSvMatlk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel-blob-HSvMatlk.mjs","names":[],"sources":["../providers/vercel-blob.ts"],"sourcesContent":["/**\n * @fileoverview Vercel Blob storage provider\n *\n * Direct implementation (no @integrations/* dependency) to keep\n * @od-oneapp/storage publishable/portable.\n */\n\nimport { del, head, list, put } from '@vercel/blob';\n\nimport type { UploadProgressEvent } from '@vercel/blob';\nimport type {\n ListOptions,\n PresignedUploadUrl,\n StorageCapabilities,\n StorageObject,\n StorageProvider,\n UploadOptions,\n} from '../types';\n\n/** Default download timeout in ms */\nconst DEFAULT_DOWNLOAD_TIMEOUT_MS = 30_000;\n\nexport class VercelBlobProvider implements StorageProvider {\n private token: string;\n\n constructor(token: string) {\n if (!token) {\n throw new Error('Vercel Blob token is required');\n }\n this.token = token;\n }\n\n async delete(key: string): Promise<void> {\n if (!key || key.trim() === '') {\n throw new Error('Blob key cannot be empty');\n }\n\n await del(key, { token: this.token });\n }\n\n async download(key: string): Promise<Blob> {\n if (!key || key.trim() === '') {\n throw new Error('Blob key cannot be empty');\n }\n\n // Add timeout to prevent hanging requests\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), DEFAULT_DOWNLOAD_TIMEOUT_MS);\n\n try {\n const response = await fetch(key, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to download blob: ${response.statusText}`);\n }\n\n return response.blob();\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Download timeout: Request exceeded ${DEFAULT_DOWNLOAD_TIMEOUT_MS}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async exists(key: string): Promise<boolean> {\n if (!key || key.trim() === '') {\n throw new Error('Blob key cannot be empty');\n }\n\n try {\n await head(key, { token: this.token });\n return true;\n } catch (error) {\n const message =\n error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n const err = error as { status?: number };\n if (err?.status === 404 || message.includes('not found') || message.includes('404')) {\n return false;\n }\n throw error;\n }\n }\n\n async getMetadata(key: string): Promise<StorageObject> {\n if (!key || key.trim() === '') {\n throw new Error('Blob key cannot be empty');\n }\n\n const blob = await head(key, { token: this.token });\n\n return {\n access: 'public', // Vercel Blob defaults to public; private access determined by URL auth\n contentType: blob.contentType,\n downloadUrl: blob.downloadUrl,\n etag: undefined,\n key: blob.pathname,\n lastModified: new Date(blob.uploadedAt),\n size: blob.size,\n url: blob.url,\n };\n }\n\n async getUrl(key: string, _options?: { expiresIn?: number }): Promise<string> {\n if (!key || key.trim() === '') {\n throw new Error('Blob key cannot be empty');\n }\n\n // Vercel Blob URLs are permanent for public files\n // For private files, they include auth in the URL\n const blob = await head(key, { token: this.token });\n return blob.url;\n }\n\n async list(options?: ListOptions): Promise<StorageObject[]> {\n const result = await list({\n cursor: options?.cursor,\n limit: options?.limit,\n prefix: options?.prefix,\n token: this.token,\n });\n\n // Avoid N+1 queries: Use data from list response when available\n // Only make head() calls if content type is truly needed and not available\n return result.blobs.map(\n (blob: {\n pathname: string;\n url: string;\n size: number;\n uploadedAt: Date;\n contentType?: string;\n }) => ({\n contentType: blob.contentType ?? 'application/octet-stream',\n etag: undefined,\n key: blob.pathname,\n lastModified: new Date(blob.uploadedAt),\n size: blob.size,\n downloadUrl: undefined, // Not available from list response\n url: blob.url,\n }),\n );\n }\n\n async upload(\n key: string,\n data: ArrayBuffer | Blob | Buffer | File | ReadableStream,\n options?: UploadOptions,\n ): Promise<StorageObject> {\n if (!key || key.trim() === '') {\n throw new Error('Blob key cannot be empty');\n }\n\n // Vercel Blob only supports public access; private uploads not yet available\n const access = 'public';\n const onUploadProgress: ((event: UploadProgressEvent) => void) | undefined = options?.onProgress\n ? event => {\n const total = event.total ?? event.loaded;\n options.onProgress?.({\n key,\n loaded: event.loaded,\n total,\n percentage: event.percentage ?? (total ? (event.loaded / total) * 100 : undefined),\n });\n }\n : undefined;\n\n const result = await put(key, data, {\n access,\n addRandomSuffix: options?.addRandomSuffix ?? false,\n allowOverwrite: options?.allowOverwrite,\n cacheControlMaxAge: options?.cacheControlMaxAge,\n contentType: options?.contentType,\n multipart: options?.multipart,\n abortSignal: options?.abortSignal,\n onUploadProgress,\n ...(options?.clientPayload && { clientPayload: options.clientPayload }),\n token: this.token,\n });\n\n // Get metadata to retrieve size and download URL\n const metadata = await head(result.url, { token: this.token });\n\n return {\n access,\n contentType: metadata.contentType,\n downloadUrl: metadata.downloadUrl,\n etag: undefined,\n key: result.pathname,\n lastModified: new Date(metadata.uploadedAt),\n metadata: options?.metadata,\n size: metadata.size,\n url: metadata.url ?? result.url,\n };\n }\n\n async createMultipartUpload(\n key: string,\n _options?: UploadOptions,\n ): Promise<{ uploadId: string; key: string }> {\n const uploadId = `vercel-blob-${crypto.randomUUID()}`;\n return { uploadId, key };\n }\n\n async uploadPart(): Promise<{ etag: string; partNumber: number }> {\n throw new Error(\n 'Vercel Blob does not support manual multipart uploads. Use multipart: true in upload options instead.',\n );\n }\n\n async completeMultipartUpload(): Promise<StorageObject> {\n throw new Error(\n 'Vercel Blob does not support manual multipart uploads. Use multipart: true in upload options instead.',\n );\n }\n\n async abortMultipartUpload(): Promise<void> {\n throw new Error(\n 'Vercel Blob does not support manual multipart uploads. Use multipart: true in upload options instead.',\n );\n }\n\n async getPresignedUploadUrl(): Promise<PresignedUploadUrl> {\n throw new Error(\n 'Vercel Blob does not support presigned URLs. Use direct upload with handleUploadUrl instead.',\n );\n }\n\n getCapabilities(): StorageCapabilities {\n return {\n multipart: false,\n presignedUrls: false,\n progressTracking: true,\n abortSignal: true,\n metadata: true,\n versioning: false,\n encryption: false,\n directoryListing: true,\n };\n }\n}\n"],"mappings":"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"}
|