@gesslar/toolkit 3.9.0 β†’ 3.13.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.
@@ -7,7 +7,6 @@
7
7
  import JSON5 from "json5"
8
8
  import fs from "node:fs/promises"
9
9
  import path from "node:path"
10
- import util from "node:util"
11
10
  import YAML from "yaml"
12
11
  import {URL} from "node:url"
13
12
 
@@ -70,65 +69,61 @@ export default class FileObject extends FS {
70
69
  isFile: true,
71
70
  isDirectory: false,
72
71
  parent: null,
72
+ parentPath: null,
73
73
  })
74
74
 
75
75
  /**
76
76
  * Constructs a FileObject instance.
77
77
  *
78
- * @param {string | FileObject} fileName - The file path or FileObject
78
+ * @param {string} fileName - The file path
79
79
  * @param {DirectoryObject|string|null} [parent] - The parent directory (object or string)
80
80
  */
81
81
  constructor(fileName, parent=null) {
82
82
  super()
83
83
 
84
- // If passed a FileObject, extract its path
85
- if(Data.isType(fileName, "FileObject"))
86
- fileName = fileName.path
87
-
88
- if(!fileName || typeof fileName !== "string" || fileName.length === 0)
89
- throw Sass.new("fileName must be a non-empty string")
84
+ Valid.type(fileName, "String", {allowEmpty: false})
85
+ Valid.type(parent, "Null|String|DirectoryObject", {allowEmpty: false})
90
86
 
91
87
  const fixedFile = FS.fixSlashes(fileName)
92
- const {dir,base,ext} = this.#deconstructFilenameToParts(fixedFile)
88
+ const {dir, base, ext} = FS.pathParts(fixedFile)
93
89
 
94
90
  const parentObject = (() => {
95
- switch(Data.typeOf(parent)) {
96
- case "String":
97
- return new DirectoryObject(parent)
98
- case "DirectoryObject":
99
- case "CappedDirectoryObject":
100
- case "TempDirectoryObject":
101
- return parent
102
- default:
103
- return new DirectoryObject(dir)
104
- }
105
- })()
91
+ if(Data.isType(parent, "String"))
92
+ return new DirectoryObject(parent)
106
93
 
107
- // Use real path if parent is capped, otherwise use path
108
- const parentPath = parentObject.realPath || parentObject.path
109
- const final = FS.resolvePath(parentPath ?? ".", fixedFile)
94
+ if(Data.isType(parent, "DirectoryObject"))
95
+ return parent
110
96
 
111
- const resolved = final
112
- const url = new URL(FS.pathToUri(resolved))
97
+ return new DirectoryObject(dir)
98
+ })()
113
99
 
114
- // Compute the actual parent directory from the resolved path
115
- const actualParentPath = path.dirname(resolved)
100
+ // If the parent is passed, we need to treat the fileName as relative,
101
+ // regardless of what you-know-who says.
102
+ const resolvedFilename = parent
103
+ ? FS.absoluteToRelative(fixedFile, true)
104
+ : fixedFile
116
105
 
117
- // If the file is directly in the provided parent directory, reuse that object
118
- // Otherwise, create a DirectoryObject for the actual parent directory
119
- // Use real path for comparison if parent is capped
120
- const parentRealPath = parentObject.realPath || parentObject.path
121
- const actualParent = parentObject && actualParentPath === parentRealPath
122
- ? parentObject
123
- : new DirectoryObject(actualParentPath)
106
+ // Use real path if parent is capped, otherwise use path
107
+ const parentPath = parentObject.real?.path || parentObject.path
108
+ const resolved = FS.resolvePath(parentPath ?? ".", resolvedFilename)
109
+ const {dir: actualParent} = FS.pathParts(resolved)
110
+ const url = new URL(FS.pathToUrl(resolved))
124
111
 
125
- this.#meta.supplied = fixedFile
112
+ this.#meta.supplied = fileName
126
113
  this.#meta.path = resolved
127
114
  this.#meta.url = url
128
115
  this.#meta.name = base
129
116
  this.#meta.extension = ext
130
117
  this.#meta.module = path.basename(this.supplied, this.extension)
131
- this.#meta.parent = actualParent
118
+ this.#meta.parentPath = actualParent
119
+ // Preserve capped parent or use actualParent path match
120
+ const useCappedParent =
121
+ parentObject.isCapped ||
122
+ FS.fixSlashes(actualParent) === FS.fixSlashes(parentObject.path)
123
+
124
+ this.#meta.parent = useCappedParent
125
+ ? parentObject
126
+ : new DirectoryObject(actualParent)
132
127
 
133
128
  Object.freeze(this.#meta)
134
129
  }
@@ -139,35 +134,9 @@ export default class FileObject extends FS {
139
134
  * @returns {string} string representation of the FileObject
140
135
  */
141
136
  toString() {
142
- return `[FileObject: ${this.path}]`
143
- }
144
-
145
- /**
146
- * Returns a JSON representation of the FileObject.
147
- *
148
- * @returns {object} JSON representation of the FileObject
149
- */
150
- toJSON() {
151
- return {
152
- supplied: this.supplied,
153
- path: this.path,
154
- url: this.url.toString(),
155
- name: this.name,
156
- module: this.module,
157
- extension: this.extension,
158
- isFile: this.isFile,
159
- isDirectory: this.isDirectory,
160
- parent: this.parent ? this.parent.path : null
161
- }
162
- }
163
-
164
- /**
165
- * Custom inspect method for Node.js console.
166
- *
167
- * @returns {object} JSON representation of this object.
168
- */
169
- [util.inspect.custom]() {
170
- return this.toJSON()
137
+ return this.parent.isCapped
138
+ ?`[${this.constructor.name}: ${this.path} β†’ ${this.real.path}]`
139
+ :`[${this.constructor.name}: ${this.path}]`
171
140
  }
172
141
 
173
142
  /**
@@ -190,7 +159,9 @@ export default class FileObject extends FS {
190
159
 
191
160
  /**
192
161
  * Returns the file path. If the parent is a capped directory, returns the
193
- * virtual path relative to the cap. Otherwise returns the real filesystem path.
162
+ * virtual path relative to the cap. Otherwise returns the real filesystem
163
+ * path.
164
+ *
194
165
  * Use `.real.path` to always get the actual filesystem path.
195
166
  *
196
167
  * @returns {string} The file path (virtual if parent is capped, real otherwise)
@@ -200,13 +171,14 @@ export default class FileObject extends FS {
200
171
  const parent = this.#meta.parent
201
172
 
202
173
  // If parent is capped, return virtual path
203
- if(parent?.capped) {
204
- const cap = parent.cap
174
+ if(parent?.isCapped) {
175
+ const cap = parent.cap.real.path
205
176
  const capResolved = path.resolve(cap)
206
- const relative = path.relative(capResolved, realPath)
177
+ const relativeRealPath = FS.absoluteToRelative(realPath)
178
+ const absolute = FS.resolvePath(capResolved, relativeRealPath)
207
179
 
208
180
  // Return with leading slash to indicate it's cap-relative
209
- return "/" + relative.split(path.sep).join("/")
181
+ return FS.absoluteToRelative(absolute)
210
182
  }
211
183
 
212
184
  // Otherwise return real path
@@ -220,12 +192,9 @@ export default class FileObject extends FS {
220
192
  * @returns {URL} The file URL (virtual if parent is capped, real otherwise)
221
193
  */
222
194
  get url() {
223
- const parent = this.#meta.parent
224
-
225
195
  // If parent is capped, return virtual URL
226
- if(parent?.capped) {
227
- return new URL(FS.pathToUri(this.path))
228
- }
196
+ if(this.parent?.isCapped)
197
+ return new URL(FS.pathToUrl(this.path))
229
198
 
230
199
  return this.#meta.url
231
200
  }
@@ -293,6 +262,10 @@ export default class FileObject extends FS {
293
262
  return this.#meta.parent
294
263
  }
295
264
 
265
+ get parentPath() {
266
+ return this.#meta.parentPath
267
+ }
268
+
296
269
  /**
297
270
  * Returns a plain FileObject representing the actual filesystem location.
298
271
  * This provides an "escape hatch" when working with capped directories,
@@ -310,7 +283,7 @@ export default class FileObject extends FS {
310
283
  * file.real.parent.parent // Can traverse outside the cap
311
284
  */
312
285
  get real() {
313
- return new FileObject(this.#meta.path)
286
+ return new FileObject(this.path)
314
287
  }
315
288
 
316
289
  /**
@@ -323,7 +296,7 @@ export default class FileObject extends FS {
323
296
  await fs.access(this.#meta.path, fs.constants.R_OK)
324
297
 
325
298
  return true
326
- } catch(_) {
299
+ } catch {
327
300
  return false
328
301
  }
329
302
  }
@@ -338,7 +311,7 @@ export default class FileObject extends FS {
338
311
  await fs.access(this.#meta.path, fs.constants.W_OK)
339
312
 
340
313
  return true
341
- } catch(_) {
314
+ } catch {
342
315
  return false
343
316
  }
344
317
  }
@@ -353,7 +326,7 @@ export default class FileObject extends FS {
353
326
  await fs.access(this.#meta.path, fs.constants.F_OK)
354
327
 
355
328
  return true
356
- } catch(_) {
329
+ } catch {
357
330
  return false
358
331
  }
359
332
  }
@@ -368,7 +341,7 @@ export default class FileObject extends FS {
368
341
  const stat = await fs.stat(this.#meta.path)
369
342
 
370
343
  return stat.size
371
- } catch(_) {
344
+ } catch {
372
345
  return null
373
346
  }
374
347
  }
@@ -384,31 +357,11 @@ export default class FileObject extends FS {
384
357
  const stat = await fs.stat(this.#meta.path)
385
358
 
386
359
  return stat.mtime
387
- } catch(_) {
360
+ } catch {
388
361
  return null
389
362
  }
390
363
  }
391
364
 
392
- /**
393
- * @typedef {object} FileParts
394
- * @property {string} base - The file name with extension
395
- * @property {string} dir - The directory path
396
- * @property {string} ext - The file extension (including dot)
397
- */
398
-
399
- /**
400
- * Deconstruct a filename into parts
401
- *
402
- * @param {string} fileName - The filename to deconstruct
403
- * @returns {FileParts} The filename parts
404
- */
405
- #deconstructFilenameToParts(fileName) {
406
- Valid.assert(typeof fileName === "string" && fileName.length > 0,
407
- "file must be a non-zero length string", 1)
408
-
409
- return path.parse(fileName)
410
- }
411
-
412
365
  /**
413
366
  * Reads the content of a file asynchronously.
414
367
  *
@@ -464,14 +417,30 @@ export default class FileObject extends FS {
464
417
  * await file.write(JSON.stringify({key: 'value'}))
465
418
  */
466
419
  async write(content, encoding="utf8") {
467
- if(!this.#meta.url)
468
- throw Sass.new("No URL in file")
420
+ const realPath = FS.virtualToRealPath(this)
421
+ if(!realPath)
422
+ throw Sass.new("No actual disk location detected.")
469
423
 
470
- if(await this.parent.exists)
471
- await fs.writeFile(this.#meta.url, content, encoding)
424
+ // On Windows, normalize the parent directory path to handle 8.3 short names
425
+ let pathToWrite = realPath
426
+ if(process.platform === "win32") {
427
+ try {
428
+ const parentPath = path.dirname(realPath)
429
+ const normalizedParent = await fs.realpath(parentPath)
430
+ pathToWrite = path.join(normalizedParent, path.basename(realPath))
431
+ } catch {
432
+ // If normalization fails, use original path
433
+ }
434
+ }
472
435
 
473
- else
474
- throw Sass.new(`Invalid directory, ${this.parent.url.href}`)
436
+ try {
437
+ await fs.writeFile(pathToWrite, content, encoding)
438
+ } catch(error) {
439
+ if(error.code === "ENOENT")
440
+ throw Sass.new(`Invalid directory: ${path.dirname(pathToWrite)}`)
441
+
442
+ throw Sass.from(error, "Failed to write file")
443
+ }
475
444
  }
476
445
 
477
446
  /**
@@ -491,7 +460,7 @@ export default class FileObject extends FS {
491
460
  * await file.writeBinary(buffer)
492
461
  */
493
462
  async writeBinary(data) {
494
- if(!this.#meta.url)
463
+ if(!this.url)
495
464
  throw Sass.new("No URL in file")
496
465
 
497
466
  const exists = await this.parent.exists
@@ -504,7 +473,7 @@ export default class FileObject extends FS {
504
473
 
505
474
  // According to the internet, if it's already binary, I don't need
506
475
  // an encoding. 🀷
507
- return await fs.writeFile(this.#meta.url, bufferData)
476
+ return await fs.writeFile(this.url, bufferData)
508
477
  }
509
478
 
510
479
  /**
@@ -555,7 +524,7 @@ export default class FileObject extends FS {
555
524
  * @returns {Promise<object>} The file contents as a module.
556
525
  */
557
526
  async import() {
558
- const url = this.#meta.url
527
+ const url = this.url
559
528
 
560
529
  if(!url)
561
530
  throw Sass.new("No URL in file map")
@@ -577,7 +546,7 @@ export default class FileObject extends FS {
577
546
  * await file.delete()
578
547
  */
579
548
  async delete() {
580
- const url = this.#meta.url
549
+ const url = this.url
581
550
 
582
551
  if(!url)
583
552
  throw Sass.new("This object does not represent a valid resource.")
@@ -4,13 +4,16 @@
4
4
  * to the OS's temporary directory tree.
5
5
  */
6
6
 
7
- import fs from "node:fs"
7
+ import fs, {mkdirSync, mkdtempSync} from "node:fs"
8
8
  import os from "node:os"
9
9
  import path from "node:path"
10
10
 
11
- import CappedDirectoryObject from "./CappedDirectoryObject.js"
12
11
  import Data from "../browser/lib/Data.js"
12
+ import CappedDirectoryObject from "./CappedDirectoryObject.js"
13
+ import DirectoryObject from "./DirectoryObject.js"
14
+ import FS from "./FS.js"
13
15
  import Sass from "./Sass.js"
16
+ import Valid from "./Valid.js"
14
17
 
15
18
  /**
16
19
  * TempDirectoryObject extends CappedDirectoryObject with the cap set to
@@ -23,6 +26,8 @@ import Sass from "./Sass.js"
23
26
  * @augments CappedDirectoryObject
24
27
  */
25
28
  export default class TempDirectoryObject extends CappedDirectoryObject {
29
+ #tmpReal
30
+ #tmpCap
26
31
 
27
32
  /**
28
33
  * Constructs a TempDirectoryObject instance and creates the directory.
@@ -34,7 +39,7 @@ export default class TempDirectoryObject extends CappedDirectoryObject {
34
39
  * is provided without a parent, creates a new directory with a unique suffix.
35
40
  * If a parent is provided, creates a subdirectory within that parent.
36
41
  *
37
- * @param {string?} [name] - Base name for the temp directory (if empty/null, uses OS temp dir)
42
+ * @param {string?} [directory] - Base name for the temp directory (if empty/null, uses OS temp dir)
38
43
  * @param {TempDirectoryObject?} [parent] - Optional parent temporary directory
39
44
  * @throws {Sass} If name is absolute
40
45
  * @throws {Sass} If name is empty (when parent is provided)
@@ -43,172 +48,118 @@ export default class TempDirectoryObject extends CappedDirectoryObject {
43
48
  * @throws {Sass} If parent's lineage does not trace back to the OS temp directory
44
49
  * @throws {Sass} If directory creation fails
45
50
  * @example
46
- * // Use OS temp directory directly
47
- * const temp = new TempDirectoryObject()
48
- * console.log(temp.path) // "/tmp"
49
- *
50
- * @example
51
51
  * // Create with unique name
52
52
  * const temp = new TempDirectoryObject("myapp")
53
- * console.log(temp.path) // "/tmp/myapp-ABC123"
54
- *
55
- * @example
56
- * // Nested temp directories
57
- * const parent = new TempDirectoryObject("parent")
58
- * const child = new TempDirectoryObject("child", parent)
59
- * await parent.remove() // Removes both parent and child
53
+ * console.log(temp.path) // "/"
54
+ * console.log(temp.real.path) // "/tmp/myapp-ABC123"
60
55
  */
61
- constructor(name, parent=null) {
62
- let dirPath
63
- let cappedParent = parent
64
-
65
- if(!parent) {
66
- // No parent: need to create a capped parent at tmpdir first
67
- cappedParent = new CappedDirectoryObject(os.tmpdir(), null, true)
68
-
69
- if(name) {
70
- // Check if name is a simple name (no separators, not absolute)
71
- const isSimpleName = !path.isAbsolute(name) &&
72
- !name.includes("/") &&
73
- !name.includes("\\") &&
74
- !name.includes(path.sep)
75
-
76
- if(isSimpleName) {
77
- // Simple name: add unique suffix
78
- const prefix = name.endsWith("-") ? name : `${name}-`
79
- const uniqueSuffix =
80
- Math.random()
81
- .toString(36)
82
- .substring(2, 8)
83
- .toUpperCase()
84
- dirPath = `${prefix}${uniqueSuffix}`
85
- } else {
86
- // Complex path: use as-is, let CappedDirectoryObject handle coercion
87
- dirPath = name
88
- }
89
- } else {
90
- // No name: use tmpdir itself (no parent)
91
- dirPath = os.tmpdir()
92
- cappedParent = null
93
- }
94
- } else {
95
- // With parent: validate it's a proper temp directory parent
96
- if(!Data.isType(parent, "CappedDirectoryObject")) {
97
- throw Sass.new(
98
- "Parent must be a CappedDirectoryObject or TempDirectoryObject."
99
- )
100
- }
101
-
102
- // SECURITY: Ensure parent's cap is within tmpdir (prevent escape to other caps)
103
- const tmpdir = path.resolve(os.tmpdir())
104
- const parentCap = path.resolve(parent.cap)
105
-
106
- if(!parentCap.startsWith(tmpdir)) {
107
- throw Sass.new(
108
- `Parent must be capped to OS temp directory (${tmpdir}) or a subdirectory thereof, ` +
109
- `got cap: ${parent.cap}`
110
- )
111
- }
112
-
113
- dirPath = name || ""
114
- if(!dirPath) {
115
- throw Sass.new("Name must not be empty when parent is provided.")
116
- }
117
- }
56
+ constructor(directory, source=null) {
57
+ Valid.type(source, "Null|TempDirectoryObject")
58
+
59
+ directory ||= "temp"
60
+ directory = Data.append(directory, source ? "" : "-")
118
61
 
119
- // Call parent constructor with new signature
120
- super(dirPath, cappedParent, true)
62
+ const parentRealPath = source?.real.path ?? os.tmpdir()
121
63
 
122
- // Temp-specific behavior: create directory immediately
123
- this.#createDirectory()
64
+ if(source && path.isAbsolute(directory)) {
65
+ const {root} = FS.pathParts(directory)
124
66
 
125
- // Re-cap to the temp directory itself for consistent behavior with CappedDirectoryObject
126
- // This makes temp.cap === temp.real.path and temp.parent === null
127
- if(!parent) {
128
- // Only re-cap if this is a root temp directory (no TempDirectoryObject parent)
129
- this._recapToSelf()
67
+ directory = Data.chopLeft(directory, root)
130
68
  }
131
- }
132
69
 
133
- /**
134
- * TempDirectoryObject does not support fromCwd() since it is specifically
135
- * designed to work within the OS temporary directory tree.
136
- *
137
- * @throws {Sass} Always throws an error
138
- */
139
- static fromCwd() {
140
- throw Sass.new(
141
- "TempDirectoryObject.fromCwd() is not supported. " +
142
- "Use CappedDirectoryObject.fromCwd() instead if you need to cap at the current working directory."
143
- )
144
- }
70
+ let realTempDirectoryPath, toSuper
145
71
 
146
- /**
147
- * Creates the directory synchronously on the filesystem.
148
- *
149
- * @private
150
- * @throws {Sass} If directory creation fails
151
- */
152
- #createDirectory() {
153
- try {
154
- // Use recursive: true to create parent directories as needed
155
- fs.mkdirSync(this.realPath, {recursive: true})
156
- } catch(e) {
157
- // EEXIST is fine - directory already exists
158
- if(e.code !== "EEXIST") {
159
- throw Sass.new(
160
- `Unable to create temporary directory '${this.realPath}': ${e.message}`
161
- )
162
- }
72
+ if(source) {
73
+ toSuper = `/${directory}`
74
+ realTempDirectoryPath =
75
+ FS.mergeOverlappingPaths(parentRealPath, directory)
76
+ if(!fs.existsSync(realTempDirectoryPath))
77
+ mkdirSync(realTempDirectoryPath)
78
+ } else {
79
+ realTempDirectoryPath =
80
+ mkdtempSync(FS.mergeOverlappingPaths(os.tmpdir(), directory))
81
+ toSuper = path.resolve(path.sep)
163
82
  }
83
+
84
+ super(toSuper, source)
85
+
86
+ this.#tmpReal = new DirectoryObject(realTempDirectoryPath)
87
+ this.#tmpCap = source?.cap ?? this
88
+ }
89
+
90
+ get isTemporary() {
91
+ return true
164
92
  }
165
93
 
166
94
  /**
167
- * Creates a new TempDirectoryObject by extending this directory's path.
95
+ * Returns a plain DirectoryObject representing the actual filesystem location.
96
+ * This provides an "escape hatch" from the capped environment to interact
97
+ * with the real filesystem when needed.
168
98
  *
169
- * Validates that the resulting path remains within the temp directory tree.
170
- *
171
- * @param {string} newPath - The path segment to append
172
- * @returns {TempDirectoryObject} A new TempDirectoryObject with the extended path
173
- * @throws {Sass} If the path would escape the temp directory
174
- * @throws {Sass} If the path is absolute
175
- * @throws {Sass} If the path contains traversal (..)
99
+ * @returns {DirectoryObject} Uncapped directory object at the real filesystem path
176
100
  * @example
177
101
  * const temp = new TempDirectoryObject("myapp")
178
- * const subDir = temp.getDirectory("data")
179
- * console.log(subDir.path) // "/tmp/myapp-ABC123/data"
102
+ * const subdir = temp.getDirectory("data")
103
+ *
104
+ * // Work within the capped environment (virtual paths)
105
+ * console.log(subdir.path) // "/data" (virtual)
106
+ * subdir.getFile("config.json") // Stays within cap
107
+ *
108
+ * // Break out to real filesystem when needed
109
+ * console.log(subdir.real.path) // "/tmp/myapp-ABC123/data" (real)
110
+ * subdir.real.parent // Can traverse outside the cap
180
111
  */
181
- getDirectory(newPath) {
182
- // Delegate to base class getDirectory() which will call TempDirectoryObject constructor
183
- return super.getDirectory(newPath)
112
+ get real() {
113
+ return this.#tmpReal
114
+ }
115
+
116
+ get cap() {
117
+ return this.#tmpCap
184
118
  }
185
119
 
186
120
  /**
187
- * Creates a new FileObject by extending this directory's path.
121
+ * Recursively removes a temporary directory and all its contents.
188
122
  *
189
- * Validates that the resulting path remains within the temp directory tree.
123
+ * This method will delete all files and subdirectories within this directory,
124
+ * then delete the directory itself. It only works on directories explicitly
125
+ * marked as temporary for safety.
190
126
  *
191
- * @param {string} filename - The filename to append
192
- * @returns {FileObject} A new FileObject with the extended path
193
- * @throws {Sass} If the path would escape the temp directory
194
- * @throws {Sass} If the path is absolute
195
- * @throws {Sass} If the path contains traversal (..)
127
+ * @async
128
+ * @returns {Promise<void>}
129
+ * @throws {Sass} If the directory is not marked as temporary
130
+ * @throws {Sass} If the directory deletion fails
196
131
  * @example
197
- * const temp = new TempDirectoryObject("myapp")
198
- * const file = temp.getFile("config.json")
199
- * console.log(file.path) // "/tmp/myapp-ABC123/config.json"
132
+ * const tempDir = new TempDirectoryObject("my-temp")
133
+ * await tempDir.assureExists()
134
+ * // ... use the directory ...
135
+ * await tempDir.remove() // Recursively deletes everything
200
136
  */
201
- getFile(filename) {
202
- // Delegate to base class getFile() which handles security checks
203
- return super.getFile(filename)
137
+ async remove() {
138
+ await this.#recurseDelete(this.real)
139
+ }
140
+
141
+ async #recurseDelete(directory) {
142
+ const {files, directories} = await directory.read()
143
+
144
+ // files first
145
+ for(const file of files)
146
+ await file.delete()
147
+
148
+ // now dir-ty dirs
149
+ for(const dir of directories)
150
+ await this.#recurseDelete(dir)
151
+
152
+ // byebyebyeeee πŸ•ΊπŸΎ
153
+ await directory.delete()
204
154
  }
205
155
 
206
156
  /**
207
- * Returns a string representation of the TempDirectoryObject.
157
+ * TempDirectoryObject does not support fromCwd() since it is specifically
158
+ * designed to work within the OS temporary directory tree.
208
159
  *
209
- * @returns {string} string representation of the TempDirectoryObject
160
+ * @throws {Sass} Always throws an error
210
161
  */
211
- toString() {
212
- return `[TempDirectoryObject: ${this.path}]`
162
+ static fromCwd() {
163
+ throw Sass.new("TempDirectoryObject.fromCwd() is not supported.")
213
164
  }
214
165
  }
package/src/lib/Valid.js CHANGED
@@ -25,7 +25,7 @@ export default class Valid {
25
25
  static type(value, type, options) {
26
26
  Valid.assert(
27
27
  Data.isType(value, type, options),
28
- `Invalid type. Expected ${type}, got ${JSON.stringify(value)}`,
28
+ `Invalid type. Expected ${type}, got ${Data.typeOf(value)}`,
29
29
  1,
30
30
  )
31
31
  }