@jamiephan/casclib 0.0.0-dev.0 → 0.0.0-dev.2

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 CHANGED
@@ -1,6 +1,26 @@
1
1
  # @jamiephan/casclib
2
2
 
3
- Node.js native bindings for [CascLib](https://github.com/ladislav-zezula/CascLib) - A library to read CASC storage from Blizzard games.
3
+ Node.js native bindings for [CascLib](https://github.com/ladislav-zezula/CascLib) - A library to read CASC (Content Addressable Storage Container) from modern Blizzard games.
4
+
5
+ ## Features
6
+
7
+ - ✅ Read CASC storage archives (local and online)
8
+ - ✅ Extract files from modern Blizzard games
9
+ - ✅ TypeScript support with full type definitions
10
+ - ✅ Cross-platform (Windows, macOS, Linux)
11
+ - ✅ Both CommonJS and ES Module support
12
+ - ✅ High-level wrapper API for ease of use
13
+ - ✅ Low-level bindings for advanced usage
14
+
15
+ ## Supported Games
16
+
17
+ Any game using CASC storage format, including:
18
+ - Heroes of the Storm
19
+ - World of Warcraft
20
+ - Diablo III & IV
21
+ - Overwatch
22
+ - Starcraft II
23
+ - Warcraft III: Reforged
4
24
 
5
25
  ## Installation
6
26
 
@@ -14,6 +34,17 @@ Or with pnpm:
14
34
  pnpm add @jamiephan/casclib
15
35
  ```
16
36
 
37
+ ## Architecture
38
+
39
+ This package provides two layers of API:
40
+
41
+ 1. **High-level Wrapper API** (Recommended) - `Storage` and `File` classes with simplified method names
42
+ 2. **Low-level Bindings API** (Advanced) - Direct access to native bindings with CascLib.h names (interfaces: `CascStorage`, `CascFile`)
43
+
44
+ Most users should use the high-level wrapper API as shown in all examples below. The low-level bindings use exact function names from CascLib.h (e.g., `CascOpenStorage`, `CascOpenFile`).
45
+
46
+ For more details, see [BINDING_NAMING_CONVENTION.md](BINDING_NAMING_CONVENTION.md).
47
+
17
48
  ## Usage
18
49
 
19
50
  ### Import
@@ -22,27 +53,30 @@ The package supports both CommonJS and ES Module imports:
22
53
 
23
54
  ```javascript
24
55
  // ES Module (recommended)
25
- import { CascStorage, CascFile } from '@jamiephan/casclib';
56
+ import { Storage, File } from '@jamiephan/casclib';
26
57
 
27
58
  // CommonJS
28
- const { CascStorage, CascFile } = require('@jamiephan/casclib');
59
+ const { Storage, File } = require('@jamiephan/casclib');
60
+
61
+ // Advanced: Direct binding access
62
+ import { CascStorageBinding, CascStorage, CascFile } from '@jamiephan/casclib/bindings';
29
63
  ```
30
64
 
31
65
  ### Opening a CASC Storage
32
66
 
33
67
  ```typescript
34
- import { CascStorage } from '@jamiephan/casclib';
68
+ import { Storage } from '@jamiephan/casclib';
35
69
 
36
- const storage = new CascStorage();
37
- storage.open('/path/to/game/Data');
70
+ const storage = new Storage();
71
+ storage.open('/path/to/heroes/HeroesData');
38
72
 
39
73
  // Check if a file exists
40
- if (storage.fileExists('some-file.txt')) {
74
+ if (storage.fileExists('mods/heroesdata.stormmod/base.stormdata/GameData/HeroData.xml')) {
41
75
  console.log('File exists!');
42
76
  }
43
77
 
44
78
  // Get file information
45
- const info = storage.getFileInfo('some-file.txt');
79
+ const info = storage.getFileInfo('mods/heroesdata.stormmod/base.stormdata/GameData/HeroData.xml');
46
80
  if (info) {
47
81
  console.log(`File: ${info.name}, Size: ${info.size} bytes`);
48
82
  }
@@ -52,7 +86,7 @@ if (info) {
52
86
 
53
87
  ```typescript
54
88
  // Open and read a file
55
- const file = storage.openFile('some-file.txt');
89
+ const file = storage.openFile('mods/heroesdata.stormmod/base.stormdata/GameData/HeroData.xml');
56
90
 
57
91
  // Read all content at once
58
92
  const content = file.readAll();
@@ -62,26 +96,100 @@ console.log(content.toString());
62
96
  file.setPosition(0); // Reset to beginning
63
97
  const chunk = file.read(1024); // Read 1024 bytes
64
98
 
99
+ // Check file size and position
100
+ console.log(`Size: ${file.getSize()}, Position: ${file.getPosition()}`);
101
+
65
102
  // Close the file when done
66
103
  file.close();
67
104
  ```
68
105
 
106
+ ### Online Storage
107
+
108
+ ```typescript
109
+ // Connect to online CASC storage
110
+ // Format: local_cache_folder[*cdn_server_url]*code_name[*region]
111
+ const storage = new Storage();
112
+
113
+ // Windows - Basic usage with cache folder and product code
114
+ storage.openOnline('C:/Temp/CASC/Cache*hero');
115
+
116
+ // Linux/macOS - Basic usage
117
+ storage.openOnline('/tmp/casc/cache*hero');
118
+
119
+ // With CDN server specified
120
+ storage.openOnline('C:/Temp/CASC/Cache*http://us.patch.battle.net:1119*hero');
121
+
122
+ // With region specified (uses default CDN)
123
+ storage.openOnline('/tmp/casc/cache*hero*us');
124
+
125
+ // Full format with all parameters
126
+ storage.openOnline('C:/Temp/CASC/Cache*http://us.patch.battle.net:1119*hero*us');
127
+
128
+ if (storage.fileExists('mods/heroesdata.stormmod/base.stormdata/UI/Layout.xml')) {
129
+ const file = storage.openFile('mods/heroesdata.stormmod/base.stormdata/UI/Layout.xml');
130
+ const data = file.readAll();
131
+ file.close();
132
+ }
133
+
134
+ storage.close();
135
+ ```
136
+
137
+ ### Finding Files
138
+
139
+ ```typescript
140
+ // Find files matching a pattern
141
+ const findData = storage.findFirstFile('*.xml');
142
+ if (findData) {
143
+ console.log(`Found: ${findData.fileName} (${findData.fileSize} bytes)`);
144
+
145
+ // Continue finding
146
+ let nextFile = storage.findNextFile();
147
+ while (nextFile) {
148
+ console.log(`Found: ${nextFile.fileName}`);
149
+ nextFile = storage.findNextFile();
150
+ }
151
+
152
+ storage.findClose();
153
+ }
154
+ ```
155
+
156
+ ### Encryption Keys
157
+
158
+ ```typescript
159
+ // Add encryption key (for encrypted files)
160
+ storage.addEncryptionKey(0x12345678, Buffer.from('your-key-data'));
161
+
162
+ // Or add from string
163
+ storage.addStringEncryptionKey(0x12345678, 'your-key-string');
164
+
165
+ // Import keys from file
166
+ storage.importKeysFromFile('/path/to/keys.txt');
167
+
168
+ // Find a key
169
+ const keyData = storage.findEncryptionKey(0x12345678);
170
+ if (keyData) {
171
+ console.log('Key found:', keyData);
172
+ }
173
+ ```
174
+
69
175
  ### Complete Example
70
176
 
71
177
  ```typescript
72
- import { CascStorage } from '@jamiephan/casclib';
178
+ import { Storage } from '@jamiephan/casclib';
73
179
 
74
180
  async function readGameFile() {
75
- const storage = new CascStorage();
181
+ const storage = new Storage();
76
182
 
77
183
  try {
78
- storage.open('/path/to/wow/Data');
184
+ storage.open('/path/to/heroes/HeroesData');
79
185
 
80
- if (storage.fileExists('Interface\\FrameXML\\UIParent.lua')) {
81
- const file = storage.openFile('Interface\\FrameXML\\UIParent.lua');
186
+ const heroDataPath = 'mods/heroesdata.stormmod/base.stormdata/GameData/HeroData.xml';
187
+ if (storage.fileExists(heroDataPath)) {
188
+ const file = storage.openFile(heroDataPath);
82
189
  const content = file.readAll();
83
190
 
84
191
  console.log(`File size: ${file.getSize()} bytes`);
192
+ console.log(`Position: ${file.getPosition()}`);
85
193
  console.log('Content:', content.toString());
86
194
 
87
195
  file.close();
@@ -94,47 +202,487 @@ async function readGameFile() {
94
202
  readGameFile();
95
203
  ```
96
204
 
97
- ## API
205
+ ## API Reference
206
+
207
+ ### Storage
98
208
 
99
- ### CascStorage
209
+ #### Constructor
210
+
211
+ ##### `constructor()`
212
+ Creates a new Storage instance.
213
+
214
+ ```typescript
215
+ const storage = new Storage();
216
+ ```
100
217
 
101
- #### `constructor()`
102
- Creates a new CascStorage instance.
218
+ #### Storage Operations
103
219
 
104
- #### `open(path: string, options?: StorageOpenOptions): void`
220
+ ##### `open(path: string, options?: StorageOpenOptions): void`
105
221
  Opens a CASC storage at the specified path.
106
222
 
107
- #### `close(): boolean`
108
- Closes the storage.
223
+ **Parameters:**
224
+ - `path`: Path to the CASC storage directory (e.g., `/path/to/game/Data`)
225
+ - `options`: Optional opening options
226
+ - `flags`: Opening flags (number)
227
+
228
+ **Example:**
229
+ ```typescript
230
+ storage.open('/path/to/heroes/HeroesData');
231
+ storage.open('/path/to/heroes/HeroesData', { flags: 0 });
232
+ ```
233
+
234
+ ##### `openOnline(path: string, options?: StorageOpenOptions): void`
235
+ Opens an online CASC storage.
236
+
237
+ **Parameters:**
238
+ - `path`: Connection string in the format: `local_cache_folder[*cdn_server_url]*code_name[*region]`
239
+ - `local_cache_folder`: Local cache directory for downloaded game data (reusable across runs)
240
+ - Windows: `C:/Temp/CASC/Cache`
241
+ - Linux/macOS: `/tmp/casc/cache`
242
+ - `cdn_server_url`: Optional CDN server URL (e.g., `http://us.patch.battle.net:1119`). If omitted, uses default CDN
243
+ - `code_name`: TACT product code - see [TACT documentation](https://wowdev.wiki/TACT) for available codes
244
+ - Examples: `hero` (Heroes of the Storm), `wow` (World of Warcraft), `s2` (StarCraft II), `d3` (Diablo III)
245
+ - `region`: Optional server region (e.g., `us`, `eu`, `kr`, `tw`, `cn`). If omitted, defaults to `us`
246
+ - `options`: Optional opening options
247
+
248
+ **Examples:**
249
+ ```typescript
250
+ // Windows - Minimal format: cache folder and product code
251
+ storage.openOnline('C:/Temp/CASC/Cache*hero');
252
+
253
+ // Linux/macOS - Minimal format
254
+ storage.openOnline('/tmp/casc/cache*hero');
255
+
256
+ // With CDN server specified
257
+ storage.openOnline('C:/Temp/CASC/Cache*http://us.patch.battle.net:1119*hero');
258
+
259
+ // With region specified (uses default CDN)
260
+ storage.openOnline('/tmp/casc/cache*hero*us');
261
+
262
+ // Full format with all parameters
263
+ storage.openOnline('C:/Temp/CASC/Cache*http://us.patch.battle.net:1119*hero*us');
264
+
265
+ // World of Warcraft EU region
266
+ storage.openOnline('/tmp/casc/cache*wow*eu');
267
+ ```
268
+
269
+ ##### `close(): boolean`
270
+ Closes the storage and releases resources.
271
+
272
+ **Returns:** `true` if closed successfully, `false` otherwise.
273
+
274
+ ```typescript
275
+ const closed = storage.close();
276
+ ```
277
+
278
+ ##### `getStorageInfo(infoClass: number): StorageInfo`
279
+ Gets storage information.
280
+
281
+ **Parameters:**
282
+ - `infoClass`: The type of information to retrieve
109
283
 
110
- #### `openFile(filename: string, options?: FileOpenOptions): CascFile`
284
+ **Returns:** Storage information object
285
+
286
+ #### File Operations
287
+
288
+ ##### `openFile(filename: string, options?: FileOpenOptions): File`
111
289
  Opens a file from the storage.
112
290
 
113
- #### `getFileInfo(filename: string): FileInfo | null`
114
- Gets information about a file.
291
+ **Parameters:**
292
+ - `filename`: Name of the file to open (use backslashes for paths)
293
+ - `options`: Optional opening options
294
+ - `flags`: Open flags (number)
295
+
296
+ **Returns:** A `File` object
115
297
 
116
- #### `fileExists(filename: string): boolean`
298
+ **Example:**
299
+ ```typescript
300
+ const file = storage.openFile('mods/heroesdata.stormmod/base.stormdata/GameData/HeroData.xml');
301
+ ```
302
+
303
+ ##### `fileExists(filename: string): boolean`
117
304
  Checks if a file exists in the storage.
118
305
 
119
- ### CascFile
306
+ **Parameters:**
307
+ - `filename`: Name of the file to check
308
+
309
+ **Returns:** `true` if file exists, `false` otherwise
310
+
311
+ **Example:**
312
+ ```typescript
313
+ if (storage.fileExists('some-file.txt')) {
314
+ console.log('File exists!');
315
+ }
316
+ ```
317
+
318
+ ##### `getFileInfo(filename: string): FileInfo | null`
319
+ Gets information about a file.
320
+
321
+ **Parameters:**
322
+ - `filename`: Name of the file
323
+
324
+ **Returns:** File information object or `null` if file doesn't exist
325
+
326
+ **TypeScript Interface:**
327
+ ```typescript
328
+ interface FileInfo {
329
+ name: string;
330
+ size: number;
331
+ }
332
+ ```
333
+
334
+ **Example:**
335
+ ```typescript
336
+ const info = storage.getFileInfo('some-file.txt');
337
+ if (info) {
338
+ console.log(`Name: ${info.name}, Size: ${info.size} bytes`);
339
+ }
340
+ ```
341
+
342
+ #### File Finding
343
+
344
+ ##### `findFirstFile(mask?: string, listFile?: string): FindData | null`
345
+ Finds the first file matching the mask.
346
+
347
+ **Parameters:**
348
+ - `mask`: File mask pattern (e.g., `*.lua`, `Interface\\*`)
349
+ - `listFile`: Optional list file path
350
+
351
+ **Returns:** Find data object or `null` if no files found
352
+
353
+ **Example:**
354
+ ```typescript
355
+ const findData = storage.findFirstFile('*.xml');
356
+ if (findData) {
357
+ console.log(`Found: ${findData.fileName}`);
358
+ }
359
+ ```
360
+
361
+ ##### `findNextFile(): FindData | null`
362
+ Finds the next file in the search.
363
+
364
+ **Returns:** Find data object or `null` if no more files
365
+
366
+ **Example:**
367
+ ```typescript
368
+ let nextFile = storage.findNextFile();
369
+ while (nextFile) {
370
+ console.log(`Found: ${nextFile.fileName}`);
371
+ nextFile = storage.findNextFile();
372
+ }
373
+ ```
374
+
375
+ ##### `findClose(): boolean`
376
+ Closes the current find operation.
377
+
378
+ **Returns:** `true` if closed successfully
379
+
380
+ **Example:**
381
+ ```typescript
382
+ storage.findClose();
383
+ ```
384
+
385
+ #### Encryption Key Management
386
+
387
+ ##### `addEncryptionKey(keyName: number, key: Buffer): boolean`
388
+ Adds an encryption key to the storage.
389
+
390
+ **Parameters:**
391
+ - `keyName`: Name/ID of the key
392
+ - `key`: Key data as Buffer
393
+
394
+ **Returns:** `true` if added successfully
395
+
396
+ **Example:**
397
+ ```typescript
398
+ const keyData = Buffer.from('your-key-bytes');
399
+ storage.addEncryptionKey(0x12345678, keyData);
400
+ ```
401
+
402
+ ##### `addStringEncryptionKey(keyName: number, keyStr: string): boolean`
403
+ Adds an encryption key from a string.
404
+
405
+ **Parameters:**
406
+ - `keyName`: Name/ID of the key
407
+ - `keyStr`: Key as string
120
408
 
121
- #### `read(bytesToRead?: number): Buffer`
122
- Reads data from the file. Default: 4096 bytes.
409
+ **Returns:** `true` if added successfully
123
410
 
124
- #### `readAll(): Buffer`
411
+ **Example:**
412
+ ```typescript
413
+ storage.addStringEncryptionKey(0x12345678, 'your-key-string');
414
+ ```
415
+
416
+ ##### `importKeysFromString(keyList: string): boolean`
417
+ Imports encryption keys from a string.
418
+
419
+ **Parameters:**
420
+ - `keyList`: String containing key list
421
+
422
+ **Returns:** `true` if imported successfully
423
+
424
+ ##### `importKeysFromFile(filePath: string): boolean`
425
+ Imports encryption keys from a file.
426
+
427
+ **Parameters:**
428
+ - `filePath`: Path to the key file
429
+
430
+ **Returns:** `true` if imported successfully
431
+
432
+ **Example:**
433
+ ```typescript
434
+ storage.importKeysFromFile('/path/to/keys.txt');
435
+ ```
436
+
437
+ ##### `findEncryptionKey(keyName: number): Buffer | null`
438
+ Finds an encryption key by name.
439
+
440
+ **Parameters:**
441
+ - `keyName`: Name/ID of the key
442
+
443
+ **Returns:** Key data or `null` if not found
444
+
445
+ **Example:**
446
+ ```typescript
447
+ const key = storage.findEncryptionKey(0x12345678);
448
+ if (key) {
449
+ console.log('Key found:', key);
450
+ }
451
+ ```
452
+
453
+ ##### `getNotFoundEncryptionKey(): number | null`
454
+ Gets the name of an encryption key that was not found.
455
+
456
+ **Returns:** Key name or `null`
457
+
458
+ ---
459
+
460
+ ### File
461
+
462
+ #### Constructor
463
+
464
+ The `File` class is instantiated by calling `storage.openFile()`. Do not construct it directly.
465
+
466
+ #### File Reading
467
+
468
+ ##### `read(bytesToRead?: number): Buffer`
469
+ Reads data from the file at the current position.
470
+
471
+ **Parameters:**
472
+ - `bytesToRead`: Number of bytes to read (default: 4096)
473
+
474
+ **Returns:** Buffer containing the read data
475
+
476
+ **Example:**
477
+ ```typescript
478
+ const chunk = file.read(1024); // Read 1024 bytes
479
+ ```
480
+
481
+ ##### `readAll(): Buffer`
125
482
  Reads all data from the file.
126
483
 
127
- #### `getSize(): number`
128
- Gets the file size in bytes.
484
+ **Returns:** Buffer containing all file data
485
+
486
+ **Example:**
487
+ ```typescript
488
+ const content = file.readAll();
489
+ console.log(content.toString());
490
+ ```
491
+
492
+ #### File Information
493
+
494
+ ##### `getSize(): number`
495
+ Gets the file size in bytes (32-bit).
496
+
497
+ **Returns:** File size as a 32-bit number
498
+
499
+ **Example:**
500
+ ```typescript
501
+ const size = file.getSize();
502
+ console.log(`File size: ${size} bytes`);
503
+ ```
504
+
505
+ ##### `getSize64(): number`
506
+ Gets the file size in bytes (64-bit).
507
+
508
+ **Returns:** File size as a 64-bit number
509
+
510
+ **Example:**
511
+ ```typescript
512
+ const size = file.getSize64();
513
+ ```
514
+
515
+ ##### `getFileInfo(infoClass: number): FileInfoResult`
516
+ Gets detailed file information.
517
+
518
+ **Parameters:**
519
+ - `infoClass`: The type of information to retrieve
520
+
521
+ **Returns:** File information result object
522
+
523
+ #### File Position
524
+
525
+ ##### `getPosition(): number`
526
+ Gets the current file position (32-bit).
527
+
528
+ **Returns:** Current position in bytes
529
+
530
+ **Example:**
531
+ ```typescript
532
+ const pos = file.getPosition();
533
+ console.log(`Current position: ${pos}`);
534
+ ```
535
+
536
+ ##### `getPosition64(): number`
537
+ Gets the current file position (64-bit).
538
+
539
+ **Returns:** Current position in bytes
129
540
 
130
- #### `getPosition(): number`
131
- Gets the current file position.
541
+ ##### `setPosition(position: number): number`
542
+ Sets the file position (32-bit).
132
543
 
133
- #### `setPosition(position: number): number`
134
- Sets the file position.
544
+ **Parameters:**
545
+ - `position`: New position in bytes
135
546
 
136
- #### `close(): boolean`
137
- Closes the file.
547
+ **Returns:** The new position
548
+
549
+ **Example:**
550
+ ```typescript
551
+ file.setPosition(0); // Reset to beginning
552
+ file.setPosition(100); // Jump to byte 100
553
+ ```
554
+
555
+ ##### `setPosition64(position: number, moveMethod?: number): number`
556
+ Sets the file position (64-bit) with move method.
557
+
558
+ **Parameters:**
559
+ - `position`: New position in bytes
560
+ - `moveMethod`: Optional move method (FILE_BEGIN, FILE_CURRENT, FILE_END)
561
+
562
+ **Returns:** The new position
563
+
564
+ **Example:**
565
+ ```typescript
566
+ file.setPosition64(0); // Reset to beginning
567
+ ```
568
+
569
+ #### File Operations
570
+
571
+ ##### `setFileFlags(flags: number): boolean`
572
+ Sets file flags.
573
+
574
+ **Parameters:**
575
+ - `flags`: Flags to set
576
+
577
+ **Returns:** `true` if set successfully
578
+
579
+ ##### `close(): boolean`
580
+ Closes the file and releases resources.
581
+
582
+ **Returns:** `true` if closed successfully, `false` otherwise
583
+
584
+ **Example:**
585
+ ```typescript
586
+ file.close();
587
+ ```
588
+
589
+ ---
590
+
591
+ ## TypeScript Interfaces
592
+
593
+ ```typescript
594
+ interface StorageOpenOptions {
595
+ flags?: number;
596
+ }
597
+
598
+ interface FileOpenOptions {
599
+ flags?: number;
600
+ }
601
+
602
+ interface FileInfo {
603
+ name: string;
604
+ size: number;
605
+ }
606
+
607
+ interface FindData {
608
+ fileName: string;
609
+ fileSize: number;
610
+ localeFlags: number;
611
+ fileDataId: number;
612
+ contentFlags: number;
613
+ // ... additional fields
614
+ }
615
+
616
+ interface StorageInfo {
617
+ // Storage-specific information
618
+ }
619
+
620
+ interface FileInfoResult {
621
+ // File-specific information
622
+ }
623
+ ```
624
+
625
+ ## Advanced Usage
626
+
627
+ ### Direct Binding Access
628
+
629
+ For advanced users who need direct access to the native bindings:
630
+
631
+ ```typescript
632
+ import { CascStorageBinding, CascStorage, CascFile } from '@jamiephan/casclib/bindings';
633
+ import * as constants from '@jamiephan/casclib';
634
+
635
+ const storage: CascStorage = new CascStorageBinding();
636
+ storage.CascOpenStorage('/path/to/storage', 0);
637
+
638
+ const file: CascFile = storage.CascOpenFile('filename.txt', constants.CASC_OPEN_BY_NAME);
639
+ const size = file.CascGetFileSize64();
640
+ const content = file.readFileAll();
641
+ file.CascCloseFile();
642
+
643
+ storage.CascCloseStorage();
644
+ ```
645
+
646
+ ### Binding Naming Convention
647
+
648
+ The low-level bindings use **exact names from CascLib.h**:
649
+ - C++ function: `CascOpenStorageEx` → JS binding: `CascOpenStorageEx`
650
+ - C++ function: `CascGetFileSize64` → JS binding: `CascGetFileSize64`
651
+ - Interfaces are prefixed with `CASC`: `CascStorage`, `CascFile`, `CascFindData`, etc.
652
+
653
+ The high-level wrapper simplifies these names:
654
+ - Binding: `CascOpenStorageEx` → Wrapper: `openEx()`
655
+ - Binding: `CascGetFileSize64` → Wrapper: `getSize64()`
656
+
657
+ See [BINDING_NAMING_CONVENTION.md](BINDING_NAMING_CONVENTION.md) for complete details.
658
+
659
+ ## Performance Tips
660
+
661
+ 1. **Use `readAll()` for small files**: More efficient than multiple `read()` calls
662
+ 2. **Use `read(size)` for large files**: Better memory management for streaming
663
+ 3. **Close files and storage**: Always close resources when done to prevent memory leaks
664
+ 4. **Online storage caching**: First access downloads data to temp directory for better subsequent performance
665
+
666
+ ## Error Handling
667
+
668
+ All methods that can fail will throw exceptions. Always use try-catch blocks:
669
+
670
+ ```typescript
671
+ try {
672
+ const storage = new Storage();
673
+ storage.open('/path/to/storage');
674
+
675
+ if (storage.fileExists('some-file.txt')) {
676
+ const file = storage.openFile('some-file.txt');
677
+ const content = file.readAll();
678
+ file.close();
679
+ }
680
+
681
+ storage.close();
682
+ } catch (error) {
683
+ console.error('Error processing storage:', error);
684
+ }
685
+ ```
138
686
 
139
687
  ## License
140
688
 
Binary file