@gesslar/toolkit 0.0.12 → 0.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.
@@ -4,11 +4,16 @@
4
4
  * resolution and existence checks.
5
5
  */
6
6
 
7
+ import JSON5 from "json5"
8
+ import fs from "node:fs/promises"
7
9
  import path from "node:path"
8
10
  import util from "node:util"
11
+ import YAML from "yaml"
9
12
 
10
13
  import DirectoryObject from "./DirectoryObject.js"
11
- import File from "./File.js"
14
+ import FS from "./FS.js"
15
+ import Sass from "./Sass.js"
16
+ import Valid from "./Valid.js"
12
17
 
13
18
  /**
14
19
  * FileObject encapsulates metadata and operations for a file, including path
@@ -26,7 +31,20 @@ import File from "./File.js"
26
31
  * @property {Promise<boolean>} exists - Whether the file exists (async)
27
32
  */
28
33
 
29
- export default class FileObject {
34
+ export default class FileObject extends FS {
35
+ /**
36
+ * Configuration mapping data types to their respective parser modules for loadData method.
37
+ * Each parser module must have a .parse() method that accepts a string and returns parsed data.
38
+ *
39
+ * @type {{[key: string]: Array<typeof JSON5 | typeof YAML>}}
40
+ */
41
+ static dataLoaderConfig = Object.freeze({
42
+ json5: [JSON5],
43
+ json: [JSON5],
44
+ yaml: [YAML],
45
+ any: [JSON5, YAML]
46
+ })
47
+
30
48
  /**
31
49
  * @type {object}
32
50
  * @private
@@ -59,19 +77,26 @@ export default class FileObject {
59
77
  * @param {DirectoryObject|string|null} [directory] - The parent directory (object or string)
60
78
  */
61
79
  constructor(fileName, directory=null) {
62
- const fixedFile = File.fixSlashes(fileName)
80
+ super()
63
81
 
64
- const {dir,base,ext} = File.deconstructFilenameToParts(fixedFile)
82
+ if(!fileName || typeof fileName !== "string" || fileName.length === 0) {
83
+ throw Sass.new("fileName must be a non-empty string")
84
+ }
85
+
86
+ const fixedFile = FS.fixSlashes(fileName)
87
+
88
+ const {dir,base,ext} = this.#deconstructFilenameToParts(fixedFile)
65
89
 
66
- if(!directory)
90
+ if(!directory) {
67
91
  directory = new DirectoryObject(dir)
92
+ } else if(typeof directory === "string") {
93
+ directory = new DirectoryObject(directory)
94
+ }
68
95
 
69
- const final = path.isAbsolute(fixedFile)
70
- ? fixedFile
71
- : path.resolve(directory?.path ?? ".", fixedFile)
96
+ const final = FS.resolvePath(directory?.path ?? ".", fixedFile)
72
97
 
73
98
  const resolved = final
74
- const fileUri = File.pathToUri(resolved)
99
+ const fileUri = FS.pathToUri(resolved)
75
100
 
76
101
  this.#meta.supplied = fixedFile
77
102
  this.#meta.path = resolved
@@ -80,7 +105,7 @@ export default class FileObject {
80
105
  this.#meta.extension = ext
81
106
  this.#meta.module = path.basename(this.supplied, this.extension)
82
107
 
83
- const {dir: newDir} = File.deconstructFilenameToParts(this.path)
108
+ const {dir: newDir} = this.#deconstructFilenameToParts(this.path)
84
109
 
85
110
  this.#meta.directory = new DirectoryObject(newDir)
86
111
 
@@ -92,6 +117,9 @@ export default class FileObject {
92
117
  *
93
118
  * @returns {string} string representation of the FileObject
94
119
  */
120
+ toString() {
121
+ return `[FileObject: ${this.path}]`
122
+ }
95
123
 
96
124
  /**
97
125
  * Returns a JSON representation of the FileObject.
@@ -127,7 +155,7 @@ export default class FileObject {
127
155
  * @returns {Promise<boolean>} - A Promise that resolves to true or false
128
156
  */
129
157
  get exists() {
130
- return File.fileExists(this)
158
+ return this.#fileExists()
131
159
  }
132
160
 
133
161
  /**
@@ -219,4 +247,165 @@ export default class FileObject {
219
247
  get directory() {
220
248
  return this.#meta.directory
221
249
  }
250
+
251
+ /**
252
+ * Check if a file can be read. Returns true if the file can be read, false
253
+ *
254
+ * @returns {Promise<boolean>} Whether the file can be read
255
+ */
256
+ async canRead() {
257
+ try {
258
+ await fs.access(this.path, fs.constants.R_OK)
259
+
260
+ return true
261
+ } catch(_) {
262
+ return false
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Check if a file can be written. Returns true if the file can be written,
268
+ *
269
+ * @returns {Promise<boolean>} Whether the file can be written
270
+ */
271
+ async canWrite() {
272
+ try {
273
+ await fs.access(this.path, fs.constants.W_OK)
274
+
275
+ return true
276
+ } catch(_) {
277
+ return false
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Check if a file exists
283
+ *
284
+ * @returns {Promise<boolean>} Whether the file exists
285
+ */
286
+ async #fileExists() {
287
+ try {
288
+ await fs.access(this.path, fs.constants.F_OK)
289
+
290
+ return true
291
+ } catch(_) {
292
+ return false
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Determines the size of a file.
298
+ *
299
+ * @returns {Promise<number?>} - The size of the file or null, if it doesn't exist.
300
+ */
301
+ async size() {
302
+ try {
303
+ const stat = await fs.stat(this.path)
304
+
305
+ return stat.size
306
+ } catch(_) {
307
+ return null
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Gets the last modification time of a file.
313
+ * Used by the caching system to determine if cached data is still valid.
314
+ *
315
+ * @returns {Promise<Date?>} The last modification time, or null if file doesn't exist
316
+ */
317
+ async modified() {
318
+ try {
319
+ const stat = await fs.stat(this.path)
320
+
321
+ return stat.mtime
322
+ } catch(_) {
323
+ return null
324
+ }
325
+ }
326
+
327
+ /**
328
+ * @typedef {object} FileParts
329
+ * @property {string} base - The file name with extension
330
+ * @property {string} dir - The directory path
331
+ * @property {string} ext - The file extension (including dot)
332
+ */
333
+
334
+ /**
335
+ * Deconstruct a filename into parts
336
+ *
337
+ * @param {string} fileName - The filename to deconstruct
338
+ * @returns {FileParts} The filename parts
339
+ */
340
+ #deconstructFilenameToParts(fileName) {
341
+ Valid.assert(typeof fileName === "string" && fileName.length > 0,
342
+ "file must be a non-zero length string", 1)
343
+
344
+ return path.parse(fileName)
345
+ }
346
+
347
+ /**
348
+ * Reads the content of a file asynchronously.
349
+ *
350
+ * @param {string} [encoding] - The encoding to read the file as.
351
+ * @returns {Promise<string>} The file contents
352
+ */
353
+ async read(encoding="utf8") {
354
+ const filePath = this.path
355
+
356
+ if(!(await this.exists))
357
+ throw Sass.new(`No such file '${filePath}'`)
358
+
359
+ if(!filePath)
360
+ throw Sass.new("No absolute path in file map")
361
+
362
+ return await fs.readFile(filePath, encoding)
363
+ }
364
+
365
+ /**
366
+ * Writes content to a file synchronously.
367
+ *
368
+ * @param {string} content - The content to write
369
+ * @param {string} encoding - The encoding in which to write.
370
+ * @returns {Promise<void>}
371
+ */
372
+ async write(content, encoding="utf8") {
373
+ if(!this.path)
374
+ throw Sass.new("No absolute path in file")
375
+
376
+ await fs.writeFile(this.path, content, encoding)
377
+ }
378
+
379
+ /**
380
+ * Loads an object from JSON or YAML provided a fileMap
381
+ *
382
+ * @param {string} [type] - The expected type of data to parse.
383
+ * @param {string} [encoding] - The encoding to read the file as.
384
+ * @returns {Promise<unknown>} The parsed data object.
385
+ */
386
+ async loadData(type="any", encoding="utf8") {
387
+ const content = await this.read(encoding)
388
+ const toTry = {
389
+ json5: [JSON5],
390
+ json: [JSON5],
391
+ yaml: [YAML],
392
+ any: [JSON5,YAML]
393
+ }[type.toLowerCase()]
394
+
395
+ if(!toTry) {
396
+ throw Sass.new(`Unsupported data type '${type}'. Supported types: json, json5, yaml, any`)
397
+ }
398
+
399
+ for(const format of toTry) {
400
+ try {
401
+ const result = format.parse(content)
402
+
403
+ return result
404
+ } catch {
405
+ // nothing to see here
406
+ }
407
+ }
408
+
409
+ throw Sass.new(`Content is neither valid JSON5 nor valid YAML:\n'${this.path}'`)
410
+ }
222
411
  }
package/src/lib/Glog.js CHANGED
@@ -21,9 +21,9 @@ import console from "node:console"
21
21
  */
22
22
  class Glog {
23
23
  /** @type {number} Current log level threshold (0-5) */
24
- logLevel = 0
24
+ static logLevel = 0
25
25
  /** @type {string} Prefix to prepend to all log messages */
26
- logPrefix = ""
26
+ static logPrefix = ""
27
27
 
28
28
  /**
29
29
  * Sets the log prefix for all subsequent log messages.
@@ -39,7 +39,7 @@ class Glog {
39
39
  static setLogPrefix(prefix) {
40
40
  this.logPrefix = prefix
41
41
 
42
- return Glog
42
+ return this
43
43
  }
44
44
 
45
45
  /**
@@ -57,7 +57,7 @@ class Glog {
57
57
  static setLogLevel(level) {
58
58
  this.logLevel = Data.clamp(level, 0, 5)
59
59
 
60
- return Glog
60
+ return this
61
61
  }
62
62
 
63
63
  /**
@@ -75,11 +75,11 @@ class Glog {
75
75
  let level, rest
76
76
 
77
77
  if(args.length === 0) {
78
- ;[level=0, rest=[""]] = null
78
+ ;[level=0, rest=[""]] = []
79
79
  } else if(args.length === 1) {
80
80
  ;[rest, level=0] = [args, 0]
81
81
  } else {
82
- ;[level, ...rest] = args
82
+ ;[level, ...rest] = typeof args[0] === "number" ? args : [0, ...args]
83
83
  }
84
84
 
85
85
  if(level > this.logLevel)
@@ -123,10 +123,18 @@ class Glog {
123
123
  // Wrap the class in a proxy
124
124
  export default new Proxy(Glog, {
125
125
  apply(target, thisArg, argumentsList) {
126
- // When called as function: MyClass(things, and, stuff)
126
+ // When called as function: call execute method internally
127
127
  return target.execute(...argumentsList)
128
128
  },
129
129
  construct(target, argumentsList) {
130
130
  return new target(...argumentsList)
131
+ },
132
+ get(target, prop) {
133
+ // Hide execute method from public API
134
+ if(prop === "execute") {
135
+ return undefined
136
+ }
137
+
138
+ return target[prop]
131
139
  }
132
140
  })
package/src/lib/Type.js CHANGED
@@ -156,20 +156,33 @@ export default class TypeSpec {
156
156
  const matchingTypeSpec = this.filter(spec => {
157
157
  const {typeName: allowedType, array: allowedArray} = spec
158
158
 
159
- if(valueType === allowedType && !isArray && !allowedArray)
160
- return !allowEmpty ? !empty : true
159
+ // Handle non-array values
160
+ if(!isArray && !allowedArray) {
161
+ if(valueType === allowedType)
162
+ return allowEmpty || !empty
161
163
 
164
+ return false
165
+ }
166
+
167
+ // Handle array values
162
168
  if(isArray) {
163
- if(allowedType === "array")
164
- if(!allowedArray)
165
- return true
169
+ // Special case for generic "array" type
170
+ if(allowedType === "array" && !allowedArray)
171
+ return allowEmpty || !empty
172
+
173
+ // Must be an array type specification
174
+ if(!allowedArray)
175
+ return false
166
176
 
177
+ // Handle empty arrays
167
178
  if(empty)
168
- if(allowEmpty)
169
- return true
179
+ return allowEmpty
170
180
 
181
+ // Check if array elements match the required type
171
182
  return Data.isArrayUniform(value, allowedType)
172
183
  }
184
+
185
+ return false
173
186
  })
174
187
 
175
188
  return matchingTypeSpec.length > 0
package/src/lib/Util.js CHANGED
@@ -98,7 +98,7 @@ export default class Util {
98
98
  .reduce((acc, curr) => acc.sign === "--" ? acc : curr, {})
99
99
  ?.option
100
100
  })
101
- .filter(Boolean)
101
+ .filter(option => option && /^[a-zA-Z0-9]/.test(option)) // Filter out options that don't start with alphanumeric
102
102
  }
103
103
 
104
104
  /**
@@ -184,6 +184,11 @@ export default class Util {
184
184
  } catch(error) {
185
185
  const argsDesc = args.length > 0 ? `with arguments: ${args.map(String).join(", ")}` : "with no arguments"
186
186
 
187
+ // If it's already a Sass error, just re-throw to avoid double-wrapping
188
+ if(error instanceof Sass) {
189
+ throw error
190
+ }
191
+
187
192
  throw Sass.new(
188
193
  `Processing '${event}' event ${argsDesc}.`,
189
194
  error
@@ -214,6 +219,11 @@ export default class Util {
214
219
  } catch(error) {
215
220
  const argsDesc = args.length > 0 ? `with arguments: ${args.map(String).join(", ")}` : "with no arguments"
216
221
 
222
+ // If it's already a Sass error, just re-throw to avoid double-wrapping
223
+ if(error instanceof Sass) {
224
+ throw error
225
+ }
226
+
217
227
  throw Sass.new(
218
228
  `Processing '${event}' event ${argsDesc}.`,
219
229
  error
@@ -24,7 +24,7 @@ declare class Cache {
24
24
  * @returns The parsed file data (JSON5 or YAML)
25
25
  * @throws If the file cannot be found or accessed
26
26
  */
27
- loadCachedData(fileObject: FileObject): Promise<object>
27
+ loadCachedData(fileObject: FileObject): Promise<unknown>
28
28
  }
29
29
 
30
30
  export default Cache
@@ -1,11 +1,21 @@
1
1
  // Implementation: ../lib/DirectoryObject.js
2
2
  // Type definitions for DirectoryObject
3
3
 
4
+ import FS from './FS.js'
5
+ import FileObject from './FileObject.js'
6
+
7
+ export interface DirectoryListing {
8
+ /** Array of FileObject instances */
9
+ files: Array<FileObject>
10
+ /** Array of DirectoryObject instances */
11
+ directories: Array<DirectoryObject>
12
+ }
13
+
4
14
  /**
5
15
  * DirectoryObject encapsulates metadata and operations for a directory,
6
16
  * including path resolution and existence checks.
7
17
  */
8
- export default class DirectoryObject {
18
+ export default class DirectoryObject extends FS {
9
19
  /**
10
20
  * Create a new DirectoryObject instance.
11
21
  * @param directory - The directory path
@@ -53,4 +63,10 @@ export default class DirectoryObject {
53
63
  isFile: boolean
54
64
  isDirectory: boolean
55
65
  }
56
- }
66
+
67
+ /** List the contents of this directory */
68
+ read(): Promise<DirectoryListing>
69
+
70
+ /** Ensure this directory exists, creating it if necessary */
71
+ assureExists(options?: any): Promise<void>
72
+ }
@@ -0,0 +1,31 @@
1
+ // Implementation: ../lib/FS.js
2
+ // Type definitions for FS utilities
3
+
4
+ import FileObject from './FileObject.js'
5
+ import DirectoryObject from './DirectoryObject.js'
6
+
7
+ /**
8
+ * Base filesystem utilities class. FileObject and DirectoryObject extend this class.
9
+ */
10
+ export default class FS {
11
+ /** Fix slashes in a path */
12
+ static fixSlashes(pathName: string): string
13
+
14
+ /** Convert a path to a URI */
15
+ static pathToUri(pathName: string): string
16
+
17
+ /** Convert a URI to a path */
18
+ static uriToPath(pathName: string): string
19
+
20
+ /** Retrieve files matching glob pattern(s) */
21
+ static getFiles(glob: string | Array<string>): Promise<Array<FileObject>>
22
+
23
+ /** Compute relative path between two file system objects */
24
+ static relativeOrAbsolutePath(from: FileObject | DirectoryObject, to: FileObject | DirectoryObject): string
25
+
26
+ /** Merge two paths by finding overlapping segments and combining them efficiently */
27
+ static mergeOverlappingPaths(path1: string, path2: string, sep?: string): string
28
+
29
+ /** Resolve a path relative to another path using various strategies. Handles absolute paths, relative navigation, and overlap-based merging */
30
+ static resolvePath(fromPath: string, toPath: string): string
31
+ }
@@ -1,6 +1,15 @@
1
1
  // Implementation: ../lib/FileObject.js
2
2
 
3
3
  import DirectoryObject from './DirectoryObject.js'
4
+ import FS from './FS.js'
5
+
6
+ /**
7
+ * Configuration for data loading parsers in loadData method.
8
+ * Maps supported data types to their respective parser functions.
9
+ */
10
+ export interface DataLoaderConfig {
11
+ [type: string]: Array<{ parse: (content: string) => unknown }>
12
+ }
4
13
 
5
14
  /**
6
15
  * FileObject encapsulates metadata and operations for a file, providing intelligent
@@ -60,7 +69,7 @@ import DirectoryObject from './DirectoryObject.js'
60
69
  *
61
70
  * for (const fileItem of files) {
62
71
  * console.log(`${fileItem.module}${fileItem.extension} -> ${fileItem.path}`)
63
- *
72
+ *
64
73
  * // Type-based processing
65
74
  * switch (fileItem.extension) {
66
75
  * case '.json':
@@ -99,7 +108,13 @@ import DirectoryObject from './DirectoryObject.js'
99
108
  * inherits the directory's resolved path, ensuring consistency in hierarchical
100
109
  * file operations.
101
110
  */
102
- export default class FileObject {
111
+ export default class FileObject extends FS {
112
+ /**
113
+ * Configuration for data parsing in the loadData method.
114
+ * Maps data type names to arrays of parser functions.
115
+ */
116
+ static readonly dataLoaderConfig: DataLoaderConfig
117
+
103
118
  /**
104
119
  * Create a new FileObject instance with intelligent path resolution.
105
120
  *
@@ -142,8 +157,8 @@ export default class FileObject {
142
157
  * // Complex directory structures and nested files
143
158
  * const projectRoot = new DirectoryObject('./my-project')
144
159
  * const srcDir = new DirectoryObject('src', projectRoot)
145
- *
146
- * // Create files within nested directory structure
160
+ *
161
+ * // Create files within nested directory structure
147
162
  * const mainApp = new FileObject('App.tsx', srcDir)
148
163
  * const stylesheet = new FileObject('styles/main.css', srcDir)
149
164
  * const testFile = new FileObject('__tests__/App.test.tsx', srcDir)
@@ -151,7 +166,7 @@ export default class FileObject {
151
166
  * console.log('Main app:', mainApp.path) // /absolute/path/my-project/src/App.tsx
152
167
  * console.log('Stylesheet:', stylesheet.path) // /absolute/path/my-project/src/styles/main.css
153
168
  * console.log('Test file:', testFile.path) // /absolute/path/my-project/src/__tests__/App.test.tsx
154
- *
169
+ *
155
170
  * // All files share the same parent directory reference
156
171
  * console.log('Same parent?', mainApp.directory === srcDir) // true
157
172
  * ```
@@ -160,7 +175,7 @@ export default class FileObject {
160
175
 
161
176
  /**
162
177
  * The original user-supplied path string used during construction.
163
- *
178
+ *
164
179
  * Preserves the exact path string passed to the constructor, including
165
180
  * any relative path indicators (./, ../) or path separators. Useful
166
181
  * for debugging, logging, or when you need to recreate the original
@@ -170,7 +185,7 @@ export default class FileObject {
170
185
  * ```typescript
171
186
  * const file1 = new FileObject('./config.json')
172
187
  * const file2 = new FileObject('../package.json')
173
- *
188
+ *
174
189
  * console.log(file1.supplied) // './config.json'
175
190
  * console.log(file2.supplied) // '../package.json'
176
191
  * console.log(file1.path) // '/absolute/path/to/config.json'
@@ -181,7 +196,7 @@ export default class FileObject {
181
196
 
182
197
  /**
183
198
  * The fully resolved absolute file path with normalized separators.
184
- *
199
+ *
185
200
  * Automatically resolved during construction using Node.js path utilities.
186
201
  * Always uses forward slashes on Unix systems and backslashes on Windows.
187
202
  * This is the canonical path that should be used for all file operations.
@@ -189,9 +204,9 @@ export default class FileObject {
189
204
  * @example
190
205
  * ```typescript
191
206
  * // Different inputs, same resolved path
192
- * const file1 = new FileObject('./src/../config.json')
207
+ * const file1 = new FileObject('./src/../config.json')
193
208
  * const file2 = new FileObject('config.json')
194
- *
209
+ *
195
210
  * console.log(file1.path) // '/absolute/path/config.json'
196
211
  * console.log(file2.path) // '/absolute/path/config.json'
197
212
  * console.log(file1.path === file2.path) // true
@@ -201,7 +216,7 @@ export default class FileObject {
201
216
 
202
217
  /**
203
218
  * The file URI representation following RFC 3986 standard.
204
- *
219
+ *
205
220
  * Converts the absolute file path to a proper file:// URI scheme,
206
221
  * handling URL encoding for special characters and proper formatting
207
222
  * for cross-platform file URI access.
@@ -209,9 +224,9 @@ export default class FileObject {
209
224
  * @example
210
225
  * ```typescript
211
226
  * const file = new FileObject('./my project/config file.json')
212
- * console.log(file.uri)
227
+ * console.log(file.uri)
213
228
  * // 'file:///absolute/path/my%20project/config%20file.json'
214
- *
229
+ *
215
230
  * // Can be used with URL constructor or file:// handlers
216
231
  * const url = new URL(file.uri)
217
232
  * console.log(url.pathname) // '/absolute/path/my project/config file.json'
@@ -221,7 +236,7 @@ export default class FileObject {
221
236
 
222
237
  /**
223
238
  * The complete filename including extension.
224
- *
239
+ *
225
240
  * Extracted from the resolved path using Node.js path utilities.
226
241
  * Includes the file extension but excludes any directory components.
227
242
  *
@@ -229,7 +244,7 @@ export default class FileObject {
229
244
  * ```typescript
230
245
  * const jsFile = new FileObject('./src/components/Button.tsx')
231
246
  * const configFile = new FileObject('../.env.production')
232
- *
247
+ *
233
248
  * console.log(jsFile.name) // 'Button.tsx'
234
249
  * console.log(configFile.name) // '.env.production'
235
250
  * ```
@@ -238,7 +253,7 @@ export default class FileObject {
238
253
 
239
254
  /**
240
255
  * The filename without its extension, suitable for module identification.
241
- *
256
+ *
242
257
  * Useful for generating module names, import statements, or when you need
243
258
  * the base name without file type information. Handles complex extensions
244
259
  * and dotfiles appropriately.
@@ -247,7 +262,7 @@ export default class FileObject {
247
262
 
248
263
  /**
249
264
  * The file extension including the leading dot.
250
- *
265
+ *
251
266
  * Extracted using Node.js path utilities, always includes the dot prefix.
252
267
  * Returns an empty string for files without extensions. Handles multiple
253
268
  * extensions by returning only the last one.
@@ -262,7 +277,7 @@ export default class FileObject {
262
277
 
263
278
  /**
264
279
  * The parent DirectoryObject containing this file.
265
- *
280
+ *
266
281
  * Automatically created during FileObject construction based on the resolved
267
282
  * file path. Provides access to parent directory operations and maintains
268
283
  * the hierarchical relationship between files and directories.
@@ -271,7 +286,7 @@ export default class FileObject {
271
286
 
272
287
  /**
273
288
  * Promise that resolves to whether the file exists on the filesystem.
274
- *
289
+ *
275
290
  * Performs an asynchronous filesystem check to determine file existence.
276
291
  * The Promise will resolve to true if the file exists and is accessible,
277
292
  * false otherwise. Always await this property before using the result.
@@ -290,4 +305,25 @@ export default class FileObject {
290
305
  isDirectory: boolean
291
306
  directory: string | null
292
307
  }
308
+
309
+ /** Check if a file can be read */
310
+ canRead(): Promise<boolean>
311
+
312
+ /** Check if a file can be written */
313
+ canWrite(): Promise<boolean>
314
+
315
+ /** Get the size of a file */
316
+ size(): Promise<number | null>
317
+
318
+ /** Get the last modification time of a file */
319
+ modified(): Promise<Date | null>
320
+
321
+ /** Read the content of a file */
322
+ read(encoding?: string): Promise<string>
323
+
324
+ /** Write content to a file */
325
+ write(content: string, encoding?: string): Promise<void>
326
+
327
+ /** Load an object from JSON5 or YAML file with type specification */
328
+ loadData(type?: 'json' | 'json5' | 'yaml' | 'any', encoding?: string): Promise<unknown>
293
329
  }
@@ -2,7 +2,7 @@
2
2
  // Core file system abstractions
3
3
  export { default as FileObject } from './FileObject.js'
4
4
  export { default as DirectoryObject } from './DirectoryObject.js'
5
- export { default as File } from './File.js'
5
+ export { default as FS } from './FS.js'
6
6
 
7
7
  // Utility classes
8
8
  export { default as Cache } from './Cache.js'
@@ -15,4 +15,4 @@ export { default as Util } from './Util.js'
15
15
  export { default as Valid } from './Valid.js'
16
16
 
17
17
  // Type exports
18
- export type { FileParts } from './File.js'
18
+ export type { FileParts } from './FS.js'