@gravito/nebula 3.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,176 +1,983 @@
1
- import { PlanetCore, GravitoOrbit } from '@gravito/core';
1
+ import { GravitoOrbit, PlanetCore } from '@gravito/core';
2
2
 
3
3
  /**
4
- * Interface for a file storage provider.
4
+ * StorageStore defines the low-level contract for storage backends.
5
5
  *
6
- * All storage backends (Local, S3, Cloudinary, etc.) must implement this interface.
7
- * It provides a consistent API for file operations across different runtimes
8
- * and cloud providers.
6
+ * All storage drivers (Local, S3, Memory, etc.) must implement this interface.
7
+ * It focuses on raw I/O operations without higher-level logic like hooks.
9
8
  *
10
9
  * @public
11
- * @since 3.0.0
12
10
  */
13
- interface StorageProvider {
11
+ interface StorageStore {
14
12
  /**
15
- * Save data to the storage backend.
13
+ * Persists data to the storage backend.
16
14
  *
17
- * @param key - Unique identifier or path for the file (e.g., 'avatars/user1.jpg').
18
- * @param data - The file content as a Blob, Buffer, or string.
19
- * @returns A promise that resolves when the file is successfully saved.
15
+ * 儲存檔案
16
+ * @param key - Unique identifier or path for the file
17
+ * @param data - Content to be stored
18
+ * @param options - Optional upload options (content-type, metadata, cache-control, etc.)
19
+ * @throws {Error} If the backend fails to write the data
20
20
  */
21
- put(key: string, data: Blob | Buffer | string): Promise<void>;
21
+ put(key: string, data: Blob | Buffer | string, options?: PutOptions): Promise<void>;
22
22
  /**
23
- * Retrieve a file from the storage backend.
23
+ * Retrieves data from the storage backend.
24
24
  *
25
- * @param key - The file identifier or path.
26
- * @returns The file as a Blob, or null if the file does not exist.
25
+ * 讀取檔案
26
+ * @param key - Unique identifier or path for the file
27
+ * @returns The file content as a Blob, or null if the key does not exist
27
28
  */
28
29
  get(key: string): Promise<Blob | null>;
29
30
  /**
30
- * Remove a file from the storage backend.
31
+ * Removes data from the storage backend.
31
32
  *
32
- * @param key - The file identifier or path to delete.
33
- * @returns A promise that resolves when the file is deleted.
33
+ * 刪除檔案
34
+ * @param key - Unique identifier or path for the file
35
+ * @returns True if the file was successfully deleted, false if it didn't exist
34
36
  */
35
- delete(key: string): Promise<void>;
37
+ delete(key: string): Promise<boolean>;
36
38
  /**
37
- * Get a publicly accessible URL for the given file key.
39
+ * Verifies the existence of a file.
38
40
  *
39
- * Note: This may return a relative URL for local storage or a full URL
40
- * for cloud storage providers.
41
+ * 檢查檔案是否存在
42
+ * @param key - Unique identifier or path for the file
43
+ * @returns True if the file exists, false otherwise
44
+ */
45
+ exists(key: string): Promise<boolean>;
46
+ /**
47
+ * Duplicates a file within the same storage backend.
48
+ *
49
+ * 複製檔案
50
+ * @param from - Source identifier
51
+ * @param to - Destination identifier
52
+ * @throws {Error} If the source file is missing or copy fails
53
+ */
54
+ copy(from: string, to: string): Promise<void>;
55
+ /**
56
+ * Relocates or renames a file within the same storage backend.
57
+ *
58
+ * 移動/重命名檔案
59
+ * @param from - Current identifier
60
+ * @param to - New identifier
61
+ * @throws {Error} If the source file is missing or move fails
62
+ */
63
+ move(from: string, to: string): Promise<void>;
64
+ /**
65
+ * Enumerates files and directories.
66
+ *
67
+ * 列出檔案 (可選實作,需要 RuntimeAdapter 支援)
68
+ * @param prefix - Optional path prefix to filter results
69
+ * @returns An async iterable of storage items
70
+ */
71
+ list?(prefix?: string): AsyncIterable<StorageItem>;
72
+ /**
73
+ * Lists files with pagination support.
74
+ *
75
+ * 分頁列舉檔案(適合大型儲存空間)
76
+ * Recommended for large storage backends (S3, GCS) to prevent OOM.
77
+ * Provides cursor-based pagination for efficient large-scale file enumeration.
78
+ *
79
+ * @param prefix - Optional path prefix to filter results
80
+ * @param options - Pagination and filtering options
81
+ * @returns Paginated list result with cursor for next page
82
+ */
83
+ listPaginated?(prefix: string, options?: ListOptions): Promise<ListResult>;
84
+ /**
85
+ * Retrieves technical information about a file.
86
+ *
87
+ * 取得檔案元資料
88
+ * @param key - Unique identifier or path for the file
89
+ * @returns Metadata object or null if file not found
90
+ */
91
+ getMetadata(key: string): Promise<StorageMetadata | null>;
92
+ /**
93
+ * Updates custom metadata for a file.
94
+ *
95
+ * 更新自定義 Metadata(可選實作)
96
+ * Note: This method only updates custom metadata, not system metadata like size or mimeType.
97
+ *
98
+ * @param key - Unique identifier or path for the file
99
+ * @param metadata - Custom metadata to set (key-value pairs)
100
+ * @throws {Error} If the file does not exist or operation fails
101
+ */
102
+ setMetadata?(key: string, metadata: Record<string, string>): Promise<void>;
103
+ /**
104
+ * Generates a publicly accessible URL for the file.
41
105
  *
42
- * @param key - The file identifier or path.
43
- * @returns The public URL string.
106
+ * 取得公開 URL
107
+ * @param key - Unique identifier or path for the file
108
+ * @returns The URL string
44
109
  */
45
110
  getUrl(key: string): string;
111
+ /**
112
+ * Generates a time-limited, authorized URL.
113
+ *
114
+ * 取得有時效的簽名 URL (可選實作)
115
+ * @param key - Unique identifier or path for the file
116
+ * @param expiresIn - Duration in seconds until the URL expires
117
+ * @returns A promise resolving to the signed URL string
118
+ */
119
+ getSignedUrl?(key: string, expiresIn: number): Promise<string>;
120
+ /**
121
+ * Writes data from a readable stream.
122
+ *
123
+ * 串流寫入檔案(可選實作)
124
+ * Useful for handling large files without loading entire content into memory.
125
+ *
126
+ * @param key - Unique identifier or path for the file
127
+ * @param stream - Readable stream containing the data
128
+ * @throws {Error} If the backend fails to write the stream
129
+ */
130
+ putStream?(key: string, stream: ReadableStream<Uint8Array>): Promise<void>;
131
+ /**
132
+ * Reads data as a readable stream.
133
+ *
134
+ * 串流讀取檔案(可選實作)
135
+ * Useful for handling large files without loading entire content into memory.
136
+ *
137
+ * @param key - Unique identifier or path for the file
138
+ * @returns A readable stream of the file content, or null if the key does not exist
139
+ */
140
+ getStream?(key: string): Promise<ReadableStream<Uint8Array> | null>;
141
+ }
142
+ /**
143
+ * StorageMetadata represents technical details of a stored file.
144
+ *
145
+ * 檔案元資料
146
+ * @public
147
+ */
148
+ interface StorageMetadata {
149
+ /** 檔案路徑 */
150
+ key: string;
151
+ /** 檔案大小 (bytes) */
152
+ size: number;
153
+ /** MIME 類型 */
154
+ mimeType?: string;
155
+ /** 最後修改時間 */
156
+ lastModified?: Date;
157
+ /** ETag (用於快取驗證) */
158
+ etag?: string;
159
+ /** 自定義 Metadata (S3 風格) */
160
+ customMetadata?: Record<string, string>;
46
161
  }
47
162
  /**
48
- * Local file system storage provider.
163
+ * PutOptions provides additional options for file uploads.
49
164
  *
50
- * Stores files on the local disk using the configured root directory and
51
- * resolves URLs using a provided base URL prefix.
165
+ * 檔案上傳選項
166
+ * @public
167
+ */
168
+ interface PutOptions {
169
+ /** Content-Type header (MIME type) */
170
+ contentType?: string;
171
+ /** 自定義 metadata (key-value pairs) */
172
+ metadata?: Record<string, string>;
173
+ /** Cache-Control header (用於 CDN) */
174
+ cacheControl?: string;
175
+ /** Content-Disposition header (下載檔名控制) */
176
+ contentDisposition?: string;
177
+ }
178
+ /**
179
+ * StorageItem represents an entry in a file listing.
180
+ *
181
+ * 檔案清單項目
182
+ * @public
183
+ */
184
+ interface StorageItem {
185
+ /** 檔案路徑 */
186
+ key: string;
187
+ /** 是否為目錄 */
188
+ isDirectory: boolean;
189
+ /** 檔案大小 (目錄為 undefined) */
190
+ size?: number;
191
+ /** 最後修改時間 */
192
+ lastModified?: Date;
193
+ }
194
+ /**
195
+ * ListOptions provides pagination and filtering options for file listing.
196
+ *
197
+ * 列舉選項(分頁與過濾)
198
+ * @public
199
+ */
200
+ interface ListOptions {
201
+ /** 最大回傳數量(預設: 1000) */
202
+ maxResults?: number;
203
+ /** 分頁游標(繼續上次的列舉) */
204
+ cursor?: string;
205
+ /** 是否包含子目錄(預設: true) */
206
+ recursive?: boolean;
207
+ }
208
+ /**
209
+ * ListResult contains paginated file listing results.
210
+ *
211
+ * 分頁列舉結果
212
+ * @public
213
+ */
214
+ interface ListResult {
215
+ /** 檔案清單項目 */
216
+ items: StorageItem[];
217
+ /** 下一頁游標(null 表示沒有更多結果) */
218
+ nextCursor: string | null;
219
+ /** 是否還有更多結果 */
220
+ hasMore: boolean;
221
+ /** 本次回傳的項目數量 */
222
+ count: number;
223
+ }
224
+
225
+ /**
226
+ * OrbitNebulaStoreConfig defines the configuration for a single storage disk.
227
+ *
228
+ * It supports multiple driver types, each with its own specific configuration requirements.
229
+ *
230
+ * 單一磁碟配置
231
+ * @public
232
+ */
233
+ type OrbitNebulaStoreConfig = {
234
+ /** Local filesystem driver */
235
+ driver: 'local';
236
+ /** Root directory for file storage */
237
+ root: string;
238
+ /** Base URL for generating public links */
239
+ baseUrl?: string;
240
+ } | {
241
+ /** Volatile in-memory driver */
242
+ driver: 'memory';
243
+ } | {
244
+ /** No-op driver for disabling storage */
245
+ driver: 'null';
246
+ } | {
247
+ /** Custom driver implementation */
248
+ driver: 'custom';
249
+ /** The custom store instance */
250
+ store: StorageStore;
251
+ };
252
+ /**
253
+ * OrbitNebulaOptions defines the global configuration for the Nebula orbit.
254
+ *
255
+ * It allows configuring multiple disks, the default disk, and how the service
256
+ * is exposed in the Gravito context.
257
+ *
258
+ * OrbitNebula 配置選項
259
+ * @public
260
+ */
261
+ interface OrbitNebulaOptions {
262
+ /** The name of the default disk to use when none is specified */
263
+ default?: string;
264
+ /** The key used to expose the StorageManager in the context (default: 'storage') */
265
+ exposeAs?: string;
266
+ /** A map of disk names to their respective configurations */
267
+ disks?: Record<string, OrbitNebulaStoreConfig>;
268
+ /** Execution mode for storage hooks */
269
+ eventsMode?: 'sync' | 'async';
270
+ /** @deprecated 使用 disks.local 替代 */
271
+ local?: {
272
+ root: string;
273
+ baseUrl?: string;
274
+ };
275
+ /** @deprecated 使用 disks[name] = { driver: 'custom', store } 替代 */
276
+ provider?: StorageStore;
277
+ }
278
+ /**
279
+ * StorageHooks defines the internal interface for triggering Gravito hooks.
280
+ *
281
+ * Hooks 回調介面
282
+ * @internal
283
+ */
284
+ interface StorageHooks {
285
+ /** Applies a filter hook to a value */
286
+ applyFilter<T>(hook: string, value: T, context?: Record<string, unknown>): Promise<T>;
287
+ /** Executes an action hook */
288
+ doAction(hook: string, context?: Record<string, unknown>): Promise<void>;
289
+ }
290
+
291
+ /**
292
+ * StorageRepository wraps a StorageStore to provide high-level features.
293
+ *
294
+ * It implements the Repository pattern, adding cross-cutting concerns like
295
+ * hooks (filters and actions) to the raw store operations. This allows for
296
+ * global behaviors like image resizing on upload or logging on deletion.
297
+ *
298
+ * @public
299
+ */
300
+ declare class StorageRepository {
301
+ private readonly store;
302
+ private readonly hooks?;
303
+ constructor(store: StorageStore, hooks?: StorageHooks | undefined);
304
+ /**
305
+ * Stores content and triggers upload hooks.
306
+ *
307
+ * Applies the `storage:upload` filter to the data before storage and
308
+ * fires the `storage:uploaded` action upon success.
309
+ *
310
+ * @param key - The destination path
311
+ * @param data - The content to store
312
+ * @param options - Optional upload options (content-type, metadata, cache-control, etc.)
313
+ * @throws {Error} If the underlying store fails to persist the data
314
+ */
315
+ put(key: string, data: Blob | Buffer | string, options?: PutOptions): Promise<void>;
316
+ /**
317
+ * Retrieves content and triggers hit/miss hooks.
318
+ *
319
+ * Fires `storage:hit` if the file exists, or `storage:miss` otherwise.
320
+ *
321
+ * @param key - The path of the file to retrieve
322
+ * @returns The file content as a Blob, or null if not found
323
+ */
324
+ get(key: string): Promise<Blob | null>;
325
+ /**
326
+ * Deletes a file and triggers the deleted hook.
327
+ *
328
+ * Fires `storage:deleted` only if the file was actually removed.
329
+ *
330
+ * @param key - The path of the file to delete
331
+ * @returns True if deleted, false if file didn't exist
332
+ */
333
+ delete(key: string): Promise<boolean>;
334
+ /**
335
+ * Checks for file existence.
336
+ *
337
+ * @param key - The path to check
338
+ * @returns True if exists, false otherwise
339
+ */
340
+ exists(key: string): Promise<boolean>;
341
+ /**
342
+ * Copies a file and triggers the copied hook.
343
+ *
344
+ * Fires `storage:copied` upon successful completion.
345
+ *
346
+ * @param from - Source path
347
+ * @param to - Destination path
348
+ * @throws {Error} If source missing or operation fails
349
+ */
350
+ copy(from: string, to: string): Promise<void>;
351
+ /**
352
+ * Moves a file and triggers the moved hook.
353
+ *
354
+ * Fires `storage:moved` upon successful completion.
355
+ *
356
+ * @param from - Current path
357
+ * @param to - New path
358
+ * @throws {Error} If source missing or operation fails
359
+ */
360
+ move(from: string, to: string): Promise<void>;
361
+ /**
362
+ * Lists files using an async generator.
363
+ *
364
+ * @param prefix - Path prefix to filter by
365
+ * @returns Async iterable of storage items
366
+ * @throws {Error} If the underlying driver does not support listing
367
+ */
368
+ list(prefix?: string): AsyncIterable<StorageItem>;
369
+ /**
370
+ * Lists files with pagination support.
371
+ *
372
+ * 分頁列舉檔案
373
+ * Recommended for large storage backends to prevent OOM.
374
+ * Provides cursor-based pagination for efficient enumeration.
375
+ *
376
+ * @param prefix - Path prefix to filter by
377
+ * @param options - Pagination and filtering options
378
+ * @returns Paginated list result with cursor for next page
379
+ * @throws {Error} If the underlying driver does not support paginated listing
380
+ */
381
+ listPaginated(prefix: string, options?: ListOptions): Promise<ListResult>;
382
+ /**
383
+ * Retrieves file metadata.
384
+ *
385
+ * @param key - Path of the file
386
+ * @returns Metadata object or null if not found
387
+ */
388
+ getMetadata(key: string): Promise<StorageMetadata | null>;
389
+ /**
390
+ * Updates custom metadata for a file.
391
+ *
392
+ * 更新自定義 Metadata
393
+ *
394
+ * @param key - Path of the file
395
+ * @param metadata - Custom metadata to set
396
+ * @throws {Error} If the underlying driver does not support metadata updates
397
+ */
398
+ setMetadata(key: string, metadata: Record<string, string>): Promise<void>;
399
+ /**
400
+ * Generates a public URL.
401
+ *
402
+ * @param key - Path of the file
403
+ * @returns URL string
404
+ */
405
+ getUrl(key: string): string;
406
+ /**
407
+ * Generates a temporary signed URL.
408
+ *
409
+ * @param key - Path of the file
410
+ * @param expiresIn - Expiration in seconds
411
+ * @returns Signed URL string
412
+ * @throws {Error} If the underlying driver does not support signed URLs
413
+ */
414
+ getSignedUrl(key: string, expiresIn: number): Promise<string>;
415
+ /**
416
+ * Stores content from a readable stream and triggers upload hooks.
417
+ *
418
+ * 串流上傳並觸發 hooks
419
+ * Useful for handling large files without loading entire content into memory.
420
+ * Applies the `storage:upload` filter and fires the `storage:uploaded` action.
421
+ *
422
+ * @param key - The destination path
423
+ * @param stream - Readable stream containing the data
424
+ * @throws {Error} If the underlying driver does not support stream writing
425
+ */
426
+ putStream(key: string, stream: ReadableStream<Uint8Array>): Promise<void>;
427
+ /**
428
+ * Retrieves content as a readable stream and triggers hit/miss hooks.
429
+ *
430
+ * 串流讀取並觸發 hooks
431
+ * Useful for handling large files without loading entire content into memory.
432
+ * Fires `storage:hit` if the file exists, or `storage:miss` otherwise.
433
+ *
434
+ * @param key - Path of the file
435
+ * @returns Readable stream of file content, or null if not found
436
+ * @throws {Error} If the underlying driver does not support stream reading
437
+ */
438
+ getStream(key: string): Promise<ReadableStream<Uint8Array> | null>;
439
+ }
440
+
441
+ /**
442
+ * StorageManager acts as the central hub for multi-disk storage management.
443
+ *
444
+ * It implements the Manager pattern to coordinate multiple storage repositories (disks).
445
+ * It provides a unified API that delegates to the default disk or a specifically
446
+ * requested disk. It also handles the lazy initialization of storage stores.
52
447
  *
53
448
  * @example
54
449
  * ```typescript
55
- * const local = new LocalStorageProvider('./storage', '/files');
56
- * await local.put('hello.txt', 'Hello World');
57
- * console.log(local.getUrl('hello.txt')); // "/files/hello.txt"
450
+ * const manager = new StorageManager(factory, { default: 'local' });
451
+ * await manager.put('hello.txt', 'world'); // Uses default 'local' disk
452
+ * await manager.disk('s3').put('backup.zip', data); // Uses 's3' disk
58
453
  * ```
59
454
  *
60
455
  * @public
61
- * @since 3.0.0
62
456
  */
63
- declare class LocalStorageProvider implements StorageProvider {
64
- private rootDir;
65
- private baseUrl;
66
- private runtime;
457
+ declare class StorageManager {
458
+ private readonly storeFactory;
459
+ private readonly options;
460
+ private readonly hooks?;
461
+ private stores;
462
+ private repositories;
463
+ constructor(storeFactory: (name: string) => StorageStore, options: {
464
+ default: string;
465
+ prefix?: string;
466
+ }, hooks?: StorageHooks | undefined);
467
+ /**
468
+ * Accesses a specific storage disk by name.
469
+ *
470
+ * If no name is provided, the default disk configured during initialization is returned.
471
+ * Repositories are lazily initialized and cached for subsequent access.
472
+ *
473
+ * @param name - The unique identifier of the disk to access
474
+ * @returns A repository instance for the requested disk
475
+ * @throws {Error} If the requested disk is not configured or factory fails
476
+ */
477
+ disk(name?: string): StorageRepository;
478
+ private resolveStore;
479
+ /**
480
+ * Stores content at the specified key on the default disk.
481
+ *
482
+ * @param key - The destination path/identifier
483
+ * @param data - The content to store
484
+ * @param options - Optional upload options (content-type, metadata, cache-control, etc.)
485
+ * @returns A promise that resolves when the operation completes
486
+ * @throws {Error} If the storage operation fails on the default disk
487
+ */
488
+ put(key: string, data: Blob | Buffer | string, options?: PutOptions): Promise<void>;
489
+ /**
490
+ * Retrieves content from the specified key on the default disk.
491
+ *
492
+ * @param key - The path/identifier of the file to retrieve
493
+ * @returns The file content as a Blob, or null if not found
494
+ */
495
+ get(key: string): Promise<Blob | null>;
496
+ /**
497
+ * Removes a file from the default disk.
498
+ *
499
+ * @param key - The path/identifier of the file to delete
500
+ * @returns True if the file was deleted, false if it didn't exist
501
+ */
502
+ delete(key: string): Promise<boolean>;
503
+ /**
504
+ * Checks if a file exists on the default disk.
505
+ *
506
+ * @param key - The path/identifier to check
507
+ * @returns True if the file exists, false otherwise
508
+ */
509
+ exists(key: string): Promise<boolean>;
510
+ /**
511
+ * Creates a copy of a file on the default disk.
512
+ *
513
+ * @param from - The source path
514
+ * @param to - The destination path
515
+ * @throws {Error} If the source file does not exist or copy fails
516
+ */
517
+ copy(from: string, to: string): Promise<void>;
518
+ /**
519
+ * Moves or renames a file on the default disk.
520
+ *
521
+ * @param from - The current path
522
+ * @param to - The new path
523
+ * @throws {Error} If the source file does not exist or move fails
524
+ */
525
+ move(from: string, to: string): Promise<void>;
526
+ /**
527
+ * Lists files and directories under a given prefix on the default disk.
528
+ *
529
+ * @param prefix - The directory or path prefix to list
530
+ * @returns An async iterable of storage items
531
+ * @throws {Error} If the default disk driver does not support listing
532
+ */
533
+ list(prefix?: string): AsyncIterable<StorageItem>;
534
+ /**
535
+ * Retrieves metadata for a specific file on the default disk.
536
+ *
537
+ * @param key - The path/identifier of the file
538
+ * @returns Metadata object or null if file not found
539
+ */
540
+ getMetadata(key: string): Promise<StorageMetadata | null>;
541
+ /**
542
+ * Generates a public URL for a file on the default disk.
543
+ *
544
+ * @param key - The path/identifier of the file
545
+ * @returns The absolute or relative URL string
546
+ */
547
+ getUrl(key: string): string;
548
+ /**
549
+ * Generates a temporary, signed URL for a file on the default disk.
550
+ *
551
+ * @param key - The path/identifier of the file
552
+ * @param expiresIn - Expiration time in seconds
553
+ * @returns A promise resolving to the signed URL string
554
+ * @throws {Error} If the default disk driver does not support signed URLs
555
+ */
556
+ getSignedUrl(key: string, expiresIn: number): Promise<string>;
557
+ /**
558
+ * Stores content from a readable stream on the default disk.
559
+ *
560
+ * 串流上傳到預設磁碟
561
+ * Useful for handling large files without loading entire content into memory.
562
+ *
563
+ * @param key - The destination path
564
+ * @param stream - Readable stream containing the data
565
+ * @throws {Error} If the default disk driver does not support stream writing
566
+ */
567
+ putStream(key: string, stream: ReadableStream<Uint8Array>): Promise<void>;
568
+ /**
569
+ * Retrieves content as a readable stream from the default disk.
570
+ *
571
+ * 從預設磁碟串流讀取
572
+ * Useful for handling large files without loading entire content into memory.
573
+ *
574
+ * @param key - The path/identifier of the file
575
+ * @returns Readable stream of file content, or null if not found
576
+ * @throws {Error} If the default disk driver does not support stream reading
577
+ */
578
+ getStream(key: string): Promise<ReadableStream<Uint8Array> | null>;
579
+ /**
580
+ * Lists files with pagination support on the default disk.
581
+ *
582
+ * 在預設磁碟上分頁列舉檔案
583
+ * Recommended for large storage backends to prevent OOM.
584
+ *
585
+ * @param prefix - Path prefix to filter by
586
+ * @param options - Pagination and filtering options
587
+ * @returns Paginated list result with cursor for next page
588
+ * @throws {Error} If the default disk driver does not support paginated listing
589
+ */
590
+ listPaginated(prefix: string, options?: ListOptions): Promise<ListResult>;
67
591
  /**
68
- * Create a new LocalStorageProvider.
592
+ * Updates custom metadata for a file on the default disk.
593
+ *
594
+ * 更新預設磁碟上檔案的自定義 Metadata
69
595
  *
70
- * @param rootDir - The absolute path to the local storage directory.
71
- * @param baseUrl - The public URL path for accessing stored files (e.g., '/storage').
596
+ * @param key - The path/identifier of the file
597
+ * @param metadata - Custom metadata to set
598
+ * @throws {Error} If the default disk driver does not support metadata updates
72
599
  */
600
+ setMetadata(key: string, metadata: Record<string, string>): Promise<void>;
601
+ }
602
+
603
+ /**
604
+ * LocalStore implements storage on the local filesystem.
605
+ *
606
+ * It uses the Gravito RuntimeAdapter to perform file operations, ensuring
607
+ * compatibility across different environments (Node.js, Bun, etc.). It includes
608
+ * built-in protection against path traversal attacks.
609
+ *
610
+ * @example
611
+ * ```typescript
612
+ * const store = new LocalStore('./uploads', '/public/files');
613
+ * await store.put('avatars/user.png', data);
614
+ * ```
615
+ *
616
+ * @public
617
+ */
618
+ declare class LocalStore implements StorageStore {
619
+ private readonly rootDir;
620
+ private readonly baseUrl;
621
+ private runtime;
73
622
  constructor(rootDir: string, baseUrl?: string);
74
623
  /**
75
- * Write data to the local disk.
624
+ * Writes data to a file on the local disk.
625
+ *
626
+ * Automatically creates parent directories if they don't exist.
627
+ *
628
+ * @param key - Relative path from the root directory
629
+ * @param data - Content to write
630
+ * @param options - Optional upload options (note: customMetadata not persisted in LocalStore)
631
+ * @throws {Error} If the key is invalid or path is outside root
76
632
  */
77
- put(key: string, data: Blob | Buffer | string): Promise<void>;
633
+ put(key: string, data: Blob | Buffer | string, options?: PutOptions): Promise<void>;
78
634
  /**
79
- * Read data from the local disk.
635
+ * Reads a file from the local disk as a Blob.
636
+ *
637
+ * @param key - Relative path from the root directory
638
+ * @returns File content as Blob, or null if not found
639
+ * @throws {Error} If the key is invalid or path is outside root
80
640
  */
81
641
  get(key: string): Promise<Blob | null>;
82
642
  /**
83
- * Delete a file from the local disk.
643
+ * Deletes a file from the local disk.
644
+ *
645
+ * @param key - Relative path from the root directory
646
+ * @returns True if deleted, false if file didn't exist
647
+ * @throws {Error} If the key is invalid or path is outside root
648
+ */
649
+ delete(key: string): Promise<boolean>;
650
+ /**
651
+ * Checks if a file exists on the local disk.
652
+ *
653
+ * @param key - Relative path from the root directory
654
+ * @returns True if exists, false otherwise
84
655
  */
85
- delete(key: string): Promise<void>;
656
+ exists(key: string): Promise<boolean>;
86
657
  /**
87
- * Resolve the public URL for a locally stored file.
658
+ * Copies a file on the local disk.
659
+ *
660
+ * @param from - Source relative path
661
+ * @param to - Destination relative path
662
+ * @throws {Error} If source missing or operation fails
663
+ */
664
+ copy(from: string, to: string): Promise<void>;
665
+ /**
666
+ * Moves a file on the local disk.
667
+ *
668
+ * @param from - Current relative path
669
+ * @param to - New relative path
670
+ * @throws {Error} If source missing or operation fails
671
+ */
672
+ move(from: string, to: string): Promise<void>;
673
+ /**
674
+ * @internal
675
+ * @throws {Error} Always, as not yet implemented
676
+ */
677
+ list(_prefix?: string): AsyncIterable<StorageItem>;
678
+ /**
679
+ * Retrieves file metadata from the local filesystem.
680
+ *
681
+ * @param key - Relative path from the root directory
682
+ * @returns Metadata object or null if not found
683
+ */
684
+ getMetadata(key: string): Promise<StorageMetadata | null>;
685
+ /**
686
+ * Generates a public URL based on the configured base URL.
687
+ *
688
+ * @param key - Relative path from the root directory
689
+ * @returns URL string
88
690
  */
89
691
  getUrl(key: string): string;
692
+ /**
693
+ * Writes data from a readable stream to a file on the local disk.
694
+ *
695
+ * 串流寫入檔案
696
+ * Automatically creates parent directories if they don't exist.
697
+ * Useful for handling large files without loading entire content into memory.
698
+ *
699
+ * @param key - Relative path from the root directory
700
+ * @param stream - Readable stream containing the data
701
+ * @throws {Error} If the key is invalid or path is outside root
702
+ */
703
+ putStream(key: string, stream: ReadableStream<Uint8Array>): Promise<void>;
704
+ /**
705
+ * Reads a file from the local disk as a readable stream.
706
+ *
707
+ * 串流讀取檔案
708
+ * Useful for handling large files without loading entire content into memory.
709
+ *
710
+ * @param key - Relative path from the root directory
711
+ * @returns Readable stream of file content, or null if not found
712
+ * @throws {Error} If the key is invalid or path is outside root
713
+ */
714
+ getStream(key: string): Promise<ReadableStream<Uint8Array> | null>;
90
715
  private normalizeKey;
91
- private resolveKeyPath;
716
+ private resolvePath;
717
+ private ensureDirectory;
718
+ private guessMimeType;
92
719
  }
720
+
93
721
  /**
94
- * Configuration options for the Nebula Storage Orbit.
722
+ * MemoryStore implements a volatile, in-memory storage backend.
723
+ *
724
+ * It is primarily intended for testing, caching, or temporary data that
725
+ * does not need to persist across application restarts. All data is stored
726
+ * in a Map as Blobs.
727
+ *
728
+ * @example
729
+ * ```typescript
730
+ * const store = new MemoryStore();
731
+ * await store.put('test.txt', 'hello');
732
+ * ```
95
733
  *
96
734
  * @public
97
- * @since 3.0.0
98
735
  */
99
- interface OrbitNebulaOptions {
736
+ declare class MemoryStore implements StorageStore {
737
+ private files;
100
738
  /**
101
- * Custom storage provider instance (e.g., S3Provider).
102
- * If not provided, a LocalStorageProvider will be created if `local` options are set.
739
+ * Stores data in the internal Map.
740
+ *
741
+ * Converts input data to a Blob before storage.
742
+ *
743
+ * @param key - Unique identifier for the file
744
+ * @param data - Content to store
745
+ * @param options - Optional upload options (content-type, metadata, etc.)
103
746
  */
104
- provider?: StorageProvider;
747
+ put(key: string, data: Blob | Buffer | string, options?: PutOptions): Promise<void>;
105
748
  /**
106
- * The key used to expose the storage service in the request context.
107
- * @default 'storage'
749
+ * Retrieves a Blob from the internal Map.
750
+ *
751
+ * @param key - Unique identifier for the file
752
+ * @returns The Blob content, or null if not found
108
753
  */
109
- exposeAs?: string;
110
- /** Configuration for the default LocalStorageProvider. */
111
- local?: {
112
- /** Absolute or relative path to the root directory on disk. */
113
- root: string;
114
- /** Base URL prefix for serving files (e.g., '/public/storage'). @default '/storage' */
115
- baseUrl?: string;
116
- };
754
+ get(key: string): Promise<Blob | null>;
755
+ /**
756
+ * Removes a file from memory.
757
+ *
758
+ * @param key - Unique identifier for the file
759
+ * @returns True if deleted, false if not found
760
+ */
761
+ delete(key: string): Promise<boolean>;
762
+ /**
763
+ * Checks if a key exists in the internal Map.
764
+ *
765
+ * @param key - Unique identifier to check
766
+ * @returns True if exists, false otherwise
767
+ */
768
+ exists(key: string): Promise<boolean>;
769
+ /**
770
+ * Copies a file within memory.
771
+ *
772
+ * @param from - Source key
773
+ * @param to - Destination key
774
+ * @throws {Error} If source file is missing
775
+ */
776
+ copy(from: string, to: string): Promise<void>;
777
+ /**
778
+ * Moves a file within memory.
779
+ *
780
+ * @param from - Current key
781
+ * @param to - New key
782
+ * @throws {Error} If source file is missing
783
+ */
784
+ move(from: string, to: string): Promise<void>;
785
+ /**
786
+ * Lists all files currently in memory.
787
+ *
788
+ * @param prefix - Optional key prefix to filter by
789
+ * @returns Async iterable of storage items
790
+ */
791
+ list(prefix?: string): AsyncIterable<StorageItem>;
792
+ /**
793
+ * Retrieves metadata for an in-memory file.
794
+ *
795
+ * @param key - Unique identifier for the file
796
+ * @returns Metadata object or null if not found
797
+ */
798
+ getMetadata(key: string): Promise<StorageMetadata | null>;
799
+ /**
800
+ * Generates a dummy URL for the in-memory file.
801
+ *
802
+ * @param key - Unique identifier for the file
803
+ * @returns A string starting with /memory/
804
+ */
805
+ getUrl(key: string): string;
806
+ /**
807
+ * Stores data from a readable stream in memory.
808
+ *
809
+ * 串流寫入記憶體
810
+ * Reads the entire stream into memory before storing.
811
+ *
812
+ * @param key - Unique identifier for the file
813
+ * @param stream - Readable stream containing the data
814
+ */
815
+ putStream(key: string, stream: ReadableStream<Uint8Array>): Promise<void>;
816
+ /**
817
+ * Reads an in-memory file as a readable stream.
818
+ *
819
+ * 串流讀取記憶體檔案
820
+ *
821
+ * @param key - Unique identifier for the file
822
+ * @returns Readable stream of file content, or null if not found
823
+ */
824
+ getStream(key: string): Promise<ReadableStream<Uint8Array> | null>;
825
+ /**
826
+ * Updates custom metadata for an in-memory file.
827
+ *
828
+ * 更新自定義 Metadata
829
+ *
830
+ * @param key - Unique identifier for the file
831
+ * @param metadata - Custom metadata to set
832
+ * @throws {Error} If file does not exist
833
+ */
834
+ setMetadata(key: string, metadata: Record<string, string>): Promise<void>;
835
+ /**
836
+ * Lists files with pagination support.
837
+ *
838
+ * 分頁列舉記憶體中的檔案
839
+ * Provides cursor-based pagination for consistent API with other drivers.
840
+ *
841
+ * @param prefix - Key prefix to filter by
842
+ * @param options - Pagination and filtering options
843
+ * @returns Paginated list result
844
+ */
845
+ listPaginated(prefix?: string, options?: ListOptions): Promise<ListResult>;
846
+ }
847
+
848
+ /**
849
+ * NullStore implements the Null Object pattern for storage.
850
+ *
851
+ * It provides a no-op implementation of the StorageStore interface. All write
852
+ * operations succeed silently without doing anything, and all read operations
853
+ * return null or empty results. This is useful for disabling storage without
854
+ * changing application logic.
855
+ *
856
+ * @example
857
+ * ```typescript
858
+ * const store = new NullStore();
859
+ * await store.put('anything', 'data'); // Does nothing
860
+ * ```
861
+ *
862
+ * @public
863
+ */
864
+ declare class NullStore implements StorageStore {
865
+ /**
866
+ * No-op: does not store any data.
867
+ */
868
+ put(_key: string, _data: Blob | Buffer | string): Promise<void>;
869
+ /**
870
+ * Always returns null.
871
+ */
872
+ get(_key: string): Promise<Blob | null>;
873
+ /**
874
+ * Always returns false.
875
+ */
876
+ delete(_key: string): Promise<boolean>;
877
+ /**
878
+ * Always returns false.
879
+ */
880
+ exists(_key: string): Promise<boolean>;
881
+ /**
882
+ * No-op: does not copy anything.
883
+ */
884
+ copy(_from: string, _to: string): Promise<void>;
885
+ /**
886
+ * No-op: does not move anything.
887
+ */
888
+ move(_from: string, _to: string): Promise<void>;
889
+ /**
890
+ * Always yields nothing.
891
+ */
892
+ list(_prefix?: string): AsyncIterable<StorageItem>;
893
+ /**
894
+ * Always returns null.
895
+ */
896
+ getMetadata(_key: string): Promise<StorageMetadata | null>;
897
+ /**
898
+ * Generates a dummy URL starting with /null/.
899
+ */
900
+ getUrl(key: string): string;
117
901
  }
902
+
903
+ /** @deprecated Use StorageStore instead */
904
+ type StorageProvider = StorageStore;
905
+
118
906
  /** @deprecated Use OrbitNebulaOptions instead */
119
907
  type OrbitStorageOptions = OrbitNebulaOptions;
120
908
  /**
121
909
  * OrbitNebula provides a unified file storage abstraction for Gravito.
122
910
  *
123
- * It supports multiple backends (local, S3, etc.) and provides a consistent API
124
- * for file operations. It also integrates with Gravito's hook system for
125
- * filtering uploads (`storage:upload`) and reacting to events (`storage:uploaded`).
911
+ * It implements the Galaxy Architecture's Orbit pattern, acting as a bridge between
912
+ * the PlanetCore micro-kernel and various storage backends. It manages the lifecycle
913
+ * of the StorageManager and integrates with the core's hook system.
126
914
  *
127
915
  * @example
128
916
  * ```typescript
129
917
  * const nebula = new OrbitNebula({
130
- * local: { root: './storage', baseUrl: '/public' }
918
+ * default: 'local',
919
+ * disks: {
920
+ * local: { driver: 'local', root: './uploads' }
921
+ * }
131
922
  * });
132
923
  * core.addOrbit(nebula);
133
- *
134
- * // Usage in controller
135
- * const storage = c.get('storage');
136
- * await storage.put('example.txt', 'Content');
137
924
  * ```
138
925
  *
139
926
  * @public
140
- * @since 3.0.0
141
927
  */
142
928
  declare class OrbitNebula implements GravitoOrbit {
143
929
  private options?;
930
+ private manager?;
144
931
  constructor(options?: OrbitNebulaOptions | undefined);
145
932
  /**
146
- * Install storage service into PlanetCore.
933
+ * Bootstraps the storage service and registers it with PlanetCore.
147
934
  *
148
- * @param core - The PlanetCore instance.
149
- * @throws {Error} If configuration or provider is missing.
935
+ * This method initializes the StorageManager, configures the default disk,
936
+ * and registers the storage service in the IoC container and middleware.
937
+ *
938
+ * @param core - The PlanetCore instance to install into
939
+ * @throws {Error} If configuration is missing or default disk cannot be initialized
150
940
  */
151
941
  install(core: PlanetCore): void;
942
+ /**
943
+ * Retrieves the initialized StorageManager instance.
944
+ *
945
+ * Use this to access storage operations directly from the orbit instance
946
+ * after it has been installed.
947
+ *
948
+ * @returns The active StorageManager instance
949
+ * @throws {Error} If called before the orbit is installed
950
+ */
951
+ getStorage(): StorageManager;
952
+ private createStoreFactory;
152
953
  }
153
954
  /**
154
- * Functional API for installing OrbitNebula.
955
+ * Factory function for quick OrbitNebula installation.
956
+ *
957
+ * Provides a functional approach to adding storage capabilities to a Gravito application.
958
+ *
959
+ * @param core - The PlanetCore instance
960
+ * @param options - Storage configuration options
961
+ * @returns The initialized StorageManager instance
155
962
  *
156
- * @param core - The PlanetCore instance.
157
- * @param options - Storage options.
158
- * @returns The configured storage provider wrapper.
159
- * @throws {Error} If provider is not configured.
963
+ * @example
964
+ * ```typescript
965
+ * const storage = orbitStorage(core, {
966
+ * default: 'local',
967
+ * disks: {
968
+ * local: { driver: 'local', root: './uploads' }
969
+ * }
970
+ * });
971
+ * ```
160
972
  */
161
- declare function orbitStorage(core: PlanetCore, options: OrbitNebulaOptions): {
162
- put: (key: string, data: Blob | Buffer | string) => Promise<void>;
163
- get: (key: string) => Promise<Blob | null>;
164
- delete: (key: string) => Promise<void>;
165
- getUrl: (key: string) => string;
166
- };
973
+ declare function orbitStorage(core: PlanetCore, options: OrbitNebulaOptions): StorageManager;
167
974
  /** @deprecated Use OrbitNebula instead */
168
975
  declare const OrbitStorage: typeof OrbitNebula;
169
976
  declare module '@gravito/core' {
170
977
  interface GravitoVariables {
171
978
  /** File storage service */
172
- storage: StorageProvider;
979
+ storage: StorageManager;
173
980
  }
174
981
  }
175
982
 
176
- export { LocalStorageProvider, OrbitNebula, type OrbitNebulaOptions, OrbitStorage, type OrbitStorageOptions, type StorageProvider, orbitStorage as default };
983
+ export { type ListOptions, type ListResult, LocalStore as LocalStorageProvider, LocalStore, MemoryStore, NullStore, OrbitNebula, type OrbitNebulaOptions, type OrbitNebulaStoreConfig, OrbitStorage, type OrbitStorageOptions, type PutOptions, type StorageHooks, type StorageItem, StorageManager, type StorageMetadata, type StorageProvider, StorageRepository, type StorageStore, orbitStorage as default };