@alepha/bucket-vercel 0.13.3 → 0.13.5

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.
@@ -1,13 +1,12 @@
1
1
  import { del, head, put } from "@vercel/blob";
2
2
  import * as typebox0 from "typebox";
3
3
  import { Static, StaticDecode as Static$1, StaticEncode, TAny, TArray, TArray as TArray$1, TBoolean, TInteger, TNumber, TObject, TObject as TObject$1, TOptional, TOptionalAdd, TRecord, TSchema, TSchema as TSchema$1, TString, TUnsafe } from "typebox";
4
- import { AsyncLocalStorage } from "node:async_hooks";
5
4
  import { Readable } from "node:stream";
6
5
  import { ReadableStream as ReadableStream$1 } from "node:stream/web";
6
+ import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { Validator } from "typebox/compile";
8
8
  import dayjsDuration from "dayjs/plugin/duration.js";
9
9
  import DayjsApi, { Dayjs, ManipulateType, PluginFunc } from "dayjs";
10
- import "node:fs";
11
10
 
12
11
  //#region ../alepha/src/core/constants/KIND.d.ts
13
12
  /**
@@ -107,6 +106,93 @@ interface LoggerInterface {
107
106
  error(message: string, data?: unknown): void;
108
107
  }
109
108
  //#endregion
109
+ //#region ../alepha/src/core/helpers/FileLike.d.ts
110
+ interface FileLike {
111
+ /**
112
+ * Filename.
113
+ * @default "file"
114
+ */
115
+ name: string;
116
+ /**
117
+ * Mandatory MIME type of the file.
118
+ * @default "application/octet-stream"
119
+ */
120
+ type: string;
121
+ /**
122
+ * Size of the file in bytes.
123
+ *
124
+ * Always 0 for streams, as the size is not known until the stream is fully read.
125
+ *
126
+ * @default 0
127
+ */
128
+ size: number;
129
+ /**
130
+ * Last modified timestamp in milliseconds since epoch.
131
+ *
132
+ * Always the current timestamp for streams, as the last modified time is not known.
133
+ * We use this field to ensure compatibility with File API.
134
+ *
135
+ * @default Date.now()
136
+ */
137
+ lastModified: number;
138
+ /**
139
+ * Returns a ReadableStream or Node.js Readable stream of the file content.
140
+ *
141
+ * For streams, this is the original stream.
142
+ */
143
+ stream(): StreamLike;
144
+ /**
145
+ * Returns the file content as an ArrayBuffer.
146
+ *
147
+ * For streams, this reads the entire stream into memory.
148
+ */
149
+ arrayBuffer(): Promise<ArrayBuffer>;
150
+ /**
151
+ * Returns the file content as a string.
152
+ *
153
+ * For streams, this reads the entire stream into memory and converts it to a string.
154
+ */
155
+ text(): Promise<string>;
156
+ /**
157
+ * Optional file path, if the file is stored on disk.
158
+ *
159
+ * This is not from the File API, but rather a custom field to indicate where the file is stored.
160
+ */
161
+ filepath?: string;
162
+ }
163
+ type StreamLike = ReadableStream | ReadableStream$1 | Readable | NodeJS.ReadableStream;
164
+ //#endregion
165
+ //#region ../alepha/src/core/providers/TypeProvider.d.ts
166
+ declare module "typebox" {
167
+ interface TString {
168
+ format?: string;
169
+ minLength?: number;
170
+ maxLength?: number;
171
+ }
172
+ interface TNumber {
173
+ format?: "int64";
174
+ }
175
+ }
176
+ //#endregion
177
+ //#region ../alepha/src/core/primitives/$atom.d.ts
178
+ type AtomOptions<T extends TAtomObject, N extends string> = {
179
+ name: N;
180
+ schema: T;
181
+ description?: string;
182
+ } & (T extends TOptionalAdd<T> ? {
183
+ default?: Static$1<T>;
184
+ } : {
185
+ default: Static$1<T>;
186
+ });
187
+ declare class Atom<T extends TAtomObject = TObject$1, N extends string = string> {
188
+ readonly options: AtomOptions<T, N>;
189
+ get schema(): T;
190
+ get key(): N;
191
+ constructor(options: AtomOptions<T, N>);
192
+ }
193
+ type TAtomObject = TObject$1<any> | TArray;
194
+ type AtomStatic<T extends TAtomObject> = T extends TOptionalAdd<T> ? Static$1<T> | undefined : Static$1<T>;
195
+ //#endregion
110
196
  //#region ../alepha/src/core/primitives/$inject.d.ts
111
197
  interface InjectOptions<T extends object = any> {
112
198
  /**
@@ -203,74 +289,6 @@ declare class Json {
203
289
  parse(text: string, reviver?: (this: any, key: string, value: any) => any): any;
204
290
  }
205
291
  //#endregion
206
- //#region ../alepha/src/core/helpers/FileLike.d.ts
207
- interface FileLike {
208
- /**
209
- * Filename.
210
- * @default "file"
211
- */
212
- name: string;
213
- /**
214
- * Mandatory MIME type of the file.
215
- * @default "application/octet-stream"
216
- */
217
- type: string;
218
- /**
219
- * Size of the file in bytes.
220
- *
221
- * Always 0 for streams, as the size is not known until the stream is fully read.
222
- *
223
- * @default 0
224
- */
225
- size: number;
226
- /**
227
- * Last modified timestamp in milliseconds since epoch.
228
- *
229
- * Always the current timestamp for streams, as the last modified time is not known.
230
- * We use this field to ensure compatibility with File API.
231
- *
232
- * @default Date.now()
233
- */
234
- lastModified: number;
235
- /**
236
- * Returns a ReadableStream or Node.js Readable stream of the file content.
237
- *
238
- * For streams, this is the original stream.
239
- */
240
- stream(): StreamLike;
241
- /**
242
- * Returns the file content as an ArrayBuffer.
243
- *
244
- * For streams, this reads the entire stream into memory.
245
- */
246
- arrayBuffer(): Promise<ArrayBuffer>;
247
- /**
248
- * Returns the file content as a string.
249
- *
250
- * For streams, this reads the entire stream into memory and converts it to a string.
251
- */
252
- text(): Promise<string>;
253
- /**
254
- * Optional file path, if the file is stored on disk.
255
- *
256
- * This is not from the File API, but rather a custom field to indicate where the file is stored.
257
- */
258
- filepath?: string;
259
- }
260
- type StreamLike = ReadableStream | ReadableStream$1 | Readable | NodeJS.ReadableStream;
261
- //#endregion
262
- //#region ../alepha/src/core/providers/TypeProvider.d.ts
263
- declare module "typebox" {
264
- interface TString {
265
- format?: string;
266
- minLength?: number;
267
- maxLength?: number;
268
- }
269
- interface TNumber {
270
- format?: "int64";
271
- }
272
- }
273
- //#endregion
274
292
  //#region ../alepha/src/core/providers/SchemaCodec.d.ts
275
293
  declare abstract class SchemaCodec {
276
294
  /**
@@ -440,25 +458,6 @@ declare class EventManager {
440
458
  }): Promise<void>;
441
459
  }
442
460
  //#endregion
443
- //#region ../alepha/src/core/primitives/$atom.d.ts
444
- type AtomOptions<T extends TAtomObject, N extends string> = {
445
- name: N;
446
- schema: T;
447
- description?: string;
448
- } & (T extends TOptionalAdd<T> ? {
449
- default?: Static$1<T>;
450
- } : {
451
- default: Static$1<T>;
452
- });
453
- declare class Atom<T extends TAtomObject = TObject$1, N extends string = string> {
454
- readonly options: AtomOptions<T, N>;
455
- get schema(): T;
456
- get key(): N;
457
- constructor(options: AtomOptions<T, N>);
458
- }
459
- type TAtomObject = TObject$1<any> | TArray;
460
- type AtomStatic<T extends TAtomObject> = T extends TOptionalAdd<T> ? Static$1<T> | undefined : Static$1<T>;
461
- //#endregion
462
461
  //#region ../alepha/src/core/providers/StateManager.d.ts
463
462
  interface AtomWithValue {
464
463
  atom: Atom;
@@ -735,6 +734,8 @@ declare class Alepha {
735
734
  */
736
735
  get env(): Readonly<Env>;
737
736
  constructor(init?: Partial<State>);
737
+ set<T extends TAtomObject>(target: Atom<T>, value: AtomStatic<T>): this;
738
+ set<Key extends keyof State>(target: Key, value: State[Key] | undefined): this;
738
739
  /**
739
740
  * True when start() is called.
740
741
  *
@@ -1999,5 +2000,4 @@ declare class VercelFileStorageProvider implements FileStorageProvider {
1999
2000
  */
2000
2001
  declare const AlephaBucketVercel: Service<Module>;
2001
2002
  //#endregion
2002
- export { AlephaBucketVercel, VercelFileStorageProvider };
2003
- //# sourceMappingURL=index.d.ts.map
2003
+ export { AlephaBucketVercel, VercelFileStorageProvider };
@@ -130,5 +130,4 @@ const AlephaBucketVercel = $module({
130
130
  });
131
131
 
132
132
  //#endregion
133
- export { AlephaBucketVercel, VercelFileStorageProvider };
134
- //# sourceMappingURL=index.js.map
133
+ export { AlephaBucketVercel, VercelFileStorageProvider };
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "blob"
11
11
  ],
12
12
  "author": "Nicolas Foures",
13
- "version": "0.13.3",
13
+ "version": "0.13.5",
14
14
  "type": "module",
15
15
  "engines": {
16
16
  "node": ">=22.0.0"
@@ -27,18 +27,18 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^24.10.1",
30
- "alepha": "0.13.3",
31
- "tsdown": "^0.16.8",
30
+ "alepha": "0.13.5",
31
+ "tsdown": "^0.17.0",
32
32
  "vitest": "^4.0.15"
33
33
  },
34
34
  "peerDependencies": {
35
- "alepha": "0.13.3"
35
+ "alepha": "0.13.5"
36
36
  },
37
37
  "scripts": {
38
38
  "lint": "alepha lint",
39
39
  "typecheck": "alepha typecheck",
40
40
  "test": "alepha test",
41
- "build": "tsdown -c=../../tsdown.config.ts"
41
+ "build": "tsdown"
42
42
  },
43
43
  "repository": {
44
44
  "type": "git",
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/providers/VercelBlobProvider.ts","../src/providers/VercelFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { del, head, put } from \"@vercel/blob\";\n\nexport class VercelBlobApi {\n put: typeof put = put;\n head: typeof head = head;\n del: typeof del = del;\n}\n","import type { Readable } from \"node:stream\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type FileLike,\n type Static,\n t,\n} from \"alepha\";\nimport {\n $bucket,\n FileNotFoundError,\n type FileStorageProvider,\n} from \"alepha/bucket\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { FileDetector, FileSystemProvider } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\nimport { VercelBlobApi } from \"./VercelBlobProvider.ts\";\n\nconst envSchema = t.object({\n BLOB_READ_WRITE_TOKEN: t.text({\n size: \"long\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Vercel Blob Storage implementation of File Storage Provider.\n */\nexport class VercelFileStorageProvider implements FileStorageProvider {\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly time = $inject(DateTimeProvider);\n protected readonly fileSystem = $inject(FileSystemProvider);\n protected readonly fileDetector = $inject(FileDetector);\n protected readonly stores: Set<string> = new Set();\n protected readonly vercelBlobApi = $inject(VercelBlobApi);\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n for (const bucket of this.alepha.primitives($bucket)) {\n if (bucket.provider !== this) {\n continue;\n }\n\n const storeName = this.convertName(bucket.name);\n\n this.log.debug(`Prepare store '${storeName}' ...`);\n\n // Vercel Blob doesn't require explicit store/container creation\n // We just track the store names for reference\n this.stores.add(storeName);\n\n this.log.info(`Blob storage '${bucket.name}' OK`);\n }\n },\n });\n\n public convertName(name: string): string {\n // Convert to a valid path-like name for Vercel Blob\n return name.replaceAll(\"/\", \"-\").toLowerCase();\n }\n\n protected createId(mimeType: string): string {\n const ext = this.fileDetector.getExtensionFromMimeType(mimeType);\n return `${crypto.randomUUID()}.${ext}`;\n }\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n fileId ??= this.createId(file.type);\n\n this.log.trace(\n `Uploading file '${file.name}' to bucket '${bucketName}' with id '${fileId}'...`,\n );\n\n const storeName = this.convertName(bucketName);\n const pathname = `${storeName}/${fileId}`;\n\n try {\n const contentBuffer = Buffer.from(await file.arrayBuffer());\n\n const result = await this.vercelBlobApi.put(\n pathname,\n contentBuffer as unknown as Readable,\n {\n access: \"public\",\n contentType: file.type || \"application/octet-stream\",\n token: this.env.BLOB_READ_WRITE_TOKEN,\n allowOverwrite: true,\n },\n );\n\n this.log.trace(`File uploaded successfully: ${result.url}`);\n return fileId;\n } catch (error) {\n this.log.error(`Failed to upload file: ${error}`);\n if (error instanceof Error) {\n throw new AlephaError(`Upload failed: ${error.message}`, {\n cause: error,\n });\n }\n\n throw error;\n }\n }\n\n public async download(bucketName: string, fileId: string): Promise<FileLike> {\n this.log.trace(\n `Downloading file '${fileId}' from bucket '${bucketName}'...`,\n );\n\n const storeName = this.convertName(bucketName);\n const pathname = `${storeName}/${fileId}`;\n\n try {\n const headResult = await this.vercelBlobApi.head(pathname, {\n token: this.env.BLOB_READ_WRITE_TOKEN,\n });\n\n if (!headResult) {\n throw new FileNotFoundError(\n `File '${fileId}' not found in bucket '${bucketName}'`,\n );\n }\n\n const response = await fetch(headResult.url);\n\n if (!response.ok) {\n throw new FileNotFoundError(\n `Failed to fetch file: ${response.statusText}`,\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n if (!arrayBuffer) {\n throw new FileNotFoundError(\"File not found - empty response body\");\n }\n\n const mimeType = this.fileDetector.getContentType(fileId);\n\n return this.fileSystem.createFile({\n buffer: Buffer.from(arrayBuffer),\n name: fileId,\n type: mimeType,\n });\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n throw error;\n }\n\n this.log.error(`Failed to download file: ${error}`);\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error downloading file\", { cause: error });\n }\n\n throw error;\n }\n }\n\n public async exists(bucketName: string, fileId: string): Promise<boolean> {\n this.log.trace(\n `Checking existence of file '${fileId}' in bucket '${bucketName}'...`,\n );\n\n const storeName = this.convertName(bucketName);\n const pathname = `${storeName}/${fileId}`;\n\n try {\n const result = await this.vercelBlobApi.head(pathname, {\n token: this.env.BLOB_READ_WRITE_TOKEN,\n });\n return result !== null;\n } catch (error) {\n // Vercel Blob head() throws for non-existent files\n return false;\n }\n }\n\n public async delete(bucketName: string, fileId: string): Promise<void> {\n this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);\n\n const storeName = this.convertName(bucketName);\n const pathname = `${storeName}/${fileId}`;\n\n try {\n await this.vercelBlobApi.del(pathname, {\n token: this.env.BLOB_READ_WRITE_TOKEN,\n });\n } catch (error) {\n this.log.error(`Failed to delete file: ${error}`);\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error deleting file\", { cause: error });\n }\n throw error;\n }\n }\n}\n","import { $module } from \"alepha\";\nimport { AlephaBucket, FileStorageProvider } from \"alepha/bucket\";\nimport { VercelFileStorageProvider } from \"./providers/VercelFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/VercelFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Bucket that provides Vercel Blob Storage capabilities.\n *\n * @see {@link VercelFileStorageProvider}\n * @module alepha.bucket.vercel\n */\nexport const AlephaBucketVercel = $module({\n name: \"alepha.bucket.vercel\",\n services: [VercelFileStorageProvider],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: FileStorageProvider,\n use: VercelFileStorageProvider,\n })\n .with(AlephaBucket),\n});\n"],"mappings":"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"}